@handy_wote/pi-storage-sqlite-node 0.81.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 +15 -0
- package/README.md +5 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +5 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/migrations/001_initial.sql +59 -0
- package/dist/sqlite/migrations.d.ts +9 -0
- package/dist/sqlite/migrations.d.ts.map +1 -0
- package/dist/sqlite/migrations.js +40 -0
- package/dist/sqlite/migrations.js.map +1 -0
- package/dist/sqlite/repo.d.ts +26 -0
- package/dist/sqlite/repo.d.ts.map +1 -0
- package/dist/sqlite/repo.js +158 -0
- package/dist/sqlite/repo.js.map +1 -0
- package/dist/sqlite/storage/branch-entries.d.ts +8 -0
- package/dist/sqlite/storage/branch-entries.d.ts.map +1 -0
- package/dist/sqlite/storage/branch-entries.js +43 -0
- package/dist/sqlite/storage/branch-entries.js.map +1 -0
- package/dist/sqlite/storage/index.d.ts +38 -0
- package/dist/sqlite/storage/index.d.ts.map +1 -0
- package/dist/sqlite/storage/index.js +347 -0
- package/dist/sqlite/storage/index.js.map +1 -0
- package/dist/sqlite/storage/session-entries.d.ts +17 -0
- package/dist/sqlite/storage/session-entries.d.ts.map +1 -0
- package/dist/sqlite/storage/session-entries.js +173 -0
- package/dist/sqlite/storage/session-entries.js.map +1 -0
- package/dist/sqlite/storage/session-materialized.d.ts +43 -0
- package/dist/sqlite/storage/session-materialized.d.ts.map +1 -0
- package/dist/sqlite/storage/session-materialized.js +282 -0
- package/dist/sqlite/storage/session-materialized.js.map +1 -0
- package/dist/sqlite/storage/session-sequences.d.ts +4 -0
- package/dist/sqlite/storage/session-sequences.d.ts.map +1 -0
- package/dist/sqlite/storage/session-sequences.js +14 -0
- package/dist/sqlite/storage/session-sequences.js.map +1 -0
- package/dist/sqlite/storage/sessions.d.ts +11 -0
- package/dist/sqlite/storage/sessions.d.ts.map +1 -0
- package/dist/sqlite/storage/sessions.js +27 -0
- package/dist/sqlite/storage/sessions.js.map +1 -0
- package/dist/sqlite/storage/shared.d.ts +10 -0
- package/dist/sqlite/storage/shared.d.ts.map +1 -0
- package/dist/sqlite/storage/shared.js +25 -0
- package/dist/sqlite/storage/shared.js.map +1 -0
- package/dist/sqlite/types.d.ts +46 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/package.json +38 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Renamed the published package scope to `@handy_wote`.
|
|
8
|
+
|
|
9
|
+
## [0.81.1] - 2026-07-21
|
|
10
|
+
|
|
11
|
+
## [0.81.0] - 2026-07-21
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added a Node.js SQLite storage backend for agent harness sessions, including migrations and materialized session views ([#6594](https://github.com/earendil-works/pi/pull/6594) by [@cristinaponcela](https://github.com/cristinaponcela)).
|
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# @handy_wote/pi-storage-sqlite-node
|
|
2
|
+
|
|
3
|
+
Node sqlite storage backend for `@handy_wote/pi-agent-core` sessions. Provides the
|
|
4
|
+
`node:sqlite` adapter (`SqliteDatabase` implementation) and the SQLite session
|
|
5
|
+
repo/storage implementation (`SqliteSessionRepo`, migrations, materialized views).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { SqliteDatabase, SqliteDatabaseFactory } from "./sqlite/types.ts";
|
|
3
|
+
export declare function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase;
|
|
4
|
+
export declare function createNodeSqliteFactory(): SqliteDatabaseFactory;
|
|
5
|
+
export * from "./sqlite/index.ts";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAoC,MAAM,mBAAmB,CAAC;AAiFjH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAEvE;AAED,wBAAgB,uBAAuB,IAAI,qBAAqB,CAM/D;AAGD,cAAc,mBAAmB,CAAC","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\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\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\tasync run(...params: unknown[]): Promise<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\tasync get<TRow extends object>(...params: unknown[]): Promise<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\tasync all<TRow extends object>(...params: unknown[]): Promise<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\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\tasync exec(sql: string): Promise<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\tasync transaction<T>(fn: () => Promise<T>): Promise<T> {\n\t\tthis.db.exec(\"BEGIN\");\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tthis.db.exec(\"COMMIT\");\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tthis.db.exec(\"ROLLBACK\");\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\tasync close(): Promise<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 storage backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
function isNamedParameters(value) {
|
|
3
|
+
if (value === null || typeof value !== "object")
|
|
4
|
+
return false;
|
|
5
|
+
if (Array.isArray(value) || ArrayBuffer.isView(value))
|
|
6
|
+
return false;
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
class NodeSqliteStatement {
|
|
10
|
+
statement;
|
|
11
|
+
constructor(statement) {
|
|
12
|
+
this.statement = statement;
|
|
13
|
+
}
|
|
14
|
+
async run(...params) {
|
|
15
|
+
const [first, ...rest] = params;
|
|
16
|
+
const result = isNamedParameters(first)
|
|
17
|
+
? this.statement.run(first, ...rest)
|
|
18
|
+
: this.statement.run(...params);
|
|
19
|
+
return {
|
|
20
|
+
changes: Number(result.changes),
|
|
21
|
+
lastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async get(...params) {
|
|
25
|
+
const [first, ...rest] = params;
|
|
26
|
+
return (isNamedParameters(first)
|
|
27
|
+
? this.statement.get(first, ...rest)
|
|
28
|
+
: this.statement.get(...params));
|
|
29
|
+
}
|
|
30
|
+
async all(...params) {
|
|
31
|
+
const [first, ...rest] = params;
|
|
32
|
+
return (isNamedParameters(first)
|
|
33
|
+
? this.statement.all(first, ...rest)
|
|
34
|
+
: this.statement.all(...params));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
class NodeSqliteDatabase {
|
|
38
|
+
db;
|
|
39
|
+
constructor(db) {
|
|
40
|
+
this.db = db;
|
|
41
|
+
}
|
|
42
|
+
async exec(sql) {
|
|
43
|
+
this.db.exec(sql);
|
|
44
|
+
}
|
|
45
|
+
prepare(sql) {
|
|
46
|
+
return new NodeSqliteStatement(this.db.prepare(sql));
|
|
47
|
+
}
|
|
48
|
+
async transaction(fn) {
|
|
49
|
+
this.db.exec("BEGIN");
|
|
50
|
+
try {
|
|
51
|
+
const result = await fn();
|
|
52
|
+
this.db.exec("COMMIT");
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
try {
|
|
57
|
+
this.db.exec("ROLLBACK");
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Ignore rollback errors to rethrow original error.
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async close() {
|
|
66
|
+
this.db.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function wrapNodeSqliteDatabase(db) {
|
|
70
|
+
return new NodeSqliteDatabase(db);
|
|
71
|
+
}
|
|
72
|
+
export function createNodeSqliteFactory() {
|
|
73
|
+
return {
|
|
74
|
+
async open(path) {
|
|
75
|
+
return new NodeSqliteDatabase(new DatabaseSync(path));
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Re-export the SQLite session storage backend and types so this package is a complete node-sqlite backend.
|
|
80
|
+
export * from "./sqlite/index.js";
|
|
81
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 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\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\tasync run(...params: unknown[]): Promise<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\tasync get<TRow extends object>(...params: unknown[]): Promise<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\tasync all<TRow extends object>(...params: unknown[]): Promise<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\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\tasync exec(sql: string): Promise<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\tasync transaction<T>(fn: () => Promise<T>): Promise<T> {\n\t\tthis.db.exec(\"BEGIN\");\n\t\ttry {\n\t\t\tconst result = await fn();\n\t\t\tthis.db.exec(\"COMMIT\");\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tthis.db.exec(\"ROLLBACK\");\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\tasync close(): Promise<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 storage backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport * from \"./repo.ts\";\nexport * from \"./storage/index.ts\";\nexport * from \"./types.ts\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport * from \"./repo.ts\";\nexport * from \"./storage/index.ts\";\nexport * from \"./types.ts\";\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
created_at TEXT NOT NULL,
|
|
4
|
+
cwd TEXT NOT NULL,
|
|
5
|
+
parent_session_id TEXT NULL,
|
|
6
|
+
metadata TEXT NULL,
|
|
7
|
+
active_leaf_id TEXT NULL
|
|
8
|
+
) WITHOUT ROWID;
|
|
9
|
+
|
|
10
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_created_at ON sessions(created_at DESC);
|
|
11
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_cwd ON sessions(cwd);
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS session_entries (
|
|
15
|
+
session_id TEXT NOT NULL,
|
|
16
|
+
id TEXT NOT NULL,
|
|
17
|
+
entry_seq INTEGER NOT NULL,
|
|
18
|
+
parent_id TEXT NULL,
|
|
19
|
+
type TEXT NOT NULL,
|
|
20
|
+
timestamp TEXT NOT NULL,
|
|
21
|
+
payload TEXT NOT NULL,
|
|
22
|
+
PRIMARY KEY (session_id, id)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_entries_session_seq ON session_entries(session_id, entry_seq);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_session_entries_session_parent ON session_entries(session_id, parent_id);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_session_entries_session_type ON session_entries(session_id, type);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS session_sequences (
|
|
30
|
+
session_id TEXT PRIMARY KEY,
|
|
31
|
+
next_seq INTEGER NOT NULL
|
|
32
|
+
) WITHOUT ROWID;
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS branch_entries (
|
|
35
|
+
session_id TEXT NOT NULL,
|
|
36
|
+
branch_id TEXT NOT NULL,
|
|
37
|
+
entry_id TEXT NOT NULL,
|
|
38
|
+
entry_seq INTEGER NOT NULL,
|
|
39
|
+
PRIMARY KEY (session_id, branch_id, entry_id)
|
|
40
|
+
) WITHOUT ROWID;
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch ON branch_entries(session_id, branch_id);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_seq ON branch_entries(session_id, branch_id, entry_seq);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_branch_entries_session_entry ON branch_entries(session_id, entry_id);
|
|
45
|
+
|
|
46
|
+
CREATE TABLE IF NOT EXISTS session_materialized (
|
|
47
|
+
session_id TEXT PRIMARY KEY,
|
|
48
|
+
payload TEXT NOT NULL
|
|
49
|
+
) WITHOUT ROWID;
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS entry_materialized (
|
|
52
|
+
session_id TEXT NOT NULL,
|
|
53
|
+
entry_seq INTEGER NOT NULL,
|
|
54
|
+
type TEXT NOT NULL,
|
|
55
|
+
payload TEXT NOT NULL,
|
|
56
|
+
PRIMARY KEY (session_id, entry_seq, type)
|
|
57
|
+
) WITHOUT ROWID;
|
|
58
|
+
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_entry_materialized_session_type_seq ON entry_materialized(session_id, type, entry_seq);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "./types.ts";
|
|
2
|
+
export interface SqliteMigration {
|
|
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>;
|
|
9
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAMD,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAQjE;AAWD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBvE","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nasync function ensureMigrationsTable(db: SqliteDatabase): Promise<void> {\n\tawait db.exec(`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tawait ensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = await db.prepare(\"SELECT id FROM migrations ORDER BY applied_at, id\").all<{ id: string }>();\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tawait db.transaction(async () => {\n\t\t\tawait db.exec(migration.sql);\n\t\t\tawait db\n\t\t\t\t.prepare(\"INSERT INTO migrations (id, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(migration.id, new Date().toISOString());\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
async function loadMigrationSql(relativePath) {
|
|
4
|
+
return readFile(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8");
|
|
5
|
+
}
|
|
6
|
+
export async function loadMigrations() {
|
|
7
|
+
return [
|
|
8
|
+
{
|
|
9
|
+
id: "001_initial.sql",
|
|
10
|
+
order: 1,
|
|
11
|
+
sql: await loadMigrationSql("./migrations/001_initial.sql"),
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
async function ensureMigrationsTable(db) {
|
|
16
|
+
await db.exec(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
applied_at TEXT NOT NULL
|
|
20
|
+
);
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
export async function applyMigrations(db) {
|
|
24
|
+
await ensureMigrationsTable(db);
|
|
25
|
+
const migrations = await loadMigrations();
|
|
26
|
+
const appliedRows = await db.prepare("SELECT id FROM migrations ORDER BY applied_at, id").all();
|
|
27
|
+
const applied = new Set(appliedRows.map((row) => row.id));
|
|
28
|
+
for (const migration of migrations) {
|
|
29
|
+
if (applied.has(migration.id))
|
|
30
|
+
continue;
|
|
31
|
+
await db.transaction(async () => {
|
|
32
|
+
await db.exec(migration.sql);
|
|
33
|
+
await db
|
|
34
|
+
.prepare("INSERT INTO migrations (id, applied_at) VALUES (?, ?)")
|
|
35
|
+
.run(migration.id, new Date().toISOString());
|
|
36
|
+
});
|
|
37
|
+
applied.add(migration.id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +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;AASzC,KAAK,UAAU,gBAAgB,CAAC,YAAoB,EAAmB;IACtE,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAAA,CAC/E;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,GAA+B;IAClE,OAAO;QACN;YACC,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,CAAC;YACR,GAAG,EAAE,MAAM,gBAAgB,CAAC,8BAA8B,CAAC;SAC3D;KACD,CAAC;AAAA,CACF;AAED,KAAK,UAAU,qBAAqB,CAAC,EAAkB,EAAiB;IACvE,MAAM,EAAE,CAAC,IAAI,CAAC;;;;;CAKd,CAAC,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAkB,EAAiB;IACxE,MAAM,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,MAAM,cAAc,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,EAAkB,CAAC;IAChH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,SAAS;QACxC,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;YAChC,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,EAAE;iBACN,OAAO,CAAC,uDAAuD,CAAC;iBAChE,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAAA,CAC9C,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;AAAA,CACD","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nasync function ensureMigrationsTable(db: SqliteDatabase): Promise<void> {\n\tawait db.exec(`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tawait ensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = await db.prepare(\"SELECT id FROM migrations ORDER BY applied_at, id\").all<{ id: string }>();\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tawait db.transaction(async () => {\n\t\t\tawait db.exec(migration.sql);\n\t\t\tawait db\n\t\t\t\t.prepare(\"INSERT INTO migrations (id, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(migration.id, new Date().toISOString());\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Session } from "@handy_wote/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabaseFactory, SqliteSessionCreateOptions, SqliteSessionListOptions, SqliteSessionMetadata, SqliteSessionRepoApi, SqliteSessionRepoEnv } from "./types.ts";
|
|
3
|
+
export declare class SqliteSessionRepo implements SqliteSessionRepoApi {
|
|
4
|
+
private readonly env;
|
|
5
|
+
private readonly sqlite;
|
|
6
|
+
private readonly databasePathInput;
|
|
7
|
+
private databasePath;
|
|
8
|
+
constructor(options: {
|
|
9
|
+
env: SqliteSessionRepoEnv;
|
|
10
|
+
sqlite: SqliteDatabaseFactory;
|
|
11
|
+
databasePath: string;
|
|
12
|
+
});
|
|
13
|
+
private getDatabasePath;
|
|
14
|
+
private ensureDatabaseDir;
|
|
15
|
+
private openDatabase;
|
|
16
|
+
create(options: SqliteSessionCreateOptions): Promise<Session<SqliteSessionMetadata>>;
|
|
17
|
+
open(metadata: SqliteSessionMetadata): Promise<Session<SqliteSessionMetadata>>;
|
|
18
|
+
list(options?: SqliteSessionListOptions): Promise<SqliteSessionMetadata[]>;
|
|
19
|
+
delete(metadata: SqliteSessionMetadata): Promise<void>;
|
|
20
|
+
fork(sourceMetadata: SqliteSessionMetadata, options: SqliteSessionCreateOptions & {
|
|
21
|
+
entryId?: string;
|
|
22
|
+
position?: "before" | "at";
|
|
23
|
+
id?: string;
|
|
24
|
+
}): Promise<Session<SqliteSessionMetadata>>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=repo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/sqlite/repo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAoC,MAAM,2BAA2B,CAAC;AAW3F,OAAO,KAAK,EAEX,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,MAAM,YAAY,CAAC;AAuBpB,qBAAa,iBAAkB,YAAW,oBAAoB;IAC7D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAuB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,OAAO,EAAE;QAAE,GAAG,EAAE,oBAAoB,CAAC;QAAC,MAAM,EAAE,qBAAqB,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAItG;YAEa,eAAe;YAUf,iBAAiB;YASjB,YAAY;IAapB,MAAM,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAezF;IAEK,IAAI,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAcnF;IAEK,IAAI,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAsBnF;IAEK,MAAM,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3D;IAEK,IAAI,CACT,cAAc,EAAE,qBAAqB,EACrC,OAAO,EAAE,0BAA0B,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GACjG,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAyBzC;CACD","sourcesContent":["import type { Session, SessionStorage, SessionTreeEntry } from \"@handy_wote/pi-agent-core\";\nimport {\n\tcreateSessionId,\n\tgetEntriesToFork,\n\tgetFileSystemResultOrThrow,\n\tSessionError,\n\ttoSession,\n} from \"@handy_wote/pi-agent-core\";\nimport { applyMigrations } from \"./migrations.ts\";\nimport { SqliteSessionStorage } from \"./storage/index.ts\";\nimport { rowToMetadata, type SessionRow } from \"./storage/sessions.ts\";\nimport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepoApi,\n\tSqliteSessionRepoEnv,\n} from \"./types.ts\";\n\nfunction getParentPath(path: string): string {\n\tconst normalized = path.replace(/[\\\\/]+$/, \"\");\n\tconst lastSlash = Math.max(normalized.lastIndexOf(\"/\"), normalized.lastIndexOf(\"\\\\\"));\n\tif (lastSlash < 0) return \".\";\n\tif (lastSlash === 0) return normalized.slice(0, 1);\n\treturn normalized.slice(0, lastSlash);\n}\n\nasync function configureSqliteDatabase(db: SqliteDatabase): Promise<void> {\n\tawait db.exec(\"PRAGMA journal_mode=WAL\");\n\tawait db.exec(\"PRAGMA synchronous=FULL\");\n\tawait db.exec(\"PRAGMA busy_timeout=5000\");\n}\n\nasync function cleanupSessionStorage(storage: SessionStorage): Promise<void> {\n\tconst maybeClosable = storage as SessionStorage & { cleanup?: () => Promise<void> };\n\tif (typeof maybeClosable.cleanup === \"function\") {\n\t\tawait maybeClosable.cleanup();\n\t}\n}\n\nexport class SqliteSessionRepo implements SqliteSessionRepoApi {\n\tprivate readonly env: SqliteSessionRepoEnv;\n\tprivate readonly sqlite: SqliteDatabaseFactory;\n\tprivate readonly databasePathInput: string;\n\tprivate databasePath: string | undefined;\n\n\tconstructor(options: { env: SqliteSessionRepoEnv; sqlite: SqliteDatabaseFactory; databasePath: string }) {\n\t\tthis.env = options.env;\n\t\tthis.sqlite = options.sqlite;\n\t\tthis.databasePathInput = options.databasePath;\n\t}\n\n\tprivate async getDatabasePath(): Promise<string> {\n\t\tif (!this.databasePath) {\n\t\t\tthis.databasePath = getFileSystemResultOrThrow(\n\t\t\t\tawait this.env.absolutePath(this.databasePathInput),\n\t\t\t\t`Failed to resolve SQLite sessions database ${this.databasePathInput}`,\n\t\t\t);\n\t\t}\n\t\treturn this.databasePath;\n\t}\n\n\tprivate async ensureDatabaseDir(): Promise<void> {\n\t\tconst path = await this.getDatabasePath();\n\t\tconst directory = getParentPath(path);\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.env.createDir(directory, { recursive: true }),\n\t\t\t`Failed to create SQLite sessions directory ${directory}`,\n\t\t);\n\t}\n\n\tprivate async openDatabase(): Promise<SqliteDatabase> {\n\t\tawait this.ensureDatabaseDir();\n\t\tconst db = await this.sqlite.open(await this.getDatabasePath());\n\t\ttry {\n\t\t\tawait configureSqliteDatabase(db);\n\t\t\tawait applyMigrations(db);\n\t\t\treturn db;\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync create(options: SqliteSessionCreateOptions): Promise<Session<SqliteSessionMetadata>> {\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst id = options.id ?? createSessionId();\n\t\t\tconst storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tsessionId: id,\n\t\t\t\tparentSessionId: options.parentSessionId,\n\t\t\t\tmetadata: options.metadata,\n\t\t\t});\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync open(metadata: SqliteSessionMetadata): Promise<Session<SqliteSessionMetadata>> {\n\t\tif (\n\t\t\t!getFileSystemResultOrThrow(await this.env.exists(metadata.path), `Failed to check database ${metadata.path}`)\n\t\t) {\n\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst storage = await SqliteSessionStorage.open(db, metadata);\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync list(options: SqliteSessionListOptions = {}): Promise<SqliteSessionMetadata[]> {\n\t\tconst path = await this.getDatabasePath();\n\t\tif (!getFileSystemResultOrThrow(await this.env.exists(path), `Failed to check database ${path}`)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst rows = options.cwd\n\t\t\t\t? await db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t\"SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions WHERE cwd = ? ORDER BY created_at DESC\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.all<SessionRow>(options.cwd)\n\t\t\t\t: await db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t\"SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions ORDER BY created_at DESC\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.all<SessionRow>();\n\t\t\treturn rows.map((row) => rowToMetadata(row, path));\n\t\t} finally {\n\t\t\tawait db.close();\n\t\t}\n\t}\n\n\tasync delete(metadata: SqliteSessionMetadata): Promise<void> {\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tawait db.transaction(async () => {\n\t\t\t\tawait db.prepare(\"DELETE FROM branch_entries WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_entries WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM entry_materialized WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_materialized WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_sequences WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tconst result = await db.prepare(\"DELETE FROM sessions WHERE id = ?\").run(metadata.id);\n\t\t\t\tif (result.changes === 0) {\n\t\t\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\t\t\t}\n\t\t\t});\n\t\t} finally {\n\t\t\tawait db.close();\n\t\t}\n\t}\n\n\tasync fork(\n\t\tsourceMetadata: SqliteSessionMetadata,\n\t\toptions: SqliteSessionCreateOptions & { entryId?: string; position?: \"before\" | \"at\"; id?: string },\n\t): Promise<Session<SqliteSessionMetadata>> {\n\t\tconst source = await this.open(sourceMetadata);\n\t\tlet forkedEntries: SessionTreeEntry[];\n\t\ttry {\n\t\t\tforkedEntries = await getEntriesToFork(source.getStorage(), options);\n\t\t} finally {\n\t\t\tawait cleanupSessionStorage(source.getStorage());\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst id = options.id ?? createSessionId();\n\t\t\tconst storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tsessionId: id,\n\t\t\t\tparentSessionId: options.parentSessionId ?? sourceMetadata.id,\n\t\t\t\tmetadata: options.metadata ?? sourceMetadata.metadata,\n\t\t\t});\n\t\t\tfor (const entry of forkedEntries) {\n\t\t\t\tawait storage.appendEntry(entry);\n\t\t\t}\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createSessionId, getEntriesToFork, getFileSystemResultOrThrow, SessionError, toSession, } from "@handy_wote/pi-agent-core";
|
|
2
|
+
import { applyMigrations } from "./migrations.js";
|
|
3
|
+
import { SqliteSessionStorage } from "./storage/index.js";
|
|
4
|
+
import { rowToMetadata } from "./storage/sessions.js";
|
|
5
|
+
function getParentPath(path) {
|
|
6
|
+
const normalized = path.replace(/[\\/]+$/, "");
|
|
7
|
+
const lastSlash = Math.max(normalized.lastIndexOf("/"), normalized.lastIndexOf("\\"));
|
|
8
|
+
if (lastSlash < 0)
|
|
9
|
+
return ".";
|
|
10
|
+
if (lastSlash === 0)
|
|
11
|
+
return normalized.slice(0, 1);
|
|
12
|
+
return normalized.slice(0, lastSlash);
|
|
13
|
+
}
|
|
14
|
+
async function configureSqliteDatabase(db) {
|
|
15
|
+
await db.exec("PRAGMA journal_mode=WAL");
|
|
16
|
+
await db.exec("PRAGMA synchronous=FULL");
|
|
17
|
+
await db.exec("PRAGMA busy_timeout=5000");
|
|
18
|
+
}
|
|
19
|
+
async function cleanupSessionStorage(storage) {
|
|
20
|
+
const maybeClosable = storage;
|
|
21
|
+
if (typeof maybeClosable.cleanup === "function") {
|
|
22
|
+
await maybeClosable.cleanup();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class SqliteSessionRepo {
|
|
26
|
+
env;
|
|
27
|
+
sqlite;
|
|
28
|
+
databasePathInput;
|
|
29
|
+
databasePath;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.env = options.env;
|
|
32
|
+
this.sqlite = options.sqlite;
|
|
33
|
+
this.databasePathInput = options.databasePath;
|
|
34
|
+
}
|
|
35
|
+
async getDatabasePath() {
|
|
36
|
+
if (!this.databasePath) {
|
|
37
|
+
this.databasePath = getFileSystemResultOrThrow(await this.env.absolutePath(this.databasePathInput), `Failed to resolve SQLite sessions database ${this.databasePathInput}`);
|
|
38
|
+
}
|
|
39
|
+
return this.databasePath;
|
|
40
|
+
}
|
|
41
|
+
async ensureDatabaseDir() {
|
|
42
|
+
const path = await this.getDatabasePath();
|
|
43
|
+
const directory = getParentPath(path);
|
|
44
|
+
getFileSystemResultOrThrow(await this.env.createDir(directory, { recursive: true }), `Failed to create SQLite sessions directory ${directory}`);
|
|
45
|
+
}
|
|
46
|
+
async openDatabase() {
|
|
47
|
+
await this.ensureDatabaseDir();
|
|
48
|
+
const db = await this.sqlite.open(await this.getDatabasePath());
|
|
49
|
+
try {
|
|
50
|
+
await configureSqliteDatabase(db);
|
|
51
|
+
await applyMigrations(db);
|
|
52
|
+
return db;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
await db.close();
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async create(options) {
|
|
60
|
+
const db = await this.openDatabase();
|
|
61
|
+
try {
|
|
62
|
+
const id = options.id ?? createSessionId();
|
|
63
|
+
const storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {
|
|
64
|
+
cwd: options.cwd,
|
|
65
|
+
sessionId: id,
|
|
66
|
+
parentSessionId: options.parentSessionId,
|
|
67
|
+
metadata: options.metadata,
|
|
68
|
+
});
|
|
69
|
+
return toSession(storage);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
await db.close();
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async open(metadata) {
|
|
77
|
+
if (!getFileSystemResultOrThrow(await this.env.exists(metadata.path), `Failed to check database ${metadata.path}`)) {
|
|
78
|
+
throw new SessionError("not_found", `Session not found: ${metadata.id}`);
|
|
79
|
+
}
|
|
80
|
+
const db = await this.openDatabase();
|
|
81
|
+
try {
|
|
82
|
+
const storage = await SqliteSessionStorage.open(db, metadata);
|
|
83
|
+
return toSession(storage);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
await db.close();
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async list(options = {}) {
|
|
91
|
+
const path = await this.getDatabasePath();
|
|
92
|
+
if (!getFileSystemResultOrThrow(await this.env.exists(path), `Failed to check database ${path}`)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const db = await this.openDatabase();
|
|
96
|
+
try {
|
|
97
|
+
const rows = options.cwd
|
|
98
|
+
? await db
|
|
99
|
+
.prepare("SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions WHERE cwd = ? ORDER BY created_at DESC")
|
|
100
|
+
.all(options.cwd)
|
|
101
|
+
: await db
|
|
102
|
+
.prepare("SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions ORDER BY created_at DESC")
|
|
103
|
+
.all();
|
|
104
|
+
return rows.map((row) => rowToMetadata(row, path));
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await db.close();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async delete(metadata) {
|
|
111
|
+
const db = await this.openDatabase();
|
|
112
|
+
try {
|
|
113
|
+
await db.transaction(async () => {
|
|
114
|
+
await db.prepare("DELETE FROM branch_entries WHERE session_id = ?").run(metadata.id);
|
|
115
|
+
await db.prepare("DELETE FROM session_entries WHERE session_id = ?").run(metadata.id);
|
|
116
|
+
await db.prepare("DELETE FROM entry_materialized WHERE session_id = ?").run(metadata.id);
|
|
117
|
+
await db.prepare("DELETE FROM session_materialized WHERE session_id = ?").run(metadata.id);
|
|
118
|
+
await db.prepare("DELETE FROM session_sequences WHERE session_id = ?").run(metadata.id);
|
|
119
|
+
const result = await db.prepare("DELETE FROM sessions WHERE id = ?").run(metadata.id);
|
|
120
|
+
if (result.changes === 0) {
|
|
121
|
+
throw new SessionError("not_found", `Session not found: ${metadata.id}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
await db.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async fork(sourceMetadata, options) {
|
|
130
|
+
const source = await this.open(sourceMetadata);
|
|
131
|
+
let forkedEntries;
|
|
132
|
+
try {
|
|
133
|
+
forkedEntries = await getEntriesToFork(source.getStorage(), options);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
await cleanupSessionStorage(source.getStorage());
|
|
137
|
+
}
|
|
138
|
+
const db = await this.openDatabase();
|
|
139
|
+
try {
|
|
140
|
+
const id = options.id ?? createSessionId();
|
|
141
|
+
const storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {
|
|
142
|
+
cwd: options.cwd,
|
|
143
|
+
sessionId: id,
|
|
144
|
+
parentSessionId: options.parentSessionId ?? sourceMetadata.id,
|
|
145
|
+
metadata: options.metadata ?? sourceMetadata.metadata,
|
|
146
|
+
});
|
|
147
|
+
for (const entry of forkedEntries) {
|
|
148
|
+
await storage.appendEntry(entry);
|
|
149
|
+
}
|
|
150
|
+
return toSession(storage);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
await db.close();
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/sqlite/repo.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Session, SessionStorage, SessionTreeEntry } from \"@handy_wote/pi-agent-core\";\nimport {\n\tcreateSessionId,\n\tgetEntriesToFork,\n\tgetFileSystemResultOrThrow,\n\tSessionError,\n\ttoSession,\n} from \"@handy_wote/pi-agent-core\";\nimport { applyMigrations } from \"./migrations.ts\";\nimport { SqliteSessionStorage } from \"./storage/index.ts\";\nimport { rowToMetadata, type SessionRow } from \"./storage/sessions.ts\";\nimport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepoApi,\n\tSqliteSessionRepoEnv,\n} from \"./types.ts\";\n\nfunction getParentPath(path: string): string {\n\tconst normalized = path.replace(/[\\\\/]+$/, \"\");\n\tconst lastSlash = Math.max(normalized.lastIndexOf(\"/\"), normalized.lastIndexOf(\"\\\\\"));\n\tif (lastSlash < 0) return \".\";\n\tif (lastSlash === 0) return normalized.slice(0, 1);\n\treturn normalized.slice(0, lastSlash);\n}\n\nasync function configureSqliteDatabase(db: SqliteDatabase): Promise<void> {\n\tawait db.exec(\"PRAGMA journal_mode=WAL\");\n\tawait db.exec(\"PRAGMA synchronous=FULL\");\n\tawait db.exec(\"PRAGMA busy_timeout=5000\");\n}\n\nasync function cleanupSessionStorage(storage: SessionStorage): Promise<void> {\n\tconst maybeClosable = storage as SessionStorage & { cleanup?: () => Promise<void> };\n\tif (typeof maybeClosable.cleanup === \"function\") {\n\t\tawait maybeClosable.cleanup();\n\t}\n}\n\nexport class SqliteSessionRepo implements SqliteSessionRepoApi {\n\tprivate readonly env: SqliteSessionRepoEnv;\n\tprivate readonly sqlite: SqliteDatabaseFactory;\n\tprivate readonly databasePathInput: string;\n\tprivate databasePath: string | undefined;\n\n\tconstructor(options: { env: SqliteSessionRepoEnv; sqlite: SqliteDatabaseFactory; databasePath: string }) {\n\t\tthis.env = options.env;\n\t\tthis.sqlite = options.sqlite;\n\t\tthis.databasePathInput = options.databasePath;\n\t}\n\n\tprivate async getDatabasePath(): Promise<string> {\n\t\tif (!this.databasePath) {\n\t\t\tthis.databasePath = getFileSystemResultOrThrow(\n\t\t\t\tawait this.env.absolutePath(this.databasePathInput),\n\t\t\t\t`Failed to resolve SQLite sessions database ${this.databasePathInput}`,\n\t\t\t);\n\t\t}\n\t\treturn this.databasePath;\n\t}\n\n\tprivate async ensureDatabaseDir(): Promise<void> {\n\t\tconst path = await this.getDatabasePath();\n\t\tconst directory = getParentPath(path);\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.env.createDir(directory, { recursive: true }),\n\t\t\t`Failed to create SQLite sessions directory ${directory}`,\n\t\t);\n\t}\n\n\tprivate async openDatabase(): Promise<SqliteDatabase> {\n\t\tawait this.ensureDatabaseDir();\n\t\tconst db = await this.sqlite.open(await this.getDatabasePath());\n\t\ttry {\n\t\t\tawait configureSqliteDatabase(db);\n\t\t\tawait applyMigrations(db);\n\t\t\treturn db;\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync create(options: SqliteSessionCreateOptions): Promise<Session<SqliteSessionMetadata>> {\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst id = options.id ?? createSessionId();\n\t\t\tconst storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tsessionId: id,\n\t\t\t\tparentSessionId: options.parentSessionId,\n\t\t\t\tmetadata: options.metadata,\n\t\t\t});\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync open(metadata: SqliteSessionMetadata): Promise<Session<SqliteSessionMetadata>> {\n\t\tif (\n\t\t\t!getFileSystemResultOrThrow(await this.env.exists(metadata.path), `Failed to check database ${metadata.path}`)\n\t\t) {\n\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst storage = await SqliteSessionStorage.open(db, metadata);\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync list(options: SqliteSessionListOptions = {}): Promise<SqliteSessionMetadata[]> {\n\t\tconst path = await this.getDatabasePath();\n\t\tif (!getFileSystemResultOrThrow(await this.env.exists(path), `Failed to check database ${path}`)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst rows = options.cwd\n\t\t\t\t? await db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t\"SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions WHERE cwd = ? ORDER BY created_at DESC\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.all<SessionRow>(options.cwd)\n\t\t\t\t: await db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t\"SELECT id, created_at, metadata, cwd, parent_session_id, active_leaf_id FROM sessions ORDER BY created_at DESC\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.all<SessionRow>();\n\t\t\treturn rows.map((row) => rowToMetadata(row, path));\n\t\t} finally {\n\t\t\tawait db.close();\n\t\t}\n\t}\n\n\tasync delete(metadata: SqliteSessionMetadata): Promise<void> {\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tawait db.transaction(async () => {\n\t\t\t\tawait db.prepare(\"DELETE FROM branch_entries WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_entries WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM entry_materialized WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_materialized WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tawait db.prepare(\"DELETE FROM session_sequences WHERE session_id = ?\").run(metadata.id);\n\t\t\t\tconst result = await db.prepare(\"DELETE FROM sessions WHERE id = ?\").run(metadata.id);\n\t\t\t\tif (result.changes === 0) {\n\t\t\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\t\t\t}\n\t\t\t});\n\t\t} finally {\n\t\t\tawait db.close();\n\t\t}\n\t}\n\n\tasync fork(\n\t\tsourceMetadata: SqliteSessionMetadata,\n\t\toptions: SqliteSessionCreateOptions & { entryId?: string; position?: \"before\" | \"at\"; id?: string },\n\t): Promise<Session<SqliteSessionMetadata>> {\n\t\tconst source = await this.open(sourceMetadata);\n\t\tlet forkedEntries: SessionTreeEntry[];\n\t\ttry {\n\t\t\tforkedEntries = await getEntriesToFork(source.getStorage(), options);\n\t\t} finally {\n\t\t\tawait cleanupSessionStorage(source.getStorage());\n\t\t}\n\t\tconst db = await this.openDatabase();\n\t\ttry {\n\t\t\tconst id = options.id ?? createSessionId();\n\t\t\tconst storage = await SqliteSessionStorage.create(db, await this.getDatabasePath(), {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tsessionId: id,\n\t\t\t\tparentSessionId: options.parentSessionId ?? sourceMetadata.id,\n\t\t\t\tmetadata: options.metadata ?? sourceMetadata.metadata,\n\t\t\t});\n\t\t\tfor (const entry of forkedEntries) {\n\t\t\t\tawait storage.appendEntry(entry);\n\t\t\t}\n\t\t\treturn toSession(storage);\n\t\t} catch (error) {\n\t\t\tawait db.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SessionTreeEntry } from "@handy_wote/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
3
|
+
export interface BranchEntryRow {
|
|
4
|
+
entry_id: string;
|
|
5
|
+
entry_seq: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getMaterializedBranchPathOrCompaction(db: SqliteDatabase, sessionId: string, branchId: string, byId: Map<string, SessionTreeEntry>): Promise<SessionTreeEntry[]>;
|
|
8
|
+
//# sourceMappingURL=branch-entries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-entries.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,qCAAqC,CAC1D,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyC7B","sourcesContent":["import type { SessionTreeEntry } from \"@handy_wote/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\nimport { decodeEntry, type SessionEntryRow } from \"./session-entries.ts\";\nimport { invalidSession } from \"./shared.ts\";\n\nexport interface BranchEntryRow {\n\tentry_id: string;\n\tentry_seq: number;\n}\n\nexport async function getMaterializedBranchPathOrCompaction(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tbyId: Map<string, SessionTreeEntry>,\n): Promise<SessionTreeEntry[]> {\n\tconst branchRows = await db\n\t\t.prepare(\n\t\t\t\"SELECT entry_id, entry_seq FROM branch_entries WHERE session_id = ? AND branch_id = ? ORDER BY entry_seq\",\n\t\t)\n\t\t.all<BranchEntryRow>(sessionId, branchId);\n\tif (branchRows.length === 0) {\n\t\treturn [];\n\t}\n\tconst entryIds = branchRows.map((row) => row.entry_id);\n\tconst placeholders = entryIds.map(() => \"?\").join(\", \");\n\tconst entryRows = await db\n\t\t.prepare(\n\t\t\t`SELECT session_id, id, entry_seq, parent_id, type, timestamp, payload FROM session_entries WHERE session_id = ? AND id IN (${placeholders})`,\n\t\t)\n\t\t.all<SessionEntryRow>(sessionId, ...entryIds);\n\tconst entryRowsById = new Map(entryRows.map((row) => [row.id, row]));\n\tconst entries: SessionTreeEntry[] = [];\n\tfor (const branchRow of branchRows) {\n\t\t// leaf entries are navigation markers used to mark which branch became active;\n\t\t// they are not part of the model/context path reconstructed from branch_entries.\n\t\tconst cached = byId.get(branchRow.entry_id);\n\t\tif (cached) {\n\t\t\tif (cached.type !== \"leaf\") {\n\t\t\t\tentries.push(cached);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst entryRow = entryRowsById.get(branchRow.entry_id);\n\t\tif (!entryRow) throw invalidSession(`missing entry row for branch entry ${branchRow.entry_id}`);\n\t\ttry {\n\t\t\tconst entry = decodeEntry(entryRow);\n\t\t\tbyId.set(entry.id, entry);\n\t\t\tif (entry.type !== \"leaf\") {\n\t\t\t\tentries.push(entry);\n\t\t\t}\n\t\t} catch {\n\t\t\tthrow invalidSession(`invalid entry row for branch entry ${branchRow.entry_id}`);\n\t\t}\n\t}\n\treturn entries;\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { decodeEntry } from "./session-entries.js";
|
|
2
|
+
import { invalidSession } from "./shared.js";
|
|
3
|
+
export async function getMaterializedBranchPathOrCompaction(db, sessionId, branchId, byId) {
|
|
4
|
+
const branchRows = await db
|
|
5
|
+
.prepare("SELECT entry_id, entry_seq FROM branch_entries WHERE session_id = ? AND branch_id = ? ORDER BY entry_seq")
|
|
6
|
+
.all(sessionId, branchId);
|
|
7
|
+
if (branchRows.length === 0) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const entryIds = branchRows.map((row) => row.entry_id);
|
|
11
|
+
const placeholders = entryIds.map(() => "?").join(", ");
|
|
12
|
+
const entryRows = await db
|
|
13
|
+
.prepare(`SELECT session_id, id, entry_seq, parent_id, type, timestamp, payload FROM session_entries WHERE session_id = ? AND id IN (${placeholders})`)
|
|
14
|
+
.all(sessionId, ...entryIds);
|
|
15
|
+
const entryRowsById = new Map(entryRows.map((row) => [row.id, row]));
|
|
16
|
+
const entries = [];
|
|
17
|
+
for (const branchRow of branchRows) {
|
|
18
|
+
// leaf entries are navigation markers used to mark which branch became active;
|
|
19
|
+
// they are not part of the model/context path reconstructed from branch_entries.
|
|
20
|
+
const cached = byId.get(branchRow.entry_id);
|
|
21
|
+
if (cached) {
|
|
22
|
+
if (cached.type !== "leaf") {
|
|
23
|
+
entries.push(cached);
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const entryRow = entryRowsById.get(branchRow.entry_id);
|
|
28
|
+
if (!entryRow)
|
|
29
|
+
throw invalidSession(`missing entry row for branch entry ${branchRow.entry_id}`);
|
|
30
|
+
try {
|
|
31
|
+
const entry = decodeEntry(entryRow);
|
|
32
|
+
byId.set(entry.id, entry);
|
|
33
|
+
if (entry.type !== "leaf") {
|
|
34
|
+
entries.push(entry);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw invalidSession(`invalid entry row for branch entry ${branchRow.entry_id}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return entries;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=branch-entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/branch-entries.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAwB,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAO7C,MAAM,CAAC,KAAK,UAAU,qCAAqC,CAC1D,EAAkB,EAClB,SAAiB,EACjB,QAAgB,EAChB,IAAmC,EACL;IAC9B,MAAM,UAAU,GAAG,MAAM,EAAE;SACzB,OAAO,CACP,0GAA0G,CAC1G;SACA,GAAG,CAAiB,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC3C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,MAAM,EAAE;SACxB,OAAO,CACP,8HAA8H,YAAY,GAAG,CAC7I;SACA,GAAG,CAAkB,SAAS,EAAE,GAAG,QAAQ,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,+EAA+E;QAC/E,iFAAiF;QACjF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;YACD,SAAS;QACV,CAAC;QACD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ;YAAE,MAAM,cAAc,CAAC,sCAAsC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChG,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,MAAM,cAAc,CAAC,sCAAsC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClF,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf","sourcesContent":["import type { SessionTreeEntry } from \"@handy_wote/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\nimport { decodeEntry, type SessionEntryRow } from \"./session-entries.ts\";\nimport { invalidSession } from \"./shared.ts\";\n\nexport interface BranchEntryRow {\n\tentry_id: string;\n\tentry_seq: number;\n}\n\nexport async function getMaterializedBranchPathOrCompaction(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tbyId: Map<string, SessionTreeEntry>,\n): Promise<SessionTreeEntry[]> {\n\tconst branchRows = await db\n\t\t.prepare(\n\t\t\t\"SELECT entry_id, entry_seq FROM branch_entries WHERE session_id = ? AND branch_id = ? ORDER BY entry_seq\",\n\t\t)\n\t\t.all<BranchEntryRow>(sessionId, branchId);\n\tif (branchRows.length === 0) {\n\t\treturn [];\n\t}\n\tconst entryIds = branchRows.map((row) => row.entry_id);\n\tconst placeholders = entryIds.map(() => \"?\").join(\", \");\n\tconst entryRows = await db\n\t\t.prepare(\n\t\t\t`SELECT session_id, id, entry_seq, parent_id, type, timestamp, payload FROM session_entries WHERE session_id = ? AND id IN (${placeholders})`,\n\t\t)\n\t\t.all<SessionEntryRow>(sessionId, ...entryIds);\n\tconst entryRowsById = new Map(entryRows.map((row) => [row.id, row]));\n\tconst entries: SessionTreeEntry[] = [];\n\tfor (const branchRow of branchRows) {\n\t\t// leaf entries are navigation markers used to mark which branch became active;\n\t\t// they are not part of the model/context path reconstructed from branch_entries.\n\t\tconst cached = byId.get(branchRow.entry_id);\n\t\tif (cached) {\n\t\t\tif (cached.type !== \"leaf\") {\n\t\t\t\tentries.push(cached);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst entryRow = entryRowsById.get(branchRow.entry_id);\n\t\tif (!entryRow) throw invalidSession(`missing entry row for branch entry ${branchRow.entry_id}`);\n\t\ttry {\n\t\t\tconst entry = decodeEntry(entryRow);\n\t\t\tbyId.set(entry.id, entry);\n\t\t\tif (entry.type !== \"leaf\") {\n\t\t\t\tentries.push(entry);\n\t\t\t}\n\t\t} catch {\n\t\t\tthrow invalidSession(`invalid entry row for branch entry ${branchRow.entry_id}`);\n\t\t}\n\t}\n\treturn entries;\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SessionEntryCursorOptions, SessionStorage, SessionTreeEntry } from "@handy_wote/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabase, SqliteSessionMetadata } from "../types.ts";
|
|
3
|
+
export declare class SqliteSessionStorage implements SessionStorage<SqliteSessionMetadata> {
|
|
4
|
+
private readonly db;
|
|
5
|
+
private readonly metadata;
|
|
6
|
+
private byId;
|
|
7
|
+
private labelsById;
|
|
8
|
+
private currentLeafId;
|
|
9
|
+
private activeBranchId;
|
|
10
|
+
private materializedState;
|
|
11
|
+
private getPathToRootOrCompactionEntries;
|
|
12
|
+
private materializeBranch;
|
|
13
|
+
private appendToActiveBranch;
|
|
14
|
+
private constructor();
|
|
15
|
+
static open(db: SqliteDatabase, metadata: SqliteSessionMetadata): Promise<SqliteSessionStorage>;
|
|
16
|
+
static create(db: SqliteDatabase, path: string, options: {
|
|
17
|
+
cwd: string;
|
|
18
|
+
sessionId: string;
|
|
19
|
+
parentSessionId?: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}): Promise<SqliteSessionStorage>;
|
|
22
|
+
getMetadata(): Promise<SqliteSessionMetadata>;
|
|
23
|
+
getLeafId(): Promise<string | null>;
|
|
24
|
+
setLeafId(leafId: string | null): Promise<void>;
|
|
25
|
+
createEntryId(): Promise<string>;
|
|
26
|
+
appendEntry(entry: SessionTreeEntry): Promise<void>;
|
|
27
|
+
getEntry(id: string): Promise<SessionTreeEntry | undefined>;
|
|
28
|
+
findEntries<TType extends SessionTreeEntry["type"]>(type: TType): Promise<Array<Extract<SessionTreeEntry, {
|
|
29
|
+
type: TType;
|
|
30
|
+
}>>>;
|
|
31
|
+
getLabel(id: string): Promise<string | undefined>;
|
|
32
|
+
getSessionName(): Promise<string | undefined>;
|
|
33
|
+
getSessionStats(): Promise<import("@handy_wote/pi-agent-core").SessionStats>;
|
|
34
|
+
getPathToRootOrCompaction(leafId: string | null): Promise<SessionTreeEntry[]>;
|
|
35
|
+
getEntries(options?: SessionEntryCursorOptions): Promise<SessionTreeEntry[]>;
|
|
36
|
+
cleanup(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|