agents 0.13.1 → 0.13.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.
@@ -0,0 +1,135 @@
1
+ import {
2
+ $ as SubAgentStub,
3
+ Q as SubAgentClass,
4
+ v as Agent
5
+ } from "../agent-tool-types-Dn9n-3SI.js";
6
+ import { Lock, QueueEntry, StateAdapter } from "chat";
7
+
8
+ //#region src/chat-sdk/agent.d.ts
9
+ interface StoredLock {
10
+ threadId: string;
11
+ token: string;
12
+ expiresAt: number;
13
+ }
14
+ declare class ChatSdkStateAgent extends Agent {
15
+ onStart(): void;
16
+ subscribe(threadId: string): void;
17
+ unsubscribe(threadId: string): void;
18
+ isSubscribed(threadId: string): boolean;
19
+ acquireLock(threadId: string, ttlMs: number): Promise<StoredLock | null>;
20
+ releaseLock(threadId: string, token: string): void;
21
+ extendLock(threadId: string, token: string, ttlMs: number): Promise<boolean>;
22
+ forceReleaseLock(threadId: string): void;
23
+ enqueue(threadId: string, value: string, maxSize: number): Promise<number>;
24
+ popQueue(threadId: string): string | null;
25
+ queueDepth(threadId: string): number;
26
+ listAppend(
27
+ key: string,
28
+ value: string,
29
+ maxLength?: number,
30
+ ttlMs?: number
31
+ ): Promise<void>;
32
+ listGet(key: string): string[];
33
+ cacheGet(key: string): string | null;
34
+ cacheSet(key: string, value: string, ttlMs?: number): Promise<void>;
35
+ cacheSetIfNotExists(
36
+ key: string,
37
+ value: string,
38
+ ttlMs?: number
39
+ ): Promise<boolean>;
40
+ cacheDelete(key: string): void;
41
+ cleanupExpired(payload?: { expiresAt?: number }): Promise<void>;
42
+ private migrate;
43
+ private readCacheValue;
44
+ private upsertCacheValue;
45
+ private scheduleCleanupForExpiry;
46
+ private scheduleNextCleanup;
47
+ private ensureCleanupScheduled;
48
+ private nextExpiry;
49
+ private readCleanupMetadata;
50
+ private writeCleanupMetadata;
51
+ private clearCleanupMetadata;
52
+ }
53
+ //#endregion
54
+ //#region src/chat-sdk/types.d.ts
55
+ interface ChatSdkStateParent {
56
+ subAgent<T extends ChatSdkStateAgent>(
57
+ agentClass: SubAgentClass<T>,
58
+ name: string
59
+ ): Promise<SubAgentStub<T>>;
60
+ }
61
+ interface ChatSdkStateAdapterOptions {
62
+ agent?: SubAgentClass<ChatSdkStateAgent>;
63
+ parent?: ChatSdkStateParent;
64
+ name?: string;
65
+ keyShard?: (key: string) => string | undefined;
66
+ shardKey?: (threadId: string) => string;
67
+ }
68
+ //#endregion
69
+ //#region src/chat-sdk/adapter.d.ts
70
+ declare function defaultThreadShard(threadId: string): string;
71
+ declare function defaultKeyShard(
72
+ key: string,
73
+ shardThread?: (threadId: string) => string
74
+ ): string | undefined;
75
+ declare class ChatSdkStateAdapter implements StateAdapter {
76
+ private readonly parent;
77
+ private readonly agentClass;
78
+ private readonly defaultName;
79
+ private readonly keyShard?;
80
+ private readonly shardKey;
81
+ private connected;
82
+ constructor(options?: ChatSdkStateAdapterOptions);
83
+ connect(): Promise<void>;
84
+ disconnect(): Promise<void>;
85
+ subscribe(threadId: string): Promise<void>;
86
+ unsubscribe(threadId: string): Promise<void>;
87
+ isSubscribed(threadId: string): Promise<boolean>;
88
+ acquireLock(threadId: string, ttlMs: number): Promise<Lock | null>;
89
+ releaseLock(lock: Lock): Promise<void>;
90
+ extendLock(lock: Lock, ttlMs: number): Promise<boolean>;
91
+ forceReleaseLock(threadId: string): Promise<void>;
92
+ enqueue(
93
+ threadId: string,
94
+ entry: QueueEntry,
95
+ maxSize: number
96
+ ): Promise<number>;
97
+ dequeue(threadId: string): Promise<QueueEntry | null>;
98
+ queueDepth(threadId: string): Promise<number>;
99
+ appendToList(
100
+ key: string,
101
+ value: unknown,
102
+ options?: {
103
+ maxLength?: number;
104
+ ttlMs?: number;
105
+ }
106
+ ): Promise<void>;
107
+ getList<T = unknown>(key: string): Promise<T[]>;
108
+ get<T = unknown>(key: string): Promise<T | null>;
109
+ set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;
110
+ setIfNotExists<T = unknown>(
111
+ key: string,
112
+ value: T,
113
+ ttlMs?: number
114
+ ): Promise<boolean>;
115
+ delete(key: string): Promise<void>;
116
+ private stateAgent;
117
+ private stateAgentForKey;
118
+ private ensureConnected;
119
+ }
120
+ //#endregion
121
+ //#region src/chat-sdk/index.d.ts
122
+ declare function createChatSdkState(
123
+ options?: ChatSdkStateAdapterOptions
124
+ ): ChatSdkStateAdapter;
125
+ //#endregion
126
+ export {
127
+ ChatSdkStateAdapter,
128
+ type ChatSdkStateAdapterOptions,
129
+ ChatSdkStateAgent,
130
+ type ChatSdkStateParent,
131
+ createChatSdkState,
132
+ defaultKeyShard,
133
+ defaultThreadShard
134
+ };
135
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,453 @@
1
+ import { Agent, getCurrentAgent } from "../index.js";
2
+ //#region src/chat-sdk/agent.ts
3
+ const NEXT_CLEANUP_AT_KEY = "next_cleanup_at";
4
+ const CLEANUP_SCHEDULE_ID_KEY = "cleanup_schedule_id";
5
+ var ChatSdkStateAgent = class extends Agent {
6
+ onStart() {
7
+ this.migrate();
8
+ this.scheduleNextCleanup();
9
+ }
10
+ subscribe(threadId) {
11
+ this.sql`
12
+ INSERT OR IGNORE INTO chat_sdk_state_subscriptions (thread_id)
13
+ VALUES (${threadId})
14
+ `;
15
+ }
16
+ unsubscribe(threadId) {
17
+ this.sql`
18
+ DELETE FROM chat_sdk_state_subscriptions
19
+ WHERE thread_id = ${threadId}
20
+ `;
21
+ }
22
+ isSubscribed(threadId) {
23
+ return this.sql`
24
+ SELECT 1 as found
25
+ FROM chat_sdk_state_subscriptions
26
+ WHERE thread_id = ${threadId}
27
+ LIMIT 1
28
+ `.length > 0;
29
+ }
30
+ async acquireLock(threadId, ttlMs) {
31
+ const result = this.ctx.storage.transactionSync(() => {
32
+ const now = Date.now();
33
+ this.ctx.storage.sql.exec("DELETE FROM chat_sdk_state_locks WHERE thread_id = ? AND expires_at <= ?", threadId, now);
34
+ if (this.ctx.storage.sql.exec("SELECT 1 FROM chat_sdk_state_locks WHERE thread_id = ? LIMIT 1", threadId).toArray().length > 0) return null;
35
+ const token = crypto.randomUUID();
36
+ const expiresAt = now + ttlMs;
37
+ this.ctx.storage.sql.exec("INSERT INTO chat_sdk_state_locks (thread_id, token, expires_at) VALUES (?, ?, ?)", threadId, token, expiresAt);
38
+ return {
39
+ threadId,
40
+ token,
41
+ expiresAt
42
+ };
43
+ });
44
+ await this.scheduleCleanupForExpiry(result?.expiresAt ?? null);
45
+ return result;
46
+ }
47
+ releaseLock(threadId, token) {
48
+ this.sql`
49
+ DELETE FROM chat_sdk_state_locks
50
+ WHERE thread_id = ${threadId} AND token = ${token}
51
+ `;
52
+ }
53
+ async extendLock(threadId, token, ttlMs) {
54
+ const result = this.ctx.storage.transactionSync(() => {
55
+ const now = Date.now();
56
+ return this.ctx.storage.sql.exec(`UPDATE chat_sdk_state_locks SET expires_at = ?
57
+ WHERE thread_id = ? AND token = ? AND expires_at > ?
58
+ RETURNING thread_id`, now + ttlMs, threadId, token, now).toArray().length > 0;
59
+ });
60
+ if (result) await this.scheduleCleanupForExpiry(Date.now() + ttlMs);
61
+ return result;
62
+ }
63
+ forceReleaseLock(threadId) {
64
+ this.sql`
65
+ DELETE FROM chat_sdk_state_locks
66
+ WHERE thread_id = ${threadId}
67
+ `;
68
+ }
69
+ async enqueue(threadId, value, maxSize) {
70
+ const parsed = parseQueueEntry(value);
71
+ const count = this.ctx.storage.transactionSync(() => {
72
+ this.ctx.storage.sql.exec("INSERT INTO chat_sdk_state_queue (thread_id, value, enqueued_at, expires_at) VALUES (?, ?, ?, ?)", threadId, value, parsed.enqueuedAt, parsed.expiresAt);
73
+ this.ctx.storage.sql.exec(`DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND id NOT IN (
74
+ SELECT id FROM chat_sdk_state_queue
75
+ WHERE thread_id = ?
76
+ ORDER BY id DESC
77
+ LIMIT ?
78
+ )`, threadId, threadId, maxSize);
79
+ return this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM chat_sdk_state_queue WHERE thread_id = ?", threadId).one().count;
80
+ });
81
+ await this.scheduleCleanupForExpiry(parsed.expiresAt);
82
+ return count;
83
+ }
84
+ popQueue(threadId) {
85
+ return this.ctx.storage.transactionSync(() => {
86
+ const now = Date.now();
87
+ this.ctx.storage.sql.exec("DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND expires_at <= ?", threadId, now);
88
+ const row = this.ctx.storage.sql.exec("SELECT id, value FROM chat_sdk_state_queue WHERE thread_id = ? ORDER BY id ASC LIMIT 1", threadId).toArray()[0];
89
+ if (!row) return null;
90
+ this.ctx.storage.sql.exec("DELETE FROM chat_sdk_state_queue WHERE id = ?", row.id);
91
+ return row.value;
92
+ });
93
+ }
94
+ queueDepth(threadId) {
95
+ return this.sql`
96
+ SELECT COUNT(*) as count
97
+ FROM chat_sdk_state_queue
98
+ WHERE thread_id = ${threadId} AND expires_at > ${Date.now()}
99
+ `[0]?.count ?? 0;
100
+ }
101
+ async listAppend(key, value, maxLength, ttlMs) {
102
+ const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;
103
+ this.ctx.storage.transactionSync(() => {
104
+ this.ctx.storage.sql.exec("INSERT INTO chat_sdk_state_lists (key, value, expires_at) VALUES (?, ?, ?)", key, value, expiresAt);
105
+ if (expiresAt !== null) this.ctx.storage.sql.exec("UPDATE chat_sdk_state_lists SET expires_at = ? WHERE key = ?", expiresAt, key);
106
+ if (maxLength != null && maxLength > 0) this.ctx.storage.sql.exec(`DELETE FROM chat_sdk_state_lists WHERE key = ? AND id NOT IN (
107
+ SELECT id FROM chat_sdk_state_lists
108
+ WHERE key = ?
109
+ ORDER BY id DESC
110
+ LIMIT ?
111
+ )`, key, key, maxLength);
112
+ });
113
+ await this.scheduleCleanupForExpiry(expiresAt);
114
+ }
115
+ listGet(key) {
116
+ const now = Date.now();
117
+ this.sql`
118
+ DELETE FROM chat_sdk_state_lists
119
+ WHERE key = ${key}
120
+ AND expires_at IS NOT NULL
121
+ AND expires_at <= ${now}
122
+ `;
123
+ return this.sql`
124
+ SELECT value
125
+ FROM chat_sdk_state_lists
126
+ WHERE key = ${key}
127
+ ORDER BY id ASC
128
+ `.map((row) => row.value);
129
+ }
130
+ cacheGet(key) {
131
+ return this.readCacheValue(key, Date.now());
132
+ }
133
+ async cacheSet(key, value, ttlMs) {
134
+ const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;
135
+ this.upsertCacheValue(key, value, expiresAt);
136
+ await this.scheduleCleanupForExpiry(expiresAt);
137
+ }
138
+ async cacheSetIfNotExists(key, value, ttlMs) {
139
+ const now = Date.now();
140
+ const inserted = this.ctx.storage.transactionSync(() => {
141
+ this.ctx.storage.sql.exec("DELETE FROM chat_sdk_state_cache WHERE key = ? AND expires_at IS NOT NULL AND expires_at <= ?", key, now);
142
+ if (this.readCacheValue(key, now) !== null) return false;
143
+ const expiresAt = ttlMs && ttlMs > 0 ? now + ttlMs : null;
144
+ this.upsertCacheValue(key, value, expiresAt);
145
+ return true;
146
+ });
147
+ if (inserted) {
148
+ const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;
149
+ await this.scheduleCleanupForExpiry(expiresAt);
150
+ }
151
+ return inserted;
152
+ }
153
+ cacheDelete(key) {
154
+ this.sql`
155
+ DELETE FROM chat_sdk_state_cache
156
+ WHERE key = ${key}
157
+ `;
158
+ }
159
+ async cleanupExpired(payload) {
160
+ const current = this.readCleanupMetadata();
161
+ if (payload?.expiresAt !== void 0 && current.nextCleanupAt !== null && payload.expiresAt !== current.nextCleanupAt) return;
162
+ const now = Date.now();
163
+ this.clearCleanupMetadata();
164
+ this.sql`
165
+ DELETE FROM chat_sdk_state_locks
166
+ WHERE expires_at <= ${now}
167
+ `;
168
+ this.sql`
169
+ DELETE FROM chat_sdk_state_cache
170
+ WHERE expires_at IS NOT NULL AND expires_at <= ${now}
171
+ `;
172
+ this.sql`
173
+ DELETE FROM chat_sdk_state_queue
174
+ WHERE expires_at <= ${now}
175
+ `;
176
+ this.sql`
177
+ DELETE FROM chat_sdk_state_lists
178
+ WHERE expires_at IS NOT NULL AND expires_at <= ${now}
179
+ `;
180
+ await this.scheduleNextCleanup();
181
+ }
182
+ migrate() {
183
+ this.sql`
184
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_subscriptions (
185
+ thread_id TEXT PRIMARY KEY
186
+ )
187
+ `;
188
+ this.sql`
189
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_locks (
190
+ thread_id TEXT PRIMARY KEY,
191
+ token TEXT NOT NULL,
192
+ expires_at INTEGER NOT NULL
193
+ )
194
+ `;
195
+ this.sql`
196
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_cache (
197
+ key TEXT PRIMARY KEY,
198
+ value TEXT NOT NULL,
199
+ expires_at INTEGER
200
+ )
201
+ `;
202
+ this.sql`
203
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_queue (
204
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
205
+ thread_id TEXT NOT NULL,
206
+ value TEXT NOT NULL,
207
+ enqueued_at INTEGER NOT NULL,
208
+ expires_at INTEGER NOT NULL
209
+ )
210
+ `;
211
+ this.sql`
212
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_lists (
213
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
214
+ key TEXT NOT NULL,
215
+ value TEXT NOT NULL,
216
+ expires_at INTEGER
217
+ )
218
+ `;
219
+ this.sql`
220
+ CREATE TABLE IF NOT EXISTS chat_sdk_state_metadata (
221
+ key TEXT PRIMARY KEY,
222
+ value TEXT NOT NULL
223
+ )
224
+ `;
225
+ this.sql`
226
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_locks_expires
227
+ ON chat_sdk_state_locks(expires_at)
228
+ `;
229
+ this.sql`
230
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_cache_expires
231
+ ON chat_sdk_state_cache(expires_at)
232
+ WHERE expires_at IS NOT NULL
233
+ `;
234
+ this.sql`
235
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_thread
236
+ ON chat_sdk_state_queue(thread_id, id)
237
+ `;
238
+ this.sql`
239
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_expires
240
+ ON chat_sdk_state_queue(expires_at)
241
+ `;
242
+ this.sql`
243
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_key
244
+ ON chat_sdk_state_lists(key, id)
245
+ `;
246
+ this.sql`
247
+ CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_expires
248
+ ON chat_sdk_state_lists(expires_at)
249
+ WHERE expires_at IS NOT NULL
250
+ `;
251
+ }
252
+ readCacheValue(key, now) {
253
+ return this.sql`
254
+ SELECT value
255
+ FROM chat_sdk_state_cache
256
+ WHERE key = ${key}
257
+ AND (expires_at IS NULL OR expires_at > ${now})
258
+ `[0]?.value ?? null;
259
+ }
260
+ upsertCacheValue(key, value, expiresAt) {
261
+ this.sql`
262
+ INSERT OR REPLACE INTO chat_sdk_state_cache (key, value, expires_at)
263
+ VALUES (${key}, ${value}, ${expiresAt})
264
+ `;
265
+ }
266
+ async scheduleCleanupForExpiry(expiresAt) {
267
+ if (expiresAt === null) return;
268
+ await this.ensureCleanupScheduled(expiresAt);
269
+ }
270
+ async scheduleNextCleanup() {
271
+ const next = this.nextExpiry();
272
+ if (next === null) {
273
+ const current = this.readCleanupMetadata();
274
+ if (current.scheduleId) await this.cancelSchedule(current.scheduleId).catch(() => false);
275
+ this.clearCleanupMetadata();
276
+ return;
277
+ }
278
+ await this.ensureCleanupScheduled(next);
279
+ }
280
+ async ensureCleanupScheduled(expiresAt) {
281
+ const current = this.readCleanupMetadata();
282
+ if (current.nextCleanupAt !== null && current.nextCleanupAt <= expiresAt) return;
283
+ if (current.scheduleId) await this.cancelSchedule(current.scheduleId).catch(() => false);
284
+ const delaySeconds = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1e3));
285
+ const schedule = await this.schedule(delaySeconds, "cleanupExpired", { expiresAt });
286
+ this.writeCleanupMetadata(expiresAt, schedule.id);
287
+ }
288
+ nextExpiry() {
289
+ return this.sql`
290
+ SELECT MIN(expires_at) as expires_at
291
+ FROM (
292
+ SELECT expires_at FROM chat_sdk_state_locks
293
+ UNION ALL
294
+ SELECT expires_at FROM chat_sdk_state_queue
295
+ UNION ALL
296
+ SELECT expires_at FROM chat_sdk_state_cache WHERE expires_at IS NOT NULL
297
+ UNION ALL
298
+ SELECT expires_at FROM chat_sdk_state_lists WHERE expires_at IS NOT NULL
299
+ )
300
+ `[0]?.expires_at ?? null;
301
+ }
302
+ readCleanupMetadata() {
303
+ const rows = this.sql`
304
+ SELECT key, value
305
+ FROM chat_sdk_state_metadata
306
+ WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})
307
+ `;
308
+ const values = new Map(rows.map((row) => [row.key, row.value]));
309
+ const nextCleanupAt = Number(values.get(NEXT_CLEANUP_AT_KEY));
310
+ return {
311
+ nextCleanupAt: Number.isFinite(nextCleanupAt) ? nextCleanupAt : null,
312
+ scheduleId: values.get(CLEANUP_SCHEDULE_ID_KEY) ?? null
313
+ };
314
+ }
315
+ writeCleanupMetadata(expiresAt, scheduleId) {
316
+ this.sql`
317
+ INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)
318
+ VALUES (${NEXT_CLEANUP_AT_KEY}, ${String(expiresAt)})
319
+ `;
320
+ this.sql`
321
+ INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)
322
+ VALUES (${CLEANUP_SCHEDULE_ID_KEY}, ${scheduleId})
323
+ `;
324
+ }
325
+ clearCleanupMetadata() {
326
+ this.sql`
327
+ DELETE FROM chat_sdk_state_metadata
328
+ WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})
329
+ `;
330
+ }
331
+ };
332
+ function parseQueueEntry(value) {
333
+ const raw = JSON.parse(value);
334
+ if (typeof raw.enqueuedAt !== "number" || typeof raw.expiresAt !== "number") throw new Error("ChatSdkStateAgent expected QueueEntry JSON with numeric TTLs");
335
+ return {
336
+ enqueuedAt: raw.enqueuedAt,
337
+ expiresAt: raw.expiresAt
338
+ };
339
+ }
340
+ //#endregion
341
+ //#region src/chat-sdk/adapter.ts
342
+ const THREAD_STATE_PREFIX = "thread-state:";
343
+ const CHANNEL_STATE_PREFIX = "channel-state:";
344
+ const MESSAGE_HISTORY_PREFIX = "msg-history:";
345
+ const TRANSCRIPTS_USER_PREFIX = "transcripts:user:";
346
+ function parseStoredJson(raw, label) {
347
+ try {
348
+ return JSON.parse(raw);
349
+ } catch (error) {
350
+ throw new Error(`ChatSdkStateAdapter expected JSON-encoded ${label}`, { cause: error });
351
+ }
352
+ }
353
+ function defaultThreadShard(threadId) {
354
+ return threadId.split(":").slice(0, 2).join(":") || "default";
355
+ }
356
+ function defaultKeyShard(key, shardThread = defaultThreadShard) {
357
+ for (const prefix of [
358
+ THREAD_STATE_PREFIX,
359
+ CHANNEL_STATE_PREFIX,
360
+ MESSAGE_HISTORY_PREFIX,
361
+ TRANSCRIPTS_USER_PREFIX
362
+ ]) if (key.startsWith(prefix)) return shardThread(key.slice(prefix.length));
363
+ }
364
+ var ChatSdkStateAdapter = class {
365
+ constructor(options = {}) {
366
+ this.connected = false;
367
+ const parent = options.parent ?? getCurrentAgent().agent;
368
+ if (!parent) throw new Error("ChatSdkStateAdapter requires a parent Agent. Pass `parent` or create it inside an Agent context.");
369
+ this.parent = parent;
370
+ this.agentClass = options.agent ?? ChatSdkStateAgent;
371
+ this.defaultName = options.name ?? "default";
372
+ this.keyShard = options.keyShard;
373
+ this.shardKey = options.shardKey ?? defaultThreadShard;
374
+ }
375
+ async connect() {
376
+ this.connected = true;
377
+ }
378
+ async disconnect() {
379
+ this.connected = false;
380
+ }
381
+ async subscribe(threadId) {
382
+ await (await this.stateAgent(threadId)).subscribe(threadId);
383
+ }
384
+ async unsubscribe(threadId) {
385
+ await (await this.stateAgent(threadId)).unsubscribe(threadId);
386
+ }
387
+ async isSubscribed(threadId) {
388
+ return (await this.stateAgent(threadId)).isSubscribed(threadId);
389
+ }
390
+ async acquireLock(threadId, ttlMs) {
391
+ return (await this.stateAgent(threadId)).acquireLock(threadId, ttlMs);
392
+ }
393
+ async releaseLock(lock) {
394
+ await (await this.stateAgent(lock.threadId)).releaseLock(lock.threadId, lock.token);
395
+ }
396
+ async extendLock(lock, ttlMs) {
397
+ return (await this.stateAgent(lock.threadId)).extendLock(lock.threadId, lock.token, ttlMs);
398
+ }
399
+ async forceReleaseLock(threadId) {
400
+ await (await this.stateAgent(threadId)).forceReleaseLock(threadId);
401
+ }
402
+ async enqueue(threadId, entry, maxSize) {
403
+ return (await this.stateAgent(threadId)).enqueue(threadId, JSON.stringify(entry), maxSize);
404
+ }
405
+ async dequeue(threadId) {
406
+ const raw = await (await this.stateAgent(threadId)).popQueue(threadId);
407
+ return raw === null ? null : parseStoredJson(raw, `queue entry for ${threadId}`);
408
+ }
409
+ async queueDepth(threadId) {
410
+ return (await this.stateAgent(threadId)).queueDepth(threadId);
411
+ }
412
+ async appendToList(key, value, options) {
413
+ await (await this.stateAgentForKey(key)).listAppend(key, JSON.stringify(value), options?.maxLength, options?.ttlMs);
414
+ }
415
+ async getList(key) {
416
+ return (await (await this.stateAgentForKey(key)).listGet(key)).map((value) => parseStoredJson(value, `list entry for ${key}`));
417
+ }
418
+ async get(key) {
419
+ const raw = await (await this.stateAgentForKey(key)).cacheGet(key);
420
+ return raw === null ? null : parseStoredJson(raw, `cache key ${key}`);
421
+ }
422
+ async set(key, value, ttlMs) {
423
+ await (await this.stateAgentForKey(key)).cacheSet(key, JSON.stringify(value), ttlMs);
424
+ }
425
+ async setIfNotExists(key, value, ttlMs) {
426
+ return (await this.stateAgentForKey(key)).cacheSetIfNotExists(key, JSON.stringify(value), ttlMs);
427
+ }
428
+ async delete(key) {
429
+ await (await this.stateAgentForKey(key)).cacheDelete(key);
430
+ }
431
+ async stateAgent(threadId) {
432
+ this.ensureConnected();
433
+ const name = threadId ? this.shardKey(threadId) : this.defaultName;
434
+ return this.parent.subAgent(this.agentClass, name);
435
+ }
436
+ async stateAgentForKey(key) {
437
+ this.ensureConnected();
438
+ const name = this.keyShard?.(key) ?? defaultKeyShard(key, this.shardKey) ?? this.defaultName;
439
+ return this.parent.subAgent(this.agentClass, name);
440
+ }
441
+ ensureConnected() {
442
+ if (!this.connected) throw new Error("ChatSdkStateAdapter is not connected");
443
+ }
444
+ };
445
+ //#endregion
446
+ //#region src/chat-sdk/index.ts
447
+ function createChatSdkState(options = {}) {
448
+ return new ChatSdkStateAdapter(options);
449
+ }
450
+ //#endregion
451
+ export { ChatSdkStateAdapter, ChatSdkStateAgent, createChatSdkState, defaultKeyShard, defaultThreadShard };
452
+
453
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/chat-sdk/agent.ts","../../src/chat-sdk/adapter.ts","../../src/chat-sdk/index.ts"],"sourcesContent":["import { Agent } from \"../index\";\n\ninterface StoredLock {\n threadId: string;\n token: string;\n expiresAt: number;\n}\n\ninterface StoredQueueEntry {\n enqueuedAt: number;\n expiresAt: number;\n}\n\nconst NEXT_CLEANUP_AT_KEY = \"next_cleanup_at\";\nconst CLEANUP_SCHEDULE_ID_KEY = \"cleanup_schedule_id\";\n\nexport class ChatSdkStateAgent extends Agent {\n onStart(): void {\n this.migrate();\n void this.scheduleNextCleanup();\n }\n\n subscribe(threadId: string): void {\n this.sql`\n INSERT OR IGNORE INTO chat_sdk_state_subscriptions (thread_id)\n VALUES (${threadId})\n `;\n }\n\n unsubscribe(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n `;\n }\n\n isSubscribed(threadId: string): boolean {\n const rows = this.sql<{ found: number }>`\n SELECT 1 as found\n FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n LIMIT 1\n `;\n return rows.length > 0;\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<StoredLock | null> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_locks WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const existing = this.ctx.storage.sql\n .exec(\n \"SELECT 1 FROM chat_sdk_state_locks WHERE thread_id = ? LIMIT 1\",\n threadId\n )\n .toArray();\n if (existing.length > 0) {\n return null;\n }\n\n const token = crypto.randomUUID();\n const expiresAt = now + ttlMs;\n\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_locks (thread_id, token, expires_at) VALUES (?, ?, ?)\",\n threadId,\n token,\n expiresAt\n );\n\n return { threadId, token, expiresAt };\n });\n\n await this.scheduleCleanupForExpiry(result?.expiresAt ?? null);\n return result;\n }\n\n releaseLock(threadId: string, token: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId} AND token = ${token}\n `;\n }\n\n async extendLock(\n threadId: string,\n token: string,\n ttlMs: number\n ): Promise<boolean> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n const rows = this.ctx.storage.sql\n .exec<{ thread_id: string }>(\n `UPDATE chat_sdk_state_locks SET expires_at = ?\n WHERE thread_id = ? AND token = ? AND expires_at > ?\n RETURNING thread_id`,\n now + ttlMs,\n threadId,\n token,\n now\n )\n .toArray();\n return rows.length > 0;\n });\n if (result) {\n await this.scheduleCleanupForExpiry(Date.now() + ttlMs);\n }\n return result;\n }\n\n forceReleaseLock(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId}\n `;\n }\n\n async enqueue(\n threadId: string,\n value: string,\n maxSize: number\n ): Promise<number> {\n const parsed = parseQueueEntry(value);\n\n const count = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_queue (thread_id, value, enqueued_at, expires_at) VALUES (?, ?, ?, ?)\",\n threadId,\n value,\n parsed.enqueuedAt,\n parsed.expiresAt\n );\n\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_queue\n WHERE thread_id = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n threadId,\n threadId,\n maxSize\n );\n\n const row = this.ctx.storage.sql\n .exec<{ count: number }>(\n \"SELECT COUNT(*) as count FROM chat_sdk_state_queue WHERE thread_id = ?\",\n threadId\n )\n .one();\n return row.count;\n });\n await this.scheduleCleanupForExpiry(parsed.expiresAt);\n return count;\n }\n\n popQueue(threadId: string): string | null {\n return this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const rows = this.ctx.storage.sql\n .exec<{ id: number; value: string }>(\n \"SELECT id, value FROM chat_sdk_state_queue WHERE thread_id = ? ORDER BY id ASC LIMIT 1\",\n threadId\n )\n .toArray();\n const row = rows[0];\n if (!row) {\n return null;\n }\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE id = ?\",\n row.id\n );\n return row.value;\n });\n }\n\n queueDepth(threadId: string): number {\n const rows = this.sql<{ count: number }>`\n SELECT COUNT(*) as count\n FROM chat_sdk_state_queue\n WHERE thread_id = ${threadId} AND expires_at > ${Date.now()}\n `;\n return rows[0]?.count ?? 0;\n }\n\n async listAppend(\n key: string,\n value: string,\n maxLength?: number,\n ttlMs?: number\n ): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n\n this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_lists (key, value, expires_at) VALUES (?, ?, ?)\",\n key,\n value,\n expiresAt\n );\n\n if (expiresAt !== null) {\n // Chat SDK history lists use a list-level TTL: any append refreshes the\n // expiry for the whole logical list, not only the new row.\n this.ctx.storage.sql.exec(\n \"UPDATE chat_sdk_state_lists SET expires_at = ? WHERE key = ?\",\n expiresAt,\n key\n );\n }\n\n if (maxLength != null && maxLength > 0) {\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_lists WHERE key = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_lists\n WHERE key = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n key,\n key,\n maxLength\n );\n }\n });\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n listGet(key: string): string[] {\n const now = Date.now();\n\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE key = ${key}\n AND expires_at IS NOT NULL\n AND expires_at <= ${now}\n `;\n\n return this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_lists\n WHERE key = ${key}\n ORDER BY id ASC\n `.map((row) => row.value);\n }\n\n cacheGet(key: string): string | null {\n return this.readCacheValue(key, Date.now());\n }\n\n async cacheSet(key: string, value: string, ttlMs?: number): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n async cacheSetIfNotExists(\n key: string,\n value: string,\n ttlMs?: number\n ): Promise<boolean> {\n const now = Date.now();\n\n const inserted = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_cache WHERE key = ? AND expires_at IS NOT NULL AND expires_at <= ?\",\n key,\n now\n );\n\n if (this.readCacheValue(key, now) !== null) {\n return false;\n }\n\n const expiresAt = ttlMs && ttlMs > 0 ? now + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n return true;\n });\n if (inserted) {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n return inserted;\n }\n\n cacheDelete(key: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE key = ${key}\n `;\n }\n\n async cleanupExpired(payload?: { expiresAt?: number }): Promise<void> {\n const current = this.readCleanupMetadata();\n if (\n payload?.expiresAt !== undefined &&\n current.nextCleanupAt !== null &&\n payload.expiresAt !== current.nextCleanupAt\n ) {\n return;\n }\n\n const now = Date.now();\n this.clearCleanupMetadata();\n\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_queue\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n await this.scheduleNextCleanup();\n }\n\n private migrate(): void {\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_subscriptions (\n thread_id TEXT PRIMARY KEY\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_locks (\n thread_id TEXT PRIMARY KEY,\n token TEXT NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_cache (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_queue (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n thread_id TEXT NOT NULL,\n value TEXT NOT NULL,\n enqueued_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_lists (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )\n `;\n\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_locks_expires\n ON chat_sdk_state_locks(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_cache_expires\n ON chat_sdk_state_cache(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_thread\n ON chat_sdk_state_queue(thread_id, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_expires\n ON chat_sdk_state_queue(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_key\n ON chat_sdk_state_lists(key, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_expires\n ON chat_sdk_state_lists(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n }\n\n private readCacheValue(key: string, now: number): string | null {\n const rows = this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_cache\n WHERE key = ${key}\n AND (expires_at IS NULL OR expires_at > ${now})\n `;\n return rows[0]?.value ?? null;\n }\n\n private upsertCacheValue(\n key: string,\n value: string,\n expiresAt: number | null\n ): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_cache (key, value, expires_at)\n VALUES (${key}, ${value}, ${expiresAt})\n `;\n }\n\n private async scheduleCleanupForExpiry(\n expiresAt: number | null\n ): Promise<void> {\n if (expiresAt === null) {\n return;\n }\n\n await this.ensureCleanupScheduled(expiresAt);\n }\n\n private async scheduleNextCleanup(): Promise<void> {\n const next = this.nextExpiry();\n if (next === null) {\n const current = this.readCleanupMetadata();\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n this.clearCleanupMetadata();\n return;\n }\n\n await this.ensureCleanupScheduled(next);\n }\n\n private async ensureCleanupScheduled(expiresAt: number): Promise<void> {\n const current = this.readCleanupMetadata();\n if (current.nextCleanupAt !== null && current.nextCleanupAt <= expiresAt) {\n return;\n }\n\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n\n const delaySeconds = Math.max(\n 0,\n Math.ceil((expiresAt - Date.now()) / 1000)\n );\n const schedule = await this.schedule(delaySeconds, \"cleanupExpired\", {\n expiresAt\n });\n this.writeCleanupMetadata(expiresAt, schedule.id);\n }\n\n private nextExpiry(): number | null {\n const rows = this.sql<{ expires_at: number | null }>`\n SELECT MIN(expires_at) as expires_at\n FROM (\n SELECT expires_at FROM chat_sdk_state_locks\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_queue\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_cache WHERE expires_at IS NOT NULL\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_lists WHERE expires_at IS NOT NULL\n )\n `;\n return rows[0]?.expires_at ?? null;\n }\n\n private readCleanupMetadata(): {\n nextCleanupAt: number | null;\n scheduleId: string | null;\n } {\n const rows = this.sql<{ key: string; value: string }>`\n SELECT key, value\n FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n const values = new Map(rows.map((row) => [row.key, row.value]));\n const nextCleanupAt = Number(values.get(NEXT_CLEANUP_AT_KEY));\n return {\n nextCleanupAt: Number.isFinite(nextCleanupAt) ? nextCleanupAt : null,\n scheduleId: values.get(CLEANUP_SCHEDULE_ID_KEY) ?? null\n };\n }\n\n private writeCleanupMetadata(expiresAt: number, scheduleId: string): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${NEXT_CLEANUP_AT_KEY}, ${String(expiresAt)})\n `;\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${CLEANUP_SCHEDULE_ID_KEY}, ${scheduleId})\n `;\n }\n\n private clearCleanupMetadata(): void {\n this.sql`\n DELETE FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n }\n}\n\nfunction parseQueueEntry(value: string): StoredQueueEntry {\n const raw = JSON.parse(value) as Record<string, unknown>;\n if (typeof raw.enqueuedAt !== \"number\" || typeof raw.expiresAt !== \"number\") {\n throw new Error(\n \"ChatSdkStateAgent expected QueueEntry JSON with numeric TTLs\"\n );\n }\n\n return {\n enqueuedAt: raw.enqueuedAt,\n expiresAt: raw.expiresAt\n };\n}\n","import type {\n Lock as ChatSdkLock,\n QueueEntry as ChatSdkQueueEntry,\n StateAdapter as ChatSdkStateAdapterInterface\n} from \"chat\";\nimport {\n getCurrentAgent,\n type SubAgentClass,\n type SubAgentStub\n} from \"../index\";\nimport { ChatSdkStateAgent } from \"./agent\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nconst THREAD_STATE_PREFIX = \"thread-state:\";\nconst CHANNEL_STATE_PREFIX = \"channel-state:\";\nconst MESSAGE_HISTORY_PREFIX = \"msg-history:\";\nconst TRANSCRIPTS_USER_PREFIX = \"transcripts:user:\";\n\nfunction parseStoredJson<T>(raw: string, label: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n throw new Error(`ChatSdkStateAdapter expected JSON-encoded ${label}`, {\n cause: error\n });\n }\n}\n\nexport function defaultThreadShard(threadId: string): string {\n return threadId.split(\":\").slice(0, 2).join(\":\") || \"default\";\n}\n\nexport function defaultKeyShard(\n key: string,\n shardThread: (threadId: string) => string = defaultThreadShard\n): string | undefined {\n for (const prefix of [\n THREAD_STATE_PREFIX,\n CHANNEL_STATE_PREFIX,\n MESSAGE_HISTORY_PREFIX,\n TRANSCRIPTS_USER_PREFIX\n ]) {\n if (key.startsWith(prefix)) {\n return shardThread(key.slice(prefix.length));\n }\n }\n\n return undefined;\n}\n\nexport class ChatSdkStateAdapter implements ChatSdkStateAdapterInterface {\n private readonly parent: NonNullable<ChatSdkStateAdapterOptions[\"parent\"]>;\n private readonly agentClass: SubAgentClass<ChatSdkStateAgent>;\n private readonly defaultName: string;\n private readonly keyShard?: (key: string) => string | undefined;\n private readonly shardKey: (threadId: string) => string;\n private connected = false;\n\n constructor(options: ChatSdkStateAdapterOptions = {}) {\n const parent = options.parent ?? getCurrentAgent().agent;\n if (!parent) {\n throw new Error(\n \"ChatSdkStateAdapter requires a parent Agent. Pass `parent` or create it inside an Agent context.\"\n );\n }\n\n this.parent = parent;\n this.agentClass = options.agent ?? ChatSdkStateAgent;\n this.defaultName = options.name ?? \"default\";\n this.keyShard = options.keyShard;\n this.shardKey = options.shardKey ?? defaultThreadShard;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async disconnect(): Promise<void> {\n this.connected = false;\n }\n\n async subscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).subscribe(threadId);\n }\n\n async unsubscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).unsubscribe(threadId);\n }\n\n async isSubscribed(threadId: string): Promise<boolean> {\n return (await this.stateAgent(threadId)).isSubscribed(threadId);\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<ChatSdkLock | null> {\n return (await this.stateAgent(threadId)).acquireLock(threadId, ttlMs);\n }\n\n async releaseLock(lock: ChatSdkLock): Promise<void> {\n await (\n await this.stateAgent(lock.threadId)\n ).releaseLock(lock.threadId, lock.token);\n }\n\n async extendLock(lock: ChatSdkLock, ttlMs: number): Promise<boolean> {\n return (await this.stateAgent(lock.threadId)).extendLock(\n lock.threadId,\n lock.token,\n ttlMs\n );\n }\n\n async forceReleaseLock(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).forceReleaseLock(threadId);\n }\n\n async enqueue(\n threadId: string,\n entry: ChatSdkQueueEntry,\n maxSize: number\n ): Promise<number> {\n return (await this.stateAgent(threadId)).enqueue(\n threadId,\n JSON.stringify(entry),\n maxSize\n );\n }\n\n async dequeue(threadId: string): Promise<ChatSdkQueueEntry | null> {\n const raw = await (await this.stateAgent(threadId)).popQueue(threadId);\n return raw === null\n ? null\n : parseStoredJson<ChatSdkQueueEntry>(raw, `queue entry for ${threadId}`);\n }\n\n async queueDepth(threadId: string): Promise<number> {\n return (await this.stateAgent(threadId)).queueDepth(threadId);\n }\n\n async appendToList(\n key: string,\n value: unknown,\n options?: { maxLength?: number; ttlMs?: number }\n ): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).listAppend(\n key,\n JSON.stringify(value),\n options?.maxLength,\n options?.ttlMs\n );\n }\n\n async getList<T = unknown>(key: string): Promise<T[]> {\n const raw = await (await this.stateAgentForKey(key)).listGet(key);\n return raw.map((value) =>\n parseStoredJson<T>(value, `list entry for ${key}`)\n );\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await (await this.stateAgentForKey(key)).cacheGet(key);\n return raw === null ? null : parseStoredJson<T>(raw, `cache key ${key}`);\n }\n\n async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).cacheSet(key, JSON.stringify(value), ttlMs);\n }\n\n async setIfNotExists<T = unknown>(\n key: string,\n value: T,\n ttlMs?: number\n ): Promise<boolean> {\n return (await this.stateAgentForKey(key)).cacheSetIfNotExists(\n key,\n JSON.stringify(value),\n ttlMs\n );\n }\n\n async delete(key: string): Promise<void> {\n await (await this.stateAgentForKey(key)).cacheDelete(key);\n }\n\n private async stateAgent(\n threadId?: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name = threadId ? this.shardKey(threadId) : this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private async stateAgentForKey(\n key: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name =\n this.keyShard?.(key) ??\n defaultKeyShard(key, this.shardKey) ??\n this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private ensureConnected(): void {\n if (!this.connected) {\n throw new Error(\"ChatSdkStateAdapter is not connected\");\n }\n }\n}\n","export {\n ChatSdkStateAdapter,\n defaultKeyShard,\n defaultThreadShard\n} from \"./adapter\";\nexport { ChatSdkStateAgent } from \"./agent\";\nexport type { ChatSdkStateParent, ChatSdkStateAdapterOptions } from \"./types\";\n\nimport { ChatSdkStateAdapter } from \"./adapter\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nexport function createChatSdkState(\n options: ChatSdkStateAdapterOptions = {}\n): ChatSdkStateAdapter {\n return new ChatSdkStateAdapter(options);\n}\n"],"mappings":";;AAaA,MAAM,sBAAsB;AAC5B,MAAM,0BAA0B;AAEhC,IAAa,oBAAb,cAAuC,MAAM;CAC3C,UAAgB;EACd,KAAK,QAAQ;EACb,KAAU,oBAAoB;CAChC;CAEA,UAAU,UAAwB;EAChC,KAAK,GAAG;;gBAEI,SAAS;;CAEvB;CAEA,YAAY,UAAwB;EAClC,KAAK,GAAG;;0BAEc,SAAS;;CAEjC;CAEA,aAAa,UAA2B;EAOtC,OAAO,KANW,GAAsB;;;0BAGlB,SAAS;;MAGnB,SAAS;CACvB;CAEA,MAAM,YACJ,UACA,OAC4B;EAC5B,MAAM,SAAS,KAAK,IAAI,QAAQ,sBAAsB;GACpD,MAAM,MAAM,KAAK,IAAI;GAErB,KAAK,IAAI,QAAQ,IAAI,KACnB,4EACA,UACA,GACF;GAQA,IANiB,KAAK,IAAI,QAAQ,IAC/B,KACC,kEACA,QACF,EACC,QACQ,EAAE,SAAS,GACpB,OAAO;GAGT,MAAM,QAAQ,OAAO,WAAW;GAChC,MAAM,YAAY,MAAM;GAExB,KAAK,IAAI,QAAQ,IAAI,KACnB,oFACA,UACA,OACA,SACF;GAEA,OAAO;IAAE;IAAU;IAAO;GAAU;EACtC,CAAC;EAED,MAAM,KAAK,yBAAyB,QAAQ,aAAa,IAAI;EAC7D,OAAO;CACT;CAEA,YAAY,UAAkB,OAAqB;EACjD,KAAK,GAAG;;0BAEc,SAAS,eAAe,MAAM;;CAEtD;CAEA,MAAM,WACJ,UACA,OACA,OACkB;EAClB,MAAM,SAAS,KAAK,IAAI,QAAQ,sBAAsB;GACpD,MAAM,MAAM,KAAK,IAAI;GAYrB,OAXa,KAAK,IAAI,QAAQ,IAC3B,KACC;;iCAGA,MAAM,OACN,UACA,OACA,GACF,EACC,QACO,EAAE,SAAS;EACvB,CAAC;EACD,IAAI,QACF,MAAM,KAAK,yBAAyB,KAAK,IAAI,IAAI,KAAK;EAExD,OAAO;CACT;CAEA,iBAAiB,UAAwB;EACvC,KAAK,GAAG;;0BAEc,SAAS;;CAEjC;CAEA,MAAM,QACJ,UACA,OACA,SACiB;EACjB,MAAM,SAAS,gBAAgB,KAAK;EAEpC,MAAM,QAAQ,KAAK,IAAI,QAAQ,sBAAsB;GACnD,KAAK,IAAI,QAAQ,IAAI,KACnB,oGACA,UACA,OACA,OAAO,YACP,OAAO,SACT;GAEA,KAAK,IAAI,QAAQ,IAAI,KACnB;;;;;YAMA,UACA,UACA,OACF;GAQA,OANY,KAAK,IAAI,QAAQ,IAC1B,KACC,0EACA,QACF,EACC,IACM,EAAE;EACb,CAAC;EACD,MAAM,KAAK,yBAAyB,OAAO,SAAS;EACpD,OAAO;CACT;CAEA,SAAS,UAAiC;EACxC,OAAO,KAAK,IAAI,QAAQ,sBAAsB;GAC5C,MAAM,MAAM,KAAK,IAAI;GAErB,KAAK,IAAI,QAAQ,IAAI,KACnB,4EACA,UACA,GACF;GAQA,MAAM,MANO,KAAK,IAAI,QAAQ,IAC3B,KACC,0FACA,QACF,EACC,QACY,EAAE;GACjB,IAAI,CAAC,KACH,OAAO;GAGT,KAAK,IAAI,QAAQ,IAAI,KACnB,iDACA,IAAI,EACN;GACA,OAAO,IAAI;EACb,CAAC;CACH;CAEA,WAAW,UAA0B;EAMnC,OAAO,KALW,GAAsB;;;0BAGlB,SAAS,oBAAoB,KAAK,IAAI,EAAE;MAElD,IAAI,SAAS;CAC3B;CAEA,MAAM,WACJ,KACA,OACA,WACA,OACe;EACf,MAAM,YAAY,SAAS,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ;EAE5D,KAAK,IAAI,QAAQ,sBAAsB;GACrC,KAAK,IAAI,QAAQ,IAAI,KACnB,8EACA,KACA,OACA,SACF;GAEA,IAAI,cAAc,MAGhB,KAAK,IAAI,QAAQ,IAAI,KACnB,gEACA,WACA,GACF;GAGF,IAAI,aAAa,QAAQ,YAAY,GACnC,KAAK,IAAI,QAAQ,IAAI,KACnB;;;;;cAMA,KACA,KACA,SACF;EAEJ,CAAC;EACD,MAAM,KAAK,yBAAyB,SAAS;CAC/C;CAEA,QAAQ,KAAuB;EAC7B,MAAM,MAAM,KAAK,IAAI;EAErB,KAAK,GAAG;;oBAEQ,IAAI;;4BAEI,IAAI;;EAG5B,OAAO,KAAK,GAAsB;;;oBAGlB,IAAI;;MAElB,KAAK,QAAQ,IAAI,KAAK;CAC1B;CAEA,SAAS,KAA4B;EACnC,OAAO,KAAK,eAAe,KAAK,KAAK,IAAI,CAAC;CAC5C;CAEA,MAAM,SAAS,KAAa,OAAe,OAA+B;EACxE,MAAM,YAAY,SAAS,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ;EAC5D,KAAK,iBAAiB,KAAK,OAAO,SAAS;EAC3C,MAAM,KAAK,yBAAyB,SAAS;CAC/C;CAEA,MAAM,oBACJ,KACA,OACA,OACkB;EAClB,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,WAAW,KAAK,IAAI,QAAQ,sBAAsB;GACtD,KAAK,IAAI,QAAQ,IAAI,KACnB,iGACA,KACA,GACF;GAEA,IAAI,KAAK,eAAe,KAAK,GAAG,MAAM,MACpC,OAAO;GAGT,MAAM,YAAY,SAAS,QAAQ,IAAI,MAAM,QAAQ;GACrD,KAAK,iBAAiB,KAAK,OAAO,SAAS;GAC3C,OAAO;EACT,CAAC;EACD,IAAI,UAAU;GACZ,MAAM,YAAY,SAAS,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ;GAC5D,MAAM,KAAK,yBAAyB,SAAS;EAC/C;EACA,OAAO;CACT;CAEA,YAAY,KAAmB;EAC7B,KAAK,GAAG;;oBAEQ,IAAI;;CAEtB;CAEA,MAAM,eAAe,SAAiD;EACpE,MAAM,UAAU,KAAK,oBAAoB;EACzC,IACE,SAAS,cAAc,KAAA,KACvB,QAAQ,kBAAkB,QAC1B,QAAQ,cAAc,QAAQ,eAE9B;EAGF,MAAM,MAAM,KAAK,IAAI;EACrB,KAAK,qBAAqB;EAE1B,KAAK,GAAG;;4BAEgB,IAAI;;EAE5B,KAAK,GAAG;;uDAE2C,IAAI;;EAEvD,KAAK,GAAG;;4BAEgB,IAAI;;EAE5B,KAAK,GAAG;;uDAE2C,IAAI;;EAEvD,MAAM,KAAK,oBAAoB;CACjC;CAEA,UAAwB;EACtB,KAAK,GAAG;;;;;EAMR,KAAK,GAAG;;;;;;;EAQR,KAAK,GAAG;;;;;;;EAQR,KAAK,GAAG;;;;;;;;;EAUR,KAAK,GAAG;;;;;;;;EASR,KAAK,GAAG;;;;;;EAOR,KAAK,GAAG;;;;EAIR,KAAK,GAAG;;;;;EAKR,KAAK,GAAG;;;;EAIR,KAAK,GAAG;;;;EAIR,KAAK,GAAG;;;;EAIR,KAAK,GAAG;;;;;CAKV;CAEA,eAAuB,KAAa,KAA4B;EAO9D,OAAO,KANW,GAAsB;;;oBAGxB,IAAI;kDAC0B,IAAI;MAEtC,IAAI,SAAS;CAC3B;CAEA,iBACE,KACA,OACA,WACM;EACN,KAAK,GAAG;;gBAEI,IAAI,IAAI,MAAM,IAAI,UAAU;;CAE1C;CAEA,MAAc,yBACZ,WACe;EACf,IAAI,cAAc,MAChB;EAGF,MAAM,KAAK,uBAAuB,SAAS;CAC7C;CAEA,MAAc,sBAAqC;EACjD,MAAM,OAAO,KAAK,WAAW;EAC7B,IAAI,SAAS,MAAM;GACjB,MAAM,UAAU,KAAK,oBAAoB;GACzC,IAAI,QAAQ,YACV,MAAM,KAAK,eAAe,QAAQ,UAAU,EAAE,YAAY,KAAK;GAEjE,KAAK,qBAAqB;GAC1B;EACF;EAEA,MAAM,KAAK,uBAAuB,IAAI;CACxC;CAEA,MAAc,uBAAuB,WAAkC;EACrE,MAAM,UAAU,KAAK,oBAAoB;EACzC,IAAI,QAAQ,kBAAkB,QAAQ,QAAQ,iBAAiB,WAC7D;EAGF,IAAI,QAAQ,YACV,MAAM,KAAK,eAAe,QAAQ,UAAU,EAAE,YAAY,KAAK;EAGjE,MAAM,eAAe,KAAK,IACxB,GACA,KAAK,MAAM,YAAY,KAAK,IAAI,KAAK,GAAI,CAC3C;EACA,MAAM,WAAW,MAAM,KAAK,SAAS,cAAc,kBAAkB,EACnE,UACF,CAAC;EACD,KAAK,qBAAqB,WAAW,SAAS,EAAE;CAClD;CAEA,aAAoC;EAalC,OAAO,KAZW,GAAkC;;;;;;;;;;;MAYxC,IAAI,cAAc;CAChC;CAEA,sBAGE;EACA,MAAM,OAAO,KAAK,GAAmC;;;sBAGnC,oBAAoB,IAAI,wBAAwB;;EAElE,MAAM,SAAS,IAAI,IAAI,KAAK,KAAK,QAAQ,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC;EAC9D,MAAM,gBAAgB,OAAO,OAAO,IAAI,mBAAmB,CAAC;EAC5D,OAAO;GACL,eAAe,OAAO,SAAS,aAAa,IAAI,gBAAgB;GAChE,YAAY,OAAO,IAAI,uBAAuB,KAAK;EACrD;CACF;CAEA,qBAA6B,WAAmB,YAA0B;EACxE,KAAK,GAAG;;gBAEI,oBAAoB,IAAI,OAAO,SAAS,EAAE;;EAEtD,KAAK,GAAG;;gBAEI,wBAAwB,IAAI,WAAW;;CAErD;CAEA,uBAAqC;EACnC,KAAK,GAAG;;sBAEU,oBAAoB,IAAI,wBAAwB;;CAEpE;AACF;AAEA,SAAS,gBAAgB,OAAiC;CACxD,MAAM,MAAM,KAAK,MAAM,KAAK;CAC5B,IAAI,OAAO,IAAI,eAAe,YAAY,OAAO,IAAI,cAAc,UACjE,MAAM,IAAI,MACR,8DACF;CAGF,OAAO;EACL,YAAY,IAAI;EAChB,WAAW,IAAI;CACjB;AACF;;;ACxhBA,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAEhC,SAAS,gBAAmB,KAAa,OAAkB;CACzD,IAAI;EACF,OAAO,KAAK,MAAM,GAAG;CACvB,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,6CAA6C,SAAS,EACpE,OAAO,MACT,CAAC;CACH;AACF;AAEA,SAAgB,mBAAmB,UAA0B;CAC3D,OAAO,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK;AACtD;AAEA,SAAgB,gBACd,KACA,cAA4C,oBACxB;CACpB,KAAK,MAAM,UAAU;EACnB;EACA;EACA;EACA;CACF,GACE,IAAI,IAAI,WAAW,MAAM,GACvB,OAAO,YAAY,IAAI,MAAM,OAAO,MAAM,CAAC;AAKjD;AAEA,IAAa,sBAAb,MAAyE;CAQvE,YAAY,UAAsC,CAAC,GAAG;EAFtD,KAAQ,YAAY;EAGlB,MAAM,SAAS,QAAQ,UAAU,gBAAgB,EAAE;EACnD,IAAI,CAAC,QACH,MAAM,IAAI,MACR,kGACF;EAGF,KAAK,SAAS;EACd,KAAK,aAAa,QAAQ,SAAS;EACnC,KAAK,cAAc,QAAQ,QAAQ;EACnC,KAAK,WAAW,QAAQ;EACxB,KAAK,WAAW,QAAQ,YAAY;CACtC;CAEA,MAAM,UAAyB;EAC7B,KAAK,YAAY;CACnB;CAEA,MAAM,aAA4B;EAChC,KAAK,YAAY;CACnB;CAEA,MAAM,UAAU,UAAiC;EAC/C,OAAO,MAAM,KAAK,WAAW,QAAQ,GAAG,UAAU,QAAQ;CAC5D;CAEA,MAAM,YAAY,UAAiC;EACjD,OAAO,MAAM,KAAK,WAAW,QAAQ,GAAG,YAAY,QAAQ;CAC9D;CAEA,MAAM,aAAa,UAAoC;EACrD,QAAQ,MAAM,KAAK,WAAW,QAAQ,GAAG,aAAa,QAAQ;CAChE;CAEA,MAAM,YACJ,UACA,OAC6B;EAC7B,QAAQ,MAAM,KAAK,WAAW,QAAQ,GAAG,YAAY,UAAU,KAAK;CACtE;CAEA,MAAM,YAAY,MAAkC;EAClD,OACE,MAAM,KAAK,WAAW,KAAK,QAAQ,GACnC,YAAY,KAAK,UAAU,KAAK,KAAK;CACzC;CAEA,MAAM,WAAW,MAAmB,OAAiC;EACnE,QAAQ,MAAM,KAAK,WAAW,KAAK,QAAQ,GAAG,WAC5C,KAAK,UACL,KAAK,OACL,KACF;CACF;CAEA,MAAM,iBAAiB,UAAiC;EACtD,OAAO,MAAM,KAAK,WAAW,QAAQ,GAAG,iBAAiB,QAAQ;CACnE;CAEA,MAAM,QACJ,UACA,OACA,SACiB;EACjB,QAAQ,MAAM,KAAK,WAAW,QAAQ,GAAG,QACvC,UACA,KAAK,UAAU,KAAK,GACpB,OACF;CACF;CAEA,MAAM,QAAQ,UAAqD;EACjE,MAAM,MAAM,OAAO,MAAM,KAAK,WAAW,QAAQ,GAAG,SAAS,QAAQ;EACrE,OAAO,QAAQ,OACX,OACA,gBAAmC,KAAK,mBAAmB,UAAU;CAC3E;CAEA,MAAM,WAAW,UAAmC;EAClD,QAAQ,MAAM,KAAK,WAAW,QAAQ,GAAG,WAAW,QAAQ;CAC9D;CAEA,MAAM,aACJ,KACA,OACA,SACe;EACf,OACE,MAAM,KAAK,iBAAiB,GAAG,GAC/B,WACA,KACA,KAAK,UAAU,KAAK,GACpB,SAAS,WACT,SAAS,KACX;CACF;CAEA,MAAM,QAAqB,KAA2B;EAEpD,QAAO,OADY,MAAM,KAAK,iBAAiB,GAAG,GAAG,QAAQ,GAAG,GACrD,KAAK,UACd,gBAAmB,OAAO,kBAAkB,KAAK,CACnD;CACF;CAEA,MAAM,IAAiB,KAAgC;EACrD,MAAM,MAAM,OAAO,MAAM,KAAK,iBAAiB,GAAG,GAAG,SAAS,GAAG;EACjE,OAAO,QAAQ,OAAO,OAAO,gBAAmB,KAAK,aAAa,KAAK;CACzE;CAEA,MAAM,IAAiB,KAAa,OAAU,OAA+B;EAC3E,OACE,MAAM,KAAK,iBAAiB,GAAG,GAC/B,SAAS,KAAK,KAAK,UAAU,KAAK,GAAG,KAAK;CAC9C;CAEA,MAAM,eACJ,KACA,OACA,OACkB;EAClB,QAAQ,MAAM,KAAK,iBAAiB,GAAG,GAAG,oBACxC,KACA,KAAK,UAAU,KAAK,GACpB,KACF;CACF;CAEA,MAAM,OAAO,KAA4B;EACvC,OAAO,MAAM,KAAK,iBAAiB,GAAG,GAAG,YAAY,GAAG;CAC1D;CAEA,MAAc,WACZ,UAC0C;EAC1C,KAAK,gBAAgB;EACrB,MAAM,OAAO,WAAW,KAAK,SAAS,QAAQ,IAAI,KAAK;EACvD,OAAO,KAAK,OAAO,SAAS,KAAK,YAAY,IAAI;CACnD;CAEA,MAAc,iBACZ,KAC0C;EAC1C,KAAK,gBAAgB;EACrB,MAAM,OACJ,KAAK,WAAW,GAAG,KACnB,gBAAgB,KAAK,KAAK,QAAQ,KAClC,KAAK;EACP,OAAO,KAAK,OAAO,SAAS,KAAK,YAAY,IAAI;CACnD;CAEA,kBAAgC;EAC9B,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MAAM,sCAAsC;CAE1D;AACF;;;AC3MA,SAAgB,mBACd,UAAsC,CAAC,GAClB;CACrB,OAAO,IAAI,oBAAoB,OAAO;AACxC"}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "durable objects"
10
10
  ],
