chatkit-bun 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -0
- package/package.json +40 -0
- package/src/actions.ts +39 -0
- package/src/agents/accumulate.ts +43 -0
- package/src/agents/annotations.ts +157 -0
- package/src/agents/context.ts +190 -0
- package/src/agents/converter.ts +290 -0
- package/src/agents/index.ts +25 -0
- package/src/agents/stream.ts +1053 -0
- package/src/agents/types.ts +30 -0
- package/src/agents/workflows.ts +220 -0
- package/src/errors.ts +19 -0
- package/src/http.ts +60 -0
- package/src/index.ts +11 -0
- package/src/serialization.ts +75 -0
- package/src/server.ts +874 -0
- package/src/sqlite-store.ts +400 -0
- package/src/store.ts +98 -0
- package/src/types/core.ts +322 -0
- package/src/types/server.ts +396 -0
- package/src/widgets/components.ts +188 -0
- package/src/widgets/diff.ts +151 -0
- package/src/widgets/index.ts +6 -0
- package/src/widgets/serialization.ts +46 -0
- package/src/widgets/stream.ts +104 -0
- package/src/widgets/template.ts +180 -0
- package/src/widgets/types.ts +52 -0
- package/types/actions.d.ts +19 -0
- package/types/agents/accumulate.d.ts +4 -0
- package/types/agents/annotations.d.ts +21 -0
- package/types/agents/context.d.ts +35 -0
- package/types/agents/converter.d.ts +60 -0
- package/types/agents/index.d.ts +9 -0
- package/types/agents/stream.d.ts +4 -0
- package/types/agents/types.d.ts +26 -0
- package/types/agents/workflows.d.ts +34 -0
- package/types/errors.d.ts +11 -0
- package/types/http.d.ts +6 -0
- package/types/index.d.ts +11 -0
- package/types/serialization.d.ts +8 -0
- package/types/server.d.ts +73 -0
- package/types/sqlite-store.d.ts +43 -0
- package/types/store.d.ts +45 -0
- package/types/types/core.d.ts +1220 -0
- package/types/types/server.d.ts +5841 -0
- package/types/widgets/components.d.ts +144 -0
- package/types/widgets/diff.d.ts +7 -0
- package/types/widgets/index.d.ts +6 -0
- package/types/widgets/serialization.d.ts +2 -0
- package/types/widgets/stream.d.ts +10 -0
- package/types/widgets/template.d.ts +19 -0
- package/types/widgets/types.d.ts +24 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
|
|
3
|
+
import { NotFoundError } from "./errors";
|
|
4
|
+
import { BaseStore } from "./store";
|
|
5
|
+
import {
|
|
6
|
+
AttachmentSchema,
|
|
7
|
+
ThreadItemSchema,
|
|
8
|
+
ThreadMetadataSchema,
|
|
9
|
+
type Attachment,
|
|
10
|
+
type Page,
|
|
11
|
+
type ThreadItem,
|
|
12
|
+
type ThreadMetadata,
|
|
13
|
+
} from "./types/core";
|
|
14
|
+
|
|
15
|
+
export interface SQLiteStoreOptions<TContext> {
|
|
16
|
+
path?: string;
|
|
17
|
+
getUserId(context: TContext): string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface DataRow {
|
|
21
|
+
data: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface CursorRow {
|
|
25
|
+
id: string;
|
|
26
|
+
created_at: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface OrderedDataRow extends DataRow, CursorRow {}
|
|
30
|
+
|
|
31
|
+
export class SQLiteStore<TContext = unknown> extends BaseStore<TContext> {
|
|
32
|
+
private readonly db: Database;
|
|
33
|
+
private readonly getUserId: (context: TContext) => string;
|
|
34
|
+
|
|
35
|
+
constructor(options: SQLiteStoreOptions<TContext>) {
|
|
36
|
+
super();
|
|
37
|
+
this.db = new Database(options.path ?? "chatkit.sqlite");
|
|
38
|
+
this.getUserId = options.getUserId;
|
|
39
|
+
this.createSchema();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
close(): void {
|
|
43
|
+
this.db.close();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async loadThread(threadId: string, context: TContext): Promise<ThreadMetadata> {
|
|
47
|
+
const row = this.threadRow(this.userId(context), threadId);
|
|
48
|
+
return this.parseThread(row.data);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async saveThread(thread: ThreadMetadata, context: TContext): Promise<void> {
|
|
52
|
+
const userId = this.userId(context);
|
|
53
|
+
const parsed = ThreadMetadataSchema.parse(thread);
|
|
54
|
+
|
|
55
|
+
this.db
|
|
56
|
+
.query(
|
|
57
|
+
`INSERT INTO threads (user_id, id, created_at, data)
|
|
58
|
+
VALUES (?, ?, ?, ?)
|
|
59
|
+
ON CONFLICT(user_id, id) DO UPDATE SET
|
|
60
|
+
created_at = excluded.created_at,
|
|
61
|
+
data = excluded.data`,
|
|
62
|
+
)
|
|
63
|
+
.run(userId, parsed.id, parsed.created_at, JSON.stringify(parsed));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async loadThreads(
|
|
67
|
+
limit: number,
|
|
68
|
+
after: string | null,
|
|
69
|
+
order: "asc" | "desc",
|
|
70
|
+
context: TContext,
|
|
71
|
+
): Promise<Page<ThreadMetadata>> {
|
|
72
|
+
this.assertPositiveLimit(limit);
|
|
73
|
+
const userId = this.userId(context);
|
|
74
|
+
const rows = after
|
|
75
|
+
? this.threadRowsAfter(userId, after, limit, order)
|
|
76
|
+
: this.threadRows(userId, limit, order);
|
|
77
|
+
|
|
78
|
+
return this.page(rows, limit, (row) => this.parseThread(row.data));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async addThreadItem(threadId: string, item: ThreadItem, context: TContext): Promise<void> {
|
|
82
|
+
const userId = this.userId(context);
|
|
83
|
+
this.threadRow(userId, threadId);
|
|
84
|
+
const parsed = ThreadItemSchema.parse({ ...item, thread_id: threadId });
|
|
85
|
+
|
|
86
|
+
this.db
|
|
87
|
+
.query(
|
|
88
|
+
`INSERT INTO items (user_id, thread_id, id, created_at, data)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
90
|
+
)
|
|
91
|
+
.run(userId, threadId, parsed.id, parsed.created_at, JSON.stringify(parsed));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async saveItem(threadId: string, item: ThreadItem, context: TContext): Promise<void> {
|
|
95
|
+
const userId = this.userId(context);
|
|
96
|
+
this.threadRow(userId, threadId);
|
|
97
|
+
const parsed = ThreadItemSchema.parse({ ...item, thread_id: threadId });
|
|
98
|
+
|
|
99
|
+
this.db
|
|
100
|
+
.query(
|
|
101
|
+
`INSERT INTO items (user_id, thread_id, id, created_at, data)
|
|
102
|
+
VALUES (?, ?, ?, ?, ?)
|
|
103
|
+
ON CONFLICT(user_id, thread_id, id) DO UPDATE SET
|
|
104
|
+
created_at = excluded.created_at,
|
|
105
|
+
data = excluded.data`,
|
|
106
|
+
)
|
|
107
|
+
.run(userId, threadId, parsed.id, parsed.created_at, JSON.stringify(parsed));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async loadItem(threadId: string, itemId: string, context: TContext): Promise<ThreadItem> {
|
|
111
|
+
const userId = this.userId(context);
|
|
112
|
+
this.threadRow(userId, threadId);
|
|
113
|
+
const row = this.itemRow(userId, threadId, itemId);
|
|
114
|
+
return this.parseItem(row.data);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async loadThreadItems(
|
|
118
|
+
threadId: string,
|
|
119
|
+
after: string | null,
|
|
120
|
+
limit: number,
|
|
121
|
+
order: "asc" | "desc",
|
|
122
|
+
context: TContext,
|
|
123
|
+
): Promise<Page<ThreadItem>> {
|
|
124
|
+
this.assertPositiveLimit(limit);
|
|
125
|
+
const userId = this.userId(context);
|
|
126
|
+
this.threadRow(userId, threadId);
|
|
127
|
+
const rows = after
|
|
128
|
+
? this.itemRowsAfter(userId, threadId, after, limit, order)
|
|
129
|
+
: this.itemRows(userId, threadId, limit, order);
|
|
130
|
+
|
|
131
|
+
return this.page(rows, limit, (row) => this.parseItem(row.data));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async deleteThread(threadId: string, context: TContext): Promise<void> {
|
|
135
|
+
const userId = this.userId(context);
|
|
136
|
+
this.threadRow(userId, threadId);
|
|
137
|
+
this.db.query("DELETE FROM threads WHERE user_id = ? AND id = ?").run(userId, threadId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async deleteThreadItem(threadId: string, itemId: string, context: TContext): Promise<void> {
|
|
141
|
+
const userId = this.userId(context);
|
|
142
|
+
this.threadRow(userId, threadId);
|
|
143
|
+
this.itemRow(userId, threadId, itemId);
|
|
144
|
+
this.db
|
|
145
|
+
.query("DELETE FROM items WHERE user_id = ? AND thread_id = ? AND id = ?")
|
|
146
|
+
.run(userId, threadId, itemId);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async saveAttachment(attachment: Attachment, context: TContext): Promise<void> {
|
|
150
|
+
const userId = this.userId(context);
|
|
151
|
+
const parsed = AttachmentSchema.parse(attachment);
|
|
152
|
+
|
|
153
|
+
this.db
|
|
154
|
+
.query(
|
|
155
|
+
`INSERT OR REPLACE INTO attachments (user_id, id, data)
|
|
156
|
+
VALUES (?, ?, ?)`,
|
|
157
|
+
)
|
|
158
|
+
.run(userId, parsed.id, JSON.stringify(parsed));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async loadAttachment(attachmentId: string, context: TContext): Promise<Attachment> {
|
|
162
|
+
const row = this.attachmentRow(this.userId(context), attachmentId);
|
|
163
|
+
return this.parseAttachment(row.data);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async deleteAttachment(attachmentId: string, context: TContext): Promise<void> {
|
|
167
|
+
const userId = this.userId(context);
|
|
168
|
+
this.attachmentRow(userId, attachmentId);
|
|
169
|
+
this.db.query("DELETE FROM attachments WHERE user_id = ? AND id = ?").run(userId, attachmentId);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private createSchema(): void {
|
|
173
|
+
this.db.exec(`
|
|
174
|
+
PRAGMA foreign_keys = ON;
|
|
175
|
+
|
|
176
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
177
|
+
user_id TEXT NOT NULL,
|
|
178
|
+
id TEXT NOT NULL,
|
|
179
|
+
created_at TEXT NOT NULL,
|
|
180
|
+
data TEXT NOT NULL,
|
|
181
|
+
PRIMARY KEY (user_id, id)
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
CREATE INDEX IF NOT EXISTS threads_user_created_at_idx
|
|
185
|
+
ON threads (user_id, created_at, id);
|
|
186
|
+
|
|
187
|
+
CREATE TABLE IF NOT EXISTS items (
|
|
188
|
+
user_id TEXT NOT NULL,
|
|
189
|
+
thread_id TEXT NOT NULL,
|
|
190
|
+
id TEXT NOT NULL,
|
|
191
|
+
created_at TEXT NOT NULL,
|
|
192
|
+
data TEXT NOT NULL,
|
|
193
|
+
PRIMARY KEY (user_id, thread_id, id),
|
|
194
|
+
FOREIGN KEY (user_id, thread_id)
|
|
195
|
+
REFERENCES threads (user_id, id)
|
|
196
|
+
ON DELETE CASCADE
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
CREATE INDEX IF NOT EXISTS items_thread_created_at_idx
|
|
200
|
+
ON items (user_id, thread_id, created_at, id);
|
|
201
|
+
|
|
202
|
+
CREATE TABLE IF NOT EXISTS attachments (
|
|
203
|
+
user_id TEXT NOT NULL,
|
|
204
|
+
id TEXT NOT NULL,
|
|
205
|
+
data TEXT NOT NULL,
|
|
206
|
+
PRIMARY KEY (user_id, id)
|
|
207
|
+
);
|
|
208
|
+
`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private userId(context: TContext): string {
|
|
212
|
+
return this.getUserId(context);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private threadRow(userId: string, threadId: string): DataRow {
|
|
216
|
+
const row = this.db
|
|
217
|
+
.query<DataRow, [string, string]>("SELECT data FROM threads WHERE user_id = ? AND id = ?")
|
|
218
|
+
.get(userId, threadId);
|
|
219
|
+
|
|
220
|
+
if (!row) {
|
|
221
|
+
throw new NotFoundError(`Thread not found: ${threadId}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return row;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private itemRow(userId: string, threadId: string, itemId: string): DataRow {
|
|
228
|
+
const row = this.db
|
|
229
|
+
.query<DataRow, [string, string, string]>(
|
|
230
|
+
"SELECT data FROM items WHERE user_id = ? AND thread_id = ? AND id = ?",
|
|
231
|
+
)
|
|
232
|
+
.get(userId, threadId, itemId);
|
|
233
|
+
|
|
234
|
+
if (!row) {
|
|
235
|
+
throw new NotFoundError(`Thread item not found: ${itemId}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return row;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private attachmentRow(userId: string, attachmentId: string): DataRow {
|
|
242
|
+
const row = this.db
|
|
243
|
+
.query<DataRow, [string, string]>("SELECT data FROM attachments WHERE user_id = ? AND id = ?")
|
|
244
|
+
.get(userId, attachmentId);
|
|
245
|
+
|
|
246
|
+
if (!row) {
|
|
247
|
+
throw new NotFoundError(`Attachment not found: ${attachmentId}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return row;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private threadCursor(userId: string, threadId: string): CursorRow {
|
|
254
|
+
const row = this.db
|
|
255
|
+
.query<CursorRow, [string, string]>(
|
|
256
|
+
"SELECT id, created_at FROM threads WHERE user_id = ? AND id = ?",
|
|
257
|
+
)
|
|
258
|
+
.get(userId, threadId);
|
|
259
|
+
|
|
260
|
+
if (!row) {
|
|
261
|
+
throw new NotFoundError(`Thread cursor not found: ${threadId}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return row;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private itemCursor(userId: string, threadId: string, itemId: string): CursorRow {
|
|
268
|
+
const row = this.db
|
|
269
|
+
.query<CursorRow, [string, string, string]>(
|
|
270
|
+
"SELECT id, created_at FROM items WHERE user_id = ? AND thread_id = ? AND id = ?",
|
|
271
|
+
)
|
|
272
|
+
.get(userId, threadId, itemId);
|
|
273
|
+
|
|
274
|
+
if (!row) {
|
|
275
|
+
throw new NotFoundError(`Thread item cursor not found: ${itemId}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return row;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private threadRows(userId: string, limit: number, order: "asc" | "desc"): OrderedDataRow[] {
|
|
282
|
+
const orderSql = this.orderSql(order);
|
|
283
|
+
|
|
284
|
+
return this.db
|
|
285
|
+
.query<OrderedDataRow, [string, number]>(
|
|
286
|
+
`SELECT id, created_at, data
|
|
287
|
+
FROM threads
|
|
288
|
+
WHERE user_id = ?
|
|
289
|
+
ORDER BY created_at ${orderSql}, id ${orderSql}
|
|
290
|
+
LIMIT ?`,
|
|
291
|
+
)
|
|
292
|
+
.all(userId, this.queryLimit(limit));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private threadRowsAfter(
|
|
296
|
+
userId: string,
|
|
297
|
+
after: string,
|
|
298
|
+
limit: number,
|
|
299
|
+
order: "asc" | "desc",
|
|
300
|
+
): OrderedDataRow[] {
|
|
301
|
+
const cursor = this.threadCursor(userId, after);
|
|
302
|
+
const orderSql = this.orderSql(order);
|
|
303
|
+
const comparator = order === "asc" ? ">" : "<";
|
|
304
|
+
|
|
305
|
+
return this.db
|
|
306
|
+
.query<OrderedDataRow, [string, string, string, string, number]>(
|
|
307
|
+
`SELECT id, created_at, data
|
|
308
|
+
FROM threads
|
|
309
|
+
WHERE user_id = ?
|
|
310
|
+
AND (created_at ${comparator} ? OR (created_at = ? AND id ${comparator} ?))
|
|
311
|
+
ORDER BY created_at ${orderSql}, id ${orderSql}
|
|
312
|
+
LIMIT ?`,
|
|
313
|
+
)
|
|
314
|
+
.all(userId, cursor.created_at, cursor.created_at, cursor.id, this.queryLimit(limit));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private itemRows(
|
|
318
|
+
userId: string,
|
|
319
|
+
threadId: string,
|
|
320
|
+
limit: number,
|
|
321
|
+
order: "asc" | "desc",
|
|
322
|
+
): OrderedDataRow[] {
|
|
323
|
+
const orderSql = this.orderSql(order);
|
|
324
|
+
|
|
325
|
+
return this.db
|
|
326
|
+
.query<OrderedDataRow, [string, string, number]>(
|
|
327
|
+
`SELECT id, created_at, data
|
|
328
|
+
FROM items
|
|
329
|
+
WHERE user_id = ? AND thread_id = ?
|
|
330
|
+
ORDER BY created_at ${orderSql}, id ${orderSql}
|
|
331
|
+
LIMIT ?`,
|
|
332
|
+
)
|
|
333
|
+
.all(userId, threadId, this.queryLimit(limit));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private itemRowsAfter(
|
|
337
|
+
userId: string,
|
|
338
|
+
threadId: string,
|
|
339
|
+
after: string,
|
|
340
|
+
limit: number,
|
|
341
|
+
order: "asc" | "desc",
|
|
342
|
+
): OrderedDataRow[] {
|
|
343
|
+
const cursor = this.itemCursor(userId, threadId, after);
|
|
344
|
+
const orderSql = this.orderSql(order);
|
|
345
|
+
const comparator = order === "asc" ? ">" : "<";
|
|
346
|
+
|
|
347
|
+
return this.db
|
|
348
|
+
.query<OrderedDataRow, [string, string, string, string, string, number]>(
|
|
349
|
+
`SELECT id, created_at, data
|
|
350
|
+
FROM items
|
|
351
|
+
WHERE user_id = ? AND thread_id = ?
|
|
352
|
+
AND (created_at ${comparator} ? OR (created_at = ? AND id ${comparator} ?))
|
|
353
|
+
ORDER BY created_at ${orderSql}, id ${orderSql}
|
|
354
|
+
LIMIT ?`,
|
|
355
|
+
)
|
|
356
|
+
.all(userId, threadId, cursor.created_at, cursor.created_at, cursor.id, this.queryLimit(limit));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private page<T>(rows: OrderedDataRow[], limit: number, parse: (row: OrderedDataRow) => T): Page<T> {
|
|
360
|
+
const requestedLimit = this.requestedLimit(limit);
|
|
361
|
+
const dataRows = rows.slice(0, requestedLimit);
|
|
362
|
+
const hasMore = rows.length > requestedLimit;
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
data: dataRows.map(parse),
|
|
366
|
+
has_more: hasMore,
|
|
367
|
+
after: hasMore && dataRows.length > 0 ? dataRows[dataRows.length - 1]!.id : null,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private requestedLimit(limit: number): number {
|
|
372
|
+
return Math.max(0, Math.trunc(limit));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private assertPositiveLimit(limit: number): void {
|
|
376
|
+
if (!Number.isFinite(limit) || limit < 1) {
|
|
377
|
+
throw new RangeError("Pagination limit must be at least 1");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private queryLimit(limit: number): number {
|
|
382
|
+
return this.requestedLimit(limit) + 1;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private orderSql(order: "asc" | "desc"): "ASC" | "DESC" {
|
|
386
|
+
return order === "asc" ? "ASC" : "DESC";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private parseThread(data: string): ThreadMetadata {
|
|
390
|
+
return ThreadMetadataSchema.parse(JSON.parse(data));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private parseItem(data: string): ThreadItem {
|
|
394
|
+
return ThreadItemSchema.parse(JSON.parse(data));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private parseAttachment(data: string): Attachment {
|
|
398
|
+
return AttachmentSchema.parse(JSON.parse(data));
|
|
399
|
+
}
|
|
400
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Attachment, Page, ThreadItem, ThreadMetadata } from "./types/core";
|
|
2
|
+
|
|
3
|
+
export type StoreItemType =
|
|
4
|
+
| "thread"
|
|
5
|
+
| "message"
|
|
6
|
+
| "tool_call"
|
|
7
|
+
| "task"
|
|
8
|
+
| "workflow"
|
|
9
|
+
| "attachment"
|
|
10
|
+
| "sdk_hidden_context";
|
|
11
|
+
|
|
12
|
+
const idPrefixes: Record<StoreItemType, string> = {
|
|
13
|
+
thread: "thr",
|
|
14
|
+
message: "msg",
|
|
15
|
+
tool_call: "tc",
|
|
16
|
+
task: "tsk",
|
|
17
|
+
workflow: "wf",
|
|
18
|
+
attachment: "atc",
|
|
19
|
+
sdk_hidden_context: "shcx",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function defaultGenerateId(itemType: StoreItemType): string {
|
|
23
|
+
return `${idPrefixes[itemType]}_${crypto.randomUUID().replaceAll("-", "").slice(0, 8)}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AttachmentCreateParams {
|
|
27
|
+
name: string;
|
|
28
|
+
size: number;
|
|
29
|
+
mime_type: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AttachmentStore<TContext = unknown> {
|
|
33
|
+
deleteAttachment(attachmentId: string, context: TContext): Promise<void>;
|
|
34
|
+
createAttachment(input: AttachmentCreateParams, context: TContext): Promise<Attachment>;
|
|
35
|
+
generateAttachmentId?(mimeType: string, context: TContext): string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface Store<TContext = unknown> {
|
|
39
|
+
generateThreadId(context: TContext): string;
|
|
40
|
+
generateItemId(itemType: StoreItemType, thread: ThreadMetadata, context: TContext): string;
|
|
41
|
+
loadThread(threadId: string, context: TContext): Promise<ThreadMetadata>;
|
|
42
|
+
saveThread(thread: ThreadMetadata, context: TContext): Promise<void>;
|
|
43
|
+
loadThreadItems(
|
|
44
|
+
threadId: string,
|
|
45
|
+
after: string | null,
|
|
46
|
+
limit: number,
|
|
47
|
+
order: "asc" | "desc",
|
|
48
|
+
context: TContext,
|
|
49
|
+
): Promise<Page<ThreadItem>>;
|
|
50
|
+
saveAttachment(attachment: Attachment, context: TContext): Promise<void>;
|
|
51
|
+
loadAttachment(attachmentId: string, context: TContext): Promise<Attachment>;
|
|
52
|
+
deleteAttachment(attachmentId: string, context: TContext): Promise<void>;
|
|
53
|
+
loadThreads(
|
|
54
|
+
limit: number,
|
|
55
|
+
after: string | null,
|
|
56
|
+
order: "asc" | "desc",
|
|
57
|
+
context: TContext,
|
|
58
|
+
): Promise<Page<ThreadMetadata>>;
|
|
59
|
+
addThreadItem(threadId: string, item: ThreadItem, context: TContext): Promise<void>;
|
|
60
|
+
saveItem(threadId: string, item: ThreadItem, context: TContext): Promise<void>;
|
|
61
|
+
loadItem(threadId: string, itemId: string, context: TContext): Promise<ThreadItem>;
|
|
62
|
+
deleteThread(threadId: string, context: TContext): Promise<void>;
|
|
63
|
+
deleteThreadItem(threadId: string, itemId: string, context: TContext): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export abstract class BaseStore<TContext = unknown> implements Store<TContext> {
|
|
67
|
+
generateThreadId(_context: TContext): string {
|
|
68
|
+
return defaultGenerateId("thread");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
generateItemId(itemType: StoreItemType, _thread: ThreadMetadata, _context: TContext): string {
|
|
72
|
+
return defaultGenerateId(itemType);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
abstract loadThread(threadId: string, context: TContext): Promise<ThreadMetadata>;
|
|
76
|
+
abstract saveThread(thread: ThreadMetadata, context: TContext): Promise<void>;
|
|
77
|
+
abstract loadThreadItems(
|
|
78
|
+
threadId: string,
|
|
79
|
+
after: string | null,
|
|
80
|
+
limit: number,
|
|
81
|
+
order: "asc" | "desc",
|
|
82
|
+
context: TContext,
|
|
83
|
+
): Promise<Page<ThreadItem>>;
|
|
84
|
+
abstract saveAttachment(attachment: Attachment, context: TContext): Promise<void>;
|
|
85
|
+
abstract loadAttachment(attachmentId: string, context: TContext): Promise<Attachment>;
|
|
86
|
+
abstract deleteAttachment(attachmentId: string, context: TContext): Promise<void>;
|
|
87
|
+
abstract loadThreads(
|
|
88
|
+
limit: number,
|
|
89
|
+
after: string | null,
|
|
90
|
+
order: "asc" | "desc",
|
|
91
|
+
context: TContext,
|
|
92
|
+
): Promise<Page<ThreadMetadata>>;
|
|
93
|
+
abstract addThreadItem(threadId: string, item: ThreadItem, context: TContext): Promise<void>;
|
|
94
|
+
abstract saveItem(threadId: string, item: ThreadItem, context: TContext): Promise<void>;
|
|
95
|
+
abstract loadItem(threadId: string, itemId: string, context: TContext): Promise<ThreadItem>;
|
|
96
|
+
abstract deleteThread(threadId: string, context: TContext): Promise<void>;
|
|
97
|
+
abstract deleteThreadItem(threadId: string, itemId: string, context: TContext): Promise<void>;
|
|
98
|
+
}
|