@memberjunction/ai-agents 3.4.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +232 -2584
- package/dist/AgentDataPreloader.d.ts +117 -1
- package/dist/AgentDataPreloader.d.ts.map +1 -1
- package/dist/AgentDataPreloader.js +156 -58
- package/dist/AgentDataPreloader.js.map +1 -1
- package/dist/AgentRunner.d.ts +212 -0
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +354 -103
- package/dist/AgentRunner.js.map +1 -1
- package/dist/PayloadChangeAnalyzer.d.ts +68 -0
- package/dist/PayloadChangeAnalyzer.d.ts.map +1 -1
- package/dist/PayloadChangeAnalyzer.js +68 -32
- package/dist/PayloadChangeAnalyzer.js.map +1 -1
- package/dist/PayloadFeedbackManager.d.ts +57 -1
- package/dist/PayloadFeedbackManager.d.ts.map +1 -1
- package/dist/PayloadFeedbackManager.js +66 -21
- package/dist/PayloadFeedbackManager.js.map +1 -1
- package/dist/PayloadManager.d.ts +286 -1
- package/dist/PayloadManager.d.ts.map +1 -1
- package/dist/PayloadManager.js +423 -50
- package/dist/PayloadManager.js.map +1 -1
- package/dist/__tests__/action-changes.test.d.ts +13 -0
- package/dist/__tests__/action-changes.test.d.ts.map +1 -1
- package/dist/__tests__/action-changes.test.js +57 -4
- package/dist/__tests__/action-changes.test.js.map +1 -1
- package/dist/__tests__/agent-memory-features.test.d.ts +52 -0
- package/dist/__tests__/agent-memory-features.test.d.ts.map +1 -1
- package/dist/__tests__/agent-memory-features.test.js +96 -13
- package/dist/__tests__/agent-memory-features.test.js.map +1 -1
- package/dist/__tests__/agent-type-prompt-params.test.d.ts +12 -0
- package/dist/__tests__/agent-type-prompt-params.test.d.ts.map +1 -1
- package/dist/__tests__/agent-type-prompt-params.test.js +112 -20
- package/dist/__tests__/agent-type-prompt-params.test.js.map +1 -1
- package/dist/__tests__/chat-handling-option.test.d.ts +26 -0
- package/dist/__tests__/chat-handling-option.test.d.ts.map +1 -1
- package/dist/__tests__/chat-handling-option.test.js +41 -2
- package/dist/__tests__/chat-handling-option.test.js.map +1 -1
- package/dist/agent-context-injector.d.ts +114 -0
- package/dist/agent-context-injector.d.ts.map +1 -1
- package/dist/agent-context-injector.js +138 -19
- package/dist/agent-context-injector.js.map +1 -1
- package/dist/agent-types/base-agent-type.d.ts +354 -0
- package/dist/agent-types/base-agent-type.d.ts.map +1 -1
- package/dist/agent-types/base-agent-type.js +288 -17
- package/dist/agent-types/base-agent-type.js.map +1 -1
- package/dist/agent-types/flow-agent-type.d.ts +328 -2
- package/dist/agent-types/flow-agent-type.d.ts.map +1 -1
- package/dist/agent-types/flow-agent-type.js +503 -63
- package/dist/agent-types/flow-agent-type.js.map +1 -1
- package/dist/agent-types/index.d.ts +14 -3
- package/dist/agent-types/index.d.ts.map +1 -1
- package/dist/agent-types/index.js +14 -23
- package/dist/agent-types/index.js.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.d.ts +190 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +23 -6
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +62 -0
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js +3 -2
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts +139 -2
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +207 -35
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +1344 -1
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +2282 -211
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +24 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -35
- package/dist/index.js.map +1 -1
- package/dist/memory-cleanup-agent.d.ts +53 -1
- package/dist/memory-cleanup-agent.d.ts.map +1 -1
- package/dist/memory-cleanup-agent.js +89 -21
- package/dist/memory-cleanup-agent.js.map +1 -1
- package/dist/memory-manager-agent.d.ts +61 -1
- package/dist/memory-manager-agent.d.ts.map +1 -1
- package/dist/memory-manager-agent.js +260 -116
- package/dist/memory-manager-agent.js.map +1 -1
- package/dist/types/payload-operations.d.ts +51 -0
- package/dist/types/payload-operations.d.ts.map +1 -1
- package/dist/types/payload-operations.js +54 -15
- package/dist/types/payload-operations.js.map +1 -1
- package/dist/utils/ConversationMessageResolver.d.ts +79 -1
- package/dist/utils/ConversationMessageResolver.d.ts.map +1 -1
- package/dist/utils/ConversationMessageResolver.js +99 -9
- package/dist/utils/ConversationMessageResolver.js.map +1 -1
- package/package.json +20 -19
|
@@ -1,40 +1,53 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
6
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
import { BaseAgent } from './base-agent.js';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { Metadata, RunView, RunQuery, LogError, LogStatus } from '@memberjunction/core';
|
|
10
|
+
import { AIPromptRunner } from '@memberjunction/ai-prompts';
|
|
11
|
+
import { AIPromptParams } from '@memberjunction/ai-core-plus';
|
|
12
|
+
import { AIEngine } from '@memberjunction/aiengine';
|
|
13
|
+
/**
|
|
14
|
+
* Configuration for extraction limits to ensure sparsity
|
|
15
|
+
*/
|
|
16
16
|
const EXTRACTION_CONFIG = {
|
|
17
|
-
maxNotesPerRun: 1000,
|
|
18
|
-
maxNotesPerConversation: 1000,
|
|
19
|
-
maxNotesPerMessage: 1000,
|
|
20
|
-
minConfidenceThreshold: 80,
|
|
21
|
-
minContentLength: 10,
|
|
22
|
-
cooldownHours: 24
|
|
17
|
+
maxNotesPerRun: 1000, // Max new notes per agent run
|
|
18
|
+
maxNotesPerConversation: 1000, // Max notes from a single conversation
|
|
19
|
+
maxNotesPerMessage: 1000, // Max notes from a single message
|
|
20
|
+
minConfidenceThreshold: 80, // Minimum confidence to extract a note
|
|
21
|
+
minContentLength: 10, // Minimum note content length (lowered from 20 to allow short names)
|
|
22
|
+
cooldownHours: 24 // Don't re-extract from same conversation within 24h
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Memory Manager Agent - automatically extracts notes and examples from conversations.
|
|
26
|
+
* This agent runs on a schedule (every 15 minutes) and analyzes high-quality conversations
|
|
27
|
+
* and agent runs to extract learnings and example interactions.
|
|
28
|
+
*/
|
|
29
|
+
let MemoryManagerAgent = class MemoryManagerAgent extends BaseAgent {
|
|
25
30
|
constructor() {
|
|
26
31
|
super(...arguments);
|
|
32
|
+
/** Verbose logging flag from params.verbose */
|
|
27
33
|
this._verbose = false;
|
|
34
|
+
/** Agent run ID for creating run steps */
|
|
28
35
|
this._agentRunID = null;
|
|
36
|
+
/** Step counter for sequential step numbering */
|
|
29
37
|
this._stepCounter = 0;
|
|
38
|
+
/** Context user for step operations */
|
|
30
39
|
this._contextUser = null;
|
|
31
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Create an agent run step record for observability.
|
|
43
|
+
* Returns null if agentRunID is not set (defensive check).
|
|
44
|
+
*/
|
|
32
45
|
async CreateRunStep(stepType, stepName, inputData, targetId) {
|
|
33
46
|
if (!this._agentRunID || !this._contextUser) {
|
|
34
47
|
return null;
|
|
35
48
|
}
|
|
36
49
|
try {
|
|
37
|
-
const md = new
|
|
50
|
+
const md = new Metadata();
|
|
38
51
|
const step = await md.GetEntityObject('MJ: AI Agent Run Steps', this._contextUser);
|
|
39
52
|
step.AgentRunID = this._agentRunID;
|
|
40
53
|
step.StepNumber = ++this._stepCounter;
|
|
@@ -52,15 +65,18 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
52
65
|
return step;
|
|
53
66
|
}
|
|
54
67
|
else {
|
|
55
|
-
|
|
68
|
+
LogError(`Memory Manager: Failed to create run step: ${JSON.stringify(step.LatestResult)}`);
|
|
56
69
|
return null;
|
|
57
70
|
}
|
|
58
71
|
}
|
|
59
72
|
catch (error) {
|
|
60
|
-
|
|
73
|
+
LogError('Memory Manager: Exception creating run step:', error);
|
|
61
74
|
return null;
|
|
62
75
|
}
|
|
63
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Finalize an agent run step with success/failure status and output data.
|
|
79
|
+
*/
|
|
64
80
|
async FinalizeRunStep(step, success, outputData, targetLogId, errorMessage) {
|
|
65
81
|
if (!step) {
|
|
66
82
|
return;
|
|
@@ -79,15 +95,19 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
79
95
|
step.OutputData = JSON.stringify(outputData);
|
|
80
96
|
}
|
|
81
97
|
if (!await step.Save()) {
|
|
82
|
-
|
|
98
|
+
LogError(`Memory Manager: Failed to finalize run step: ${JSON.stringify(step.LatestResult)}`);
|
|
83
99
|
}
|
|
84
100
|
}
|
|
85
101
|
catch (error) {
|
|
86
|
-
|
|
102
|
+
LogError('Memory Manager: Exception finalizing run step:', error);
|
|
87
103
|
}
|
|
88
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the last run timestamp for this agent to determine what to process.
|
|
107
|
+
* For first run, returns null to process all history (limited by MaxRows).
|
|
108
|
+
*/
|
|
89
109
|
async GetLastRunTime(agentId, contextUser) {
|
|
90
|
-
const rv = new
|
|
110
|
+
const rv = new RunView();
|
|
91
111
|
const result = await rv.RunView({
|
|
92
112
|
EntityName: 'MJ: AI Agent Runs',
|
|
93
113
|
ExtraFilter: `AgentID='${agentId}' AND Status='Completed'`,
|
|
@@ -96,38 +116,52 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
96
116
|
ResultType: 'entity_object'
|
|
97
117
|
}, contextUser);
|
|
98
118
|
if (result.Success && result.Results && result.Results.length > 0) {
|
|
119
|
+
// Ensure we return a proper Date object
|
|
99
120
|
const startedAt = result.Results[0].StartedAt;
|
|
100
121
|
return startedAt instanceof Date ? startedAt : new Date(startedAt);
|
|
101
122
|
}
|
|
123
|
+
// First run - return null to process all history (with MaxRows limit)
|
|
102
124
|
return null;
|
|
103
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Load agents that have note or example injection enabled.
|
|
128
|
+
* Only extract notes/examples for agents that actually use these features.
|
|
129
|
+
*/
|
|
104
130
|
async LoadAgentsUsingMemory(contextUser) {
|
|
105
|
-
const allAgents =
|
|
131
|
+
const allAgents = AIEngine.Instance.Agents;
|
|
106
132
|
const filteredAgents = allAgents.filter(a => a.Status === 'Active' && (a.InjectNotes || a.InjectExamples));
|
|
133
|
+
// Debug logging only in verbose mode
|
|
107
134
|
if (this._verbose) {
|
|
108
|
-
|
|
135
|
+
LogStatus(`Memory Manager: AIEngine has ${allAgents.length} total agents cached`);
|
|
109
136
|
if (filteredAgents.length > 0) {
|
|
110
137
|
const agentNames = filteredAgents.map(a => `${a.Name} (InjectNotes=${a.InjectNotes}, InjectExamples=${a.InjectExamples})`).join(', ');
|
|
111
|
-
|
|
138
|
+
LogStatus(`Memory Manager: Agents with memory enabled: ${agentNames}`);
|
|
112
139
|
}
|
|
113
140
|
const sage = allAgents.find(a => a.Name === 'Sage');
|
|
114
141
|
if (sage) {
|
|
115
|
-
|
|
142
|
+
LogStatus(`Memory Manager: Sage agent - ID=${sage.ID}, InjectNotes=${sage.InjectNotes}, InjectExamples=${sage.InjectExamples}, Status=${sage.Status}`);
|
|
116
143
|
}
|
|
117
144
|
}
|
|
145
|
+
// Warning: Always log if Sage is missing
|
|
118
146
|
const sage = allAgents.find(a => a.Name === 'Sage');
|
|
119
147
|
if (!sage) {
|
|
120
|
-
|
|
148
|
+
LogStatus(`Memory Manager: WARNING - Sage agent not found in AIEngine cache!`);
|
|
121
149
|
}
|
|
122
150
|
return filteredAgents;
|
|
123
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Load conversations with new activity since last run, including rating data.
|
|
154
|
+
* Uses a single optimized RunQuery that replaces 4 separate database queries.
|
|
155
|
+
* Returns conversations with their details, ratings, and agent run IDs for scope inheritance.
|
|
156
|
+
*/
|
|
124
157
|
async LoadConversationsWithNewActivity(since, agentsUsingMemory, contextUser) {
|
|
125
158
|
if (agentsUsingMemory.length === 0) {
|
|
126
|
-
|
|
159
|
+
LogStatus('Memory Manager: No agents have memory injection enabled - skipping');
|
|
127
160
|
return [];
|
|
128
161
|
}
|
|
129
162
|
const agentIds = agentsUsingMemory.map(a => `'${a.ID}'`).join(',');
|
|
130
|
-
|
|
163
|
+
// Use RunQuery to fetch all data in a single optimized query
|
|
164
|
+
const rq = new RunQuery();
|
|
131
165
|
const result = await rq.RunQuery({
|
|
132
166
|
QueryName: 'GetConversationsForMemoryManager',
|
|
133
167
|
CategoryPath: '/MJ/AI/Agents/',
|
|
@@ -135,14 +169,16 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
135
169
|
since: since?.toISOString() || null,
|
|
136
170
|
agentIds: agentIds
|
|
137
171
|
},
|
|
138
|
-
MaxRows: 1000
|
|
172
|
+
MaxRows: 1000 // Limit to 1000 conversations per run
|
|
139
173
|
}, contextUser);
|
|
140
174
|
if (!result.Success || !result.Results || result.Results.length === 0) {
|
|
141
|
-
|
|
175
|
+
LogStatus('Memory Manager: No conversations with new activity found');
|
|
142
176
|
return [];
|
|
143
177
|
}
|
|
178
|
+
// Parse the query results into ConversationWithRatings objects
|
|
144
179
|
const conversations = [];
|
|
145
180
|
for (const row of result.Results) {
|
|
181
|
+
// Parse the MessagesJSON from the query result
|
|
146
182
|
let messages = [];
|
|
147
183
|
if (row.MessagesJSON) {
|
|
148
184
|
try {
|
|
@@ -157,7 +193,7 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
157
193
|
}));
|
|
158
194
|
}
|
|
159
195
|
catch (e) {
|
|
160
|
-
|
|
196
|
+
LogError(`Memory Manager: Failed to parse MessagesJSON for conversation ${row.ConversationID}:`, e);
|
|
161
197
|
continue;
|
|
162
198
|
}
|
|
163
199
|
}
|
|
@@ -173,12 +209,17 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
173
209
|
}
|
|
174
210
|
const totalMessages = conversations.reduce((sum, c) => sum + c.messages.length, 0);
|
|
175
211
|
if (this._verbose) {
|
|
176
|
-
|
|
212
|
+
LogStatus(`Memory Manager: Loaded ${conversations.length} conversations with ${totalMessages} total messages (positive: ${conversations.filter(c => c.hasPositiveRating).length}, negative: ${conversations.filter(c => c.hasNegativeRating).length}, unrated: ${conversations.filter(c => c.isUnrated).length})`);
|
|
177
213
|
}
|
|
178
214
|
return conversations;
|
|
179
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Load agent runs with high-usage artifacts since last run.
|
|
218
|
+
* Links through: ArtifactUse -> ArtifactVersion -> ConversationDetailArtifact -> ConversationDetail -> Conversation -> AIAgentRun
|
|
219
|
+
*/
|
|
180
220
|
async LoadHighValueAgentRuns(since, contextUser) {
|
|
181
|
-
const rv = new
|
|
221
|
+
const rv = new RunView();
|
|
222
|
+
// Use subquery to find agent runs with high-usage artifacts
|
|
182
223
|
const sinceFilter = since ? `AND au.__mj_CreatedAt >= '${since.toISOString()}'` : '';
|
|
183
224
|
const filter = `
|
|
184
225
|
ID IN (
|
|
@@ -205,21 +246,27 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
205
246
|
EntityName: 'MJ: AI Agent Runs',
|
|
206
247
|
ExtraFilter: filter,
|
|
207
248
|
OrderBy: '__mj_CreatedAt DESC',
|
|
208
|
-
MaxRows: 50,
|
|
249
|
+
MaxRows: 50, // Limit to most recent 50 high-value runs
|
|
209
250
|
ResultType: 'entity_object'
|
|
210
251
|
}, contextUser);
|
|
211
252
|
return result.Success ? (result.Results || []) : [];
|
|
212
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Extract notes from conversations with rating data using AI analysis.
|
|
256
|
+
* This is the primary method that handles rated, unrated, positive, and negative feedback.
|
|
257
|
+
* Uses LLM-based deduplication and applies sparsity controls.
|
|
258
|
+
*/
|
|
213
259
|
async ExtractNotesFromConversations(conversations, contextUser) {
|
|
214
260
|
if (conversations.length === 0) {
|
|
215
261
|
return [];
|
|
216
262
|
}
|
|
217
|
-
const allNotes =
|
|
263
|
+
const allNotes = AIEngine.Instance.AgentNotes;
|
|
218
264
|
const existingNotes = allNotes.filter(n => n.Status === 'Active');
|
|
265
|
+
// Prepare conversation threads with rating data and user context
|
|
219
266
|
const conversationThreads = conversations.map(conv => ({
|
|
220
267
|
conversationId: conv.conversationId,
|
|
221
|
-
userId: conv.userId,
|
|
222
|
-
agentRunId: conv.agentRunId,
|
|
268
|
+
userId: conv.userId, // User who owns the conversation - for scoping
|
|
269
|
+
agentRunId: conv.agentRunId, // Linked agent run - for scope inheritance
|
|
223
270
|
hasPositiveRating: conv.hasPositiveRating,
|
|
224
271
|
hasNegativeRating: conv.hasNegativeRating,
|
|
225
272
|
isUnrated: conv.isUnrated,
|
|
@@ -245,88 +292,100 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
245
292
|
};
|
|
246
293
|
return this.executeNoteExtraction(promptData, existingNotes, contextUser);
|
|
247
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Common extraction logic for note extraction.
|
|
297
|
+
* Applies sparsity controls and deduplication.
|
|
298
|
+
*/
|
|
248
299
|
async executeNoteExtraction(promptData, existingNotes, contextUser) {
|
|
249
300
|
const conversationThreads = promptData.conversationThreads;
|
|
250
|
-
|
|
301
|
+
// Find extraction prompt
|
|
302
|
+
const prompt = AIEngine.Instance.Prompts.find(p => p.Name === 'Memory Manager - Extract Notes' && p.Category === 'MJ: System');
|
|
251
303
|
if (!prompt) {
|
|
252
|
-
|
|
304
|
+
LogError('Memory Manager note extraction prompt not found');
|
|
253
305
|
if (this._verbose) {
|
|
254
|
-
|
|
306
|
+
LogStatus(`Memory Manager: Available prompts in MJ: System category: ${AIEngine.Instance.Prompts.filter(p => p.Category === 'MJ: System').map(p => p.Name).join(', ')}`);
|
|
255
307
|
}
|
|
256
308
|
return [];
|
|
257
309
|
}
|
|
258
310
|
if (this._verbose) {
|
|
259
|
-
|
|
260
|
-
|
|
311
|
+
LogStatus(`Memory Manager: Found extraction prompt "${prompt.Name}" (ID: ${prompt.ID})`);
|
|
312
|
+
LogStatus(`Memory Manager: Sending ${conversationThreads.length} conversation threads with ${conversationThreads.reduce((sum, t) => sum + t.messages.length, 0)} total messages for note extraction`);
|
|
261
313
|
if (conversationThreads.length > 0) {
|
|
262
314
|
const firstThread = conversationThreads[0];
|
|
263
|
-
|
|
315
|
+
LogStatus(`Memory Manager: Sample thread (conv ${firstThread.conversationId}): ${firstThread.messages.map(m => `[${m.role}] ${m.message?.substring(0, 80)}...`).join(' | ')}`);
|
|
264
316
|
}
|
|
265
317
|
}
|
|
318
|
+
// Step 3: Execute AI extraction
|
|
266
319
|
const step3 = await this.CreateRunStep('Prompt', 'Extract Notes from Conversations', {
|
|
267
320
|
conversationCount: conversationThreads.length,
|
|
268
321
|
messageCount: conversationThreads.reduce((sum, t) => sum + t.messages.length, 0),
|
|
269
322
|
existingNoteCount: existingNotes.length
|
|
270
323
|
}, prompt.ID);
|
|
271
|
-
const runner = new
|
|
272
|
-
const params = new
|
|
324
|
+
const runner = new AIPromptRunner();
|
|
325
|
+
const params = new AIPromptParams();
|
|
273
326
|
params.prompt = prompt;
|
|
274
327
|
params.data = promptData;
|
|
275
328
|
params.contextUser = contextUser;
|
|
276
329
|
const result = await runner.ExecutePrompt(params);
|
|
330
|
+
// Finalize Step 3 after extraction
|
|
277
331
|
await this.FinalizeRunStep(step3, result.success, {
|
|
278
332
|
success: result.success,
|
|
279
333
|
rawNoteCount: result.result && typeof result.result !== 'string' ? (result.result.notes?.length || 0) : 0
|
|
280
334
|
}, result.promptRun?.ID, result.errorMessage || undefined);
|
|
281
335
|
if (this._verbose) {
|
|
282
|
-
|
|
336
|
+
LogStatus(`Memory Manager: Extraction prompt result - success: ${result.success}, hasResult: ${!!result.result}`);
|
|
283
337
|
if (result.errorMessage) {
|
|
284
|
-
|
|
338
|
+
LogStatus(`Memory Manager: Extraction error: ${result.errorMessage}`);
|
|
285
339
|
}
|
|
286
340
|
if (result.result) {
|
|
287
|
-
|
|
341
|
+
LogStatus(`Memory Manager: Raw extraction result: ${JSON.stringify(result.result)}`);
|
|
288
342
|
}
|
|
289
343
|
}
|
|
290
344
|
if (!result.success || !result.result) {
|
|
291
|
-
|
|
345
|
+
LogError('Failed to extract notes:', result.errorMessage);
|
|
292
346
|
return [];
|
|
293
347
|
}
|
|
348
|
+
// Parse result if it's a string (AI sometimes returns JSON as string)
|
|
294
349
|
let parsedResult;
|
|
295
350
|
if (typeof result.result === 'string') {
|
|
296
351
|
try {
|
|
297
352
|
parsedResult = JSON.parse(result.result);
|
|
298
353
|
if (this._verbose) {
|
|
299
|
-
|
|
354
|
+
LogStatus(`Memory Manager: Parsed string result into object with ${parsedResult.notes?.length || 0} notes`);
|
|
300
355
|
}
|
|
301
356
|
}
|
|
302
357
|
catch (e) {
|
|
303
|
-
|
|
358
|
+
LogError('Failed to parse extraction result as JSON:', e);
|
|
304
359
|
return [];
|
|
305
360
|
}
|
|
306
361
|
}
|
|
307
362
|
else {
|
|
308
363
|
parsedResult = result.result;
|
|
309
364
|
}
|
|
365
|
+
// Log raw notes before filtering (for debugging)
|
|
310
366
|
if (this._verbose && parsedResult.notes) {
|
|
311
|
-
|
|
367
|
+
LogStatus(`Memory Manager: LLM returned ${parsedResult.notes.length} raw notes before filtering`);
|
|
312
368
|
for (const note of parsedResult.notes) {
|
|
313
|
-
|
|
369
|
+
LogStatus(`Memory Manager: Raw note: [${note.type}] "${note.content}" (confidence: ${note.confidence})`);
|
|
314
370
|
}
|
|
315
371
|
}
|
|
372
|
+
// Filter by confidence threshold (use EXTRACTION_CONFIG)
|
|
316
373
|
const candidateNotes = (parsedResult.notes || []).filter(n => n.confidence >= EXTRACTION_CONFIG.minConfidenceThreshold &&
|
|
317
374
|
n.content && n.content.length >= EXTRACTION_CONFIG.minContentLength);
|
|
318
375
|
if (candidateNotes.length === 0) {
|
|
319
376
|
if (this._verbose) {
|
|
320
|
-
|
|
377
|
+
LogStatus('Memory Manager: No candidates passed confidence/length thresholds');
|
|
321
378
|
}
|
|
322
379
|
return [];
|
|
323
380
|
}
|
|
324
381
|
if (this._verbose) {
|
|
325
|
-
|
|
382
|
+
LogStatus(`Memory Manager: ${candidateNotes.length} candidates passed confidence threshold (>=${EXTRACTION_CONFIG.minConfidenceThreshold})`);
|
|
383
|
+
// Log each extracted note for debugging
|
|
326
384
|
for (const note of candidateNotes) {
|
|
327
|
-
|
|
385
|
+
LogStatus(`Memory Manager: Extracted note: [${note.type}] "${note.content}" (confidence: ${note.confidence})`);
|
|
328
386
|
}
|
|
329
387
|
}
|
|
388
|
+
// Apply sparsity: limit notes per message AND per conversation
|
|
330
389
|
const notesByMessage = new Map();
|
|
331
390
|
const noteCountByConversation = new Map();
|
|
332
391
|
for (const note of candidateNotes) {
|
|
@@ -334,14 +393,15 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
334
393
|
const convId = note.sourceConversationId || 'unknown';
|
|
335
394
|
const existingForMsg = notesByMessage.get(msgId) || [];
|
|
336
395
|
const convCount = noteCountByConversation.get(convId) || 0;
|
|
396
|
+
// Check both per-message and per-conversation limits
|
|
337
397
|
if (existingForMsg.length >= EXTRACTION_CONFIG.maxNotesPerMessage) {
|
|
338
398
|
if (this._verbose) {
|
|
339
|
-
|
|
399
|
+
LogStatus(`Memory Manager: Skipping note (max ${EXTRACTION_CONFIG.maxNotesPerMessage} per message reached)`);
|
|
340
400
|
}
|
|
341
401
|
}
|
|
342
402
|
else if (convCount >= EXTRACTION_CONFIG.maxNotesPerConversation) {
|
|
343
403
|
if (this._verbose) {
|
|
344
|
-
|
|
404
|
+
LogStatus(`Memory Manager: Skipping note (max ${EXTRACTION_CONFIG.maxNotesPerConversation} per conversation reached)`);
|
|
345
405
|
}
|
|
346
406
|
}
|
|
347
407
|
else {
|
|
@@ -352,8 +412,9 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
352
412
|
}
|
|
353
413
|
const sparseCandidates = Array.from(notesByMessage.values()).flat();
|
|
354
414
|
if (this._verbose) {
|
|
355
|
-
|
|
415
|
+
LogStatus(`Memory Manager: ${sparseCandidates.length} candidates after sparsity filter`);
|
|
356
416
|
}
|
|
417
|
+
// Step 4: Apply deduplication (summary step)
|
|
357
418
|
const step4 = await this.CreateRunStep('Decision', 'Deduplicate Note Candidates', {
|
|
358
419
|
candidateCount: sparseCandidates.length,
|
|
359
420
|
existingNoteCount: existingNotes.length
|
|
@@ -361,26 +422,33 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
361
422
|
const approvedNotes = [];
|
|
362
423
|
let dedupeRejectedCount = 0;
|
|
363
424
|
let dedupeLlmCallCount = 0;
|
|
364
|
-
|
|
425
|
+
// Find deduplication prompt
|
|
426
|
+
const dedupePrompt = AIEngine.Instance.Prompts.find(p => p.Name === 'Memory Manager - Deduplicate Note' && p.Category === 'MJ: System');
|
|
365
427
|
for (const candidate of sparseCandidates) {
|
|
428
|
+
// Check global max notes per run
|
|
366
429
|
if (approvedNotes.length >= EXTRACTION_CONFIG.maxNotesPerRun) {
|
|
367
430
|
if (this._verbose) {
|
|
368
|
-
|
|
431
|
+
LogStatus(`Memory Manager: Stopping - max notes per run (${EXTRACTION_CONFIG.maxNotesPerRun}) reached`);
|
|
369
432
|
}
|
|
370
433
|
break;
|
|
371
434
|
}
|
|
435
|
+
// Step 1: Check for exact content match FIRST (case-insensitive, trimmed)
|
|
372
436
|
const normalizedContent = candidate.content.toLowerCase().trim();
|
|
373
437
|
const exactMatch = existingNotes.find(n => n.Note && n.Note.toLowerCase().trim() === normalizedContent);
|
|
374
438
|
if (exactMatch) {
|
|
375
439
|
dedupeRejectedCount++;
|
|
376
440
|
if (this._verbose) {
|
|
377
|
-
|
|
441
|
+
LogStatus(`Memory Manager: Skipping exact duplicate: "${candidate.content.substring(0, 50)}..."`);
|
|
378
442
|
}
|
|
379
443
|
continue;
|
|
380
444
|
}
|
|
381
|
-
|
|
445
|
+
// Step 2: Find similar existing notes using semantic search with tighter threshold
|
|
446
|
+
const similarNotes = await AIEngine.Instance.FindSimilarAgentNotes(candidate.content, candidate.agentId, candidate.userId, candidate.companyId, 10, // Top 10 similar (increased from 5)
|
|
447
|
+
0.85 // 85% similarity threshold (increased from 70%)
|
|
448
|
+
);
|
|
449
|
+
// Step 3: If deduplication prompt exists and similar notes found, ask LLM
|
|
382
450
|
if (dedupePrompt && similarNotes.length > 0) {
|
|
383
|
-
const dedupeParams = new
|
|
451
|
+
const dedupeParams = new AIPromptParams();
|
|
384
452
|
dedupeParams.prompt = dedupePrompt;
|
|
385
453
|
dedupeParams.data = {
|
|
386
454
|
candidateNote: candidate,
|
|
@@ -399,31 +467,35 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
399
467
|
if (dedupeResult.success && dedupeResult.result && dedupeResult.result.shouldAdd) {
|
|
400
468
|
approvedNotes.push(candidate);
|
|
401
469
|
if (this._verbose) {
|
|
402
|
-
|
|
470
|
+
LogStatus(`Memory Manager: Approved note - ${dedupeResult.result.reason}`);
|
|
403
471
|
}
|
|
404
472
|
}
|
|
405
473
|
else {
|
|
406
474
|
dedupeRejectedCount++;
|
|
407
475
|
if (this._verbose) {
|
|
408
|
-
|
|
476
|
+
LogStatus(`Memory Manager: Skipped duplicate note - ${dedupeResult.result?.reason || 'too similar to existing notes'}`);
|
|
409
477
|
}
|
|
410
478
|
}
|
|
411
479
|
}
|
|
412
480
|
else {
|
|
481
|
+
// No similar notes found or no deduplication prompt, add the note
|
|
413
482
|
approvedNotes.push(candidate);
|
|
414
483
|
if (this._verbose) {
|
|
415
|
-
|
|
484
|
+
LogStatus(`Memory Manager: Approved note (no similar notes found): "${candidate.content.substring(0, 50)}..."`);
|
|
416
485
|
}
|
|
417
486
|
}
|
|
418
487
|
}
|
|
488
|
+
// Finalize Step 4 after deduplication loop
|
|
419
489
|
await this.FinalizeRunStep(step4, true, {
|
|
420
490
|
approvedCount: approvedNotes.length,
|
|
421
491
|
rejectedCount: dedupeRejectedCount,
|
|
422
492
|
llmCallCount: dedupeLlmCallCount
|
|
423
493
|
});
|
|
424
494
|
if (this._verbose) {
|
|
425
|
-
|
|
495
|
+
LogStatus(`Memory Manager: Final approved notes: ${approvedNotes.length}`);
|
|
426
496
|
}
|
|
497
|
+
// Enrich notes with userId and agentRunId from conversation context
|
|
498
|
+
// Build a map of conversationId -> context for quick lookup
|
|
427
499
|
const conversationContext = new Map();
|
|
428
500
|
for (const thread of conversationThreads) {
|
|
429
501
|
conversationContext.set(thread.conversationId, {
|
|
@@ -431,13 +503,16 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
431
503
|
agentRunId: thread.agentRunId || null
|
|
432
504
|
});
|
|
433
505
|
}
|
|
506
|
+
// Enrich each note with conversation context
|
|
434
507
|
for (const note of approvedNotes) {
|
|
435
508
|
if (note.sourceConversationId) {
|
|
436
509
|
const ctx = conversationContext.get(note.sourceConversationId);
|
|
437
510
|
if (ctx) {
|
|
511
|
+
// Set userId from conversation if not already set
|
|
438
512
|
if (!note.userId && ctx.userId) {
|
|
439
513
|
note.userId = ctx.userId;
|
|
440
514
|
}
|
|
515
|
+
// Set sourceAgentRunId from conversation if not already set
|
|
441
516
|
if (!note.sourceAgentRunId && ctx.agentRunId) {
|
|
442
517
|
note.sourceAgentRunId = ctx.agentRunId;
|
|
443
518
|
}
|
|
@@ -446,10 +521,15 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
446
521
|
}
|
|
447
522
|
return approvedNotes;
|
|
448
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Extract examples from conversation details with high ratings.
|
|
526
|
+
* Uses LLM-based deduplication to avoid adding redundant examples.
|
|
527
|
+
*/
|
|
449
528
|
async ExtractExamples(conversationDetails, contextUser) {
|
|
450
529
|
if (conversationDetails.length === 0) {
|
|
451
530
|
return [];
|
|
452
531
|
}
|
|
532
|
+
// Prepare Q&A pairs from conversation details
|
|
453
533
|
const qaPairs = conversationDetails.map(detail => ({
|
|
454
534
|
id: detail.ID,
|
|
455
535
|
conversationId: detail.ConversationID,
|
|
@@ -457,16 +537,18 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
457
537
|
message: detail.Message,
|
|
458
538
|
createdAt: detail.__mj_CreatedAt
|
|
459
539
|
}));
|
|
460
|
-
|
|
540
|
+
// Find extraction prompt
|
|
541
|
+
const extractPrompt = AIEngine.Instance.Prompts.find(p => p.Name === 'Memory Manager - Extract Examples' && p.Category === 'MJ: System');
|
|
461
542
|
if (!extractPrompt) {
|
|
462
|
-
|
|
543
|
+
LogError('Memory Manager example extraction prompt not found');
|
|
463
544
|
return [];
|
|
464
545
|
}
|
|
546
|
+
// Step 5: Execute AI extraction
|
|
465
547
|
const step5 = await this.CreateRunStep('Prompt', 'Extract Examples from Conversations', {
|
|
466
548
|
qaPairCount: qaPairs.length
|
|
467
549
|
}, extractPrompt.ID);
|
|
468
|
-
const runner = new
|
|
469
|
-
const extractParams = new
|
|
550
|
+
const runner = new AIPromptRunner();
|
|
551
|
+
const extractParams = new AIPromptParams();
|
|
470
552
|
extractParams.prompt = extractPrompt;
|
|
471
553
|
extractParams.data = { qaPairs };
|
|
472
554
|
extractParams.contextUser = contextUser;
|
|
@@ -475,16 +557,17 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
475
557
|
await this.FinalizeRunStep(step5, false, {
|
|
476
558
|
success: false
|
|
477
559
|
}, extractResult.promptRun?.ID, extractResult.errorMessage || undefined);
|
|
478
|
-
|
|
560
|
+
LogError('Failed to extract examples:', extractResult.errorMessage);
|
|
479
561
|
return [];
|
|
480
562
|
}
|
|
563
|
+
// Parse result if it's a string (AI sometimes returns JSON as string)
|
|
481
564
|
let parsedResult;
|
|
482
565
|
if (typeof extractResult.result === 'string') {
|
|
483
566
|
try {
|
|
484
567
|
parsedResult = JSON.parse(extractResult.result);
|
|
485
568
|
}
|
|
486
569
|
catch (e) {
|
|
487
|
-
|
|
570
|
+
LogError('Failed to parse example extraction result as JSON:', e);
|
|
488
571
|
return [];
|
|
489
572
|
}
|
|
490
573
|
}
|
|
@@ -493,6 +576,7 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
493
576
|
}
|
|
494
577
|
const candidateExamples = (parsedResult.examples || [])
|
|
495
578
|
.filter(e => e.successScore >= 70 && e.confidence >= 70);
|
|
579
|
+
// Finalize Step 5 after extraction parsing
|
|
496
580
|
await this.FinalizeRunStep(step5, true, {
|
|
497
581
|
rawExampleCount: parsedResult.examples?.length || 0,
|
|
498
582
|
candidateCount: candidateExamples.length
|
|
@@ -500,17 +584,23 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
500
584
|
if (candidateExamples.length === 0) {
|
|
501
585
|
return [];
|
|
502
586
|
}
|
|
587
|
+
// Step 6: Deduplicate example candidates (summary step)
|
|
503
588
|
const step6 = await this.CreateRunStep('Decision', 'Deduplicate Example Candidates', {
|
|
504
589
|
candidateCount: candidateExamples.length
|
|
505
590
|
});
|
|
506
591
|
const approvedExamples = [];
|
|
507
592
|
let exampleDedupeRejectedCount = 0;
|
|
508
593
|
let exampleDedupeLlmCallCount = 0;
|
|
594
|
+
// Process each candidate example with LLM-based deduplication
|
|
509
595
|
for (const candidate of candidateExamples) {
|
|
510
|
-
|
|
511
|
-
const
|
|
596
|
+
// Find similar existing examples using semantic search
|
|
597
|
+
const similarExamples = await AIEngine.Instance.FindSimilarAgentExamples(candidate.exampleInput, candidate.agentId, candidate.userId, candidate.companyId, 5, // Top 5 similar
|
|
598
|
+
0.7 // 70% similarity threshold
|
|
599
|
+
);
|
|
600
|
+
// Ask LLM if this candidate adds value given existing similar examples
|
|
601
|
+
const dedupePrompt = AIEngine.Instance.Prompts.find(p => p.Name === 'Memory Manager - Deduplicate Example' && p.Category === 'MJ: System');
|
|
512
602
|
if (dedupePrompt && similarExamples.length > 0) {
|
|
513
|
-
const dedupeParams = new
|
|
603
|
+
const dedupeParams = new AIPromptParams();
|
|
514
604
|
dedupeParams.prompt = dedupePrompt;
|
|
515
605
|
dedupeParams.data = {
|
|
516
606
|
candidateExample: candidate,
|
|
@@ -527,20 +617,22 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
527
617
|
if (dedupeResult.success && dedupeResult.result && dedupeResult.result.shouldAdd) {
|
|
528
618
|
approvedExamples.push(candidate);
|
|
529
619
|
if (this._verbose) {
|
|
530
|
-
|
|
620
|
+
LogStatus(`Memory Manager: Approved example - ${dedupeResult.result.reason}`);
|
|
531
621
|
}
|
|
532
622
|
}
|
|
533
623
|
else {
|
|
534
624
|
exampleDedupeRejectedCount++;
|
|
535
625
|
if (this._verbose) {
|
|
536
|
-
|
|
626
|
+
LogStatus(`Memory Manager: Skipped duplicate example - ${dedupeResult.result?.reason || 'too similar'}`);
|
|
537
627
|
}
|
|
538
628
|
}
|
|
539
629
|
}
|
|
540
630
|
else {
|
|
631
|
+
// No similar examples found, add it
|
|
541
632
|
approvedExamples.push(candidate);
|
|
542
633
|
}
|
|
543
634
|
}
|
|
635
|
+
// Finalize Step 6 after deduplication loop
|
|
544
636
|
await this.FinalizeRunStep(step6, true, {
|
|
545
637
|
approvedCount: approvedExamples.length,
|
|
546
638
|
rejectedCount: exampleDedupeRejectedCount,
|
|
@@ -548,23 +640,33 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
548
640
|
});
|
|
549
641
|
return approvedExamples;
|
|
550
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Check if a string is a valid UUID format.
|
|
645
|
+
*/
|
|
551
646
|
isValidUUID(str) {
|
|
552
647
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
553
648
|
return uuidRegex.test(str);
|
|
554
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Create note records from extracted data.
|
|
652
|
+
* Inherits scope from source agent run and applies scopeLevel to determine scope specificity.
|
|
653
|
+
*/
|
|
555
654
|
async CreateNoteRecords(extractedNotes, contextUser) {
|
|
655
|
+
// Step 7: Create Note Records
|
|
556
656
|
const step7 = await this.CreateRunStep('Decision', 'Create Note Records', {
|
|
557
657
|
noteCount: extractedNotes.length
|
|
558
658
|
});
|
|
559
659
|
let created = 0;
|
|
560
660
|
let merged = 0;
|
|
561
661
|
let failed = 0;
|
|
562
|
-
const md = new
|
|
563
|
-
const rv = new
|
|
662
|
+
const md = new Metadata();
|
|
663
|
+
const rv = new RunView();
|
|
664
|
+
// Cache source agent runs to avoid repeated lookups
|
|
564
665
|
const runCache = new Map();
|
|
565
|
-
|
|
666
|
+
// Get the "AI" note type ID for AI-generated notes
|
|
667
|
+
const aiNoteTypeId = AIEngine.Instance.AgenteNoteTypeIDByName('AI');
|
|
566
668
|
if (!aiNoteTypeId) {
|
|
567
|
-
|
|
669
|
+
LogError('Memory Manager: Could not find "AI" note type - cannot create notes');
|
|
568
670
|
await this.FinalizeRunStep(step7, false, {
|
|
569
671
|
created: 0,
|
|
570
672
|
merged: 0,
|
|
@@ -574,6 +676,7 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
574
676
|
}
|
|
575
677
|
for (const extracted of extractedNotes) {
|
|
576
678
|
try {
|
|
679
|
+
// Load source agent run for scope inheritance (if available)
|
|
577
680
|
let sourceRun = null;
|
|
578
681
|
if (extracted.sourceAgentRunId) {
|
|
579
682
|
if (!runCache.has(extracted.sourceAgentRunId)) {
|
|
@@ -587,9 +690,11 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
587
690
|
}
|
|
588
691
|
sourceRun = runCache.get(extracted.sourceAgentRunId) || null;
|
|
589
692
|
}
|
|
693
|
+
// Check if we should merge with existing
|
|
590
694
|
if (extracted.mergeWithExistingId) {
|
|
591
695
|
const existingNote = await md.GetEntityObject('AI Agent Notes', contextUser);
|
|
592
696
|
if (await existingNote.Load(extracted.mergeWithExistingId)) {
|
|
697
|
+
// Update existing note
|
|
593
698
|
existingNote.Note = extracted.content;
|
|
594
699
|
existingNote.Type = extracted.type;
|
|
595
700
|
existingNote.AgentNoteTypeID = aiNoteTypeId;
|
|
@@ -605,12 +710,15 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
605
710
|
}
|
|
606
711
|
}
|
|
607
712
|
else {
|
|
713
|
+
// Create new note
|
|
608
714
|
const note = await md.GetEntityObject('AI Agent Notes', contextUser);
|
|
715
|
+
// Only use valid UUIDs, filter out placeholders like "user-uuid-here"
|
|
609
716
|
const isValidUUID = (id) => {
|
|
610
717
|
if (!id)
|
|
611
718
|
return false;
|
|
612
719
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
|
|
613
720
|
};
|
|
721
|
+
// Determine AgentID - prefer extracted, then inherit from source run
|
|
614
722
|
let agentId = isValidUUID(extracted.agentId) ? extracted.agentId : null;
|
|
615
723
|
if (!agentId && sourceRun?.AgentID) {
|
|
616
724
|
agentId = sourceRun.AgentID;
|
|
@@ -618,15 +726,17 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
618
726
|
note.AgentID = agentId;
|
|
619
727
|
note.UserID = isValidUUID(extracted.userId) ? extracted.userId : null;
|
|
620
728
|
note.CompanyID = isValidUUID(extracted.companyId) ? extracted.companyId : null;
|
|
621
|
-
note.AgentNoteTypeID = aiNoteTypeId;
|
|
622
|
-
note.Type = extracted.type;
|
|
729
|
+
note.AgentNoteTypeID = aiNoteTypeId; // "AI" type for AI-generated notes
|
|
730
|
+
note.Type = extracted.type; // Category: Preference, Constraint, Context, Issue, Example
|
|
623
731
|
note.Note = extracted.content;
|
|
624
732
|
note.IsAutoGenerated = true;
|
|
625
|
-
note.Status = 'Active';
|
|
626
|
-
note.AccessCount = 1;
|
|
733
|
+
note.Status = 'Active'; // Auto-approve high-confidence notes
|
|
734
|
+
note.AccessCount = 1; // Required field
|
|
627
735
|
note.SourceConversationID = extracted.sourceConversationId || null;
|
|
736
|
+
// Only use if it's a valid UUID (LLM now sees message IDs in the prompt)
|
|
628
737
|
note.SourceConversationDetailID = isValidUUID(extracted.sourceConversationDetailId) ? extracted.sourceConversationDetailId : null;
|
|
629
738
|
note.SourceAIAgentRunID = extracted.sourceAgentRunId || null;
|
|
739
|
+
// Apply scope: Lean towards USER-specific memories by default
|
|
630
740
|
const scopeLevel = extracted.scopeLevel || 'contact';
|
|
631
741
|
if (scopeLevel === 'global') {
|
|
632
742
|
note.PrimaryScopeEntityID = null;
|
|
@@ -649,15 +759,16 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
649
759
|
}
|
|
650
760
|
else {
|
|
651
761
|
failed++;
|
|
652
|
-
|
|
762
|
+
LogError(`Memory Manager: Failed to save note - Validation errors: ${JSON.stringify(note.LatestResult)}`);
|
|
653
763
|
}
|
|
654
764
|
}
|
|
655
765
|
}
|
|
656
766
|
catch (error) {
|
|
657
767
|
failed++;
|
|
658
|
-
|
|
768
|
+
LogError('Memory Manager: Exception creating note:', error);
|
|
659
769
|
}
|
|
660
770
|
}
|
|
771
|
+
// Finalize Step 7
|
|
661
772
|
await this.FinalizeRunStep(step7, failed === 0 || created > 0 || merged > 0, {
|
|
662
773
|
created,
|
|
663
774
|
merged,
|
|
@@ -665,18 +776,25 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
665
776
|
});
|
|
666
777
|
return created + merged;
|
|
667
778
|
}
|
|
779
|
+
/**
|
|
780
|
+
* Create example records from extracted data.
|
|
781
|
+
* Inherits scope from source agent run and applies scopeLevel to determine scope specificity.
|
|
782
|
+
*/
|
|
668
783
|
async CreateExampleRecords(extractedExamples, contextUser) {
|
|
784
|
+
// Step 8: Create Example Records
|
|
669
785
|
const step8 = await this.CreateRunStep('Decision', 'Create Example Records', {
|
|
670
786
|
exampleCount: extractedExamples.length
|
|
671
787
|
});
|
|
672
788
|
let created = 0;
|
|
673
789
|
let skipped = 0;
|
|
674
790
|
let failed = 0;
|
|
675
|
-
const md = new
|
|
676
|
-
const rv = new
|
|
791
|
+
const md = new Metadata();
|
|
792
|
+
const rv = new RunView();
|
|
793
|
+
// Cache source agent runs to avoid repeated lookups
|
|
677
794
|
const runCache = new Map();
|
|
678
795
|
for (const extracted of extractedExamples) {
|
|
679
796
|
try {
|
|
797
|
+
// Load source agent run for scope inheritance (if available)
|
|
680
798
|
let sourceRun = null;
|
|
681
799
|
if (extracted.sourceAgentRunId) {
|
|
682
800
|
if (!runCache.has(extracted.sourceAgentRunId)) {
|
|
@@ -691,10 +809,11 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
691
809
|
sourceRun = runCache.get(extracted.sourceAgentRunId) || null;
|
|
692
810
|
}
|
|
693
811
|
const example = await md.GetEntityObject('MJ: AI Agent Examples', contextUser);
|
|
812
|
+
// AgentID must come from source run - LLM doesn't know real agent IDs
|
|
694
813
|
if (!sourceRun?.AgentID) {
|
|
695
814
|
skipped++;
|
|
696
815
|
if (this._verbose) {
|
|
697
|
-
|
|
816
|
+
LogStatus(`Memory Manager: Skipping example - no source run to inherit AgentID from`);
|
|
698
817
|
}
|
|
699
818
|
continue;
|
|
700
819
|
}
|
|
@@ -706,23 +825,28 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
706
825
|
example.ExampleOutput = extracted.exampleOutput;
|
|
707
826
|
example.IsAutoGenerated = true;
|
|
708
827
|
example.SuccessScore = extracted.successScore;
|
|
709
|
-
example.Status = 'Active';
|
|
828
|
+
example.Status = 'Active'; // Auto-approve high-confidence examples
|
|
710
829
|
example.SourceConversationID = extracted.sourceConversationId || null;
|
|
830
|
+
// Only use if it's a valid UUID
|
|
711
831
|
example.SourceConversationDetailID = this.isValidUUID(extracted.sourceConversationDetailId) ? extracted.sourceConversationDetailId : null;
|
|
712
832
|
example.SourceAIAgentRunID = extracted.sourceAgentRunId || null;
|
|
833
|
+
// Apply scope from source agent run based on scopeLevel hint
|
|
713
834
|
if (sourceRun && sourceRun.PrimaryScopeEntityID) {
|
|
714
|
-
const scopeLevel = extracted.scopeLevel || 'contact';
|
|
835
|
+
const scopeLevel = extracted.scopeLevel || 'contact'; // Default to most specific
|
|
715
836
|
if (scopeLevel === 'global') {
|
|
837
|
+
// Global example - no scope fields set
|
|
716
838
|
example.PrimaryScopeEntityID = null;
|
|
717
839
|
example.PrimaryScopeRecordID = null;
|
|
718
840
|
example.SecondaryScopes = null;
|
|
719
841
|
}
|
|
720
842
|
else if (scopeLevel === 'organization') {
|
|
843
|
+
// Org-level example - primary scope only, no secondary
|
|
721
844
|
example.PrimaryScopeEntityID = sourceRun.PrimaryScopeEntityID;
|
|
722
845
|
example.PrimaryScopeRecordID = sourceRun.PrimaryScopeRecordID;
|
|
723
846
|
example.SecondaryScopes = null;
|
|
724
847
|
}
|
|
725
848
|
else {
|
|
849
|
+
// Fully-scoped example (contact level) - inherit full scope
|
|
726
850
|
example.PrimaryScopeEntityID = sourceRun.PrimaryScopeEntityID;
|
|
727
851
|
example.PrimaryScopeRecordID = sourceRun.PrimaryScopeRecordID;
|
|
728
852
|
example.SecondaryScopes = sourceRun.SecondaryScopes;
|
|
@@ -733,14 +857,15 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
733
857
|
}
|
|
734
858
|
else {
|
|
735
859
|
failed++;
|
|
736
|
-
|
|
860
|
+
LogError(`Memory Manager: Failed to save example - Validation errors: ${JSON.stringify(example.LatestResult)}`);
|
|
737
861
|
}
|
|
738
862
|
}
|
|
739
863
|
catch (error) {
|
|
740
864
|
failed++;
|
|
741
|
-
|
|
865
|
+
LogError('Memory Manager: Exception creating example:', error);
|
|
742
866
|
}
|
|
743
867
|
}
|
|
868
|
+
// Finalize Step 8
|
|
744
869
|
await this.FinalizeRunStep(step8, failed === 0 || created > 0, {
|
|
745
870
|
created,
|
|
746
871
|
skipped,
|
|
@@ -748,19 +873,25 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
748
873
|
});
|
|
749
874
|
return created;
|
|
750
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Main execution method called by scheduler
|
|
878
|
+
*/
|
|
751
879
|
async executeAgentInternal(params, config) {
|
|
752
880
|
try {
|
|
881
|
+
// Use verbose flag from agent execution params
|
|
753
882
|
this._verbose = params.verbose ?? false;
|
|
883
|
+
// Initialize observability state for this run
|
|
754
884
|
this._agentRunID = this.AgentRun?.ID || null;
|
|
755
885
|
this._stepCounter = 0;
|
|
756
886
|
this._contextUser = params.contextUser || null;
|
|
757
|
-
|
|
887
|
+
LogStatus('Memory Manager: Starting analysis cycle');
|
|
758
888
|
const lastRunTime = await this.GetLastRunTime(params.agent.ID, params.contextUser);
|
|
889
|
+
// Load agents that have memory injection enabled
|
|
759
890
|
const agentsUsingMemory = await this.LoadAgentsUsingMemory(params.contextUser);
|
|
760
891
|
if (this._verbose) {
|
|
761
892
|
const sinceMessage = lastRunTime ? `since ${lastRunTime.toISOString()}` : 'all history';
|
|
762
|
-
|
|
763
|
-
|
|
893
|
+
LogStatus(`Memory Manager: Processing ${sinceMessage}`);
|
|
894
|
+
LogStatus(`Memory Manager: Found ${agentsUsingMemory.length} agents with memory injection enabled`);
|
|
764
895
|
}
|
|
765
896
|
if (agentsUsingMemory.length === 0) {
|
|
766
897
|
const finalStep = {
|
|
@@ -770,6 +901,7 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
770
901
|
};
|
|
771
902
|
return { finalStep, stepCount: 1 };
|
|
772
903
|
}
|
|
904
|
+
// Step 1: Load conversations with new activity (includes rating data)
|
|
773
905
|
const step1 = await this.CreateRunStep('Decision', 'Load Conversations With New Activity', {
|
|
774
906
|
since: lastRunTime?.toISOString() || null,
|
|
775
907
|
agentCount: agentsUsingMemory.length,
|
|
@@ -785,8 +917,9 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
785
917
|
unratedCount: conversations.filter(c => c.isUnrated).length
|
|
786
918
|
});
|
|
787
919
|
if (this._verbose) {
|
|
788
|
-
|
|
920
|
+
LogStatus(`Memory Manager: Found ${conversations.length} conversations with new activity`);
|
|
789
921
|
}
|
|
922
|
+
// Step 2: Load high-value agent runs
|
|
790
923
|
const step2 = await this.CreateRunStep('Decision', 'Load High-Value Agent Runs', {
|
|
791
924
|
since: lastRunTime?.toISOString() || null
|
|
792
925
|
});
|
|
@@ -795,11 +928,11 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
795
928
|
runCount: agentRuns.length
|
|
796
929
|
});
|
|
797
930
|
if (this._verbose) {
|
|
798
|
-
|
|
931
|
+
LogStatus(`Memory Manager: Found ${agentRuns.length} high-value agent runs`);
|
|
799
932
|
}
|
|
800
933
|
if (conversations.length === 0 && agentRuns.length === 0) {
|
|
801
934
|
if (this._verbose) {
|
|
802
|
-
|
|
935
|
+
LogStatus('Memory Manager: No data to process');
|
|
803
936
|
}
|
|
804
937
|
const finalStep = {
|
|
805
938
|
terminate: true,
|
|
@@ -808,10 +941,13 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
808
941
|
};
|
|
809
942
|
return { finalStep, stepCount: 1 };
|
|
810
943
|
}
|
|
944
|
+
// Extract notes from conversations (with rating context)
|
|
811
945
|
const extractedNotes = await this.ExtractNotesFromConversations(conversations, params.contextUser);
|
|
812
946
|
if (this._verbose) {
|
|
813
|
-
|
|
947
|
+
LogStatus(`Memory Manager: Extracted ${extractedNotes.length} potential notes`);
|
|
814
948
|
}
|
|
949
|
+
// Extract examples from conversation details (legacy method for now)
|
|
950
|
+
// Convert conversations back to flat details for example extraction
|
|
815
951
|
const conversationDetails = conversations.flatMap(conv => conv.messages.map(msg => ({
|
|
816
952
|
ID: msg.id,
|
|
817
953
|
ConversationID: conv.conversationId,
|
|
@@ -822,8 +958,10 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
822
958
|
})));
|
|
823
959
|
const extractedExamples = await this.ExtractExamples(conversationDetails, params.contextUser);
|
|
824
960
|
if (this._verbose) {
|
|
825
|
-
|
|
961
|
+
LogStatus(`Memory Manager: Extracted ${extractedExamples.length} potential examples`);
|
|
826
962
|
}
|
|
963
|
+
// Enrich examples with userId and agentRunId from conversation context
|
|
964
|
+
// Build a map from conversationId to context (same as notes enrichment)
|
|
827
965
|
const conversationContextForExamples = new Map();
|
|
828
966
|
for (const conv of conversations) {
|
|
829
967
|
conversationContextForExamples.set(conv.conversationId, {
|
|
@@ -835,34 +973,40 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
835
973
|
if (example.sourceConversationId) {
|
|
836
974
|
const ctx = conversationContextForExamples.get(example.sourceConversationId);
|
|
837
975
|
if (ctx) {
|
|
976
|
+
// Enrich userId if not set
|
|
838
977
|
if (!example.userId && ctx.userId) {
|
|
839
978
|
example.userId = ctx.userId;
|
|
840
979
|
if (this._verbose) {
|
|
841
|
-
|
|
980
|
+
LogStatus(`Memory Manager: Enriched example with userId from conversation: ${ctx.userId}`);
|
|
842
981
|
}
|
|
843
982
|
}
|
|
983
|
+
// Enrich agentRunId if not set
|
|
844
984
|
if (!example.sourceAgentRunId && ctx.agentRunId) {
|
|
845
985
|
example.sourceAgentRunId = ctx.agentRunId;
|
|
846
986
|
if (this._verbose) {
|
|
847
|
-
|
|
987
|
+
LogStatus(`Memory Manager: Enriched example with agentRunId from conversation: ${ctx.agentRunId}`);
|
|
848
988
|
}
|
|
849
989
|
}
|
|
850
990
|
}
|
|
851
991
|
}
|
|
992
|
+
// Clear invalid agentId (LLM sometimes returns placeholder values like "agent-12345")
|
|
852
993
|
if (example.agentId && !this.isValidUUID(example.agentId)) {
|
|
853
994
|
if (this._verbose) {
|
|
854
|
-
|
|
995
|
+
LogStatus(`Memory Manager: Clearing invalid agentId "${example.agentId}" from example`);
|
|
855
996
|
}
|
|
856
997
|
example.agentId = '';
|
|
857
998
|
}
|
|
858
999
|
}
|
|
1000
|
+
// Create records
|
|
859
1001
|
const notesCreated = await this.CreateNoteRecords(extractedNotes, params.contextUser);
|
|
860
1002
|
const examplesCreated = await this.CreateExampleRecords(extractedExamples, params.contextUser);
|
|
861
|
-
|
|
1003
|
+
LogStatus(`Memory Manager: Created ${notesCreated} notes and ${examplesCreated} examples`);
|
|
1004
|
+
// Refresh AIEngine memory cache if any new memories were created
|
|
1005
|
+
// This ensures subsequent agent runs can find the new notes/examples via semantic search
|
|
862
1006
|
if (notesCreated > 0 || examplesCreated > 0) {
|
|
863
|
-
|
|
864
|
-
await
|
|
865
|
-
|
|
1007
|
+
LogStatus(`Memory Manager: Refreshing AIEngine memory cache...`);
|
|
1008
|
+
await AIEngine.Instance.Config(true, params.contextUser);
|
|
1009
|
+
LogStatus(`Memory Manager: Memory cache refreshed`);
|
|
866
1010
|
}
|
|
867
1011
|
const finalStep = {
|
|
868
1012
|
terminate: true,
|
|
@@ -879,7 +1023,7 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
879
1023
|
return { finalStep, stepCount: 1 };
|
|
880
1024
|
}
|
|
881
1025
|
catch (error) {
|
|
882
|
-
|
|
1026
|
+
LogError('Memory Manager execution failed:', error);
|
|
883
1027
|
const finalStep = {
|
|
884
1028
|
terminate: true,
|
|
885
1029
|
step: 'Failed',
|
|
@@ -889,8 +1033,8 @@ let MemoryManagerAgent = class MemoryManagerAgent extends base_agent_1.BaseAgent
|
|
|
889
1033
|
}
|
|
890
1034
|
}
|
|
891
1035
|
};
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
(0, global_1.RegisterClass)(base_agent_1.BaseAgent, 'MemoryManagerAgent')
|
|
1036
|
+
MemoryManagerAgent = __decorate([
|
|
1037
|
+
RegisterClass(BaseAgent, 'MemoryManagerAgent')
|
|
895
1038
|
], MemoryManagerAgent);
|
|
1039
|
+
export { MemoryManagerAgent };
|
|
896
1040
|
//# sourceMappingURL=memory-manager-agent.js.map
|