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