33 * Provides high-level API for managing threads, messages, resources, and working memory
44 */
55
6+ import { randomUUID } from "node:crypto" ;
67import type { Message } from "../../models/message.js" ;
78import type { MemoryStorage } from "./storage/interface.js" ;
89import type {
@@ -19,7 +20,7 @@ import type {
1920/**
2021 * Resolved configuration with all defaults applied
2122 */
22- type ResolvedMemoryConfig = Required < Pick < MemoryConfig , 'maxHistoryMessages' | 'autoInject' | 'autoSave' | 'trackTokenUsage' > > & Pick < MemoryConfig , 'contextWindow' > ;
23+ type ResolvedMemoryConfig = Required < Pick < MemoryConfig , 'maxHistoryMessages' > > & Pick < MemoryConfig , 'contextWindow' > ;
2324
2425/**
2526 * Memory class for managing conversation history, threads, and working memory
@@ -37,10 +38,7 @@ export class Memory {
3738 this . storage = storage ;
3839 this . config = {
3940 maxHistoryMessages : config . maxHistoryMessages ?? 10 ,
40- autoInject : config . autoInject ?? true ,
41- autoSave : config . autoSave ?? true ,
4241 ...( config . contextWindow !== undefined && { contextWindow : config . contextWindow } ) ,
43- trackTokenUsage : config . trackTokenUsage ?? false ,
4442 } ;
4543 }
4644
@@ -272,29 +270,43 @@ export class Memory {
272270 * Update an existing message
273271 * @param messageId The message ID
274272 * @param updates Partial message updates
275- * @returns The updated message, or null if storage doesn't support updates
273+ * @returns The updated message
274+ * @throws Error if storage doesn't support message updates
276275 */
277276 async updateMessage (
278277 messageId : string ,
279278 updates : Partial < Message > ,
280- ) : Promise < MemoryMessage | null > {
279+ ) : Promise < MemoryMessage > {
281280 if ( ! this . storage . updateMessage ) {
282- return null ;
281+ throw new Error (
282+ 'Message editing is not supported by this storage backend. ' +
283+ 'Please use a storage implementation that provides the updateMessage method.'
284+ ) ;
283285 }
284286
285- return await this . storage . updateMessage ( messageId , {
287+ const result = await this . storage . updateMessage ( messageId , {
286288 message : updates as Message ,
287289 } as Partial < MemoryMessage > ) ;
290+
291+ if ( ! result ) {
292+ throw new Error ( `Message with ID "${ messageId } " not found` ) ;
293+ }
294+
295+ return result ;
288296 }
289297
290298 /**
291299 * Get edit history for a message
292300 * @param messageId The message ID
293- * @returns Array of message versions, or empty if storage doesn't support history
301+ * @returns Array of message versions (oldest to newest)
302+ * @throws Error if storage doesn't support message history
294303 */
295304 async getMessageVersions ( messageId : string ) : Promise < MemoryMessage [ ] > {
296305 if ( ! this . storage . getMessageHistory ) {
297- return [ ] ;
306+ throw new Error (
307+ 'Message version history is not supported by this storage backend. ' +
308+ 'Please use a storage implementation that provides the getMessageHistory method.'
309+ ) ;
298310 }
299311
300312 return await this . storage . getMessageHistory ( messageId ) ;
@@ -305,22 +317,26 @@ export class Memory {
305317 /**
306318 * Get total token count for a thread
307319 * @param threadId The thread ID
308- * @returns Token count, or 0 if storage doesn't support token counting
320+ * @returns Token count
321+ * @throws Error if storage doesn't support token counting
309322 */
310323 async getThreadTokenCount ( threadId : string ) : Promise < number > {
311324 if ( ! this . storage . getThreadTokenCount ) {
312- return 0 ;
325+ throw new Error (
326+ 'Token counting is not supported by this storage backend. ' +
327+ 'Please use a storage implementation that provides the getThreadTokenCount method.'
328+ ) ;
313329 }
314330
315331 return await this . storage . getThreadTokenCount ( threadId ) ;
316332 }
317333
318334 /**
319335 * Get messages within a token budget
320- * Uses contextWindow config if available, otherwise falls back to maxHistoryMessages
321336 * @param threadId The thread ID
322- * @param maxTokens Optional max tokens (uses config if not provided )
337+ * @param maxTokens Max tokens (required - use config.contextWindow.maxTokens or provide explicitly )
323338 * @returns Array of messages within token budget
339+ * @throws Error if maxTokens not provided or storage doesn't support token-based selection
324340 */
325341 async getMessagesWithinBudget (
326342 threadId : string ,
@@ -329,9 +345,17 @@ export class Memory {
329345 const tokenLimit =
330346 maxTokens || this . config . contextWindow ?. maxTokens ;
331347
332- if ( ! tokenLimit || ! this . storage . getMessagesByTokenBudget ) {
333- // Fall back to regular getRecentMessages
334- return await this . getRecentMessages ( threadId ) ;
348+ if ( ! tokenLimit ) {
349+ throw new Error (
350+ 'Token budget not specified. Please provide maxTokens parameter or configure contextWindow.maxTokens.'
351+ ) ;
352+ }
353+
354+ if ( ! this . storage . getMessagesByTokenBudget ) {
355+ throw new Error (
356+ 'Token-based message selection is not supported by this storage backend. ' +
357+ 'Please use a storage implementation that provides the getMessagesByTokenBudget method.'
358+ ) ;
335359 }
336360
337361 const memoryMessages = await this . storage . getMessagesByTokenBudget (
@@ -346,11 +370,13 @@ export class Memory {
346370
347371 /**
348372 * Invalidate cache for messages
373+ * Note: This is a no-op if the storage backend doesn't support caching
349374 * @param threadId The thread ID
350375 * @param beforeDate Optional date - invalidate cache before this date
351376 */
352377 async invalidateCache ( threadId : string , beforeDate ?: Date ) : Promise < void > {
353378 if ( ! this . storage . invalidateCache ) {
379+ // No-op: Storage doesn't support caching
354380 return ;
355381 }
356382
@@ -382,10 +408,10 @@ export class Memory {
382408
383409 /**
384410 * Generate a unique ID for messages
385- * @returns A unique ID string
411+ * @returns A unique ID string (UUID v4)
386412 */
387413 private generateId ( ) : string {
388- return ` ${ Date . now ( ) } - ${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
414+ return randomUUID ( ) ;
389415 }
390416
391417 /**
0 commit comments