@igniter-js/agents 0.1.0

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,1021 @@
1
+ import { readFile, writeFile, mkdir } from 'fs/promises';
2
+ import { join } from 'path';
3
+
4
+ // src/adapters/memory.adapter.ts
5
+ var IgniterAgentInMemoryAdapter = class _IgniterAgentInMemoryAdapter {
6
+ /**
7
+ * Creates a new IgniterAgentInMemoryAdapter.
8
+ *
9
+ * @param options - Adapter configuration
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const adapter = new IgniterAgentInMemoryAdapter({
14
+ * namespace: 'myapp',
15
+ * maxMessages: 1000,
16
+ * maxChats: 100
17
+ * });
18
+ * ```
19
+ */
20
+ constructor(options = {}) {
21
+ /**
22
+ * Storage for working memory.
23
+ * Key format: `{namespace}:{scope}:{identifier}`
24
+ * @internal
25
+ */
26
+ this._workingMemory = /* @__PURE__ */ new Map();
27
+ /**
28
+ * Storage for conversation messages.
29
+ * Key format: `{namespace}:{chatId}`
30
+ * @internal
31
+ */
32
+ this._messages = /* @__PURE__ */ new Map();
33
+ /**
34
+ * Storage for chat sessions.
35
+ * Key format: `{namespace}:{chatId}`
36
+ * @internal
37
+ */
38
+ this._chats = /* @__PURE__ */ new Map();
39
+ this.options = {
40
+ namespace: options.namespace ?? "igniter",
41
+ maxMessages: options.maxMessages ?? 1e3,
42
+ maxChats: options.maxChats ?? 100
43
+ };
44
+ }
45
+ /**
46
+ * Factory method to create a new adapter instance.
47
+ * @param options - Adapter configuration
48
+ * @returns A new IgniterAgentInMemoryAdapter instance
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const adapter = IgniterAgentInMemoryAdapter.create({
53
+ * namespace: 'test',
54
+ * });
55
+ * ```
56
+ */
57
+ static create(options) {
58
+ return new _IgniterAgentInMemoryAdapter(options);
59
+ }
60
+ /* ---------------------------------------------------------------------------
61
+ * HELPER METHODS
62
+ * --------------------------------------------------------------------------- */
63
+ /**
64
+ * Generates a storage key with namespace prefix.
65
+ * @internal
66
+ */
67
+ _key(...parts) {
68
+ return [this.options.namespace, ...parts].join(":");
69
+ }
70
+ /* ---------------------------------------------------------------------------
71
+ * LIFECYCLE METHODS
72
+ * --------------------------------------------------------------------------- */
73
+ /**
74
+ * Connects to the storage backend (no-op for in-memory).
75
+ *
76
+ * @description
77
+ * In-memory adapter doesn't require connection setup.
78
+ * This method exists for interface compatibility.
79
+ */
80
+ async connect() {
81
+ }
82
+ /**
83
+ * Disconnects from the storage backend (no-op for in-memory).
84
+ *
85
+ * @description
86
+ * In-memory adapter doesn't require disconnection.
87
+ * This method exists for interface compatibility.
88
+ */
89
+ async disconnect() {
90
+ }
91
+ /**
92
+ * Checks if the adapter is connected.
93
+ *
94
+ * @returns Always true for in-memory adapter
95
+ */
96
+ isConnected() {
97
+ return true;
98
+ }
99
+ /**
100
+ * Clears all stored data.
101
+ *
102
+ * @description
103
+ * Removes all working memory, messages, and chat sessions.
104
+ * Use with caution.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * // Clear all data (useful for testing)
109
+ * await adapter.clear();
110
+ * ```
111
+ */
112
+ async clear() {
113
+ this._workingMemory.clear();
114
+ this._messages.clear();
115
+ this._chats.clear();
116
+ }
117
+ /* ---------------------------------------------------------------------------
118
+ * WORKING MEMORY METHODS
119
+ * --------------------------------------------------------------------------- */
120
+ /**
121
+ * Gets working memory for a scope and identifier.
122
+ *
123
+ * @param params - The scope and identifier
124
+ * @returns The working memory or null if not found
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const memory = await adapter.getWorkingMemory({
129
+ * scope: 'chat',
130
+ * identifier: 'chat_123'
131
+ * });
132
+ *
133
+ * if (memory) {
134
+ * console.log('Content:', memory.content);
135
+ * console.log('Updated:', memory.updatedAt);
136
+ * }
137
+ * ```
138
+ */
139
+ async getWorkingMemory(params) {
140
+ const key = this._key("memory", params.scope, params.identifier);
141
+ return this._workingMemory.get(key) ?? null;
142
+ }
143
+ /**
144
+ * Updates working memory for a scope and identifier.
145
+ *
146
+ * @param params - The scope, identifier, and new content
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * await adapter.updateWorkingMemory({
151
+ * scope: 'chat',
152
+ * identifier: 'chat_123',
153
+ * content: `
154
+ * ## User Preferences
155
+ * - Prefers TypeScript
156
+ * - Uses VS Code
157
+ * `
158
+ * });
159
+ * ```
160
+ */
161
+ async updateWorkingMemory(params) {
162
+ const key = this._key("memory", params.scope, params.identifier);
163
+ this._workingMemory.set(key, {
164
+ content: params.content,
165
+ updatedAt: /* @__PURE__ */ new Date()
166
+ });
167
+ }
168
+ /* ---------------------------------------------------------------------------
169
+ * MESSAGE METHODS
170
+ * --------------------------------------------------------------------------- */
171
+ /**
172
+ * Saves a message to the conversation history.
173
+ *
174
+ * @param message - The message to save
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * await adapter.saveMessage({
179
+ * chatId: 'chat_123',
180
+ * userId: 'user_456',
181
+ * role: 'user',
182
+ * content: 'How do I use TypeScript?',
183
+ * timestamp: new Date()
184
+ * });
185
+ * ```
186
+ */
187
+ async saveMessage(message) {
188
+ const key = this._key("messages", message.chatId);
189
+ let messages = this._messages.get(key);
190
+ if (!messages) {
191
+ messages = [];
192
+ this._messages.set(key, messages);
193
+ }
194
+ messages.push(message);
195
+ if (messages.length > this.options.maxMessages) {
196
+ messages.splice(0, messages.length - this.options.maxMessages);
197
+ }
198
+ }
199
+ /**
200
+ * Gets messages from the conversation history.
201
+ *
202
+ * @typeParam T - The message type to return
203
+ * @param params - Query parameters
204
+ * @returns Array of messages
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const messages = await adapter.getMessages({
209
+ * chatId: 'chat_123',
210
+ * limit: 50
211
+ * });
212
+ *
213
+ * for (const msg of messages) {
214
+ * console.log(`[${msg.role}]: ${msg.content}`);
215
+ * }
216
+ * ```
217
+ */
218
+ async getMessages(params) {
219
+ const key = this._key("messages", params.chatId);
220
+ let messages = this._messages.get(key) ?? [];
221
+ if (params.userId) {
222
+ messages = messages.filter((m) => m.userId === params.userId);
223
+ }
224
+ if (params.limit && params.limit > 0) {
225
+ messages = messages.slice(-params.limit);
226
+ }
227
+ return messages.map((m) => ({
228
+ id: `${m.chatId}-${m.timestamp.getTime()}`,
229
+ role: m.role,
230
+ content: m.content,
231
+ createdAt: m.timestamp
232
+ }));
233
+ }
234
+ /* ---------------------------------------------------------------------------
235
+ * CHAT SESSION METHODS
236
+ * --------------------------------------------------------------------------- */
237
+ /**
238
+ * Saves or updates a chat session.
239
+ *
240
+ * @param chat - The chat session to save
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * await adapter.saveChat({
245
+ * chatId: 'chat_123',
246
+ * userId: 'user_456',
247
+ * title: 'TypeScript Help',
248
+ * createdAt: new Date(),
249
+ * updatedAt: new Date(),
250
+ * messageCount: 0
251
+ * });
252
+ * ```
253
+ */
254
+ async saveChat(chat) {
255
+ const key = this._key("chats", chat.chatId);
256
+ this._chats.set(key, chat);
257
+ if (this._chats.size > this.options.maxChats) {
258
+ const entries = Array.from(this._chats.entries()).sort((a, b) => a[1].updatedAt.getTime() - b[1].updatedAt.getTime());
259
+ const toRemove = entries.slice(0, this._chats.size - this.options.maxChats);
260
+ for (const [k] of toRemove) {
261
+ this._chats.delete(k);
262
+ }
263
+ }
264
+ }
265
+ /**
266
+ * Gets chat sessions matching the query parameters.
267
+ *
268
+ * @param params - Query parameters
269
+ * @returns Array of chat sessions
270
+ *
271
+ * @example
272
+ * ```typescript
273
+ * const chats = await adapter.getChats({
274
+ * userId: 'user_456',
275
+ * search: 'typescript',
276
+ * limit: 10
277
+ * });
278
+ *
279
+ * for (const chat of chats) {
280
+ * console.log(`${chat.title} (${chat.messageCount} messages)`);
281
+ * }
282
+ * ```
283
+ */
284
+ async getChats(params) {
285
+ let chats = Array.from(this._chats.values());
286
+ if (params.userId) {
287
+ chats = chats.filter((c) => c.userId === params.userId);
288
+ }
289
+ if (params.search) {
290
+ const searchLower = params.search.toLowerCase();
291
+ chats = chats.filter(
292
+ (c) => c.title?.toLowerCase().includes(searchLower)
293
+ );
294
+ }
295
+ chats.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
296
+ if (params.limit && params.limit > 0) {
297
+ chats = chats.slice(0, params.limit);
298
+ }
299
+ return chats;
300
+ }
301
+ /**
302
+ * Gets a specific chat session by ID.
303
+ *
304
+ * @param chatId - The chat ID
305
+ * @returns The chat session or null if not found
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const chat = await adapter.getChat('chat_123');
310
+ *
311
+ * if (chat) {
312
+ * console.log('Title:', chat.title);
313
+ * console.log('Messages:', chat.messageCount);
314
+ * }
315
+ * ```
316
+ */
317
+ async getChat(chatId) {
318
+ const key = this._key("chats", chatId);
319
+ return this._chats.get(key) ?? null;
320
+ }
321
+ /**
322
+ * Updates the title of a chat session.
323
+ *
324
+ * @param chatId - The chat ID
325
+ * @param title - The new title
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * await adapter.updateChatTitle('chat_123', 'New Title');
330
+ * ```
331
+ */
332
+ async updateChatTitle(chatId, title) {
333
+ const key = this._key("chats", chatId);
334
+ const chat = this._chats.get(key);
335
+ if (chat) {
336
+ chat.title = title;
337
+ chat.updatedAt = /* @__PURE__ */ new Date();
338
+ }
339
+ }
340
+ /**
341
+ * Deletes a chat session and its messages.
342
+ *
343
+ * @param chatId - The chat ID to delete
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * await adapter.deleteChat('chat_123');
348
+ * ```
349
+ */
350
+ async deleteChat(chatId) {
351
+ const chatKey = this._key("chats", chatId);
352
+ const messagesKey = this._key("messages", chatId);
353
+ this._chats.delete(chatKey);
354
+ this._messages.delete(messagesKey);
355
+ }
356
+ /* ---------------------------------------------------------------------------
357
+ * STATS METHODS
358
+ * --------------------------------------------------------------------------- */
359
+ /**
360
+ * Gets storage statistics.
361
+ *
362
+ * @returns Current storage stats
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const stats = await adapter.getStats();
367
+ * console.log(`Storing ${stats.messageCount} messages`);
368
+ * console.log(`Across ${stats.chatCount} chats`);
369
+ * ```
370
+ */
371
+ async getStats() {
372
+ let messageCount = 0;
373
+ for (const messages of this._messages.values()) {
374
+ messageCount += messages.length;
375
+ }
376
+ return {
377
+ workingMemoryCount: this._workingMemory.size,
378
+ messageCount,
379
+ chatCount: this._chats.size,
380
+ timestamp: /* @__PURE__ */ new Date()
381
+ };
382
+ }
383
+ };
384
+ var IgniterAgentJSONFileAdapter = class _IgniterAgentJSONFileAdapter {
385
+ /**
386
+ * Creates a new IgniterAgentJSONFileAdapter.
387
+ *
388
+ * @param options - Adapter configuration
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * const adapter = new IgniterAgentJSONFileAdapter({
393
+ * dataDir: './memory',
394
+ * namespace: 'myapp'
395
+ * });
396
+ * ```
397
+ */
398
+ constructor(options = {}) {
399
+ /**
400
+ * Whether the adapter is currently connected and ready to use.
401
+ * @internal
402
+ */
403
+ this._connected = false;
404
+ /**
405
+ * In-memory cache of working memory entries.
406
+ * @internal
407
+ */
408
+ this._workingMemoryCache = /* @__PURE__ */ new Map();
409
+ /**
410
+ * In-memory cache of messages.
411
+ * @internal
412
+ */
413
+ this._messagesCache = /* @__PURE__ */ new Map();
414
+ /**
415
+ * In-memory cache of chat sessions.
416
+ * @internal
417
+ */
418
+ this._chatsCache = /* @__PURE__ */ new Map();
419
+ this.options = {
420
+ dataDir: options.dataDir ?? "./igniter-agent-memory",
421
+ namespace: options.namespace ?? "igniter",
422
+ autoSync: options.autoSync ?? true,
423
+ debug: options.debug ?? false,
424
+ maxMessages: options.maxMessages ?? 1e3,
425
+ maxChats: options.maxChats ?? 100
426
+ };
427
+ }
428
+ /**
429
+ * Factory method to create a new adapter instance.
430
+ * @param options - Adapter configuration
431
+ * @returns A new IgniterAgentJSONFileAdapter instance
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * const adapter = IgniterAgentJSONFileAdapter.create({
436
+ * dataDir: './data',
437
+ * });
438
+ * ```
439
+ */
440
+ static create(options) {
441
+ return new _IgniterAgentJSONFileAdapter(options);
442
+ }
443
+ /* ---------------------------------------------------------------------------
444
+ * PRIVATE HELPER METHODS
445
+ * --------------------------------------------------------------------------- */
446
+ /**
447
+ * Logs debug messages if debug mode is enabled.
448
+ * @internal
449
+ */
450
+ _log(message, data) {
451
+ if (this.options.debug) {
452
+ console.log(`[IgniterAgentJSONFileAdapter] ${message}`, data ?? "");
453
+ }
454
+ }
455
+ /**
456
+ * Gets the path for the working memory JSON file.
457
+ * @internal
458
+ */
459
+ _getWorkingMemoryPath() {
460
+ return join(this.options.dataDir, "working-memory.json");
461
+ }
462
+ /**
463
+ * Gets the path for the chats JSON file.
464
+ * @internal
465
+ */
466
+ _getChatsPath() {
467
+ return join(this.options.dataDir, "chats.json");
468
+ }
469
+ /**
470
+ * Gets the directory for message files.
471
+ * @internal
472
+ */
473
+ _getMessagesDir() {
474
+ return join(this.options.dataDir, "messages");
475
+ }
476
+ /**
477
+ * Gets the path for a specific chat's messages JSON file.
478
+ * @internal
479
+ */
480
+ _getMessagesPath(chatId) {
481
+ return join(this._getMessagesDir(), `${chatId}.json`);
482
+ }
483
+ /**
484
+ * Generates a cache key with namespace prefix.
485
+ * @internal
486
+ */
487
+ _key(...parts) {
488
+ return [this.options.namespace, ...parts].join(":");
489
+ }
490
+ /**
491
+ * Loads working memory from disk.
492
+ * @internal
493
+ */
494
+ async _loadWorkingMemory() {
495
+ try {
496
+ const path = this._getWorkingMemoryPath();
497
+ const content = await readFile(path, "utf-8");
498
+ const data = JSON.parse(content);
499
+ this._workingMemoryCache.clear();
500
+ for (const [key, value] of Object.entries(data)) {
501
+ this._workingMemoryCache.set(key, {
502
+ content: value.content,
503
+ updatedAt: new Date(value.updatedAt)
504
+ });
505
+ }
506
+ this._log("Loaded working memory", {
507
+ entries: this._workingMemoryCache.size
508
+ });
509
+ } catch (err) {
510
+ this._log("Working memory file not found, starting fresh");
511
+ }
512
+ }
513
+ /**
514
+ * Loads chats from disk.
515
+ * @internal
516
+ */
517
+ async _loadChats() {
518
+ try {
519
+ const path = this._getChatsPath();
520
+ const content = await readFile(path, "utf-8");
521
+ const data = JSON.parse(content);
522
+ this._chatsCache.clear();
523
+ for (const [key, value] of Object.entries(data)) {
524
+ const chat = value;
525
+ this._chatsCache.set(key, {
526
+ chatId: chat.chatId,
527
+ userId: chat.userId,
528
+ title: chat.title,
529
+ createdAt: new Date(chat.createdAt),
530
+ updatedAt: new Date(chat.updatedAt),
531
+ messageCount: chat.messageCount
532
+ });
533
+ }
534
+ this._log("Loaded chats", { entries: this._chatsCache.size });
535
+ } catch (err) {
536
+ this._log("Chats file not found, starting fresh");
537
+ }
538
+ }
539
+ /**
540
+ * Loads a specific chat's messages from disk.
541
+ * @internal
542
+ */
543
+ async _loadMessages(chatId) {
544
+ try {
545
+ const path = this._getMessagesPath(chatId);
546
+ const content = await readFile(path, "utf-8");
547
+ const data = JSON.parse(content);
548
+ const key = this._key("messages", chatId);
549
+ const messages = data.map(
550
+ (msg) => ({
551
+ chatId: msg.chatId,
552
+ userId: msg.userId,
553
+ role: msg.role,
554
+ content: msg.content,
555
+ timestamp: new Date(msg.timestamp)
556
+ })
557
+ );
558
+ this._messagesCache.set(key, messages);
559
+ this._log("Loaded messages", { chatId, count: messages.length });
560
+ } catch (err) {
561
+ this._log("Messages file not found for chat", { chatId });
562
+ }
563
+ }
564
+ /**
565
+ * Saves working memory to disk.
566
+ * @internal
567
+ */
568
+ async _saveWorkingMemory() {
569
+ const path = this._getWorkingMemoryPath();
570
+ const data = {};
571
+ for (const [key, value] of this._workingMemoryCache.entries()) {
572
+ data[key] = {
573
+ content: value.content,
574
+ updatedAt: value.updatedAt.toISOString()
575
+ };
576
+ }
577
+ await writeFile(path, JSON.stringify(data, null, 2));
578
+ this._log("Saved working memory");
579
+ }
580
+ /**
581
+ * Saves chats to disk.
582
+ * @internal
583
+ */
584
+ async _saveChats() {
585
+ const path = this._getChatsPath();
586
+ const data = {};
587
+ for (const [key, value] of this._chatsCache.entries()) {
588
+ data[key] = {
589
+ chatId: value.chatId,
590
+ userId: value.userId,
591
+ title: value.title,
592
+ createdAt: value.createdAt.toISOString(),
593
+ updatedAt: value.updatedAt.toISOString(),
594
+ messageCount: value.messageCount
595
+ };
596
+ }
597
+ await writeFile(path, JSON.stringify(data, null, 2));
598
+ this._log("Saved chats");
599
+ }
600
+ /**
601
+ * Saves messages for a specific chat to disk.
602
+ * @internal
603
+ */
604
+ async _saveMessages(chatId) {
605
+ const key = this._key("messages", chatId);
606
+ const messages = this._messagesCache.get(key) ?? [];
607
+ const path = this._getMessagesPath(chatId);
608
+ const data = messages.map((msg) => ({
609
+ chatId: msg.chatId,
610
+ userId: msg.userId,
611
+ role: msg.role,
612
+ content: msg.content,
613
+ timestamp: msg.timestamp.toISOString()
614
+ }));
615
+ await writeFile(path, JSON.stringify(data, null, 2));
616
+ this._log("Saved messages", { chatId, count: messages.length });
617
+ }
618
+ /* ---------------------------------------------------------------------------
619
+ * LIFECYCLE METHODS
620
+ * --------------------------------------------------------------------------- */
621
+ /**
622
+ * Connects to the storage backend and loads data from disk.
623
+ *
624
+ * @description
625
+ * Creates necessary directories and loads all data into memory cache.
626
+ * Must be called before using the adapter.
627
+ *
628
+ * @throws {Error} If directory creation fails
629
+ *
630
+ * @example
631
+ * ```typescript
632
+ * const adapter = new IgniterAgentJSONFileAdapter();
633
+ * await adapter.connect();
634
+ * ```
635
+ */
636
+ async connect() {
637
+ try {
638
+ await mkdir(this.options.dataDir, { recursive: true });
639
+ await mkdir(this._getMessagesDir(), { recursive: true });
640
+ await this._loadWorkingMemory();
641
+ await this._loadChats();
642
+ this._connected = true;
643
+ this._log("Connected successfully", {
644
+ dataDir: this.options.dataDir
645
+ });
646
+ } catch (err) {
647
+ this._log("Connection failed", err);
648
+ throw err;
649
+ }
650
+ }
651
+ /**
652
+ * Disconnects from the storage backend.
653
+ *
654
+ * @description
655
+ * Syncs any pending changes to disk before disconnecting.
656
+ *
657
+ * @example
658
+ * ```typescript
659
+ * await adapter.disconnect();
660
+ * ```
661
+ */
662
+ async disconnect() {
663
+ if (this._connected) {
664
+ await this.sync();
665
+ this._connected = false;
666
+ this._log("Disconnected");
667
+ }
668
+ }
669
+ /**
670
+ * Checks if the adapter is connected and ready to use.
671
+ *
672
+ * @returns True if connected, false otherwise
673
+ */
674
+ isConnected() {
675
+ return this._connected;
676
+ }
677
+ /**
678
+ * Manually syncs all in-memory data to disk.
679
+ *
680
+ * @description
681
+ * Called automatically if autoSync is enabled. Can be called manually
682
+ * to ensure data is persisted to disk.
683
+ *
684
+ * @example
685
+ * ```typescript
686
+ * await adapter.updateWorkingMemory({ ... });
687
+ * await adapter.sync(); // Ensure written to disk
688
+ * ```
689
+ */
690
+ async sync() {
691
+ try {
692
+ await this._saveWorkingMemory();
693
+ await this._saveChats();
694
+ for (const chatId of this._messagesCache.keys()) {
695
+ const idParts = chatId.split(":");
696
+ if (idParts.length > 0) {
697
+ const actualChatId = idParts[idParts.length - 1];
698
+ await this._saveMessages(actualChatId);
699
+ }
700
+ }
701
+ this._log("Synced all data to disk");
702
+ } catch (err) {
703
+ this._log("Sync failed", err);
704
+ throw err;
705
+ }
706
+ }
707
+ /**
708
+ * Clears all stored data from disk and memory.
709
+ *
710
+ * @description
711
+ * Removes all working memory, messages, and chat sessions.
712
+ * Use with caution.
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * await adapter.clear();
717
+ * ```
718
+ */
719
+ async clear() {
720
+ this._workingMemoryCache.clear();
721
+ this._messagesCache.clear();
722
+ this._chatsCache.clear();
723
+ if (this.options.autoSync) {
724
+ await this.sync();
725
+ }
726
+ this._log("Cleared all data");
727
+ }
728
+ /* ---------------------------------------------------------------------------
729
+ * WORKING MEMORY METHODS
730
+ * --------------------------------------------------------------------------- */
731
+ /**
732
+ * Gets working memory for a scope and identifier.
733
+ *
734
+ * @param params - The scope and identifier
735
+ * @returns The working memory or null if not found
736
+ *
737
+ * @example
738
+ * ```typescript
739
+ * const memory = await adapter.getWorkingMemory({
740
+ * scope: 'chat',
741
+ * identifier: 'chat_123'
742
+ * });
743
+ *
744
+ * if (memory) {
745
+ * console.log('Content:', memory.content);
746
+ * }
747
+ * ```
748
+ */
749
+ async getWorkingMemory(params) {
750
+ const key = this._key("memory", params.scope, params.identifier);
751
+ return this._workingMemoryCache.get(key) ?? null;
752
+ }
753
+ /**
754
+ * Updates working memory for a scope and identifier.
755
+ *
756
+ * @param params - The scope, identifier, and new content
757
+ *
758
+ * @example
759
+ * ```typescript
760
+ * await adapter.updateWorkingMemory({
761
+ * scope: 'chat',
762
+ * identifier: 'chat_123',
763
+ * content: 'User prefers TypeScript'
764
+ * });
765
+ * ```
766
+ */
767
+ async updateWorkingMemory(params) {
768
+ const key = this._key("memory", params.scope, params.identifier);
769
+ this._workingMemoryCache.set(key, {
770
+ content: params.content,
771
+ updatedAt: /* @__PURE__ */ new Date()
772
+ });
773
+ if (this.options.autoSync) {
774
+ await this._saveWorkingMemory();
775
+ }
776
+ }
777
+ /* ---------------------------------------------------------------------------
778
+ * MESSAGE METHODS
779
+ * --------------------------------------------------------------------------- */
780
+ /**
781
+ * Saves a message to the conversation history.
782
+ *
783
+ * @param message - The message to save
784
+ *
785
+ * @example
786
+ * ```typescript
787
+ * await adapter.saveMessage({
788
+ * chatId: 'chat_123',
789
+ * userId: 'user_456',
790
+ * role: 'user',
791
+ * content: 'How do I use TypeScript?',
792
+ * timestamp: new Date()
793
+ * });
794
+ * ```
795
+ */
796
+ async saveMessage(message) {
797
+ const key = this._key("messages", message.chatId);
798
+ let messages = this._messagesCache.get(key);
799
+ if (!messages) {
800
+ messages = [];
801
+ this._messagesCache.set(key, messages);
802
+ }
803
+ messages.push(message);
804
+ if (messages.length > this.options.maxMessages) {
805
+ messages.splice(0, messages.length - this.options.maxMessages);
806
+ }
807
+ if (this.options.autoSync) {
808
+ await this._saveMessages(message.chatId);
809
+ }
810
+ }
811
+ /**
812
+ * Gets messages from the conversation history.
813
+ *
814
+ * @typeParam T - The message type to return
815
+ * @param params - Query parameters
816
+ * @returns Array of messages
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * const messages = await adapter.getMessages({
821
+ * chatId: 'chat_123',
822
+ * limit: 50
823
+ * });
824
+ *
825
+ * for (const msg of messages) {
826
+ * console.log(`[${msg.role}]: ${msg.content}`);
827
+ * }
828
+ * ```
829
+ */
830
+ async getMessages(params) {
831
+ const key = this._key("messages", params.chatId);
832
+ if (!this._messagesCache.has(key)) {
833
+ await this._loadMessages(params.chatId);
834
+ }
835
+ let messages = this._messagesCache.get(key) ?? [];
836
+ if (params.userId) {
837
+ messages = messages.filter((m) => m.userId === params.userId);
838
+ }
839
+ if (params.limit && params.limit > 0) {
840
+ messages = messages.slice(-params.limit);
841
+ }
842
+ return messages.map((m) => ({
843
+ id: `${m.chatId}-${m.timestamp.getTime()}`,
844
+ role: m.role,
845
+ content: m.content,
846
+ createdAt: m.timestamp
847
+ }));
848
+ }
849
+ /* ---------------------------------------------------------------------------
850
+ * CHAT SESSION METHODS
851
+ * --------------------------------------------------------------------------- */
852
+ /**
853
+ * Saves or updates a chat session.
854
+ *
855
+ * @param chat - The chat session to save
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * await adapter.saveChat({
860
+ * chatId: 'chat_123',
861
+ * userId: 'user_456',
862
+ * title: 'TypeScript Help',
863
+ * createdAt: new Date(),
864
+ * updatedAt: new Date(),
865
+ * messageCount: 0
866
+ * });
867
+ * ```
868
+ */
869
+ async saveChat(chat) {
870
+ const key = this._key("chats", chat.chatId);
871
+ this._chatsCache.set(key, chat);
872
+ if (this._chatsCache.size > this.options.maxChats) {
873
+ const entries = Array.from(this._chatsCache.entries()).sort(
874
+ (a, b) => a[1].updatedAt.getTime() - b[1].updatedAt.getTime()
875
+ );
876
+ const toRemove = entries.slice(
877
+ 0,
878
+ this._chatsCache.size - this.options.maxChats
879
+ );
880
+ for (const [k] of toRemove) {
881
+ this._chatsCache.delete(k);
882
+ }
883
+ }
884
+ if (this.options.autoSync) {
885
+ await this._saveChats();
886
+ }
887
+ }
888
+ /**
889
+ * Gets chat sessions matching the query parameters.
890
+ *
891
+ * @param params - Query parameters
892
+ * @returns Array of chat sessions
893
+ *
894
+ * @example
895
+ * ```typescript
896
+ * const chats = await adapter.getChats({
897
+ * userId: 'user_456',
898
+ * search: 'typescript',
899
+ * limit: 10
900
+ * });
901
+ *
902
+ * for (const chat of chats) {
903
+ * console.log(`${chat.title} (${chat.messageCount} messages)`);
904
+ * }
905
+ * ```
906
+ */
907
+ async getChats(params) {
908
+ let chats = Array.from(this._chatsCache.values());
909
+ if (params.userId) {
910
+ chats = chats.filter((c) => c.userId === params.userId);
911
+ }
912
+ if (params.search) {
913
+ const searchLower = params.search.toLowerCase();
914
+ chats = chats.filter(
915
+ (c) => c.title?.toLowerCase().includes(searchLower)
916
+ );
917
+ }
918
+ chats.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
919
+ if (params.limit && params.limit > 0) {
920
+ chats = chats.slice(0, params.limit);
921
+ }
922
+ return chats;
923
+ }
924
+ /**
925
+ * Gets a specific chat session by ID.
926
+ *
927
+ * @param chatId - The chat ID
928
+ * @returns The chat session or null if not found
929
+ *
930
+ * @example
931
+ * ```typescript
932
+ * const chat = await adapter.getChat('chat_123');
933
+ *
934
+ * if (chat) {
935
+ * console.log('Title:', chat.title);
936
+ * }
937
+ * ```
938
+ */
939
+ async getChat(chatId) {
940
+ const key = this._key("chats", chatId);
941
+ return this._chatsCache.get(key) ?? null;
942
+ }
943
+ /**
944
+ * Updates the title of a chat session.
945
+ *
946
+ * @param chatId - The chat ID
947
+ * @param title - The new title
948
+ *
949
+ * @example
950
+ * ```typescript
951
+ * await adapter.updateChatTitle('chat_123', 'New Title');
952
+ * ```
953
+ */
954
+ async updateChatTitle(chatId, title) {
955
+ const key = this._key("chats", chatId);
956
+ const chat = this._chatsCache.get(key);
957
+ if (chat) {
958
+ chat.title = title;
959
+ chat.updatedAt = /* @__PURE__ */ new Date();
960
+ if (this.options.autoSync) {
961
+ await this._saveChats();
962
+ }
963
+ }
964
+ }
965
+ /**
966
+ * Deletes a chat session and its messages.
967
+ *
968
+ * @param chatId - The chat ID to delete
969
+ *
970
+ * @example
971
+ * ```typescript
972
+ * await adapter.deleteChat('chat_123');
973
+ * ```
974
+ */
975
+ async deleteChat(chatId) {
976
+ const chatKey = this._key("chats", chatId);
977
+ const messagesKey = this._key("messages", chatId);
978
+ this._chatsCache.delete(chatKey);
979
+ this._messagesCache.delete(messagesKey);
980
+ if (this.options.autoSync) {
981
+ await this._saveChats();
982
+ try {
983
+ const path = this._getMessagesPath(chatId);
984
+ await readFile(path);
985
+ this._log("Messages file for deleted chat still exists");
986
+ } catch {
987
+ }
988
+ }
989
+ }
990
+ /* ---------------------------------------------------------------------------
991
+ * STATS METHODS
992
+ * --------------------------------------------------------------------------- */
993
+ /**
994
+ * Gets storage statistics.
995
+ *
996
+ * @returns Current storage stats
997
+ *
998
+ * @example
999
+ * ```typescript
1000
+ * const stats = await adapter.getStats();
1001
+ * console.log(`Storing ${stats.messageCount} messages`);
1002
+ * console.log(`Across ${stats.chatCount} chats`);
1003
+ * ```
1004
+ */
1005
+ async getStats() {
1006
+ let messageCount = 0;
1007
+ for (const messages of this._messagesCache.values()) {
1008
+ messageCount += messages.length;
1009
+ }
1010
+ return {
1011
+ workingMemoryCount: this._workingMemoryCache.size,
1012
+ messageCount,
1013
+ chatCount: this._chatsCache.size,
1014
+ timestamp: /* @__PURE__ */ new Date()
1015
+ };
1016
+ }
1017
+ };
1018
+
1019
+ export { IgniterAgentInMemoryAdapter, IgniterAgentJSONFileAdapter };
1020
+ //# sourceMappingURL=index.mjs.map
1021
+ //# sourceMappingURL=index.mjs.map