11
11
  "type": "module",
12
- "version": "0.13.1",
12
+ "version": "0.13.2",
13
13
  "license": "MIT",
14
14
  "repository": {
15
15
  "directory": "packages/agents",
@@ -44,6 +44,7 @@
44
44
  "@x402/core": "^2.12.0",
45
45
  "@x402/evm": "^2.12.0",
46
46
  "ai": "^6.0.185",
47
+ "chat": "^4.29.0",
47
48
  "react": "^19.2.6",
48
49
  "vitest-browser-react": "^2.2.0",
49
50
  "zod": "^4.4.3"
@@ -55,6 +56,7 @@
55
56
  "@x402/core": "^2.0.0",
56
57
  "@x402/evm": "^2.0.0",
57
58
  "ai": "^6.0.0",
59
+ "chat": "^4.29.0",
58
60
  "react": "^19.0.0",
59
61
  "vite": ">=6.0.0 <9.0.0",
60
62
  "zod": "^4.0.0"
@@ -75,6 +77,9 @@
75
77
  "@x402/evm": {
76
78
  "optional": true
77
79
  },
80
+ "chat": {
81
+ "optional": true
82
+ },
78
83
  "vite": {
79
84
  "optional": true
80
85
  }
@@ -166,6 +171,11 @@
166
171
  "import": "./dist/chat/index.js",
167
172
  "require": "./dist/chat/index.js"
168
173
  },
174
+ "./chat-sdk": {
175
+ "types": "./dist/chat-sdk/index.d.ts",
176
+ "import": "./dist/chat-sdk/index.js",
177
+ "require": "./dist/chat-sdk/index.js"
178
+ },
169
179
  "./ai-chat-agent": {
170
180
  "types": "./dist/ai-chat-agent.d.ts",
171
181
  "import": "./dist/ai-chat-agent.js",