@earendil-works/pi-session-backend-sqlite-node 0.84.0 → 0.84.2
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 +11 -1
- package/README.md +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/sqlite/branch-cache.d.ts.map +1 -1
- package/dist/sqlite/branch-cache.js +12 -13
- package/dist/sqlite/branch-cache.js.map +1 -1
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.d.ts.map +1 -1
- package/dist/sqlite/index.js +1 -0
- package/dist/sqlite/index.js.map +1 -1
- package/dist/sqlite/migrations/001_initial.sql +9 -10
- package/dist/sqlite/migrations.d.ts.map +1 -1
- package/dist/sqlite/migrations.js +5 -4
- package/dist/sqlite/migrations.js.map +1 -1
- package/dist/sqlite/repo.d.ts.map +1 -1
- package/dist/sqlite/repo.js +60 -46
- package/dist/sqlite/repo.js.map +1 -1
- package/dist/sqlite/search-backend.d.ts +7 -2
- package/dist/sqlite/search-backend.d.ts.map +1 -1
- package/dist/sqlite/search-backend.js +60 -25
- package/dist/sqlite/search-backend.js.map +1 -1
- package/dist/sqlite/sql.d.ts +19 -0
- package/dist/sqlite/sql.d.ts.map +1 -0
- package/dist/sqlite/sql.js +58 -0
- package/dist/sqlite/sql.js.map +1 -0
- package/dist/sqlite/storage/branch-entries.d.ts +8 -2
- package/dist/sqlite/storage/branch-entries.d.ts.map +1 -1
- package/dist/sqlite/storage/branch-entries.js +87 -61
- package/dist/sqlite/storage/branch-entries.js.map +1 -1
- package/dist/sqlite/storage/branch-tips.d.ts.map +1 -1
- package/dist/sqlite/storage/branch-tips.js +8 -11
- package/dist/sqlite/storage/branch-tips.js.map +1 -1
- package/dist/sqlite/storage/entries.d.ts +7 -2
- package/dist/sqlite/storage/entries.d.ts.map +1 -1
- package/dist/sqlite/storage/entries.js +22 -23
- package/dist/sqlite/storage/entries.js.map +1 -1
- package/dist/sqlite/storage/facts.d.ts +1 -0
- package/dist/sqlite/storage/facts.d.ts.map +1 -1
- package/dist/sqlite/storage/facts.js +26 -29
- package/dist/sqlite/storage/facts.js.map +1 -1
- package/dist/sqlite/storage/lanes.d.ts +1 -0
- package/dist/sqlite/storage/lanes.d.ts.map +1 -1
- package/dist/sqlite/storage/lanes.js +41 -52
- package/dist/sqlite/storage/lanes.js.map +1 -1
- package/dist/sqlite/storage/records.d.ts +2 -2
- package/dist/sqlite/storage/records.d.ts.map +1 -1
- package/dist/sqlite/storage/records.js +27 -47
- package/dist/sqlite/storage/records.js.map +1 -1
- package/dist/sqlite/storage/session-sequences.d.ts.map +1 -1
- package/dist/sqlite/storage/session-sequences.js +5 -6
- package/dist/sqlite/storage/session-sequences.js.map +1 -1
- package/dist/sqlite/storage/session-stats.d.ts.map +1 -1
- package/dist/sqlite/storage/session-stats.js +14 -19
- package/dist/sqlite/storage/session-stats.js.map +1 -1
- package/dist/sqlite/storage/sessions.d.ts +2 -2
- package/dist/sqlite/storage/sessions.d.ts.map +1 -1
- package/dist/sqlite/storage/sessions.js +38 -41
- package/dist/sqlite/storage/sessions.js.map +1 -1
- package/dist/sqlite/storage/writer-leases.d.ts.map +1 -1
- package/dist/sqlite/storage/writer-leases.js +18 -17
- package/dist/sqlite/storage/writer-leases.js.map +1 -1
- package/dist/sqlite/types.d.ts +1 -0
- package/dist/sqlite/types.d.ts.map +1 -1
- package/dist/sqlite/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/sqlite/sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElE,KAAK,gBAAgB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3C,4DAA4D;AAC5D,qBAAa,QAAQ;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IAEpC,YAAY,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,OAAO,EAAO,EAG7D;IAED,IAAI,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAG7B;IAED,GAAG,CAAC,EAAE,EAAE,cAAc,GAAG,eAAe,CAEvC;IAED,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAE7D;IAED,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,EAAE,CAEnD;IAED,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,CAE/D;CACD;AAED,4GAA4G;AAC5G,wBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,gBAAgB,EAAE,GAAG,QAAQ,CAe1F;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAU5F","sourcesContent":["import type { SqliteDatabase, SqliteRunResult } from \"./types.ts\";\n\ntype SqlTemplateValue = unknown | SqlQuery;\n\n/** A parameterized SQLite query produced by {@link sql}. */\nexport class SqlQuery {\n\treadonly queryText: string;\n\treadonly params: readonly unknown[];\n\n\tconstructor(queryText: string, params: readonly unknown[] = []) {\n\t\tthis.queryText = queryText;\n\t\tthis.params = params;\n\t}\n\n\texec(db: SqliteDatabase): void {\n\t\tif (this.params.length > 0) throw new TypeError(\"SQLite exec queries cannot have parameters\");\n\t\tdb.exec(this.queryText);\n\t}\n\n\trun(db: SqliteDatabase): SqliteRunResult {\n\t\treturn db.prepare(this.queryText).run(...this.params);\n\t}\n\n\tget<TRow extends object>(db: SqliteDatabase): TRow | undefined {\n\t\treturn db.prepare(this.queryText).get<TRow>(...this.params);\n\t}\n\n\tall<TRow extends object>(db: SqliteDatabase): TRow[] {\n\t\treturn db.prepare(this.queryText).all<TRow>(...this.params);\n\t}\n\n\titerate<TRow extends object>(db: SqliteDatabase): Iterable<TRow> {\n\t\treturn db.prepare(this.queryText).iterate<TRow>(...this.params);\n\t}\n}\n\n/** Builds a parameterized query. Nested queries are inlined; other interpolations become `?` parameters. */\nexport function sql(strings: TemplateStringsArray, ...values: SqlTemplateValue[]): SqlQuery {\n\tlet queryText = strings[0] ?? \"\";\n\tconst params: unknown[] = [];\n\tfor (let index = 0; index < values.length; index++) {\n\t\tconst value = values[index];\n\t\tif (value instanceof SqlQuery) {\n\t\t\tqueryText += value.queryText;\n\t\t\tparams.push(...value.params);\n\t\t} else {\n\t\t\tqueryText += \"?\";\n\t\t\tparams.push(value);\n\t\t}\n\t\tqueryText += strings[index + 1] ?? \"\";\n\t}\n\treturn new SqlQuery(queryText, params);\n}\n\n/** Joins trusted query fragments while preserving their parameter order. */\nexport function joinSqlFragments(fragments: readonly SqlQuery[], separator: string): SqlQuery {\n\tlet queryText = \"\";\n\tconst params: unknown[] = [];\n\tfor (let index = 0; index < fragments.length; index++) {\n\t\tif (index > 0) queryText += separator;\n\t\tconst fragment = fragments[index]!;\n\t\tqueryText += fragment.queryText;\n\t\tparams.push(...fragment.params);\n\t}\n\treturn new SqlQuery(queryText, params);\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** A parameterized SQLite query produced by {@link sql}. */
|
|
2
|
+
export class SqlQuery {
|
|
3
|
+
queryText;
|
|
4
|
+
params;
|
|
5
|
+
constructor(queryText, params = []) {
|
|
6
|
+
this.queryText = queryText;
|
|
7
|
+
this.params = params;
|
|
8
|
+
}
|
|
9
|
+
exec(db) {
|
|
10
|
+
if (this.params.length > 0)
|
|
11
|
+
throw new TypeError("SQLite exec queries cannot have parameters");
|
|
12
|
+
db.exec(this.queryText);
|
|
13
|
+
}
|
|
14
|
+
run(db) {
|
|
15
|
+
return db.prepare(this.queryText).run(...this.params);
|
|
16
|
+
}
|
|
17
|
+
get(db) {
|
|
18
|
+
return db.prepare(this.queryText).get(...this.params);
|
|
19
|
+
}
|
|
20
|
+
all(db) {
|
|
21
|
+
return db.prepare(this.queryText).all(...this.params);
|
|
22
|
+
}
|
|
23
|
+
iterate(db) {
|
|
24
|
+
return db.prepare(this.queryText).iterate(...this.params);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Builds a parameterized query. Nested queries are inlined; other interpolations become `?` parameters. */
|
|
28
|
+
export function sql(strings, ...values) {
|
|
29
|
+
let queryText = strings[0] ?? "";
|
|
30
|
+
const params = [];
|
|
31
|
+
for (let index = 0; index < values.length; index++) {
|
|
32
|
+
const value = values[index];
|
|
33
|
+
if (value instanceof SqlQuery) {
|
|
34
|
+
queryText += value.queryText;
|
|
35
|
+
params.push(...value.params);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
queryText += "?";
|
|
39
|
+
params.push(value);
|
|
40
|
+
}
|
|
41
|
+
queryText += strings[index + 1] ?? "";
|
|
42
|
+
}
|
|
43
|
+
return new SqlQuery(queryText, params);
|
|
44
|
+
}
|
|
45
|
+
/** Joins trusted query fragments while preserving their parameter order. */
|
|
46
|
+
export function joinSqlFragments(fragments, separator) {
|
|
47
|
+
let queryText = "";
|
|
48
|
+
const params = [];
|
|
49
|
+
for (let index = 0; index < fragments.length; index++) {
|
|
50
|
+
if (index > 0)
|
|
51
|
+
queryText += separator;
|
|
52
|
+
const fragment = fragments[index];
|
|
53
|
+
queryText += fragment.queryText;
|
|
54
|
+
params.push(...fragment.params);
|
|
55
|
+
}
|
|
56
|
+
return new SqlQuery(queryText, params);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sqlite/sql.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SqliteDatabase, SqliteRunResult } from \"./types.ts\";\n\ntype SqlTemplateValue = unknown | SqlQuery;\n\n/** A parameterized SQLite query produced by {@link sql}. */\nexport class SqlQuery {\n\treadonly queryText: string;\n\treadonly params: readonly unknown[];\n\n\tconstructor(queryText: string, params: readonly unknown[] = []) {\n\t\tthis.queryText = queryText;\n\t\tthis.params = params;\n\t}\n\n\texec(db: SqliteDatabase): void {\n\t\tif (this.params.length > 0) throw new TypeError(\"SQLite exec queries cannot have parameters\");\n\t\tdb.exec(this.queryText);\n\t}\n\n\trun(db: SqliteDatabase): SqliteRunResult {\n\t\treturn db.prepare(this.queryText).run(...this.params);\n\t}\n\n\tget<TRow extends object>(db: SqliteDatabase): TRow | undefined {\n\t\treturn db.prepare(this.queryText).get<TRow>(...this.params);\n\t}\n\n\tall<TRow extends object>(db: SqliteDatabase): TRow[] {\n\t\treturn db.prepare(this.queryText).all<TRow>(...this.params);\n\t}\n\n\titerate<TRow extends object>(db: SqliteDatabase): Iterable<TRow> {\n\t\treturn db.prepare(this.queryText).iterate<TRow>(...this.params);\n\t}\n}\n\n/** Builds a parameterized query. Nested queries are inlined; other interpolations become `?` parameters. */\nexport function sql(strings: TemplateStringsArray, ...values: SqlTemplateValue[]): SqlQuery {\n\tlet queryText = strings[0] ?? \"\";\n\tconst params: unknown[] = [];\n\tfor (let index = 0; index < values.length; index++) {\n\t\tconst value = values[index];\n\t\tif (value instanceof SqlQuery) {\n\t\t\tqueryText += value.queryText;\n\t\t\tparams.push(...value.params);\n\t\t} else {\n\t\t\tqueryText += \"?\";\n\t\t\tparams.push(value);\n\t\t}\n\t\tqueryText += strings[index + 1] ?? \"\";\n\t}\n\treturn new SqlQuery(queryText, params);\n}\n\n/** Joins trusted query fragments while preserving their parameter order. */\nexport function joinSqlFragments(fragments: readonly SqlQuery[], separator: string): SqlQuery {\n\tlet queryText = \"\";\n\tconst params: unknown[] = [];\n\tfor (let index = 0; index < fragments.length; index++) {\n\t\tif (index > 0) queryText += separator;\n\t\tconst fragment = fragments[index]!;\n\t\tqueryText += fragment.queryText;\n\t\tparams.push(...fragment.params);\n\t}\n\treturn new SqlQuery(queryText, params);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Entry } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { SqliteDatabase } from "../types.ts";
|
|
3
3
|
/** Derived root-to-tip branch cache membership. Canonical parent links remain in entries. */
|
|
4
4
|
export interface CachedBranch {
|
|
@@ -11,13 +11,19 @@ export interface CachedBranchEntryRow {
|
|
|
11
11
|
entry_seq: number;
|
|
12
12
|
parent_id: string | null;
|
|
13
13
|
type: Entry["type"];
|
|
14
|
-
timestamp:
|
|
14
|
+
timestamp: number;
|
|
15
15
|
payload: string;
|
|
16
16
|
}
|
|
17
17
|
export interface CachedBranchQuery {
|
|
18
|
+
type?: Entry["type"];
|
|
19
|
+
customType?: string;
|
|
18
20
|
stopAtType?: Entry["type"];
|
|
19
21
|
stopAtId?: string;
|
|
22
|
+
cursor?: {
|
|
23
|
+
afterSeq: number;
|
|
24
|
+
};
|
|
20
25
|
order?: "newestFirst" | "oldestFirst";
|
|
26
|
+
limit?: number;
|
|
21
27
|
}
|
|
22
28
|
export declare function readCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string): {
|
|
23
29
|
branchId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"branch-entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAgB,MAAM,+BAA+B,CAAC;AAEzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,6FAA6F;AAC7F,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,aAAa,GAAG,aAAa,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAUD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;cAQrF;AAED,wBAAgB,qBAAqB,CACpC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,iBAAiB,0BAyCxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAExE;AAED,wBAAgB,iBAAiB,CAChC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,QAKzB;AAqBD,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAmBjH;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;cAO/F;AAED,wBAAgB,2BAA2B,CAC1C,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,QAMlB","sourcesContent":["import { type Entry, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\n/** Derived root-to-tip branch cache membership. Canonical parent links remain in entries. */\nexport interface CachedBranch {\n\tbranchId: string;\n\tleafSeq: number;\n}\n\nexport interface CachedBranchEntryRow {\n\tsession_id: string;\n\tid: string;\n\tentry_seq: number;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: number;\n\tpayload: string;\n}\n\nexport interface CachedBranchQuery {\n\ttype?: Entry[\"type\"];\n\tcustomType?: string;\n\tstopAtType?: Entry[\"type\"];\n\tstopAtId?: string;\n\tcursor?: { afterSeq: number };\n\torder?: \"newestFirst\" | \"oldestFirst\";\n\tlimit?: number;\n}\n\ninterface BranchPathEntryRow {\n\tid: string;\n\tseq: number;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\tpayload: string;\n}\n\nexport function readCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tconst membership = sql`SELECT branch_id, entry_seq\n\t\tFROM branch_entries\n\t\tWHERE session_id = ${sessionId} AND entry_id = ${leafId}\n\t\tORDER BY branch_id\n\t\tLIMIT 1`.get<{ branch_id: string; entry_seq: number }>(db);\n\tif (!membership) return undefined;\n\treturn { branchId: membership.branch_id, leafSeq: membership.entry_seq };\n}\n\nexport function queryCachedBranchRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranch: CachedBranch,\n\tquery: CachedBranchQuery,\n) {\n\tconst oldestFirst = query.order === \"oldestFirst\";\n\tconst stopPredicates: ReturnType<typeof sql>[] = [];\n\tif (query.stopAtType !== undefined) stopPredicates.push(sql`stop.entry_type = ${query.stopAtType}`);\n\tif (query.stopAtId !== undefined) stopPredicates.push(sql`stop.entry_id = ${query.stopAtId}`);\n\n\tconst aggregate = oldestFirst ? sql`MIN` : sql`MAX`;\n\tconst boundaryComparison = oldestFirst ? sql`<=` : sql`>=`;\n\tconst cursorComparison = oldestFirst ? sql`>` : sql`<`;\n\tconst direction = oldestFirst ? sql`ASC` : sql`DESC`;\n\tconst boundary =\n\t\tstopPredicates.length === 0\n\t\t\t? sql``\n\t\t\t: sql`SELECT ${aggregate}(stop.entry_seq)\n\t\t\t\tFROM branch_entries AS stop\n\t\t\t\tWHERE stop.session_id = ${sessionId}\n\t\t\t\t\tAND stop.branch_id = ${branch.branchId}\n\t\t\t\t\tAND stop.entry_seq <= ${branch.leafSeq}\n\t\t\t\t\tAND (${joinSqlFragments(stopPredicates, \" OR \")})`;\n\n\tconst predicates = [\n\t\tsql`b.session_id = ${sessionId}`,\n\t\tsql`b.branch_id = ${branch.branchId}`,\n\t\tsql`b.entry_seq <= ${branch.leafSeq}`,\n\t];\n\tif (stopPredicates.length > 0) {\n\t\tpredicates.push(\n\t\t\tsql`b.entry_seq ${boundaryComparison} COALESCE((${boundary}), ${oldestFirst ? branch.leafSeq : 0})`,\n\t\t);\n\t}\n\tif (query.cursor !== undefined) predicates.push(sql`b.entry_seq ${cursorComparison} ${query.cursor.afterSeq}`);\n\tif (query.type !== undefined) predicates.push(sql`b.entry_type = ${query.type}`);\n\tif (query.customType !== undefined) predicates.push(sql`b.custom_type = ${query.customType}`);\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\n\treturn sql`SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload\n\t\tFROM branch_entries AS b\n\t\tJOIN entries AS e ON e.session_id = b.session_id AND e.id = b.entry_id\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY b.entry_seq ${direction}${limit}`.all<CachedBranchEntryRow>(db);\n}\n\nexport function deleteBranchEntries(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);\n}\n\nexport function insertBranchEntry(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tsql`INSERT INTO branch_entries\n\t\t\t(session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\t\tVALUES (${sessionId}, ${branchId}, ${entryId}, ${entrySeq}, ${entryType}, ${customType})`.run(db);\n}\n\nfunction customTypeFromPayload(row: BranchPathEntryRow): string | null {\n\tif (row.type !== \"custom\") return null;\n\ttry {\n\t\tconst payload = JSON.parse(row.payload) as unknown;\n\t\tif (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n\t\t\tthrow new Error(\"Payload is not an object\");\n\t\t}\n\t\tconst customType = (payload as { customType?: unknown }).customType;\n\t\tif (typeof customType !== \"string\") throw new Error(\"Invalid custom payload\");\n\t\treturn customType;\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_entry\",\n\t\t\t`Invalid SQLite session entry ${row.id}: failed to decode entry ${row.id}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function insertBranchEntriesForPath(db: SqliteDatabase, sessionId: string, branchId: string, leafId: string) {\n\tconst path: BranchPathEntryRow[] = [];\n\tconst seen = new Set<string>();\n\tlet entryId: string | null = leafId;\n\n\twhile (entryId !== null) {\n\t\tif (seen.has(entryId)) throw new SessionError(\"invalid_entry\", `Entry parent cycle at ${entryId}`);\n\t\tseen.add(entryId);\n\t\tconst row: BranchPathEntryRow | undefined = sql`SELECT id, seq, parent_id, type, payload\n\t\t\tFROM entries\n\t\t\tWHERE session_id = ${sessionId} AND id = ${entryId}`.get<BranchPathEntryRow>(db);\n\t\tif (!row) throw new SessionError(\"invalid_entry\", `Entry ${entryId} not found`);\n\t\tpath.push(row);\n\t\tentryId = row.parent_id;\n\t}\n\n\tfor (const row of path.reverse()) {\n\t\tinsertBranchEntry(db, sessionId, branchId, row.id, row.seq, row.type, customTypeFromPayload(row));\n\t}\n}\n\nexport function readBranchContainingEntry(db: SqliteDatabase, sessionId: string, entryId: string) {\n\tconst row = sql`SELECT b.branch_id, b.entry_seq\n\t\tFROM branch_entries AS b\n\t\tWHERE b.session_id = ${sessionId} AND b.entry_id = ${entryId}\n\t\tORDER BY b.branch_id\n\t\tLIMIT 1`.get<{ branch_id: string; entry_seq: number }>(db);\n\treturn row === undefined ? undefined : { branchId: row.branch_id, entrySeq: row.entry_seq };\n}\n\nexport function copyBranchEntriesThroughSeq(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\ttargetBranchId: string,\n\tsourceBranchId: string,\n\tthroughSeq: number,\n) {\n\tsql`INSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\tSELECT session_id, ${targetBranchId}, entry_id, entry_seq, entry_type, custom_type\n\t\tFROM branch_entries\n\t\tWHERE session_id = ${sessionId} AND branch_id = ${sourceBranchId} AND entry_seq <= ${throughSeq}`.run(db);\n}\n"]}
|
|
@@ -1,86 +1,112 @@
|
|
|
1
|
+
import { SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { joinSqlFragments, sql } from "../sql.js";
|
|
1
3
|
export function readCachedBranch(db, sessionId, leafId) {
|
|
2
|
-
const membership =
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
const membership = sql `SELECT branch_id, entry_seq
|
|
5
|
+
FROM branch_entries
|
|
6
|
+
WHERE session_id = ${sessionId} AND entry_id = ${leafId}
|
|
7
|
+
ORDER BY branch_id
|
|
8
|
+
LIMIT 1`.get(db);
|
|
5
9
|
if (!membership)
|
|
6
10
|
return undefined;
|
|
7
11
|
return { branchId: membership.branch_id, leafSeq: membership.entry_seq };
|
|
8
12
|
}
|
|
9
13
|
export function queryCachedBranchRows(db, sessionId, branch, query) {
|
|
10
14
|
const oldestFirst = query.order === "oldestFirst";
|
|
11
|
-
const boundaryParams = [sessionId, branch.branchId, branch.leafSeq];
|
|
12
15
|
const stopPredicates = [];
|
|
13
|
-
if (query.stopAtType !== undefined)
|
|
14
|
-
stopPredicates.push(
|
|
15
|
-
|
|
16
|
+
if (query.stopAtType !== undefined)
|
|
17
|
+
stopPredicates.push(sql `stop.entry_type = ${query.stopAtType}`);
|
|
18
|
+
if (query.stopAtId !== undefined)
|
|
19
|
+
stopPredicates.push(sql `stop.entry_id = ${query.stopAtId}`);
|
|
20
|
+
const aggregate = oldestFirst ? sql `MIN` : sql `MAX`;
|
|
21
|
+
const boundaryComparison = oldestFirst ? sql `<=` : sql `>=`;
|
|
22
|
+
const cursorComparison = oldestFirst ? sql `>` : sql `<`;
|
|
23
|
+
const direction = oldestFirst ? sql `ASC` : sql `DESC`;
|
|
24
|
+
const boundary = stopPredicates.length === 0
|
|
25
|
+
? sql ``
|
|
26
|
+
: sql `SELECT ${aggregate}(stop.entry_seq)
|
|
27
|
+
FROM branch_entries AS stop
|
|
28
|
+
WHERE stop.session_id = ${sessionId}
|
|
29
|
+
AND stop.branch_id = ${branch.branchId}
|
|
30
|
+
AND stop.entry_seq <= ${branch.leafSeq}
|
|
31
|
+
AND (${joinSqlFragments(stopPredicates, " OR ")})`;
|
|
32
|
+
const predicates = [
|
|
33
|
+
sql `b.session_id = ${sessionId}`,
|
|
34
|
+
sql `b.branch_id = ${branch.branchId}`,
|
|
35
|
+
sql `b.entry_seq <= ${branch.leafSeq}`,
|
|
36
|
+
];
|
|
37
|
+
if (stopPredicates.length > 0) {
|
|
38
|
+
predicates.push(sql `b.entry_seq ${boundaryComparison} COALESCE((${boundary}), ${oldestFirst ? branch.leafSeq : 0})`);
|
|
16
39
|
}
|
|
17
|
-
if (query.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
JOIN entries AS stop_entry
|
|
26
|
-
ON stop_entry.session_id = stop.session_id AND stop_entry.id = stop.entry_id
|
|
27
|
-
WHERE stop.session_id = ? AND stop.branch_id = ? AND stop.entry_seq <= ?
|
|
28
|
-
AND (${stopPredicates.join(" OR ")})
|
|
29
|
-
)`
|
|
30
|
-
: "";
|
|
31
|
-
const range = stopPredicates.length
|
|
32
|
-
? `AND b.entry_seq ${oldestFirst ? "<=" : ">="} COALESCE(
|
|
33
|
-
(SELECT entry_seq FROM boundary), ${oldestFirst ? branch.leafSeq : 0}
|
|
34
|
-
)`
|
|
35
|
-
: "";
|
|
36
|
-
const sql = `${boundary}
|
|
37
|
-
SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload
|
|
40
|
+
if (query.cursor !== undefined)
|
|
41
|
+
predicates.push(sql `b.entry_seq ${cursorComparison} ${query.cursor.afterSeq}`);
|
|
42
|
+
if (query.type !== undefined)
|
|
43
|
+
predicates.push(sql `b.entry_type = ${query.type}`);
|
|
44
|
+
if (query.customType !== undefined)
|
|
45
|
+
predicates.push(sql `b.custom_type = ${query.customType}`);
|
|
46
|
+
const limit = query.limit === undefined ? sql `` : sql ` LIMIT ${query.limit}`;
|
|
47
|
+
return sql `SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload
|
|
38
48
|
FROM branch_entries AS b
|
|
39
49
|
JOIN entries AS e ON e.session_id = b.session_id AND e.id = b.entry_id
|
|
40
|
-
WHERE
|
|
41
|
-
|
|
42
|
-
ORDER BY b.entry_seq ${oldestFirst ? "ASC" : "DESC"}`;
|
|
43
|
-
const params = [...(stopPredicates.length === 0 ? [] : boundaryParams), sessionId, branch.branchId, branch.leafSeq];
|
|
44
|
-
return db.prepare(sql).all(...params);
|
|
50
|
+
WHERE ${joinSqlFragments(predicates, " AND ")}
|
|
51
|
+
ORDER BY b.entry_seq ${direction}${limit}`.all(db);
|
|
45
52
|
}
|
|
46
53
|
export function deleteBranchEntries(db, sessionId) {
|
|
47
|
-
|
|
54
|
+
sql `DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);
|
|
48
55
|
}
|
|
49
56
|
export function insertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType) {
|
|
50
|
-
|
|
57
|
+
sql `INSERT INTO branch_entries
|
|
51
58
|
(session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)
|
|
52
|
-
VALUES (
|
|
59
|
+
VALUES (${sessionId}, ${branchId}, ${entryId}, ${entrySeq}, ${entryType}, ${customType})`.run(db);
|
|
60
|
+
}
|
|
61
|
+
function customTypeFromPayload(row) {
|
|
62
|
+
if (row.type !== "custom")
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const payload = JSON.parse(row.payload);
|
|
66
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
67
|
+
throw new Error("Payload is not an object");
|
|
68
|
+
}
|
|
69
|
+
const customType = payload.customType;
|
|
70
|
+
if (typeof customType !== "string")
|
|
71
|
+
throw new Error("Invalid custom payload");
|
|
72
|
+
return customType;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new SessionError("invalid_entry", `Invalid SQLite session entry ${row.id}: failed to decode entry ${row.id}`, error instanceof Error ? error : undefined);
|
|
76
|
+
}
|
|
53
77
|
}
|
|
54
78
|
export function insertBranchEntriesForPath(db, sessionId, branchId, leafId) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
const path = [];
|
|
80
|
+
const seen = new Set();
|
|
81
|
+
let entryId = leafId;
|
|
82
|
+
while (entryId !== null) {
|
|
83
|
+
if (seen.has(entryId))
|
|
84
|
+
throw new SessionError("invalid_entry", `Entry parent cycle at ${entryId}`);
|
|
85
|
+
seen.add(entryId);
|
|
86
|
+
const row = sql `SELECT id, seq, parent_id, type, payload
|
|
87
|
+
FROM entries
|
|
88
|
+
WHERE session_id = ${sessionId} AND id = ${entryId}`.get(db);
|
|
89
|
+
if (!row)
|
|
90
|
+
throw new SessionError("invalid_entry", `Entry ${entryId} not found`);
|
|
91
|
+
path.push(row);
|
|
92
|
+
entryId = row.parent_id;
|
|
93
|
+
}
|
|
94
|
+
for (const row of path.reverse()) {
|
|
95
|
+
insertBranchEntry(db, sessionId, branchId, row.id, row.seq, row.type, customTypeFromPayload(row));
|
|
96
|
+
}
|
|
69
97
|
}
|
|
70
98
|
export function readBranchContainingEntry(db, sessionId, entryId) {
|
|
71
|
-
const row =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
LIMIT 1`)
|
|
77
|
-
.get(sessionId, entryId);
|
|
99
|
+
const row = sql `SELECT b.branch_id, b.entry_seq
|
|
100
|
+
FROM branch_entries AS b
|
|
101
|
+
WHERE b.session_id = ${sessionId} AND b.entry_id = ${entryId}
|
|
102
|
+
ORDER BY b.branch_id
|
|
103
|
+
LIMIT 1`.get(db);
|
|
78
104
|
return row === undefined ? undefined : { branchId: row.branch_id, entrySeq: row.entry_seq };
|
|
79
105
|
}
|
|
80
106
|
export function copyBranchEntriesThroughSeq(db, sessionId, targetBranchId, sourceBranchId, throughSeq) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
sql `INSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)
|
|
108
|
+
SELECT session_id, ${targetBranchId}, entry_id, entry_seq, entry_type, custom_type
|
|
109
|
+
FROM branch_entries
|
|
110
|
+
WHERE session_id = ${sessionId} AND branch_id = ${sourceBranchId} AND entry_seq <= ${throughSeq}`.run(db);
|
|
85
111
|
}
|
|
86
112
|
//# sourceMappingURL=branch-entries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Entry } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\n/** Derived root-to-tip branch cache membership. Canonical parent links remain in entries. */\nexport interface CachedBranch {\n\tbranchId: string;\n\tleafSeq: number;\n}\n\nexport interface CachedBranchEntryRow {\n\tsession_id: string;\n\tid: string;\n\tentry_seq: number;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport interface CachedBranchQuery {\n\tstopAtType?: Entry[\"type\"];\n\tstopAtId?: string;\n\torder?: \"newestFirst\" | \"oldestFirst\";\n}\n\nexport function readCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tconst membership = db\n\t\t.prepare(\n\t\t\t\"SELECT branch_id, entry_seq FROM branch_entries WHERE session_id = ? AND entry_id = ? ORDER BY branch_id LIMIT 1\",\n\t\t)\n\t\t.get<{ branch_id: string; entry_seq: number }>(sessionId, leafId);\n\tif (!membership) return undefined;\n\treturn { branchId: membership.branch_id, leafSeq: membership.entry_seq };\n}\n\nexport function queryCachedBranchRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranch: CachedBranch,\n\tquery: CachedBranchQuery,\n) {\n\tconst oldestFirst = query.order === \"oldestFirst\";\n\tconst boundaryParams: unknown[] = [sessionId, branch.branchId, branch.leafSeq];\n\tconst stopPredicates: string[] = [];\n\tif (query.stopAtType !== undefined) {\n\t\tstopPredicates.push(\"stop_entry.type = ?\");\n\t\tboundaryParams.push(query.stopAtType);\n\t}\n\tif (query.stopAtId !== undefined) {\n\t\tstopPredicates.push(\"stop.entry_id = ?\");\n\t\tboundaryParams.push(query.stopAtId);\n\t}\n\n\tconst boundary = stopPredicates.length\n\t\t? `WITH boundary AS (\n\t\t\tSELECT ${oldestFirst ? \"MIN\" : \"MAX\"}(stop.entry_seq) AS entry_seq\n\t\t\tFROM branch_entries AS stop\n\t\t\tJOIN entries AS stop_entry\n\t\t\t\tON stop_entry.session_id = stop.session_id AND stop_entry.id = stop.entry_id\n\t\t\tWHERE stop.session_id = ? AND stop.branch_id = ? AND stop.entry_seq <= ?\n\t\t\t\tAND (${stopPredicates.join(\" OR \")})\n\t\t)`\n\t\t: \"\";\n\tconst range = stopPredicates.length\n\t\t? `AND b.entry_seq ${oldestFirst ? \"<=\" : \">=\"} COALESCE(\n\t\t\t(SELECT entry_seq FROM boundary), ${oldestFirst ? branch.leafSeq : 0}\n\t\t)`\n\t\t: \"\";\n\tconst sql = `${boundary}\n\t\tSELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload\n\t\tFROM branch_entries AS b\n\t\tJOIN entries AS e ON e.session_id = b.session_id AND e.id = b.entry_id\n\t\tWHERE b.session_id = ? AND b.branch_id = ? AND b.entry_seq <= ?\n\t\t\t${range}\n\t\tORDER BY b.entry_seq ${oldestFirst ? \"ASC\" : \"DESC\"}`;\n\n\tconst params = [...(stopPredicates.length === 0 ? [] : boundaryParams), sessionId, branch.branchId, branch.leafSeq];\n\treturn db.prepare(sql).all<CachedBranchEntryRow>(...params);\n}\n\nexport function deleteBranchEntries(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM branch_entries WHERE session_id = ?\").run(sessionId);\n}\n\nexport function insertBranchEntry(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tdb.prepare(\n\t\t`INSERT INTO branch_entries\n\t\t\t(session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\t\tVALUES (?, ?, ?, ?, ?, ?)`,\n\t).run(sessionId, branchId, entryId, entrySeq, entryType, customType);\n}\n\nexport function insertBranchEntriesForPath(db: SqliteDatabase, sessionId: string, branchId: string, leafId: string) {\n\tdb.prepare(\n\t\t`WITH RECURSIVE path(id, entry_seq, parent_id, type, custom_type) AS (\n\t\t\t\tSELECT id, seq, parent_id, type,\n\t\t\t\t\tCASE WHEN type = 'custom' THEN json_extract(payload, '$.customType') ELSE NULL END\n\t\t\t\tFROM entries\n\t\t\t\tWHERE session_id = ? AND id = ?\n\t\t\t\tUNION ALL\n\t\t\t\tSELECT parent.id, parent.seq, parent.parent_id, parent.type,\n\t\t\t\t\tCASE WHEN parent.type = 'custom' THEN json_extract(parent.payload, '$.customType') ELSE NULL END\n\t\t\t\tFROM entries AS parent\n\t\t\t\tJOIN path AS child ON child.parent_id = parent.id\n\t\t\t\tWHERE parent.session_id = ?\n\t\t\t)\n\t\t\tINSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\t\tSELECT ?, ?, id, entry_seq, type, custom_type FROM path`,\n\t).run(sessionId, leafId, sessionId, sessionId, branchId);\n}\n\nexport function readBranchContainingEntry(db: SqliteDatabase, sessionId: string, entryId: string) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT b.branch_id, b.entry_seq\n\t\t\tFROM branch_entries AS b\n\t\t\tWHERE b.session_id = ? AND b.entry_id = ?\n\t\t\tORDER BY b.branch_id\n\t\t\tLIMIT 1`,\n\t\t)\n\t\t.get<{ branch_id: string; entry_seq: number }>(sessionId, entryId);\n\treturn row === undefined ? undefined : { branchId: row.branch_id, entrySeq: row.entry_seq };\n}\n\nexport function copyBranchEntriesThroughSeq(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\ttargetBranchId: string,\n\tsourceBranchId: string,\n\tthroughSeq: number,\n) {\n\tdb.prepare(\n\t\t`INSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\t\tSELECT session_id, ?, entry_id, entry_seq, entry_type, custom_type\n\t\t\tFROM branch_entries\n\t\t\tWHERE session_id = ? AND branch_id = ? AND entry_seq <= ?`,\n\t).run(targetBranchId, sessionId, sourceBranchId, throughSeq);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"branch-entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Entry, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\n/** Derived root-to-tip branch cache membership. Canonical parent links remain in entries. */\nexport interface CachedBranch {\n\tbranchId: string;\n\tleafSeq: number;\n}\n\nexport interface CachedBranchEntryRow {\n\tsession_id: string;\n\tid: string;\n\tentry_seq: number;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: number;\n\tpayload: string;\n}\n\nexport interface CachedBranchQuery {\n\ttype?: Entry[\"type\"];\n\tcustomType?: string;\n\tstopAtType?: Entry[\"type\"];\n\tstopAtId?: string;\n\tcursor?: { afterSeq: number };\n\torder?: \"newestFirst\" | \"oldestFirst\";\n\tlimit?: number;\n}\n\ninterface BranchPathEntryRow {\n\tid: string;\n\tseq: number;\n\tparent_id: string | null;\n\ttype: Entry[\"type\"];\n\tpayload: string;\n}\n\nexport function readCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tconst membership = sql`SELECT branch_id, entry_seq\n\t\tFROM branch_entries\n\t\tWHERE session_id = ${sessionId} AND entry_id = ${leafId}\n\t\tORDER BY branch_id\n\t\tLIMIT 1`.get<{ branch_id: string; entry_seq: number }>(db);\n\tif (!membership) return undefined;\n\treturn { branchId: membership.branch_id, leafSeq: membership.entry_seq };\n}\n\nexport function queryCachedBranchRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranch: CachedBranch,\n\tquery: CachedBranchQuery,\n) {\n\tconst oldestFirst = query.order === \"oldestFirst\";\n\tconst stopPredicates: ReturnType<typeof sql>[] = [];\n\tif (query.stopAtType !== undefined) stopPredicates.push(sql`stop.entry_type = ${query.stopAtType}`);\n\tif (query.stopAtId !== undefined) stopPredicates.push(sql`stop.entry_id = ${query.stopAtId}`);\n\n\tconst aggregate = oldestFirst ? sql`MIN` : sql`MAX`;\n\tconst boundaryComparison = oldestFirst ? sql`<=` : sql`>=`;\n\tconst cursorComparison = oldestFirst ? sql`>` : sql`<`;\n\tconst direction = oldestFirst ? sql`ASC` : sql`DESC`;\n\tconst boundary =\n\t\tstopPredicates.length === 0\n\t\t\t? sql``\n\t\t\t: sql`SELECT ${aggregate}(stop.entry_seq)\n\t\t\t\tFROM branch_entries AS stop\n\t\t\t\tWHERE stop.session_id = ${sessionId}\n\t\t\t\t\tAND stop.branch_id = ${branch.branchId}\n\t\t\t\t\tAND stop.entry_seq <= ${branch.leafSeq}\n\t\t\t\t\tAND (${joinSqlFragments(stopPredicates, \" OR \")})`;\n\n\tconst predicates = [\n\t\tsql`b.session_id = ${sessionId}`,\n\t\tsql`b.branch_id = ${branch.branchId}`,\n\t\tsql`b.entry_seq <= ${branch.leafSeq}`,\n\t];\n\tif (stopPredicates.length > 0) {\n\t\tpredicates.push(\n\t\t\tsql`b.entry_seq ${boundaryComparison} COALESCE((${boundary}), ${oldestFirst ? branch.leafSeq : 0})`,\n\t\t);\n\t}\n\tif (query.cursor !== undefined) predicates.push(sql`b.entry_seq ${cursorComparison} ${query.cursor.afterSeq}`);\n\tif (query.type !== undefined) predicates.push(sql`b.entry_type = ${query.type}`);\n\tif (query.customType !== undefined) predicates.push(sql`b.custom_type = ${query.customType}`);\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\n\treturn sql`SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload\n\t\tFROM branch_entries AS b\n\t\tJOIN entries AS e ON e.session_id = b.session_id AND e.id = b.entry_id\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY b.entry_seq ${direction}${limit}`.all<CachedBranchEntryRow>(db);\n}\n\nexport function deleteBranchEntries(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);\n}\n\nexport function insertBranchEntry(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tsql`INSERT INTO branch_entries\n\t\t\t(session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\t\tVALUES (${sessionId}, ${branchId}, ${entryId}, ${entrySeq}, ${entryType}, ${customType})`.run(db);\n}\n\nfunction customTypeFromPayload(row: BranchPathEntryRow): string | null {\n\tif (row.type !== \"custom\") return null;\n\ttry {\n\t\tconst payload = JSON.parse(row.payload) as unknown;\n\t\tif (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n\t\t\tthrow new Error(\"Payload is not an object\");\n\t\t}\n\t\tconst customType = (payload as { customType?: unknown }).customType;\n\t\tif (typeof customType !== \"string\") throw new Error(\"Invalid custom payload\");\n\t\treturn customType;\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_entry\",\n\t\t\t`Invalid SQLite session entry ${row.id}: failed to decode entry ${row.id}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function insertBranchEntriesForPath(db: SqliteDatabase, sessionId: string, branchId: string, leafId: string) {\n\tconst path: BranchPathEntryRow[] = [];\n\tconst seen = new Set<string>();\n\tlet entryId: string | null = leafId;\n\n\twhile (entryId !== null) {\n\t\tif (seen.has(entryId)) throw new SessionError(\"invalid_entry\", `Entry parent cycle at ${entryId}`);\n\t\tseen.add(entryId);\n\t\tconst row: BranchPathEntryRow | undefined = sql`SELECT id, seq, parent_id, type, payload\n\t\t\tFROM entries\n\t\t\tWHERE session_id = ${sessionId} AND id = ${entryId}`.get<BranchPathEntryRow>(db);\n\t\tif (!row) throw new SessionError(\"invalid_entry\", `Entry ${entryId} not found`);\n\t\tpath.push(row);\n\t\tentryId = row.parent_id;\n\t}\n\n\tfor (const row of path.reverse()) {\n\t\tinsertBranchEntry(db, sessionId, branchId, row.id, row.seq, row.type, customTypeFromPayload(row));\n\t}\n}\n\nexport function readBranchContainingEntry(db: SqliteDatabase, sessionId: string, entryId: string) {\n\tconst row = sql`SELECT b.branch_id, b.entry_seq\n\t\tFROM branch_entries AS b\n\t\tWHERE b.session_id = ${sessionId} AND b.entry_id = ${entryId}\n\t\tORDER BY b.branch_id\n\t\tLIMIT 1`.get<{ branch_id: string; entry_seq: number }>(db);\n\treturn row === undefined ? undefined : { branchId: row.branch_id, entrySeq: row.entry_seq };\n}\n\nexport function copyBranchEntriesThroughSeq(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\ttargetBranchId: string,\n\tsourceBranchId: string,\n\tthroughSeq: number,\n) {\n\tsql`INSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\tSELECT session_id, ${targetBranchId}, entry_id, entry_seq, entry_type, custom_type\n\t\tFROM branch_entries\n\t\tWHERE session_id = ${sessionId} AND branch_id = ${sourceBranchId} AND entry_seq <= ${throughSeq}`.run(db);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-tips.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-tips.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch-tips.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-tips.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAIrE;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAKzF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAErG;AAED,wBAAgB,eAAe,CAC9B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,WAKhB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function readBranchTipIds(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT tip_id FROM branch_tips WHERE session_id = ${sessionId} ORDER BY tip_id`\n\t\t.all<{ tip_id: string }>(db)\n\t\t.map((row) => row.tip_id);\n}\n\nexport function readBranchTipBranchId(db: SqliteDatabase, sessionId: string, tipId: string) {\n\tconst tip = sql`SELECT branch_id FROM branch_tips WHERE session_id = ${sessionId} AND tip_id = ${tipId}`.get<{\n\t\tbranch_id: string;\n\t}>(db);\n\treturn tip?.branch_id;\n}\n\nexport function insertBranchTip(db: SqliteDatabase, sessionId: string, tipId: string, branchId: string) {\n\tsql`INSERT INTO branch_tips (session_id, tip_id, branch_id) VALUES (${sessionId}, ${tipId}, ${branchId})`.run(db);\n}\n\nexport function updateBranchTip(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\toldTipId: string,\n\tnewTipId: string,\n) {\n\tconst result = sql`UPDATE branch_tips SET tip_id = ${newTipId}\n\t\tWHERE session_id = ${sessionId} AND branch_id = ${branchId} AND tip_id = ${oldTipId}`.run(db);\n\treturn result.changes === 1;\n}\n\nexport function deleteBranchTips(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM branch_tips WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
+
import { sql } from "../sql.js";
|
|
1
2
|
export function readBranchTipIds(db, sessionId) {
|
|
2
|
-
return
|
|
3
|
-
.
|
|
4
|
-
.all(sessionId)
|
|
3
|
+
return sql `SELECT tip_id FROM branch_tips WHERE session_id = ${sessionId} ORDER BY tip_id`
|
|
4
|
+
.all(db)
|
|
5
5
|
.map((row) => row.tip_id);
|
|
6
6
|
}
|
|
7
7
|
export function readBranchTipBranchId(db, sessionId, tipId) {
|
|
8
|
-
const tip = db
|
|
9
|
-
.prepare("SELECT branch_id FROM branch_tips WHERE session_id = ? AND tip_id = ?")
|
|
10
|
-
.get(sessionId, tipId);
|
|
8
|
+
const tip = sql `SELECT branch_id FROM branch_tips WHERE session_id = ${sessionId} AND tip_id = ${tipId}`.get(db);
|
|
11
9
|
return tip?.branch_id;
|
|
12
10
|
}
|
|
13
11
|
export function insertBranchTip(db, sessionId, tipId, branchId) {
|
|
14
|
-
|
|
12
|
+
sql `INSERT INTO branch_tips (session_id, tip_id, branch_id) VALUES (${sessionId}, ${tipId}, ${branchId})`.run(db);
|
|
15
13
|
}
|
|
16
14
|
export function updateBranchTip(db, sessionId, branchId, oldTipId, newTipId) {
|
|
17
|
-
const result =
|
|
18
|
-
|
|
19
|
-
.run(newTipId, sessionId, branchId, oldTipId);
|
|
15
|
+
const result = sql `UPDATE branch_tips SET tip_id = ${newTipId}
|
|
16
|
+
WHERE session_id = ${sessionId} AND branch_id = ${branchId} AND tip_id = ${oldTipId}`.run(db);
|
|
20
17
|
return result.changes === 1;
|
|
21
18
|
}
|
|
22
19
|
export function deleteBranchTips(db, sessionId) {
|
|
23
|
-
|
|
20
|
+
sql `DELETE FROM branch_tips WHERE session_id = ${sessionId}`.run(db);
|
|
24
21
|
}
|
|
25
22
|
//# sourceMappingURL=branch-tips.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-tips.js","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-tips.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch-tips.js","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-tips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGhC,MAAM,UAAU,gBAAgB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACvE,OAAO,GAAG,CAAA,qDAAqD,SAAS,kBAAkB;SACxF,GAAG,CAAqB,EAAE,CAAC;SAC3B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAa,EAAE;IAC3F,MAAM,GAAG,GAAG,GAAG,CAAA,wDAAwD,SAAS,iBAAiB,KAAK,EAAE,CAAC,GAAG,CAEzG,EAAE,CAAC,CAAC;IACP,OAAO,GAAG,EAAE,SAAS,CAAC;AAAA,CACtB;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAa,EAAE,QAAgB,EAAE;IACvG,GAAG,CAAA,mEAAmE,SAAS,KAAK,KAAK,KAAK,QAAQ,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAClH;AAED,MAAM,UAAU,eAAe,CAC9B,EAAkB,EAClB,SAAiB,EACjB,QAAgB,EAChB,QAAgB,EAChB,QAAgB,EACf;IACD,MAAM,MAAM,GAAG,GAAG,CAAA,mCAAmC,QAAQ;uBACvC,SAAS,oBAAoB,QAAQ,iBAAiB,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/F,OAAO,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACvE,GAAG,CAAA,8CAA8C,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACrE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function readBranchTipIds(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT tip_id FROM branch_tips WHERE session_id = ${sessionId} ORDER BY tip_id`\n\t\t.all<{ tip_id: string }>(db)\n\t\t.map((row) => row.tip_id);\n}\n\nexport function readBranchTipBranchId(db: SqliteDatabase, sessionId: string, tipId: string) {\n\tconst tip = sql`SELECT branch_id FROM branch_tips WHERE session_id = ${sessionId} AND tip_id = ${tipId}`.get<{\n\t\tbranch_id: string;\n\t}>(db);\n\treturn tip?.branch_id;\n}\n\nexport function insertBranchTip(db: SqliteDatabase, sessionId: string, tipId: string, branchId: string) {\n\tsql`INSERT INTO branch_tips (session_id, tip_id, branch_id) VALUES (${sessionId}, ${tipId}, ${branchId})`.run(db);\n}\n\nexport function updateBranchTip(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\toldTipId: string,\n\tnewTipId: string,\n) {\n\tconst result = sql`UPDATE branch_tips SET tip_id = ${newTipId}\n\t\tWHERE session_id = ${sessionId} AND branch_id = ${branchId} AND tip_id = ${oldTipId}`.run(db);\n\treturn result.changes === 1;\n}\n\nexport function deleteBranchTips(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM branch_tips WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -6,7 +6,7 @@ export interface EntryRow {
|
|
|
6
6
|
id: string;
|
|
7
7
|
parent_id: string | null;
|
|
8
8
|
type: Entry["type"];
|
|
9
|
-
timestamp:
|
|
9
|
+
timestamp: number;
|
|
10
10
|
payload: string;
|
|
11
11
|
}
|
|
12
12
|
export interface NewEntryRow {
|
|
@@ -14,7 +14,7 @@ export interface NewEntryRow {
|
|
|
14
14
|
id: string;
|
|
15
15
|
parentId: string | null;
|
|
16
16
|
type: Entry["type"];
|
|
17
|
-
timestamp:
|
|
17
|
+
timestamp: number;
|
|
18
18
|
payload: string;
|
|
19
19
|
}
|
|
20
20
|
export declare function entryPayload(entry: Entry): Record<string, unknown>;
|
|
@@ -22,7 +22,12 @@ export declare function insertEntryRow(db: SqliteDatabase, sessionId: string, en
|
|
|
22
22
|
export declare function readEntryRow(db: SqliteDatabase, sessionId: string, entryId: string): EntryRow | undefined;
|
|
23
23
|
export declare function readEntryRows(db: SqliteDatabase, sessionId: string, options?: {
|
|
24
24
|
afterSeq?: number;
|
|
25
|
+
cursor?: {
|
|
26
|
+
afterSeq: number;
|
|
27
|
+
};
|
|
28
|
+
type?: Entry["type"];
|
|
25
29
|
order?: EntryOrder;
|
|
30
|
+
limit?: number;
|
|
26
31
|
}): EntryRow[];
|
|
27
32
|
export declare function idExistsInEntries(db: SqliteDatabase, sessionId: string, id: string): boolean;
|
|
28
33
|
export declare function deleteEntryRows(db: SqliteDatabase, sessionId: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,QAAQ;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGlE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,QAKvF;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,wBAIlF;AAED,wBAAgB,aAAa,CAC5B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACV,cAiBN;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,WAIlF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEpE","sourcesContent":["import type { Entry, EntryOrder } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\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: number;\n\tpayload: string;\n}\n\nexport interface NewEntryRow {\n\tseq: number;\n\tid: string;\n\tparentId: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: number;\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\nexport function insertEntryRow(db: SqliteDatabase, sessionId: string, entry: NewEntryRow) {\n\tsql`INSERT INTO entries (session_id, id, seq, parent_id, type, timestamp, payload)\n\t\tVALUES (${sessionId}, ${entry.id}, ${entry.seq}, ${entry.parentId}, ${entry.type}, ${entry.timestamp}, ${entry.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readEntryRow(db: SqliteDatabase, sessionId: string, entryId: string) {\n\treturn sql`SELECT session_id, seq, id, parent_id, type, timestamp, payload\n\t\tFROM entries\n\t\tWHERE session_id = ${sessionId} AND id = ${entryId}`.get<EntryRow>(db);\n}\n\nexport function readEntryRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: {\n\t\tafterSeq?: number;\n\t\tcursor?: { afterSeq: number };\n\t\ttype?: Entry[\"type\"];\n\t\torder?: EntryOrder;\n\t\tlimit?: number;\n\t} = {},\n) {\n\tconst oldestFirst = options.order === \"oldestFirst\";\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst cursor =\n\t\toptions.cursor === undefined\n\t\t\t? sql``\n\t\t\t: oldestFirst\n\t\t\t\t? sql` AND seq > ${options.cursor.afterSeq}`\n\t\t\t\t: sql` AND seq < ${options.cursor.afterSeq}`;\n\tconst type = options.type === undefined ? sql`` : sql` AND type = ${options.type}`;\n\tconst direction = oldestFirst ? sql`ASC` : sql`DESC`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, id, parent_id, type, timestamp, payload\n\t\tFROM entries\n\t\tWHERE session_id = ${sessionId}${after}${cursor}${type}\n\t\tORDER BY seq ${direction}${limit}`.all<EntryRow>(db);\n}\n\nexport function idExistsInEntries(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM entries WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteEntryRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -1,38 +1,37 @@
|
|
|
1
|
+
import { sql } from "../sql.js";
|
|
1
2
|
export function entryPayload(entry) {
|
|
2
3
|
const { type: _type, id: _id, seq: _seq, parentId: _parentId, timestamp: _timestamp, ...payload } = entry;
|
|
3
4
|
return payload;
|
|
4
5
|
}
|
|
5
|
-
function orderedSql(order) {
|
|
6
|
-
return order === "oldestFirst" ? "ASC" : "DESC";
|
|
7
|
-
}
|
|
8
6
|
export function insertEntryRow(db, sessionId, entry) {
|
|
9
|
-
|
|
7
|
+
sql `INSERT INTO entries (session_id, id, seq, parent_id, type, timestamp, payload)
|
|
8
|
+
VALUES (${sessionId}, ${entry.id}, ${entry.seq}, ${entry.parentId}, ${entry.type}, ${entry.timestamp}, ${entry.payload})`.run(db);
|
|
10
9
|
}
|
|
11
10
|
export function readEntryRow(db, sessionId, entryId) {
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
return sql `SELECT session_id, seq, id, parent_id, type, timestamp, payload
|
|
12
|
+
FROM entries
|
|
13
|
+
WHERE session_id = ${sessionId} AND id = ${entryId}`.get(db);
|
|
15
14
|
}
|
|
16
15
|
export function readEntryRows(db, sessionId, options = {}) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
const oldestFirst = options.order === "oldestFirst";
|
|
17
|
+
const after = options.afterSeq === undefined ? sql `` : sql ` AND seq > ${options.afterSeq}`;
|
|
18
|
+
const cursor = options.cursor === undefined
|
|
19
|
+
? sql ``
|
|
20
|
+
: oldestFirst
|
|
21
|
+
? sql ` AND seq > ${options.cursor.afterSeq}`
|
|
22
|
+
: sql ` AND seq < ${options.cursor.afterSeq}`;
|
|
23
|
+
const type = options.type === undefined ? sql `` : sql ` AND type = ${options.type}`;
|
|
24
|
+
const direction = oldestFirst ? sql `ASC` : sql `DESC`;
|
|
25
|
+
const limit = options.limit === undefined ? sql `` : sql ` LIMIT ${options.limit}`;
|
|
26
|
+
return sql `SELECT session_id, seq, id, parent_id, type, timestamp, payload
|
|
27
|
+
FROM entries
|
|
28
|
+
WHERE session_id = ${sessionId}${after}${cursor}${type}
|
|
29
|
+
ORDER BY seq ${direction}${limit}`.all(db);
|
|
29
30
|
}
|
|
30
31
|
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);
|
|
32
|
+
return !!sql `SELECT 1 AS found FROM entries WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get(db);
|
|
34
33
|
}
|
|
35
34
|
export function deleteEntryRows(db, sessionId) {
|
|
36
|
-
|
|
35
|
+
sql `DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);
|
|
37
36
|
}
|
|
38
37
|
//# sourceMappingURL=entries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/entries.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/entries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAsBhC,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,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAkB,EAAE;IACzF,GAAG,CAAA;YACQ,SAAS,KAAK,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,GAAG,CAAC,GAAG,CAC7H,EAAE,CACF,CAAC;AAAA,CACF;AAED,MAAM,UAAU,YAAY,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAE;IACpF,OAAO,GAAG,CAAA;;uBAEY,SAAS,aAAa,OAAO,EAAE,CAAC,GAAG,CAAW,EAAE,CAAC,CAAC;AAAA,CACxE;AAED,MAAM,UAAU,aAAa,CAC5B,EAAkB,EAClB,SAAiB,EACjB,OAAO,GAMH,EAAE,EACL;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,KAAK,aAAa,CAAC;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAA,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,cAAc,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC3F,MAAM,MAAM,GACX,OAAO,CAAC,MAAM,KAAK,SAAS;QAC3B,CAAC,CAAC,GAAG,CAAA,EAAE;QACP,CAAC,CAAC,WAAW;YACZ,CAAC,CAAC,GAAG,CAAA,cAAc,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE;YAC5C,CAAC,CAAC,GAAG,CAAA,cAAc,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAA,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,eAAe,OAAO,CAAC,IAAI,EAAE,CAAC;IACnF,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAA,KAAK,CAAC,CAAC,CAAC,GAAG,CAAA,MAAM,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAA,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,UAAU,OAAO,CAAC,KAAK,EAAE,CAAC;IACjF,OAAO,GAAG,CAAA;;uBAEY,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI;iBACvC,SAAS,GAAG,KAAK,EAAE,CAAC,GAAG,CAAW,EAAE,CAAC,CAAC;AAAA,CACtD;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE,EAAU,EAAE;IACpF,OAAO,CAAC,CAAC,GAAG,CAAA,qDAAqD,SAAS,aAAa,EAAE,UAAU,CAAC,GAAG,CAEpG,EAAE,CAAC,CAAC;AAAA,CACP;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACtE,GAAG,CAAA,0CAA0C,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACjE","sourcesContent":["import type { Entry, EntryOrder } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\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: number;\n\tpayload: string;\n}\n\nexport interface NewEntryRow {\n\tseq: number;\n\tid: string;\n\tparentId: string | null;\n\ttype: Entry[\"type\"];\n\ttimestamp: number;\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\nexport function insertEntryRow(db: SqliteDatabase, sessionId: string, entry: NewEntryRow) {\n\tsql`INSERT INTO entries (session_id, id, seq, parent_id, type, timestamp, payload)\n\t\tVALUES (${sessionId}, ${entry.id}, ${entry.seq}, ${entry.parentId}, ${entry.type}, ${entry.timestamp}, ${entry.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readEntryRow(db: SqliteDatabase, sessionId: string, entryId: string) {\n\treturn sql`SELECT session_id, seq, id, parent_id, type, timestamp, payload\n\t\tFROM entries\n\t\tWHERE session_id = ${sessionId} AND id = ${entryId}`.get<EntryRow>(db);\n}\n\nexport function readEntryRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: {\n\t\tafterSeq?: number;\n\t\tcursor?: { afterSeq: number };\n\t\ttype?: Entry[\"type\"];\n\t\torder?: EntryOrder;\n\t\tlimit?: number;\n\t} = {},\n) {\n\tconst oldestFirst = options.order === \"oldestFirst\";\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst cursor =\n\t\toptions.cursor === undefined\n\t\t\t? sql``\n\t\t\t: oldestFirst\n\t\t\t\t? sql` AND seq > ${options.cursor.afterSeq}`\n\t\t\t\t: sql` AND seq < ${options.cursor.afterSeq}`;\n\tconst type = options.type === undefined ? sql`` : sql` AND type = ${options.type}`;\n\tconst direction = oldestFirst ? sql`ASC` : sql`DESC`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, id, parent_id, type, timestamp, payload\n\t\tFROM entries\n\t\tWHERE session_id = ${sessionId}${after}${cursor}${type}\n\t\tORDER BY seq ${direction}${limit}`.all<EntryRow>(db);\n}\n\nexport function idExistsInEntries(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM entries WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteEntryRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -14,6 +14,7 @@ export declare function readLatestLabelFacts(db: SqliteDatabase, sessionId: stri
|
|
|
14
14
|
}[];
|
|
15
15
|
export declare function readFactRows(db: SqliteDatabase, sessionId: string, options?: {
|
|
16
16
|
afterSeq?: number;
|
|
17
|
+
limit?: number;
|
|
17
18
|
}): FactRow[];
|
|
18
19
|
export declare function deleteFactRows(db: SqliteDatabase, sessionId: string): void;
|
|
19
20
|
//# sourceMappingURL=facts.d.ts.map
|