@elizaos/plugin-memory 1.0.5 → 2.0.1

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.
Files changed (36) hide show
  1. package/README.md +339 -207
  2. package/dist/browser/index.browser.js +348 -151
  3. package/dist/browser/index.browser.js.map +23 -13
  4. package/dist/cjs/index.node.cjs +2200 -1047
  5. package/dist/cjs/index.node.js.map +23 -13
  6. package/dist/evaluators/consolidation.d.ts +19 -0
  7. package/dist/evaluators/summarization.d.ts +5 -24
  8. package/dist/index.d.ts +152 -30
  9. package/dist/node/index.node.js +2242 -1084
  10. package/dist/node/index.node.js.map +23 -13
  11. package/dist/prompts/consolidation.d.ts +35 -0
  12. package/dist/prompts/summarization.d.ts +25 -0
  13. package/dist/providers/action-results.d.ts +2 -0
  14. package/dist/providers/long-term-memory.d.ts +18 -11
  15. package/dist/providers/recent-conversation-summary.d.ts +2 -0
  16. package/dist/repositories/conversation-summary.d.ts +33 -0
  17. package/dist/repositories/index.d.ts +17 -0
  18. package/dist/repositories/long-term-memory.d.ts +53 -0
  19. package/dist/schemas/conversation-summaries.d.ts +494 -0
  20. package/dist/schemas/index.d.ts +16 -6
  21. package/dist/schemas/long-term-memories.d.ts +308 -70
  22. package/dist/services/memory-service.d.ts +95 -51
  23. package/dist/types/index.d.ts +299 -55
  24. package/dist/utils/db-mapping.d.ts +20 -0
  25. package/dist/utils/decay-scoring.d.ts +41 -0
  26. package/dist/utils/embedding.d.ts +21 -0
  27. package/dist/utils/formatting.d.ts +17 -0
  28. package/dist/utils/index.d.ts +17 -0
  29. package/dist/utils/search-merging.d.ts +18 -0
  30. package/dist/utils/token-counter.d.ts +53 -0
  31. package/package.json +83 -1
  32. package/dist/actions/remember.d.ts +0 -11
  33. package/dist/evaluators/long-term-extraction.d.ts +0 -8
  34. package/dist/providers/short-term-memory.d.ts +0 -19
  35. package/dist/schemas/memory-access-logs.d.ts +0 -154
  36. package/dist/schemas/session-summaries.d.ts +0 -283
