stitchkit 0.68.7 → 0.68.8

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.
@@ -0,0 +1,403 @@
1
+ import {
2
+ AgentAdmissionReceiptSchema,
3
+ AgentHistoryMutationSchema,
4
+ AgentRecoverableDescriptorSchema,
5
+ AgentRecoverablePageSchema,
6
+ AgentRuntimeHeadSchema,
7
+ AgentStoredRunSchema,
8
+ createAgentRuntimeStore
9
+ } from "./index-hqza5nde.js";
10
+ import {
11
+ AgentMessageSchema,
12
+ AgentRunSchema
13
+ } from "./index-ysphyxax.js";
14
+
15
+ // src/agent-runtime/sqlite.ts
16
+ import { z } from "zod";
17
+ var HeadRowSchema = z.object({ version: z.number().int().nonnegative() });
18
+ var RunRowSchema = z.object({
19
+ payload: z.string(),
20
+ terminal_assistant_payload: z.string().nullable()
21
+ });
22
+ var AdmissionRowSchema = z.object({
23
+ conversation_id: z.string(),
24
+ idempotency_key: z.string(),
25
+ input_payload: z.string(),
26
+ run_id: z.string(),
27
+ assistant_message_id: z.string()
28
+ });
29
+ var MessageRowSchema = z.object({ payload: z.string() });
30
+ var PositionedMessageRowSchema = z.object({ position: z.number().int().nonnegative() });
31
+ var RecoverableRowSchema = z.object({
32
+ conversation_id: z.string(),
33
+ run_id: z.string(),
34
+ payload: z.string()
35
+ });
36
+ var MetaRowSchema = z.object({ value: z.string() });
37
+ var TableRowSchema = z.object({ name: z.string() });
38
+ var SCHEMA_VERSION = 1;
39
+ var TABLES = [
40
+ "stitchkit_agent_runtime_heads",
41
+ "stitchkit_agent_runtime_runs",
42
+ "stitchkit_agent_runtime_admissions",
43
+ "stitchkit_agent_runtime_messages"
44
+ ];
45
+ function parseJson(value) {
46
+ return JSON.parse(value);
47
+ }
48
+ function encodeJson(value) {
49
+ return JSON.stringify(value);
50
+ }
51
+ function missing(value) {
52
+ return value === null || value === undefined;
53
+ }
54
+ function placeholders(count) {
55
+ return Array.from({ length: count }, () => "?").join(", ");
56
+ }
57
+ function parseRunRow(value) {
58
+ const row = RunRowSchema.parse(value);
59
+ return AgentStoredRunSchema.parse({
60
+ schemaVersion: 1,
61
+ run: AgentRunSchema.parse(parseJson(row.payload)),
62
+ ...row.terminal_assistant_payload === null ? {} : {
63
+ terminalAssistant: AgentMessageSchema.parse(parseJson(row.terminal_assistant_payload))
64
+ }
65
+ });
66
+ }
67
+ function recoveryCursor(conversationId, runId) {
68
+ return encodeJson([conversationId, runId]);
69
+ }
70
+ function parseRecoveryCursor(cursor) {
71
+ return z.tuple([z.string().min(1), z.string().min(1)]).parse(parseJson(cursor));
72
+ }
73
+ function initializeAgentRuntimeSqlite(database) {
74
+ const existing = database.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'stitchkit_agent_runtime_%' ORDER BY name`).all().map((row) => TableRowSchema.parse(row).name);
75
+ const hasMeta = existing.includes("stitchkit_agent_runtime_meta");
76
+ if (!hasMeta && existing.length > 0) {
77
+ throw new Error("Refusing an unversioned partial Stitchkit agent-runtime SQLite schema");
78
+ }
79
+ database.exec("BEGIN IMMEDIATE");
80
+ try {
81
+ database.exec(`
82
+ CREATE TABLE IF NOT EXISTS stitchkit_agent_runtime_meta (
83
+ key TEXT PRIMARY KEY,
84
+ value TEXT NOT NULL
85
+ );
86
+ `);
87
+ const versionRow = database.prepare("SELECT value FROM stitchkit_agent_runtime_meta WHERE key = 'schema_version'").get();
88
+ if (!missing(versionRow)) {
89
+ const version = Number(MetaRowSchema.parse(versionRow).value);
90
+ if (version !== SCHEMA_VERSION) {
91
+ throw new Error(`Unsupported Stitchkit agent-runtime SQLite schema version ${version}; expected ${SCHEMA_VERSION}`);
92
+ }
93
+ const missingTables = TABLES.filter((table) => !existing.includes(table));
94
+ if (missingTables.length > 0) {
95
+ throw new Error(`Refusing a partial Stitchkit agent-runtime SQLite schema; missing ${missingTables.join(", ")}`);
96
+ }
97
+ database.exec("COMMIT");
98
+ return;
99
+ }
100
+ if (existing.some((table) => table !== "stitchkit_agent_runtime_meta")) {
101
+ throw new Error("Refusing an unversioned partial Stitchkit agent-runtime SQLite schema");
102
+ }
103
+ database.exec(`
104
+ CREATE TABLE stitchkit_agent_runtime_heads (
105
+ conversation_id TEXT PRIMARY KEY,
106
+ version INTEGER NOT NULL CHECK (version >= 0)
107
+ );
108
+ CREATE TABLE stitchkit_agent_runtime_runs (
109
+ conversation_id TEXT NOT NULL,
110
+ run_id TEXT NOT NULL,
111
+ assistant_message_id TEXT NOT NULL,
112
+ state TEXT NOT NULL,
113
+ created_at TEXT NOT NULL,
114
+ payload TEXT NOT NULL,
115
+ terminal_assistant_payload TEXT,
116
+ PRIMARY KEY (conversation_id, run_id),
117
+ UNIQUE (conversation_id, assistant_message_id)
118
+ );
119
+ CREATE INDEX stitchkit_agent_runtime_recoverable
120
+ ON stitchkit_agent_runtime_runs (state, conversation_id, run_id);
121
+ CREATE TABLE stitchkit_agent_runtime_admissions (
122
+ conversation_id TEXT NOT NULL,
123
+ idempotency_key TEXT NOT NULL,
124
+ input_message_id TEXT NOT NULL,
125
+ run_id TEXT NOT NULL,
126
+ assistant_message_id TEXT NOT NULL,
127
+ input_payload TEXT NOT NULL,
128
+ PRIMARY KEY (conversation_id, idempotency_key),
129
+ UNIQUE (conversation_id, input_message_id)
130
+ );
131
+ CREATE TABLE stitchkit_agent_runtime_messages (
132
+ conversation_id TEXT NOT NULL,
133
+ id TEXT NOT NULL,
134
+ position INTEGER NOT NULL CHECK (position >= 0),
135
+ active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
136
+ payload TEXT NOT NULL,
137
+ PRIMARY KEY (conversation_id, id)
138
+ );
139
+ INSERT INTO stitchkit_agent_runtime_meta (key, value) VALUES ('schema_version', '1');
140
+ `);
141
+ database.exec("COMMIT");
142
+ } catch (error) {
143
+ database.exec("ROLLBACK");
144
+ throw error;
145
+ }
146
+ }
147
+ function createSqliteAgentRuntimeStore(config) {
148
+ const database = config.database;
149
+ if (config.initialize !== false) {
150
+ try {
151
+ initializeAgentRuntimeSqlite(database);
152
+ } catch (error) {
153
+ database.close();
154
+ throw error;
155
+ }
156
+ }
157
+ let closing = false;
158
+ let closed = false;
159
+ let tail = Promise.resolve();
160
+ const serial = (work) => {
161
+ if (closing || closed) {
162
+ return Promise.reject(new Error("SQLite agent-runtime store is closing"));
163
+ }
164
+ const result = tail.then(work, work);
165
+ tail = result.then(() => {
166
+ return;
167
+ }, () => {
168
+ return;
169
+ });
170
+ return result;
171
+ };
172
+ const driver = {
173
+ transaction: (work) => serial(async () => {
174
+ database.exec("BEGIN IMMEDIATE");
175
+ try {
176
+ const result = await work(database);
177
+ database.exec("COMMIT");
178
+ return result;
179
+ } catch (error) {
180
+ database.exec("ROLLBACK");
181
+ throw error;
182
+ }
183
+ }),
184
+ head: {
185
+ async load(transaction, conversationId) {
186
+ const value = transaction.prepare("SELECT version FROM stitchkit_agent_runtime_heads WHERE conversation_id = ?").get(conversationId);
187
+ if (missing(value))
188
+ return;
189
+ const row = HeadRowSchema.parse(value);
190
+ return AgentRuntimeHeadSchema.parse({
191
+ schemaVersion: 1,
192
+ conversationId,
193
+ version: row.version
194
+ });
195
+ },
196
+ async compareAndSwap(transaction, input) {
197
+ const result = transaction.prepare(`
198
+ INSERT INTO stitchkit_agent_runtime_heads (conversation_id, version)
199
+ VALUES (?, ?)
200
+ ON CONFLICT (conversation_id) DO UPDATE SET version = excluded.version
201
+ WHERE stitchkit_agent_runtime_heads.version = ?
202
+ `).run(input.conversationId, input.next.version, input.expectedVersion);
203
+ if (result.changes === 1)
204
+ return { outcome: "applied" };
205
+ const current = transaction.prepare("SELECT version FROM stitchkit_agent_runtime_heads WHERE conversation_id = ?").get(input.conversationId);
206
+ return {
207
+ outcome: "conflict",
208
+ actualVersion: missing(current) ? 0 : HeadRowSchema.parse(current).version
209
+ };
210
+ }
211
+ },
212
+ runs: {
213
+ async load(transaction, input) {
214
+ const row = transaction.prepare(`
215
+ SELECT payload, terminal_assistant_payload
216
+ FROM stitchkit_agent_runtime_runs
217
+ WHERE conversation_id = ? AND run_id = ?
218
+ `).get(input.conversationId, input.runId);
219
+ return missing(row) ? undefined : parseRunRow(row);
220
+ },
221
+ async loadByAssistantMessageId(transaction, input) {
222
+ const row = transaction.prepare(`
223
+ SELECT payload, terminal_assistant_payload
224
+ FROM stitchkit_agent_runtime_runs
225
+ WHERE conversation_id = ? AND assistant_message_id = ?
226
+ `).get(input.conversationId, input.assistantMessageId);
227
+ return missing(row) ? undefined : parseRunRow(row);
228
+ },
229
+ async loadMany(transaction, input) {
230
+ if (input.runIds.length === 0)
231
+ return [];
232
+ return transaction.prepare(`
233
+ SELECT payload, terminal_assistant_payload
234
+ FROM stitchkit_agent_runtime_runs
235
+ WHERE conversation_id = ? AND run_id IN (${placeholders(input.runIds.length)})
236
+ `).all(input.conversationId, ...input.runIds).map(parseRunRow);
237
+ },
238
+ async listActive(transaction, conversationId) {
239
+ return transaction.prepare(`
240
+ SELECT payload, terminal_assistant_payload
241
+ FROM stitchkit_agent_runtime_runs
242
+ WHERE conversation_id = ? AND state IN ('queued', 'running', 'interrupt_requested')
243
+ `).all(conversationId).map(parseRunRow);
244
+ },
245
+ async save(transaction, rawRecord) {
246
+ const record = AgentStoredRunSchema.parse(rawRecord);
247
+ transaction.prepare(`
248
+ INSERT INTO stitchkit_agent_runtime_runs (
249
+ conversation_id, run_id, assistant_message_id, state, created_at, payload,
250
+ terminal_assistant_payload
251
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
252
+ ON CONFLICT (conversation_id, run_id) DO UPDATE SET
253
+ assistant_message_id = excluded.assistant_message_id,
254
+ state = excluded.state,
255
+ created_at = excluded.created_at,
256
+ payload = excluded.payload,
257
+ terminal_assistant_payload = excluded.terminal_assistant_payload
258
+ `).run(record.run.conversationId, record.run.id, record.run.assistantMessageId, record.run.state, record.run.createdAt, encodeJson(record.run), record.terminalAssistant ? encodeJson(record.terminalAssistant) : null);
259
+ }
260
+ },
261
+ admissions: {
262
+ async load(transaction, input) {
263
+ const value = transaction.prepare(`
264
+ SELECT conversation_id, idempotency_key, input_payload, run_id, assistant_message_id
265
+ FROM stitchkit_agent_runtime_admissions
266
+ WHERE conversation_id = ? AND idempotency_key = ?
267
+ `).get(input.conversationId, input.idempotencyKey);
268
+ if (missing(value))
269
+ return;
270
+ const row = AdmissionRowSchema.parse(value);
271
+ return AgentAdmissionReceiptSchema.parse({
272
+ schemaVersion: 1,
273
+ conversationId: row.conversation_id,
274
+ idempotencyKey: row.idempotency_key,
275
+ input: AgentMessageSchema.parse(parseJson(row.input_payload)),
276
+ runId: row.run_id,
277
+ assistantMessageId: row.assistant_message_id
278
+ });
279
+ },
280
+ async loadByInputMessageId(transaction, input) {
281
+ const value = transaction.prepare(`
282
+ SELECT conversation_id, idempotency_key, input_payload, run_id, assistant_message_id
283
+ FROM stitchkit_agent_runtime_admissions
284
+ WHERE conversation_id = ? AND input_message_id = ?
285
+ `).get(input.conversationId, input.inputMessageId);
286
+ if (missing(value))
287
+ return;
288
+ const row = AdmissionRowSchema.parse(value);
289
+ return AgentAdmissionReceiptSchema.parse({
290
+ schemaVersion: 1,
291
+ conversationId: row.conversation_id,
292
+ idempotencyKey: row.idempotency_key,
293
+ input: AgentMessageSchema.parse(parseJson(row.input_payload)),
294
+ runId: row.run_id,
295
+ assistantMessageId: row.assistant_message_id
296
+ });
297
+ },
298
+ async create(transaction, rawReceipt) {
299
+ const receipt = AgentAdmissionReceiptSchema.parse(rawReceipt);
300
+ transaction.prepare(`
301
+ INSERT INTO stitchkit_agent_runtime_admissions (
302
+ conversation_id, idempotency_key, input_message_id, run_id,
303
+ assistant_message_id, input_payload
304
+ ) VALUES (?, ?, ?, ?, ?, ?)
305
+ `).run(receipt.conversationId, receipt.idempotencyKey, receipt.input.id, receipt.runId, receipt.assistantMessageId, encodeJson(receipt.input));
306
+ }
307
+ },
308
+ history: {
309
+ async load(transaction, conversationId) {
310
+ return transaction.prepare(`
311
+ SELECT payload FROM stitchkit_agent_runtime_messages
312
+ WHERE conversation_id = ? AND active = 1 ORDER BY position ASC
313
+ `).all(conversationId).map((value) => AgentMessageSchema.parse(parseJson(MessageRowSchema.parse(value).payload)));
314
+ },
315
+ async apply(transaction, rawMutation) {
316
+ const mutation = AgentHistoryMutationSchema.parse(rawMutation);
317
+ const message = mutation.type === "admit" ? mutation.input : mutation.type === "upsert-assistant" ? mutation.message : mutation.summary;
318
+ if (mutation.type === "replace-compacted-range") {
319
+ const parameters = mutation.replacedMessageIds;
320
+ const rows = transaction.prepare(`
321
+ SELECT position FROM stitchkit_agent_runtime_messages
322
+ WHERE conversation_id = ? AND active = 1
323
+ AND id IN (${placeholders(parameters.length)})
324
+ ORDER BY position ASC
325
+ `).all(message.conversationId, ...parameters).map((row) => PositionedMessageRowSchema.parse(row));
326
+ const first = rows[0];
327
+ if (!first || rows.length !== parameters.length) {
328
+ throw new Error("Compaction range changed inside the transaction");
329
+ }
330
+ transaction.prepare(`
331
+ UPDATE stitchkit_agent_runtime_messages SET active = 0
332
+ WHERE conversation_id = ? AND id IN (${placeholders(parameters.length)})
333
+ `).run(message.conversationId, ...parameters);
334
+ transaction.prepare(`
335
+ INSERT INTO stitchkit_agent_runtime_messages
336
+ (conversation_id, id, position, active, payload)
337
+ VALUES (?, ?, ?, 1, ?)
338
+ `).run(message.conversationId, message.id, first.position, encodeJson(message));
339
+ return;
340
+ }
341
+ const existing = transaction.prepare(`
342
+ SELECT position FROM stitchkit_agent_runtime_messages
343
+ WHERE conversation_id = ? AND id = ?
344
+ `).get(message.conversationId, message.id);
345
+ if (!missing(existing)) {
346
+ transaction.prepare(`
347
+ UPDATE stitchkit_agent_runtime_messages SET payload = ?, active = 1
348
+ WHERE conversation_id = ? AND id = ?
349
+ `).run(encodeJson(message), message.conversationId, message.id);
350
+ return;
351
+ }
352
+ const last = transaction.prepare(`
353
+ SELECT position FROM stitchkit_agent_runtime_messages
354
+ WHERE conversation_id = ? ORDER BY position DESC LIMIT 1
355
+ `).get(message.conversationId);
356
+ const position = missing(last) ? 0 : PositionedMessageRowSchema.parse(last).position + 1;
357
+ transaction.prepare(`
358
+ INSERT INTO stitchkit_agent_runtime_messages
359
+ (conversation_id, id, position, active, payload)
360
+ VALUES (?, ?, ?, 1, ?)
361
+ `).run(message.conversationId, message.id, position, encodeJson(message));
362
+ }
363
+ },
364
+ scanRecoverable: (input) => serial(async () => {
365
+ const cursor = input.cursor ? parseRecoveryCursor(input.cursor) : undefined;
366
+ const values = cursor ? [cursor[0], cursor[0], cursor[1], input.limit + 1] : [input.limit + 1];
367
+ const rows = database.prepare(`
368
+ SELECT conversation_id, run_id, payload
369
+ FROM stitchkit_agent_runtime_runs
370
+ WHERE state IN ('queued', 'running', 'interrupt_requested')
371
+ ${cursor ? "AND (conversation_id > ? OR (conversation_id = ? AND run_id > ?))" : ""}
372
+ ORDER BY conversation_id ASC, run_id ASC LIMIT ?
373
+ `).all(...values).map((row) => RecoverableRowSchema.parse(row));
374
+ const hasMore = rows.length > input.limit;
375
+ const pageRows = rows.slice(0, input.limit);
376
+ const items = pageRows.map((row) => {
377
+ const run = AgentRunSchema.parse(parseJson(row.payload));
378
+ return AgentRecoverableDescriptorSchema.parse({
379
+ conversationId: row.conversation_id,
380
+ run
381
+ });
382
+ });
383
+ const last = pageRows.at(-1);
384
+ return AgentRecoverablePageSchema.parse({
385
+ items,
386
+ ...hasMore && last ? { nextCursor: recoveryCursor(last.conversation_id, last.run_id) } : {}
387
+ });
388
+ })
389
+ };
390
+ return {
391
+ store: createAgentRuntimeStore(driver),
392
+ async close() {
393
+ if (closed)
394
+ return;
395
+ closing = true;
396
+ await tail;
397
+ database.close();
398
+ closed = true;
399
+ }
400
+ };
401
+ }
402
+
403
+ export { initializeAgentRuntimeSqlite, createSqliteAgentRuntimeStore };