@earendil-works/pi-session-backend-sqlite-node 0.84.1 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +10 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/sqlite/migrations/001_initial.sql +4 -8
  7. package/dist/sqlite/repo.d.ts.map +1 -1
  8. package/dist/sqlite/repo.js +16 -21
  9. package/dist/sqlite/repo.js.map +1 -1
  10. package/dist/sqlite/search-backend.d.ts +7 -2
  11. package/dist/sqlite/search-backend.d.ts.map +1 -1
  12. package/dist/sqlite/search-backend.js +71 -34
  13. package/dist/sqlite/search-backend.js.map +1 -1
  14. package/dist/sqlite/sql.d.ts +1 -0
  15. package/dist/sqlite/sql.d.ts.map +1 -1
  16. package/dist/sqlite/sql.js +3 -0
  17. package/dist/sqlite/sql.js.map +1 -1
  18. package/dist/sqlite/storage/branch-entries.d.ts +2 -2
  19. package/dist/sqlite/storage/branch-entries.d.ts.map +1 -1
  20. package/dist/sqlite/storage/branch-entries.js +39 -22
  21. package/dist/sqlite/storage/branch-entries.js.map +1 -1
  22. package/dist/sqlite/storage/entries.d.ts +6 -2
  23. package/dist/sqlite/storage/entries.d.ts.map +1 -1
  24. package/dist/sqlite/storage/entries.js +9 -2
  25. package/dist/sqlite/storage/entries.js.map +1 -1
  26. package/dist/sqlite/storage/facts.d.ts.map +1 -1
  27. package/dist/sqlite/storage/facts.js +13 -11
  28. package/dist/sqlite/storage/facts.js.map +1 -1
  29. package/dist/sqlite/storage/records.d.ts +2 -2
  30. package/dist/sqlite/storage/records.d.ts.map +1 -1
  31. package/dist/sqlite/storage/records.js.map +1 -1
  32. package/dist/sqlite/storage/sessions.d.ts +2 -2
  33. package/dist/sqlite/storage/sessions.d.ts.map +1 -1
  34. package/dist/sqlite/storage/sessions.js +3 -4
  35. package/dist/sqlite/storage/sessions.js.map +1 -1
  36. package/dist/sqlite/types.d.ts +1 -0
  37. package/dist/sqlite/types.d.ts.map +1 -1
  38. package/dist/sqlite/types.js.map +1 -1
  39. package/package.json +3 -3
@@ -9,6 +9,7 @@ export declare class SqlQuery {
9
9
  run(db: SqliteDatabase): SqliteRunResult;
10
10
  get<TRow extends object>(db: SqliteDatabase): TRow | undefined;
11
11
  all<TRow extends object>(db: SqliteDatabase): TRow[];
12
+ iterate<TRow extends object>(db: SqliteDatabase): Iterable<TRow>;
12
13
  }
13
14
  /** Builds a parameterized query. Nested queries are inlined; other interpolations become `?` parameters. */
14
15
  export declare function sql(strings: TemplateStringsArray, ...values: SqlTemplateValue[]): SqlQuery;
@@ -1 +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;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\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
+ {"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"]}
@@ -20,6 +20,9 @@ export class SqlQuery {
20
20
  all(db) {
21
21
  return db.prepare(this.queryText).all(...this.params);
22
22
  }
23
+ iterate(db) {
24
+ return db.prepare(this.queryText).iterate(...this.params);
25
+ }
23
26
  }
24
27
  /** Builds a parameterized query. Nested queries are inlined; other interpolations become `?` parameters. */
