@earendil-works/pi-session-backend-sqlite-node 0.84.4 → 0.85.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +26 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/sqlite/index.d.ts +3 -4
- package/dist/sqlite/index.d.ts.map +1 -1
- package/dist/sqlite/index.js +2 -3
- package/dist/sqlite/index.js.map +1 -1
- package/dist/sqlite/migrations/001_initial.sql +88 -89
- package/dist/sqlite/migrations.d.ts +1 -7
- package/dist/sqlite/migrations.d.ts.map +1 -1
- package/dist/sqlite/migrations.js +3 -35
- package/dist/sqlite/migrations.js.map +1 -1
- package/dist/sqlite/repo.d.ts +38 -38
- package/dist/sqlite/repo.d.ts.map +1 -1
- package/dist/sqlite/repo.js +353 -695
- package/dist/sqlite/repo.js.map +1 -1
- package/dist/sqlite/session/branch-entries.d.ts +6 -0
- package/dist/sqlite/session/branch-entries.d.ts.map +1 -0
- package/dist/sqlite/session/branch-entries.js +213 -0
- package/dist/sqlite/session/branch-entries.js.map +1 -0
- package/dist/sqlite/session/entries.d.ts +24 -0
- package/dist/sqlite/session/entries.d.ts.map +1 -0
- package/dist/sqlite/session/entries.js +126 -0
- package/dist/sqlite/session/entries.js.map +1 -0
- package/dist/sqlite/session/session-row.d.ts +23 -0
- package/dist/sqlite/session/session-row.d.ts.map +1 -0
- package/dist/sqlite/session/session-row.js +69 -0
- package/dist/sqlite/session/session-row.js.map +1 -0
- package/dist/sqlite/session/session-sequences.d.ts +4 -0
- package/dist/sqlite/session/session-sequences.d.ts.map +1 -0
- package/dist/sqlite/session/session-sequences.js +13 -0
- package/dist/sqlite/session/session-sequences.js.map +1 -0
- package/dist/sqlite/session/session-stats.d.ts +6 -0
- package/dist/sqlite/session/session-stats.d.ts.map +1 -0
- package/dist/sqlite/session/session-stats.js +39 -0
- package/dist/sqlite/session/session-stats.js.map +1 -0
- package/dist/sqlite/session/usage-ledger.d.ts +20 -0
- package/dist/sqlite/session/usage-ledger.d.ts.map +1 -0
- package/dist/sqlite/session/usage-ledger.js +50 -0
- package/dist/sqlite/session/usage-ledger.js.map +1 -0
- package/dist/sqlite/session/values.d.ts +23 -0
- package/dist/sqlite/session/values.d.ts.map +1 -0
- package/dist/sqlite/session/values.js +91 -0
- package/dist/sqlite/session/values.js.map +1 -0
- package/dist/sqlite/session.d.ts +42 -0
- package/dist/sqlite/session.d.ts.map +1 -0
- package/dist/sqlite/session.js +163 -0
- package/dist/sqlite/session.js.map +1 -0
- package/dist/sqlite/storage.d.ts +38 -0
- package/dist/sqlite/storage.d.ts.map +1 -0
- package/dist/sqlite/storage.js +161 -0
- package/dist/sqlite/storage.js.map +1 -0
- package/dist/sqlite/types.d.ts +6 -22
- package/dist/sqlite/types.d.ts.map +1 -1
- package/dist/sqlite/types.js.map +1 -1
- package/package.json +5 -4
- package/dist/sqlite/branch-cache.d.ts +0 -6
- package/dist/sqlite/branch-cache.d.ts.map +0 -1
- package/dist/sqlite/branch-cache.js +0 -70
- package/dist/sqlite/branch-cache.js.map +0 -1
- package/dist/sqlite/search-backend.d.ts +0 -14
- package/dist/sqlite/search-backend.d.ts.map +0 -1
- package/dist/sqlite/search-backend.js +0 -153
- package/dist/sqlite/search-backend.js.map +0 -1
- package/dist/sqlite/storage/branch-entries.d.ts +0 -41
- package/dist/sqlite/storage/branch-entries.d.ts.map +0 -1
- package/dist/sqlite/storage/branch-entries.js +0 -112
- package/dist/sqlite/storage/branch-entries.js.map +0 -1
- package/dist/sqlite/storage/branch-tips.d.ts +0 -7
- package/dist/sqlite/storage/branch-tips.d.ts.map +0 -1
- package/dist/sqlite/storage/branch-tips.js +0 -22
- package/dist/sqlite/storage/branch-tips.js.map +0 -1
- package/dist/sqlite/storage/entries.d.ts +0 -34
- package/dist/sqlite/storage/entries.d.ts.map +0 -1
- package/dist/sqlite/storage/entries.js +0 -37
- package/dist/sqlite/storage/entries.js.map +0 -1
- package/dist/sqlite/storage/facts.d.ts +0 -20
- package/dist/sqlite/storage/facts.d.ts.map +0 -1
- package/dist/sqlite/storage/facts.js +0 -38
- package/dist/sqlite/storage/facts.js.map +0 -1
- package/dist/sqlite/storage/lanes.d.ts +0 -35
- package/dist/sqlite/storage/lanes.d.ts.map +0 -1
- package/dist/sqlite/storage/lanes.js +0 -95
- package/dist/sqlite/storage/lanes.js.map +0 -1
- package/dist/sqlite/storage/records.d.ts +0 -38
- package/dist/sqlite/storage/records.d.ts.map +0 -1
- package/dist/sqlite/storage/records.js +0 -49
- package/dist/sqlite/storage/records.js.map +0 -1
- package/dist/sqlite/storage/session-sequences.d.ts +0 -7
- package/dist/sqlite/storage/session-sequences.d.ts.map +0 -1
- package/dist/sqlite/storage/session-sequences.js +0 -22
- package/dist/sqlite/storage/session-sequences.js.map +0 -1
- package/dist/sqlite/storage/session-stats.d.ts +0 -17
- package/dist/sqlite/storage/session-stats.d.ts.map +0 -1
- package/dist/sqlite/storage/session-stats.js +0 -40
- package/dist/sqlite/storage/session-stats.js.map +0 -1
- package/dist/sqlite/storage/sessions.d.ts +0 -26
- package/dist/sqlite/storage/sessions.d.ts.map +0 -1
- package/dist/sqlite/storage/sessions.js +0 -99
- package/dist/sqlite/storage/sessions.js.map +0 -1
- package/dist/sqlite/storage/writer-leases.d.ts +0 -15
- package/dist/sqlite/storage/writer-leases.d.ts.map +0 -1
- package/dist/sqlite/storage/writer-leases.js +0 -31
- package/dist/sqlite/storage/writer-leases.js.map +0 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
# @earendil-works/pi-session-backend-sqlite-node
|
|
2
2
|
|
|
3
|
-
Node sqlite
|
|
4
|
-
`node:sqlite` adapter (`SqliteDatabase` implementation), SQLite session repository,
|
|
5
|
-
migrations, materialized views, and optional FTS search.
|
|
3
|
+
Node `node:sqlite` Session backend for `@earendil-works/pi-agent-core`.
|
|
6
4
|
|
|
7
5
|
```ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/pi-agent-core";
|
|
7
|
+
import {
|
|
8
|
+
createNodeSqliteFactory,
|
|
9
|
+
SqliteSessionRepo,
|
|
10
|
+
} from "@earendil-works/pi-session-backend-sqlite-node";
|
|
12
11
|
|
|
13
|
-
const
|
|
14
|
-
|
|
12
|
+
const repository = new SqliteSessionRepo({
|
|
13
|
+
directory: "/var/lib/pi/sessions",
|
|
14
|
+
databaseFactory: createNodeSqliteFactory(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const session = await repository.create({}, BACKGROUND_CONTEXT);
|
|
18
|
+
const main = await session.createBranch("main", null, BACKGROUND_CONTEXT);
|
|
19
|
+
await main.appendMessage(
|
|
20
|
+
{ role: "user", content: "hello", timestamp: Date.now() },
|
|
21
|
+
BACKGROUND_CONTEXT,
|
|
22
|
+
);
|
|
23
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
24
|
+
await repository.close(BACKGROUND_CONTEXT);
|
|
15
25
|
```
|
|
16
26
|
|
|
17
|
-
The
|
|
18
|
-
|
|
19
|
-
The
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
The default layout creates one file per Session under `directory`. IDs containing only ASCII letters, digits, `_`, and `-` retain `{sessionId}.sqlite`; every other ID uses a `~`-prefixed base64url encoding of its UTF-16 code units. The durable ID is unchanged, and returned/listed metadata contains the canonical physical path. Pass `databasePath` to place multiple Sessions in one supported shared container; its parent is created when needed.
|
|
28
|
+
|
|
29
|
+
The database factory distinguishes intentional creation, no-create read-write open, and no-create read-only open. Session `open()` and deletion reject metadata outside the configured repository and never create a missing database. Listing is read-only and best-effort. Forking deliberately permits a foreign source metadata path: it reads that exact existing container read-only and never substitutes an active local Session with the same ID.
|
|
30
|
+
|
|
31
|
+
The host lifecycle, not this backend, guarantees one writable owner per Session. Directly opening the same Session for writes in another process is unsupported. The repository rejects overlapping local create/open/fork/delete ownership for one ID, but implements no cross-process lease, lock, fence, heartbeat, or takeover. The host must close a worker before deletion.
|
|
32
|
+
|
|
33
|
+
A fork of a source open in the same repository queues its snapshot on that source's commit queue. Any other source, including one held open by a live Session worker, uses an independent read-only connection and one deferred WAL transaction; later worker commits may complete while that snapshot remains open. Shared-container deletion removes only the selected Session's rows. Repository close waits for every open Session cleanup attempt before reporting errors. The package does not export a search service or FTS index; search is the separate S3 projection.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAoC,MAAM,mBAAmB,CAAC;AAiGjH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAEvE;AAED,wBAAgB,uBAAuB,IAAI,qBAAqB,CAc/D;AAGD,cAAc,mBAAmB,CAAC","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { pathToFileURL } from \"node:url\";\nimport { sql } from \"./sqlite/sql.ts\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow> {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.iterate(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.iterate(...(params as SQLInputValue[]))\n\t\t) as Iterable<TRow>;\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tsql`BEGIN IMMEDIATE`.exec(this);\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tsql`COMMIT`.exec(this);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tsql`ROLLBACK`.exec(this);\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t\tasync openExisting(path: string): Promise<SqliteDatabase> {\n\t\t\tconst url = pathToFileURL(path);\n\t\t\turl.searchParams.set(\"mode\", \"rw\");\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(url));\n\t\t},\n\t\tasync openReadOnly(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path, { readOnly: true }));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
2
3
|
import { sql } from "./sqlite/sql.js";
|
|
3
4
|
function isNamedParameters(value) {
|
|
4
5
|
if (value === null || typeof value !== "object")
|
|
@@ -87,6 +88,14 @@ export function createNodeSqliteFactory() {
|
|
|
87
88
|
async open(path) {
|
|
88
89
|
return new NodeSqliteDatabase(new DatabaseSync(path));
|
|
89
90
|
},
|
|
91
|
+
async openExisting(path) {
|
|
92
|
+
const url = pathToFileURL(path);
|
|
93
|
+
url.searchParams.set("mode", "rw");
|
|
94
|
+
return new NodeSqliteDatabase(new DatabaseSync(url));
|
|
95
|
+
},
|
|
96
|
+
async openReadOnly(path) {
|
|
97
|
+
return new NodeSqliteDatabase(new DatabaseSync(path, { readOnly: true }));
|
|
98
|
+
},
|
|
90
99
|
};
|
|
91
100
|
}
|
|
92
101
|
// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGtC,SAAS,iBAAiB,CAAC,KAAc,EAA0C;IAClF,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,SAAS,aAAa,CAAC,KAAc,EAAW;IAC/C,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,UAAU,CAAC,IAAI,MAAM,IAAI,KAAK,CAAC;AAAA,CACvG;AAED,MAAM,mBAAmB;IACP,SAAS,CAAsC;IAEhE,YAAY,SAA8C,EAAE;QAC3D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAAA,CAC3B;IAED,GAAG,CAAC,GAAG,MAAiB,EAAmB;QAC1C,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;QAChC,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC;YACtC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAI,IAAwB,CAAC;YACzD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAI,MAA0B,CAAC,CAAC;QACtD,OAAO;YACN,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;YAC/B,eAAe,EAAE,MAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC;SAClG,CAAC;IAAA,CACF;IAED,GAAG,CAAsB,GAAG,MAAiB,EAAoB;QAChE,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;QAChC,OAAO,CACN,iBAAiB,CAAC,KAAK,CAAC;YACvB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAI,IAAwB,CAAC;YACzD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAI,MAA0B,CAAC,CACjC,CAAC;IAAA,CACtB;IAED,GAAG,CAAsB,GAAG,MAAiB,EAAU;QACtD,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;QAChC,OAAO,CACN,iBAAiB,CAAC,KAAK,CAAC;YACvB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAI,IAAwB,CAAC;YACzD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAI,MAA0B,CAAC,CAC3C,CAAC;IAAA,CACZ;IAED,OAAO,CAAsB,GAAG,MAAiB,EAAkB;QAClE,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;QAChC,OAAO,CACN,iBAAiB,CAAC,KAAK,CAAC;YACvB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,GAAI,IAAwB,CAAC;YAC7D,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAI,MAA0B,CAAC,CACvC,CAAC;IAAA,CACpB;CACD;AAED,MAAM,kBAAkB;IACN,EAAE,CAAe;IAElC,YAAY,EAAgB,EAAE;QAC7B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IAAA,CACb;IAED,IAAI,CAAC,GAAW,EAAQ;QACvB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAAA,CAClB;IAED,OAAO,CAAC,GAAW,EAAmB;QACrC,OAAO,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAAA,CACrD;IAED,WAAW,CAAI,EAAW,EAAK;QAC9B,GAAG,CAAA,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;YACpB,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;YACzE,CAAC;YACD,GAAG,CAAA,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvB,OAAO,MAAM,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC;gBACJ,GAAG,CAAA,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACR,oDAAoD;YACrD,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IAAA,CACD;IAED,KAAK,GAAS;QACb,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAAA,CAChB;CACD;AAED,MAAM,UAAU,sBAAsB,CAAC,EAAgB,EAAkB;IACxE,OAAO,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAC;AAAA,CAClC;AAED,MAAM,UAAU,uBAAuB,GAA0B;IAChE,OAAO;QACN,KAAK,CAAC,IAAI,CAAC,IAAY,EAA2B;YACjD,OAAO,IAAI,kBAAkB,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAAA,CACtD;KACD,CAAC;AAAA,CACF;AAED,oGAAoG;AACpG,cAAc,mBAAmB,CAAC","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { sql } from \"./sqlite/sql.ts\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow> {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.iterate(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.iterate(...(params as SQLInputValue[]))\n\t\t) as Iterable<TRow>;\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tsql`BEGIN IMMEDIATE`.exec(this);\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tsql`COMMIT`.exec(this);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tsql`ROLLBACK`.exec(this);\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { pathToFileURL } from \"node:url\";\nimport { sql } from \"./sqlite/sql.ts\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow> {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.iterate(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.iterate(...(params as SQLInputValue[]))\n\t\t) as Iterable<TRow>;\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tsql`BEGIN IMMEDIATE`.exec(this);\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tsql`COMMIT`.exec(this);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tsql`ROLLBACK`.exec(this);\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t\tasync openExisting(path: string): Promise<SqliteDatabase> {\n\t\t\tconst url = pathToFileURL(path);\n\t\t\turl.searchParams.set(\"mode\", \"rw\");\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(url));\n\t\t},\n\t\tasync openReadOnly(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path, { readOnly: true }));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
|
package/dist/sqlite/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export { SqliteSessionRepository, type SqliteSessionRepositoryOptions, type SqliteWriterLeaseOptions, } from "./repo.ts";
|
|
3
|
-
export * from "./search-backend.ts";
|
|
1
|
+
export * from "./repo.ts";
|
|
4
2
|
export * from "./sql.ts";
|
|
5
|
-
export
|
|
3
|
+
export * from "./storage.ts";
|
|
4
|
+
export type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement, } from "./types.ts";
|
|
6
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,YAAY,EACX,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,eAAe,GACf,MAAM,YAAY,CAAC","sourcesContent":["export * from \"./repo.ts\";\nexport * from \"./sql.ts\";\nexport * from \"./storage.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
|
package/dist/sqlite/index.js
CHANGED
package/dist/sqlite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./repo.ts\";\nexport * from \"./sql.ts\";\nexport * from \"./storage.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
|
|
@@ -1,122 +1,121 @@
|
|
|
1
|
+
-- AgentHarness storage format 4 / storageVersion 1.
|
|
2
|
+
-- A SQLite database file is a session container. The current default repo
|
|
3
|
+
-- placement still creates one file per session, but the schema supports any
|
|
4
|
+
-- number of sessions per file by scoping every durable row with session_id.
|
|
5
|
+
-- Authoritative durable state is entries + scalar_values + list_values +
|
|
6
|
+
-- usage_ledger; branch_* and stats columns on sessions are maintained
|
|
7
|
+
-- projections/caches.
|
|
8
|
+
|
|
1
9
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
2
10
|
id TEXT PRIMARY KEY,
|
|
3
11
|
created_at INTEGER NOT NULL,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
metadata TEXT
|
|
12
|
+
parent_session_id TEXT,
|
|
13
|
+
storage_version INTEGER NOT NULL,
|
|
14
|
+
metadata TEXT,
|
|
15
|
+
message_count INTEGER NOT NULL,
|
|
16
|
+
usage_payload TEXT NOT NULL,
|
|
17
|
+
next_seq INTEGER NOT NULL
|
|
7
18
|
) WITHOUT ROWID;
|
|
8
19
|
|
|
9
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_created_at ON sessions(created_at DESC);
|
|
10
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_cwd_created_at ON sessions(cwd, created_at DESC);
|
|
11
|
-
|
|
12
20
|
CREATE TABLE IF NOT EXISTS entries (
|
|
13
21
|
session_id TEXT NOT NULL,
|
|
14
|
-
seq INTEGER NOT NULL,
|
|
15
22
|
id TEXT NOT NULL,
|
|
16
|
-
parent_id TEXT
|
|
23
|
+
parent_id TEXT,
|
|
24
|
+
seq INTEGER NOT NULL,
|
|
17
25
|
type TEXT NOT NULL,
|
|
26
|
+
custom_type TEXT,
|
|
18
27
|
timestamp INTEGER NOT NULL,
|
|
19
28
|
payload TEXT NOT NULL,
|
|
20
|
-
PRIMARY KEY (session_id, id)
|
|
21
|
-
UNIQUE (session_id, seq)
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
CREATE INDEX IF NOT EXISTS idx_entries_session_parent ON entries(session_id, parent_id);
|
|
25
|
-
CREATE INDEX IF NOT EXISTS idx_entries_session_type_seq ON entries(session_id, type, seq);
|
|
26
|
-
|
|
27
|
-
CREATE TABLE IF NOT EXISTS session_sequences (
|
|
28
|
-
session_id TEXT PRIMARY KEY,
|
|
29
|
-
next_seq INTEGER NOT NULL
|
|
29
|
+
PRIMARY KEY (session_id, id)
|
|
30
30
|
) WITHOUT ROWID;
|
|
31
31
|
|
|
32
|
-
CREATE
|
|
33
|
-
|
|
34
|
-
message_count INTEGER NOT NULL,
|
|
35
|
-
cached_tokens REAL NOT NULL,
|
|
36
|
-
uncached_tokens REAL NOT NULL,
|
|
37
|
-
total_tokens REAL NOT NULL,
|
|
38
|
-
cost_total REAL NOT NULL
|
|
39
|
-
) WITHOUT ROWID;
|
|
32
|
+
CREATE INDEX IF NOT EXISTS ix_entry_parent ON entries(session_id, parent_id);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS ix_entry_seq ON entries(session_id, seq, type);
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
-- exists only to make branch scans cheap.
|
|
43
|
-
CREATE TABLE IF NOT EXISTS branch_entries (
|
|
35
|
+
CREATE TABLE IF NOT EXISTS scalar_values (
|
|
44
36
|
session_id TEXT NOT NULL,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
PRIMARY KEY (session_id, branch_id, entry_id)
|
|
51
|
-
) WITHOUT ROWID;
|
|
52
|
-
|
|
53
|
-
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_seq ON branch_entries(session_id, branch_id, entry_seq);
|
|
54
|
-
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_entry ON branch_entries(session_id, entry_id, branch_id, entry_seq);
|
|
55
|
-
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_type_seq ON branch_entries(session_id, branch_id, entry_type, entry_seq);
|
|
56
|
-
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_custom_seq ON branch_entries(session_id, branch_id, custom_type, entry_seq);
|
|
57
|
-
|
|
58
|
-
CREATE TABLE IF NOT EXISTS lanes (
|
|
59
|
-
session_id TEXT NOT NULL,
|
|
60
|
-
lane TEXT NOT NULL,
|
|
61
|
-
leaf_id TEXT NULL,
|
|
62
|
-
open_operation_id TEXT NULL,
|
|
63
|
-
PRIMARY KEY (session_id, lane)
|
|
37
|
+
namespace TEXT NOT NULL,
|
|
38
|
+
key TEXT NOT NULL,
|
|
39
|
+
seq INTEGER NOT NULL,
|
|
40
|
+
value TEXT NOT NULL,
|
|
41
|
+
PRIMARY KEY (session_id, namespace, key)
|
|
64
42
|
) WITHOUT ROWID;
|
|
65
43
|
|
|
66
|
-
CREATE TABLE IF NOT EXISTS
|
|
44
|
+
CREATE TABLE IF NOT EXISTS list_values (
|
|
67
45
|
session_id TEXT NOT NULL,
|
|
46
|
+
namespace TEXT NOT NULL,
|
|
47
|
+
key TEXT NOT NULL,
|
|
68
48
|
seq INTEGER NOT NULL,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
run_id TEXT NULL,
|
|
72
|
-
type TEXT NOT NULL,
|
|
73
|
-
op_kind TEXT NULL,
|
|
74
|
-
timestamp INTEGER NOT NULL,
|
|
75
|
-
payload TEXT NOT NULL,
|
|
76
|
-
PRIMARY KEY (session_id, id),
|
|
77
|
-
UNIQUE (session_id, seq)
|
|
49
|
+
value TEXT NOT NULL,
|
|
50
|
+
PRIMARY KEY (session_id, namespace, key, seq)
|
|
78
51
|
) WITHOUT ROWID;
|
|
79
52
|
|
|
80
|
-
CREATE
|
|
81
|
-
CREATE INDEX IF NOT EXISTS idx_records_session_type_seq ON records(session_id, type, seq);
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_records_session_type_op_kind_seq ON records(session_id, type, op_kind, seq);
|
|
83
|
-
CREATE INDEX IF NOT EXISTS idx_records_session_lane_type_seq ON records(session_id, lane, type, seq);
|
|
84
|
-
CREATE INDEX IF NOT EXISTS idx_records_session_lane_type_op_kind_seq ON records(session_id, lane, type, op_kind, seq);
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_records_session_run_id_seq ON records(session_id, run_id, seq);
|
|
86
|
-
|
|
87
|
-
CREATE TABLE IF NOT EXISTS lane_moves (
|
|
53
|
+
CREATE TABLE IF NOT EXISTS usage_ledger (
|
|
88
54
|
session_id TEXT NOT NULL,
|
|
55
|
+
id TEXT NOT NULL,
|
|
89
56
|
seq INTEGER NOT NULL,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
57
|
+
entry_id TEXT,
|
|
58
|
+
adjustment INTEGER NOT NULL,
|
|
59
|
+
usage TEXT NOT NULL,
|
|
60
|
+
details TEXT,
|
|
61
|
+
PRIMARY KEY (session_id, id)
|
|
93
62
|
) WITHOUT ROWID;
|
|
94
63
|
|
|
95
|
-
|
|
96
|
-
|
|
64
|
+
CREATE INDEX IF NOT EXISTS ix_usage_seq ON usage_ledger(session_id, seq);
|
|
65
|
+
|
|
66
|
+
-- Storage-level integrity that spans rows/tables. Primary keys enforce
|
|
67
|
+
-- same-table duplicate ids; these triggers enforce the shared entry/usage id
|
|
68
|
+
-- namespace and ordered parent insertion without a TypeScript preflight pass.
|
|
69
|
+
CREATE TRIGGER IF NOT EXISTS trg_entries_validate
|
|
70
|
+
BEFORE INSERT ON entries
|
|
71
|
+
BEGIN
|
|
72
|
+
SELECT RAISE(ABORT, 'missing parent entry')
|
|
73
|
+
WHERE NEW.parent_id IS NOT NULL
|
|
74
|
+
AND NOT EXISTS (
|
|
75
|
+
SELECT 1 FROM entries WHERE session_id = NEW.session_id AND id = NEW.parent_id
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
SELECT RAISE(ABORT, 'duplicate entry or usage id')
|
|
79
|
+
WHERE EXISTS (
|
|
80
|
+
SELECT 1 FROM usage_ledger WHERE session_id = NEW.session_id AND id = NEW.id
|
|
81
|
+
);
|
|
82
|
+
END;
|
|
83
|
+
|
|
84
|
+
CREATE TRIGGER IF NOT EXISTS trg_usage_ledger_validate
|
|
85
|
+
BEFORE INSERT ON usage_ledger
|
|
86
|
+
BEGIN
|
|
87
|
+
SELECT RAISE(ABORT, 'duplicate entry or usage id')
|
|
88
|
+
WHERE EXISTS (
|
|
89
|
+
SELECT 1 FROM entries WHERE session_id = NEW.session_id AND id = NEW.id
|
|
90
|
+
);
|
|
91
|
+
END;
|
|
92
|
+
|
|
93
|
+
-- Private branch index. Not values/lists; no equivalent in the other backends.
|
|
94
|
+
CREATE TABLE IF NOT EXISTS branch_entries (
|
|
97
95
|
session_id TEXT NOT NULL,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
PRIMARY KEY (session_id,
|
|
96
|
+
branch_id TEXT NOT NULL,
|
|
97
|
+
entry_id TEXT NOT NULL,
|
|
98
|
+
entry_seq INTEGER NOT NULL,
|
|
99
|
+
entry_type TEXT NOT NULL,
|
|
100
|
+
PRIMARY KEY (session_id, branch_id, entry_id)
|
|
103
101
|
) WITHOUT ROWID;
|
|
104
102
|
|
|
105
|
-
|
|
103
|
+
-- Ordered scans. entry_seq must follow session_id, branch_id directly or ORDER
|
|
104
|
+
-- BY needs a temp b-tree; entry_id and entry_type trail so the index covers
|
|
105
|
+
-- id-only reads.
|
|
106
|
+
CREATE INDEX IF NOT EXISTS ix_be_seq ON branch_entries(session_id, branch_id, entry_seq, entry_id, entry_type);
|
|
107
|
+
-- Type-filtered scans.
|
|
108
|
+
CREATE INDEX IF NOT EXISTS ix_be_type ON branch_entries(session_id, branch_id, entry_type, entry_seq, entry_id);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS ix_be_entry ON branch_entries(session_id, entry_id);
|
|
106
110
|
|
|
107
|
-
CREATE TABLE IF NOT EXISTS
|
|
111
|
+
CREATE TABLE IF NOT EXISTS branch_meta (
|
|
108
112
|
session_id TEXT NOT NULL,
|
|
109
113
|
branch_id TEXT NOT NULL,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
tip_entry_id TEXT NOT NULL,
|
|
115
|
+
tip_seq INTEGER NOT NULL,
|
|
116
|
+
base_branch_id TEXT,
|
|
117
|
+
base_seq INTEGER,
|
|
118
|
+
PRIMARY KEY (session_id, branch_id)
|
|
113
119
|
) WITHOUT ROWID;
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
-- after a new owner takes over the session.
|
|
117
|
-
CREATE TABLE IF NOT EXISTS writer_leases (
|
|
118
|
-
session_id TEXT PRIMARY KEY,
|
|
119
|
-
owner_id TEXT NOT NULL,
|
|
120
|
-
fence INTEGER NOT NULL,
|
|
121
|
-
expires_at_ms INTEGER NOT NULL
|
|
122
|
-
) WITHOUT ROWID;
|
|
121
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ix_bm_tip ON branch_meta(session_id, tip_entry_id);
|
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { SqliteDatabase } from "./types.ts";
|
|
2
|
-
export
|
|
3
|
-
id: string;
|
|
4
|
-
order: number;
|
|
5
|
-
sql: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function loadMigrations(): Promise<SqliteMigration[]>;
|
|
8
|
-
export declare function applyMigrations(db: SqliteDatabase): Promise<void>;
|
|
2
|
+
export declare function applyInitialSchema(db: SqliteDatabase): Promise<void>;
|
|
9
3
|
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport async function applyInitialSchema(db: SqliteDatabase): Promise<void> {\n\tconst migration = await readFile(fileURLToPath(new URL(\"./migrations/001_initial.sql\", import.meta.url)), \"utf8\");\n\tdb.exec(migration);\n}\n"]}
|
|
@@ -1,39 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
export async function loadMigrations() {
|
|
8
|
-
return [
|
|
9
|
-
{
|
|
10
|
-
id: "001_initial.sql",
|
|
11
|
-
order: 1,
|
|
12
|
-
sql: await loadMigrationSql("./migrations/001_initial.sql"),
|
|
13
|
-
},
|
|
14
|
-
];
|
|
15
|
-
}
|
|
16
|
-
function ensureMigrationsTable(db) {
|
|
17
|
-
sql `
|
|
18
|
-
CREATE TABLE IF NOT EXISTS migrations (
|
|
19
|
-
id TEXT PRIMARY KEY,
|
|
20
|
-
applied_at TEXT NOT NULL
|
|
21
|
-
);
|
|
22
|
-
`.exec(db);
|
|
23
|
-
}
|
|
24
|
-
export async function applyMigrations(db) {
|
|
25
|
-
ensureMigrationsTable(db);
|
|
26
|
-
const migrations = await loadMigrations();
|
|
27
|
-
const appliedRows = sql `SELECT id FROM migrations ORDER BY applied_at, id`.all(db);
|
|
28
|
-
const applied = new Set(appliedRows.map((row) => row.id));
|
|
29
|
-
for (const migration of migrations) {
|
|
30
|
-
if (applied.has(migration.id))
|
|
31
|
-
continue;
|
|
32
|
-
db.transaction(() => {
|
|
33
|
-
db.exec(migration.sql);
|
|
34
|
-
sql `INSERT INTO migrations (id, applied_at) VALUES (${migration.id}, ${new Date().toISOString()})`.run(db);
|
|
35
|
-
});
|
|
36
|
-
applied.add(migration.id);
|
|
37
|
-
}
|
|
3
|
+
export async function applyInitialSchema(db) {
|
|
4
|
+
const migration = await readFile(fileURLToPath(new URL("./migrations/001_initial.sql", import.meta.url)), "utf8");
|
|
5
|
+
db.exec(migration);
|
|
38
6
|
}
|
|
39
7
|
//# sourceMappingURL=migrations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAkB,EAAiB;IAC3E,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,8BAA8B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAClH,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAAA,CACnB","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport async function applyInitialSchema(db: SqliteDatabase): Promise<void> {\n\tconst migration = await readFile(fileURLToPath(new URL(\"./migrations/001_initial.sql\", import.meta.url)), \"utf8\");\n\tdb.exec(migration);\n}\n"]}
|
package/dist/sqlite/repo.d.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import type { Context, ForkOptions, SessionCreateOptions } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type SqliteSessionMetadata } from "./session/session-row.ts";
|
|
3
|
+
import { SqliteOpenSession } from "./session.ts";
|
|
4
|
+
import type { SqliteDatabaseFactory } from "./types.ts";
|
|
5
|
+
export declare const SQLITE_STORAGE_VERSION = 1;
|
|
6
|
+
export declare const SQLITE_SESSION_EXTENSION = ".sqlite";
|
|
7
|
+
export type SqliteSessionCreateOptions = SessionCreateOptions;
|
|
8
|
+
export interface SqliteSessionRepoOptions {
|
|
9
|
+
directory: string;
|
|
10
|
+
/** Optional single container path. Defaults to one encoded `${id}.sqlite` file per session under directory. */
|
|
11
|
+
databasePath?: string;
|
|
12
|
+
databaseFactory: SqliteDatabaseFactory;
|
|
13
|
+
now?: () => number;
|
|
8
14
|
}
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fork(source: SqliteSessionMetadata, options: ForkOptions & SqliteSessionCreateOptions): Promise<Session<SqliteSessionMetadata>>;
|
|
35
|
-
close(): Promise<void>;
|
|
36
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
37
|
-
private getDatabasePath;
|
|
38
|
-
private getDatabase;
|
|
39
|
-
private openDatabase;
|
|
15
|
+
export declare class SqliteSessionRepo {
|
|
16
|
+
private readonly directory;
|
|
17
|
+
private readonly databasePath;
|
|
18
|
+
private readonly databaseFactory;
|
|
19
|
+
private readonly now;
|
|
20
|
+
private readonly pendingIds;
|
|
21
|
+
private readonly openStorages;
|
|
22
|
+
private readonly openSessions;
|
|
23
|
+
private closed;
|
|
24
|
+
private closePromise;
|
|
25
|
+
constructor(options: SqliteSessionRepoOptions);
|
|
26
|
+
create(options: SqliteSessionCreateOptions | undefined, _context: Context): Promise<SqliteOpenSession>;
|
|
27
|
+
open(metadata: SqliteSessionMetadata, _context: Context): Promise<SqliteOpenSession>;
|
|
28
|
+
list(_options: undefined, _context: Context): Promise<SqliteSessionMetadata[]>;
|
|
29
|
+
delete(metadata: SqliteSessionMetadata, _context: Context): Promise<void>;
|
|
30
|
+
fork(source: SqliteSessionMetadata, options: ForkOptions, context: Context): Promise<SqliteOpenSession>;
|
|
31
|
+
close(context: Context): Promise<void>;
|
|
32
|
+
private createForkSnapshotFromExternalSource;
|
|
33
|
+
private closeOpenSessions;
|
|
34
|
+
private openStorageBackedSession;
|
|
35
|
+
private repositoryPathForMetadata;
|
|
36
|
+
private reserveId;
|
|
37
|
+
private pathForSession;
|
|
38
|
+
private usesSharedDatabase;
|
|
39
|
+
private assertOpen;
|
|
40
40
|
}
|
|
41
41
|
//# sourceMappingURL=repo.d.ts.map
|