@statelyai/agent 2.0.0-alpha.11 → 2.0.0-alpha.13
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/dist/ai-sdk.cjs +4 -5
- package/dist/ai-sdk.d.cts +7 -4
- package/dist/ai-sdk.d.mts +7 -4
- package/dist/ai-sdk.mjs +1 -2
- package/dist/{events-JiVPYrct.mjs → decision-BezSD_YC.mjs} +327 -20
- package/dist/{events-CRQj3VtP.cjs → decision-dWGhBh0P.cjs} +401 -28
- package/dist/errors-BQRk9eiZ.d.cts +19 -0
- package/dist/errors-C9rxnWbX.d.mts +19 -0
- package/dist/errors-CeSXQx0v.mjs +23 -0
- package/dist/errors-DUBBzRLP.cjs +28 -0
- package/dist/event-log-store-CNT_7F0V.cjs +452 -0
- package/dist/event-log-store-CriMgX1D.d.mts +144 -0
- package/dist/event-log-store-D7pWtIhb.mjs +411 -0
- package/dist/event-log-store-Ruq18mGp.d.cts +144 -0
- package/dist/index.cjs +1050 -705
- package/dist/index.d.cts +538 -565
- package/dist/index.d.mts +538 -565
- package/dist/index.mjs +950 -644
- package/dist/machines.cjs +752 -0
- package/dist/machines.d.cts +372 -0
- package/dist/machines.d.mts +372 -0
- package/dist/machines.mjs +741 -0
- package/dist/otel.cjs +268 -0
- package/dist/otel.d.cts +67 -0
- package/dist/otel.d.mts +67 -0
- package/dist/otel.mjs +267 -0
- package/dist/run-agent-C3mFDGTf.d.mts +1111 -0
- package/dist/run-agent-DnvtcnTZ.d.cts +1111 -0
- package/dist/setup-agent-DAZZSjDS.mjs +1711 -0
- package/dist/setup-agent-DP95MFrI.cjs +1836 -0
- package/dist/sqlite.cjs +135 -0
- package/dist/sqlite.d.cts +57 -0
- package/dist/sqlite.d.mts +57 -0
- package/dist/sqlite.mjs +133 -0
- package/dist/{text-logic-CaKqgX4Y.d.mts → text-logic-BDxwQNsD.d.cts} +155 -72
- package/dist/{text-logic-Ckhr2kKC.d.cts → text-logic-TkKPw8Aq.d.mts} +155 -72
- package/dist/{types-qm00QF91.d.mts → types-QbEfCVny.d.cts} +1 -1
- package/dist/{types-C9QiMjre.d.cts → types-_FXoFBGO.d.mts} +1 -1
- package/package.json +47 -39
- package/readme.md +49 -12
- package/schemas/agent-workflow.json +40 -21
- package/skills/generate-machine/SKILL.md +267 -0
- package/dist/adapter.cjs +0 -15
- package/dist/adapter.d.cts +0 -4
- package/dist/adapter.d.mts +0 -4
- package/dist/adapter.mjs +0 -2
- package/dist/decision-C3k4ve51.mjs +0 -227
- package/dist/decision-D8wJrM8W.cjs +0 -286
- package/dist/openai-compat.cjs +0 -309
- package/dist/openai-compat.d.cts +0 -59
- package/dist/openai-compat.d.mts +0 -59
- package/dist/openai-compat.mjs +0 -308
- package/dist/steps-BALp1eZo.d.mts +0 -198
- package/dist/steps-CVe54GPP.cjs +0 -420
- package/dist/steps-CkyyyuHd.mjs +0 -379
- package/dist/steps-MjnQI4aB.d.cts +0 -198
- package/dist/steps.cjs +0 -12
- package/dist/steps.d.cts +0 -3
- package/dist/steps.d.mts +0 -3
- package/dist/steps.mjs +0 -3
- package/dist/utils-BYqT_Dyv.d.cts +0 -108
- package/dist/utils-Do5wIJrh.d.mts +0 -108
- package/dist/zod.cjs +0 -31
- package/dist/zod.d.cts +0 -30
- package/dist/zod.d.mts +0 -30
- package/dist/zod.mjs +0 -30
package/dist/sqlite.cjs
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_event_log_store = require("./event-log-store-CNT_7F0V.cjs");
|
|
3
|
+
//#region src/sqlite/index.ts
|
|
4
|
+
function openDatabase(path) {
|
|
5
|
+
const sqlite = process.getBuiltinModule?.("node:sqlite");
|
|
6
|
+
if (!sqlite?.DatabaseSync) throw new Error("createSqlite*Store: 'node:sqlite' is unavailable in this runtime; Node >= 22.18 is required.");
|
|
7
|
+
return new sqlite.DatabaseSync(path);
|
|
8
|
+
}
|
|
9
|
+
function quoteIdentifier(name) {
|
|
10
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error(`Invalid SQLite table name '${name}': use letters, digits, and underscores only.`);
|
|
11
|
+
return `"${name}"`;
|
|
12
|
+
}
|
|
13
|
+
function resolveDatabase(database) {
|
|
14
|
+
return typeof database === "string" ? {
|
|
15
|
+
db: openDatabase(database),
|
|
16
|
+
owned: true
|
|
17
|
+
} : {
|
|
18
|
+
db: database,
|
|
19
|
+
owned: false
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* An {@link AgentEventLogStore} on one SQLite table, keyed by
|
|
24
|
+
* `(thread_id, idx)` with a unique `(thread_id, entry_id)` index. Entries are
|
|
25
|
+
* stored as JSON text, so reads and writes are deep copies by construction.
|
|
26
|
+
* `append` runs its length check and inserts inside one `BEGIN IMMEDIATE`
|
|
27
|
+
* transaction: `node:sqlite` is synchronous, so no `await` interleaves between
|
|
28
|
+
* the check and the write and racing appends resolve to exactly one winner.
|
|
29
|
+
*/
|
|
30
|
+
function createSqliteEventLogStore(options) {
|
|
31
|
+
const { db, owned } = resolveDatabase(options.database);
|
|
32
|
+
const table = quoteIdentifier(options.tableName ?? "agent_event_log");
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
35
|
+
thread_id TEXT NOT NULL,
|
|
36
|
+
idx INTEGER NOT NULL,
|
|
37
|
+
entry_id TEXT NOT NULL,
|
|
38
|
+
entry TEXT NOT NULL,
|
|
39
|
+
PRIMARY KEY (thread_id, idx)
|
|
40
|
+
);
|
|
41
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdentifier(`${options.tableName ?? "agent_event_log"}_thread_entry_id`)} ON ${table} (thread_id, entry_id);
|
|
42
|
+
`);
|
|
43
|
+
const lengthOf = (threadId) => {
|
|
44
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM ${table} WHERE thread_id = ?`).get(threadId);
|
|
45
|
+
return Number(row?.n ?? 0);
|
|
46
|
+
};
|
|
47
|
+
const transact = (fn) => {
|
|
48
|
+
db.exec("BEGIN IMMEDIATE");
|
|
49
|
+
try {
|
|
50
|
+
const result = fn();
|
|
51
|
+
db.exec("COMMIT");
|
|
52
|
+
return result;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
try {
|
|
55
|
+
db.exec("ROLLBACK");
|
|
56
|
+
} catch {}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
async append({ threadId, expectedIndex, entries }) {
|
|
62
|
+
for (const entry of entries) require_event_log_store.assertAgentLogEntry(entry);
|
|
63
|
+
for (let i = 0; i < entries.length; i++) if (entries[i].index !== expectedIndex + i) throw new Error(`AgentEventLogStore.append: entry.index (${entries[i].index}) must be contiguous from expectedIndex (${expectedIndex}); expected ${expectedIndex + i} at position ${i} on thread "${threadId}".`);
|
|
64
|
+
transact(() => {
|
|
65
|
+
const length = lengthOf(threadId);
|
|
66
|
+
if (length !== expectedIndex) throw new require_event_log_store.AgentEventLogConflictError(threadId, expectedIndex, length);
|
|
67
|
+
const insert = db.prepare(`INSERT INTO ${table} (thread_id, idx, entry_id, entry) VALUES (?, ?, ?, ?)`);
|
|
68
|
+
for (const entry of entries) try {
|
|
69
|
+
insert.run(threadId, entry.index, entry.id, JSON.stringify(entry));
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (String(error.message).includes("UNIQUE")) throw new Error(`AgentEventLogStore.append: duplicate event id "${entry.id}" in thread "${threadId}".`);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
async read(threadId, options) {
|
|
77
|
+
const from = options?.from ?? 0;
|
|
78
|
+
return db.prepare(`SELECT entry FROM ${table} WHERE thread_id = ? AND idx >= ? ORDER BY idx ASC`).all(threadId, from).map((row) => JSON.parse(row.entry));
|
|
79
|
+
},
|
|
80
|
+
async length(threadId) {
|
|
81
|
+
return lengthOf(threadId);
|
|
82
|
+
},
|
|
83
|
+
async fork({ threadId, newThreadId, upToIndex, atEventId }) {
|
|
84
|
+
if (upToIndex !== void 0 && atEventId !== void 0) throw new Error("AgentEventLogStore.fork: pass either upToIndex or atEventId, not both.");
|
|
85
|
+
transact(() => {
|
|
86
|
+
if (lengthOf(newThreadId) > 0) throw new Error(`AgentEventLogStore.fork: newThreadId "${newThreadId}" already has entries.`);
|
|
87
|
+
const sourceLength = lengthOf(threadId);
|
|
88
|
+
if (sourceLength === 0) throw new Error(`AgentEventLogStore.fork: unknown source thread "${threadId}".`);
|
|
89
|
+
let upTo = upToIndex ?? sourceLength;
|
|
90
|
+
if (atEventId !== void 0) {
|
|
91
|
+
const row = db.prepare(`SELECT idx FROM ${table} WHERE thread_id = ? AND entry_id = ?`).get(threadId, atEventId);
|
|
92
|
+
if (!row) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" has no event id "${atEventId}".`);
|
|
93
|
+
upTo = Number(row.idx) + 1;
|
|
94
|
+
}
|
|
95
|
+
if (upTo < 0 || upTo > sourceLength) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" (length ${sourceLength}) has no index ${upTo} to fork up to.`);
|
|
96
|
+
db.prepare(`INSERT INTO ${table} (thread_id, idx, entry_id, entry)
|
|
97
|
+
SELECT ?, idx, entry_id, entry FROM ${table} WHERE thread_id = ? AND idx < ?`).run(newThreadId, threadId, upTo);
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
close() {
|
|
101
|
+
if (owned) db.close();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* An {@link AgentSnapshotStore} on one SQLite table: a `key -> JSON` upsert.
|
|
107
|
+
* Snapshots are the compaction cache over the event log, so `save` overwrites
|
|
108
|
+
* whatever the id previously held.
|
|
109
|
+
*/
|
|
110
|
+
function createSqliteSnapshotStore(options) {
|
|
111
|
+
const { db, owned } = resolveDatabase(options.database);
|
|
112
|
+
const table = quoteIdentifier(options.tableName ?? "agent_snapshots");
|
|
113
|
+
db.exec(`
|
|
114
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
115
|
+
id TEXT PRIMARY KEY,
|
|
116
|
+
snapshot TEXT NOT NULL
|
|
117
|
+
);
|
|
118
|
+
`);
|
|
119
|
+
return {
|
|
120
|
+
async load(id) {
|
|
121
|
+
const row = db.prepare(`SELECT snapshot FROM ${table} WHERE id = ?`).get(id);
|
|
122
|
+
return row === void 0 ? void 0 : JSON.parse(row.snapshot);
|
|
123
|
+
},
|
|
124
|
+
async save(id, snapshot) {
|
|
125
|
+
db.prepare(`INSERT INTO ${table} (id, snapshot) VALUES (?, ?)
|
|
126
|
+
ON CONFLICT(id) DO UPDATE SET snapshot = excluded.snapshot`).run(id, JSON.stringify(snapshot));
|
|
127
|
+
},
|
|
128
|
+
close() {
|
|
129
|
+
if (owned) db.close();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
exports.createSqliteEventLogStore = createSqliteEventLogStore;
|
|
135
|
+
exports.createSqliteSnapshotStore = createSqliteSnapshotStore;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { r as AgentSnapshotStore } from "./types-QbEfCVny.cjs";
|
|
2
|
+
import { r as AgentEventLogStore } from "./event-log-store-Ruq18mGp.cjs";
|
|
3
|
+
|
|
4
|
+
//#region src/sqlite/index.d.ts
|
|
5
|
+
/** A prepared statement, structurally compatible with `node:sqlite`'s `StatementSync`. */
|
|
6
|
+
interface SqliteStatement {
|
|
7
|
+
run(...params: any[]): unknown;
|
|
8
|
+
all(...params: any[]): unknown[];
|
|
9
|
+
get(...params: any[]): unknown;
|
|
10
|
+
}
|
|
11
|
+
/** A database handle, structurally compatible with `node:sqlite`'s `DatabaseSync`. */
|
|
12
|
+
interface SqliteDatabase {
|
|
13
|
+
prepare(sql: string): SqliteStatement;
|
|
14
|
+
exec(sql: string): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
/** A store that owns nothing unless it opened the file itself.
|
|
18
|
+
*
|
|
19
|
+
* Exported because it appears in the store factories' return types — without
|
|
20
|
+
* it, consumers' declaration emit fails with TS4058 "cannot be named". */
|
|
21
|
+
interface Closable {
|
|
22
|
+
/**
|
|
23
|
+
* Closes the underlying database — but only when this store opened it from a
|
|
24
|
+
* path. When a `DatabaseSync` handle was passed in, the caller owns it and
|
|
25
|
+
* this is a no-op.
|
|
26
|
+
*/
|
|
27
|
+
close(): void;
|
|
28
|
+
}
|
|
29
|
+
interface SqliteStoreOptions {
|
|
30
|
+
/** A file path (or `':memory:'`) to open, or an existing `node:sqlite` handle to share. */
|
|
31
|
+
database: string | SqliteDatabase;
|
|
32
|
+
}
|
|
33
|
+
interface SqliteEventLogStoreOptions extends SqliteStoreOptions {
|
|
34
|
+
/** Table holding the log entries. Defaults to `agent_event_log`. */
|
|
35
|
+
tableName?: string;
|
|
36
|
+
}
|
|
37
|
+
interface SqliteSnapshotStoreOptions extends SqliteStoreOptions {
|
|
38
|
+
/** Table holding the snapshots. Defaults to `agent_snapshots`. */
|
|
39
|
+
tableName?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* An {@link AgentEventLogStore} on one SQLite table, keyed by
|
|
43
|
+
* `(thread_id, idx)` with a unique `(thread_id, entry_id)` index. Entries are
|
|
44
|
+
* stored as JSON text, so reads and writes are deep copies by construction.
|
|
45
|
+
* `append` runs its length check and inserts inside one `BEGIN IMMEDIATE`
|
|
46
|
+
* transaction: `node:sqlite` is synchronous, so no `await` interleaves between
|
|
47
|
+
* the check and the write and racing appends resolve to exactly one winner.
|
|
48
|
+
*/
|
|
49
|
+
declare function createSqliteEventLogStore(options: SqliteEventLogStoreOptions): AgentEventLogStore & Closable;
|
|
50
|
+
/**
|
|
51
|
+
* An {@link AgentSnapshotStore} on one SQLite table: a `key -> JSON` upsert.
|
|
52
|
+
* Snapshots are the compaction cache over the event log, so `save` overwrites
|
|
53
|
+
* whatever the id previously held.
|
|
54
|
+
*/
|
|
55
|
+
declare function createSqliteSnapshotStore(options: SqliteSnapshotStoreOptions): AgentSnapshotStore & Closable;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { Closable, SqliteDatabase, SqliteEventLogStoreOptions, SqliteSnapshotStoreOptions, SqliteStatement, SqliteStoreOptions, createSqliteEventLogStore, createSqliteSnapshotStore };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { r as AgentSnapshotStore } from "./types-_FXoFBGO.mjs";
|
|
2
|
+
import { r as AgentEventLogStore } from "./event-log-store-CriMgX1D.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/sqlite/index.d.ts
|
|
5
|
+
/** A prepared statement, structurally compatible with `node:sqlite`'s `StatementSync`. */
|
|
6
|
+
interface SqliteStatement {
|
|
7
|
+
run(...params: any[]): unknown;
|
|
8
|
+
all(...params: any[]): unknown[];
|
|
9
|
+
get(...params: any[]): unknown;
|
|
10
|
+
}
|
|
11
|
+
/** A database handle, structurally compatible with `node:sqlite`'s `DatabaseSync`. */
|
|
12
|
+
interface SqliteDatabase {
|
|
13
|
+
prepare(sql: string): SqliteStatement;
|
|
14
|
+
exec(sql: string): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
/** A store that owns nothing unless it opened the file itself.
|
|
18
|
+
*
|
|
19
|
+
* Exported because it appears in the store factories' return types — without
|
|
20
|
+
* it, consumers' declaration emit fails with TS4058 "cannot be named". */
|
|
21
|
+
interface Closable {
|
|
22
|
+
/**
|
|
23
|
+
* Closes the underlying database — but only when this store opened it from a
|
|
24
|
+
* path. When a `DatabaseSync` handle was passed in, the caller owns it and
|
|
25
|
+
* this is a no-op.
|
|
26
|
+
*/
|
|
27
|
+
close(): void;
|
|
28
|
+
}
|
|
29
|
+
interface SqliteStoreOptions {
|
|
30
|
+
/** A file path (or `':memory:'`) to open, or an existing `node:sqlite` handle to share. */
|
|
31
|
+
database: string | SqliteDatabase;
|
|
32
|
+
}
|
|
33
|
+
interface SqliteEventLogStoreOptions extends SqliteStoreOptions {
|
|
34
|
+
/** Table holding the log entries. Defaults to `agent_event_log`. */
|
|
35
|
+
tableName?: string;
|
|
36
|
+
}
|
|
37
|
+
interface SqliteSnapshotStoreOptions extends SqliteStoreOptions {
|
|
38
|
+
/** Table holding the snapshots. Defaults to `agent_snapshots`. */
|
|
39
|
+
tableName?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* An {@link AgentEventLogStore} on one SQLite table, keyed by
|
|
43
|
+
* `(thread_id, idx)` with a unique `(thread_id, entry_id)` index. Entries are
|
|
44
|
+
* stored as JSON text, so reads and writes are deep copies by construction.
|
|
45
|
+
* `append` runs its length check and inserts inside one `BEGIN IMMEDIATE`
|
|
46
|
+
* transaction: `node:sqlite` is synchronous, so no `await` interleaves between
|
|
47
|
+
* the check and the write and racing appends resolve to exactly one winner.
|
|
48
|
+
*/
|
|
49
|
+
declare function createSqliteEventLogStore(options: SqliteEventLogStoreOptions): AgentEventLogStore & Closable;
|
|
50
|
+
/**
|
|
51
|
+
* An {@link AgentSnapshotStore} on one SQLite table: a `key -> JSON` upsert.
|
|
52
|
+
* Snapshots are the compaction cache over the event log, so `save` overwrites
|
|
53
|
+
* whatever the id previously held.
|
|
54
|
+
*/
|
|
55
|
+
declare function createSqliteSnapshotStore(options: SqliteSnapshotStoreOptions): AgentSnapshotStore & Closable;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { Closable, SqliteDatabase, SqliteEventLogStoreOptions, SqliteSnapshotStoreOptions, SqliteStatement, SqliteStoreOptions, createSqliteEventLogStore, createSqliteSnapshotStore };
|
package/dist/sqlite.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { i as assertAgentLogEntry, n as AgentEventLogConflictError } from "./event-log-store-D7pWtIhb.mjs";
|
|
2
|
+
//#region src/sqlite/index.ts
|
|
3
|
+
function openDatabase(path) {
|
|
4
|
+
const sqlite = process.getBuiltinModule?.("node:sqlite");
|
|
5
|
+
if (!sqlite?.DatabaseSync) throw new Error("createSqlite*Store: 'node:sqlite' is unavailable in this runtime; Node >= 22.18 is required.");
|
|
6
|
+
return new sqlite.DatabaseSync(path);
|
|
7
|
+
}
|
|
8
|
+
function quoteIdentifier(name) {
|
|
9
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error(`Invalid SQLite table name '${name}': use letters, digits, and underscores only.`);
|
|
10
|
+
return `"${name}"`;
|
|
11
|
+
}
|
|
12
|
+
function resolveDatabase(database) {
|
|
13
|
+
return typeof database === "string" ? {
|
|
14
|
+
db: openDatabase(database),
|
|
15
|
+
owned: true
|
|
16
|
+
} : {
|
|
17
|
+
db: database,
|
|
18
|
+
owned: false
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An {@link AgentEventLogStore} on one SQLite table, keyed by
|
|
23
|
+
* `(thread_id, idx)` with a unique `(thread_id, entry_id)` index. Entries are
|
|
24
|
+
* stored as JSON text, so reads and writes are deep copies by construction.
|
|
25
|
+
* `append` runs its length check and inserts inside one `BEGIN IMMEDIATE`
|
|
26
|
+
* transaction: `node:sqlite` is synchronous, so no `await` interleaves between
|
|
27
|
+
* the check and the write and racing appends resolve to exactly one winner.
|
|
28
|
+
*/
|
|
29
|
+
function createSqliteEventLogStore(options) {
|
|
30
|
+
const { db, owned } = resolveDatabase(options.database);
|
|
31
|
+
const table = quoteIdentifier(options.tableName ?? "agent_event_log");
|
|
32
|
+
db.exec(`
|
|
33
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
34
|
+
thread_id TEXT NOT NULL,
|
|
35
|
+
idx INTEGER NOT NULL,
|
|
36
|
+
entry_id TEXT NOT NULL,
|
|
37
|
+
entry TEXT NOT NULL,
|
|
38
|
+
PRIMARY KEY (thread_id, idx)
|
|
39
|
+
);
|
|
40
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdentifier(`${options.tableName ?? "agent_event_log"}_thread_entry_id`)} ON ${table} (thread_id, entry_id);
|
|
41
|
+
`);
|
|
42
|
+
const lengthOf = (threadId) => {
|
|
43
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM ${table} WHERE thread_id = ?`).get(threadId);
|
|
44
|
+
return Number(row?.n ?? 0);
|
|
45
|
+
};
|
|
46
|
+
const transact = (fn) => {
|
|
47
|
+
db.exec("BEGIN IMMEDIATE");
|
|
48
|
+
try {
|
|
49
|
+
const result = fn();
|
|
50
|
+
db.exec("COMMIT");
|
|
51
|
+
return result;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
try {
|
|
54
|
+
db.exec("ROLLBACK");
|
|
55
|
+
} catch {}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
async append({ threadId, expectedIndex, entries }) {
|
|
61
|
+
for (const entry of entries) assertAgentLogEntry(entry);
|
|
62
|
+
for (let i = 0; i < entries.length; i++) if (entries[i].index !== expectedIndex + i) throw new Error(`AgentEventLogStore.append: entry.index (${entries[i].index}) must be contiguous from expectedIndex (${expectedIndex}); expected ${expectedIndex + i} at position ${i} on thread "${threadId}".`);
|
|
63
|
+
transact(() => {
|
|
64
|
+
const length = lengthOf(threadId);
|
|
65
|
+
if (length !== expectedIndex) throw new AgentEventLogConflictError(threadId, expectedIndex, length);
|
|
66
|
+
const insert = db.prepare(`INSERT INTO ${table} (thread_id, idx, entry_id, entry) VALUES (?, ?, ?, ?)`);
|
|
67
|
+
for (const entry of entries) try {
|
|
68
|
+
insert.run(threadId, entry.index, entry.id, JSON.stringify(entry));
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (String(error.message).includes("UNIQUE")) throw new Error(`AgentEventLogStore.append: duplicate event id "${entry.id}" in thread "${threadId}".`);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
async read(threadId, options) {
|
|
76
|
+
const from = options?.from ?? 0;
|
|
77
|
+
return db.prepare(`SELECT entry FROM ${table} WHERE thread_id = ? AND idx >= ? ORDER BY idx ASC`).all(threadId, from).map((row) => JSON.parse(row.entry));
|
|
78
|
+
},
|
|
79
|
+
async length(threadId) {
|
|
80
|
+
return lengthOf(threadId);
|
|
81
|
+
},
|
|
82
|
+
async fork({ threadId, newThreadId, upToIndex, atEventId }) {
|
|
83
|
+
if (upToIndex !== void 0 && atEventId !== void 0) throw new Error("AgentEventLogStore.fork: pass either upToIndex or atEventId, not both.");
|
|
84
|
+
transact(() => {
|
|
85
|
+
if (lengthOf(newThreadId) > 0) throw new Error(`AgentEventLogStore.fork: newThreadId "${newThreadId}" already has entries.`);
|
|
86
|
+
const sourceLength = lengthOf(threadId);
|
|
87
|
+
if (sourceLength === 0) throw new Error(`AgentEventLogStore.fork: unknown source thread "${threadId}".`);
|
|
88
|
+
let upTo = upToIndex ?? sourceLength;
|
|
89
|
+
if (atEventId !== void 0) {
|
|
90
|
+
const row = db.prepare(`SELECT idx FROM ${table} WHERE thread_id = ? AND entry_id = ?`).get(threadId, atEventId);
|
|
91
|
+
if (!row) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" has no event id "${atEventId}".`);
|
|
92
|
+
upTo = Number(row.idx) + 1;
|
|
93
|
+
}
|
|
94
|
+
if (upTo < 0 || upTo > sourceLength) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" (length ${sourceLength}) has no index ${upTo} to fork up to.`);
|
|
95
|
+
db.prepare(`INSERT INTO ${table} (thread_id, idx, entry_id, entry)
|
|
96
|
+
SELECT ?, idx, entry_id, entry FROM ${table} WHERE thread_id = ? AND idx < ?`).run(newThreadId, threadId, upTo);
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
close() {
|
|
100
|
+
if (owned) db.close();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* An {@link AgentSnapshotStore} on one SQLite table: a `key -> JSON` upsert.
|
|
106
|
+
* Snapshots are the compaction cache over the event log, so `save` overwrites
|
|
107
|
+
* whatever the id previously held.
|
|
108
|
+
*/
|
|
109
|
+
function createSqliteSnapshotStore(options) {
|
|
110
|
+
const { db, owned } = resolveDatabase(options.database);
|
|
111
|
+
const table = quoteIdentifier(options.tableName ?? "agent_snapshots");
|
|
112
|
+
db.exec(`
|
|
113
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
114
|
+
id TEXT PRIMARY KEY,
|
|
115
|
+
snapshot TEXT NOT NULL
|
|
116
|
+
);
|
|
117
|
+
`);
|
|
118
|
+
return {
|
|
119
|
+
async load(id) {
|
|
120
|
+
const row = db.prepare(`SELECT snapshot FROM ${table} WHERE id = ?`).get(id);
|
|
121
|
+
return row === void 0 ? void 0 : JSON.parse(row.snapshot);
|
|
122
|
+
},
|
|
123
|
+
async save(id, snapshot) {
|
|
124
|
+
db.prepare(`INSERT INTO ${table} (id, snapshot) VALUES (?, ?)
|
|
125
|
+
ON CONFLICT(id) DO UPDATE SET snapshot = excluded.snapshot`).run(id, JSON.stringify(snapshot));
|
|
126
|
+
},
|
|
127
|
+
close() {
|
|
128
|
+
if (owned) db.close();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { createSqliteEventLogStore, createSqliteSnapshotStore };
|