25
28
  export function sql(strings, ...values) {
@@ -1 +1 @@
1
- {"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sqlite/sql.ts"],"names":[],"mappings":"AAIA,4DAA4D;AAC5D,MAAM,OAAO,QAAQ;IACX,SAAS,CAAS;IAClB,MAAM,CAAqB;IAEpC,YAAY,SAAiB,EAAE,MAAM,GAAuB,EAAE,EAAE;QAC/D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAAA,CACrB;IAED,IAAI,CAAC,EAAkB,EAAQ;QAC9B,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QAC9F,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAAA,CACxB;IAED,GAAG,CAAC,EAAkB,EAAmB;QACxC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAAA,CACtD;IAED,GAAG,CAAsB,EAAkB,EAAoB;QAC9D,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAAA,CAC5D;IAED,GAAG,CAAsB,EAAkB,EAAU;QACpD,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAAA,CAC5D;CACD;AAED,4GAA4G;AAC5G,MAAM,UAAU,GAAG,CAAC,OAA6B,EAAE,GAAG,MAA0B,EAAY;IAC3F,IAAI,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC/B,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACP,SAAS,IAAI,GAAG,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAAA,CACvC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,SAA8B,EAAE,SAAiB,EAAY;IAC7F,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACvD,IAAI,KAAK,GAAG,CAAC;YAAE,SAAS,IAAI,SAAS,CAAC;QACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAE,CAAC;QACnC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAAA,CACvC","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\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
+ {"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 { Entry } from "@earendil-works/pi-agent-core";
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,7 +11,7 @@ export interface CachedBranchEntryRow {
11
11
  entry_seq: number;
12
12
  parent_id: string | null;
13
13
  type: Entry["type"];
14
- timestamp: string;
14
+ timestamp: number;
15
15
  payload: string;
16
16
  }
17
17
  export interface CachedBranchQuery {
@@ -1 +1 @@
1
- {"version":3,"file":"branch-entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAE3D,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;AAED,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,0BA4CxB;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;AAED,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAejH;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 } 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: string;\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\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`WITH boundary AS (\n\t\t\t\tSELECT ${aggregate}(stop.entry_seq) AS 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\t\t\t)`;\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(sql`b.entry_seq ${boundaryComparison} COALESCE(\n\t\t\t(SELECT entry_seq FROM 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`${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 ${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\nexport function insertBranchEntriesForPath(db: SqliteDatabase, sessionId: string, branchId: string, leafId: string) {\n\tsql`WITH RECURSIVE path(id, entry_seq, parent_id, type, custom_type) AS (\n\t\t\tSELECT id, seq, parent_id, type,\n\t\t\t\tCASE WHEN type = 'custom' THEN json_extract(payload, '$.customType') ELSE NULL END\n\t\t\tFROM entries\n\t\t\tWHERE session_id = ${sessionId} AND id = ${leafId}\n\t\t\tUNION ALL\n\t\t\tSELECT parent.id, parent.seq, parent.parent_id, parent.type,\n\t\t\t\tCASE WHEN parent.type = 'custom' THEN json_extract(parent.payload, '$.customType') ELSE NULL END\n\t\t\tFROM entries AS parent\n\t\t\tJOIN path AS child ON child.parent_id = parent.id\n\t\t\tWHERE parent.session_id = ${sessionId}\n\t\t)\n\t\tINSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\tSELECT ${sessionId}, ${branchId}, id, entry_seq, type, custom_type FROM path`.run(db);\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
+ {"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,3 +1,4 @@
1
+ import { SessionError } from "@earendil-works/pi-agent-core";
1
2
  import { joinSqlFragments, sql } from "../sql.js";
2
3
  export function readCachedBranch(db, sessionId, leafId) {
3
4
  const membership = sql `SELECT branch_id, entry_seq
@@ -22,23 +23,19 @@ export function queryCachedBranchRows(db, sessionId, branch, query) {
22
23
  const direction = oldestFirst ? sql `ASC` : sql `DESC`;
23
24
  const boundary = stopPredicates.length === 0
24
25
  ? sql ``
25
- : sql `WITH boundary AS (
26
- SELECT ${aggregate}(stop.entry_seq) AS entry_seq
26
+ : sql `SELECT ${aggregate}(stop.entry_seq)
27
27
  FROM branch_entries AS stop
28
28
  WHERE stop.session_id = ${sessionId}
29
29
  AND stop.branch_id = ${branch.branchId}
30
30
  AND stop.entry_seq <= ${branch.leafSeq}
31
- AND (${joinSqlFragments(stopPredicates, " OR ")})
32
- )`;
31
+ AND (${joinSqlFragments(stopPredicates, " OR ")})`;
33
32
  const predicates = [
34
33
  sql `b.session_id = ${sessionId}`,
35
34
  sql `b.branch_id = ${branch.branchId}`,
36
35
  sql `b.entry_seq <= ${branch.leafSeq}`,
37
36
  ];
38
37
  if (stopPredicates.length > 0) {
39
- predicates.push(sql `b.entry_seq ${boundaryComparison} COALESCE(
40
- (SELECT entry_seq FROM boundary), ${oldestFirst ? branch.leafSeq : 0}
41
- )`);
38
+ predicates.push(sql `b.entry_seq ${boundaryComparison} COALESCE((${boundary}), ${oldestFirst ? branch.leafSeq : 0})`);
42
39
  }
43
40
  if (query.cursor !== undefined)
44
41
  predicates.push(sql `b.entry_seq ${cursorComparison} ${query.cursor.afterSeq}`);
@@ -47,8 +44,7 @@ export function queryCachedBranchRows(db, sessionId, branch, query) {
47
44
  if (query.customType !== undefined)
48
45
  predicates.push(sql `b.custom_type = ${query.customType}`);
49
46
  const limit = query.limit === undefined ? sql `` : sql ` LIMIT ${query.limit}`;
50
- return sql `${boundary}
51
- SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload
47
+ return sql `SELECT e.session_id, e.id, e.seq AS entry_seq, e.parent_id, e.type, e.timestamp, e.payload
52
48
  FROM branch_entries AS b
53
49
  JOIN entries AS e ON e.session_id = b.session_id AND e.id = b.entry_id
54
50
  WHERE ${joinSqlFragments(predicates, " AND ")}
@@ -62,21 +58,42 @@ export function insertBranchEntry(db, sessionId, branchId, entryId, entrySeq, en
62
58
  (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)
63
59
  VALUES (${sessionId}, ${branchId}, ${entryId}, ${entrySeq}, ${entryType}, ${customType})`.run(db);
64
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
+ }
77
+ }
65
78
  export function insertBranchEntriesForPath(db, sessionId, branchId, leafId) {
66
- sql `WITH RECURSIVE path(id, entry_seq, parent_id, type, custom_type) AS (
67
- SELECT id, seq, parent_id, type,
68
- CASE WHEN type = 'custom' THEN json_extract(payload, '$.customType') ELSE NULL END
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
69
87
  FROM entries
70
- WHERE session_id = ${sessionId} AND id = ${leafId}
71
- UNION ALL
72
- SELECT parent.id, parent.seq, parent.parent_id, parent.type,
73
- CASE WHEN parent.type = 'custom' THEN json_extract(parent.payload, '$.customType') ELSE NULL END
74
- FROM entries AS parent
75
- JOIN path AS child ON child.parent_id = parent.id
76
- WHERE parent.session_id = ${sessionId}
77
- )
78
- INSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)
79
- SELECT ${sessionId}, ${branchId}, id, entry_seq, type, custom_type FROM path`.run(db);
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
+ }
80
97
  }
81
98
  export function readBranchContainingEntry(db, sessionId, entryId) {
82
99
  const row = sql `SELECT b.branch_id, b.entry_seq
@@ -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 { 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: string;\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\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`WITH boundary AS (\n\t\t\t\tSELECT ${aggregate}(stop.entry_seq) AS 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\t\t\t)`;\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(sql`b.entry_seq ${boundaryComparison} COALESCE(\n\t\t\t(SELECT entry_seq FROM 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`${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 ${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\nexport function insertBranchEntriesForPath(db: SqliteDatabase, sessionId: string, branchId: string, leafId: string) {\n\tsql`WITH RECURSIVE path(id, entry_seq, parent_id, type, custom_type) AS (\n\t\t\tSELECT id, seq, parent_id, type,\n\t\t\t\tCASE WHEN type = 'custom' THEN json_extract(payload, '$.customType') ELSE NULL END\n\t\t\tFROM entries\n\t\t\tWHERE session_id = ${sessionId} AND id = ${leafId}\n\t\t\tUNION ALL\n\t\t\tSELECT parent.id, parent.seq, parent.parent_id, parent.type,\n\t\t\t\tCASE WHEN parent.type = 'custom' THEN json_extract(parent.payload, '$.customType') ELSE NULL END\n\t\t\tFROM entries AS parent\n\t\t\tJOIN path AS child ON child.parent_id = parent.id\n\t\t\tWHERE parent.session_id = ${sessionId}\n\t\t)\n\t\tINSERT INTO branch_entries (session_id, branch_id, entry_id, entry_seq, entry_type, custom_type)\n\t\tSELECT ${sessionId}, ${branchId}, id, entry_seq, type, custom_type FROM path`.run(db);\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
+ {"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"]}
@@ -6,7 +6,7 @@ export interface EntryRow {
6
6
  id: string;
7
7
  parent_id: string | null;
8
8
  type: Entry["type"];
9
- timestamp: string;
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: string;
17
+ timestamp: number;
18
18
  payload: string;
19
19
  }
20
20
  export declare function entryPayload(entry: Entry): Record<string, unknown>;
@@ -22,6 +22,10 @@ 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;
26
30
  limit?: number;
27
31
  }): EntryRow[];
@@ -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;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;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,cASvE;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: 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\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: { afterSeq?: number; order?: EntryOrder; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst direction = options.order === \"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}\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
+ {"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"]}
@@ -13,12 +13,19 @@ export function readEntryRow(db, sessionId, entryId) {
13
13
  WHERE session_id = ${sessionId} AND id = ${entryId}`.get(db);
14
14
  }
15
15
  export function readEntryRows(db, sessionId, options = {}) {
16
+ const oldestFirst = options.order === "oldestFirst";
16
17
  const after = options.afterSeq === undefined ? sql `` : sql ` AND seq > ${options.afterSeq}`;
17
- const direction = options.order === "oldestFirst" ? sql `ASC` : sql `DESC`;
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`;
18
25
  const limit = options.limit === undefined ? sql `` : sql ` LIMIT ${options.limit}`;
19
26
  return sql `SELECT session_id, seq, id, parent_id, type, timestamp, payload
20
27
  FROM entries
21
- WHERE session_id = ${sessionId}${after}
28
+ WHERE session_id = ${sessionId}${after}${cursor}${type}
22
29
  ORDER BY seq ${direction}${limit}`.all(db);
23
30
  }
24
31
  export function idExistsInEntries(db, sessionId, id) {
@@ -1 +1 @@
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,GAA8D,EAAE,EACtE;IACD,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,SAAS,GAAG,OAAO,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAA,KAAK,CAAC,CAAC,CAAC,GAAG,CAAA,MAAM,CAAC;IACzE,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;iBACvB,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: 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\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: { afterSeq?: number; order?: EntryOrder; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst direction = options.order === \"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}\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
+ {"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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AACA,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,QAKpB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,uBAMrG;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM;;;IAYzE;AAED,wBAAgB,YAAY,CAC3B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,aAQnD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEnE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport 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\tsql`INSERT INTO facts (session_id, seq, kind, key, value) VALUES (${sessionId}, ${seq}, ${kind}, ${key}, ${value})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE session_id = ${sessionId} AND kind = ${kind} AND key IS ${key}\n\t\tORDER BY seq DESC\n\t\tLIMIT 1`.get<FactRow>(db);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn sql`WITH latest AS (\n\t\t\tSELECT key, MAX(seq) AS seq\n\t\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\t\tWHERE session_id = ${sessionId} AND kind = 'label'\n\t\t\tGROUP BY key\n\t\t)\n\t\tSELECT latest.key, f.value\n\t\tFROM latest\n\t\tJOIN facts AS f ON f.session_id = ${sessionId} AND f.seq = latest.seq\n\t\tWHERE f.value IS NOT NULL\n\t\tORDER BY latest.key`.all<{ key: string; value: string }>(db);\n}\n\nexport function readFactRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: { afterSeq?: number; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts\n\t\tWHERE session_id = ${sessionId}${after}\n\t\tORDER BY seq${limit}`.all<FactRow>(db);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM facts WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
1
+ {"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AACA,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,QAKpB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,uBAMrG;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM;;;IAczE;AAED,wBAAgB,YAAY,CAC3B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,aAQnD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEnE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport 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\tsql`INSERT INTO facts (session_id, seq, kind, key, value) VALUES (${sessionId}, ${seq}, ${kind}, ${key}, ${value})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE session_id = ${sessionId} AND kind = ${kind} AND key IS ${key}\n\t\tORDER BY seq DESC\n\t\tLIMIT 1`.get<FactRow>(db);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT f.key, f.value\n\t\tFROM facts AS f INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE f.session_id = ${sessionId}\n\t\t\tAND f.kind = 'label'\n\t\t\tAND f.value IS NOT NULL\n\t\t\tAND f.seq = (\n\t\t\t\tSELECT MAX(candidate.seq)\n\t\t\t\tFROM facts AS candidate INDEXED BY idx_facts_session_kind_key_seq\n\t\t\t\tWHERE candidate.session_id = f.session_id\n\t\t\t\t\tAND candidate.kind = f.kind\n\t\t\t\t\tAND candidate.key IS f.key\n\t\t\t)\n\t\tORDER BY f.key`.all<{ key: string; value: string }>(db);\n}\n\nexport function readFactRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: { afterSeq?: number; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts\n\t\tWHERE session_id = ${sessionId}${after}\n\t\tORDER BY seq${limit}`.all<FactRow>(db);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM facts WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -10,17 +10,19 @@ export function readLatestFact(db, sessionId, kind, key) {
10
10
  LIMIT 1`.get(db);
11
11
  }
12
12
  export function readLatestLabelFacts(db, sessionId) {
13
- return sql `WITH latest AS (
14
- SELECT key, MAX(seq) AS seq
15
- FROM facts INDEXED BY idx_facts_session_kind_key_seq
16
- WHERE session_id = ${sessionId} AND kind = 'label'
17
- GROUP BY key
18
- )
19
- SELECT latest.key, f.value
20
- FROM latest
21
- JOIN facts AS f ON f.session_id = ${sessionId} AND f.seq = latest.seq
22
- WHERE f.value IS NOT NULL
23
- ORDER BY latest.key`.all(db);
13
+ return sql `SELECT f.key, f.value
14
+ FROM facts AS f INDEXED BY idx_facts_session_kind_key_seq
15
+ WHERE f.session_id = ${sessionId}
16
+ AND f.kind = 'label'
17
+ AND f.value IS NOT NULL
18
+ AND f.seq = (
19
+ SELECT MAX(candidate.seq)
20
+ FROM facts AS candidate INDEXED BY idx_facts_session_kind_key_seq
21
+ WHERE candidate.session_id = f.session_id
22
+ AND candidate.kind = f.kind
23
+ AND candidate.key IS f.key
24
+ )
25
+ ORDER BY f.key`.all(db);
24
26
  }
25
27
  export function readFactRows(db, sessionId, options = {}) {
26
28
  const after = options.afterSeq === undefined ? sql `` : sql ` AND seq > ${options.afterSeq}`;
@@ -1 +1 @@
1
- {"version":3,"file":"facts.js","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAWhC,MAAM,UAAU,UAAU,CACzB,EAAkB,EAClB,SAAiB,EACjB,GAAW,EACX,IAAY,EACZ,GAAkB,EAClB,KAAoB,EACnB;IACD,GAAG,CAAA,iEAAiE,SAAS,KAAK,GAAG,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,GAAG,CAAC,GAAG,CACtH,EAAE,CACF,CAAC;AAAA,CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,IAAY,EAAE,GAAkB,EAAE;IACvG,OAAO,GAAG,CAAA;;uBAEY,SAAS,eAAe,IAAI,eAAe,GAAG;;UAE3D,CAAC,GAAG,CAAU,EAAE,CAAC,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IAC3E,OAAO,GAAG,CAAA;;;wBAGa,SAAS;;;;;sCAKK,SAAS;;sBAEzB,CAAC,GAAG,CAAiC,EAAE,CAAC,CAAC;AAAA,CAC9D;AAED,MAAM,UAAU,YAAY,CAC3B,EAAkB,EAClB,SAAiB,EACjB,OAAO,GAA0C,EAAE,EAClD;IACD,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,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;gBACxB,KAAK,EAAE,CAAC,GAAG,CAAU,EAAE,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,GAAG,CAAA,wCAAwC,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAC/D","sourcesContent":["import { sql } from \"../sql.ts\";\nimport 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\tsql`INSERT INTO facts (session_id, seq, kind, key, value) VALUES (${sessionId}, ${seq}, ${kind}, ${key}, ${value})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE session_id = ${sessionId} AND kind = ${kind} AND key IS ${key}\n\t\tORDER BY seq DESC\n\t\tLIMIT 1`.get<FactRow>(db);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn sql`WITH latest AS (\n\t\t\tSELECT key, MAX(seq) AS seq\n\t\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\t\tWHERE session_id = ${sessionId} AND kind = 'label'\n\t\t\tGROUP BY key\n\t\t)\n\t\tSELECT latest.key, f.value\n\t\tFROM latest\n\t\tJOIN facts AS f ON f.session_id = ${sessionId} AND f.seq = latest.seq\n\t\tWHERE f.value IS NOT NULL\n\t\tORDER BY latest.key`.all<{ key: string; value: string }>(db);\n}\n\nexport function readFactRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: { afterSeq?: number; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts\n\t\tWHERE session_id = ${sessionId}${after}\n\t\tORDER BY seq${limit}`.all<FactRow>(db);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM facts WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
1
+ {"version":3,"file":"facts.js","sourceRoot":"","sources":["../../../src/sqlite/storage/facts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAWhC,MAAM,UAAU,UAAU,CACzB,EAAkB,EAClB,SAAiB,EACjB,GAAW,EACX,IAAY,EACZ,GAAkB,EAClB,KAAoB,EACnB;IACD,GAAG,CAAA,iEAAiE,SAAS,KAAK,GAAG,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,GAAG,CAAC,GAAG,CACtH,EAAE,CACF,CAAC;AAAA,CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,IAAY,EAAE,GAAkB,EAAE;IACvG,OAAO,GAAG,CAAA;;uBAEY,SAAS,eAAe,IAAI,eAAe,GAAG;;UAE3D,CAAC,GAAG,CAAU,EAAE,CAAC,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IAC3E,OAAO,GAAG,CAAA;;yBAEc,SAAS;;;;;;;;;;iBAUjB,CAAC,GAAG,CAAiC,EAAE,CAAC,CAAC;AAAA,CACzD;AAED,MAAM,UAAU,YAAY,CAC3B,EAAkB,EAClB,SAAiB,EACjB,OAAO,GAA0C,EAAE,EAClD;IACD,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,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;gBACxB,KAAK,EAAE,CAAC,GAAG,CAAU,EAAE,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,GAAG,CAAA,wCAAwC,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAC/D","sourcesContent":["import { sql } from \"../sql.ts\";\nimport 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\tsql`INSERT INTO facts (session_id, seq, kind, key, value) VALUES (${sessionId}, ${seq}, ${kind}, ${key}, ${value})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readLatestFact(db: SqliteDatabase, sessionId: string, kind: string, key: string | null) {\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE session_id = ${sessionId} AND kind = ${kind} AND key IS ${key}\n\t\tORDER BY seq DESC\n\t\tLIMIT 1`.get<FactRow>(db);\n}\n\nexport function readLatestLabelFacts(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT f.key, f.value\n\t\tFROM facts AS f INDEXED BY idx_facts_session_kind_key_seq\n\t\tWHERE f.session_id = ${sessionId}\n\t\t\tAND f.kind = 'label'\n\t\t\tAND f.value IS NOT NULL\n\t\t\tAND f.seq = (\n\t\t\t\tSELECT MAX(candidate.seq)\n\t\t\t\tFROM facts AS candidate INDEXED BY idx_facts_session_kind_key_seq\n\t\t\t\tWHERE candidate.session_id = f.session_id\n\t\t\t\t\tAND candidate.kind = f.kind\n\t\t\t\t\tAND candidate.key IS f.key\n\t\t\t)\n\t\tORDER BY f.key`.all<{ key: string; value: string }>(db);\n}\n\nexport function readFactRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\toptions: { afterSeq?: number; limit?: number } = {},\n) {\n\tconst after = options.afterSeq === undefined ? sql`` : sql` AND seq > ${options.afterSeq}`;\n\tconst limit = options.limit === undefined ? sql`` : sql` LIMIT ${options.limit}`;\n\treturn sql`SELECT session_id, seq, kind, key, value\n\t\tFROM facts\n\t\tWHERE session_id = ${sessionId}${after}\n\t\tORDER BY seq${limit}`.all<FactRow>(db);\n}\n\nexport function deleteFactRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM facts WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -7,7 +7,7 @@ export interface RecordRow {
7
7
  run_id: string | null;
8
8
  type: string;
9
9
  op_kind: string | null;
10
- timestamp: string;
10
+ timestamp: number;
11
11
  payload: string;
12
12
  }
13
13
  export interface NewRecordRow {
@@ -17,7 +17,7 @@ export interface NewRecordRow {
17
17
  runId?: string;
18
18
  type: string;
19
19
  opKind?: string;
20
- timestamp: string;
20
+ timestamp: number;
21
21
  payload: string;
22
22
  }
23
23
  export declare function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow): void;
@@ -1 +1 @@
1
- {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"AAEA,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,QAM1F;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,eAcN;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,CAiBb","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\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\tsql`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (${sessionId}, ${record.seq}, ${record.id}, ${record.lane}, ${record.runId ?? null}, ${record.type}, ${record.opKind ?? null}, ${record.timestamp}, ${record.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM records WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM records WHERE session_id = ${sessionId}`.run(db);\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 = [sql`session_id = ${sessionId}`];\n\tif (query.lane !== undefined) predicates.push(sql`lane = ${query.lane}`);\n\tif (query.type !== undefined) predicates.push(sql`type = ${query.type}`);\n\tif (query.runId !== undefined) predicates.push(sql`run_id = ${query.runId}`);\n\tif (query.operationKind !== undefined) predicates.push(sql`op_kind = ${query.operationKind}`);\n\tif (query.afterSeq !== undefined) predicates.push(sql`seq > ${query.afterSeq}`);\n\tconst direction = query.order === \"oldestFirst\" ? sql`ASC` : sql`DESC`;\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\treturn sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY seq ${direction}${limit}`.all<RecordRow>(db);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = sql`SELECT open_operation_id FROM lanes WHERE session_id = ${sessionId} AND lane = ${lane}`.get<{\n\t\topen_operation_id: string | null;\n\t}>(db);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND id = ${laneRow.open_operation_id}`.get<RecordRow>(db);\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"]}
1
+ {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"AAEA,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,QAM1F;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,eAcN;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,CAiBb","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\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: number;\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: number;\n\tpayload: string;\n}\n\nexport function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow) {\n\tsql`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (${sessionId}, ${record.seq}, ${record.id}, ${record.lane}, ${record.runId ?? null}, ${record.type}, ${record.opKind ?? null}, ${record.timestamp}, ${record.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM records WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM records WHERE session_id = ${sessionId}`.run(db);\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 = [sql`session_id = ${sessionId}`];\n\tif (query.lane !== undefined) predicates.push(sql`lane = ${query.lane}`);\n\tif (query.type !== undefined) predicates.push(sql`type = ${query.type}`);\n\tif (query.runId !== undefined) predicates.push(sql`run_id = ${query.runId}`);\n\tif (query.operationKind !== undefined) predicates.push(sql`op_kind = ${query.operationKind}`);\n\tif (query.afterSeq !== undefined) predicates.push(sql`seq > ${query.afterSeq}`);\n\tconst direction = query.order === \"oldestFirst\" ? sql`ASC` : sql`DESC`;\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\treturn sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY seq ${direction}${limit}`.all<RecordRow>(db);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = sql`SELECT open_operation_id FROM lanes WHERE session_id = ${sessionId} AND lane = ${lane}`.get<{\n\t\topen_operation_id: string | null;\n\t}>(db);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND id = ${laneRow.open_operation_id}`.get<RecordRow>(db);\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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\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\tsql`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (${sessionId}, ${record.seq}, ${record.id}, ${record.lane}, ${record.runId ?? null}, ${record.type}, ${record.opKind ?? null}, ${record.timestamp}, ${record.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM records WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM records WHERE session_id = ${sessionId}`.run(db);\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 = [sql`session_id = ${sessionId}`];\n\tif (query.lane !== undefined) predicates.push(sql`lane = ${query.lane}`);\n\tif (query.type !== undefined) predicates.push(sql`type = ${query.type}`);\n\tif (query.runId !== undefined) predicates.push(sql`run_id = ${query.runId}`);\n\tif (query.operationKind !== undefined) predicates.push(sql`op_kind = ${query.operationKind}`);\n\tif (query.afterSeq !== undefined) predicates.push(sql`seq > ${query.afterSeq}`);\n\tconst direction = query.order === \"oldestFirst\" ? sql`ASC` : sql`DESC`;\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\treturn sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY seq ${direction}${limit}`.all<RecordRow>(db);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = sql`SELECT open_operation_id FROM lanes WHERE session_id = ${sessionId} AND lane = ${lane}`.get<{\n\t\topen_operation_id: string | null;\n\t}>(db);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND id = ${laneRow.open_operation_id}`.get<RecordRow>(db);\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"]}
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/sqlite/storage/records.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, sql } from \"../sql.ts\";\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: number;\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: number;\n\tpayload: string;\n}\n\nexport function appendRecordRow(db: SqliteDatabase, sessionId: string, record: NewRecordRow) {\n\tsql`INSERT INTO records\n\t\t\t(session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload)\n\t\t\tVALUES (${sessionId}, ${record.seq}, ${record.id}, ${record.lane}, ${record.runId ?? null}, ${record.type}, ${record.opKind ?? null}, ${record.timestamp}, ${record.payload})`.run(\n\t\tdb,\n\t);\n}\n\nexport function idExistsInRecords(db: SqliteDatabase, sessionId: string, id: string) {\n\treturn !!sql`SELECT 1 AS found FROM records WHERE session_id = ${sessionId} AND id = ${id} LIMIT 1`.get<{\n\t\tfound: number;\n\t}>(db);\n}\n\nexport function deleteRecordRows(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM records WHERE session_id = ${sessionId}`.run(db);\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 = [sql`session_id = ${sessionId}`];\n\tif (query.lane !== undefined) predicates.push(sql`lane = ${query.lane}`);\n\tif (query.type !== undefined) predicates.push(sql`type = ${query.type}`);\n\tif (query.runId !== undefined) predicates.push(sql`run_id = ${query.runId}`);\n\tif (query.operationKind !== undefined) predicates.push(sql`op_kind = ${query.operationKind}`);\n\tif (query.afterSeq !== undefined) predicates.push(sql`seq > ${query.afterSeq}`);\n\tconst direction = query.order === \"oldestFirst\" ? sql`ASC` : sql`DESC`;\n\tconst limit = query.limit === undefined ? sql`` : sql` LIMIT ${query.limit}`;\n\treturn sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE ${joinSqlFragments(predicates, \" AND \")}\n\t\tORDER BY seq ${direction}${limit}`.all<RecordRow>(db);\n}\n\nexport function readOpenOperationRows(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlane: string,\n\t_options: { limit?: number } = {},\n): RecordRow[] {\n\tconst laneRow = sql`SELECT open_operation_id FROM lanes WHERE session_id = ${sessionId} AND lane = ${lane}`.get<{\n\t\topen_operation_id: string | null;\n\t}>(db);\n\tif (!laneRow?.open_operation_id) return [];\n\n\tconst record = sql`SELECT session_id, seq, id, lane, run_id, type, op_kind, timestamp, payload\n\t\tFROM records\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND id = ${laneRow.open_operation_id}`.get<RecordRow>(db);\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"]}
@@ -1,7 +1,7 @@
1
1
  import type { SqliteDatabase, SqliteSessionMetadata } from "../types.ts";
2
2
  export interface SessionRow {
3
3
  id: string;
4
- created_at: string;
4
+ created_at: number;
5
5
  metadata: string | null;
6
6
  cwd: string;
7
7
  parent_session_id: string | null;
@@ -10,7 +10,7 @@ export interface SessionRow {
10
10
  }
11
11
  export interface NewSessionRow {
12
12
  id: string;
13
- createdAt: string;
13
+ createdAt: number;
14
14
  cwd: string;
15
15
  parentSessionId?: string;
16
16
  metadata?: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAElE;AAWD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,QAK1E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAenE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,gBAiBjF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAsBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAY1F","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: string;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: string;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!sql`SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get<{ found: number }>(db);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tsql`INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id)\n\t\tVALUES (${session.id}, ${session.createdAt}, ${serializeMetadata(session.metadata)}, ${session.cwd}, ${session.parentSessionId ?? null})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\tWHERE s.id = ${sessionId}`.get<SessionRow>(db);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? sql`` : sql`WHERE s.cwd = ${options.cwd}`;\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\t${where}\n\t\tORDER BY s.created_at DESC`.all<SessionRow>(db);\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string {\n\tif (value === null) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: Date.parse(row.created_at),\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAElE;AAWD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,QAK1E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAenE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,gBAiBjF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAoBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAY1F","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: number;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: number;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!sql`SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get<{ found: number }>(db);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tsql`INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id)\n\t\tVALUES (${session.id}, ${session.createdAt}, ${serializeMetadata(session.metadata)}, ${session.cwd}, ${session.parentSessionId ?? null})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\tWHERE s.id = ${sessionId}`.get<SessionRow>(db);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? sql`` : sql`WHERE s.cwd = ${options.cwd}`;\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\t${where}\n\t\tORDER BY s.created_at DESC`.all<SessionRow>(db);\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string | undefined {\n\tif (value === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: row.created_at,\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
@@ -69,9 +69,8 @@ export function deleteSessionRow(db, sessionId) {
69
69
  sql `DELETE FROM sessions WHERE id = ${sessionId}`.run(db);
70
70
  }
71
71
  function parseSessionName(value, sessionId) {
72
- if (value === null) {
73
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name must be a string`);
74
- }
72
+ if (value === null)
73
+ return undefined;
75
74
  let parsed;
76
75
  try {
77
76
  parsed = JSON.parse(value);
@@ -89,7 +88,7 @@ export function decodeSessionMetadata(row, path) {
89
88
  const name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);
90
89
  return {
91
90
  id: row.id,
92
- createdAt: Date.parse(row.created_at),
91
+ createdAt: row.created_at,
93
92
  ...(name === undefined ? {} : { name }),
94
93
  cwd: row.cwd,
95
94
  path,