@earendil-works/pi-session-backend-sqlite-node 0.84.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 +32 -0
- package/README.md +15 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/sqlite/branch-cache.d.ts +6 -0
- package/dist/sqlite/branch-cache.d.ts.map +1 -0
- package/dist/sqlite/branch-cache.js +71 -0
- package/dist/sqlite/branch-cache.js.map +1 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +4 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/migrations/001_initial.sql +123 -0
- package/dist/sqlite/migrations.d.ts +9 -0
- package/dist/sqlite/migrations.d.ts.map +1 -0
- package/dist/sqlite/migrations.js +38 -0
- package/dist/sqlite/migrations.js.map +1 -0
- package/dist/sqlite/repo.d.ts +41 -0
- package/dist/sqlite/repo.d.ts.map +1 -0
- package/dist/sqlite/repo.js +736 -0
- package/dist/sqlite/repo.js.map +1 -0
- package/dist/sqlite/search-backend.d.ts +9 -0
- package/dist/sqlite/search-backend.d.ts.map +1 -0
- package/dist/sqlite/search-backend.js +118 -0
- package/dist/sqlite/search-backend.js.map +1 -0
- package/dist/sqlite/storage/branch-entries.d.ts +35 -0
- package/dist/sqlite/storage/branch-entries.d.ts.map +1 -0
- package/dist/sqlite/storage/branch-entries.js +86 -0
- package/dist/sqlite/storage/branch-entries.js.map +1 -0
- package/dist/sqlite/storage/branch-tips.d.ts +7 -0
- package/dist/sqlite/storage/branch-tips.d.ts.map +1 -0
- package/dist/sqlite/storage/branch-tips.js +25 -0
- package/dist/sqlite/storage/branch-tips.js.map +1 -0
- package/dist/sqlite/storage/entries.d.ts +29 -0
- package/dist/sqlite/storage/entries.d.ts.map +1 -0
- package/dist/sqlite/storage/entries.js +38 -0
- package/dist/sqlite/storage/entries.js.map +1 -0
- package/dist/sqlite/storage/facts.d.ts +19 -0
- package/dist/sqlite/storage/facts.d.ts.map +1 -0
- package/dist/sqlite/storage/facts.js +41 -0
- package/dist/sqlite/storage/facts.js.map +1 -0
- package/dist/sqlite/storage/lanes.d.ts +34 -0
- package/dist/sqlite/storage/lanes.d.ts.map +1 -0
- package/dist/sqlite/storage/lanes.js +106 -0
- package/dist/sqlite/storage/lanes.js.map +1 -0
- package/dist/sqlite/storage/records.d.ts +38 -0
- package/dist/sqlite/storage/records.d.ts.map +1 -0
- package/dist/sqlite/storage/records.js +69 -0
- package/dist/sqlite/storage/records.js.map +1 -0
- package/dist/sqlite/storage/session-sequences.d.ts +7 -0
- package/dist/sqlite/storage/session-sequences.d.ts.map +1 -0
- package/dist/sqlite/storage/session-sequences.js +23 -0
- package/dist/sqlite/storage/session-sequences.js.map +1 -0
- package/dist/sqlite/storage/session-stats.d.ts +17 -0
- package/dist/sqlite/storage/session-stats.d.ts.map +1 -0
- package/dist/sqlite/storage/session-stats.js +45 -0
- package/dist/sqlite/storage/session-stats.js.map +1 -0
- package/dist/sqlite/storage/sessions.d.ts +26 -0
- package/dist/sqlite/storage/sessions.d.ts.map +1 -0
- package/dist/sqlite/storage/sessions.js +102 -0
- package/dist/sqlite/storage/sessions.js.map +1 -0
- package/dist/sqlite/storage/writer-leases.d.ts +15 -0
- package/dist/sqlite/storage/writer-leases.d.ts.map +1 -0
- package/dist/sqlite/storage/writer-leases.js +30 -0
- package/dist/sqlite/storage/writer-leases.js.map +1 -0
- package/dist/sqlite/types.d.ts +44 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function entryPayload(entry) {
|
|
2
|
+
const { type: _type, id: _id, seq: _seq, parentId: _parentId, timestamp: _timestamp, ...payload } = entry;
|
|
3
|
+
return payload;
|
|
4
|
+
}
|
|
5
|
+
function orderedSql(order) {
|
|
6
|
+
return order === "oldestFirst" ? "ASC" : "DESC";
|
|
7
|
+
}
|
|
8
|
+
export function insertEntryRow(db, sessionId, entry) {
|
|
9
|
+
db.prepare("INSERT INTO entries (session_id, id, seq, parent_id, type, timestamp, payload) VALUES (?, ?, ?, ?, ?, ?, ?)").run(sessionId, entry.id, entry.seq, entry.parentId, entry.type, entry.timestamp, entry.payload);
|
|
10
|
+
}
|
|
11
|
+
export function readEntryRow(db, sessionId, entryId) {
|
|
12
|
+
return db
|
|
13
|
+
.prepare("SELECT session_id, seq, id, parent_id, type, timestamp, payload FROM entries WHERE session_id = ? AND id = ?")
|
|
14
|
+
.get(sessionId, entryId);
|
|
15
|
+
}
|
|
16
|
+
export function readEntryRows(db, sessionId, options = {}) {
|
|
17
|
+
const predicates = ["session_id = ?"];
|
|
18
|
+
const params = [sessionId];
|
|
19
|
+
if (options.afterSeq !== undefined) {
|
|
20
|
+
predicates.push("seq > ?");
|
|
21
|
+
params.push(options.afterSeq);
|
|
22
|
+
}
|
|
23
|
+
return db
|
|
24
|
+
.prepare(`SELECT session_id, seq, id, parent_id, type, timestamp, payload
|
|
25
|
+
FROM entries
|
|
26
|
+
WHERE ${predicates.join(" AND ")}
|
|
27
|
+
ORDER BY seq ${orderedSql(options.order)}`)
|
|
28
|
+
.all(...params);
|
|
29
|
+
}
|
|
30
|
+
export function idExistsInEntries(db, sessionId, id) {
|
|
31
|
+
return !!db
|
|
32
|
+
.prepare("SELECT 1 AS found FROM entries WHERE session_id = ? AND id = ? LIMIT 1")
|
|
33
|
+
.get(sessionId, id);
|
|
34
|
+
}
|
|
35
|
+
export function deleteEntryRows(db, sessionId) {
|
|
36
|
+
db.prepare("DELETE FROM entries WHERE session_id = ?").run(sessionId);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/entries.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,YAAY,CAAC,KAAY,EAA2B;IACnE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1G,OAAO,OAAO,CAAC;AAAA,CACf;AAED,SAAS,UAAU,CAAC,KAA6B,EAAU;IAC1D,OAAO,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,CAChD;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAkB,EAAE;IACzF,EAAE,CAAC,OAAO,CACT,6GAA6G,CAC7G,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;AAAA,CAClG;AAED,MAAM,UAAU,YAAY,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAE;IACpF,OAAO,EAAE;SACP,OAAO,CACP,8GAA8G,CAC9G;SACA,GAAG,CAAW,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,CACpC;AAED,MAAM,UAAU,aAAa,CAC5B,EAAkB,EAClB,SAAiB,EACjB,OAAO,GAA8C,EAAE,EACtD;IACD,MAAM,UAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,MAAM,MAAM,GAAc,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE;SACP,OAAO,CACP;;WAEQ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;kBACjB,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAC1C;SACA,GAAG,CAAW,GAAG,MAAM,CAAC,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE,EAAU,EAAE;IACpF,OAAO,CAAC,CAAC,EAAE;SACT,OAAO,CAAC,wEAAwE,CAAC;SACjF,GAAG,CAAoB,SAAS,EAAE,EAAE,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACtE,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CACtE","sourcesContent":["import type { Entry, EntryOrder } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface EntryRow {\n\tsession_id: string;\n\tseq: number;\n\tid: string;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport interface NewEntryRow {\n\tseq: number;\n\tid: string;\n\tparentId: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport function entryPayload(entry: Entry): Record<string, unknown> {\n\tconst { type: _type, id: _id, seq: _seq, parentId: _parentId, timestamp: _timestamp, ...payload } = entry;\n\treturn payload;\n}\n\nfunction orderedSql(order: EntryOrder | undefined): string {\n\treturn order === \"oldestFirst\" ? \"ASC\" : \"DESC\";\n}\n\nexport function insertEntryRow(db: SqliteDatabase, sessionId: string, entry: NewEntryRow) {\n\tdb.prepare(\n\t\t\"INSERT INTO entries (session_id, id, seq, parent_id, type, timestamp, payload) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n\t).run(sessionId, entry.id, entry.seq, entry.parentId, entry.type, entry.timestamp, entry.payload);\n}\n\nexport function readEntryRow(db: SqliteDatabase, sessionId: string, entryId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t\"SELECT session_id, seq, id, parent_id, type, timestamp, payload FROM entries WHERE session_id = ? AND id = ?\",\n\t\t)\n\t\t.get<EntryRow>(sessionId, entryId);\n}\n\nexport function readEntryRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: { afterSeq?: number; order?: EntryOrder } = {},\n) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (options.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(options.afterSeq);\n\t}\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, id, parent_id, type, timestamp, payload\n\t\t\tFROM entries\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq ${orderedSql(options.order)}`,\n\t\t)\n\t\t.all<EntryRow>(...params);\n}\n\nexport function idExistsInEntries(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!db\n\t\t.prepare(\"SELECT 1 AS found FROM entries WHERE session_id = ? AND id = ? LIMIT 1\")\n\t\t.get<{ found: number }>(sessionId, id);\n}\n\nexport function deleteEntryRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM entries WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export interface FactRow {
|
|
3
|
+
session_id: string;
|
|
4
|
+
seq: number;
|
|
5
|
+
kind: string;
|
|
6
|
+
key: string | null;
|
|
7
|
+
value: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function appendFact(db: SqliteDatabase, sessionId: string, seq: number, kind: string, key: string | null, value: string | null): void;
|
|
10
|
+
export declare function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null): FactRow | undefined;
|
|
11
|
+
export declare function readLatestLabelFacts(db: SqliteDatabase, sessionId: string): {
|
|
12
|
+
key: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}[];
|
|
15
|
+
export declare function readFactRows(db: SqliteDatabase, sessionId: string, options?: {
|
|
16
|
+
afterSeq?: number;
|
|
17
|
+
}): FactRow[];
|
|
18
|
+
export declare function deleteFactRows(db: SqliteDatabase, sessionId: string): void;
|
|
19
|
+
//# sourceMappingURL=facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,OAAO;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,UAAU,CACzB,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,QASpB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,uBAUrG;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM;;;IAYzE;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,aAetG;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEnE","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface FactRow {\n\tsession_id: string;\n\tseq: number;\n\tkind: string;\n\tkey: string | null;\n\tvalue: string | null;\n}\n\nexport function appendFact(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tseq: number,\n\tkind: string,\n\tkey: string | null,\n\tvalue: string | null,\n) {\n\tdb.prepare(\"INSERT INTO facts (session_id, seq, kind, key, value) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsessionId,\n\t\tseq,\n\t\tkind,\n\t\tkey,\n\t\tvalue,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, kind, key, value\n\t\t\tFROM facts\n\t\t\tWHERE session_id = ? AND kind = ? AND key IS ?\n\t\t\tORDER BY seq DESC\n\t\t\tLIMIT 1`,\n\t\t)\n\t\t.get<FactRow>(sessionId, kind, key);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT key, value FROM (\n\t\t\t\tSELECT key, value, ROW_NUMBER() OVER (PARTITION BY key ORDER BY seq DESC) AS rank\n\t\t\t\tFROM facts\n\t\t\t\tWHERE session_id = ? AND kind = 'label'\n\t\t\t)\n\t\t\tWHERE rank = 1 AND value IS NOT NULL\n\t\t\tORDER BY key`,\n\t\t)\n\t\t.all<{ key: string; value: string }>(sessionId);\n}\n\nexport function readFactRows(db: SqliteDatabase, sessionId: string, options: { afterSeq?: number } = {}) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (options.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(options.afterSeq);\n\t}\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, kind, key, value\n\t\t\tFROM facts\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq`,\n\t\t)\n\t\t.all<FactRow>(...params);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM facts WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function appendFact(db, sessionId, seq, kind, key, value) {
|
|
2
|
+
db.prepare("INSERT INTO facts (session_id, seq, kind, key, value) VALUES (?, ?, ?, ?, ?)").run(sessionId, seq, kind, key, value);
|
|
3
|
+
}
|
|
4
|
+
export function readLatestFact(db, sessionId, kind, key) {
|
|
5
|
+
return db
|
|
6
|
+
.prepare(`SELECT session_id, seq, kind, key, value
|
|
7
|
+
FROM facts
|
|
8
|
+
WHERE session_id = ? AND kind = ? AND key IS ?
|
|
9
|
+
ORDER BY seq DESC
|
|
10
|
+
LIMIT 1`)
|
|
11
|
+
.get(sessionId, kind, key);
|
|
12
|
+
}
|
|
13
|
+
export function readLatestLabelFacts(db, sessionId) {
|
|
14
|
+
return db
|
|
15
|
+
.prepare(`SELECT key, value FROM (
|
|
16
|
+
SELECT key, value, ROW_NUMBER() OVER (PARTITION BY key ORDER BY seq DESC) AS rank
|
|
17
|
+
FROM facts
|
|
18
|
+
WHERE session_id = ? AND kind = 'label'
|
|
19
|
+
)
|
|
20
|
+
WHERE rank = 1 AND value IS NOT NULL
|
|
21
|
+
ORDER BY key`)
|
|
22
|
+
.all(sessionId);
|
|
23
|
+
}
|
|
24
|
+
export function readFactRows(db, sessionId, options = {}) {
|
|
25
|
+
const predicates = ["session_id = ?"];
|
|
26
|
+
const params = [sessionId];
|
|
27
|
+
if (options.afterSeq !== undefined) {
|
|
28
|
+
predicates.push("seq > ?");
|
|
29
|
+
params.push(options.afterSeq);
|
|
30
|
+
}
|
|
31
|
+
return db
|
|
32
|
+
.prepare(`SELECT session_id, seq, kind, key, value
|
|
33
|
+
FROM facts
|
|
34
|
+
WHERE ${predicates.join(" AND ")}
|
|
35
|
+
ORDER BY seq`)
|
|
36
|
+
.all(...params);
|
|
37
|
+
}
|
|
38
|
+
export function deleteFactRows(db, sessionId) {
|
|
39
|
+
db.prepare("DELETE FROM facts WHERE session_id = ?").run(sessionId);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facts.js","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,UAAU,CACzB,EAAkB,EAClB,SAAiB,EACjB,GAAW,EACX,IAAY,EACZ,GAAkB,EAClB,KAAoB,EACnB;IACD,EAAE,CAAC,OAAO,CAAC,8EAA8E,CAAC,CAAC,GAAG,CAC7F,SAAS,EACT,GAAG,EACH,IAAI,EACJ,GAAG,EACH,KAAK,CACL,CAAC;AAAA,CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,IAAY,EAAE,GAAkB,EAAE;IACvG,OAAO,EAAE;SACP,OAAO,CACP;;;;WAIQ,CACR;SACA,GAAG,CAAU,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAAA,CACrC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IAC3E,OAAO,EAAE;SACP,OAAO,CACP;;;;;;gBAMa,CACb;SACA,GAAG,CAAiC,SAAS,CAAC,CAAC;AAAA,CACjD;AAED,MAAM,UAAU,YAAY,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAO,GAA0B,EAAE,EAAE;IACxG,MAAM,UAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,MAAM,MAAM,GAAc,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE;SACP,OAAO,CACP;;WAEQ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;gBACnB,CACb;SACA,GAAG,CAAU,GAAG,MAAM,CAAC,CAAC;AAAA,CAC1B;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CACpE","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface FactRow {\n\tsession_id: string;\n\tseq: number;\n\tkind: string;\n\tkey: string | null;\n\tvalue: string | null;\n}\n\nexport function appendFact(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tseq: number,\n\tkind: string,\n\tkey: string | null,\n\tvalue: string | null,\n) {\n\tdb.prepare(\"INSERT INTO facts (session_id, seq, kind, key, value) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsessionId,\n\t\tseq,\n\t\tkind,\n\t\tkey,\n\t\tvalue,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, kind, key, value\n\t\t\tFROM facts\n\t\t\tWHERE session_id = ? AND kind = ? AND key IS ?\n\t\t\tORDER BY seq DESC\n\t\t\tLIMIT 1`,\n\t\t)\n\t\t.get<FactRow>(sessionId, kind, key);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT key, value FROM (\n\t\t\t\tSELECT key, value, ROW_NUMBER() OVER (PARTITION BY key ORDER BY seq DESC) AS rank\n\t\t\t\tFROM facts\n\t\t\t\tWHERE session_id = ? AND kind = 'label'\n\t\t\t)\n\t\t\tWHERE rank = 1 AND value IS NOT NULL\n\t\t\tORDER BY key`,\n\t\t)\n\t\t.all<{ key: string; value: string }>(sessionId);\n}\n\nexport function readFactRows(db: SqliteDatabase, sessionId: string, options: { afterSeq?: number } = {}) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (options.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(options.afterSeq);\n\t}\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, kind, key, value\n\t\t\tFROM facts\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq`,\n\t\t)\n\t\t.all<FactRow>(...params);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM facts WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export interface LaneRow {
|
|
3
|
+
session_id: string;
|
|
4
|
+
lane: string;
|
|
5
|
+
leaf_id: string | null;
|
|
6
|
+
open_operation_id: string | null;
|
|
7
|
+
}
|
|
8
|
+
export interface LaneMoveRow {
|
|
9
|
+
session_id: string;
|
|
10
|
+
seq: number;
|
|
11
|
+
lane: string;
|
|
12
|
+
leaf_id: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function createInitialLane(db: SqliteDatabase, sessionId: string, lane?: string, leafId?: string | null): void;
|
|
15
|
+
export declare function readLanes(db: SqliteDatabase, sessionId: string): {
|
|
16
|
+
session_id: string;
|
|
17
|
+
lane: string;
|
|
18
|
+
leaf_id: string | null;
|
|
19
|
+
open_operation_id: string | null;
|
|
20
|
+
}[];
|
|
21
|
+
export declare function readLane(db: SqliteDatabase, sessionId: string, lane: string): LaneRow | undefined;
|
|
22
|
+
export declare function readLaneHead(db: SqliteDatabase, sessionId: string, lane: string): {
|
|
23
|
+
leafId: string | null;
|
|
24
|
+
};
|
|
25
|
+
export declare function createLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null): void;
|
|
26
|
+
export declare function moveLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null): void;
|
|
27
|
+
export declare function setLaneLeaf(db: SqliteDatabase, sessionId: string, lane: string, leafId: string | null): void;
|
|
28
|
+
export declare function startLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string): void;
|
|
29
|
+
export declare function finishLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string): void;
|
|
30
|
+
export declare function readLaneMoveRows(db: SqliteDatabase, sessionId: string, options?: {
|
|
31
|
+
afterSeq?: number;
|
|
32
|
+
}): LaneMoveRow[];
|
|
33
|
+
export declare function deleteLaneRows(db: SqliteDatabase, sessionId: string): void;
|
|
34
|
+
//# sourceMappingURL=lanes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lanes.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/lanes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,OAAO;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,SAAS,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW,QAMnH;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM;;;;;IA2B9D;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,uBAI3E;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;;EAe/E;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,QAOjH;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,QAM/G;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,QAKrG;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAQpG;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAIrG;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,iBAe1G;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAGnE","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface LaneRow {\n\tsession_id: string;\n\tlane: string;\n\tleaf_id: string | null;\n\topen_operation_id: string | null;\n}\n\nexport interface LaneMoveRow {\n\tsession_id: string;\n\tseq: number;\n\tlane: string;\n\tleaf_id: string | null;\n}\n\nexport function createInitialLane(db: SqliteDatabase, sessionId: string, lane = \"main\", leafId: string | null = null) {\n\tdb.prepare(\"INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)\").run(\n\t\tsessionId,\n\t\tlane,\n\t\tleafId,\n\t);\n}\n\nexport function readLanes(db: SqliteDatabase, sessionId: string) {\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT\n\t\t\t\tl.session_id,\n\t\t\t\tl.lane,\n\t\t\t\tl.leaf_id,\n\t\t\t\tl.open_operation_id,\n\t\t\t\t(l.leaf_id IS NULL OR EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id\n\t\t\t\t)) AS leaf_exists\n\t\t\tFROM lanes AS l\n\t\t\tWHERE l.session_id = ?\n\t\t\tORDER BY l.lane`,\n\t\t)\n\t\t.all<LaneRow & { leaf_exists: number }>(sessionId);\n\tfor (const row of rows) {\n\t\tif (row.leaf_exists === 0) {\n\t\t\tthrow new SessionError(\"storage\", `Lane ${row.lane} points at missing entry ${row.leaf_id}`);\n\t\t}\n\t}\n\treturn rows.map(({ session_id, lane, leaf_id, open_operation_id }) => ({\n\t\tsession_id,\n\t\tlane,\n\t\tleaf_id,\n\t\topen_operation_id,\n\t}));\n}\n\nexport function readLane(db: SqliteDatabase, sessionId: string, lane: string) {\n\treturn db\n\t\t.prepare(\"SELECT session_id, lane, leaf_id, open_operation_id FROM lanes WHERE session_id = ? AND lane = ?\")\n\t\t.get<LaneRow>(sessionId, lane);\n}\n\nexport function readLaneHead(db: SqliteDatabase, sessionId: string, lane: string) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT\n\t\t\t\tl.leaf_id,\n\t\t\t\t(l.leaf_id IS NULL OR EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id\n\t\t\t\t)) AS leaf_exists\n\t\t\tFROM lanes AS l\n\t\t\tWHERE l.session_id = ? AND l.lane = ?`,\n\t\t)\n\t\t.get<{ leaf_id: string | null; leaf_exists: number }>(sessionId, lane);\n\tif (!row) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tif (row.leaf_exists === 0) throw new SessionError(\"storage\", `Entry ${row.leaf_id} not found`);\n\treturn { leafId: row.leaf_id };\n}\n\nexport function createLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tdb.prepare(\"INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)\").run(\n\t\tsessionId,\n\t\tlane,\n\t\tleafId,\n\t);\n\tappendLaneMove(db, sessionId, seq, lane, leafId);\n}\n\nexport function moveLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?\")\n\t\t.run(leafId, sessionId, lane);\n\tif (result.changes !== 1) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tappendLaneMove(db, sessionId, seq, lane, leafId);\n}\n\nexport function setLaneLeaf(db: SqliteDatabase, sessionId: string, lane: string, leafId: string | null) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?\")\n\t\t.run(leafId, sessionId, lane);\n\tif (result.changes !== 1) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n}\n\nexport function startLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET open_operation_id = ? WHERE session_id = ? AND lane = ? AND open_operation_id IS NULL\")\n\t\t.run(runId, sessionId, lane);\n\tif (result.changes === 1) return;\n\tconst current = readLane(db, sessionId, lane);\n\tif (!current) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tthrow new SessionError(\"storage\", `Lane ${lane} already has an open operation ${current.open_operation_id}`);\n}\n\nexport function finishLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string) {\n\tdb.prepare(\n\t\t\"UPDATE lanes SET open_operation_id = NULL WHERE session_id = ? AND lane = ? AND open_operation_id = ?\",\n\t).run(sessionId, lane, runId);\n}\n\nexport function readLaneMoveRows(db: SqliteDatabase, sessionId: string, options: { afterSeq?: number } = {}) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (options.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(options.afterSeq);\n\t}\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, lane, leaf_id\n\t\t\tFROM lane_moves\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq`,\n\t\t)\n\t\t.all<LaneMoveRow>(...params);\n}\n\nexport function deleteLaneRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM lane_moves WHERE session_id = ?\").run(sessionId);\n\tdb.prepare(\"DELETE FROM lanes WHERE session_id = ?\").run(sessionId);\n}\n\nfunction appendLaneMove(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tdb.prepare(\"INSERT INTO lane_moves (session_id, seq, lane, leaf_id) VALUES (?, ?, ?, ?)\").run(\n\t\tsessionId,\n\t\tseq,\n\t\tlane,\n\t\tleafId,\n\t);\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export function createInitialLane(db, sessionId, lane = "main", leafId = null) {
|
|
3
|
+
db.prepare("INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)").run(sessionId, lane, leafId);
|
|
4
|
+
}
|
|
5
|
+
export function readLanes(db, sessionId) {
|
|
6
|
+
const rows = db
|
|
7
|
+
.prepare(`SELECT
|
|
8
|
+
l.session_id,
|
|
9
|
+
l.lane,
|
|
10
|
+
l.leaf_id,
|
|
11
|
+
l.open_operation_id,
|
|
12
|
+
(l.leaf_id IS NULL OR EXISTS (
|
|
13
|
+
SELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id
|
|
14
|
+
)) AS leaf_exists
|
|
15
|
+
FROM lanes AS l
|
|
16
|
+
WHERE l.session_id = ?
|
|
17
|
+
ORDER BY l.lane`)
|
|
18
|
+
.all(sessionId);
|
|
19
|
+
for (const row of rows) {
|
|
20
|
+
if (row.leaf_exists === 0) {
|
|
21
|
+
throw new SessionError("storage", `Lane ${row.lane} points at missing entry ${row.leaf_id}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return rows.map(({ session_id, lane, leaf_id, open_operation_id }) => ({
|
|
25
|
+
session_id,
|
|
26
|
+
lane,
|
|
27
|
+
leaf_id,
|
|
28
|
+
open_operation_id,
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
export function readLane(db, sessionId, lane) {
|
|
32
|
+
return db
|
|
33
|
+
.prepare("SELECT session_id, lane, leaf_id, open_operation_id FROM lanes WHERE session_id = ? AND lane = ?")
|
|
34
|
+
.get(sessionId, lane);
|
|
35
|
+
}
|
|
36
|
+
export function readLaneHead(db, sessionId, lane) {
|
|
37
|
+
const row = db
|
|
38
|
+
.prepare(`SELECT
|
|
39
|
+
l.leaf_id,
|
|
40
|
+
(l.leaf_id IS NULL OR EXISTS (
|
|
41
|
+
SELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id
|
|
42
|
+
)) AS leaf_exists
|
|
43
|
+
FROM lanes AS l
|
|
44
|
+
WHERE l.session_id = ? AND l.lane = ?`)
|
|
45
|
+
.get(sessionId, lane);
|
|
46
|
+
if (!row)
|
|
47
|
+
throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
|
|
48
|
+
if (row.leaf_exists === 0)
|
|
49
|
+
throw new SessionError("storage", `Entry ${row.leaf_id} not found`);
|
|
50
|
+
return { leafId: row.leaf_id };
|
|
51
|
+
}
|
|
52
|
+
export function createLane(db, sessionId, seq, lane, leafId) {
|
|
53
|
+
db.prepare("INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)").run(sessionId, lane, leafId);
|
|
54
|
+
appendLaneMove(db, sessionId, seq, lane, leafId);
|
|
55
|
+
}
|
|
56
|
+
export function moveLane(db, sessionId, seq, lane, leafId) {
|
|
57
|
+
const result = db
|
|
58
|
+
.prepare("UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?")
|
|
59
|
+
.run(leafId, sessionId, lane);
|
|
60
|
+
if (result.changes !== 1)
|
|
61
|
+
throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
|
|
62
|
+
appendLaneMove(db, sessionId, seq, lane, leafId);
|
|
63
|
+
}
|
|
64
|
+
export function setLaneLeaf(db, sessionId, lane, leafId) {
|
|
65
|
+
const result = db
|
|
66
|
+
.prepare("UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?")
|
|
67
|
+
.run(leafId, sessionId, lane);
|
|
68
|
+
if (result.changes !== 1)
|
|
69
|
+
throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
|
|
70
|
+
}
|
|
71
|
+
export function startLaneOperation(db, sessionId, lane, runId) {
|
|
72
|
+
const result = db
|
|
73
|
+
.prepare("UPDATE lanes SET open_operation_id = ? WHERE session_id = ? AND lane = ? AND open_operation_id IS NULL")
|
|
74
|
+
.run(runId, sessionId, lane);
|
|
75
|
+
if (result.changes === 1)
|
|
76
|
+
return;
|
|
77
|
+
const current = readLane(db, sessionId, lane);
|
|
78
|
+
if (!current)
|
|
79
|
+
throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
|
|
80
|
+
throw new SessionError("storage", `Lane ${lane} already has an open operation ${current.open_operation_id}`);
|
|
81
|
+
}
|
|
82
|
+
export function finishLaneOperation(db, sessionId, lane, runId) {
|
|
83
|
+
db.prepare("UPDATE lanes SET open_operation_id = NULL WHERE session_id = ? AND lane = ? AND open_operation_id = ?").run(sessionId, lane, runId);
|
|
84
|
+
}
|
|
85
|
+
export function readLaneMoveRows(db, sessionId, options = {}) {
|
|
86
|
+
const predicates = ["session_id = ?"];
|
|
87
|
+
const params = [sessionId];
|
|
88
|
+
if (options.afterSeq !== undefined) {
|
|
89
|
+
predicates.push("seq > ?");
|
|
90
|
+
params.push(options.afterSeq);
|
|
91
|
+
}
|
|
92
|
+
return db
|
|
93
|
+
.prepare(`SELECT session_id, seq, lane, leaf_id
|
|
94
|
+
FROM lane_moves
|
|
95
|
+
WHERE ${predicates.join(" AND ")}
|
|
96
|
+
ORDER BY seq`)
|
|
97
|
+
.all(...params);
|
|
98
|
+
}
|
|
99
|
+
export function deleteLaneRows(db, sessionId) {
|
|
100
|
+
db.prepare("DELETE FROM lane_moves WHERE session_id = ?").run(sessionId);
|
|
101
|
+
db.prepare("DELETE FROM lanes WHERE session_id = ?").run(sessionId);
|
|
102
|
+
}
|
|
103
|
+
function appendLaneMove(db, sessionId, seq, lane, leafId) {
|
|
104
|
+
db.prepare("INSERT INTO lane_moves (session_id, seq, lane, leaf_id) VALUES (?, ?, ?, ?)").run(sessionId, seq, lane, leafId);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=lanes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lanes.js","sourceRoot":"","sources":["../../../src/sqlite/storage/lanes.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface LaneRow {\n\tsession_id: string;\n\tlane: string;\n\tleaf_id: string | null;\n\topen_operation_id: string | null;\n}\n\nexport interface LaneMoveRow {\n\tsession_id: string;\n\tseq: number;\n\tlane: string;\n\tleaf_id: string | null;\n}\n\nexport function createInitialLane(db: SqliteDatabase, sessionId: string, lane = \"main\", leafId: string | null = null) {\n\tdb.prepare(\"INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)\").run(\n\t\tsessionId,\n\t\tlane,\n\t\tleafId,\n\t);\n}\n\nexport function readLanes(db: SqliteDatabase, sessionId: string) {\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT\n\t\t\t\tl.session_id,\n\t\t\t\tl.lane,\n\t\t\t\tl.leaf_id,\n\t\t\t\tl.open_operation_id,\n\t\t\t\t(l.leaf_id IS NULL OR EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id\n\t\t\t\t)) AS leaf_exists\n\t\t\tFROM lanes AS l\n\t\t\tWHERE l.session_id = ?\n\t\t\tORDER BY l.lane`,\n\t\t)\n\t\t.all<LaneRow & { leaf_exists: number }>(sessionId);\n\tfor (const row of rows) {\n\t\tif (row.leaf_exists === 0) {\n\t\t\tthrow new SessionError(\"storage\", `Lane ${row.lane} points at missing entry ${row.leaf_id}`);\n\t\t}\n\t}\n\treturn rows.map(({ session_id, lane, leaf_id, open_operation_id }) => ({\n\t\tsession_id,\n\t\tlane,\n\t\tleaf_id,\n\t\topen_operation_id,\n\t}));\n}\n\nexport function readLane(db: SqliteDatabase, sessionId: string, lane: string) {\n\treturn db\n\t\t.prepare(\"SELECT session_id, lane, leaf_id, open_operation_id FROM lanes WHERE session_id = ? AND lane = ?\")\n\t\t.get<LaneRow>(sessionId, lane);\n}\n\nexport function readLaneHead(db: SqliteDatabase, sessionId: string, lane: string) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT\n\t\t\t\tl.leaf_id,\n\t\t\t\t(l.leaf_id IS NULL OR EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS e WHERE e.session_id = l.session_id AND e.id = l.leaf_id\n\t\t\t\t)) AS leaf_exists\n\t\t\tFROM lanes AS l\n\t\t\tWHERE l.session_id = ? AND l.lane = ?`,\n\t\t)\n\t\t.get<{ leaf_id: string | null; leaf_exists: number }>(sessionId, lane);\n\tif (!row) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tif (row.leaf_exists === 0) throw new SessionError(\"storage\", `Entry ${row.leaf_id} not found`);\n\treturn { leafId: row.leaf_id };\n}\n\nexport function createLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tdb.prepare(\"INSERT INTO lanes (session_id, lane, leaf_id, open_operation_id) VALUES (?, ?, ?, NULL)\").run(\n\t\tsessionId,\n\t\tlane,\n\t\tleafId,\n\t);\n\tappendLaneMove(db, sessionId, seq, lane, leafId);\n}\n\nexport function moveLane(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?\")\n\t\t.run(leafId, sessionId, lane);\n\tif (result.changes !== 1) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tappendLaneMove(db, sessionId, seq, lane, leafId);\n}\n\nexport function setLaneLeaf(db: SqliteDatabase, sessionId: string, lane: string, leafId: string | null) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET leaf_id = ? WHERE session_id = ? AND lane = ?\")\n\t\t.run(leafId, sessionId, lane);\n\tif (result.changes !== 1) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n}\n\nexport function startLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string) {\n\tconst result = db\n\t\t.prepare(\"UPDATE lanes SET open_operation_id = ? WHERE session_id = ? AND lane = ? AND open_operation_id IS NULL\")\n\t\t.run(runId, sessionId, lane);\n\tif (result.changes === 1) return;\n\tconst current = readLane(db, sessionId, lane);\n\tif (!current) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\tthrow new SessionError(\"storage\", `Lane ${lane} already has an open operation ${current.open_operation_id}`);\n}\n\nexport function finishLaneOperation(db: SqliteDatabase, sessionId: string, lane: string, runId: string) {\n\tdb.prepare(\n\t\t\"UPDATE lanes SET open_operation_id = NULL WHERE session_id = ? AND lane = ? AND open_operation_id = ?\",\n\t).run(sessionId, lane, runId);\n}\n\nexport function readLaneMoveRows(db: SqliteDatabase, sessionId: string, options: { afterSeq?: number } = {}) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (options.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(options.afterSeq);\n\t}\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, lane, leaf_id\n\t\t\tFROM lane_moves\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq`,\n\t\t)\n\t\t.all<LaneMoveRow>(...params);\n}\n\nexport function deleteLaneRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM lane_moves WHERE session_id = ?\").run(sessionId);\n\tdb.prepare(\"DELETE FROM lanes WHERE session_id = ?\").run(sessionId);\n}\n\nfunction appendLaneMove(db: SqliteDatabase, sessionId: string, seq: number, lane: string, leafId: string | null) {\n\tdb.prepare(\"INSERT INTO lane_moves (session_id, seq, lane, leaf_id) VALUES (?, ?, ?, ?)\").run(\n\t\tsessionId,\n\t\tseq,\n\t\tlane,\n\t\tleafId,\n\t);\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export interface RecordRow {
|
|
3
|
+
session_id: string;
|
|
4
|
+
seq: number;
|
|
5
|
+
id: string;
|
|
6
|
+
lane: string;
|
|
7
|
+
run_id: string | null;
|
|
8
|
+
type: string;
|
|
9
|
+
op_kind: string | null;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
payload: string;
|
|
12
|
+
}
|
|
13
|
+
export interface NewRecordRow {
|
|
14
|
+
seq: number;
|
|
15
|
+
id: string;
|
|
16
|
+
lane: string;
|
|
17
|
+
runId?: string;
|
|
18
|
+
type: string;
|
|
19
|
+
opKind?: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
payload: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow): void;
|
|
24
|
+
export declare function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string): boolean;
|
|
25
|
+
export declare function deleteRecordRows(db: SqliteDatabase, sessionId: string): void;
|
|
26
|
+
export declare function readRecordRows(db: SqliteDatabase, sessionId: string, query?: {
|
|
27
|
+
lane?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
runId?: string;
|
|
30
|
+
operationKind?: string;
|
|
31
|
+
afterSeq?: number;
|
|
32
|
+
order?: "newestFirst" | "oldestFirst";
|
|
33
|
+
limit?: number;
|
|
34
|
+
}): RecordRow[];
|
|
35
|
+
export declare function readOpenOperationRows(db: SqliteDatabase, sessionId: string, lane: string, _options?: {
|
|
36
|
+
limit?: number;
|
|
37
|
+
}): RecordRow[];
|
|
38
|
+
//# sourceMappingURL=records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,SAAS;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,QAgB1F;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,WAIlF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAED,wBAAgB,cAAc,CAC7B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE;IACN,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,aAAa,GAAG,aAAa,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CACV,eAmCN;AAED,wBAAgB,qBAAqB,CACpC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,SAAS,EAAE,CAqBb","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface RecordRow {\n\tsession_id: string;\n\tseq: number;\n\tid: string;\n\tlane: string;\n\trun_id: string | null;\n\ttype: string;\n\top_kind: string | null;\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport interface NewRecordRow {\n\tseq: number;\n\tid: string;\n\tlane: string;\n\trunId?: string;\n\ttype: string;\n\topKind?: string;\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow) {\n\tdb.prepare(\n\t\t`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t).run(\n\t\tsessionId,\n\t\trecord.seq,\n\t\trecord.id,\n\t\trecord.lane,\n\t\trecord.runId ?? null,\n\t\trecord.type,\n\t\trecord.opKind ?? null,\n\t\trecord.timestamp,\n\t\trecord.payload,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!db\n\t\t.prepare(\"SELECT 1 AS found FROM records WHERE session_id = ? AND id = ? LIMIT 1\")\n\t\t.get<{ found: number }>(sessionId, id);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM records WHERE session_id = ?\").run(sessionId);\n}\n\nexport function readRecordRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tquery: {\n\t\tlane?: string;\n\t\ttype?: string;\n\t\trunId?: string;\n\t\toperationKind?: string;\n\t\tafterSeq?: number;\n\t\torder?: \"newestFirst\" | \"oldestFirst\";\n\t\tlimit?: number;\n\t} = {},\n) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (query.lane !== undefined) {\n\t\tpredicates.push(\"lane = ?\");\n\t\tparams.push(query.lane);\n\t}\n\tif (query.type !== undefined) {\n\t\tpredicates.push(\"type = ?\");\n\t\tparams.push(query.type);\n\t}\n\tif (query.runId !== undefined) {\n\t\tpredicates.push(\"run_id = ?\");\n\t\tparams.push(query.runId);\n\t}\n\tif (query.operationKind !== undefined) {\n\t\tpredicates.push(\"op_kind = ?\");\n\t\tparams.push(query.operationKind);\n\t}\n\tif (query.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(query.afterSeq);\n\t}\n\tconst limit = query.limit === undefined ? \"\" : \" LIMIT ?\";\n\tif (query.limit !== undefined) params.push(query.limit);\n\tconst direction = query.order === \"oldestFirst\" ? \"ASC\" : \"DESC\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\t\tFROM records\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq ${direction}${limit}`,\n\t\t)\n\t\t.all<RecordRow>(...params);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = db\n\t\t.prepare(\"SELECT open_operation_id FROM lanes WHERE session_id = ? AND lane = ?\")\n\t\t.get<{ open_operation_id: string | null }>(sessionId, lane);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\t\tFROM records\n\t\t\tWHERE session_id = ?\n\t\t\t\tAND id = ?`,\n\t\t)\n\t\t.get<RecordRow>(sessionId, laneRow.open_operation_id);\n\tif (!record) {\n\t\tthrow new SessionError(\"storage\", `Lane ${lane} points at missing open operation ${laneRow.open_operation_id}`);\n\t}\n\tif (record.lane !== lane || record.type !== \"operation_started\") {\n\t\tthrow new SessionError(\"storage\", `Lane ${lane} points at invalid open operation ${laneRow.open_operation_id}`);\n\t}\n\treturn [record];\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export function appendRecordRow(db, sessionId, record) {
|
|
3
|
+
db.prepare(`INSERT INTO records
|
|
4
|
+
(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)
|
|
5
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sessionId, record.seq, record.id, record.lane, record.runId ?? null, record.type, record.opKind ?? null, record.timestamp, record.payload);
|
|
6
|
+
}
|
|
7
|
+
export function idExistsInRecords(db, sessionId, id) {
|
|
8
|
+
return !!db
|
|
9
|
+
.prepare("SELECT 1 AS found FROM records WHERE session_id = ? AND id = ? LIMIT 1")
|
|
10
|
+
.get(sessionId, id);
|
|
11
|
+
}
|
|
12
|
+
export function deleteRecordRows(db, sessionId) {
|
|
13
|
+
db.prepare("DELETE FROM records WHERE session_id = ?").run(sessionId);
|
|
14
|
+
}
|
|
15
|
+
export function readRecordRows(db, sessionId, query = {}) {
|
|
16
|
+
const predicates = ["session_id = ?"];
|
|
17
|
+
const params = [sessionId];
|
|
18
|
+
if (query.lane !== undefined) {
|
|
19
|
+
predicates.push("lane = ?");
|
|
20
|
+
params.push(query.lane);
|
|
21
|
+
}
|
|
22
|
+
if (query.type !== undefined) {
|
|
23
|
+
predicates.push("type = ?");
|
|
24
|
+
params.push(query.type);
|
|
25
|
+
}
|
|
26
|
+
if (query.runId !== undefined) {
|
|
27
|
+
predicates.push("run_id = ?");
|
|
28
|
+
params.push(query.runId);
|
|
29
|
+
}
|
|
30
|
+
if (query.operationKind !== undefined) {
|
|
31
|
+
predicates.push("op_kind = ?");
|
|
32
|
+
params.push(query.operationKind);
|
|
33
|
+
}
|
|
34
|
+
if (query.afterSeq !== undefined) {
|
|
35
|
+
predicates.push("seq > ?");
|
|
36
|
+
params.push(query.afterSeq);
|
|
37
|
+
}
|
|
38
|
+
const limit = query.limit === undefined ? "" : " LIMIT ?";
|
|
39
|
+
if (query.limit !== undefined)
|
|
40
|
+
params.push(query.limit);
|
|
41
|
+
const direction = query.order === "oldestFirst" ? "ASC" : "DESC";
|
|
42
|
+
return db
|
|
43
|
+
.prepare(`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload
|
|
44
|
+
FROM records
|
|
45
|
+
WHERE ${predicates.join(" AND ")}
|
|
46
|
+
ORDER BY seq ${direction}${limit}`)
|
|
47
|
+
.all(...params);
|
|
48
|
+
}
|
|
49
|
+
export function readOpenOperationRows(db, sessionId, lane, _options = {}) {
|
|
50
|
+
const laneRow = db
|
|
51
|
+
.prepare("SELECT open_operation_id FROM lanes WHERE session_id = ? AND lane = ?")
|
|
52
|
+
.get(sessionId, lane);
|
|
53
|
+
if (!laneRow?.open_operation_id)
|
|
54
|
+
return [];
|
|
55
|
+
const record = db
|
|
56
|
+
.prepare(`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload
|
|
57
|
+
FROM records
|
|
58
|
+
WHERE session_id = ?
|
|
59
|
+
AND id = ?`)
|
|
60
|
+
.get(sessionId, laneRow.open_operation_id);
|
|
61
|
+
if (!record) {
|
|
62
|
+
throw new SessionError("storage", `Lane ${lane} points at missing open operation ${laneRow.open_operation_id}`);
|
|
63
|
+
}
|
|
64
|
+
if (record.lane !== lane || record.type !== "operation_started") {
|
|
65
|
+
throw new SessionError("storage", `Lane ${lane} points at invalid open operation ${laneRow.open_operation_id}`);
|
|
66
|
+
}
|
|
67
|
+
return [record];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA0B7D,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,MAAoB,EAAE;IAC5F,EAAE,CAAC,OAAO,CACT;;sCAEoC,CACpC,CAAC,GAAG,CACJ,SAAS,EACT,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,EAAE,EACT,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,IAAI,IAAI,EACpB,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,MAAM,IAAI,IAAI,EACrB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,OAAO,CACd,CAAC;AAAA,CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE,EAAU,EAAE;IACpF,OAAO,CAAC,CAAC,EAAE;SACT,OAAO,CAAC,wEAAwE,CAAC;SACjF,GAAG,CAAoB,SAAS,EAAE,EAAE,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACvE,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CACtE;AAED,MAAM,UAAU,cAAc,CAC7B,EAAkB,EAClB,SAAiB,EACjB,KAAK,GAQD,EAAE,EACL;IACD,MAAM,UAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,MAAM,MAAM,GAAc,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACvC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IAC1D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IACjE,OAAO,EAAE;SACP,OAAO,CACP;;WAEQ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;kBACjB,SAAS,GAAG,KAAK,EAAE,CAClC;SACA,GAAG,CAAY,GAAG,MAAM,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,qBAAqB,CACpC,EAAkB,EAClB,SAAiB,EACjB,IAAY,EACZ,QAAQ,GAAuB,EAAE,EACnB;IACd,MAAM,OAAO,GAAG,EAAE;SAChB,OAAO,CAAC,uEAAuE,CAAC;SAChF,GAAG,CAAuC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,iBAAiB;QAAE,OAAO,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CACP;;;eAGY,CACZ;SACA,GAAG,CAAY,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,QAAQ,IAAI,qCAAqC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACjH,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QACjE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,QAAQ,IAAI,qCAAqC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,CAAC;AAAA,CAChB","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface RecordRow {\n\tsession_id: string;\n\tseq: number;\n\tid: string;\n\tlane: string;\n\trun_id: string | null;\n\ttype: string;\n\top_kind: string | null;\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport interface NewRecordRow {\n\tseq: number;\n\tid: string;\n\tlane: string;\n\trunId?: string;\n\ttype: string;\n\topKind?: string;\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow) {\n\tdb.prepare(\n\t\t`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t).run(\n\t\tsessionId,\n\t\trecord.seq,\n\t\trecord.id,\n\t\trecord.lane,\n\t\trecord.runId ?? null,\n\t\trecord.type,\n\t\trecord.opKind ?? null,\n\t\trecord.timestamp,\n\t\trecord.payload,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!db\n\t\t.prepare(\"SELECT 1 AS found FROM records WHERE session_id = ? AND id = ? LIMIT 1\")\n\t\t.get<{ found: number }>(sessionId, id);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM records WHERE session_id = ?\").run(sessionId);\n}\n\nexport function readRecordRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tquery: {\n\t\tlane?: string;\n\t\ttype?: string;\n\t\trunId?: string;\n\t\toperationKind?: string;\n\t\tafterSeq?: number;\n\t\torder?: \"newestFirst\" | \"oldestFirst\";\n\t\tlimit?: number;\n\t} = {},\n) {\n\tconst predicates = [\"session_id = ?\"];\n\tconst params: unknown[] = [sessionId];\n\tif (query.lane !== undefined) {\n\t\tpredicates.push(\"lane = ?\");\n\t\tparams.push(query.lane);\n\t}\n\tif (query.type !== undefined) {\n\t\tpredicates.push(\"type = ?\");\n\t\tparams.push(query.type);\n\t}\n\tif (query.runId !== undefined) {\n\t\tpredicates.push(\"run_id = ?\");\n\t\tparams.push(query.runId);\n\t}\n\tif (query.operationKind !== undefined) {\n\t\tpredicates.push(\"op_kind = ?\");\n\t\tparams.push(query.operationKind);\n\t}\n\tif (query.afterSeq !== undefined) {\n\t\tpredicates.push(\"seq > ?\");\n\t\tparams.push(query.afterSeq);\n\t}\n\tconst limit = query.limit === undefined ? \"\" : \" LIMIT ?\";\n\tif (query.limit !== undefined) params.push(query.limit);\n\tconst direction = query.order === \"oldestFirst\" ? \"ASC\" : \"DESC\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\t\tFROM records\n\t\t\tWHERE ${predicates.join(\" AND \")}\n\t\t\tORDER BY seq ${direction}${limit}`,\n\t\t)\n\t\t.all<RecordRow>(...params);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = db\n\t\t.prepare(\"SELECT open_operation_id FROM lanes WHERE session_id = ? AND lane = ?\")\n\t\t.get<{ open_operation_id: string | null }>(sessionId, lane);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\t\tFROM records\n\t\t\tWHERE session_id = ?\n\t\t\t\tAND id = ?`,\n\t\t)\n\t\t.get<RecordRow>(sessionId, laneRow.open_operation_id);\n\tif (!record) {\n\t\tthrow new SessionError(\"storage\", `Lane ${lane} points at missing open operation ${laneRow.open_operation_id}`);\n\t}\n\tif (record.lane !== lane || record.type !== \"operation_started\") {\n\t\tthrow new SessionError(\"storage\", `Lane ${lane} points at invalid open operation ${laneRow.open_operation_id}`);\n\t}\n\treturn [record];\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export declare function createSequence(db: SqliteDatabase, sessionId: string, nextSeq?: number): void;
|
|
3
|
+
export declare function getNextSequence(db: SqliteDatabase, sessionId: string): number;
|
|
4
|
+
export declare function setNextSequence(db: SqliteDatabase, sessionId: string, nextSeq: number): void;
|
|
5
|
+
export declare function advanceSequence(db: SqliteDatabase, sessionId: string, seq: number): void;
|
|
6
|
+
export declare function deleteSequence(db: SqliteDatabase, sessionId: string): void;
|
|
7
|
+
//# sourceMappingURL=session-sequences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-sequences.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-sequences.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,SAAI,QAEhF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,UAQpE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAErF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAEjF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEnE","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function createSequence(db: SqliteDatabase, sessionId: string, nextSeq = 1) {\n\tdb.prepare(\"INSERT INTO session_sequences (session_id, next_seq) VALUES (?, ?)\").run(sessionId, nextSeq);\n}\n\nexport function getNextSequence(db: SqliteDatabase, sessionId: string) {\n\tconst sequenceRow = db\n\t\t.prepare(\"SELECT next_seq FROM session_sequences WHERE session_id = ?\")\n\t\t.get<{ next_seq: number }>(sessionId);\n\tif (!sequenceRow) {\n\t\tthrow new SessionError(\"storage\", `Missing sequence row for session ${sessionId}`);\n\t}\n\treturn sequenceRow.next_seq;\n}\n\nexport function setNextSequence(db: SqliteDatabase, sessionId: string, nextSeq: number) {\n\tdb.prepare(\"UPDATE session_sequences SET next_seq = ? WHERE session_id = ?\").run(nextSeq, sessionId);\n}\n\nexport function advanceSequence(db: SqliteDatabase, sessionId: string, seq: number) {\n\tsetNextSequence(db, sessionId, seq + 1);\n}\n\nexport function deleteSequence(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM session_sequences WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export function createSequence(db, sessionId, nextSeq = 1) {
|
|
3
|
+
db.prepare("INSERT INTO session_sequences (session_id, next_seq) VALUES (?, ?)").run(sessionId, nextSeq);
|
|
4
|
+
}
|
|
5
|
+
export function getNextSequence(db, sessionId) {
|
|
6
|
+
const sequenceRow = db
|
|
7
|
+
.prepare("SELECT next_seq FROM session_sequences WHERE session_id = ?")
|
|
8
|
+
.get(sessionId);
|
|
9
|
+
if (!sequenceRow) {
|
|
10
|
+
throw new SessionError("storage", `Missing sequence row for session ${sessionId}`);
|
|
11
|
+
}
|
|
12
|
+
return sequenceRow.next_seq;
|
|
13
|
+
}
|
|
14
|
+
export function setNextSequence(db, sessionId, nextSeq) {
|
|
15
|
+
db.prepare("UPDATE session_sequences SET next_seq = ? WHERE session_id = ?").run(nextSeq, sessionId);
|
|
16
|
+
}
|
|
17
|
+
export function advanceSequence(db, sessionId, seq) {
|
|
18
|
+
setNextSequence(db, sessionId, seq + 1);
|
|
19
|
+
}
|
|
20
|
+
export function deleteSequence(db, sessionId) {
|
|
21
|
+
db.prepare("DELETE FROM session_sequences WHERE session_id = ?").run(sessionId);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=session-sequences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-sequences.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAO,GAAG,CAAC,EAAE;IAClF,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,CACzG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACtE,MAAM,WAAW,GAAG,EAAE;SACpB,OAAO,CAAC,6DAA6D,CAAC;SACtE,GAAG,CAAuB,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,oCAAoC,SAAS,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,WAAW,CAAC,QAAQ,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAE;IACvF,EAAE,CAAC,OAAO,CAAC,gEAAgE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAAA,CACrG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,GAAW,EAAE;IACnF,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAChF","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function createSequence(db: SqliteDatabase, sessionId: string, nextSeq = 1) {\n\tdb.prepare(\"INSERT INTO session_sequences (session_id, next_seq) VALUES (?, ?)\").run(sessionId, nextSeq);\n}\n\nexport function getNextSequence(db: SqliteDatabase, sessionId: string) {\n\tconst sequenceRow = db\n\t\t.prepare(\"SELECT next_seq FROM session_sequences WHERE session_id = ?\")\n\t\t.get<{ next_seq: number }>(sessionId);\n\tif (!sequenceRow) {\n\t\tthrow new SessionError(\"storage\", `Missing sequence row for session ${sessionId}`);\n\t}\n\treturn sequenceRow.next_seq;\n}\n\nexport function setNextSequence(db: SqliteDatabase, sessionId: string, nextSeq: number) {\n\tdb.prepare(\"UPDATE session_sequences SET next_seq = ? WHERE session_id = ?\").run(nextSeq, sessionId);\n}\n\nexport function advanceSequence(db: SqliteDatabase, sessionId: string, seq: number) {\n\tsetNextSequence(db, sessionId, seq + 1);\n}\n\nexport function deleteSequence(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM session_sequences WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type SessionStats } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
4
|
+
export interface SessionStatsRow {
|
|
5
|
+
session_id: string;
|
|
6
|
+
message_count: number;
|
|
7
|
+
cached_tokens: number;
|
|
8
|
+
uncached_tokens: number;
|
|
9
|
+
total_tokens: number;
|
|
10
|
+
cost_total: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function createStats(db: SqliteDatabase, sessionId: string, messageCount?: number): void;
|
|
13
|
+
export declare function readStats(db: SqliteDatabase, sessionId: string): SessionStats;
|
|
14
|
+
export declare function incrementMessageCount(db: SqliteDatabase, sessionId: string): void;
|
|
15
|
+
export declare function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void;
|
|
16
|
+
export declare function deleteStats(db: SqliteDatabase, sessionId: string): void;
|
|
17
|
+
//# sourceMappingURL=session-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-stats.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI,CAMzF;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAgB7E;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAKjF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAYzF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAEvE","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tdb.prepare(\n\t\t`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (?, ?, 0, 0, 0, 0)`,\n\t).run(sessionId, messageCount);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\t\tFROM session_stats\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.get<SessionStatsRow>(sessionId);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = db\n\t\t.prepare(\"UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?\")\n\t\t.run(sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE session_stats\n\t\t\tSET cached_tokens = cached_tokens + ?,\n\t\t\t\tuncached_tokens = uncached_tokens + ?,\n\t\t\t\ttotal_tokens = total_tokens + ?,\n\t\t\t\tcost_total = cost_total + ?\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tdb.prepare(\"DELETE FROM session_stats WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export function createStats(db, sessionId, messageCount = 0) {
|
|
3
|
+
db.prepare(`INSERT INTO session_stats
|
|
4
|
+
(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)
|
|
5
|
+
VALUES (?, ?, 0, 0, 0, 0)`).run(sessionId, messageCount);
|
|
6
|
+
}
|
|
7
|
+
export function readStats(db, sessionId) {
|
|
8
|
+
const row = db
|
|
9
|
+
.prepare(`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total
|
|
10
|
+
FROM session_stats
|
|
11
|
+
WHERE session_id = ?`)
|
|
12
|
+
.get(sessionId);
|
|
13
|
+
if (!row)
|
|
14
|
+
throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
|
|
15
|
+
return {
|
|
16
|
+
messageCount: row.message_count,
|
|
17
|
+
cachedTokens: row.cached_tokens,
|
|
18
|
+
uncachedTokens: row.uncached_tokens,
|
|
19
|
+
totalTokens: row.total_tokens,
|
|
20
|
+
costTotal: row.cost_total,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function incrementMessageCount(db, sessionId) {
|
|
24
|
+
const result = db
|
|
25
|
+
.prepare("UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?")
|
|
26
|
+
.run(sessionId);
|
|
27
|
+
if (result.changes !== 1)
|
|
28
|
+
throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
|
|
29
|
+
}
|
|
30
|
+
export function addUsageToStats(db, sessionId, usage) {
|
|
31
|
+
const result = db
|
|
32
|
+
.prepare(`UPDATE session_stats
|
|
33
|
+
SET cached_tokens = cached_tokens + ?,
|
|
34
|
+
uncached_tokens = uncached_tokens + ?,
|
|
35
|
+
total_tokens = total_tokens + ?,
|
|
36
|
+
cost_total = cost_total + ?
|
|
37
|
+
WHERE session_id = ?`)
|
|
38
|
+
.run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);
|
|
39
|
+
if (result.changes !== 1)
|
|
40
|
+
throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
|
|
41
|
+
}
|
|
42
|
+
export function deleteStats(db, sessionId) {
|
|
43
|
+
db.prepare("DELETE FROM session_stats WHERE session_id = ?").run(sessionId);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=session-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-stats.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,+BAA+B,CAAC;AAahF,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAE,YAAY,GAAG,CAAC,EAAQ;IAC1F,EAAE,CAAC,OAAO,CACT;;6BAE2B,CAC3B,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAAA,CAC/B;AAED,MAAM,UAAU,SAAS,CAAC,EAAkB,EAAE,SAAiB,EAAgB;IAC9E,MAAM,GAAG,GAAG,EAAE;SACZ,OAAO,CACP;;wBAEqB,CACrB;SACA,GAAG,CAAkB,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;IAC1F,OAAO;QACN,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,cAAc,EAAE,GAAG,CAAC,eAAe;QACnC,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,SAAS,EAAE,GAAG,CAAC,UAAU;KACzB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IAClF,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CAAC,iFAAiF,CAAC;SAC1F,GAAG,CAAC,SAAS,CAAC,CAAC;IACjB,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAY,EAAQ;IAC1F,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CACP;;;;;wBAKqB,CACrB;SACA,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACvG,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IACxE,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAC5E","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tdb.prepare(\n\t\t`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (?, ?, 0, 0, 0, 0)`,\n\t).run(sessionId, messageCount);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\t\tFROM session_stats\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.get<SessionStatsRow>(sessionId);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = db\n\t\t.prepare(\"UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?\")\n\t\t.run(sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE session_stats\n\t\t\tSET cached_tokens = cached_tokens + ?,\n\t\t\t\tuncached_tokens = uncached_tokens + ?,\n\t\t\t\ttotal_tokens = total_tokens + ?,\n\t\t\t\tcost_total = cost_total + ?\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tdb.prepare(\"DELETE FROM session_stats WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SqliteDatabase, SqliteSessionMetadata } from "../types.ts";
|
|
2
|
+
export interface SessionRow {
|
|
3
|
+
id: string;
|
|
4
|
+
created_at: string;
|
|
5
|
+
metadata: string | null;
|
|
6
|
+
cwd: string;
|
|
7
|
+
parent_session_id: string | null;
|
|
8
|
+
has_session_name: number;
|
|
9
|
+
session_name: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface NewSessionRow {
|
|
12
|
+
id: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
cwd: string;
|
|
15
|
+
parentSessionId?: string;
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export declare function sessionExists(db: SqliteDatabase, sessionId: string): boolean;
|
|
19
|
+
export declare function insertSessionRow(db: SqliteDatabase, session: NewSessionRow): void;
|
|
20
|
+
export declare function readSessionRow(db: SqliteDatabase, sessionId: string): SessionRow | undefined;
|
|
21
|
+
export declare function readSessionRows(db: SqliteDatabase, options?: {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
}): SessionRow[];
|
|
24
|
+
export declare function deleteSessionRow(db: SqliteDatabase, sessionId: string): void;
|
|
25
|
+
export declare function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata;
|
|
26
|
+
//# sourceMappingURL=sessions.d.ts.map
|