@elizaos/plugin-experience 2.0.0-alpha.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.
@@ -0,0 +1,1274 @@
1
+ // index.ts
2
+ import { logger as logger5 } from "@elizaos/core";
3
+
4
+ // actions/record-experience.ts
5
+ import {
6
+ createUniqueUuid,
7
+ logger
8
+ } from "@elizaos/core";
9
+
10
+ // generated/specs/specs.ts
11
+ var coreActionsSpec = {
12
+ version: "1.0.0",
13
+ actions: [
14
+ {
15
+ name: "RECORD_EXPERIENCE",
16
+ description: "Record a learning or experience for future reference. Use this when the user explicitly asks you to remember something or when you've learned something important.",
17
+ similes: ["REMEMBER", "LEARN", "STORE_EXPERIENCE", "SAVE_EXPERIENCE", "RECORD_LEARNING"],
18
+ parameters: [],
19
+ examples: [
20
+ [
21
+ {
22
+ name: "{{name1}}",
23
+ content: {
24
+ text: "Remember that installing dependencies is required for Python scripts"
25
+ }
26
+ },
27
+ {
28
+ name: "{{name2}}",
29
+ content: {
30
+ text: "I'll record that experience. Learning: Need to install dependencies before running Python scripts.",
31
+ actions: ["RECORD_EXPERIENCE"]
32
+ }
33
+ }
34
+ ],
35
+ [
36
+ {
37
+ name: "{{name1}}",
38
+ content: {
39
+ text: "Remember that users prefer shorter responses"
40
+ }
41
+ },
42
+ {
43
+ name: "{{name2}}",
44
+ content: {
45
+ text: "I'll remember that preference.",
46
+ actions: ["RECORD_EXPERIENCE"]
47
+ }
48
+ }
49
+ ],
50
+ [
51
+ {
52
+ name: "{{name1}}",
53
+ content: {
54
+ text: "What's 2+2?"
55
+ }
56
+ },
57
+ {
58
+ name: "{{name2}}",
59
+ content: {
60
+ text: "2+2 equals 4."
61
+ }
62
+ }
63
+ ],
64
+ [
65
+ {
66
+ name: "{{name1}}",
67
+ content: {
68
+ text: "Can you help me with math?"
69
+ }
70
+ },
71
+ {
72
+ name: "{{name2}}",
73
+ content: {
74
+ text: "Of course! What math problem do you need help with?"
75
+ }
76
+ }
77
+ ]
78
+ ]
79
+ }
80
+ ]
81
+ };
82
+ var allActionsSpec = {
83
+ version: "1.0.0",
84
+ actions: [
85
+ {
86
+ name: "RECORD_EXPERIENCE",
87
+ description: "Record a learning or experience for future reference. Use this when the user explicitly asks you to remember something or when you've learned something important.",
88
+ similes: ["REMEMBER", "LEARN", "STORE_EXPERIENCE", "SAVE_EXPERIENCE", "RECORD_LEARNING"],
89
+ parameters: [],
90
+ examples: [
91
+ [
92
+ {
93
+ name: "{{name1}}",
94
+ content: {
95
+ text: "Remember that installing dependencies is required for Python scripts"
96
+ }
97
+ },
98
+ {
99
+ name: "{{name2}}",
100
+ content: {
101
+ text: "I'll record that experience. Learning: Need to install dependencies before running Python scripts.",
102
+ actions: ["RECORD_EXPERIENCE"]
103
+ }
104
+ }
105
+ ],
106
+ [
107
+ {
108
+ name: "{{name1}}",
109
+ content: {
110
+ text: "Remember that users prefer shorter responses"
111
+ }
112
+ },
113
+ {
114
+ name: "{{name2}}",
115
+ content: {
116
+ text: "I'll remember that preference.",
117
+ actions: ["RECORD_EXPERIENCE"]
118
+ }
119
+ }
120
+ ],
121
+ [
122
+ {
123
+ name: "{{name1}}",
124
+ content: {
125
+ text: "What's 2+2?"
126
+ }
127
+ },
128
+ {
129
+ name: "{{name2}}",
130
+ content: {
131
+ text: "2+2 equals 4."
132
+ }
133
+ }
134
+ ],
135
+ [
136
+ {
137
+ name: "{{name1}}",
138
+ content: {
139
+ text: "Can you help me with math?"
140
+ }
141
+ },
142
+ {
143
+ name: "{{name2}}",
144
+ content: {
145
+ text: "Of course! What math problem do you need help with?"
146
+ }
147
+ }
148
+ ]
149
+ ]
150
+ }
151
+ ]
152
+ };
153
+ var coreProvidersSpec = {
154
+ version: "1.0.0",
155
+ providers: [
156
+ {
157
+ name: "experienceProvider",
158
+ description: "Provides relevant past experiences and learnings for the current context",
159
+ dynamic: true
160
+ }
161
+ ]
162
+ };
163
+ var allProvidersSpec = {
164
+ version: "1.0.0",
165
+ providers: [
166
+ {
167
+ name: "experienceProvider",
168
+ description: "Provides relevant past experiences and learnings for the current context",
169
+ dynamic: true
170
+ }
171
+ ]
172
+ };
173
+ var coreEvaluatorsSpec = {
174
+ version: "1.0.0",
175
+ evaluators: []
176
+ };
177
+ var allEvaluatorsSpec = {
178
+ version: "1.0.0",
179
+ evaluators: []
180
+ };
181
+ var coreActionDocs = coreActionsSpec.actions;
182
+ var allActionDocs = allActionsSpec.actions;
183
+ var coreProviderDocs = coreProvidersSpec.providers;
184
+ var allProviderDocs = allProvidersSpec.providers;
185
+ var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
186
+ var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
187
+
188
+ // generated/specs/spec-helpers.ts
189
+ var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
190
+ var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
191
+ var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
192
+ var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
193
+ var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
194
+ var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
195
+ function getActionSpec(name) {
196
+ return coreActionMap.get(name) ?? allActionMap.get(name);
197
+ }
198
+ function requireActionSpec(name) {
199
+ const spec = getActionSpec(name);
200
+ if (!spec) {
201
+ throw new Error(`Action spec not found: ${name}`);
202
+ }
203
+ return spec;
204
+ }
205
+ function getProviderSpec(name) {
206
+ return coreProviderMap.get(name) ?? allProviderMap.get(name);
207
+ }
208
+ function requireProviderSpec(name) {
209
+ const spec = getProviderSpec(name);
210
+ if (!spec) {
211
+ throw new Error(`Provider spec not found: ${name}`);
212
+ }
213
+ return spec;
214
+ }
215
+
216
+ // actions/record-experience.ts
217
+ var spec = requireActionSpec("RECORD_EXPERIENCE");
218
+ var recordExperienceAction = {
219
+ name: spec.name,
220
+ similes: spec.similes ? [...spec.similes] : [],
221
+ description: spec.description,
222
+ examples: spec.examples ?? [],
223
+ async validate(_runtime, message) {
224
+ const text = message.content.text?.toLowerCase();
225
+ return text?.includes("remember") || text?.includes("record") || false;
226
+ },
227
+ async handler(runtime, message, state, _options, _callback) {
228
+ logger.info("Recording experience for message:", message.id);
229
+ const experienceMemory = {
230
+ id: createUniqueUuid(runtime, `experience-${message.id}`),
231
+ entityId: message.entityId,
232
+ agentId: runtime.agentId,
233
+ roomId: message.roomId,
234
+ content: {
235
+ text: message.content.text,
236
+ source: message.content.source,
237
+ type: "experience",
238
+ context: state?.text
239
+ },
240
+ createdAt: Date.now()
241
+ };
242
+ await runtime.createMemory(experienceMemory, "experiences", true);
243
+ logger.info("Experience recorded successfully");
244
+ return {
245
+ success: true,
246
+ text: "Experience recorded.",
247
+ data: {
248
+ experienceMemoryId: experienceMemory.id
249
+ }
250
+ };
251
+ }
252
+ };
253
+
254
+ // evaluators/experienceEvaluator.ts
255
+ import {
256
+ composePrompt,
257
+ logger as logger2,
258
+ ModelType
259
+ } from "@elizaos/core";
260
+
261
+ // generated/prompts/typescript/prompts.ts
262
+ var extractExperiencesTemplate = `# Task: Extract Novel Learning Experiences
263
+
264
+ Analyze this conversation for novel learning experiences that would be surprising or valuable to remember.
265
+
266
+ ## Conversation context
267
+ {{conversation_context}}
268
+
269
+ ## Existing similar experiences
270
+ {{existing_experiences}}
271
+
272
+ ## Instructions
273
+ Extract ONLY experiences that are:
274
+ 1. Genuinely novel (not in existing experiences)
275
+ 2. Actionable learnings about how things work
276
+ 3. Corrections of previous mistakes or assumptions
277
+ 4. Discoveries of new capabilities or patterns
278
+ 5. Surprising outcomes that contradict expectations
279
+
280
+ Focus on technical knowledge, patterns, and cause-effect relationships that transfer to other contexts.
281
+ Avoid personal details, user-specific information, or routine interactions.
282
+
283
+ Respond with JSON array of experiences (max 3):
284
+ [{
285
+ "type": "DISCOVERY|CORRECTION|SUCCESS|LEARNING",
286
+ "learning": "What was learned (generic, transferable)",
287
+ "context": "What situation triggered this (anonymized)",
288
+ "confidence": 0.0-1.0,
289
+ "reasoning": "Why this is novel and valuable"
290
+ }]
291
+
292
+ Return empty array [] if no novel experiences found.`;
293
+ var EXTRACT_EXPERIENCES_TEMPLATE = extractExperiencesTemplate;
294
+
295
+ // types.ts
296
+ var ExperienceServiceType = {
297
+ EXPERIENCE: "EXPERIENCE"
298
+ };
299
+ var ExperienceType;
300
+ ((ExperienceType2) => {
301
+ ExperienceType2["SUCCESS"] = "success";
302
+ ExperienceType2["FAILURE"] = "failure";
303
+ ExperienceType2["DISCOVERY"] = "discovery";
304
+ ExperienceType2["CORRECTION"] = "correction";
305
+ ExperienceType2["LEARNING"] = "learning";
306
+ ExperienceType2["HYPOTHESIS"] = "hypothesis";
307
+ ExperienceType2["VALIDATION"] = "validation";
308
+ ExperienceType2["WARNING"] = "warning";
309
+ })(ExperienceType ||= {});
310
+ var OutcomeType;
311
+ ((OutcomeType2) => {
312
+ OutcomeType2["POSITIVE"] = "positive";
313
+ OutcomeType2["NEGATIVE"] = "negative";
314
+ OutcomeType2["NEUTRAL"] = "neutral";
315
+ OutcomeType2["MIXED"] = "mixed";
316
+ })(OutcomeType ||= {});
317
+
318
+ // evaluators/experienceEvaluator.ts
319
+ var experienceEvaluator = {
320
+ name: "EXPERIENCE_EVALUATOR",
321
+ similes: ["experience recorder", "learning evaluator", "self-reflection"],
322
+ description: "Periodically analyzes conversation patterns to extract novel learning experiences",
323
+ alwaysRun: false,
324
+ examples: [
325
+ {
326
+ prompt: "The agent successfully executed a shell command after initially failing",
327
+ messages: [
328
+ {
329
+ name: "Autoliza",
330
+ content: {
331
+ text: "Let me try to run this Python script."
332
+ }
333
+ },
334
+ {
335
+ name: "Autoliza",
336
+ content: {
337
+ text: "Error: ModuleNotFoundError for pandas. I need to install it first."
338
+ }
339
+ },
340
+ {
341
+ name: "Autoliza",
342
+ content: {
343
+ text: "After installing pandas, the script ran successfully and produced the expected output."
344
+ }
345
+ }
346
+ ],
347
+ outcome: "Record a CORRECTION experience about needing to install dependencies before running Python scripts"
348
+ },
349
+ {
350
+ prompt: "The agent discovered a new system capability",
351
+ messages: [
352
+ {
353
+ name: "Autoliza",
354
+ content: {
355
+ text: "I found that the system has jq installed, which is perfect for parsing JSON data."
356
+ }
357
+ }
358
+ ],
359
+ outcome: "Record a DISCOVERY experience about the availability of jq for JSON processing"
360
+ }
361
+ ],
362
+ async validate(runtime, message, _state) {
363
+ if (message.entityId !== runtime.agentId) {
364
+ return false;
365
+ }
366
+ const lastExtractionKey = "experience-extraction:last-message-count";
367
+ const currentCount = await runtime.getCache(lastExtractionKey) || "0";
368
+ const messageCount = Number.parseInt(currentCount, 10);
369
+ const newMessageCount = messageCount + 1;
370
+ await runtime.setCache(lastExtractionKey, newMessageCount.toString());
371
+ const shouldExtract = newMessageCount % 10 === 0;
372
+ if (shouldExtract) {
373
+ logger2.info(`[experienceEvaluator] Triggering experience extraction after ${newMessageCount} messages`);
374
+ }
375
+ return shouldExtract;
376
+ },
377
+ async handler(runtime, message, state, _options, _callback, _responses) {
378
+ const experienceService = runtime.getService("EXPERIENCE");
379
+ if (!experienceService) {
380
+ logger2.warn("[experienceEvaluator] Experience service not available");
381
+ return;
382
+ }
383
+ const recentMessages = await runtime.getMemories({
384
+ tableName: "messages",
385
+ roomId: message.roomId,
386
+ count: 10,
387
+ unique: false
388
+ });
389
+ if (recentMessages.length < 3) {
390
+ logger2.debug("[experienceEvaluator] Not enough messages for experience extraction");
391
+ return;
392
+ }
393
+ const conversationContext = recentMessages.map((m) => m.content.text).filter(Boolean).join(" ");
394
+ const existingExperiences = await experienceService.queryExperiences({
395
+ query: conversationContext,
396
+ limit: 10,
397
+ minConfidence: 0.7
398
+ });
399
+ const existingExperiencesText = existingExperiences.length > 0 ? existingExperiences.map((exp) => `- ${exp.learning}`).join(`
400
+ `) : "None";
401
+ const extractionPrompt = composePrompt({
402
+ state: {
403
+ conversation_context: conversationContext,
404
+ existing_experiences: existingExperiencesText
405
+ },
406
+ template: EXTRACT_EXPERIENCES_TEMPLATE
407
+ });
408
+ const response = await runtime.useModel(ModelType.TEXT_LARGE, {
409
+ prompt: extractionPrompt
410
+ });
411
+ const experiences = parseExtractedExperiences(response);
412
+ const threshold = getNumberSetting(runtime, "AUTO_RECORD_THRESHOLD", 0.6);
413
+ const experienceTypeMap = {
414
+ DISCOVERY: "discovery" /* DISCOVERY */,
415
+ CORRECTION: "correction" /* CORRECTION */,
416
+ SUCCESS: "success" /* SUCCESS */,
417
+ LEARNING: "learning" /* LEARNING */
418
+ };
419
+ for (const exp of experiences.slice(0, 3)) {
420
+ if (!exp.learning || typeof exp.confidence !== "number" || exp.confidence < threshold) {
421
+ continue;
422
+ }
423
+ const normalizedType = typeof exp.type === "string" ? exp.type.toUpperCase() : "";
424
+ const experienceType = experienceTypeMap[normalizedType] ?? "learning" /* LEARNING */;
425
+ const experienceTag = experienceType;
426
+ await experienceService.recordExperience({
427
+ type: experienceType,
428
+ outcome: experienceType === "correction" /* CORRECTION */ ? "positive" /* POSITIVE */ : "neutral" /* NEUTRAL */,
429
+ context: sanitizeContext(exp.context || "Conversation analysis"),
430
+ action: "pattern_recognition",
431
+ result: exp.learning,
432
+ learning: sanitizeContext(exp.learning),
433
+ domain: detectDomain(exp.learning),
434
+ tags: ["extracted", "novel", experienceTag],
435
+ confidence: Math.min(exp.confidence, 0.9),
436
+ importance: 0.8
437
+ });
438
+ logger2.info(`[experienceEvaluator] Recorded novel experience: ${exp.learning.substring(0, 100)}...`);
439
+ }
440
+ if (experiences.length > 0) {
441
+ logger2.info(`[experienceEvaluator] Extracted ${experiences.length} novel experiences from conversation`);
442
+ } else {
443
+ logger2.debug("[experienceEvaluator] No novel experiences found in recent conversation");
444
+ }
445
+ return {
446
+ success: true,
447
+ data: {
448
+ extractedCount: experiences.length
449
+ },
450
+ values: {
451
+ extractedCount: experiences.length.toString()
452
+ }
453
+ };
454
+ }
455
+ };
456
+ function parseExtractedExperiences(response) {
457
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
458
+ if (!jsonMatch)
459
+ return [];
460
+ try {
461
+ const parsed = JSON.parse(jsonMatch[0]);
462
+ if (!Array.isArray(parsed))
463
+ return [];
464
+ return parsed.filter((item) => item && typeof item === "object");
465
+ } catch {
466
+ return [];
467
+ }
468
+ }
469
+ function getNumberSetting(runtime, key, fallback) {
470
+ const value = runtime.getSetting(key);
471
+ if (typeof value === "number")
472
+ return value;
473
+ if (typeof value === "string") {
474
+ const parsed = Number.parseFloat(value);
475
+ return Number.isFinite(parsed) ? parsed : fallback;
476
+ }
477
+ return fallback;
478
+ }
479
+ function sanitizeContext(text) {
480
+ if (!text)
481
+ return "Unknown context";
482
+ return text.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, "[EMAIL]").replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, "[IP]").replace(/\/Users\/[^/\s]+/g, "/Users/[USER]").replace(/\/home\/[^/\s]+/g, "/home/[USER]").replace(/\b[A-Z0-9]{20,}\b/g, "[TOKEN]").replace(/\b(user|person|someone|they)\s+(said|asked|told|mentioned)/gi, "when asked").substring(0, 200);
483
+ }
484
+ function detectDomain(text) {
485
+ const domains = {
486
+ shell: ["command", "terminal", "bash", "shell", "execute", "script", "cli"],
487
+ coding: [
488
+ "code",
489
+ "function",
490
+ "variable",
491
+ "syntax",
492
+ "programming",
493
+ "debug",
494
+ "typescript",
495
+ "javascript"
496
+ ],
497
+ system: ["file", "directory", "process", "memory", "cpu", "system", "install", "package"],
498
+ network: ["http", "api", "request", "response", "url", "network", "fetch", "curl"],
499
+ data: ["json", "csv", "database", "query", "data", "sql", "table"],
500
+ ai: ["model", "llm", "embedding", "prompt", "token", "inference"]
501
+ };
502
+ const lowerText = text.toLowerCase();
503
+ for (const [domain, keywords] of Object.entries(domains)) {
504
+ if (keywords.some((keyword) => lowerText.includes(keyword))) {
505
+ return domain;
506
+ }
507
+ }
508
+ return "general";
509
+ }
510
+
511
+ // providers/experienceProvider.ts
512
+ import {
513
+ logger as logger3
514
+ } from "@elizaos/core";
515
+ var spec2 = requireProviderSpec("experienceProvider");
516
+ var experienceProvider = {
517
+ name: spec2.name,
518
+ description: "Provides relevant past experiences and learnings for the current context",
519
+ async get(runtime, message, _state) {
520
+ const experienceService = runtime.getService("EXPERIENCE");
521
+ if (!experienceService) {
522
+ return { text: "", data: {}, values: {} };
523
+ }
524
+ const messageText = message.content.text || "";
525
+ if (messageText.length < 10) {
526
+ return { text: "", data: {}, values: {} };
527
+ }
528
+ const relevantExperiences = await experienceService.queryExperiences({
529
+ query: messageText,
530
+ limit: 5,
531
+ minConfidence: 0.6,
532
+ minImportance: 0.5
533
+ });
534
+ if (relevantExperiences.length === 0) {
535
+ return { text: "", data: {}, values: {} };
536
+ }
537
+ const experienceText = relevantExperiences.map((exp, index) => {
538
+ return `Experience ${index + 1}: In ${exp.domain} context, when ${exp.context}, I learned: ${exp.learning}`;
539
+ }).join(`
540
+ `);
541
+ const contextText = `[RELEVANT EXPERIENCES]
542
+ ${experienceText}
543
+ [/RELEVANT EXPERIENCES]`;
544
+ logger3.debug(`[experienceProvider] Injecting ${relevantExperiences.length} relevant experiences`);
545
+ return {
546
+ text: contextText,
547
+ data: {
548
+ experiences: relevantExperiences,
549
+ count: relevantExperiences.length
550
+ },
551
+ values: {
552
+ experienceCount: relevantExperiences.length.toString()
553
+ }
554
+ };
555
+ }
556
+ };
557
+
558
+ // service.ts
559
+ import {
560
+ logger as logger4,
561
+ ModelType as ModelType2,
562
+ Service
563
+ } from "@elizaos/core";
564
+ import { v4 as uuidv4 } from "uuid";
565
+
566
+ // utils/confidenceDecay.ts
567
+ var DEFAULT_DECAY_CONFIG = {
568
+ halfLife: 30 * 24 * 60 * 60 * 1000,
569
+ minConfidence: 0.1,
570
+ decayStartDelay: 7 * 24 * 60 * 60 * 1000
571
+ };
572
+
573
+ class ConfidenceDecayManager {
574
+ config;
575
+ constructor(config = {}) {
576
+ this.config = { ...DEFAULT_DECAY_CONFIG, ...config };
577
+ }
578
+ getDecayedConfidence(experience) {
579
+ const now = Date.now();
580
+ const age = now - experience.createdAt;
581
+ const specificConfig = this.getDomainSpecificDecay(experience);
582
+ if (age < specificConfig.decayStartDelay) {
583
+ return experience.confidence;
584
+ }
585
+ const decayTime = age - specificConfig.decayStartDelay;
586
+ const halfLives = decayTime / specificConfig.halfLife;
587
+ const decayFactor = 0.5 ** halfLives;
588
+ const decayedConfidence = experience.confidence * decayFactor;
589
+ return Math.max(specificConfig.minConfidence, decayedConfidence);
590
+ }
591
+ getExperiencesNeedingReinforcement(experiences, threshold = 0.3) {
592
+ return experiences.filter((exp) => {
593
+ const decayed = this.getDecayedConfidence(exp);
594
+ return decayed < threshold && decayed > this.config.minConfidence;
595
+ });
596
+ }
597
+ calculateReinforcementBoost(experience, validationStrength = 1) {
598
+ const currentConfidence = this.getDecayedConfidence(experience);
599
+ const boost = (1 - currentConfidence) * validationStrength * 0.5;
600
+ return Math.min(1, currentConfidence + boost);
601
+ }
602
+ getDomainSpecificDecay(experience) {
603
+ const config = { ...this.config };
604
+ if (experience.type === "discovery" /* DISCOVERY */ || experience.type === "learning" /* LEARNING */) {
605
+ config.halfLife *= 2;
606
+ }
607
+ if (experience.type === "warning" /* WARNING */ || experience.type === "correction" /* CORRECTION */) {
608
+ config.halfLife *= 1.5;
609
+ config.minConfidence = 0.2;
610
+ }
611
+ switch (experience.domain) {
612
+ case "security":
613
+ case "safety":
614
+ config.halfLife *= 3;
615
+ config.minConfidence = 0.3;
616
+ break;
617
+ case "performance":
618
+ config.halfLife *= 0.5;
619
+ break;
620
+ case "user_preference":
621
+ config.halfLife *= 0.7;
622
+ break;
623
+ }
624
+ return config;
625
+ }
626
+ getConfidenceTrend(experience, points = 10) {
627
+ const trend = [];
628
+ const now = Date.now();
629
+ const totalTime = now - experience.createdAt;
630
+ const interval = totalTime / (points - 1);
631
+ const specificConfig = this.getDomainSpecificDecay(experience);
632
+ for (let i = 0;i < points; i++) {
633
+ const timestamp = experience.createdAt + interval * i;
634
+ const age = timestamp - experience.createdAt;
635
+ let confidence;
636
+ if (age < specificConfig.decayStartDelay) {
637
+ confidence = experience.confidence;
638
+ } else {
639
+ const decayTime = age - specificConfig.decayStartDelay;
640
+ const halfLives = decayTime / specificConfig.halfLife;
641
+ const decayFactor = 0.5 ** halfLives;
642
+ confidence = Math.max(specificConfig.minConfidence, experience.confidence * decayFactor);
643
+ }
644
+ trend.push({ timestamp, confidence });
645
+ }
646
+ return trend;
647
+ }
648
+ }
649
+
650
+ // utils/experienceRelationships.ts
651
+ class ExperienceRelationshipManager {
652
+ relationships = new Map;
653
+ addRelationship(relationship) {
654
+ const { fromId } = relationship;
655
+ if (!this.relationships.has(fromId)) {
656
+ this.relationships.set(fromId, []);
657
+ }
658
+ this.relationships.get(fromId)?.push(relationship);
659
+ }
660
+ findRelationships(experienceId, type) {
661
+ const rels = this.relationships.get(experienceId) || [];
662
+ if (type) {
663
+ return rels.filter((r) => r.type === type);
664
+ }
665
+ return rels;
666
+ }
667
+ detectCausalChain(experiences) {
668
+ const chains = [];
669
+ const sorted = [...experiences].sort((a, b) => a.createdAt - b.createdAt);
670
+ for (let i = 0;i < sorted.length - 1; i++) {
671
+ const current = sorted[i];
672
+ if (!current) {
673
+ continue;
674
+ }
675
+ if (current.type === "hypothesis" /* HYPOTHESIS */) {
676
+ const chain = [current.id];
677
+ let j = i + 1;
678
+ while (j < sorted.length) {
679
+ const next = sorted[j];
680
+ if (!next) {
681
+ j++;
682
+ continue;
683
+ }
684
+ if (next.relatedExperiences?.includes(current.id) || this.isRelated(current, next)) {
685
+ chain.push(next.id);
686
+ if (next.type === "validation" /* VALIDATION */) {
687
+ chains.push({
688
+ rootExperience: current.id,
689
+ chain,
690
+ strength: next.confidence,
691
+ validated: next.outcome === "positive" /* POSITIVE */
692
+ });
693
+ break;
694
+ }
695
+ }
696
+ j++;
697
+ }
698
+ }
699
+ }
700
+ return chains;
701
+ }
702
+ isRelated(exp1, exp2) {
703
+ if (exp1.domain === exp2.domain) {
704
+ const timeDiff = Math.abs(exp2.createdAt - exp1.createdAt);
705
+ if (timeDiff < 5 * 60 * 1000) {
706
+ if (this.contentSimilarity(exp1, exp2) > 0.7) {
707
+ return true;
708
+ }
709
+ }
710
+ }
711
+ return false;
712
+ }
713
+ contentSimilarity(exp1, exp2) {
714
+ const words1 = new Set(exp1.learning.toLowerCase().split(/\s+/));
715
+ const words2 = new Set(exp2.learning.toLowerCase().split(/\s+/));
716
+ const intersection = new Set([...words1].filter((x) => words2.has(x)));
717
+ const union = new Set([...words1, ...words2]);
718
+ return intersection.size / union.size;
719
+ }
720
+ findContradictions(experience, allExperiences) {
721
+ const contradictions = [];
722
+ for (const other of allExperiences) {
723
+ if (other.id === experience.id)
724
+ continue;
725
+ if (other.action === experience.action && other.outcome !== experience.outcome && other.domain === experience.domain) {
726
+ contradictions.push(other);
727
+ }
728
+ const rels = this.findRelationships(experience.id, "contradicts");
729
+ if (rels.some((r) => r.toId === other.id)) {
730
+ contradictions.push(other);
731
+ }
732
+ }
733
+ return contradictions;
734
+ }
735
+ getExperienceImpact(experienceId, allExperiences) {
736
+ let impact = 0;
737
+ for (const exp of allExperiences) {
738
+ if (exp.relatedExperiences?.includes(experienceId)) {
739
+ impact += exp.importance;
740
+ }
741
+ }
742
+ const relationships = this.findRelationships(experienceId);
743
+ for (const rel of relationships) {
744
+ if (rel.type === "causes") {
745
+ impact += rel.strength;
746
+ }
747
+ }
748
+ return impact;
749
+ }
750
+ }
751
+
752
+ // service.ts
753
+ class ExperienceService extends Service {
754
+ static serviceType = ExperienceServiceType.EXPERIENCE;
755
+ capabilityDescription = "Manages agent experiences, learning from successes and failures to improve future decisions";
756
+ experiences = new Map;
757
+ experiencesByDomain = new Map;
758
+ experiencesByType = new Map;
759
+ maxExperiences = 1e4;
760
+ decayManager;
761
+ relationshipManager;
762
+ constructor(runtime) {
763
+ super(runtime);
764
+ this.decayManager = new ConfidenceDecayManager;
765
+ this.relationshipManager = new ExperienceRelationshipManager;
766
+ const max = getNumberSetting2(runtime, "MAX_EXPERIENCES", this.maxExperiences);
767
+ if (Number.isFinite(max) && max > 0) {
768
+ this.maxExperiences = Math.floor(max);
769
+ }
770
+ this.loadExperiences();
771
+ }
772
+ static async start(runtime) {
773
+ const service = new ExperienceService(runtime);
774
+ return service;
775
+ }
776
+ setMaxExperiences(maxExperiences) {
777
+ if (!Number.isFinite(maxExperiences) || maxExperiences <= 0)
778
+ return;
779
+ this.maxExperiences = Math.floor(maxExperiences);
780
+ }
781
+ async loadExperiences() {
782
+ const allMemories = await this.runtime.getMemories({
783
+ entityId: this.runtime.agentId,
784
+ count: this.maxExperiences,
785
+ tableName: "memories"
786
+ });
787
+ const memories = allMemories.filter((m) => m.content.type === "experience");
788
+ for (const memory of memories) {
789
+ const experienceData = memory.content.data;
790
+ if (experienceData?.id) {
791
+ const memoryCreatedAt = typeof memory.createdAt === "number" ? memory.createdAt : Date.now();
792
+ const toTimestamp = (value, fallback) => {
793
+ if (value === undefined)
794
+ return fallback;
795
+ if (typeof value === "number")
796
+ return value;
797
+ if (value instanceof Date)
798
+ return value.getTime();
799
+ return fallback;
800
+ };
801
+ const experience = {
802
+ id: experienceData.id,
803
+ agentId: this.runtime.agentId,
804
+ type: experienceData.type || "learning" /* LEARNING */,
805
+ outcome: experienceData.outcome || "neutral" /* NEUTRAL */,
806
+ context: experienceData.context || "",
807
+ action: experienceData.action || "",
808
+ result: experienceData.result || "",
809
+ learning: experienceData.learning || "",
810
+ domain: experienceData.domain || "general",
811
+ tags: experienceData.tags || [],
812
+ confidence: experienceData.confidence || 0.5,
813
+ importance: experienceData.importance || 0.5,
814
+ createdAt: toTimestamp(experienceData.createdAt, memoryCreatedAt),
815
+ updatedAt: toTimestamp(experienceData.updatedAt, memoryCreatedAt),
816
+ accessCount: experienceData.accessCount ?? 0,
817
+ lastAccessedAt: toTimestamp(experienceData.lastAccessedAt, memoryCreatedAt),
818
+ embedding: experienceData.embedding,
819
+ relatedExperiences: experienceData.relatedExperiences,
820
+ supersedes: experienceData.supersedes,
821
+ previousBelief: experienceData.previousBelief,
822
+ correctedBelief: experienceData.correctedBelief
823
+ };
824
+ this.experiences.set(experience.id, experience);
825
+ if (!this.experiencesByDomain.has(experience.domain)) {
826
+ this.experiencesByDomain.set(experience.domain, new Set);
827
+ }
828
+ this.experiencesByDomain.get(experience.domain)?.add(experience.id);
829
+ if (!this.experiencesByType.has(experience.type)) {
830
+ this.experiencesByType.set(experience.type, new Set);
831
+ }
832
+ this.experiencesByType.get(experience.type)?.add(experience.id);
833
+ }
834
+ }
835
+ logger4.info(`[ExperienceService] Loaded ${this.experiences.size} experiences from memory`);
836
+ }
837
+ async recordExperience(experienceData) {
838
+ const embeddingText = `${experienceData.context} ${experienceData.action} ${experienceData.result} ${experienceData.learning}`;
839
+ const embedding = await this.runtime.useModel(ModelType2.TEXT_EMBEDDING, {
840
+ text: embeddingText
841
+ });
842
+ const now = Date.now();
843
+ const experience = {
844
+ id: uuidv4(),
845
+ agentId: this.runtime.agentId,
846
+ type: experienceData.type || "learning" /* LEARNING */,
847
+ outcome: experienceData.outcome || "neutral" /* NEUTRAL */,
848
+ context: experienceData.context || "",
849
+ action: experienceData.action || "",
850
+ result: experienceData.result || "",
851
+ learning: experienceData.learning || "",
852
+ domain: experienceData.domain || "general",
853
+ tags: experienceData.tags || [],
854
+ confidence: experienceData.confidence || 0.5,
855
+ importance: experienceData.importance || 0.5,
856
+ createdAt: now,
857
+ updatedAt: now,
858
+ accessCount: 0,
859
+ lastAccessedAt: now,
860
+ embedding,
861
+ relatedExperiences: experienceData.relatedExperiences,
862
+ supersedes: experienceData.supersedes,
863
+ previousBelief: experienceData.previousBelief,
864
+ correctedBelief: experienceData.correctedBelief
865
+ };
866
+ this.experiences.set(experience.id, experience);
867
+ if (!this.experiencesByDomain.has(experience.domain)) {
868
+ this.experiencesByDomain.set(experience.domain, new Set);
869
+ }
870
+ this.experiencesByDomain.get(experience.domain)?.add(experience.id);
871
+ if (!this.experiencesByType.has(experience.type)) {
872
+ this.experiencesByType.set(experience.type, new Set);
873
+ }
874
+ this.experiencesByType.get(experience.type)?.add(experience.id);
875
+ await this.saveExperienceToMemory(experience);
876
+ const allExperiences = Array.from(this.experiences.values());
877
+ const contradictions = this.relationshipManager.findContradictions(experience, allExperiences);
878
+ for (const contradiction of contradictions) {
879
+ this.relationshipManager.addRelationship({
880
+ fromId: experience.id,
881
+ toId: contradiction.id,
882
+ type: "contradicts",
883
+ strength: 0.8
884
+ });
885
+ }
886
+ if (this.experiences.size > this.maxExperiences) {
887
+ await this.pruneOldExperiences();
888
+ }
889
+ logger4.info(`[ExperienceService] Recorded experience: ${experience.id} (${experience.type})`);
890
+ return experience;
891
+ }
892
+ async saveExperienceToMemory(experience) {
893
+ const memory = {
894
+ id: experience.id,
895
+ entityId: this.runtime.agentId,
896
+ agentId: this.runtime.agentId,
897
+ roomId: this.runtime.agentId,
898
+ content: {
899
+ text: `Experience: ${experience.learning}`,
900
+ type: "experience",
901
+ data: {
902
+ id: experience.id,
903
+ agentId: experience.agentId,
904
+ type: experience.type,
905
+ outcome: experience.outcome,
906
+ context: experience.context,
907
+ action: experience.action,
908
+ result: experience.result,
909
+ learning: experience.learning,
910
+ domain: experience.domain,
911
+ tags: experience.tags,
912
+ confidence: experience.confidence,
913
+ importance: experience.importance,
914
+ createdAt: experience.createdAt,
915
+ updatedAt: experience.updatedAt,
916
+ accessCount: experience.accessCount,
917
+ lastAccessedAt: experience.lastAccessedAt,
918
+ embedding: experience.embedding,
919
+ relatedExperiences: experience.relatedExperiences,
920
+ supersedes: experience.supersedes,
921
+ previousBelief: experience.previousBelief,
922
+ correctedBelief: experience.correctedBelief
923
+ }
924
+ },
925
+ createdAt: experience.createdAt
926
+ };
927
+ await this.runtime.createMemory(memory, "experiences", true);
928
+ }
929
+ async queryExperiences(query) {
930
+ let results = [];
931
+ if (query.query) {
932
+ const similarExperiences = await this.findSimilarExperiences(query.query, query.limit || 10);
933
+ let candidates = similarExperiences;
934
+ if (query.type) {
935
+ const types = Array.isArray(query.type) ? query.type : [query.type];
936
+ candidates = candidates.filter((exp) => types.includes(exp.type));
937
+ }
938
+ if (query.outcome) {
939
+ candidates = candidates.filter((exp) => exp.outcome === query.outcome);
940
+ }
941
+ if (query.domain) {
942
+ const domains = Array.isArray(query.domain) ? query.domain : [query.domain];
943
+ candidates = candidates.filter((exp) => domains.includes(exp.domain));
944
+ }
945
+ if (query.tags && query.tags.length > 0) {
946
+ candidates = candidates.filter((exp) => query.tags?.some((tag) => exp.tags.includes(tag)));
947
+ }
948
+ if (query.minConfidence !== undefined) {
949
+ const minConfidence = query.minConfidence;
950
+ candidates = candidates.filter((exp) => {
951
+ const decayedConfidence = this.decayManager.getDecayedConfidence(exp);
952
+ return decayedConfidence >= minConfidence;
953
+ });
954
+ }
955
+ if (query.minImportance !== undefined) {
956
+ const minImportance = query.minImportance;
957
+ candidates = candidates.filter((exp) => exp.importance >= minImportance);
958
+ }
959
+ if (query.timeRange) {
960
+ candidates = candidates.filter((exp) => {
961
+ if (query.timeRange?.start && exp.createdAt < query.timeRange?.start)
962
+ return false;
963
+ if (query.timeRange?.end && exp.createdAt > query.timeRange?.end)
964
+ return false;
965
+ return true;
966
+ });
967
+ }
968
+ results = candidates;
969
+ } else {
970
+ let candidates = Array.from(this.experiences.values());
971
+ if (query.type) {
972
+ const types = Array.isArray(query.type) ? query.type : [query.type];
973
+ candidates = candidates.filter((exp) => types.includes(exp.type));
974
+ }
975
+ if (query.outcome) {
976
+ candidates = candidates.filter((exp) => exp.outcome === query.outcome);
977
+ }
978
+ if (query.domain) {
979
+ const domains = Array.isArray(query.domain) ? query.domain : [query.domain];
980
+ candidates = candidates.filter((exp) => domains.includes(exp.domain));
981
+ }
982
+ if (query.tags && query.tags.length > 0) {
983
+ candidates = candidates.filter((exp) => query.tags?.some((tag) => exp.tags.includes(tag)));
984
+ }
985
+ if (query.minConfidence !== undefined) {
986
+ const minConfidence = query.minConfidence;
987
+ candidates = candidates.filter((exp) => {
988
+ const decayedConfidence = this.decayManager.getDecayedConfidence(exp);
989
+ return decayedConfidence >= minConfidence;
990
+ });
991
+ }
992
+ if (query.minImportance !== undefined) {
993
+ const minImportance = query.minImportance;
994
+ candidates = candidates.filter((exp) => exp.importance >= minImportance);
995
+ }
996
+ if (query.timeRange) {
997
+ candidates = candidates.filter((exp) => {
998
+ if (query.timeRange?.start && exp.createdAt < query.timeRange?.start)
999
+ return false;
1000
+ if (query.timeRange?.end && exp.createdAt > query.timeRange?.end)
1001
+ return false;
1002
+ return true;
1003
+ });
1004
+ }
1005
+ candidates.sort((a, b) => {
1006
+ const scoreA = this.decayManager.getDecayedConfidence(a) * a.importance;
1007
+ const scoreB = this.decayManager.getDecayedConfidence(b) * b.importance;
1008
+ return scoreB - scoreA;
1009
+ });
1010
+ results = candidates.slice(0, query.limit || 10);
1011
+ }
1012
+ if (query.includeRelated) {
1013
+ const relatedIds = new Set;
1014
+ for (const exp of results) {
1015
+ if (exp.relatedExperiences) {
1016
+ exp.relatedExperiences.forEach((id) => {
1017
+ relatedIds.add(id);
1018
+ });
1019
+ }
1020
+ }
1021
+ const related = Array.from(relatedIds).map((id) => this.experiences.get(id)).filter((exp) => exp !== undefined).filter((exp) => !results.some((r) => r.id === exp.id));
1022
+ results.push(...related);
1023
+ }
1024
+ for (const exp of results) {
1025
+ exp.accessCount++;
1026
+ exp.lastAccessedAt = Date.now();
1027
+ }
1028
+ return results;
1029
+ }
1030
+ async findSimilarExperiences(text, limit = 5) {
1031
+ if (!text || this.experiences.size === 0) {
1032
+ return [];
1033
+ }
1034
+ const queryEmbedding = await this.runtime.useModel(ModelType2.TEXT_EMBEDDING, {
1035
+ text
1036
+ });
1037
+ const similarities = [];
1038
+ for (const experience of this.experiences.values()) {
1039
+ if (experience.embedding) {
1040
+ const similarity = this.cosineSimilarity(queryEmbedding, experience.embedding);
1041
+ similarities.push({ experience, similarity });
1042
+ }
1043
+ }
1044
+ similarities.sort((a, b) => b.similarity - a.similarity);
1045
+ const results = similarities.slice(0, limit).map((item) => item.experience);
1046
+ for (const exp of results) {
1047
+ exp.accessCount++;
1048
+ exp.lastAccessedAt = Date.now();
1049
+ }
1050
+ return results;
1051
+ }
1052
+ async analyzeExperiences(domain, type) {
1053
+ const experiences = await this.queryExperiences({
1054
+ domain: domain ? [domain] : undefined,
1055
+ type: type ? [type] : undefined,
1056
+ limit: 100
1057
+ });
1058
+ if (experiences.length === 0) {
1059
+ return {
1060
+ pattern: "No experiences found for analysis",
1061
+ frequency: 0,
1062
+ reliability: 0,
1063
+ alternatives: [],
1064
+ recommendations: []
1065
+ };
1066
+ }
1067
+ const learnings = experiences.map((exp) => exp.learning);
1068
+ const commonWords = this.findCommonPatterns(learnings);
1069
+ const avgConfidence = experiences.reduce((sum, exp) => sum + exp.confidence, 0) / experiences.length;
1070
+ const outcomeConsistency = this.calculateOutcomeConsistency(experiences);
1071
+ const reliability = (avgConfidence + outcomeConsistency) / 2;
1072
+ const alternatives = this.extractAlternatives(experiences);
1073
+ const recommendations = this.generateRecommendations(experiences, reliability);
1074
+ return {
1075
+ pattern: commonWords.length > 0 ? `Common patterns: ${commonWords.join(", ")}` : "No clear patterns detected",
1076
+ frequency: experiences.length,
1077
+ reliability,
1078
+ alternatives,
1079
+ recommendations
1080
+ };
1081
+ }
1082
+ cosineSimilarity(a, b) {
1083
+ if (a.length !== b.length)
1084
+ return 0;
1085
+ let dotProduct = 0;
1086
+ let normA = 0;
1087
+ let normB = 0;
1088
+ for (let i = 0;i < a.length; i++) {
1089
+ const valueA = a[i] ?? 0;
1090
+ const valueB = b[i] ?? 0;
1091
+ dotProduct += valueA * valueB;
1092
+ normA += valueA * valueA;
1093
+ normB += valueB * valueB;
1094
+ }
1095
+ if (normA === 0 || normB === 0)
1096
+ return 0;
1097
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
1098
+ }
1099
+ findCommonPatterns(texts) {
1100
+ const wordFreq = new Map;
1101
+ for (const text of texts) {
1102
+ const words = text.toLowerCase().split(/\s+/);
1103
+ for (const word of words) {
1104
+ if (word.length > 3) {
1105
+ wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
1106
+ }
1107
+ }
1108
+ }
1109
+ const threshold = texts.length * 0.3;
1110
+ return Array.from(wordFreq.entries()).filter(([_, count]) => count >= threshold).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([word]) => word);
1111
+ }
1112
+ calculateOutcomeConsistency(experiences) {
1113
+ if (experiences.length === 0)
1114
+ return 0;
1115
+ const outcomeCounts = new Map;
1116
+ for (const exp of experiences) {
1117
+ outcomeCounts.set(exp.outcome, (outcomeCounts.get(exp.outcome) || 0) + 1);
1118
+ }
1119
+ const maxCount = Math.max(...outcomeCounts.values());
1120
+ return maxCount / experiences.length;
1121
+ }
1122
+ extractAlternatives(experiences) {
1123
+ const alternatives = new Set;
1124
+ for (const exp of experiences) {
1125
+ if (exp.type === "correction" /* CORRECTION */ && exp.correctedBelief) {
1126
+ alternatives.add(exp.correctedBelief);
1127
+ }
1128
+ if (exp.outcome === "negative" /* NEGATIVE */ && exp.learning.includes("instead")) {
1129
+ const match = exp.learning.match(/instead\s+(.+?)(?:\.|$)/i);
1130
+ const alternative = match?.[1]?.trim();
1131
+ if (alternative) {
1132
+ alternatives.add(alternative);
1133
+ }
1134
+ }
1135
+ }
1136
+ return Array.from(alternatives).slice(0, 5);
1137
+ }
1138
+ generateRecommendations(experiences, reliability) {
1139
+ const recommendations = [];
1140
+ if (reliability > 0.8) {
1141
+ recommendations.push("Continue using successful approaches");
1142
+ recommendations.push("Document and share these reliable methods");
1143
+ } else if (reliability > 0.6) {
1144
+ recommendations.push("Continue using successful approaches with caution");
1145
+ recommendations.push("Monitor for potential issues");
1146
+ recommendations.push("Consider backup strategies");
1147
+ } else if (reliability > 0.4) {
1148
+ recommendations.push("Review and improve current approaches");
1149
+ recommendations.push("Investigate failure patterns");
1150
+ recommendations.push("Consider alternative methods");
1151
+ } else {
1152
+ recommendations.push("Significant changes needed to current approach");
1153
+ recommendations.push("Analyze failure causes thoroughly");
1154
+ recommendations.push("Seek alternative solutions");
1155
+ }
1156
+ const failureTypes = new Map;
1157
+ experiences.filter((e) => e.outcome === "negative" /* NEGATIVE */).forEach((e) => {
1158
+ const key = e.learning.toLowerCase();
1159
+ failureTypes.set(key, (failureTypes.get(key) || 0) + 1);
1160
+ });
1161
+ if (failureTypes.size > 0) {
1162
+ const mostCommonFailure = Array.from(failureTypes.entries()).sort((a, b) => b[1] - a[1])[0];
1163
+ if (mostCommonFailure && mostCommonFailure[1] > 1) {
1164
+ recommendations.push(`Address recurring issue: ${mostCommonFailure[0]}`);
1165
+ }
1166
+ }
1167
+ const domains = new Set(experiences.map((e) => e.domain));
1168
+ if (domains.has("shell")) {
1169
+ recommendations.push("Verify command syntax and permissions");
1170
+ }
1171
+ if (domains.has("coding")) {
1172
+ recommendations.push("Test thoroughly before deployment");
1173
+ }
1174
+ if (domains.has("network")) {
1175
+ recommendations.push("Implement retry logic and error handling");
1176
+ }
1177
+ return recommendations.slice(0, 5);
1178
+ }
1179
+ async pruneOldExperiences() {
1180
+ if (this.experiences.size <= this.maxExperiences) {
1181
+ return;
1182
+ }
1183
+ const experienceArray = Array.from(this.experiences.values());
1184
+ experienceArray.sort((a, b) => {
1185
+ if (a.importance !== b.importance) {
1186
+ return a.importance - b.importance;
1187
+ }
1188
+ if (a.accessCount !== b.accessCount) {
1189
+ return a.accessCount - b.accessCount;
1190
+ }
1191
+ return a.createdAt - b.createdAt;
1192
+ });
1193
+ const toRemove = experienceArray.slice(0, experienceArray.length - this.maxExperiences);
1194
+ let removedCount = 0;
1195
+ for (const experience of toRemove) {
1196
+ this.experiences.delete(experience.id);
1197
+ const domainSet = this.experiencesByDomain.get(experience.domain);
1198
+ if (domainSet) {
1199
+ domainSet.delete(experience.id);
1200
+ if (domainSet.size === 0) {
1201
+ this.experiencesByDomain.delete(experience.domain);
1202
+ }
1203
+ }
1204
+ const typeSet = this.experiencesByType.get(experience.type);
1205
+ if (typeSet) {
1206
+ typeSet.delete(experience.id);
1207
+ if (typeSet.size === 0) {
1208
+ this.experiencesByType.delete(experience.type);
1209
+ }
1210
+ }
1211
+ removedCount++;
1212
+ }
1213
+ logger4.info(`[ExperienceService] Pruned ${removedCount} old experiences`);
1214
+ }
1215
+ async stop() {
1216
+ logger4.info("[ExperienceService] Stopping...");
1217
+ const experiencesToSave = Array.from(this.experiences.values());
1218
+ let savedCount = 0;
1219
+ for (const experience of experiencesToSave) {
1220
+ await this.saveExperienceToMemory(experience);
1221
+ savedCount++;
1222
+ }
1223
+ logger4.info(`[ExperienceService] Saved ${savedCount} experiences`);
1224
+ }
1225
+ }
1226
+ function getNumberSetting2(runtime, key, fallback) {
1227
+ const value = runtime.getSetting(key);
1228
+ if (typeof value === "number")
1229
+ return value;
1230
+ if (typeof value === "string") {
1231
+ const parsed = Number.parseFloat(value);
1232
+ return Number.isFinite(parsed) ? parsed : fallback;
1233
+ }
1234
+ return fallback;
1235
+ }
1236
+
1237
+ // index.ts
1238
+ var experiencePlugin = {
1239
+ name: "experience",
1240
+ description: "Self-learning experience system that records and recalls transferable agent learnings",
1241
+ actions: [recordExperienceAction],
1242
+ services: [ExperienceService],
1243
+ providers: [experienceProvider],
1244
+ evaluators: [experienceEvaluator],
1245
+ async init(config, runtime) {
1246
+ logger5.info("[ExperiencePlugin] Initializing experience learning system");
1247
+ const maxExperiences = parseOptionalNumber(config.MAX_EXPERIENCES, 1e4);
1248
+ const autoRecordThreshold = parseOptionalNumber(config.AUTO_RECORD_THRESHOLD, 0.7);
1249
+ runtime.setSetting("MAX_EXPERIENCES", maxExperiences.toString());
1250
+ runtime.setSetting("AUTO_RECORD_THRESHOLD", autoRecordThreshold.toString());
1251
+ const experienceService = runtime.getService("EXPERIENCE");
1252
+ experienceService?.setMaxExperiences(maxExperiences);
1253
+ logger5.info(`[ExperiencePlugin] Configuration:
1254
+ - MAX_EXPERIENCES: ${maxExperiences}
1255
+ - AUTO_RECORD_THRESHOLD: ${autoRecordThreshold}`);
1256
+ }
1257
+ };
1258
+ var typescript_default = experiencePlugin;
1259
+ function parseOptionalNumber(value, fallback) {
1260
+ if (!value)
1261
+ return fallback;
1262
+ const parsed = Number.parseFloat(value);
1263
+ return Number.isFinite(parsed) ? parsed : fallback;
1264
+ }
1265
+ export {
1266
+ experiencePlugin,
1267
+ typescript_default as default,
1268
+ OutcomeType,
1269
+ ExperienceType,
1270
+ ExperienceServiceType,
1271
+ ExperienceService
1272
+ };
1273
+
1274
+ //# debugId=332CCD4C6A890DF464756E2164756E21