@@ -29,1244 +29,2397 @@ var __export = (target, all) => {
29
29
  // src/index.node.ts
30
30
  var exports_index_node = {};
31
31
  __export(exports_index_node, {
32
- sessionSummaries: () => sessionSummaries,
32
+ trimToTokenBudget: () => trimToTokenBudget,
33
+ recentContextProvider: () => recentContextProvider,
34
+ mergeSearchResults: () => mergeSearchResults,
33
35
  memoryPlugin: () => memoryPlugin,
34
- memoryAccessLogs: () => memoryAccessLogs,
36
+ mapDbRowToLongTermMemory: () => mapDbRowToLongTermMemory,
37
+ mapDbRowToConversationSummary: () => mapDbRowToConversationSummary,
38
+ longTermMemoryProvider: () => longTermMemoryProvider,
39
+ longTermMemoryEmbeddings: () => longTermMemoryEmbeddings,
35
40
  longTermMemories: () => longTermMemories,
41
+ generateEmbedding: () => generateEmbedding,
42
+ formatTokenCount: () => formatTokenCount,
43
+ formatMemoriesForContext: () => formatMemoriesForContext,
44
+ estimateTokensInSummary: () => estimateTokensInSummary,
45
+ estimateTokenCountForArray: () => estimateTokenCountForArray,
46
+ estimateTokenCount: () => estimateTokenCount,
36
47
  default: () => src_default,
48
+ conversationSummaryEmbeddings: () => conversationSummaryEmbeddings,
49
+ conversationSummaries: () => conversationSummaries,
50
+ cleanEmbedding: () => cleanEmbedding,
51
+ calculateDecayFactor: () => calculateDecayFactor,
52
+ calculateAccessBoost: () => calculateAccessBoost,
53
+ buildLevel1SummaryPrompt: () => buildLevel1SummaryPrompt,
54
+ buildHigherLevelSummaryPrompt: () => buildHigherLevelSummaryPrompt,
55
+ buildExtractionPrompt: () => buildExtractionPrompt,
56
+ buildContradictionPrompt: () => buildContradictionPrompt,
57
+ applyDecayScoring: () => applyDecayScoring,
58
+ actionResultsProvider: () => actionResultsProvider,
59
+ SUMMARIZATION_SYSTEM_PROMPT: () => SUMMARIZATION_SYSTEM_PROMPT,
60
+ MemoryType: () => MemoryType,
37
61
  MemoryService: () => MemoryService,
38
- LongTermMemoryCategory: () => LongTermMemoryCategory
62
+ MEMORY_DIMENSION_MAP: () => MEMORY_DIMENSION_MAP,
63
+ LongTermMemoryRepository: () => LongTermMemoryRepository,
64
+ HIGHER_LEVEL_SUMMARIZATION_SYSTEM_PROMPT: () => HIGHER_LEVEL_SUMMARIZATION_SYSTEM_PROMPT,
65
+ DecayFunction: () => DecayFunction,
66
+ ConversationSummaryRepository: () => ConversationSummaryRepository,
67
+ CONSOLIDATION_SYSTEM_PROMPT: () => CONSOLIDATION_SYSTEM_PROMPT
39
68
  });
40
69
  module.exports = __toCommonJS(exports_index_node);
41
70
 
42
71
  // src/services/memory-service.ts
43
- var import_core = require("@elizaos/core");
44
- var import_drizzle_orm4 = require("drizzle-orm");
72
+ var import_core6 = require("@elizaos/core");
73
+ var import_core7 = require("@elizaos/core");
74
+
75
+ // src/types/index.ts
76
+ var MEMORY_DIMENSION_MAP = {
77
+ 384: "dim384",
78
+ 512: "dim512",
79
+ 768: "dim768",
80
+ 1024: "dim1024",
81
+ 1536: "dim1536",
82
+ 3072: "dim3072"
83
+ };
84
+ var MemoryType;
85
+ ((MemoryType2) => {
86
+ MemoryType2["EPISODIC"] = "EPISODIC";
87
+ MemoryType2["SEMANTIC"] = "SEMANTIC";
88
+ MemoryType2["PROCEDURAL"] = "PROCEDURAL";
89
+ })(MemoryType ||= {});
90
+ var DecayFunction;
91
+ ((DecayFunction2) => {
92
+ DecayFunction2["EXPONENTIAL"] = "EXPONENTIAL";
93
+ DecayFunction2["LINEAR"] = "LINEAR";
94
+ DecayFunction2["NONE"] = "NONE";
95
+ })(DecayFunction ||= {});
96
+
97
+ // src/repositories/long-term-memory.ts
98
+ var import_core3 = require("@elizaos/core");
99
+ var import_drizzle_orm3 = require("drizzle-orm");
45
100
 
46
101
  // src/schemas/index.ts
47
102
  var exports_schemas = {};
48
103
  __export(exports_schemas, {
49
- sessionSummaries: () => sessionSummaries,
50
- memoryAccessLogs: () => memoryAccessLogs,
51
- longTermMemories: () => longTermMemories
104
+ longTermMemoryEmbeddings: () => longTermMemoryEmbeddings,
105
+ longTermMemories: () => longTermMemories,
106
+ conversationSummaryEmbeddings: () => conversationSummaryEmbeddings,
107
+ conversationSummaries: () => conversationSummaries
52
108
  });
53
109
 
54
110
  // src/schemas/long-term-memories.ts
55
111
  var import_drizzle_orm = require("drizzle-orm");
56
112
  var import_pg_core = require("drizzle-orm/pg-core");
113
+ var import_core = require("@elizaos/core");
57
114
  var longTermMemories = import_pg_core.pgTable("long_term_memories", {
58
115
  id: import_pg_core.varchar("id", { length: 36 }).primaryKey(),
59
116
  agentId: import_pg_core.varchar("agent_id", { length: 36 }).notNull(),
60
117
  entityId: import_pg_core.varchar("entity_id", { length: 36 }).notNull(),
61
- category: import_pg_core.text("category").notNull(),
118
+ roomId: import_pg_core.varchar("room_id", { length: 36 }),
119
+ type: import_pg_core.text("type").notNull(),
62
120
  content: import_pg_core.text("content").notNull(),
63
- metadata: import_pg_core.jsonb("metadata"),
64
- embedding: import_pg_core.real("embedding").array(),
65
- confidence: import_pg_core.real("confidence").default(1),
66
- source: import_pg_core.text("source"),
121
+ embeddingContext: import_pg_core.text("embedding_context").notNull(),
122
+ confidence: import_pg_core.real("confidence").notNull().default(1),
123
+ decayRate: import_pg_core.real("decay_rate").notNull().default(0.01),
124
+ decayFunction: import_pg_core.text("decay_function").notNull().default("EXPONENTIAL"),
67
125
  createdAt: import_pg_core.timestamp("created_at").default(import_drizzle_orm.sql`now()`).notNull(),
68
- updatedAt: import_pg_core.timestamp("updated_at").default(import_drizzle_orm.sql`now()`).notNull(),
69
126
  lastAccessedAt: import_pg_core.timestamp("last_accessed_at"),
70
- accessCount: import_pg_core.integer("access_count").default(0)
127
+ accessCount: import_pg_core.integer("access_count").default(0).notNull(),
128
+ isActive: import_pg_core.boolean("is_active").default(true).notNull(),
129
+ source: import_pg_core.jsonb("source").notNull().default({}),
130
+ metadata: import_pg_core.jsonb("metadata").notNull().default({}),
131
+ supersedesId: import_pg_core.varchar("supersedes_id", { length: 36 })
71
132
  }, (table) => ({
72
- agentEntityIdx: import_pg_core.index("long_term_memories_agent_entity_idx").on(table.agentId, table.entityId),
73
- categoryIdx: import_pg_core.index("long_term_memories_category_idx").on(table.category),
74
- confidenceIdx: import_pg_core.index("long_term_memories_confidence_idx").on(table.confidence),
75
- createdAtIdx: import_pg_core.index("long_term_memories_created_at_idx").on(table.createdAt)
133
+ agentEntityIdx: import_pg_core.index("ltm_agent_entity_idx").on(table.agentId, table.entityId),
134
+ typeIdx: import_pg_core.index("ltm_type_idx").on(table.type),
135
+ roomIdx: import_pg_core.index("ltm_room_idx").on(table.roomId),
136
+ activeIdx: import_pg_core.index("ltm_active_idx").on(table.isActive),
137
+ confidenceIdx: import_pg_core.index("ltm_confidence_idx").on(table.confidence),
138
+ createdAtIdx: import_pg_core.index("ltm_created_at_idx").on(table.createdAt),
139
+ lastAccessedIdx: import_pg_core.index("ltm_last_accessed_idx").on(table.lastAccessedAt),
140
+ agentEntityActiveConfidenceIdx: import_pg_core.index("ltm_agent_entity_active_conf_idx").on(table.agentId, table.entityId, table.isActive, table.confidence)
76
141
  }));
77
- // src/schemas/session-summaries.ts
142
+ var longTermMemoryEmbeddings = import_pg_core.pgTable("long_term_memory_embeddings", {
143
+ id: import_pg_core.varchar("id", { length: 36 }).primaryKey(),
144
+ memoryId: import_pg_core.varchar("memory_id", { length: 36 }).notNull().references(() => longTermMemories.id, { onDelete: "cascade" }),
145
+ dim384: import_pg_core.vector("dim_384", { dimensions: import_core.VECTOR_DIMS.SMALL }),
146
+ dim512: import_pg_core.vector("dim_512", { dimensions: import_core.VECTOR_DIMS.MEDIUM }),
147
+ dim768: import_pg_core.vector("dim_768", { dimensions: import_core.VECTOR_DIMS.LARGE }),
148
+ dim1024: import_pg_core.vector("dim_1024", { dimensions: import_core.VECTOR_DIMS.XL }),
149
+ dim1536: import_pg_core.vector("dim_1536", { dimensions: import_core.VECTOR_DIMS.XXL }),
150
+ dim3072: import_pg_core.vector("dim_3072", { dimensions: import_core.VECTOR_DIMS.XXXL }),
151
+ createdAt: import_pg_core.timestamp("created_at").default(import_drizzle_orm.sql`now()`).notNull()
152
+ }, (table) => [
153
+ import_pg_core.index("idx_ltm_embedding_memory_id").on(table.memoryId),
154
+ import_pg_core.foreignKey({
155
+ name: "fk_ltm_embedding_memory",
156
+ columns: [table.memoryId],
157
+ foreignColumns: [longTermMemories.id]
158
+ }).onDelete("cascade")
159
+ ]);
160
+ // src/schemas/conversation-summaries.ts
78
161
  var import_drizzle_orm2 = require("drizzle-orm");
79
162
  var import_pg_core2 = require("drizzle-orm/pg-core");
80
- var sessionSummaries = import_pg_core2.pgTable("session_summaries", {
163
+ var import_core2 = require("@elizaos/core");
164
+ var conversationSummaries = import_pg_core2.pgTable("conversation_summaries", {
81
165
  id: import_pg_core2.varchar("id", { length: 36 }).primaryKey(),
82
166
  agentId: import_pg_core2.varchar("agent_id", { length: 36 }).notNull(),
167
+ entityId: import_pg_core2.varchar("entity_id", { length: 36 }).notNull(),
83
168
  roomId: import_pg_core2.varchar("room_id", { length: 36 }).notNull(),
84
- entityId: import_pg_core2.varchar("entity_id", { length: 36 }),
85
- summary: import_pg_core2.text("summary").notNull(),
86
- messageCount: import_pg_core2.integer("message_count").notNull(),
87
- lastMessageOffset: import_pg_core2.integer("last_message_offset").notNull().default(0),
169
+ level: import_pg_core2.integer("level").notNull().default(1),
170
+ parentSummaryId: import_pg_core2.varchar("parent_summary_id", { length: 36 }),
171
+ content: import_pg_core2.text("content").notNull(),
172
+ tokenCount: import_pg_core2.integer("token_count").notNull(),
88
173
  startTime: import_pg_core2.timestamp("start_time").notNull(),
89
174
  endTime: import_pg_core2.timestamp("end_time").notNull(),
90
- topics: import_pg_core2.jsonb("topics"),
91
- metadata: import_pg_core2.jsonb("metadata"),
92
- embedding: import_pg_core2.real("embedding").array(),
175
+ sourceCount: import_pg_core2.integer("source_count").notNull(),
176
+ sourceIds: import_pg_core2.jsonb("source_ids").notNull().default([]),
93
177
  createdAt: import_pg_core2.timestamp("created_at").default(import_drizzle_orm2.sql`now()`).notNull(),
94
- updatedAt: import_pg_core2.timestamp("updated_at").default(import_drizzle_orm2.sql`now()`).notNull()
178
+ lastAccessedAt: import_pg_core2.timestamp("last_accessed_at"),
179
+ accessCount: import_pg_core2.integer("access_count").default(0).notNull(),
180
+ metadata: import_pg_core2.jsonb("metadata").notNull().default({})
95
181
  }, (table) => ({
96
- agentRoomIdx: import_pg_core2.index("session_summaries_agent_room_idx").on(table.agentId, table.roomId),
97
- entityIdx: import_pg_core2.index("session_summaries_entity_idx").on(table.entityId),
98
- startTimeIdx: import_pg_core2.index("session_summaries_start_time_idx").on(table.startTime)
182
+ agentEntityRoomIdx: import_pg_core2.index("cs_agent_entity_room_idx").on(table.agentId, table.entityId, table.roomId),
183
+ levelIdx: import_pg_core2.index("cs_level_idx").on(table.level),
184
+ parentIdx: import_pg_core2.index("cs_parent_idx").on(table.parentSummaryId),
185
+ timeRangeIdx: import_pg_core2.index("cs_time_range_idx").on(table.startTime, table.endTime),
186
+ createdAtIdx: import_pg_core2.index("cs_created_at_idx").on(table.createdAt),
187
+ lastAccessedIdx: import_pg_core2.index("cs_last_accessed_idx").on(table.lastAccessedAt),
188
+ entityRoomLevelTimeIdx: import_pg_core2.index("cs_entity_room_level_time_idx").on(table.entityId, table.roomId, table.level, table.createdAt)
99
189
  }));
100
- // src/schemas/memory-access-logs.ts
101
- var import_drizzle_orm3 = require("drizzle-orm");
102
- var import_pg_core3 = require("drizzle-orm/pg-core");
103
- var memoryAccessLogs = import_pg_core3.pgTable("memory_access_logs", {
104
- id: import_pg_core3.varchar("id", { length: 36 }).primaryKey(),
105
- agentId: import_pg_core3.varchar("agent_id", { length: 36 }).notNull(),
106
- memoryId: import_pg_core3.varchar("memory_id", { length: 36 }).notNull(),
107
- memoryType: import_pg_core3.text("memory_type").notNull(),
108
- accessedAt: import_pg_core3.timestamp("accessed_at").default(import_drizzle_orm3.sql`now()`).notNull(),
109
- roomId: import_pg_core3.varchar("room_id", { length: 36 }),
110
- relevanceScore: import_pg_core3.real("relevance_score"),
111
- wasUseful: import_pg_core3.integer("was_useful")
112
- }, (table) => ({
113
- memoryIdx: import_pg_core3.index("memory_access_logs_memory_idx").on(table.memoryId),
114
- agentIdx: import_pg_core3.index("memory_access_logs_agent_idx").on(table.agentId),
115
- accessedAtIdx: import_pg_core3.index("memory_access_logs_accessed_at_idx").on(table.accessedAt)
116
- }));
117
- // src/services/memory-service.ts
118
- class MemoryService extends import_core.Service {
119
- static serviceType = "memory";
120
- sessionMessageCounts;
121
- memoryConfig;
122
- lastExtractionCheckpoints;
123
- capabilityDescription = "Advanced memory management with short-term summarization and long-term persistent facts";
124
- constructor(runtime) {
125
- super(runtime);
126
- this.sessionMessageCounts = new Map;
127
- this.lastExtractionCheckpoints = new Map;
128
- this.memoryConfig = {
129
- shortTermSummarizationThreshold: 16,
130
- shortTermRetainRecent: 10,
131
- shortTermSummarizationInterval: 10,
132
- longTermExtractionEnabled: true,
133
- longTermVectorSearchEnabled: false,
134
- longTermConfidenceThreshold: 0.7,
135
- longTermExtractionThreshold: 20,
136
- longTermExtractionInterval: 5,
137
- summaryModelType: "TEXT_LARGE",
138
- summaryMaxTokens: 2500,
139
- summaryMaxNewMessages: 20
140
- };
141
- }
142
- static async start(runtime) {
143
- const service = new MemoryService(runtime);
144
- await service.initialize(runtime);
145
- return service;
146
- }
147
- async stop() {
148
- import_core.logger.info("MemoryService stopped");
149
- }
150
- async initialize(runtime) {
190
+ var conversationSummaryEmbeddings = import_pg_core2.pgTable("conversation_summary_embeddings", {
191
+ id: import_pg_core2.varchar("id", { length: 36 }).primaryKey(),
192
+ summaryId: import_pg_core2.varchar("summary_id", { length: 36 }).notNull().references(() => conversationSummaries.id, { onDelete: "cascade" }),
193
+ dim384: import_pg_core2.vector("dim_384", { dimensions: import_core2.VECTOR_DIMS.SMALL }),
194
+ dim512: import_pg_core2.vector("dim_512", { dimensions: import_core2.VECTOR_DIMS.MEDIUM }),
195
+ dim768: import_pg_core2.vector("dim_768", { dimensions: import_core2.VECTOR_DIMS.LARGE }),
196
+ dim1024: import_pg_core2.vector("dim_1024", { dimensions: import_core2.VECTOR_DIMS.XL }),
197
+ dim1536: import_pg_core2.vector("dim_1536", { dimensions: import_core2.VECTOR_DIMS.XXL }),
198
+ dim3072: import_pg_core2.vector("dim_3072", { dimensions: import_core2.VECTOR_DIMS.XXXL }),
199
+ createdAt: import_pg_core2.timestamp("created_at").default(import_drizzle_orm2.sql`now()`).notNull()
200
+ }, (table) => [
201
+ import_pg_core2.index("idx_cs_embedding_summary_id").on(table.summaryId),
202
+ import_pg_core2.foreignKey({
203
+ name: "fk_cs_embedding_summary",
204
+ columns: [table.summaryId],
205
+ foreignColumns: [conversationSummaries.id]
206
+ }).onDelete("cascade")
207
+ ]);
208
+ // src/utils/db-mapping.ts
209
+ function mapDbRowToLongTermMemory(row) {
210
+ return {
211
+ id: row.id,
212
+ agentId: row.agentId,
213
+ entityId: row.entityId,
214
+ roomId: row.roomId,
215
+ type: row.type,
216
+ content: row.content,
217
+ embeddingContext: row.embeddingContext,
218
+ embedding: row.embedding,
219
+ confidence: row.confidence,
220
+ decayRate: row.decayRate,
221
+ decayFunction: row.decayFunction,
222
+ createdAt: row.createdAt,
223
+ lastAccessedAt: row.lastAccessedAt,
224
+ accessCount: row.accessCount,
225
+ isActive: row.isActive,
226
+ source: row.source,
227
+ metadata: row.metadata,
228
+ supersedesId: row.supersedesId
229
+ };
230
+ }
231
+ function mapDbRowToConversationSummary(row) {
232
+ return {
233
+ id: row.id,
234
+ agentId: row.agentId,
235
+ entityId: row.entityId,
236
+ roomId: row.roomId,
237
+ level: row.level,
238
+ parentSummaryId: row.parentSummaryId,
239
+ content: row.content,
240
+ embedding: row.embedding,
241
+ tokenCount: row.tokenCount,
242
+ startTime: row.startTime,
243
+ endTime: row.endTime,
244
+ sourceCount: row.sourceCount,
245
+ sourceIds: row.sourceIds,
246
+ createdAt: row.createdAt,
247
+ lastAccessedAt: row.lastAccessedAt,
248
+ accessCount: row.accessCount,
249
+ metadata: row.metadata || {}
250
+ };
251
+ }
252
+
253
+ // src/repositories/long-term-memory.ts
254
+ class LongTermMemoryRepository {
255
+ runtime;
256
+ embeddingDimension;
257
+ constructor(runtime, embeddingDimension) {
151
258
  this.runtime = runtime;
152
- const threshold = runtime.getSetting("MEMORY_SUMMARIZATION_THRESHOLD");
153
- if (threshold) {
154
- this.memoryConfig.shortTermSummarizationThreshold = parseInt(threshold, 10);
155
- }
156
- const retainRecent = runtime.getSetting("MEMORY_RETAIN_RECENT");
157
- if (retainRecent) {
158
- this.memoryConfig.shortTermRetainRecent = parseInt(retainRecent, 10);
159
- }
160
- const summarizationInterval = runtime.getSetting("MEMORY_SUMMARIZATION_INTERVAL");
161
- if (summarizationInterval) {
162
- this.memoryConfig.shortTermSummarizationInterval = parseInt(summarizationInterval, 10);
163
- }
164
- const maxNewMessages = runtime.getSetting("MEMORY_MAX_NEW_MESSAGES");
165
- if (maxNewMessages) {
166
- this.memoryConfig.summaryMaxNewMessages = parseInt(maxNewMessages, 10);
167
- }
168
- const longTermEnabled = runtime.getSetting("MEMORY_LONG_TERM_ENABLED");
169
- if (longTermEnabled === "false") {
170
- this.memoryConfig.longTermExtractionEnabled = false;
171
- } else if (longTermEnabled === "true") {
172
- this.memoryConfig.longTermExtractionEnabled = true;
173
- }
174
- const confidenceThreshold = runtime.getSetting("MEMORY_CONFIDENCE_THRESHOLD");
175
- if (confidenceThreshold) {
176
- this.memoryConfig.longTermConfidenceThreshold = parseFloat(confidenceThreshold);
177
- }
178
- const extractionThreshold = runtime.getSetting("MEMORY_EXTRACTION_THRESHOLD");
179
- if (extractionThreshold) {
180
- this.memoryConfig.longTermExtractionThreshold = parseInt(extractionThreshold, 10);
181
- }
182
- const extractionInterval = runtime.getSetting("MEMORY_EXTRACTION_INTERVAL");
183
- if (extractionInterval) {
184
- this.memoryConfig.longTermExtractionInterval = parseInt(extractionInterval, 10);
185
- }
186
- import_core.logger.info({
187
- summarizationThreshold: this.memoryConfig.shortTermSummarizationThreshold,
188
- summarizationInterval: this.memoryConfig.shortTermSummarizationInterval,
189
- maxNewMessages: this.memoryConfig.summaryMaxNewMessages,
190
- retainRecent: this.memoryConfig.shortTermRetainRecent,
191
- longTermEnabled: this.memoryConfig.longTermExtractionEnabled,
192
- extractionThreshold: this.memoryConfig.longTermExtractionThreshold,
193
- extractionInterval: this.memoryConfig.longTermExtractionInterval,
194
- confidenceThreshold: this.memoryConfig.longTermConfidenceThreshold
195
- }, "MemoryService initialized");
196
- }
197
- getDb() {
198
- const db = this.runtime.db;
259
+ this.embeddingDimension = embeddingDimension;
260
+ }
261
+ async getDb() {
262
+ const adapter = this.runtime.adapter || this.runtime;
263
+ const db = adapter.db;
199
264
  if (!db) {
200
265
  throw new Error("Database not available");
201
266
  }
202
- return db;
203
- }
204
- getConfig() {
205
- return { ...this.memoryConfig };
206
- }
207
- updateConfig(updates) {
208
- this.memoryConfig = { ...this.memoryConfig, ...updates };
209
- }
210
- incrementMessageCount(roomId) {
211
- const current = this.sessionMessageCounts.get(roomId) || 0;
212
- const newCount = current + 1;
213
- this.sessionMessageCounts.set(roomId, newCount);
214
- return newCount;
215
- }
216
- resetMessageCount(roomId) {
217
- this.sessionMessageCounts.set(roomId, 0);
218
- }
219
- async shouldSummarize(roomId) {
220
- const count = await this.runtime.countMemories(roomId, false, "messages");
221
- return count >= this.memoryConfig.shortTermSummarizationThreshold;
222
- }
223
- getExtractionKey(entityId, roomId) {
224
- return `memory:extraction:${entityId}:${roomId}`;
225
- }
226
- async getLastExtractionCheckpoint(entityId, roomId) {
227
- const key = this.getExtractionKey(entityId, roomId);
228
- const cached = this.lastExtractionCheckpoints.get(key);
229
- if (cached !== undefined) {
230
- return cached;
231
- }
232
- try {
233
- const checkpoint = await this.runtime.getCache(key);
234
- const messageCount = checkpoint ?? 0;
235
- this.lastExtractionCheckpoints.set(key, messageCount);
236
- return messageCount;
237
- } catch (error) {
238
- import_core.logger.warn({ error }, "Failed to get extraction checkpoint from cache");
239
- return 0;
240
- }
241
- }
242
- async setLastExtractionCheckpoint(entityId, roomId, messageCount) {
243
- const key = this.getExtractionKey(entityId, roomId);
244
- this.lastExtractionCheckpoints.set(key, messageCount);
245
267
  try {
246
- await this.runtime.setCache(key, messageCount);
247
- import_core.logger.debug(`Set extraction checkpoint for ${entityId} in room ${roomId} at message count ${messageCount}`);
268
+ const isReady = await adapter.isReady();
269
+ if (!isReady) {
270
+ import_core3.logger.warn("[LongTermMemoryRepository] Database not ready, attempting reconnect...");
271
+ await new Promise((resolve) => setTimeout(resolve, 1000));
272
+ const stillNotReady = await adapter.isReady();
273
+ if (stillNotReady === false) {
274
+ throw new Error("Database connection lost and could not reconnect");
275
+ }
276
+ }
248
277
  } catch (error) {
249
- import_core.logger.error({ error }, "Failed to persist extraction checkpoint to cache");
250
- }
251
- }
252
- async shouldRunExtraction(entityId, roomId, currentMessageCount) {
253
- const threshold = this.memoryConfig.longTermExtractionThreshold;
254
- const interval = this.memoryConfig.longTermExtractionInterval;
255
- if (currentMessageCount < threshold) {
256
- import_core.logger.debug({
257
- entityId,
258
- roomId,
259
- currentMessageCount,
260
- threshold,
261
- shouldRun: false
262
- }, "Extraction check: below threshold");
263
- return false;
278
+ import_core3.logger.error("[LongTermMemoryRepository] Database health check failed:", error);
279
+ throw new Error("Database connection health check failed");
264
280
  }
265
- const lastCheckpoint = await this.getLastExtractionCheckpoint(entityId, roomId);
266
- const currentCheckpoint = Math.floor(currentMessageCount / interval) * interval;
267
- const shouldRun = currentMessageCount >= threshold && currentCheckpoint > lastCheckpoint;
268
- import_core.logger.debug({
269
- entityId,
270
- roomId,
271
- currentMessageCount,
272
- threshold,
273
- interval,
274
- lastCheckpoint,
275
- currentCheckpoint,
276
- shouldRun
277
- }, "Extraction check");
278
- return shouldRun;
281
+ return db;
279
282
  }
280
- async storeLongTermMemory(memory) {
281
- const db = this.getDb();
283
+ async insert(memory, embedding) {
284
+ const db = await this.getDb();
282
285
  const id = crypto.randomUUID();
283
286
  const now = new Date;
284
287
  const newMemory = {
285
288
  id,
286
289
  createdAt: now,
287
- updatedAt: now,
290
+ lastAccessedAt: null,
288
291
  accessCount: 0,
292
+ isActive: true,
293
+ embedding: embedding || [],
289
294
  ...memory
290
295
  };
291
- try {
292
- await db.insert(longTermMemories).values({
296
+ await db.transaction(async (tx) => {
297
+ await tx.insert(longTermMemories).values({
293
298
  id: newMemory.id,
294
299
  agentId: newMemory.agentId,
295
300
  entityId: newMemory.entityId,
296
- category: newMemory.category,
301
+ roomId: newMemory.roomId || null,
302
+ type: newMemory.type,
297
303
  content: newMemory.content,
298
- metadata: newMemory.metadata || {},
299
- embedding: newMemory.embedding,
304
+ embeddingContext: newMemory.embeddingContext,
300
305
  confidence: newMemory.confidence,
301
- source: newMemory.source,
302
- accessCount: newMemory.accessCount,
306
+ decayRate: newMemory.decayRate,
307
+ decayFunction: newMemory.decayFunction,
303
308
  createdAt: now,
304
- updatedAt: now,
305
- lastAccessedAt: newMemory.lastAccessedAt
309
+ lastAccessedAt: null,
310
+ accessCount: 0,
311
+ isActive: true,
312
+ source: newMemory.source,
313
+ metadata: newMemory.metadata,
314
+ supersedesId: newMemory.supersedesId || null
306
315
  });
307
- } catch (error) {
308
- import_core.logger.error({ error }, "Failed to store long-term memory");
309
- throw error;
310
- }
311
- import_core.logger.info(`Stored long-term memory: ${newMemory.category} for entity ${newMemory.entityId}`);
316
+ if (embedding && this.embeddingDimension) {
317
+ const embeddingValues = {
318
+ id: crypto.randomUUID(),
319
+ memoryId: id,
320
+ createdAt: now
321
+ };
322
+ embeddingValues[this.embeddingDimension] = embedding;
323
+ await tx.insert(longTermMemoryEmbeddings).values(embeddingValues);
324
+ }
325
+ });
326
+ import_core3.logger.info({
327
+ id: newMemory.id,
328
+ type: newMemory.type,
329
+ entityId: newMemory.entityId,
330
+ confidence: newMemory.confidence
331
+ }, "Stored new long-term memory");
312
332
  return newMemory;
313
333
  }
314
- async getLongTermMemories(entityId, category, limit = 10) {
315
- const db = this.getDb();
316
- const conditions = [
317
- import_drizzle_orm4.eq(longTermMemories.agentId, this.runtime.agentId),
318
- import_drizzle_orm4.eq(longTermMemories.entityId, entityId)
319
- ];
320
- if (category) {
321
- conditions.push(import_drizzle_orm4.eq(longTermMemories.category, category));
334
+ async findById(id) {
335
+ const db = await this.getDb();
336
+ const results = await db.select().from(longTermMemories).where(import_drizzle_orm3.eq(longTermMemories.id, id)).limit(1);
337
+ if (results.length === 0) {
338
+ return null;
322
339
  }
323
- const results = await db.select().from(longTermMemories).where(import_drizzle_orm4.and(...conditions)).orderBy(import_drizzle_orm4.desc(longTermMemories.confidence), import_drizzle_orm4.desc(longTermMemories.updatedAt)).limit(limit);
324
- return results.map((row) => ({
325
- id: row.id,
326
- agentId: row.agentId,
327
- entityId: row.entityId,
328
- category: row.category,
329
- content: row.content,
330
- metadata: row.metadata,
331
- embedding: row.embedding,
332
- confidence: row.confidence,
333
- source: row.source,
334
- createdAt: row.createdAt,
335
- updatedAt: row.updatedAt,
336
- lastAccessedAt: row.lastAccessedAt,
337
- accessCount: row.accessCount
338
- }));
340
+ return mapDbRowToLongTermMemory(results[0]);
339
341
  }
340
- async updateLongTermMemory(id, entityId, updates) {
341
- const db = this.getDb();
342
- const updateData = {
343
- updatedAt: new Date
344
- };
345
- if (updates.content !== undefined) {
342
+ async update(id, updates, newEmbedding) {
343
+ const db = await this.getDb();
344
+ const updateData = {};
345
+ if (updates.content !== undefined)
346
346
  updateData.content = updates.content;
347
- }
348
- if (updates.metadata !== undefined) {
347
+ if (updates.embeddingContext !== undefined)
348
+ updateData.embeddingContext = updates.embeddingContext;
349
+ if (updates.confidence !== undefined)
350
+ updateData.confidence = updates.confidence;
351
+ if (updates.decayRate !== undefined)
352
+ updateData.decayRate = updates.decayRate;
353
+ if (updates.decayFunction !== undefined)
354
+ updateData.decayFunction = updates.decayFunction;
355
+ if (updates.lastAccessedAt !== undefined)
356
+ updateData.lastAccessedAt = updates.lastAccessedAt;
357
+ if (updates.accessCount !== undefined)
358
+ updateData.accessCount = updates.accessCount;
359
+ if (updates.isActive !== undefined)
360
+ updateData.isActive = updates.isActive;
361
+ if (updates.source !== undefined)
362
+ updateData.source = updates.source;
363
+ if (updates.metadata !== undefined)
349
364
  updateData.metadata = updates.metadata;
365
+ if (updates.supersedesId !== undefined)
366
+ updateData.supersedesId = updates.supersedesId;
367
+ await db.transaction(async (tx) => {
368
+ await tx.update(longTermMemories).set(updateData).where(import_drizzle_orm3.eq(longTermMemories.id, id));
369
+ if (newEmbedding && this.embeddingDimension) {
370
+ const embeddingUpdate = {};
371
+ embeddingUpdate[this.embeddingDimension] = newEmbedding;
372
+ await tx.update(longTermMemoryEmbeddings).set(embeddingUpdate).where(import_drizzle_orm3.eq(longTermMemoryEmbeddings.memoryId, id));
373
+ }
374
+ });
375
+ import_core3.logger.info({ id }, "Updated long-term memory");
376
+ }
377
+ async delete(id) {
378
+ const db = await this.getDb();
379
+ await db.delete(longTermMemories).where(import_drizzle_orm3.eq(longTermMemories.id, id));
380
+ import_core3.logger.info({ id }, "Deleted long-term memory");
381
+ }
382
+ async findByEntity(entityId, type, limit = 20, includeInactive = false) {
383
+ const db = await this.getDb();
384
+ const conditions = [
385
+ import_drizzle_orm3.eq(longTermMemories.agentId, this.runtime.agentId),
386
+ import_drizzle_orm3.eq(longTermMemories.entityId, entityId)
387
+ ];
388
+ if (!includeInactive) {
389
+ conditions.push(import_drizzle_orm3.eq(longTermMemories.isActive, true));
350
390
  }
351
- if (updates.confidence !== undefined) {
352
- updateData.confidence = updates.confidence;
391
+ if (type) {
392
+ conditions.push(import_drizzle_orm3.eq(longTermMemories.type, type));
353
393
  }
354
- if (updates.embedding !== undefined) {
355
- updateData.embedding = updates.embedding;
394
+ const results = await db.select().from(longTermMemories).where(import_drizzle_orm3.and(...conditions)).orderBy(import_drizzle_orm3.desc(longTermMemories.confidence), import_drizzle_orm3.desc(longTermMemories.createdAt)).limit(limit);
395
+ return results.map((row) => mapDbRowToLongTermMemory(row));
396
+ }
397
+ async vectorSearch(params, queryEmbedding, similarityThreshold = 0.3) {
398
+ if (!this.embeddingDimension) {
399
+ import_core3.logger.warn("Embedding dimension not set, skipping vector search");
400
+ return [];
356
401
  }
357
- if (updates.lastAccessedAt !== undefined) {
358
- updateData.lastAccessedAt = updates.lastAccessedAt;
402
+ const db = await this.getDb();
403
+ try {
404
+ const similarity = import_drizzle_orm3.sql`1 - (${import_drizzle_orm3.cosineDistance(longTermMemoryEmbeddings[this.embeddingDimension], queryEmbedding)})`;
405
+ const conditions = [
406
+ import_drizzle_orm3.eq(longTermMemories.agentId, this.runtime.agentId),
407
+ import_drizzle_orm3.eq(longTermMemories.entityId, params.entityId),
408
+ import_drizzle_orm3.sql`${longTermMemoryEmbeddings[this.embeddingDimension]} IS NOT NULL`,
409
+ import_drizzle_orm3.gte(similarity, similarityThreshold)
410
+ ];
411
+ if (params.minConfidence) {
412
+ conditions.push(import_drizzle_orm3.gte(longTermMemories.confidence, params.minConfidence));
413
+ }
414
+ if (!params.includeInactive) {
415
+ conditions.push(import_drizzle_orm3.eq(longTermMemories.isActive, true));
416
+ }
417
+ if (params.type) {
418
+ conditions.push(import_drizzle_orm3.eq(longTermMemories.type, params.type));
419
+ }
420
+ if (params.roomId) {
421
+ conditions.push(import_drizzle_orm3.or(import_drizzle_orm3.eq(longTermMemories.roomId, params.roomId), import_drizzle_orm3.isNull(longTermMemories.roomId)));
422
+ }
423
+ const results = await db.select({
424
+ memory: longTermMemories,
425
+ embedding: longTermMemoryEmbeddings[this.embeddingDimension],
426
+ similarity
427
+ }).from(longTermMemories).innerJoin(longTermMemoryEmbeddings, import_drizzle_orm3.eq(longTermMemoryEmbeddings.memoryId, longTermMemories.id)).where(import_drizzle_orm3.and(...conditions)).orderBy(import_drizzle_orm3.desc(similarity)).limit(params.limit || 20);
428
+ return results.map((row) => ({
429
+ ...mapDbRowToLongTermMemory(row.memory),
430
+ embedding: row.embedding,
431
+ relevanceScore: row.similarity,
432
+ activationScore: 0,
433
+ finalScore: 0
434
+ }));
435
+ } catch (error) {
436
+ import_core3.logger.error("Failed to execute vector search:", JSON.stringify(error));
437
+ return [];
359
438
  }
360
- if (updates.accessCount !== undefined) {
361
- updateData.accessCount = updates.accessCount;
439
+ }
440
+ async fetchAllActive() {
441
+ const db = await this.getDb();
442
+ const memories = await db.select().from(longTermMemories).where(import_drizzle_orm3.and(import_drizzle_orm3.eq(longTermMemories.agentId, this.runtime.agentId), import_drizzle_orm3.eq(longTermMemories.isActive, true)));
443
+ return memories.map((row) => ({
444
+ id: row.id,
445
+ content: row.content,
446
+ embeddingContext: row.embeddingContext
447
+ }));
448
+ }
449
+ async updateAccessMetadata(memoryIds) {
450
+ if (memoryIds.length === 0)
451
+ return;
452
+ const db = await this.getDb();
453
+ const now = new Date;
454
+ try {
455
+ for (const id of memoryIds) {
456
+ await db.update(longTermMemories).set({
457
+ lastAccessedAt: now,
458
+ accessCount: import_drizzle_orm3.sql`${longTermMemories.accessCount} + 1`
459
+ }).where(import_drizzle_orm3.eq(longTermMemories.id, id));
460
+ }
461
+ import_core3.logger.debug({ count: memoryIds.length }, "Updated access metadata");
462
+ } catch (error) {
463
+ import_core3.logger.error({ error }, "Failed to update access metadata");
362
464
  }
363
- await db.update(longTermMemories).set(updateData).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(longTermMemories.id, id), import_drizzle_orm4.eq(longTermMemories.agentId, this.runtime.agentId), import_drizzle_orm4.eq(longTermMemories.entityId, entityId)));
364
- import_core.logger.info(`Updated long-term memory: ${id} for entity ${entityId}`);
365
465
  }
366
- async deleteLongTermMemory(id, entityId) {
367
- const db = this.getDb();
368
- await db.delete(longTermMemories).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(longTermMemories.id, id), import_drizzle_orm4.eq(longTermMemories.agentId, this.runtime.agentId), import_drizzle_orm4.eq(longTermMemories.entityId, entityId)));
369
- import_core.logger.info(`Deleted long-term memory: ${id} for entity ${entityId}`);
466
+ }
467
+
468
+ // src/repositories/conversation-summary.ts
469
+ var import_core4 = require("@elizaos/core");
470
+ var import_drizzle_orm4 = require("drizzle-orm");
471
+ class ConversationSummaryRepository {
472
+ runtime;
473
+ embeddingDimension;
474
+ constructor(runtime, embeddingDimension) {
475
+ this.runtime = runtime;
476
+ this.embeddingDimension = embeddingDimension;
370
477
  }
371
- async getCurrentSessionSummary(roomId) {
372
- const db = this.getDb();
373
- const results = await db.select().from(sessionSummaries).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(sessionSummaries.agentId, this.runtime.agentId), import_drizzle_orm4.eq(sessionSummaries.roomId, roomId))).orderBy(import_drizzle_orm4.desc(sessionSummaries.updatedAt)).limit(1);
374
- if (results.length === 0) {
375
- return null;
478
+ async getDb() {
479
+ const adapter = this.runtime.adapter || this.runtime;
480
+ const db = adapter.db;
481
+ if (!db) {
482
+ throw new Error("Database not available");
376
483
  }
377
- const row = results[0];
378
- return {
379
- id: row.id,
380
- agentId: row.agentId,
381
- roomId: row.roomId,
382
- entityId: row.entityId,
383
- summary: row.summary,
384
- messageCount: row.messageCount,
385
- lastMessageOffset: row.lastMessageOffset,
386
- startTime: row.startTime,
387
- endTime: row.endTime,
388
- topics: row.topics || [],
389
- metadata: row.metadata,
390
- embedding: row.embedding,
391
- createdAt: row.createdAt,
392
- updatedAt: row.updatedAt
393
- };
484
+ try {
485
+ const isReady = await adapter.isReady();
486
+ if (!isReady) {
487
+ import_core4.logger.warn("[ConversationSummaryRepository] Database not ready, attempting reconnect...");
488
+ await new Promise((resolve) => setTimeout(resolve, 1000));
489
+ const stillNotReady = await adapter.isReady();
490
+ if (stillNotReady === false) {
491
+ throw new Error("Database connection lost and could not reconnect");
492
+ }
493
+ }
494
+ } catch (error) {
495
+ import_core4.logger.error("[ConversationSummaryRepository] Database health check failed:", error);
496
+ throw new Error("Database connection health check failed");
497
+ }
498
+ return db;
394
499
  }
395
- async storeSessionSummary(summary) {
396
- const db = this.getDb();
500
+ async insert(summary, embedding) {
501
+ const db = await this.getDb();
397
502
  const id = crypto.randomUUID();
398
503
  const now = new Date;
399
504
  const newSummary = {
400
505
  id,
401
506
  createdAt: now,
402
- updatedAt: now,
507
+ lastAccessedAt: null,
508
+ accessCount: 0,
509
+ embedding: embedding || [],
403
510
  ...summary
404
511
  };
405
- await db.insert(sessionSummaries).values({
406
- id: newSummary.id,
407
- agentId: newSummary.agentId,
408
- roomId: newSummary.roomId,
409
- entityId: newSummary.entityId || null,
410
- summary: newSummary.summary,
411
- messageCount: newSummary.messageCount,
412
- lastMessageOffset: newSummary.lastMessageOffset,
413
- startTime: newSummary.startTime,
414
- endTime: newSummary.endTime,
415
- topics: newSummary.topics || [],
416
- metadata: newSummary.metadata || {},
417
- embedding: newSummary.embedding,
418
- createdAt: now,
419
- updatedAt: now
512
+ await db.transaction(async (tx) => {
513
+ await tx.insert(conversationSummaries).values({
514
+ id: newSummary.id,
515
+ agentId: newSummary.agentId,
516
+ entityId: newSummary.entityId,
517
+ roomId: newSummary.roomId,
518
+ level: newSummary.level,
519
+ parentSummaryId: newSummary.parentSummaryId || null,
520
+ content: newSummary.content,
521
+ tokenCount: newSummary.tokenCount,
522
+ startTime: newSummary.startTime,
523
+ endTime: newSummary.endTime,
524
+ sourceCount: newSummary.sourceCount,
525
+ sourceIds: newSummary.sourceIds,
526
+ createdAt: now,
527
+ lastAccessedAt: null,
528
+ accessCount: 0,
529
+ metadata: newSummary.metadata
530
+ });
531
+ if (embedding && this.embeddingDimension) {
532
+ const embeddingValues = {
533
+ id: crypto.randomUUID(),
534
+ summaryId: id,
535
+ createdAt: now
536
+ };
537
+ embeddingValues[this.embeddingDimension] = embedding;
538
+ await tx.insert(conversationSummaryEmbeddings).values(embeddingValues);
539
+ }
420
540
  });
421
- import_core.logger.info(`Stored session summary for room ${newSummary.roomId}`);
541
+ import_core4.logger.info({
542
+ id: newSummary.id,
543
+ level: newSummary.level,
544
+ tokenCount: newSummary.tokenCount,
545
+ sourceCount: newSummary.sourceCount
546
+ }, "Stored conversation summary");
422
547
  return newSummary;
423
548
  }
424
- async updateSessionSummary(id, roomId, updates) {
425
- const db = this.getDb();
426
- const updateData = {
427
- updatedAt: new Date
428
- };
429
- if (updates.summary !== undefined) {
430
- updateData.summary = updates.summary;
431
- }
432
- if (updates.messageCount !== undefined) {
433
- updateData.messageCount = updates.messageCount;
434
- }
435
- if (updates.lastMessageOffset !== undefined) {
436
- updateData.lastMessageOffset = updates.lastMessageOffset;
437
- }
438
- if (updates.endTime !== undefined) {
439
- updateData.endTime = updates.endTime;
440
- }
441
- if (updates.topics !== undefined) {
442
- updateData.topics = updates.topics;
443
- }
444
- if (updates.metadata !== undefined) {
445
- updateData.metadata = updates.metadata;
446
- }
447
- if (updates.embedding !== undefined) {
448
- updateData.embedding = updates.embedding;
449
- }
450
- await db.update(sessionSummaries).set(updateData).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(sessionSummaries.id, id), import_drizzle_orm4.eq(sessionSummaries.agentId, this.runtime.agentId), import_drizzle_orm4.eq(sessionSummaries.roomId, roomId)));
451
- import_core.logger.info(`Updated session summary: ${id} for room ${roomId}`);
452
- }
453
- async getSessionSummaries(roomId, limit = 5) {
454
- const db = this.getDb();
455
- const results = await db.select().from(sessionSummaries).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(sessionSummaries.agentId, this.runtime.agentId), import_drizzle_orm4.eq(sessionSummaries.roomId, roomId))).orderBy(import_drizzle_orm4.desc(sessionSummaries.updatedAt)).limit(limit);
456
- return results.map((row) => ({
457
- id: row.id,
458
- agentId: row.agentId,
459
- roomId: row.roomId,
460
- entityId: row.entityId,
461
- summary: row.summary,
462
- messageCount: row.messageCount,
463
- lastMessageOffset: row.lastMessageOffset,
464
- startTime: row.startTime,
465
- endTime: row.endTime,
466
- topics: row.topics || [],
467
- metadata: row.metadata,
468
- embedding: row.embedding,
469
- createdAt: row.createdAt,
470
- updatedAt: row.updatedAt
471
- }));
549
+ async findByLevel(roomId, level) {
550
+ const db = await this.getDb();
551
+ const results = await db.select().from(conversationSummaries).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(conversationSummaries.agentId, this.runtime.agentId), import_drizzle_orm4.eq(conversationSummaries.roomId, roomId), import_drizzle_orm4.eq(conversationSummaries.level, level))).orderBy(import_drizzle_orm4.desc(conversationSummaries.createdAt));
552
+ return results.map((r) => mapDbRowToConversationSummary(r));
472
553
  }
473
- async searchLongTermMemories(entityId, queryEmbedding, limit = 5, matchThreshold = 0.7) {
474
- if (!this.memoryConfig.longTermVectorSearchEnabled) {
475
- import_core.logger.warn("Vector search is not enabled, falling back to recent memories");
476
- return this.getLongTermMemories(entityId, undefined, limit);
554
+ async vectorSearch(entityId, roomId, queryEmbedding, limit = 5) {
555
+ if (!this.embeddingDimension) {
556
+ import_core4.logger.warn("Embedding dimension not set, skipping summary search");
557
+ return [];
477
558
  }
478
- const db = this.getDb();
559
+ const db = await this.getDb();
479
560
  try {
480
- const cleanVector = queryEmbedding.map((n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0);
481
- const similarity = import_drizzle_orm4.sql`1 - (${import_drizzle_orm4.cosineDistance(longTermMemories.embedding, cleanVector)})`;
482
- const conditions = [
483
- import_drizzle_orm4.eq(longTermMemories.agentId, this.runtime.agentId),
484
- import_drizzle_orm4.eq(longTermMemories.entityId, entityId),
485
- import_drizzle_orm4.sql`${longTermMemories.embedding} IS NOT NULL`
486
- ];
487
- if (matchThreshold > 0) {
488
- conditions.push(import_drizzle_orm4.gte(similarity, matchThreshold));
489
- }
561
+ const similarity = import_drizzle_orm4.sql`1 - (${import_drizzle_orm4.cosineDistance(conversationSummaryEmbeddings[this.embeddingDimension], queryEmbedding)})`;
490
562
  const results = await db.select({
491
- memory: longTermMemories,
563
+ summary: conversationSummaries,
564
+ embedding: conversationSummaryEmbeddings[this.embeddingDimension],
492
565
  similarity
493
- }).from(longTermMemories).where(import_drizzle_orm4.and(...conditions)).orderBy(import_drizzle_orm4.desc(similarity)).limit(limit);
494
- return results.map((row) => ({
495
- id: row.memory.id,
496
- agentId: row.memory.agentId,
497
- entityId: row.memory.entityId,
498
- category: row.memory.category,
499
- content: row.memory.content,
500
- metadata: row.memory.metadata,
501
- embedding: row.memory.embedding,
502
- confidence: row.memory.confidence,
503
- source: row.memory.source,
504
- createdAt: row.memory.createdAt,
505
- updatedAt: row.memory.updatedAt,
506
- lastAccessedAt: row.memory.lastAccessedAt,
507
- accessCount: row.memory.accessCount,
508
- similarity: row.similarity
566
+ }).from(conversationSummaries).innerJoin(conversationSummaryEmbeddings, import_drizzle_orm4.eq(conversationSummaryEmbeddings.summaryId, conversationSummaries.id)).where(import_drizzle_orm4.and(import_drizzle_orm4.eq(conversationSummaries.agentId, this.runtime.agentId), import_drizzle_orm4.eq(conversationSummaries.entityId, entityId), import_drizzle_orm4.eq(conversationSummaries.roomId, roomId), import_drizzle_orm4.sql`${conversationSummaryEmbeddings[this.embeddingDimension]} IS NOT NULL`)).orderBy(import_drizzle_orm4.desc(similarity)).limit(limit * 2);
567
+ return results.map((r) => ({
568
+ ...mapDbRowToConversationSummary(r.summary),
569
+ embedding: r.embedding
509
570
  }));
510
571
  } catch (error) {
511
- import_core.logger.warn({ error }, "Vector search failed, falling back to recent memories");
512
- return this.getLongTermMemories(entityId, undefined, limit);
572
+ import_core4.logger.error("Failed to search summaries:", JSON.stringify(error));
573
+ return [];
513
574
  }
514
575
  }
515
- async getFormattedLongTermMemories(entityId) {
516
- const memories = await this.getLongTermMemories(entityId, undefined, 20);
517
- if (memories.length === 0) {
518
- return "";
519
- }
520
- const grouped = new Map;
521
- for (const memory of memories) {
522
- if (!grouped.has(memory.category)) {
523
- grouped.set(memory.category, []);
576
+ async updateAccessMetadata(summaryIds) {
577
+ if (summaryIds.length === 0)
578
+ return;
579
+ const db = await this.getDb();
580
+ const now = new Date;
581
+ try {
582
+ for (const id of summaryIds) {
583
+ await db.update(conversationSummaries).set({
584
+ lastAccessedAt: now,
585
+ accessCount: import_drizzle_orm4.sql`${conversationSummaries.accessCount} + 1`
586
+ }).where(import_drizzle_orm4.eq(conversationSummaries.id, id));
524
587
  }
525
- grouped.get(memory.category)?.push(memory);
588
+ import_core4.logger.debug({ count: summaryIds.length }, "Updated summary access metadata");
589
+ } catch (error) {
590
+ import_core4.logger.error({ error }, "Failed to update summary access metadata");
526
591
  }
527
- const sections = [];
528
- for (const [category, categoryMemories] of grouped.entries()) {
529
- const categoryName = category.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
530
- const items = categoryMemories.map((m) => `- ${m.content}`).join(`
531
- `);
532
- sections.push(`**${categoryName}**:
533
- ${items}`);
592
+ }
593
+ }
594
+
595
+ // src/utils/embedding.ts
596
+ var import_core5 = require("@elizaos/core");
597
+ async function generateEmbedding(runtime, text3) {
598
+ try {
599
+ const embedding = await runtime.useModel(import_core5.ModelType.TEXT_EMBEDDING, text3);
600
+ return embedding;
601
+ } catch (error) {
602
+ import_core5.logger.error({ error }, "Failed to generate embedding");
603
+ return new Array(1536).fill(0);
604
+ }
605
+ }
606
+ function cleanEmbedding(embedding) {
607
+ return embedding.map((n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0);
608
+ }
609
+
610
+ // src/utils/decay-scoring.ts
611
+ function calculateDecayFactor(decayFunction, decayRate, timeDeltaDays) {
612
+ switch (decayFunction) {
613
+ case "EXPONENTIAL" /* EXPONENTIAL */:
614
+ return Math.exp(-decayRate * timeDeltaDays);
615
+ case "LINEAR" /* LINEAR */:
616
+ return Math.max(0, 1 - decayRate * timeDeltaDays);
617
+ case "NONE" /* NONE */:
618
+ return 1;
619
+ default:
620
+ return 1;
621
+ }
622
+ }
623
+ function calculateAccessBoost(accessCount) {
624
+ return 1 + Math.log(1 + accessCount) * 0.1;
625
+ }
626
+ function applyDecayScoring(memories) {
627
+ const now = Date.now();
628
+ return memories.map((memory) => {
629
+ const lastAccessed = memory.lastAccessedAt?.getTime() || memory.createdAt.getTime();
630
+ const timeDeltaDays = (now - lastAccessed) / (1000 * 60 * 60 * 24);
631
+ const decayFactor = calculateDecayFactor(memory.decayFunction, memory.decayRate, timeDeltaDays);
632
+ const accessBoost = calculateAccessBoost(memory.accessCount);
633
+ const activationScore = memory.confidence * decayFactor * accessBoost;
634
+ const finalScore = memory.relevanceScore * activationScore;
635
+ return {
636
+ ...memory,
637
+ activationScore,
638
+ finalScore
639
+ };
640
+ });
641
+ }
642
+
643
+ // src/utils/search-merging.ts
644
+ function mergeSearchResults(vectorResults, bm25Results) {
645
+ const merged = new Map;
646
+ for (const result of vectorResults) {
647
+ merged.set(result.id, result);
648
+ }
649
+ for (const result of bm25Results) {
650
+ if (merged.has(result.id)) {
651
+ const existing = merged.get(result.id);
652
+ existing.relevanceScore = (existing.relevanceScore + result.relevanceScore) / 2;
653
+ } else {
654
+ merged.set(result.id, result);
534
655
  }
535
- return sections.join(`
656
+ }
657
+ return Array.from(merged.values());
658
+ }
536
659
 
660
+ // src/utils/formatting.ts
661
+ function formatMemoriesForContext(memories) {
662
+ if (memories.length === 0) {
663
+ return "";
664
+ }
665
+ const grouped = new Map;
666
+ for (const memory of memories) {
667
+ if (!grouped.has(memory.type)) {
668
+ grouped.set(memory.type, []);
669
+ }
670
+ grouped.get(memory.type).push(memory);
671
+ }
672
+ const sections = [];
673
+ if (grouped.has("SEMANTIC" /* SEMANTIC */)) {
674
+ const facts = grouped.get("SEMANTIC" /* SEMANTIC */);
675
+ const items = facts.map((m) => `- ${m.content} (confidence: ${m.confidence.toFixed(2)})`).join(`
676
+ `);
677
+ sections.push(`**Semantic Knowledge (Facts)**:
678
+ ${items}`);
679
+ }
680
+ if (grouped.has("EPISODIC" /* EPISODIC */)) {
681
+ const episodes = grouped.get("EPISODIC" /* EPISODIC */);
682
+ const items = episodes.map((m) => `- ${m.content} (${m.createdAt.toLocaleDateString()})`).join(`
683
+ `);
684
+ sections.push(`**Episodic Memory (Events)**:
685
+ ${items}`);
686
+ }
687
+ if (grouped.has("PROCEDURAL" /* PROCEDURAL */)) {
688
+ const skills = grouped.get("PROCEDURAL" /* PROCEDURAL */);
689
+ const items = skills.map((m) => `- ${m.content}`).join(`
537
690
  `);
691
+ sections.push(`**Procedural Knowledge (Skills)**:
692
+ ${items}`);
538
693
  }
694
+ return sections.join(`
695
+
696
+ `);
539
697
  }
540
698
 
541
- // src/evaluators/summarization.ts
542
- var import_core2 = require("@elizaos/core");
543
- var initialSummarizationTemplate = `# Task: Summarize Conversation
544
-
545
- You are analyzing a conversation to create a concise summary that captures the key points, topics, and important details.
546
-
547
- # Recent Messages
548
- {{recentMessages}}
549
-
550
- # Instructions
551
- Generate a summary that:
552
- 1. Captures the main topics discussed
553
- 2. Highlights key information shared
554
- 3. Notes any decisions made or questions asked
555
- 4. Maintains context for future reference
556
- 5. Is concise but comprehensive
557
-
558
- **IMPORTANT**: Keep the summary under 2500 tokens. Be comprehensive but concise.
559
-
560
- Also extract:
561
- - **Topics**: List of main topics discussed (comma-separated)
562
- - **Key Points**: Important facts or decisions (bullet points)
563
-
564
- Respond in this XML format:
565
- <summary>
566
- <text>Your comprehensive summary here</text>
567
- <topics>topic1, topic2, topic3</topics>
568
- <keyPoints>
569
- <point>First key point</point>
570
- <point>Second key point</point>
571
- </keyPoints>
572
- </summary>`;
573
- var updateSummarizationTemplate = `# Task: Update and Condense Conversation Summary
574
-
575
- You are updating an existing conversation summary with new messages, while keeping the total summary concise.
576
-
577
- # Existing Summary
578
- {{existingSummary}}
579
-
580
- # Existing Topics
581
- {{existingTopics}}
582
-
583
- # New Messages Since Last Summary
584
- {{newMessages}}
585
-
586
- # Instructions
587
- Update the summary by:
588
- 1. Merging the existing summary with insights from the new messages
589
- 2. Removing redundant or less important details to stay under the token limit
590
- 3. Keeping the most important context and decisions
591
- 4. Adding new topics if they emerge
592
- 5. **CRITICAL**: Keep the ENTIRE updated summary under 2500 tokens
593
-
594
- The goal is a rolling summary that captures the essence of the conversation without growing indefinitely.
595
-
596
- Respond in this XML format:
597
- <summary>
598
- <text>Your updated and condensed summary here</text>
599
- <topics>topic1, topic2, topic3</topics>
600
- <keyPoints>
601
- <point>First key point</point>
602
- <point>Second key point</point>
603
- </keyPoints>
604
- </summary>`;
605
- function parseSummaryXML(xml) {
606
- const summaryMatch = xml.match(/<text>([\s\S]*?)<\/text>/);
607
- const topicsMatch = xml.match(/<topics>([\s\S]*?)<\/topics>/);
608
- const keyPointsMatches = xml.matchAll(/<point>([\s\S]*?)<\/point>/g);
609
- const summary = summaryMatch ? summaryMatch[1].trim() : "Summary not available";
610
- const topics = topicsMatch ? topicsMatch[1].split(",").map((t) => t.trim()).filter(Boolean) : [];
611
- const keyPoints = Array.from(keyPointsMatches).map((match) => match[1].trim());
612
- return { summary, topics, keyPoints };
699
+ // src/utils/token-counter.ts
700
+ function estimateTokenCount(text3) {
701
+ if (!text3 || text3.length === 0) {
702
+ return 0;
703
+ }
704
+ const charCount = text3.length;
705
+ const estimatedTokens = Math.ceil(charCount / 4);
706
+ return estimatedTokens;
613
707
  }
614
- var summarizationEvaluator = {
615
- name: "MEMORY_SUMMARIZATION",
616
- description: "Automatically summarizes conversations to optimize context usage",
617
- similes: ["CONVERSATION_SUMMARY", "CONTEXT_COMPRESSION", "MEMORY_OPTIMIZATION"],
618
- alwaysRun: true,
619
- validate: async (runtime, message) => {
620
- if (!message.content?.text) {
621
- import_core2.logger.debug("Skipping summarization: no message text");
622
- return false;
708
+ function estimateTokenCountForArray(texts) {
709
+ return texts.reduce((total, text3) => total + estimateTokenCount(text3), 0);
710
+ }
711
+ function trimToTokenBudget(items, budget, getText, includeOverhead = 10) {
712
+ const result = [];
713
+ let currentTokens = 0;
714
+ for (const item of items) {
715
+ const itemText = getText(item);
716
+ const itemTokens = estimateTokenCount(itemText) + includeOverhead;
717
+ if (currentTokens + itemTokens > budget) {
718
+ break;
623
719
  }
624
- const memoryService = runtime.getService("memory");
625
- if (!memoryService) {
626
- import_core2.logger.debug("Skipping summarization: memory service not available");
627
- return false;
720
+ result.push(item);
721
+ currentTokens += itemTokens;
722
+ }
723
+ return result;
724
+ }
725
+ function formatTokenCount(count) {
726
+ if (count < 1000) {
727
+ return `${count} tokens`;
728
+ } else if (count < 1e6) {
729
+ return `${(count / 1000).toFixed(1)}K tokens`;
730
+ } else {
731
+ return `${(count / 1e6).toFixed(1)}M tokens`;
732
+ }
733
+ }
734
+
735
+ // src/services/memory-service.ts
736
+ class MemoryService extends import_core6.Service {
737
+ static serviceType = "memory";
738
+ bm25Index = null;
739
+ embeddingDimension;
740
+ isInitialized = false;
741
+ longTermMemoryRepo;
742
+ conversationSummaryRepo;
743
+ capabilityDescription = "State-of-the-art cognitive memory system with episodic, semantic, and procedural memory";
744
+ static defaultConfig = {
745
+ consolidationThreshold: 12,
746
+ minConfidence: 0.7,
747
+ enableVectorSearch: true,
748
+ enableBM25: true,
749
+ retrievalLimit: 5,
750
+ tokenBudget: 1000,
751
+ defaultDecayRates: {
752
+ ["EPISODIC" /* EPISODIC */]: 0.05,
753
+ ["SEMANTIC" /* SEMANTIC */]: 0.01,
754
+ ["PROCEDURAL" /* PROCEDURAL */]: 0.02
755
+ },
756
+ enableContradictionDetection: true,
757
+ summarization: {
758
+ enabled: true,
759
+ messagesPerSummary: 7,
760
+ summariesPerLevel: 5,
761
+ maxDepth: 3,
762
+ summaryTokenBudget: 500
628
763
  }
629
- const config = memoryService.getConfig();
630
- const currentMessageCount = await runtime.countMemories(message.roomId, false, "messages");
631
- const existingSummary = await memoryService.getCurrentSessionSummary(message.roomId);
632
- if (!existingSummary) {
633
- const shouldSummarize = currentMessageCount >= config.shortTermSummarizationThreshold;
634
- import_core2.logger.debug({
635
- roomId: message.roomId,
636
- currentMessageCount,
637
- threshold: config.shortTermSummarizationThreshold,
638
- shouldSummarize,
639
- reason: "initial_summary_check"
640
- }, "Summarization validation check");
641
- return shouldSummarize;
764
+ };
765
+ constructor(runtime) {
766
+ super(runtime);
767
+ this.config = { ...MemoryService.defaultConfig };
768
+ }
769
+ static async start(runtime) {
770
+ const service = new MemoryService(runtime);
771
+ await service.initialize(runtime);
772
+ return service;
773
+ }
774
+ async stop() {
775
+ import_core6.logger.info("MemoryService stopped");
776
+ }
777
+ async initialize(runtime) {
778
+ this.runtime = runtime;
779
+ this.loadConfiguration();
780
+ await this.ensureEmbeddingDimension();
781
+ this.longTermMemoryRepo = new LongTermMemoryRepository(runtime, this.embeddingDimension);
782
+ this.conversationSummaryRepo = new ConversationSummaryRepository(runtime, this.embeddingDimension);
783
+ import_core6.logger.info({ config: this.config }, "MemoryService initialized");
784
+ if (this.config.enableBM25) {
785
+ await this.rebuildBM25Index();
786
+ }
787
+ this.isInitialized = true;
788
+ }
789
+ loadConfiguration() {
790
+ const threshold = this.runtime.getSetting("MEMORY_CONSOLIDATION_THRESHOLD");
791
+ if (threshold) {
792
+ this.config.consolidationThreshold = parseInt(threshold, 10);
793
+ }
794
+ const minConfidence = this.runtime.getSetting("MEMORY_MIN_CONFIDENCE");
795
+ if (minConfidence) {
796
+ this.config.minConfidence = parseFloat(minConfidence);
797
+ }
798
+ const enableVector = this.runtime.getSetting("MEMORY_ENABLE_VECTOR_SEARCH");
799
+ import_core6.logger.debug({
800
+ enableVector,
801
+ type: typeof enableVector,
802
+ defaultValue: this.config.enableVectorSearch
803
+ }, "Loading MEMORY_ENABLE_VECTOR_SEARCH setting");
804
+ if (enableVector !== undefined && enableVector !== null && enableVector !== "") {
805
+ this.config.enableVectorSearch = enableVector === "true" || enableVector === true;
806
+ import_core6.logger.info({ enabled: this.config.enableVectorSearch }, "Vector search explicitly configured via environment variable");
642
807
  } else {
643
- const newMessageCount = currentMessageCount - existingSummary.lastMessageOffset;
644
- const shouldUpdate = newMessageCount >= config.shortTermSummarizationInterval;
645
- import_core2.logger.debug({
646
- roomId: message.roomId,
647
- currentMessageCount,
648
- lastOffset: existingSummary.lastMessageOffset,
649
- newMessageCount,
650
- interval: config.shortTermSummarizationInterval,
651
- shouldUpdate,
652
- reason: "summary_update_check"
653
- }, "Summarization validation check");
654
- return shouldUpdate;
808
+ import_core6.logger.info({ enabled: this.config.enableVectorSearch }, "Vector search using default configuration");
655
809
  }
656
- },
657
- handler: async (runtime, message) => {
658
- const memoryService = runtime.getService("memory");
659
- if (!memoryService) {
660
- import_core2.logger.error("MemoryService not found");
661
- return;
810
+ const enableBM25 = this.runtime.getSetting("MEMORY_ENABLE_BM25");
811
+ import_core6.logger.debug({
812
+ enableBM25,
813
+ type: typeof enableBM25,
814
+ defaultValue: this.config.enableBM25
815
+ }, "Loading MEMORY_ENABLE_BM25 setting");
816
+ if (enableBM25 !== undefined && enableBM25 !== null && enableBM25 !== "") {
817
+ this.config.enableBM25 = enableBM25 === "true" || enableBM25 === true;
818
+ import_core6.logger.info({ enabled: this.config.enableBM25 }, "BM25 search explicitly configured via environment variable");
819
+ } else {
820
+ import_core6.logger.info({ enabled: this.config.enableBM25 }, "BM25 search using default configuration");
662
821
  }
663
- const config = memoryService.getConfig();
664
- const { roomId } = message;
822
+ const retrievalLimit = this.runtime.getSetting("MEMORY_RETRIEVAL_LIMIT");
823
+ if (retrievalLimit) {
824
+ this.config.retrievalLimit = parseInt(retrievalLimit, 10);
825
+ }
826
+ const tokenBudget = this.runtime.getSetting("MEMORY_TOKEN_BUDGET");
827
+ if (tokenBudget) {
828
+ this.config.tokenBudget = parseInt(tokenBudget, 10);
829
+ }
830
+ const summaryEnabled = this.runtime.getSetting("MEMORY_SUMMARY_ENABLED");
831
+ import_core6.logger.debug({
832
+ summaryEnabled,
833
+ type: typeof summaryEnabled,
834
+ defaultValue: this.config.summarization?.enabled
835
+ }, "Loading MEMORY_SUMMARY_ENABLED setting");
836
+ if (summaryEnabled !== undefined && summaryEnabled !== null && summaryEnabled !== "") {
837
+ this.config.summarization.enabled = summaryEnabled === "true" || summaryEnabled === true;
838
+ import_core6.logger.info({ enabled: this.config.summarization.enabled }, "Summarization explicitly configured via environment variable");
839
+ } else {
840
+ import_core6.logger.info({ enabled: this.config.summarization.enabled }, "Summarization using default configuration");
841
+ }
842
+ const messagesPerSummary = this.runtime.getSetting("MEMORY_MESSAGES_PER_SUMMARY");
843
+ if (messagesPerSummary) {
844
+ this.config.summarization.messagesPerSummary = parseInt(messagesPerSummary, 10);
845
+ }
846
+ const summariesPerLevel = this.runtime.getSetting("MEMORY_SUMMARIES_PER_LEVEL");
847
+ if (summariesPerLevel) {
848
+ this.config.summarization.summariesPerLevel = parseInt(summariesPerLevel, 10);
849
+ }
850
+ const maxDepth = this.runtime.getSetting("MEMORY_SUMMARY_MAX_DEPTH");
851
+ if (maxDepth) {
852
+ this.config.summarization.maxDepth = parseInt(maxDepth, 10);
853
+ }
854
+ const summaryTokenBudget = this.runtime.getSetting("MEMORY_SUMMARY_TOKEN_BUDGET");
855
+ if (summaryTokenBudget) {
856
+ this.config.summarization.summaryTokenBudget = parseInt(summaryTokenBudget, 10);
857
+ }
858
+ }
859
+ async ensureEmbeddingDimension() {
665
860
  try {
666
- import_core2.logger.info(`Starting summarization for room ${roomId}`);
667
- const existingSummary = await memoryService.getCurrentSessionSummary(roomId);
668
- const lastOffset = existingSummary?.lastMessageOffset || 0;
669
- const totalMessageCount = await runtime.countMemories(roomId, false, "messages");
670
- const newMessageCount = totalMessageCount - lastOffset;
671
- const maxNewMessages = config.summaryMaxNewMessages || 50;
672
- const messagesToFetch = Math.min(newMessageCount, maxNewMessages);
673
- if (messagesToFetch === 0) {
674
- import_core2.logger.debug("No new messages to summarize");
861
+ const embeddingModel = this.runtime.getModel(import_core6.ModelType.TEXT_EMBEDDING);
862
+ if (!embeddingModel) {
863
+ import_core6.logger.warn("No TEXT_EMBEDDING model registered. Embeddings will not be generated.");
675
864
  return;
676
865
  }
677
- if (newMessageCount > maxNewMessages) {
678
- import_core2.logger.warn(`Capping new messages at ${maxNewMessages} (${newMessageCount} available). Oldest messages will be skipped.`);
679
- }
680
- const newMessages = await runtime.getMemories({
681
- tableName: "messages",
682
- roomId,
683
- count: messagesToFetch,
684
- unique: false,
685
- start: lastOffset
866
+ const testEmbedding = await this.runtime.useModel(import_core6.ModelType.TEXT_EMBEDDING, {
867
+ text: "test"
686
868
  });
687
- if (newMessages.length === 0) {
688
- import_core2.logger.debug("No new messages retrieved");
689
- return;
869
+ if (!testEmbedding || !Array.isArray(testEmbedding)) {
870
+ throw new Error("Invalid embedding received from model");
690
871
  }
691
- const sortedMessages = newMessages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
692
- const formattedMessages = sortedMessages.map((msg) => {
693
- const sender = msg.entityId === runtime.agentId ? runtime.character.name : "User";
694
- return `${sender}: ${msg.content.text || "[non-text message]"}`;
695
- }).join(`
696
- `);
697
- const state = await runtime.composeState(message);
698
- let prompt;
699
- let template;
700
- if (existingSummary) {
701
- template = updateSummarizationTemplate;
702
- prompt = import_core2.composePromptFromState({
703
- state: {
704
- ...state,
705
- existingSummary: existingSummary.summary,
706
- existingTopics: existingSummary.topics?.join(", ") || "None",
707
- newMessages: formattedMessages
708
- },
709
- template
710
- });
711
- } else {
712
- template = initialSummarizationTemplate;
713
- prompt = import_core2.composePromptFromState({
714
- state: {
715
- ...state,
716
- recentMessages: formattedMessages
717
- },
718
- template
719
- });
872
+ const dimension = testEmbedding.length;
873
+ const dimensionColumn = MEMORY_DIMENSION_MAP[dimension];
874
+ if (!dimensionColumn) {
875
+ throw new Error(`Unsupported embedding dimension: ${dimension}. Supported dimensions: ${Object.keys(MEMORY_DIMENSION_MAP).join(", ")}`);
876
+ }
877
+ this.embeddingDimension = dimensionColumn;
878
+ import_core6.logger.info(`Memory embedding dimension set to ${dimension} (${dimensionColumn})`);
879
+ } catch (error) {
880
+ import_core6.logger.error("Failed to ensure embedding dimension:", JSON.stringify(error));
881
+ throw error;
882
+ }
883
+ }
884
+ getConfig() {
885
+ return { ...this.config };
886
+ }
887
+ updateConfig(updates) {
888
+ this.config = { ...this.config, ...updates };
889
+ if (updates.enableBM25 !== undefined) {
890
+ if (updates.enableBM25 && !this.bm25Index) {
891
+ this.rebuildBM25Index();
892
+ } else if (!updates.enableBM25) {
893
+ this.bm25Index = null;
894
+ }
895
+ }
896
+ }
897
+ async storeLongTermMemory(memory) {
898
+ let embedding;
899
+ if (this.embeddingDimension) {
900
+ try {
901
+ const rawEmbedding = await generateEmbedding(this.runtime, memory.embeddingContext);
902
+ embedding = cleanEmbedding(rawEmbedding);
903
+ } catch (error) {
904
+ import_core6.logger.warn("Failed to generate embedding for long-term memory:", JSON.stringify(error));
905
+ }
906
+ }
907
+ const stored = await this.longTermMemoryRepo.insert(memory, embedding);
908
+ if (this.config.enableBM25 && this.bm25Index) {
909
+ await this.rebuildBM25Index();
910
+ }
911
+ return stored;
912
+ }
913
+ async getLongTermMemory(id) {
914
+ return this.longTermMemoryRepo.findById(id);
915
+ }
916
+ async updateLongTermMemory(id, updates) {
917
+ let newEmbedding;
918
+ if (updates.embeddingContext !== undefined && this.embeddingDimension) {
919
+ try {
920
+ const rawEmbedding = await generateEmbedding(this.runtime, updates.embeddingContext);
921
+ newEmbedding = cleanEmbedding(rawEmbedding);
922
+ } catch (error) {
923
+ import_core6.logger.warn("Failed to regenerate embedding:", JSON.stringify(error));
720
924
  }
721
- const response = await runtime.useModel(import_core2.ModelType.TEXT_LARGE, {
925
+ }
926
+ await this.longTermMemoryRepo.update(id, updates, newEmbedding);
927
+ }
928
+ async deleteLongTermMemory(id) {
929
+ await this.longTermMemoryRepo.delete(id);
930
+ }
931
+ async getLongTermMemories(entityId, type, limit = 20, includeInactive = false) {
932
+ return this.longTermMemoryRepo.findByEntity(entityId, type, limit, includeInactive);
933
+ }
934
+ async handleContradiction(entityId, newMemory) {
935
+ const searchResults = await this.searchLongTermMemories({
936
+ entityId,
937
+ query: newMemory.content,
938
+ type: newMemory.type,
939
+ limit: 5,
940
+ includeInactive: false
941
+ });
942
+ if (searchResults.length === 0) {
943
+ await this.storeLongTermMemory(newMemory);
944
+ return;
945
+ }
946
+ const contradictingMemory = await this.detectContradiction(newMemory, searchResults);
947
+ if (contradictingMemory) {
948
+ import_core6.logger.info({
949
+ oldMemoryId: contradictingMemory.id,
950
+ newContent: newMemory.content
951
+ }, "Contradiction detected, superseding old memory");
952
+ await this.updateLongTermMemory(contradictingMemory.id, {
953
+ isActive: false
954
+ });
955
+ const stored = await this.storeLongTermMemory({
956
+ ...newMemory,
957
+ supersedesId: contradictingMemory.id
958
+ });
959
+ import_core6.logger.info({ newMemoryId: stored.id }, "Stored superseding memory");
960
+ } else {
961
+ await this.storeLongTermMemory(newMemory);
962
+ }
963
+ }
964
+ async detectContradiction(newMemory, existingMemories) {
965
+ const prompt = `# TASK: Contradiction Detection
966
+
967
+ You are analyzing whether a new long-term memory contradicts any existing long-term memories.
968
+
969
+ ## New Long-Term Memory:
970
+ "${newMemory.content}"
971
+
972
+ ## Existing Long-Term Memories:
973
+ ${existingMemories.map((m, idx) => `${idx + 1}. "${m.content}" (confidence: ${m.confidence}, created: ${m.createdAt.toISOString()})`).join(`
974
+ `)}
975
+
976
+ ## Instructions:
977
+ Determine if the new long-term memory directly contradicts any of the existing long-term memories. A contradiction means the statements cannot both be true.
978
+
979
+ Examples of contradictions:
980
+ - "User likes blue" vs "User hates blue"
981
+ - "User lives in Paris" vs "User lives in London"
982
+
983
+ Examples of non-contradictions (these are compatible):
984
+ - "User likes blue" vs "User likes blue only for clothes" (nuance, not contradiction)
985
+ - "User was in Paris" vs "User moved to London" (state change over time)
986
+
987
+ ## Output Format:
988
+ Return an XML response:
989
+
990
+ <response>
991
+ <hasContradiction>true or false</hasContradiction>
992
+ <contradictingMemoryIndex>number or null</contradictingMemoryIndex>
993
+ <reasoning>Explanation</reasoning>
994
+ </response>
995
+
996
+ If no contradiction is found, set hasContradiction to false and contradictingMemoryIndex to null.`;
997
+ try {
998
+ const response = await this.runtime.useModel(import_core6.ModelType.TEXT_LARGE, {
722
999
  prompt,
723
- maxTokens: config.summaryMaxTokens || 2500
1000
+ temperature: 0.2
724
1001
  });
725
- const summaryResult = parseSummaryXML(response);
726
- import_core2.logger.info(`${existingSummary ? "Updated" : "Generated"} summary: ${summaryResult.summary.substring(0, 100)}...`);
727
- const newOffset = totalMessageCount;
728
- const firstMessage = sortedMessages[0];
729
- const lastMessage = sortedMessages[sortedMessages.length - 1];
730
- const startTime = existingSummary ? existingSummary.startTime : firstMessage?.createdAt && firstMessage.createdAt > 0 ? new Date(firstMessage.createdAt) : new Date;
731
- const endTime = lastMessage?.createdAt && lastMessage.createdAt > 0 ? new Date(lastMessage.createdAt) : new Date;
732
- if (existingSummary) {
733
- await memoryService.updateSessionSummary(existingSummary.id, roomId, {
734
- summary: summaryResult.summary,
735
- messageCount: existingSummary.messageCount + sortedMessages.length,
736
- lastMessageOffset: newOffset,
737
- endTime,
738
- topics: summaryResult.topics,
739
- metadata: {
740
- keyPoints: summaryResult.keyPoints
741
- }
742
- });
743
- import_core2.logger.info(`Updated summary for room ${roomId}: ${sortedMessages.length} new messages processed (offset: ${lastOffset} → ${newOffset})`);
744
- } else {
745
- await memoryService.storeSessionSummary({
746
- agentId: runtime.agentId,
747
- roomId,
748
- entityId: message.entityId !== runtime.agentId ? message.entityId : undefined,
749
- summary: summaryResult.summary,
750
- messageCount: sortedMessages.length,
751
- lastMessageOffset: newOffset,
752
- startTime,
753
- endTime,
754
- topics: summaryResult.topics,
755
- metadata: {
756
- keyPoints: summaryResult.keyPoints
757
- }
1002
+ const responseText = typeof response === "string" ? response : JSON.stringify(response);
1003
+ const result = import_core6.parseKeyValueXml(responseText);
1004
+ if (!result) {
1005
+ import_core6.logger.warn("Failed to parse contradiction detection XML response");
1006
+ return null;
1007
+ }
1008
+ const hasContradiction = import_core6.parseBooleanFromText(typeof result.hasContradiction === "boolean" ? result.hasContradiction ? "true" : "false" : result.hasContradiction);
1009
+ let contradictingMemoryIndex = null;
1010
+ if (result.contradictingMemoryIndex !== null && result.contradictingMemoryIndex !== "null") {
1011
+ const parsed = parseInt(result.contradictingMemoryIndex, 10);
1012
+ if (!isNaN(parsed)) {
1013
+ contradictingMemoryIndex = parsed;
1014
+ }
1015
+ }
1016
+ if (hasContradiction && contradictingMemoryIndex !== null) {
1017
+ const contradictingMemory = existingMemories[contradictingMemoryIndex];
1018
+ import_core6.logger.info({ reasoning: result.reasoning }, "Contradiction detected");
1019
+ return contradictingMemory;
1020
+ }
1021
+ return null;
1022
+ } catch (error) {
1023
+ import_core6.logger.error({ error }, "Failed to detect contradiction");
1024
+ return null;
1025
+ }
1026
+ }
1027
+ async searchLongTermMemories(params) {
1028
+ const limit = params.limit || this.config.retrievalLimit;
1029
+ const tokenBudget = params.tokenBudget || this.config.tokenBudget;
1030
+ const minConfidence = params.minConfidence !== undefined ? params.minConfidence : this.config.minConfidence;
1031
+ const similarityThreshold = params.similarityThreshold !== undefined ? params.similarityThreshold : 0.3;
1032
+ import_core6.logger.debug({
1033
+ limit,
1034
+ tokenBudget,
1035
+ minConfidence,
1036
+ similarityThreshold,
1037
+ vectorSearchEnabled: this.config.enableVectorSearch,
1038
+ bm25Enabled: this.config.enableBM25
1039
+ }, "Searching long-term memories");
1040
+ let vectorResults = [];
1041
+ if (this.config.enableVectorSearch) {
1042
+ import_core6.logger.debug("Vector search enabled, searching...");
1043
+ vectorResults = await this.vectorSearch(params, similarityThreshold);
1044
+ }
1045
+ import_core6.logger.debug({ vectorResults: vectorResults.length }, "Vector search results");
1046
+ let bm25Results = [];
1047
+ if (this.config.enableBM25) {
1048
+ bm25Results = await this.bm25Search(params);
1049
+ }
1050
+ const mergedResults = mergeSearchResults(vectorResults, bm25Results);
1051
+ const confidenceFiltered = mergedResults.filter((m) => m.confidence >= minConfidence);
1052
+ const decayedResults = applyDecayScoring(confidenceFiltered);
1053
+ const sorted = decayedResults.sort((a, b) => b.finalScore - a.finalScore);
1054
+ const topResults = sorted.slice(0, limit);
1055
+ const budgetedResults = trimToTokenBudget(topResults, tokenBudget, (memory) => memory.content, 15);
1056
+ import_core6.logger.debug({
1057
+ totalResults: sorted.length,
1058
+ afterCountLimit: topResults.length,
1059
+ afterTokenBudget: budgetedResults.length,
1060
+ tokenBudget,
1061
+ estimatedTokens: budgetedResults.reduce((sum, m) => sum + estimateTokenCount(m.content) + 15, 0)
1062
+ }, "Applied token budget to memory retrieval");
1063
+ await this.longTermMemoryRepo.updateAccessMetadata(budgetedResults.map((r) => r.id));
1064
+ return budgetedResults;
1065
+ }
1066
+ async vectorSearch(params, similarityThreshold = 0.3) {
1067
+ if (!this.embeddingDimension) {
1068
+ import_core6.logger.warn("Embedding dimension not set, skipping vector search");
1069
+ return [];
1070
+ }
1071
+ try {
1072
+ const rawEmbedding = await generateEmbedding(this.runtime, params.query);
1073
+ const queryEmbedding = cleanEmbedding(rawEmbedding);
1074
+ if (!queryEmbedding || !Array.isArray(queryEmbedding)) {
1075
+ import_core6.logger.warn("Failed to generate query embedding");
1076
+ return [];
1077
+ }
1078
+ return await this.longTermMemoryRepo.vectorSearch(params, queryEmbedding, similarityThreshold);
1079
+ } catch (error) {
1080
+ import_core6.logger.error("Failed to execute vector search:", JSON.stringify(error));
1081
+ return [];
1082
+ }
1083
+ }
1084
+ async bm25Search(params) {
1085
+ if (!this.bm25Index) {
1086
+ return [];
1087
+ }
1088
+ try {
1089
+ const bm25Results = this.bm25Index.search(params.query, params.limit || 20);
1090
+ const memoryPromises = bm25Results.map(async (result) => {
1091
+ const doc = this.bm25Index.documents[result.index];
1092
+ if (!doc || !doc.id) {
1093
+ import_core6.logger.warn({ resultIndex: result.index }, "BM25 result has no document ID");
1094
+ return null;
1095
+ }
1096
+ const memory = await this.getLongTermMemory(doc.id);
1097
+ if (!memory)
1098
+ return null;
1099
+ if (memory.entityId !== params.entityId)
1100
+ return null;
1101
+ if (!params.includeInactive && !memory.isActive)
1102
+ return null;
1103
+ if (params.type && memory.type !== params.type)
1104
+ return null;
1105
+ if (params.minConfidence && memory.confidence < params.minConfidence)
1106
+ return null;
1107
+ return {
1108
+ ...memory,
1109
+ relevanceScore: result.score,
1110
+ activationScore: 0,
1111
+ finalScore: 0
1112
+ };
1113
+ });
1114
+ const results = await Promise.all(memoryPromises);
1115
+ return results.filter((r) => r !== null);
1116
+ } catch (error) {
1117
+ import_core6.logger.error({ error }, "BM25 search failed");
1118
+ return [];
1119
+ }
1120
+ }
1121
+ async rebuildBM25Index() {
1122
+ try {
1123
+ const memories = await this.longTermMemoryRepo.fetchAllActive();
1124
+ const documents = memories.map((row) => ({
1125
+ id: row.id,
1126
+ content: row.content,
1127
+ embeddingContext: row.embeddingContext
1128
+ }));
1129
+ this.bm25Index = new import_core7.BM25(documents, {
1130
+ k1: 1.2,
1131
+ b: 0.75,
1132
+ stemming: true,
1133
+ minLength: 2
1134
+ });
1135
+ import_core6.logger.info({ documentCount: documents.length }, "Rebuilt BM25 index");
1136
+ } catch (error) {
1137
+ import_core6.logger.error({ error }, "Failed to rebuild BM25 index");
1138
+ this.bm25Index = null;
1139
+ }
1140
+ }
1141
+ async getFormattedLongTermMemoriesForContext(entityId, query, roomId) {
1142
+ const memories = await this.searchLongTermMemories({
1143
+ entityId,
1144
+ query,
1145
+ roomId,
1146
+ limit: this.config.retrievalLimit
1147
+ });
1148
+ return formatMemoriesForContext(memories);
1149
+ }
1150
+ async storeSummary(summary) {
1151
+ let embedding;
1152
+ if (this.embeddingDimension) {
1153
+ try {
1154
+ const rawEmbedding = await generateEmbedding(this.runtime, summary.content);
1155
+ embedding = cleanEmbedding(rawEmbedding);
1156
+ } catch (error) {
1157
+ import_core6.logger.warn("Failed to generate embedding for summary:", JSON.stringify(error));
1158
+ }
1159
+ }
1160
+ return this.conversationSummaryRepo.insert(summary, embedding);
1161
+ }
1162
+ async getSummariesByLevel(roomId, level) {
1163
+ return this.conversationSummaryRepo.findByLevel(roomId, level);
1164
+ }
1165
+ async searchSummaries(params) {
1166
+ if (!this.embeddingDimension) {
1167
+ import_core6.logger.warn("Embedding dimension not set, skipping summary search");
1168
+ return [];
1169
+ }
1170
+ const limit = params.limit || 5;
1171
+ const tokenBudget = params.tokenBudget || this.config.summarization?.summaryTokenBudget || 500;
1172
+ try {
1173
+ const rawEmbedding = await generateEmbedding(this.runtime, params.query);
1174
+ const queryEmbedding = cleanEmbedding(rawEmbedding);
1175
+ if (!queryEmbedding || !Array.isArray(queryEmbedding)) {
1176
+ import_core6.logger.warn("Failed to generate query embedding");
1177
+ return [];
1178
+ }
1179
+ const summaries = await this.conversationSummaryRepo.vectorSearch(params.entityId, params.roomId, queryEmbedding, limit);
1180
+ const budgetedSummaries = trimToTokenBudget(summaries, tokenBudget, (s) => s.content, 10);
1181
+ await this.conversationSummaryRepo.updateAccessMetadata(budgetedSummaries.map((s) => s.id));
1182
+ return budgetedSummaries;
1183
+ } catch (error) {
1184
+ import_core6.logger.error("Failed to search summaries:", JSON.stringify(error));
1185
+ return [];
1186
+ }
1187
+ }
1188
+ async getMostRecentLevel1Summary(roomId, entityId) {
1189
+ try {
1190
+ const summaries = await this.conversationSummaryRepo.findByLevel(roomId, 1);
1191
+ if (summaries.length === 0) {
1192
+ return null;
1193
+ }
1194
+ const entitySummaries = summaries.filter((s) => s.entityId === entityId).sort((a, b) => b.endTime.getTime() - a.endTime.getTime());
1195
+ return entitySummaries.length > 0 ? entitySummaries[0] : null;
1196
+ } catch (error) {
1197
+ import_core6.logger.error("Failed to get most recent Level 1 summary:", JSON.stringify(error));
1198
+ return null;
1199
+ }
1200
+ }
1201
+ }
1202
+
1203
+ // src/evaluators/consolidation.ts
1204
+ var import_core8 = require("@elizaos/core");
1205
+
1206
+ // src/prompts/consolidation.ts
1207
+ var CONSOLIDATION_SYSTEM_PROMPT = `You are the "Cortex" — an advanced Memory Extraction Engine.
1208
+ Your function is to parse conversation logs and extract persistent facts into a structured database format.
1209
+
1210
+ # CORE DIRECTIVE: "Subject-First" Extraction
1211
+ You must rephrase memories to focus on the *topic*, not the user. This optimizes vector retrieval.
1212
+ - BAD: "User likes to trade Bitcoin." (Too generic)
1213
+ - GOOD: "Bitcoin (BTC) is a preferred trading asset." (Topic-focused)
1214
+
1215
+ # COMPRESSION RULES (CRITICAL)
1216
+ 1. **Aggressive Filtering**: Most user chatter is noise. If it won't be relevant in 30 days, DO NOT extract it.
1217
+ 2. **Merge & Dedupe**: Do not create three separate memories for one topic. Combine them.
1218
+ - *Input:* "I like Red. I also like Blue. And Green."
1219
+ - *Output:* "Red, Blue, and Green are the preferred colors."
1220
+ 3. **Conflict Resolution**: If a new fact contradicts an old one, mark 'isContradiction' as true.
1221
+
1222
+ # OUTPUT FORMAT
1223
+ Phase 1: [ANALYSIS]
1224
+ - List extracted points.
1225
+ - MARK items as [TRANSIENT] (Ignore) or [MERGE] (Combine).
1226
+ - Refine the final wording.
1227
+
1228
+ Phase 2: [MEMORIES]
1229
+ Format: \`MEM|TYPE|CATEGORY|CONFIDENCE|IS_CONTRADICTION|CONTENT\`
1230
+
1231
+ Types: EPISODIC, SEMANTIC, PROCEDURAL
1232
+ Categories: bio, health, finance, preferences, relationships, skills, work
1233
+ `;
1234
+ function buildExtractionPrompt(conversationLog) {
1235
+ const refDate = new Date().toISOString();
1236
+ return `# INPUT DATA
1237
+ **Reference Date:** ${refDate} (Use this to resolve relative dates like "yesterday" or "next Friday")
1238
+
1239
+ <conversation_log>
1240
+ ${conversationLog}
1241
+ </conversation_log>
1242
+
1243
+ # FEW-SHOT EXAMPLES (DENSE INPUTS)
1244
+
1245
+ <example_1_finance_consolidation>
1246
+ Input: "Okay, market is looking bad. I'm closing my ETH long. Too risky. Also, can you check the weather in Tokyo? I might fly there. Actually, cancel all my limit orders on Solana too, I want to go all cash for the weekend."
1247
+ Output:
1248
+ [ANALYSIS]
1249
+ - "Market looking bad" -> Context, not memory.
1250
+ - "Closing ETH long" -> Actionable preference change.
1251
+ - "Check weather" -> [TRANSIENT] Ignore.
1252
+ - "Fly to Tokyo" -> [TRANSIENT] "Might" implies uncertainty. Ignore until confirmed.
1253
+ - "Cancel SOL orders" -> Actionable strategy.
1254
+ - "Go all cash" -> High-level strategy.
1255
+ - MERGE: Combine ETH close, SOL cancel, and Cash strategy into one record.
1256
+ [MEMORIES]
1257
+ MEM|PROCEDURAL|finance|0.95|true|Portfolio Strategy: All positions (ETH, SOL) liquidated; Cash-only stance adopted for weekend.
1258
+ </example_1_finance_consolidation>
1259
+
1260
+ <example_2_companion_emotional>
1261
+ Input: "I had a huge fight with my sister, Jenny. She's so controlling. I don't want to talk about her anymore. Let's play a game. Maybe chess? Actually no, I hate chess, it's boring. Let's do a quiz."
1262
+ Output:
1263
+ [ANALYSIS]
1264
+ - "Fight with sister Jenny" -> Relationship dynamic.
1265
+ - "She's controlling" -> Character attribute.
1266
+ - "Don't want to talk about her" -> Boundary/Preference.
1267
+ - "Play a game" -> [TRANSIENT] Immediate desire.
1268
+ - "Hate chess" -> Negative Preference.
1269
+ - "Do a quiz" -> [TRANSIENT] Immediate desire.
1270
+ - MERGE: Combine Jenny details. Separate Chess preference.
1271
+ [MEMORIES]
1272
+ MEM|EPISODIC|relationships|0.9|false|Jenny (sister) is characterized as controlling; currently a sensitive topic to be avoided.
1273
+ MEM|SEMANTIC|preferences|0.95|false|Chess is a disliked activity (described as boring).
1274
+ </example_2_companion_emotional>
1275
+
1276
+ <example_3_coding_stack>
1277
+ Input: "This node_modules folder is huge. I'm done with NPM. From now on we only use Bun for all projects. It's faster. Also, help me debug this loop. It's printing 'undefined'."
1278
+ Output:
1279
+ [ANALYSIS]
1280
+ - "node_modules huge" -> Rationale.
1281
+ - "Done with NPM" -> Deprecation.
1282
+ - "Use Bun" -> New Standard.
1283
+ - "Debug this loop" -> [TRANSIENT] Immediate task.
1284
+ - MERGE: Bun adoption and NPM rejection.
1285
+ [MEMORIES]
1286
+ MEM|PROCEDURAL|skills|0.95|true|Bun is the mandated package manager; NPM usage is deprecated/forbidden.
1287
+ </example_3_coding_stack>
1288
+
1289
+ <example_4_health_routine>
1290
+ Input: "I ate a burger today, felt kinda heavy. I think I'm going to start intermittent fasting. 16/8 window. Start eating at 12pm, stop at 8pm. Remind me to drink water."
1291
+ Output:
1292
+ [ANALYSIS]
1293
+ - "Ate a burger" -> [TRANSIENT] One-off meal.
1294
+ - "Felt heavy" -> [TRANSIENT] Temporary sensation.
1295
+ - "Start intermittent fasting" -> New Health Protocol.
1296
+ - "16/8 window, 12-8" -> Specific details of protocol.
1297
+ - "Remind me to drink water" -> [TRANSIENT] Command.
1298
+ - MERGE: All fasting details into one concise protocol.
1299
+ [MEMORIES]
1300
+ MEM|PROCEDURAL|health|0.9|false|Intermittent Fasting (16/8 protocol) adopted: Eating window restricted to 12pm-8pm.
1301
+ </example_4_health_routine>
1302
+
1303
+ <example_5_work_milestones>
1304
+ Input: "Meeting went well. The client, Apex Corp, agreed to the $50k budget. But they want the deadline moved to March 1st. Can you write a thank you note? Oh, and I need to update my resume."
1305
+ Output:
1306
+ [ANALYSIS]
1307
+ - "Meeting went well" -> [TRANSIENT] Sentiment.
1308
+ - "Apex Corp" -> Client Entity.
1309
+ - "$50k budget" -> Financial Fact.
1310
+ - "Deadline March 1st" -> Project Constraint.
1311
+ - "Write note" -> [TRANSIENT] Task.
1312
+ - "Update resume" -> [TRANSIENT] Generic task unless specific details given.
1313
+ - MERGE: Client details, budget, and deadline.
1314
+ [MEMORIES]
1315
+ MEM|SEMANTIC|work|1.0|false|Apex Corp project secured: $50k budget with March 1st deadline.
1316
+ </example_5_work_milestones>
1317
+
1318
+ # EXTRACTION CHECKLIST
1319
+ 1. **Search for STATE CHANGES**: Did the user move, change jobs, break up, or alter a portfolio? These are high-value.
1320
+ 2. **Search for HARD CONSTRAINTS**: Look for phrases like "Never do X", "Always use Y", "I hate Z".
1321
+ 3. **COMPRESSION**:
1322
+ - You have received a long conversation.
1323
+ - **MERGE** related details into single, dense records.
1324
+ - **IGNORE** all small talk, greetings, and transient requests.
1325
+ 4. **QUALITY CONTROL**: If a fact feels temporary or weak, **DO NOT** extract it. Silence is better than noise.
1326
+
1327
+ Begin the [ANALYSIS] phase now.`;
1328
+ }
1329
+ function buildContradictionPrompt(newMemoryContent, existingMemories) {
1330
+ return `Does this new memory contradict any existing memories?
1331
+
1332
+ New: "${newMemoryContent}"
1333
+
1334
+ Existing:
1335
+ ${existingMemories.map((m, idx) => `${idx + 1}. "${m.content}" (confidence: ${m.confidence})`).join(`
1336
+ `)}
1337
+
1338
+ A contradiction means both statements cannot be true simultaneously.
1339
+
1340
+ TRUE contradictions:
1341
+ - "User likes blue" vs "User hates blue"
1342
+ - "User lives in Paris" vs "User lives in London"
1343
+ - "User is vegetarian" vs "User eats meat"
1344
+
1345
+ NOT contradictions:
1346
+ - "User likes blue" vs "User likes blue for clothes" (nuance)
1347
+ - "User was in Paris" vs "User moved to London" (time change)
1348
+ - "User likes Python" vs "User likes JavaScript" (not exclusive)
1349
+
1350
+ <response>
1351
+ <hasContradiction>true or false</hasContradiction>
1352
+ <contradictingIndex>number or null</contradictingIndex>
1353
+ <reasoning>Brief explanation</reasoning>
1354
+ </response>`;
1355
+ }
1356
+
1357
+ // src/evaluators/consolidation.ts
1358
+ var consolidationBuffers = new Map;
1359
+ var consolidationEvaluator = {
1360
+ name: "CONSOLIDATION",
1361
+ similes: ["MEMORY_CONSOLIDATION", "EXTRACT_FACTS", "MEMORY_BUFFER"],
1362
+ description: "Buffers conversation messages and performs periodic consolidation to extract persistent facts " + "using LLM analysis. Distinguishes transient intents from long-term knowledge.",
1363
+ validate: async (runtime, message) => {
1364
+ if (!consolidationBuffers.has(message.roomId)) {
1365
+ consolidationBuffers.set(message.roomId, []);
1366
+ }
1367
+ consolidationBuffers.get(message.roomId).push(message);
1368
+ const memoryService = runtime.getService("memory");
1369
+ if (!memoryService) {
1370
+ return false;
1371
+ }
1372
+ const config = memoryService.getConfig();
1373
+ const bufferSize = consolidationBuffers.get(message.roomId).length;
1374
+ const shouldConsolidate = bufferSize >= config.consolidationThreshold;
1375
+ if (shouldConsolidate) {
1376
+ import_core8.logger.info({
1377
+ roomId: message.roomId,
1378
+ bufferSize,
1379
+ threshold: config.consolidationThreshold
1380
+ }, "Consolidation threshold reached");
1381
+ }
1382
+ return shouldConsolidate;
1383
+ },
1384
+ handler: async (runtime, message) => {
1385
+ const roomId = message.roomId;
1386
+ try {
1387
+ const memoryService = runtime.getService("memory");
1388
+ if (!memoryService) {
1389
+ import_core8.logger.warn("Memory service not available for consolidation");
1390
+ return;
1391
+ }
1392
+ const buffer = consolidationBuffers.get(roomId) || [];
1393
+ if (buffer.length === 0) {
1394
+ return;
1395
+ }
1396
+ import_core8.logger.info({ roomId, messageCount: buffer.length }, "Starting memory consolidation");
1397
+ const conversationLog = buffer.map((m, idx) => {
1398
+ const content = typeof m.content === "string" ? m.content : m.content.text || JSON.stringify(m.content);
1399
+ return `[${idx + 1}] ${m.entityId}: ${content}`;
1400
+ }).join(`
1401
+ `);
1402
+ const extractionPrompt = buildExtractionPrompt(conversationLog);
1403
+ const originalSystemPrompt = runtime.character.system;
1404
+ try {
1405
+ runtime.character.system = CONSOLIDATION_SYSTEM_PROMPT;
1406
+ import_core8.logger.debug("Calling LLM for memory extraction");
1407
+ const response = await runtime.useModel(import_core8.ModelType.TEXT_LARGE, {
1408
+ prompt: extractionPrompt,
1409
+ temperature: 0.3
758
1410
  });
759
- import_core2.logger.info(`Created new summary for room ${roomId}: ${sortedMessages.length} messages summarized (offset: 0 → ${newOffset})`);
1411
+ const consolidationResult = parseConsolidationResult(response);
1412
+ import_core8.logger.debug({
1413
+ reasoning: consolidationResult.reasoningTrace,
1414
+ extracted: consolidationResult.extractedMemories.length
1415
+ }, "Extraction complete");
1416
+ const config = memoryService.getConfig();
1417
+ const validMemories = consolidationResult.extractedMemories.filter((m) => m.confidence >= config.minConfidence);
1418
+ import_core8.logger.info({
1419
+ total: consolidationResult.extractedMemories.length,
1420
+ valid: validMemories.length,
1421
+ minConfidence: config.minConfidence
1422
+ }, "Filtered extracted memories");
1423
+ for (const extracted of validMemories) {
1424
+ await storeExtractedMemory(runtime, memoryService, roomId, buffer, extracted);
1425
+ }
1426
+ consolidationBuffers.set(roomId, []);
1427
+ import_core8.logger.info({ roomId, stored: validMemories.length }, "Memory consolidation complete");
1428
+ } finally {
1429
+ runtime.character.system = originalSystemPrompt;
760
1430
  }
761
1431
  } catch (error) {
762
- import_core2.logger.error({ error }, "Error during summarization:");
1432
+ import_core8.logger.error({ error, roomId }, "Consolidation evaluator failed");
763
1433
  }
764
1434
  },
765
1435
  examples: []
766
1436
  };
1437
+ function parseConsolidationResult(response) {
1438
+ try {
1439
+ const responseText = typeof response === "string" ? response : JSON.stringify(response);
1440
+ const analysisMatch = responseText.match(/\[ANALYSIS\](.*?)(?:\[MEMORIES\]|$)/s);
1441
+ const reasoningTrace = analysisMatch ? analysisMatch[1].trim() : "";
1442
+ const memoriesMatch = responseText.match(/\[MEMORIES\](.*?)$/s);
1443
+ const memoriesText = memoriesMatch ? memoriesMatch[1].trim() : "";
1444
+ const extractedMemories = [];
1445
+ if (memoriesText) {
1446
+ const lines = memoriesText.split(`
1447
+ `);
1448
+ for (const line of lines) {
1449
+ const trimmedLine = line.trim();
1450
+ if (!trimmedLine || !trimmedLine.startsWith("MEM|")) {
1451
+ continue;
1452
+ }
1453
+ const parts = trimmedLine.split("|");
1454
+ if (parts.length < 6) {
1455
+ import_core8.logger.warn({ line: trimmedLine }, "Skipping malformed memory line (insufficient fields)");
1456
+ continue;
1457
+ }
1458
+ const type = parts[1].trim();
1459
+ const category = parts[2].trim();
1460
+ const confidenceStr = parts[3].trim();
1461
+ const isContradictionStr = parts[4].trim();
1462
+ const content = parts.slice(5).join("|").trim();
1463
+ if (!["EPISODIC", "SEMANTIC", "PROCEDURAL"].includes(type)) {
1464
+ import_core8.logger.warn({ type, line: trimmedLine }, "Invalid memory type");
1465
+ continue;
1466
+ }
1467
+ if (!category) {
1468
+ import_core8.logger.warn({ line: trimmedLine }, "Missing category");
1469
+ continue;
1470
+ }
1471
+ const confidence = parseFloat(confidenceStr);
1472
+ if (isNaN(confidence) || confidence < 0 || confidence > 1) {
1473
+ import_core8.logger.warn({ confidenceStr, line: trimmedLine }, "Invalid confidence value");
1474
+ continue;
1475
+ }
1476
+ const isContradiction = isContradictionStr.toLowerCase() === "true";
1477
+ if (!content) {
1478
+ import_core8.logger.warn({ line: trimmedLine }, "Missing content");
1479
+ continue;
1480
+ }
1481
+ const metadata = {
1482
+ category
1483
+ };
1484
+ extractedMemories.push({
1485
+ type,
1486
+ content,
1487
+ confidence,
1488
+ isContradiction,
1489
+ metadata
1490
+ });
1491
+ }
1492
+ }
1493
+ return {
1494
+ reasoningTrace,
1495
+ transientSummary: "",
1496
+ extractedMemories
1497
+ };
1498
+ } catch (error) {
1499
+ import_core8.logger.error({ error }, "Failed to parse consolidation response");
1500
+ return {
1501
+ reasoningTrace: "Parse error",
1502
+ transientSummary: "",
1503
+ extractedMemories: []
1504
+ };
1505
+ }
1506
+ }
1507
+ async function storeExtractedMemory(runtime, memoryService, roomId, buffer, extracted) {
1508
+ const entityId = buffer[0]?.entityId || runtime.agentId;
1509
+ const embeddingContext = generateContextualString(extracted);
1510
+ const config = memoryService.getConfig();
1511
+ const decayRate = config.defaultDecayRates[extracted.type];
1512
+ const memoryData = {
1513
+ agentId: runtime.agentId,
1514
+ entityId,
1515
+ roomId,
1516
+ type: extracted.type,
1517
+ content: extracted.content,
1518
+ embeddingContext,
1519
+ confidence: extracted.confidence,
1520
+ decayRate,
1521
+ decayFunction: "EXPONENTIAL" /* EXPONENTIAL */,
1522
+ source: {
1523
+ sessionId: roomId,
1524
+ messageId: extracted.sourceMessageId,
1525
+ textSnippet: extracted.content.substring(0, 200)
1526
+ },
1527
+ metadata: extracted.metadata || {}
1528
+ };
1529
+ if (config.enableContradictionDetection && extracted.isContradiction) {
1530
+ await memoryService.handleContradiction(entityId, memoryData);
1531
+ } else {
1532
+ await memoryService.storeLongTermMemory(memoryData);
1533
+ }
1534
+ }
1535
+ function generateContextualString(extracted) {
1536
+ const typeLabel = {
1537
+ ["EPISODIC" /* EPISODIC */]: "Event",
1538
+ ["SEMANTIC" /* SEMANTIC */]: "Fact",
1539
+ ["PROCEDURAL" /* PROCEDURAL */]: "Skill"
1540
+ }[extracted.type];
1541
+ const category = extracted.metadata?.category || "general";
1542
+ return `[${typeLabel} about ${category}]: ${extracted.content}`;
1543
+ }
767
1544
 
768
- // src/evaluators/long-term-extraction.ts
769
- var import_core3 = require("@elizaos/core");
1545
+ // src/evaluators/summarization.ts
1546
+ var import_core9 = require("@elizaos/core");
770
1547
 
771
- // src/types/index.ts
772
- var LongTermMemoryCategory;
773
- ((LongTermMemoryCategory2) => {
774
- LongTermMemoryCategory2["IDENTITY"] = "identity";
775
- LongTermMemoryCategory2["EXPERTISE"] = "expertise";
776
- LongTermMemoryCategory2["PROJECTS"] = "projects";
777
- LongTermMemoryCategory2["PREFERENCES"] = "preferences";
778
- LongTermMemoryCategory2["DATA_SOURCES"] = "data_sources";
779
- LongTermMemoryCategory2["GOALS"] = "goals";
780
- LongTermMemoryCategory2["CONSTRAINTS"] = "constraints";
781
- LongTermMemoryCategory2["DEFINITIONS"] = "definitions";
782
- LongTermMemoryCategory2["BEHAVIORAL_PATTERNS"] = "behavioral_patterns";
783
- })(LongTermMemoryCategory ||= {});
784
-
785
- // src/evaluators/long-term-extraction.ts
786
- var extractionTemplate = `# Task: Extract Long-Term Memory
787
-
788
- You are analyzing a conversation to extract ONLY the most important, persistent facts about the user that should be remembered long-term.
789
-
790
- # Recent Messages
791
- {{recentMessages}}
792
-
793
- # Current Long-Term Memories
794
- {{existingMemories}}
795
-
796
- # Memory Categories
797
- 1. **identity**: User's name, role, identity (e.g., "I'm a data scientist")
798
- 2. **expertise**: User's skills, knowledge domains, or unfamiliarity with topics
799
- 3. **projects**: Ongoing projects, past interactions, recurring topics
800
- 4. **preferences**: Communication style, format preferences, verbosity, etc.
801
- 5. **data_sources**: Frequently used files, databases, APIs
802
- 6. **goals**: Broader intentions (e.g., "preparing for interview")
803
- 7. **constraints**: User-defined rules or limitations
804
- 8. **definitions**: Custom terms, acronyms, glossaries
805
- 9. **behavioral_patterns**: How the user tends to interact
806
-
807
- # STRICT EXTRACTION CRITERIA
808
-
809
- **DO EXTRACT** - Facts that are:
810
- - Explicitly stated personal information (name, role, profession)
811
- - Repeated patterns across multiple conversations (3+ occurrences)
812
- - Core preferences stated with clear emphasis ("I always prefer...", "I never want...")
813
- - Important ongoing projects or goals mentioned multiple times
814
- - Persistent technical constraints or requirements
815
- - Domain expertise demonstrated consistently
816
- - Custom terminology the user defines and uses repeatedly
817
-
818
- **DO NOT EXTRACT** - Facts that are:
819
- - One-time requests or isolated tasks (e.g., "generate an image or similar")
820
- - Single interactions without pattern repetition
821
- - Casual preferences from single occurrences
822
- - Testing or exploratory questions
823
- - Temporary context or transient information
824
- - General courtesy or social patterns (greetings, thank yous)
825
- - Random or playful requests that don't indicate lasting preference
826
- - Information that could change or is situational
827
-
828
- # Quality Standards
829
- - **Confidence threshold**: Only extract if confidence >= 0.8
830
- - **Persistence required**: Must see evidence across multiple messages or strong explicit statement
831
- - **Meaningful value**: Would this fact improve future interactions weeks from now?
832
- - **Not redundant**: Avoid if existing memories already cover this information
833
-
834
- # Instructions
835
- Extract ONLY truly important NEW information that meets the strict criteria above. For each item:
836
- - Determine which category it belongs to
837
- - Write a clear, factual statement
838
- - Assess confidence (0.0 to 1.0) - BE CONSERVATIVE
839
- - Require strong evidence before extraction
840
-
841
- **When in doubt, DO NOT extract.** It's better to miss temporary information than to clutter long-term memory.
842
-
843
- If there are no new long-term facts to extract, respond with <memories></memories>
844
-
845
- Respond in this XML format:
846
- <memories>
847
- <memory>
848
- <category>identity</category>
849
- <content>User is a software engineer specializing in backend development</content>
850
- <confidence>0.95</confidence>
851
- </memory>
852
- <memory>
853
- <category>preferences</category>
854
- <content>Prefers code examples over lengthy explanations</content>
855
- <confidence>0.85</confidence>
856
- </memory>
857
- </memories>`;
858
- function parseMemoryExtractionXML(xml) {
859
- const memoryMatches = xml.matchAll(/<memory>[\s\S]*?<category>(.*?)<\/category>[\s\S]*?<content>(.*?)<\/content>[\s\S]*?<confidence>(.*?)<\/confidence>[\s\S]*?<\/memory>/g);
860
- const extractions = [];
861
- for (const match of memoryMatches) {
862
- const category = match[1].trim();
863
- const content = match[2].trim();
864
- const confidence = parseFloat(match[3].trim());
865
- if (!Object.values(LongTermMemoryCategory).includes(category)) {
866
- import_core3.logger.warn(`Invalid memory category: ${category}`);
867
- continue;
868
- }
869
- if (content && !isNaN(confidence)) {
870
- extractions.push({ category, content, confidence });
871
- }
872
- }
873
- return extractions;
1548
+ // src/prompts/summarization.ts
1549
+ var SUMMARIZATION_SYSTEM_PROMPT = `You are "Chronos", a master summarizer.
1550
+ Your function is to condense conversation logs into concise, subject-first narrative summaries.
1551
+
1552
+ # CORE DIRECTIVE: "Subject-First" Summarization
1553
+ You must rephrase the narrative to focus on the *topic*, not the user. This optimizes vector retrieval.
1554
+ - BAD: "User asked about Python." (Too generic)
1555
+ - GOOD: "Python programming inquiries were addressed." (Topic-focused)
1556
+
1557
+ # COMPRESSION RULES
1558
+ 1. **Be Concise**: Target 2-4 sentences. Maximum 100 words.
1559
+ 2. **Be Factual**: No interpretation, no speculation. Only what actually happened.
1560
+ 3. **Be Narrative**: Write as a story, not a bullet list.
1561
+ 4. **Preserve Key Facts**: If the user revealed important information (preferences, identity, needs), include it.
1562
+ 5. **Exclude Trivia**: Skip greetings, acknowledgments, and filler conversation.
1563
+
1564
+ # OUTPUT FORMAT
1565
+ Phase 1: [ANALYSIS]
1566
+ - Identify key topics.
1567
+ - Draft the summary.
1568
+ - Refine wording to be subject-first.
1569
+
1570
+ Phase 2: [RESULT]
1571
+ Format: \`SUMM|TAGS|CONTENT\`
1572
+ - TAGS: Comma-separated list of key topics (lowercase)
1573
+ - CONTENT: The narrative summary text (must be a single line, no newlines)
1574
+ `;
1575
+ function buildLevel1SummaryPrompt(formattedMessages, previousSummary) {
1576
+ const previousContext = previousSummary ? `
1577
+ # PREVIOUS SUMMARY
1578
+ <previous_summary>
1579
+ ${previousSummary}
1580
+ </previous_summary>
1581
+
1582
+ **Note**: The above is what was discussed before. Ensure your new summary:
1583
+ - Does not duplicate information already captured in the previous summary
1584
+ - Focuses only on the NEW conversation below
1585
+ - Maintains continuity (e.g., "Conversation continued with...")
1586
+ ` : "";
1587
+ return `${previousContext}
1588
+ # INPUT DATA
1589
+ <messages>
1590
+ ${formattedMessages}
1591
+ </messages>
1592
+
1593
+ # FEW-SHOT EXAMPLES
1594
+
1595
+ <example_1_task_oriented>
1596
+ Input:
1597
+ [Message 1] User: I need help deploying my React app to Vercel
1598
+ [Message 2] Agent: Sure! First, make sure you have the Vercel CLI installed...
1599
+ [Message 3] User: Done. What's next?
1600
+ [Message 4] Agent: Run 'vercel' in your project directory...
1601
+ [Message 5] User: It worked! Thanks!
1602
+
1603
+ Output:
1604
+ [ANALYSIS]
1605
+ - Topic: React app deployment to Vercel.
1606
+ - Action: CLI installation and deployment.
1607
+ - Outcome: Success.
1608
+ - Draft: User requested help... -> React app deployment...
1609
+ [RESULT]
1610
+ SUMM|deployment,react,vercel,cli|React app deployment to Vercel was successfully completed following CLI installation and configuration guidance.
1611
+ </example_1_task_oriented>
1612
+
1613
+ <example_2_identity_revelation>
1614
+ Input:
1615
+ [Message 1] User: I'm working on a side project in my spare time
1616
+ [Message 2] Agent: That's great! What kind of project?
1617
+ [Message 3] User: A machine learning app for analyzing stock data. I'm a data scientist by profession.
1618
+ [Message 4] Agent: Interesting! Are you using Python?
1619
+ [Message 5] User: Yeah, mostly PyTorch and pandas.
1620
+
1621
+ Output:
1622
+ [ANALYSIS]
1623
+ - Identity: Data scientist.
1624
+ - Project: Stock data analysis (ML).
1625
+ - Tech Stack: Python, PyTorch, pandas.
1626
+ - Draft: User discussed... -> Stock data analysis project...
1627
+ [RESULT]
1628
+ SUMM|machine learning,data science,python,stocks|Stock data analysis project (Machine Learning) utilizing Python (PyTorch, pandas) is in development by a data scientist.
1629
+ </example_2_identity_revelation>
1630
+
1631
+ <example_3_chitchat>
1632
+ Input:
1633
+ [Message 1] User: Hey, how's it going?
1634
+ [Message 2] Agent: I'm doing well, thanks for asking! How can I help you today?
1635
+ [Message 3] User: Just saying hi
1636
+ [Message 4] Agent: Nice to hear from you!
1637
+
1638
+ Output:
1639
+ [ANALYSIS]
1640
+ - Content: Greetings only.
1641
+ - Substance: None.
1642
+ [RESULT]
1643
+ SUMM|greeting,casual|Casual greeting exchanged; no substantive topics discussed.
1644
+ </example_3_chitchat>
1645
+
1646
+ Begin the [ANALYSIS] phase now.`;
874
1647
  }
875
- var longTermExtractionEvaluator = {
876
- name: "LONG_TERM_MEMORY_EXTRACTION",
877
- description: "Extracts long-term facts about users from conversations",
878
- similes: ["MEMORY_EXTRACTION", "FACT_LEARNING", "USER_PROFILING"],
879
- alwaysRun: true,
1648
+ var HIGHER_LEVEL_SUMMARIZATION_SYSTEM_PROMPT = `You are "Chronos", a Meta-Summarization Agent.
1649
+ Your task is to compress multiple conversation summaries into a single, higher-level summary.
1650
+
1651
+ # MISSION
1652
+ Transform a list of conversation summaries into one concise meta-summary that captures:
1653
+ 1. **Overarching themes** across the summaries
1654
+ 2. **Key events or milestones** (e.g., "User onboarded", "Project completed")
1655
+ 3. **Evolving context** (e.g., "User's preferences shifted from X to Y")
1656
+
1657
+ # RULES
1658
+ - **Subject-First**: Focus on the topic, not the user.
1659
+ - **Abstract Higher**: Don't repeat specifics from each summary. Find the pattern.
1660
+ - **Chronological Flow**: Maintain temporal order if it matters.
1661
+ - **Preserve Critical Facts**: If summaries mention important identity or preferences, keep them.
1662
+
1663
+ # OUTPUT FORMAT
1664
+ Phase 1: [ANALYSIS]
1665
+ - Identify themes and milestones.
1666
+ - Combine related points.
1667
+ - Refine to subject-first.
1668
+
1669
+ Phase 2: [RESULT]
1670
+ Format: \`SUMM|TAGS|CONTENT\`
1671
+ - TAGS: Comma-separated list of key topics (lowercase)
1672
+ - CONTENT: The meta-summary text (must be a single line, no newlines)
1673
+ `;
1674
+ function buildHigherLevelSummaryPrompt(formattedSummaries) {
1675
+ return `# INPUT DATA
1676
+ <summaries>
1677
+ ${formattedSummaries}
1678
+ </summaries>
1679
+
1680
+ # FEW-SHOT EXAMPLES
1681
+
1682
+ <example_1_meta_summary>
1683
+ Input:
1684
+ <summary1>Stock data analysis project (Machine Learning) utilizing Python (PyTorch, pandas) is in development by a data scientist.</summary1>
1685
+ <summary2>Flask and Vercel were suggested for ML model deployment to a web app.</summary2>
1686
+ <summary3>CORS configuration issues were resolved, leading to successful deployment.</summary3>
1687
+
1688
+ Output:
1689
+ [ANALYSIS]
1690
+ - Theme: ML App Development & Deployment.
1691
+ - Flow: Development -> Stack Choice -> Deployment -> Troubleshooting -> Success.
1692
+ - Draft: User, a data scientist... -> Machine learning stock analysis app...
1693
+ [RESULT]
1694
+ SUMM|data science,machine learning,python,deployment,flask,vercel|Machine learning stock analysis app (Python/PyTorch) was developed and successfully deployed to Vercel using Flask after resolving CORS configuration issues.
1695
+ </example_1_meta_summary>
1696
+
1697
+ Begin the [ANALYSIS] phase now.`;
1698
+ }
1699
+ function estimateTokensInSummary(text3) {
1700
+ return Math.ceil(text3.length / 4);
1701
+ }
1702
+
1703
+ // src/evaluators/summarization.ts
1704
+ var summarizationCounters = new Map;
1705
+ var summarizationEvaluator = {
1706
+ name: "SUMMARIZATION",
1707
+ similes: ["HIERARCHICAL_SUMMARIZATION", "EPISODIC_COMPRESSION", "CONVERSATION_SUMMARY"],
1708
+ description: "Hierarchical conversation summarization that compresses message history into multi-level narrative summaries " + "for token-efficient long-term episodic memory.",
880
1709
  validate: async (runtime, message) => {
881
- import_core3.logger.debug(`Validating long-term memory extraction for message: ${message.content?.text}`);
882
- if (message.entityId === runtime.agentId) {
883
- import_core3.logger.debug("Skipping long-term memory extraction for agent's own message");
884
- return false;
885
- }
886
- if (!message.content?.text) {
887
- import_core3.logger.debug("Skipping long-term memory extraction for message without text");
888
- return false;
889
- }
890
1710
  const memoryService = runtime.getService("memory");
891
1711
  if (!memoryService) {
892
- import_core3.logger.debug("MemoryService not found");
893
1712
  return false;
894
1713
  }
895
1714
  const config = memoryService.getConfig();
896
- if (!config.longTermExtractionEnabled) {
897
- import_core3.logger.debug("Long-term memory extraction is disabled");
1715
+ if (!config.summarization?.enabled) {
1716
+ import_core9.logger.debug("Summarization is not enabled");
898
1717
  return false;
899
1718
  }
900
- const currentMessageCount = await runtime.countMemories(message.roomId, false, "messages");
901
- const shouldRun = await memoryService.shouldRunExtraction(message.entityId, message.roomId, currentMessageCount);
902
- import_core3.logger.debug(`Should run extraction: ${shouldRun}`);
903
- return shouldRun;
1719
+ if (!summarizationCounters.has(message.roomId)) {
1720
+ summarizationCounters.set(message.roomId, 0);
1721
+ }
1722
+ const currentCount = summarizationCounters.get(message.roomId) + 1;
1723
+ summarizationCounters.set(message.roomId, currentCount);
1724
+ const threshold = config.summarization.messagesPerSummary;
1725
+ import_core9.logger.debug({
1726
+ currentCount,
1727
+ threshold,
1728
+ messageId: message.id,
1729
+ entityId: message.entityId,
1730
+ roomId: message.roomId
1731
+ }, "Message counted for summarization (user + agent messages)");
1732
+ const shouldSummarize = currentCount >= threshold;
1733
+ if (shouldSummarize) {
1734
+ import_core9.logger.info({
1735
+ roomId: message.roomId,
1736
+ messageCount: currentCount,
1737
+ threshold
1738
+ }, "Summarization threshold reached (Level 1) - triggering summarization");
1739
+ }
1740
+ return shouldSummarize;
904
1741
  },
905
1742
  handler: async (runtime, message) => {
906
1743
  const memoryService = runtime.getService("memory");
907
1744
  if (!memoryService) {
908
- import_core3.logger.error("MemoryService not found");
1745
+ import_core9.logger.warn("MemoryService not available for summarization");
909
1746
  return;
910
1747
  }
911
1748
  const config = memoryService.getConfig();
912
- const { entityId, roomId } = message;
1749
+ if (!config.summarization?.enabled) {
1750
+ return;
1751
+ }
1752
+ const messageCount = summarizationCounters.get(message.roomId) || 0;
1753
+ if (messageCount === 0) {
1754
+ return;
1755
+ }
1756
+ import_core9.logger.info({
1757
+ roomId: message.roomId,
1758
+ messageCount
1759
+ }, "Starting Level 1 summarization - pulling messages from database");
913
1760
  try {
914
- import_core3.logger.info(`Extracting long-term memories for entity ${entityId}`);
915
- const recentMessages = await runtime.getMemories({
916
- tableName: "messages",
917
- roomId,
918
- count: 20,
919
- unique: false
920
- });
921
- const formattedMessages = recentMessages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0)).map((msg) => {
922
- const sender = msg.entityId === runtime.agentId ? runtime.character.name : "User";
923
- return `${sender}: ${msg.content.text || "[non-text message]"}`;
924
- }).join(`
925
- `);
926
- const existingMemories = await memoryService.getLongTermMemories(entityId, undefined, 30);
927
- const formattedExisting = existingMemories.length > 0 ? existingMemories.map((m) => `[${m.category}] ${m.content} (confidence: ${m.confidence})`).join(`
928
- `) : "None yet";
929
- const state = await runtime.composeState(message);
930
- const prompt = import_core3.composePromptFromState({
931
- state: {
932
- ...state,
933
- recentMessages: formattedMessages,
934
- existingMemories: formattedExisting
935
- },
936
- template: extractionTemplate
937
- });
938
- const response = await runtime.useModel(import_core3.ModelType.TEXT_LARGE, {
939
- prompt
940
- });
941
- const extractions = parseMemoryExtractionXML(response);
942
- import_core3.logger.info(`Extracted ${extractions.length} long-term memories`);
943
- for (const extraction of extractions) {
944
- if (extraction.confidence >= config.longTermConfidenceThreshold) {
945
- await memoryService.storeLongTermMemory({
946
- agentId: runtime.agentId,
947
- entityId,
948
- category: extraction.category,
949
- content: extraction.content,
950
- confidence: extraction.confidence,
951
- source: "conversation",
952
- metadata: {
953
- roomId,
954
- extractedAt: new Date().toISOString()
955
- }
956
- });
957
- import_core3.logger.info(`Stored long-term memory: [${extraction.category}] ${extraction.content.substring(0, 50)}...`);
958
- } else {
959
- import_core3.logger.debug(`Skipped low-confidence memory: ${extraction.content} (confidence: ${extraction.confidence})`);
960
- }
1761
+ const previousSummary = await memoryService.getMostRecentLevel1Summary(message.roomId, message.entityId);
1762
+ const messages = await fetchMessagesSinceLastSummary(runtime, message.roomId, previousSummary?.endTime);
1763
+ if (messages.length === 0) {
1764
+ import_core9.logger.warn({ roomId: message.roomId }, "No messages found for summarization");
1765
+ return;
1766
+ }
1767
+ const summary = await createLevel1Summary(runtime, memoryService, messages, message.roomId, previousSummary);
1768
+ if (summary) {
1769
+ import_core9.logger.info({
1770
+ summaryId: summary.id,
1771
+ tokenCount: summary.tokenCount,
1772
+ messageCount: messages.length,
1773
+ startTime: summary.startTime,
1774
+ endTime: summary.endTime
1775
+ }, "Created Level 1 summary from database messages");
1776
+ summarizationCounters.set(message.roomId, 0);
1777
+ await checkAndTriggerHigherLevelSummarization(runtime, memoryService, summary.roomId, summary.level);
961
1778
  }
962
- const currentMessageCount = await runtime.countMemories(roomId, false, "messages");
963
- await memoryService.setLastExtractionCheckpoint(entityId, roomId, currentMessageCount);
964
- import_core3.logger.debug(`Updated extraction checkpoint to ${currentMessageCount} for entity ${entityId} in room ${roomId}`);
965
1779
  } catch (error) {
966
- import_core3.logger.error({ error }, "Error during long-term memory extraction:");
1780
+ import_core9.logger.error({
1781
+ error,
1782
+ roomId: message.roomId
1783
+ }, "Failed to create summary");
967
1784
  }
968
1785
  },
969
1786
  examples: []
970
1787
  };
1788
+ async function fetchMessagesSinceLastSummary(runtime, roomId, lastSummaryEndTime) {
1789
+ const allMessages = await runtime.getMemories({
1790
+ tableName: "messages",
1791
+ roomId,
1792
+ count: 1000,
1793
+ unique: false
1794
+ });
1795
+ const dialogueMessages = allMessages.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result"));
1796
+ let messages = dialogueMessages;
1797
+ if (lastSummaryEndTime) {
1798
+ messages = dialogueMessages.filter((msg) => {
1799
+ if (!msg.createdAt)
1800
+ return false;
1801
+ const msgTime = new Date(msg.createdAt);
1802
+ return msgTime > lastSummaryEndTime;
1803
+ });
1804
+ }
1805
+ messages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
1806
+ import_core9.logger.debug({
1807
+ roomId,
1808
+ totalMessages: allMessages.length,
1809
+ dialogueMessages: dialogueMessages.length,
1810
+ filteredMessages: messages.length,
1811
+ lastSummaryEndTime: lastSummaryEndTime?.toISOString()
1812
+ }, "Fetched messages for summarization");
1813
+ return messages;
1814
+ }
1815
+ function parseSummarizationResult(response) {
1816
+ try {
1817
+ const responseText = typeof response === "string" ? response : JSON.stringify(response);
1818
+ const analysisMatch = responseText.match(/\[ANALYSIS\](.*?)(?:\[RESULT\]|$)/s);
1819
+ const reasoningTrace = analysisMatch ? analysisMatch[1].trim() : "";
1820
+ const resultMatch = responseText.match(/\[RESULT\](.*?)$/s);
1821
+ const resultText = resultMatch ? resultMatch[1].trim() : "";
1822
+ let summary = "";
1823
+ let keyTopics = "";
1824
+ if (resultText) {
1825
+ const lines = resultText.split(`
1826
+ `);
1827
+ for (const line of lines) {
1828
+ const trimmed = line.trim();
1829
+ if (trimmed.startsWith("SUMM|")) {
1830
+ const parts = trimmed.split("|");
1831
+ if (parts.length >= 3) {
1832
+ keyTopics = parts[1].trim();
1833
+ summary = parts.slice(2).join("|").trim();
1834
+ break;
1835
+ }
1836
+ }
1837
+ }
1838
+ }
1839
+ return { summary, keyTopics, reasoningTrace };
1840
+ } catch (error) {
1841
+ import_core9.logger.error({ error }, "Failed to parse summarization response");
1842
+ return { summary: "", keyTopics: "", reasoningTrace: "" };
1843
+ }
1844
+ }
1845
+ async function createLevel1Summary(runtime, memoryService, messages, roomId, previousSummary) {
1846
+ const formattedMessages = messages.map((m, i) => {
1847
+ const text3 = typeof m.content === "string" ? m.content : m.content.text || "";
1848
+ const author = m.entityId === runtime.agentId ? "Agent" : "User";
1849
+ const timestamp3 = m.createdAt ? new Date(m.createdAt).toISOString() : "Unknown time";
1850
+ return `[${timestamp3}] ${author}: ${text3}`;
1851
+ }).join(`
1852
+ `);
1853
+ let prompt;
1854
+ if (previousSummary) {
1855
+ prompt = buildLevel1SummaryPrompt(formattedMessages, previousSummary.content);
1856
+ } else {
1857
+ prompt = buildLevel1SummaryPrompt(formattedMessages);
1858
+ }
1859
+ const originalSystemPrompt = runtime.character.system;
1860
+ try {
1861
+ runtime.character.system = SUMMARIZATION_SYSTEM_PROMPT;
1862
+ import_core9.logger.debug("Calling LLM for Level 1 summarization with timestamped conversation log");
1863
+ const response = await runtime.useModel(import_core9.ModelType.TEXT_LARGE, {
1864
+ prompt,
1865
+ temperature: 0.3
1866
+ });
1867
+ runtime.character.system = originalSystemPrompt;
1868
+ const { summary, keyTopics, reasoningTrace } = parseSummarizationResult(response);
1869
+ if (!summary) {
1870
+ import_core9.logger.warn({ reasoningTrace }, "No summary extracted from LLM response");
1871
+ return null;
1872
+ }
1873
+ const tokenCount = estimateTokensInSummary(summary);
1874
+ const startTime = messages[0].createdAt ? new Date(messages[0].createdAt) : new Date;
1875
+ const endTime = messages[messages.length - 1].createdAt ? new Date(messages[messages.length - 1].createdAt) : new Date;
1876
+ const summaryData = {
1877
+ agentId: runtime.agentId,
1878
+ entityId: messages[0].entityId,
1879
+ roomId,
1880
+ level: 1,
1881
+ parentSummaryId: undefined,
1882
+ content: summary,
1883
+ tokenCount,
1884
+ startTime,
1885
+ endTime,
1886
+ sourceCount: messages.length,
1887
+ sourceIds: messages.map((m) => m.id),
1888
+ metadata: {
1889
+ keyTopics,
1890
+ hasPreviousSummary: !!previousSummary
1891
+ }
1892
+ };
1893
+ return await memoryService.storeSummary(summaryData);
1894
+ } catch (error) {
1895
+ import_core9.logger.error({ error }, "Failed to call LLM for Level 1 summarization");
1896
+ runtime.character.system = originalSystemPrompt;
1897
+ return null;
1898
+ }
1899
+ }
1900
+ async function checkAndTriggerHigherLevelSummarization(runtime, memoryService, roomId, currentLevel) {
1901
+ const config = memoryService.getConfig();
1902
+ if (!config.summarization) {
1903
+ return;
1904
+ }
1905
+ if (currentLevel >= config.summarization.maxDepth) {
1906
+ import_core9.logger.debug({ currentLevel, maxDepth: config.summarization.maxDepth }, "Max depth reached");
1907
+ return;
1908
+ }
1909
+ const summaries = await memoryService.getSummariesByLevel(roomId, currentLevel);
1910
+ const threshold = config.summarization.summariesPerLevel;
1911
+ if (summaries.length < threshold) {
1912
+ return;
1913
+ }
1914
+ import_core9.logger.info({
1915
+ level: currentLevel,
1916
+ count: summaries.length,
1917
+ nextLevel: currentLevel + 1
1918
+ }, `Triggering Level ${currentLevel + 1} summarization`);
1919
+ const higherSummary = await createHigherLevelSummary(runtime, memoryService, summaries, roomId, currentLevel + 1);
1920
+ if (higherSummary) {
1921
+ await checkAndTriggerHigherLevelSummarization(runtime, memoryService, roomId, currentLevel + 1);
1922
+ }
1923
+ }
1924
+ async function createHigherLevelSummary(runtime, memoryService, lowerSummaries, roomId, level) {
1925
+ const formattedSummaries = lowerSummaries.map((s, i) => `<summary${i + 1}>${s.content}</summary${i + 1}>`).join(`
1926
+ `);
1927
+ const prompt = buildHigherLevelSummaryPrompt(formattedSummaries);
1928
+ const originalSystemPrompt = runtime.character.system;
1929
+ try {
1930
+ runtime.character.system = HIGHER_LEVEL_SUMMARIZATION_SYSTEM_PROMPT;
1931
+ const response = await runtime.useModel(import_core9.ModelType.TEXT_LARGE, {
1932
+ prompt,
1933
+ temperature: 0.3
1934
+ });
1935
+ runtime.character.system = originalSystemPrompt;
1936
+ const { summary, keyTopics, reasoningTrace } = parseSummarizationResult(response);
1937
+ if (!summary) {
1938
+ import_core9.logger.warn({ reasoningTrace }, "No higher-level summary extracted from LLM response");
1939
+ return null;
1940
+ }
1941
+ const tokenCount = estimateTokensInSummary(summary);
1942
+ const startTime = new Date(Math.min(...lowerSummaries.map((s) => s.startTime.getTime())));
1943
+ const endTime = new Date(Math.max(...lowerSummaries.map((s) => s.endTime.getTime())));
1944
+ const summaryData = {
1945
+ agentId: runtime.agentId,
1946
+ entityId: lowerSummaries[0].entityId,
1947
+ roomId,
1948
+ level,
1949
+ parentSummaryId: undefined,
1950
+ content: summary,
1951
+ tokenCount,
1952
+ startTime,
1953
+ endTime,
1954
+ sourceCount: lowerSummaries.length,
1955
+ sourceIds: lowerSummaries.map((s) => s.id),
1956
+ metadata: {
1957
+ keyTopics,
1958
+ compressedSummaries: lowerSummaries.length
1959
+ }
1960
+ };
1961
+ return await memoryService.storeSummary(summaryData);
1962
+ } catch (error) {
1963
+ import_core9.logger.error({ error, level }, "Failed to create higher-level summary");
1964
+ runtime.character.system = originalSystemPrompt;
1965
+ return null;
1966
+ }
1967
+ }
971
1968
 
972
- // src/providers/short-term-memory.ts
973
- var import_core4 = require("@elizaos/core");
974
- var shortTermMemoryProvider = {
975
- name: "SHORT_TERM_MEMORY",
976
- description: "Adaptive conversation context with smart summarization",
977
- position: 95,
978
- get: async (runtime, message, _state) => {
1969
+ // src/providers/long-term-memory.ts
1970
+ var import_core10 = require("@elizaos/core");
1971
+ var longTermMemoryProvider = {
1972
+ name: "LONG_TERM_MEMORY",
1973
+ description: "User knowledge and facts (semantic + procedural memory)",
1974
+ position: 80,
1975
+ get: async (runtime, message, state) => {
979
1976
  try {
980
1977
  const memoryService = runtime.getService("memory");
981
1978
  if (!memoryService) {
1979
+ import_core10.logger.warn("Memory service not available");
982
1980
  return {
983
- data: { summaries: [], recentMessages: [], mode: "disabled" },
1981
+ data: {},
984
1982
  values: {},
985
1983
  text: ""
986
1984
  };
987
1985
  }
988
- const { roomId } = message;
1986
+ const entityId = message.entityId;
1987
+ const roomId = message.roomId;
1988
+ const query = typeof message.content === "string" ? message.content : message.content.text || JSON.stringify(message.content);
1989
+ import_core10.logger.debug({
1990
+ entityId,
1991
+ roomId,
1992
+ queryLength: query.length
1993
+ }, "Retrieving unified memories");
989
1994
  const config = memoryService.getConfig();
990
- const totalMessageCount = await runtime.countMemories(roomId, false, "messages");
991
- if (totalMessageCount < config.shortTermSummarizationThreshold) {
992
- const conversationLength = runtime.getConversationLength();
993
- const [entitiesData, room, recentMessagesData] = await Promise.all([
994
- import_core4.getEntityDetails({ runtime, roomId }),
995
- runtime.getRoom(roomId),
996
- runtime.getMemories({
997
- tableName: "messages",
998
- roomId,
999
- count: conversationLength,
1000
- unique: false
1001
- })
1002
- ]);
1003
- const actionResultMessages = recentMessagesData.filter((msg) => msg.content?.type === "action_result" && msg.metadata?.type === "action_result");
1004
- const dialogueMessages = recentMessagesData.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result"));
1005
- const isPostFormat = room?.type ? room.type === import_core4.ChannelType.FEED || room.type === import_core4.ChannelType.THREAD : false;
1006
- const [formattedRecentMessages, formattedRecentPosts] = await Promise.all([
1007
- import_core4.formatMessages({
1008
- messages: dialogueMessages,
1009
- entities: entitiesData
1010
- }),
1011
- import_core4.formatPosts({
1012
- messages: dialogueMessages,
1013
- entities: entitiesData,
1014
- conversationHeader: false
1015
- })
1016
- ]);
1017
- let actionResultsText = "";
1018
- if (actionResultMessages.length > 0) {
1019
- const groupedByRun = new Map;
1020
- for (const mem of actionResultMessages) {
1021
- const runId = String(mem.content?.runId || "unknown");
1022
- if (!groupedByRun.has(runId)) {
1023
- groupedByRun.set(runId, []);
1024
- }
1025
- groupedByRun.get(runId)?.push(mem);
1026
- }
1027
- const formattedActionResults = Array.from(groupedByRun.entries()).slice(-3).map(([runId, memories]) => {
1028
- const sortedMemories = memories.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
1029
- const thought = sortedMemories[0]?.content?.planThought || "";
1030
- const runText = sortedMemories.map((mem) => {
1031
- const actionName = mem.content?.actionName || "Unknown";
1032
- const status = mem.content?.actionStatus || "unknown";
1033
- const planStep = mem.content?.planStep || "";
1034
- const text5 = mem.content?.text || "";
1035
- const error = mem.content?.error || "";
1036
- let memText = ` - ${actionName} (${status})`;
1037
- if (planStep)
1038
- memText += ` [${planStep}]`;
1039
- if (error) {
1040
- memText += `: Error - ${error}`;
1041
- } else if (text5 && text5 !== `Executed action: ${actionName}`) {
1042
- memText += `: ${text5}`;
1043
- }
1044
- return memText;
1045
- }).join(`
1995
+ const [semanticMemories, proceduralMemories, episodicMemories] = await Promise.all([
1996
+ memoryService.searchLongTermMemories({
1997
+ entityId,
1998
+ query,
1999
+ roomId,
2000
+ type: "SEMANTIC" /* SEMANTIC */,
2001
+ limit: config.retrievalLimit,
2002
+ minConfidence: config.minConfidence,
2003
+ similarityThreshold: 0.15
2004
+ }),
2005
+ memoryService.searchLongTermMemories({
2006
+ entityId,
2007
+ query,
2008
+ roomId,
2009
+ type: "PROCEDURAL" /* PROCEDURAL */,
2010
+ limit: Math.floor(config.retrievalLimit / 2),
2011
+ minConfidence: config.minConfidence,
2012
+ similarityThreshold: 0.15
2013
+ }),
2014
+ memoryService.searchLongTermMemories({
2015
+ entityId,
2016
+ query,
2017
+ roomId,
2018
+ type: "EPISODIC" /* EPISODIC */,
2019
+ limit: config.retrievalLimit,
2020
+ minConfidence: config.minConfidence,
2021
+ similarityThreshold: 0.15
2022
+ })
2023
+ ]);
2024
+ const longTermSections = [];
2025
+ if (semanticMemories.length > 0) {
2026
+ const items = semanticMemories.map((m) => `- ${m.content} (confidence: ${m.confidence.toFixed(2)}, strength: ${m.activationScore.toFixed(2)})`).join(`
1046
2027
  `);
1047
- return `**Action Run ${runId.slice(0, 8)}**${thought ? ` - "${thought}"` : ""}
1048
- ${runText}`;
1049
- }).join(`
1050
-
2028
+ longTermSections.push(import_core10.addHeader("## Semantic Knowledge (Facts)", items));
2029
+ }
2030
+ if (proceduralMemories.length > 0) {
2031
+ const items = proceduralMemories.map((m) => `- ${m.content}`).join(`
1051
2032
  `);
1052
- actionResultsText = formattedActionResults ? import_core4.addHeader("# Recent Action Executions", formattedActionResults) : "";
1053
- }
1054
- const recentPosts = formattedRecentPosts && formattedRecentPosts.length > 0 ? import_core4.addHeader("# Posts in Thread", formattedRecentPosts) : "";
1055
- const recentMessages = formattedRecentMessages && formattedRecentMessages.length > 0 ? import_core4.addHeader("# Conversation Messages", formattedRecentMessages) : "";
1056
- if (!recentPosts && !recentMessages && dialogueMessages.length === 0 && !message.content.text) {
1057
- return {
1058
- data: {
1059
- summaries: [],
1060
- recentMessages: [],
1061
- actionResults: [],
1062
- mode: "full_conversation"
1063
- },
1064
- values: {
1065
- recentMessage: "No recent message available."
1066
- },
1067
- text: "No recent messages available"
1068
- };
1069
- }
1070
- let recentMessage = "No recent message available.";
1071
- if (dialogueMessages.length > 0) {
1072
- const mostRecentMessage = [...dialogueMessages].sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0))[0];
1073
- const formattedSingleMessage = import_core4.formatMessages({
1074
- messages: [mostRecentMessage],
1075
- entities: entitiesData
1076
- });
1077
- if (formattedSingleMessage) {
1078
- recentMessage = formattedSingleMessage;
1079
- }
1080
- }
1081
- const metaData = message.metadata;
1082
- const senderName = entitiesData.find((entity) => entity.id === message.entityId)?.names[0] || metaData?.entityName || "Unknown User";
1083
- const receivedMessageContent = message.content.text;
1084
- const hasReceivedMessage = !!receivedMessageContent?.trim();
1085
- const receivedMessageHeader = hasReceivedMessage ? import_core4.addHeader("# Received Message", `${senderName}: ${receivedMessageContent}`) : "";
1086
- const focusHeader = hasReceivedMessage ? import_core4.addHeader("# Focus your response", `You are replying to the above message from **${senderName}**. Keep your answer relevant to that message. Do not repeat earlier replies unless the sender asks again.`) : "";
1087
- const text4 = [
1088
- isPostFormat ? recentPosts : recentMessages,
1089
- actionResultsText,
1090
- recentMessages || recentPosts || message.content.text ? receivedMessageHeader : "",
1091
- recentMessages || recentPosts || message.content.text ? focusHeader : ""
1092
- ].filter(Boolean).join(`
2033
+ longTermSections.push(import_core10.addHeader("## Procedural Knowledge (Skills & Patterns)", items));
2034
+ }
2035
+ if (episodicMemories.length > 0) {
2036
+ const items = episodicMemories.map((m) => `- ${m.content} (occurred: ${m.createdAt ? new Date(m.createdAt).toLocaleDateString() : "Unknown"})`).join(`
2037
+ `);
2038
+ longTermSections.push(import_core10.addHeader("## Significant Past Events", items));
2039
+ }
2040
+ const longTermMemoriesText = longTermSections.join(`
1093
2041
 
1094
2042
  `);
1095
- return {
1096
- data: {
1097
- summaries: [],
1098
- recentMessages: dialogueMessages,
1099
- actionResults: actionResultMessages,
1100
- mode: "full_conversation"
1101
- },
1102
- values: {
1103
- ...(isPostFormat ? recentPosts : recentMessages) && {
1104
- recentMessages: isPostFormat ? recentPosts : recentMessages
1105
- },
1106
- ...recentPosts && { recentPosts },
1107
- ...actionResultsText && { recentActionResults: actionResultsText },
1108
- ...recentMessage && { recentMessage },
1109
- ...receivedMessageHeader && { receivedMessageHeader },
1110
- ...focusHeader && { focusHeader }
1111
- },
1112
- text: text4
1113
- };
1114
- } else {
1115
- const currentSummary = await memoryService.getCurrentSessionSummary(roomId);
1116
- const lastOffset = currentSummary?.lastMessageOffset || 0;
1117
- const unsummarizedMessages = await runtime.getMemories({
1118
- tableName: "messages",
1119
- roomId,
1120
- count: config.shortTermRetainRecent,
1121
- unique: false,
1122
- start: lastOffset
1123
- });
1124
- const entitiesData = await import_core4.getEntityDetails({ runtime, roomId });
1125
- const room = await runtime.getRoom(roomId);
1126
- const isPostFormat = room?.type ? room.type === import_core4.ChannelType.FEED || room.type === import_core4.ChannelType.THREAD : false;
1127
- let recentMessagesText = "";
1128
- if (unsummarizedMessages.length > 0) {
1129
- const dialogueMessages = unsummarizedMessages.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result"));
1130
- if (isPostFormat) {
1131
- recentMessagesText = import_core4.formatPosts({
1132
- messages: dialogueMessages,
1133
- entities: entitiesData,
1134
- conversationHeader: false
1135
- });
1136
- } else {
1137
- recentMessagesText = import_core4.formatMessages({
1138
- messages: dialogueMessages,
1139
- entities: entitiesData
1140
- });
1141
- }
1142
- if (recentMessagesText) {
1143
- recentMessagesText = import_core4.addHeader("# Recent Messages", recentMessagesText);
1144
- }
2043
+ const data = {
2044
+ semanticMemories,
2045
+ proceduralMemories,
2046
+ episodicMemories,
2047
+ config: {
2048
+ retrievalLimit: config.retrievalLimit,
2049
+ tokenBudget: config.tokenBudget
1145
2050
  }
1146
- let summaryText = "";
1147
- if (currentSummary) {
1148
- const messageRange = `${currentSummary.messageCount} messages`;
1149
- const timeRange = new Date(currentSummary.startTime).toLocaleDateString();
1150
- summaryText = `**Previous Conversation** (${messageRange}, ${timeRange})
1151
- `;
1152
- summaryText += currentSummary.summary;
1153
- if (currentSummary.topics && currentSummary.topics.length > 0) {
1154
- summaryText += `
1155
- *Topics: ${currentSummary.topics.join(", ")}*`;
1156
- }
1157
- summaryText = import_core4.addHeader("# Conversation Summary", summaryText);
1158
- }
1159
- const metaData = message.metadata;
1160
- const senderName = entitiesData.find((entity) => entity.id === message.entityId)?.names[0] || metaData?.entityName || "Unknown User";
1161
- const receivedMessageContent = message.content.text;
1162
- const hasReceivedMessage = !!receivedMessageContent?.trim();
1163
- const receivedMessageHeader = hasReceivedMessage ? import_core4.addHeader("# Received Message", `${senderName}: ${receivedMessageContent}`) : "";
1164
- const focusHeader = hasReceivedMessage ? import_core4.addHeader("# Focus your response", `You are replying to the above message from **${senderName}**. Keep your answer relevant to that message.`) : "";
1165
- const text4 = [
1166
- summaryText,
1167
- recentMessagesText,
1168
- hasReceivedMessage ? receivedMessageHeader : "",
1169
- hasReceivedMessage ? focusHeader : ""
1170
- ].filter(Boolean).join(`
2051
+ };
2052
+ const values = {
2053
+ longTermMemories: longTermMemoriesText
2054
+ };
2055
+ const text3 = longTermMemoriesText;
2056
+ import_core10.logger.info({
2057
+ semanticCount: semanticMemories.length,
2058
+ proceduralCount: proceduralMemories.length,
2059
+ episodicCount: episodicMemories.length
2060
+ }, "Retrieved long-term memory facts");
2061
+ return {
2062
+ data,
2063
+ values,
2064
+ text: text3
2065
+ };
2066
+ } catch (error) {
2067
+ import_core10.logger.error({ error }, "Failed to retrieve long-term memories");
2068
+ return {
2069
+ data: {},
2070
+ values: {
2071
+ longTermMemories: ""
2072
+ },
2073
+ text: ""
2074
+ };
2075
+ }
2076
+ }
2077
+ };
2078
+
2079
+ // src/providers/recent-conversation-summary.ts
2080
+ var import_core11 = require("@elizaos/core");
2081
+ var getProviderConfig = (runtime) => ({
2082
+ overlapUserMessageCount: parseInt(runtime.getSetting("CONTEXT_OVERLAP_USER_MESSAGES") || "2", 10)
2083
+ });
2084
+ async function fetchConversationData(runtime, roomId) {
2085
+ const [entities, room, allMessages] = await Promise.all([
2086
+ import_core11.getEntityDetails({ runtime, roomId }),
2087
+ runtime.getRoom(roomId),
2088
+ runtime.getMemories({
2089
+ tableName: "messages",
2090
+ roomId,
2091
+ count: 100,
2092
+ unique: false
2093
+ })
2094
+ ]);
2095
+ return { entities, room, allMessages };
2096
+ }
2097
+ function filterAndSortDialogueMessages(allMessages) {
2098
+ const dialogueMessages = allMessages.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result"));
2099
+ dialogueMessages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
2100
+ return dialogueMessages;
2101
+ }
2102
+ async function buildCompressedHistory(runtime, message, dialogueMessages, sessionStartTime) {
2103
+ let compressedHistoryText = "";
2104
+ let lastSummarizedIndex = -1;
2105
+ const memoryService = runtime.getService("memory");
2106
+ if (!memoryService?.searchSummaries) {
2107
+ return { compressedHistoryText, lastSummarizedIndex };
2108
+ }
2109
+ try {
2110
+ const summaries = await memoryService.searchSummaries({
2111
+ entityId: message.entityId,
2112
+ roomId: message.roomId,
2113
+ query: typeof message.content === "string" ? message.content : message.content.text || "",
2114
+ limit: 3,
2115
+ tokenBudget: 500
2116
+ });
2117
+ if (summaries.length === 0) {
2118
+ return { compressedHistoryText, lastSummarizedIndex };
2119
+ }
2120
+ lastSummarizedIndex = findLastSummarizedMessageIndex(summaries, dialogueMessages);
2121
+ const summaryItems = summaries.map((s) => {
2122
+ const levelLabel = s.level === 1 ? "Recent Session" : `Overview (L${s.level})`;
2123
+ return `**[${levelLabel}]** ${s.content}`;
2124
+ }).join(`
1171
2125
 
1172
2126
  `);
1173
- return {
1174
- data: {
1175
- summaries: currentSummary ? [currentSummary] : [],
1176
- recentMessages: unsummarizedMessages,
1177
- mode: "summarized"
1178
- },
1179
- values: {
1180
- ...summaryText && { sessionSummaries: summaryText },
1181
- ...recentMessagesText && { recentMessages: recentMessagesText },
1182
- ...receivedMessageHeader && { receivedMessageHeader },
1183
- ...focusHeader && { focusHeader }
1184
- },
1185
- text: text4
1186
- };
2127
+ const headerText = `# Conversation History (Compressed)
2128
+ **Session Started:** ${formatDateTime(sessionStartTime)}`;
2129
+ compressedHistoryText = import_core11.addHeader(headerText, summaryItems);
2130
+ import_core11.logger.debug({
2131
+ summaryCount: summaries.length,
2132
+ totalTokens: summaries.reduce((sum, s) => sum + s.tokenCount, 0),
2133
+ lastSummarizedIndex
2134
+ }, "Using hierarchical summaries for compressed history");
2135
+ } catch (error) {
2136
+ import_core11.logger.warn({ error }, "Failed to retrieve summaries");
2137
+ }
2138
+ return { compressedHistoryText, lastSummarizedIndex };
2139
+ }
2140
+ function findLastSummarizedMessageIndex(summaries, dialogueMessages) {
2141
+ const level1Summaries = summaries.filter((s) => s.level === 1);
2142
+ if (level1Summaries.length === 0) {
2143
+ return -1;
2144
+ }
2145
+ const allSummarizedMessageIds = new Set;
2146
+ level1Summaries.forEach((summary) => {
2147
+ summary.sourceIds.forEach((id) => allSummarizedMessageIds.add(id));
2148
+ });
2149
+ for (let i = dialogueMessages.length - 1;i >= 0; i--) {
2150
+ if (allSummarizedMessageIds.has(dialogueMessages[i].id)) {
2151
+ import_core11.logger.debug({
2152
+ lastSummarizedIndex: i,
2153
+ totalMessages: dialogueMessages.length,
2154
+ summarizedCount: allSummarizedMessageIds.size
2155
+ }, "Determined last summarized message index");
2156
+ return i;
2157
+ }
2158
+ }
2159
+ return -1;
2160
+ }
2161
+ function calculateBufferMessages(runtime, dialogueMessages, lastSummarizedIndex, hasSummaries, overlapUserMessageCount) {
2162
+ let bufferMessages;
2163
+ if (lastSummarizedIndex >= 0 && hasSummaries) {
2164
+ const summarizedMessages = dialogueMessages.slice(0, lastSummarizedIndex + 1);
2165
+ const summarizedUserMessages = summarizedMessages.filter((m) => m.entityId !== runtime.agentId);
2166
+ const overlapUserMessages = summarizedUserMessages.slice(-overlapUserMessageCount);
2167
+ const newUnsummarizedMessages = dialogueMessages.slice(lastSummarizedIndex + 1);
2168
+ let overlapStartIndex = lastSummarizedIndex + 1;
2169
+ if (overlapUserMessages.length > 0) {
2170
+ const firstOverlapUserMessageId = overlapUserMessages[0].id;
2171
+ const foundIndex = dialogueMessages.findIndex((m) => m.id === firstOverlapUserMessageId);
2172
+ if (foundIndex >= 0) {
2173
+ overlapStartIndex = foundIndex;
1187
2174
  }
2175
+ }
2176
+ bufferMessages = dialogueMessages.slice(overlapStartIndex);
2177
+ import_core11.logger.debug({
2178
+ lastSummarizedIndex,
2179
+ summarizedMessageCount: summarizedMessages.length,
2180
+ overlapStartIndex,
2181
+ overlapSize: lastSummarizedIndex + 1 - overlapStartIndex,
2182
+ newUnsummarizedCount: newUnsummarizedMessages.length,
2183
+ bufferSize: bufferMessages.length,
2184
+ totalDialogueMessages: dialogueMessages.length,
2185
+ overlapUserMessageCount
2186
+ }, `Dynamic buffer: [overlap: last ${overlapUserMessageCount} user msgs from summary] + [all new unsummarized messages]`);
2187
+ } else {
2188
+ bufferMessages = dialogueMessages;
2189
+ import_core11.logger.debug({
2190
+ bufferSize: bufferMessages.length,
2191
+ totalMessages: dialogueMessages.length
2192
+ }, "Using full conversation: no summaries yet");
2193
+ }
2194
+ return { bufferMessages, lastSummarizedIndex };
2195
+ }
2196
+ function formatDateTime(timestamp3) {
2197
+ const date = new Date(timestamp3);
2198
+ return date.toLocaleString("en-US", {
2199
+ year: "numeric",
2200
+ month: "short",
2201
+ day: "numeric",
2202
+ hour: "2-digit",
2203
+ minute: "2-digit",
2204
+ hour12: false
2205
+ });
2206
+ }
2207
+ function removeEntityIds(text3) {
2208
+ return text3.replace(/\s*\[[\w\-]+\]/g, "").replace(/\s*\([^)]*'s internal thought:[^)]*\)/gi, "").split(`
2209
+ `).map((line) => line.trim()).join(`
2210
+ `).replace(/\n{3,}/g, `
2211
+
2212
+ `);
2213
+ }
2214
+ async function formatBufferMessages(bufferMessages, entities, room, sessionStartTime) {
2215
+ const isPostFormat = room?.type ? room.type === import_core11.ChannelType.FEED || room.type === import_core11.ChannelType.THREAD : false;
2216
+ const [formattedRecentMessages, formattedRecentPosts] = await Promise.all([
2217
+ import_core11.formatMessages({
2218
+ messages: bufferMessages,
2219
+ entities
2220
+ }),
2221
+ import_core11.formatPosts({
2222
+ messages: bufferMessages,
2223
+ entities,
2224
+ conversationHeader: false
2225
+ })
2226
+ ]);
2227
+ let recentBufferText = (isPostFormat ? formattedRecentPosts : formattedRecentMessages) || "";
2228
+ recentBufferText = removeEntityIds(recentBufferText);
2229
+ const firstBufferTime = bufferMessages.length > 0 ? bufferMessages[0].createdAt : null;
2230
+ const lastBufferTime = bufferMessages.length > 0 ? bufferMessages[bufferMessages.length - 1].createdAt : null;
2231
+ let headerText = `# Recent Messages (Last ${bufferMessages.length})`;
2232
+ if (firstBufferTime && lastBufferTime) {
2233
+ headerText += `
2234
+ **Time Range:** ${formatDateTime(firstBufferTime)} - ${formatDateTime(lastBufferTime)}`;
2235
+ }
2236
+ return recentBufferText ? import_core11.addHeader(headerText, recentBufferText) : "";
2237
+ }
2238
+ function buildReceivedMessageSection(message, entities) {
2239
+ const metaData = message.metadata;
2240
+ const senderName = entities.find((entity) => entity.id === message.entityId)?.names[0] || metaData?.entityName || "Unknown User";
2241
+ const receivedMessageContent = message.content.text;
2242
+ const hasReceivedMessage = !!receivedMessageContent?.trim();
2243
+ const receivedMessageHeader = hasReceivedMessage ? import_core11.addHeader("# Current Message", `**From ${senderName}:** ${receivedMessageContent}`) : "";
2244
+ const focusHeader = hasReceivedMessage ? import_core11.addHeader("# Response Focus", `Reply to **${senderName}**'s current message above. Stay relevant to their question. Don't repeat previous responses unless asked again.`) : "";
2245
+ return { receivedMessageHeader, focusHeader };
2246
+ }
2247
+ function assembleContextSections(compressedHistoryText, recentBufferHeader, receivedMessageHeader, focusHeader) {
2248
+ const textSections = [
2249
+ compressedHistoryText,
2250
+ recentBufferHeader,
2251
+ receivedMessageHeader,
2252
+ focusHeader
2253
+ ].filter(Boolean);
2254
+ return textSections.join(`
2255
+
2256
+ `);
2257
+ }
2258
+ var recentContextProvider = {
2259
+ name: "RECENT_CONVERSATION_SUMMARY",
2260
+ description: "Intelligent context management combining recent messages with hierarchical summaries for optimal token efficiency",
2261
+ position: 100,
2262
+ get: async (runtime, message) => {
2263
+ try {
2264
+ const { roomId } = message;
2265
+ const config = getProviderConfig(runtime);
2266
+ const { entities, room, allMessages } = await fetchConversationData(runtime, roomId);
2267
+ const dialogueMessages = filterAndSortDialogueMessages(allMessages);
2268
+ const sessionStartTime = dialogueMessages.length > 0 ? dialogueMessages[0].createdAt || Date.now() : Date.now();
2269
+ const { compressedHistoryText, lastSummarizedIndex } = await buildCompressedHistory(runtime, message, dialogueMessages, sessionStartTime);
2270
+ const hasSummaries = !!compressedHistoryText;
2271
+ const { bufferMessages } = calculateBufferMessages(runtime, dialogueMessages, lastSummarizedIndex, hasSummaries, config.overlapUserMessageCount);
2272
+ const recentBufferHeader = await formatBufferMessages(bufferMessages, entities, room, sessionStartTime);
2273
+ const { receivedMessageHeader, focusHeader } = buildReceivedMessageSection(message, entities);
2274
+ const text3 = assembleContextSections(compressedHistoryText, recentBufferHeader, receivedMessageHeader, focusHeader);
2275
+ const data = {
2276
+ dialogueMessages: bufferMessages,
2277
+ messageCount: dialogueMessages.length,
2278
+ lastSummarizedIndex,
2279
+ config
2280
+ };
2281
+ const isPostFormat = room?.type ? room.type === import_core11.ChannelType.FEED || room.type === import_core11.ChannelType.THREAD : false;
2282
+ const values = {
2283
+ compressedHistory: compressedHistoryText,
2284
+ recentMessages: recentBufferHeader,
2285
+ receivedMessage: receivedMessageHeader,
2286
+ focusInstruction: focusHeader,
2287
+ recentPosts: isPostFormat ? recentBufferHeader : ""
2288
+ };
2289
+ import_core11.logger.info({
2290
+ messageCount: dialogueMessages.length,
2291
+ bufferSize: bufferMessages.length,
2292
+ hasSummaries,
2293
+ lastSummarizedIndex,
2294
+ estimatedTokens: Math.ceil(text3.length / 4),
2295
+ overlapUserMessageCount: config.overlapUserMessageCount
2296
+ }, "Recent context assembled");
2297
+ return {
2298
+ data,
2299
+ values,
2300
+ text: text3
2301
+ };
1188
2302
  } catch (error) {
1189
- import_core4.logger.error({ error }, "Error in shortTermMemoryProvider:");
2303
+ import_core11.logger.error({ error }, "Error in recentContextProvider");
1190
2304
  return {
1191
- data: { summaries: [], recentMessages: [], mode: "error" },
1192
- values: {},
1193
- text: "Error retrieving conversation context."
2305
+ data: {
2306
+ dialogueMessages: [],
2307
+ messageCount: 0,
2308
+ lastSummarizedIndex: -1,
2309
+ config: getProviderConfig(runtime)
2310
+ },
2311
+ values: {
2312
+ compressedHistory: "",
2313
+ recentMessages: "",
2314
+ receivedMessage: "",
2315
+ focusInstruction: "",
2316
+ recentPosts: ""
2317
+ },
2318
+ text: "Error retrieving context."
1194
2319
  };
1195
2320
  }
1196
2321
  }
1197
2322
  };
1198
2323
 
1199
- // src/providers/long-term-memory.ts
1200
- var import_core5 = require("@elizaos/core");
1201
- var longTermMemoryProvider = {
1202
- name: "LONG_TERM_MEMORY",
1203
- description: "Persistent facts and preferences about the user",
1204
- position: 50,
1205
- get: async (runtime, message, _state) => {
1206
- try {
1207
- const memoryService = runtime.getService("memory");
1208
- if (!memoryService) {
1209
- return {
1210
- data: { memories: [] },
1211
- values: { longTermMemories: "" },
1212
- text: ""
1213
- };
1214
- }
1215
- const { entityId } = message;
1216
- if (entityId === runtime.agentId) {
1217
- return {
1218
- data: { memories: [] },
1219
- values: { longTermMemories: "" },
1220
- text: ""
1221
- };
1222
- }
1223
- const memories = await memoryService.getLongTermMemories(entityId, undefined, 25);
1224
- if (memories.length === 0) {
1225
- return {
1226
- data: { memories: [] },
1227
- values: { longTermMemories: "" },
1228
- text: ""
1229
- };
1230
- }
1231
- const formattedMemories = await memoryService.getFormattedLongTermMemories(entityId);
1232
- const text4 = import_core5.addHeader("# What I Know About You", formattedMemories);
1233
- const categoryCounts = new Map;
1234
- for (const memory of memories) {
1235
- const count = categoryCounts.get(memory.category) || 0;
1236
- categoryCounts.set(memory.category, count + 1);
2324
+ // src/providers/action-results.ts
2325
+ var import_core12 = require("@elizaos/core");
2326
+ var getActionResultsConfig = (runtime) => ({
2327
+ limit: parseInt(runtime.getSetting("CONTEXT_ACTION_RESULTS_LIMIT") || "3", 10)
2328
+ });
2329
+ function formatActionResults(actionResultMessages, limit) {
2330
+ if (actionResultMessages.length === 0) {
2331
+ return "";
2332
+ }
2333
+ const groupedByRun = new Map;
2334
+ for (const mem of actionResultMessages) {
2335
+ const runId = String(mem.content?.runId || "unknown");
2336
+ if (!groupedByRun.has(runId)) {
2337
+ groupedByRun.set(runId, []);
2338
+ }
2339
+ groupedByRun.get(runId).push(mem);
2340
+ }
2341
+ const formattedActionResults = Array.from(groupedByRun.entries()).slice(-limit).map(([runId, memories]) => {
2342
+ const sortedMemories = memories.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
2343
+ const thought = sortedMemories[0]?.content?.planThought || "";
2344
+ const runText = sortedMemories.map((mem) => {
2345
+ const actionName = mem.content?.actionName || "Unknown";
2346
+ const status = mem.content?.actionStatus || "unknown";
2347
+ const planStep = mem.content?.planStep || "";
2348
+ const text3 = mem.content?.text || "";
2349
+ const error = mem.content?.error || "";
2350
+ let memText = ` - ${actionName} (${status})`;
2351
+ if (planStep)
2352
+ memText += ` [${planStep}]`;
2353
+ if (error) {
2354
+ memText += `: Error - ${error}`;
2355
+ } else if (text3 && text3 !== `Executed action: ${actionName}`) {
2356
+ memText += `: ${text3}`;
1237
2357
  }
1238
- const categoryList = Array.from(categoryCounts.entries()).map(([cat, count]) => `${cat}: ${count}`).join(", ");
2358
+ return memText;
2359
+ }).join(`
2360
+ `);
2361
+ return `**Action Run ${runId.slice(0, 8)}**${thought ? ` - "${thought}"` : ""}
2362
+ ${runText}`;
2363
+ }).join(`
2364
+
2365
+ `);
2366
+ return formattedActionResults ? import_core12.addHeader("# Recent Action Executions", formattedActionResults) : "";
2367
+ }
2368
+ var actionResultsProvider = {
2369
+ name: "ACTION_RESULTS",
2370
+ description: "Recent action executions with their outcomes (tool memory)",
2371
+ position: 101,
2372
+ get: async (runtime, message) => {
2373
+ try {
2374
+ const { roomId } = message;
2375
+ const config = getActionResultsConfig(runtime);
2376
+ const allMessages = await runtime.getMemories({
2377
+ tableName: "messages",
2378
+ roomId,
2379
+ count: 50,
2380
+ unique: false
2381
+ });
2382
+ const actionResultMessages = allMessages.filter((msg) => msg.content?.type === "action_result" && msg.metadata?.type === "action_result");
2383
+ actionResultMessages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
2384
+ const text3 = formatActionResults(actionResultMessages, config.limit);
2385
+ import_core12.logger.debug({
2386
+ actionResultCount: actionResultMessages.length,
2387
+ limit: config.limit
2388
+ }, "Action results provider assembled");
1239
2389
  return {
1240
2390
  data: {
1241
- memories,
1242
- categoryCounts: Object.fromEntries(categoryCounts)
2391
+ actionResults: actionResultMessages,
2392
+ config
1243
2393
  },
1244
2394
  values: {
1245
- longTermMemories: text4,
1246
- memoryCategories: categoryList
2395
+ recentActionResults: text3
1247
2396
  },
1248
- text: text4
2397
+ text: text3
1249
2398
  };
1250
2399
  } catch (error) {
1251
- import_core5.logger.error({ error }, "Error in longTermMemoryProvider:");
2400
+ import_core12.logger.error({ error }, "Error in actionResultsProvider");
1252
2401
  return {
1253
- data: { memories: [] },
1254
- values: { longTermMemories: "" },
2402
+ data: {
2403
+ actionResults: [],
2404
+ config: getActionResultsConfig(runtime)
2405
+ },
2406
+ values: {
2407
+ recentActionResults: ""
2408
+ },
1255
2409
  text: ""
1256
2410
  };
1257
2411
  }
1258
2412
  }
1259
2413
  };
1260
-
1261
2414
  // src/index.ts
1262
2415
  var memoryPlugin = {
1263
2416
  name: "memory",
1264
- description: "Advanced memory management with conversation summarization and long-term persistent memory",
2417
+ description: "State-of-the-art cognitive memory system with episodic, semantic, and procedural memory, " + "featuring hybrid retrieval (Vector + BM25 + Graph), exponential decay, contextual embeddings, " + "and contradiction detection",
1265
2418
  services: [MemoryService],
1266
- evaluators: [summarizationEvaluator, longTermExtractionEvaluator],
1267
- providers: [longTermMemoryProvider, shortTermMemoryProvider],
2419
+ evaluators: [consolidationEvaluator, summarizationEvaluator],
2420
+ providers: [longTermMemoryProvider, recentContextProvider, actionResultsProvider],
1268
2421
  schema: exports_schemas
1269
2422
  };
1270
2423
  var src_default = memoryPlugin;
1271
2424
 
1272
- //# debugId=3C20A9F0BA14039464756E2164756E21
2425
+ //# debugId=CCBB98EF3F397EB764756E2164756E21