@memberjunction/ai-agents 2.106.0 → 2.108.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 +154 -0
- package/dist/agent-types/loop-agent-response-type.d.ts +2 -1
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +4 -2
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +37 -5
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +846 -30
- package/dist/base-agent.js.map +1 -1
- package/package.json +8 -8
package/dist/base-agent.js
CHANGED
|
@@ -49,6 +49,7 @@ class BaseAgent {
|
|
|
49
49
|
this._subAgentRuns = [];
|
|
50
50
|
this._payloadManager = new PayloadManager_1.PayloadManager();
|
|
51
51
|
this._validationRetryCount = 0;
|
|
52
|
+
this._contextRecoveryAttempted = false;
|
|
52
53
|
}
|
|
53
54
|
get AgentTypeState() {
|
|
54
55
|
return this._agentTypeState;
|
|
@@ -158,6 +159,8 @@ class BaseAgent {
|
|
|
158
159
|
await this.initializeAgentRun(wrappedParams);
|
|
159
160
|
this._validationRetryCount = 0;
|
|
160
161
|
this._generalValidationRetryCount = 0;
|
|
162
|
+
this._contextRecoveryAttempted = false;
|
|
163
|
+
this._messageLifecycleCallback = params.onMessageLifecycle;
|
|
161
164
|
await this.initializeEngines(params.contextUser);
|
|
162
165
|
if (params.cancellationToken?.aborted) {
|
|
163
166
|
return await this.createCancelledResult('Cancelled during initialization', params.contextUser);
|
|
@@ -219,6 +222,7 @@ class BaseAgent {
|
|
|
219
222
|
if (params.cancellationToken?.aborted) {
|
|
220
223
|
throw new Error('Cancelled during execution');
|
|
221
224
|
}
|
|
225
|
+
await this.pruneAndCompactExpiredMessages(params, stepCount);
|
|
222
226
|
this.logStatus(`🔄 Executing step ${stepCount + 1} for agent '${params.agent.Name}'`, true, params);
|
|
223
227
|
const nextStep = await this.executeNextStep(params, config, currentNextStep);
|
|
224
228
|
stepCount++;
|
|
@@ -897,6 +901,163 @@ class BaseAgent {
|
|
|
897
901
|
}
|
|
898
902
|
return { exceeded: false };
|
|
899
903
|
}
|
|
904
|
+
isFatalPromptError(promptResult) {
|
|
905
|
+
if (!promptResult?.chatResult?.errorInfo) {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
const errorInfo = promptResult.chatResult.errorInfo;
|
|
909
|
+
if (errorInfo.severity === 'Fatal') {
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
const fatalErrorTypes = [
|
|
913
|
+
'ContextLengthExceeded',
|
|
914
|
+
'Authentication',
|
|
915
|
+
'InvalidRequest'
|
|
916
|
+
];
|
|
917
|
+
return fatalErrorTypes.includes(errorInfo.errorType);
|
|
918
|
+
}
|
|
919
|
+
contentToString(content) {
|
|
920
|
+
if (typeof content === 'string') {
|
|
921
|
+
return content;
|
|
922
|
+
}
|
|
923
|
+
return content.map((block) => {
|
|
924
|
+
if (block.type === 'text') {
|
|
925
|
+
return block.content;
|
|
926
|
+
}
|
|
927
|
+
return `[${block.type}: ${block.content}]`;
|
|
928
|
+
}).join('\n');
|
|
929
|
+
}
|
|
930
|
+
smartTrimContent(content, maxLength = 1000) {
|
|
931
|
+
const originalLength = content.length;
|
|
932
|
+
try {
|
|
933
|
+
const data = JSON.parse(content);
|
|
934
|
+
if (Array.isArray(data)) {
|
|
935
|
+
const itemsToKeep = 10;
|
|
936
|
+
if (data.length > itemsToKeep) {
|
|
937
|
+
const truncated = data.slice(0, itemsToKeep);
|
|
938
|
+
const trimmed = JSON.stringify(truncated, null, 2) +
|
|
939
|
+
`\n\n... (${(data.length - itemsToKeep).toLocaleString()} more items truncated due to context length)`;
|
|
940
|
+
return {
|
|
941
|
+
trimmed,
|
|
942
|
+
strategy: 'JSON array truncation',
|
|
943
|
+
originalLength
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
else if (typeof data === 'object' && data !== null) {
|
|
948
|
+
const truncated = {};
|
|
949
|
+
let fieldCount = 0;
|
|
950
|
+
const maxFields = 20;
|
|
951
|
+
for (const [key, value] of Object.entries(data)) {
|
|
952
|
+
if (fieldCount >= maxFields) {
|
|
953
|
+
truncated['...'] = `(${Object.keys(data).length - maxFields} more fields truncated)`;
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
956
|
+
if (typeof value === 'string' && value.length > 200) {
|
|
957
|
+
truncated[key] = value.substring(0, 200) + '... (truncated)';
|
|
958
|
+
}
|
|
959
|
+
else if (Array.isArray(value) && value.length > 5) {
|
|
960
|
+
truncated[key] = [...value.slice(0, 5), `... (${value.length - 5} more items)`];
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
truncated[key] = value;
|
|
964
|
+
}
|
|
965
|
+
fieldCount++;
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
trimmed: JSON.stringify(truncated, null, 2),
|
|
969
|
+
strategy: 'JSON object field truncation',
|
|
970
|
+
originalLength
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
catch {
|
|
975
|
+
}
|
|
976
|
+
if (content.includes('\n')) {
|
|
977
|
+
const lines = content.split('\n');
|
|
978
|
+
const firstLine = lines[0];
|
|
979
|
+
if (firstLine.includes(',') && lines.length > 1) {
|
|
980
|
+
const rowsToKeep = 10;
|
|
981
|
+
if (lines.length > rowsToKeep + 1) {
|
|
982
|
+
const header = lines[0];
|
|
983
|
+
const dataRows = lines.slice(1, rowsToKeep + 1);
|
|
984
|
+
const trimmed = [header, ...dataRows].join('\n') +
|
|
985
|
+
`\n... (${(lines.length - rowsToKeep - 1).toLocaleString()} more rows truncated due to context length)`;
|
|
986
|
+
return {
|
|
987
|
+
trimmed,
|
|
988
|
+
strategy: 'CSV row truncation',
|
|
989
|
+
originalLength
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (content.length > maxLength) {
|
|
995
|
+
return {
|
|
996
|
+
trimmed: content.substring(0, maxLength) + '\n\n... (truncated due to context length)',
|
|
997
|
+
strategy: 'character truncation',
|
|
998
|
+
originalLength
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
trimmed: content,
|
|
1003
|
+
strategy: 'no truncation needed',
|
|
1004
|
+
originalLength
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
async attemptContextRecovery(params, payload, errorMessage) {
|
|
1008
|
+
this.logStatus(`⚠️ Context length exceeded - attempting recovery by trimming conversation messages`, true, params);
|
|
1009
|
+
const messages = params.conversationMessages;
|
|
1010
|
+
let lastUserMessageIndex = -1;
|
|
1011
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1012
|
+
if (messages[i].role === 'user') {
|
|
1013
|
+
lastUserMessageIndex = i;
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (lastUserMessageIndex === -1) {
|
|
1018
|
+
this.logStatus(`❌ Context recovery failed: No trimmable user messages found`, true, params);
|
|
1019
|
+
return {
|
|
1020
|
+
errorMessage: `Context overflow with no trimmable messages: ${errorMessage}`,
|
|
1021
|
+
step: 'Failed',
|
|
1022
|
+
terminate: true,
|
|
1023
|
+
previousPayload: payload,
|
|
1024
|
+
newPayload: payload
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
const originalMessage = messages[lastUserMessageIndex];
|
|
1028
|
+
const contentString = this.contentToString(originalMessage.content);
|
|
1029
|
+
const trimResult = this.smartTrimContent(contentString, 1000);
|
|
1030
|
+
this.logStatus(`✂️ Trimmed message from ${trimResult.originalLength.toLocaleString()} to ${trimResult.trimmed.length.toLocaleString()} characters using ${trimResult.strategy}`, true, params);
|
|
1031
|
+
messages[lastUserMessageIndex] = {
|
|
1032
|
+
role: 'user',
|
|
1033
|
+
content: `⚠️ CONTEXT OVERFLOW RECOVERY ⚠️
|
|
1034
|
+
|
|
1035
|
+
The previous step returned a result that exceeded the context window (${(trimResult.originalLength - trimResult.trimmed.length).toLocaleString()} characters truncated).
|
|
1036
|
+
|
|
1037
|
+
Here is a PARTIAL result from the previous action:
|
|
1038
|
+
---
|
|
1039
|
+
${trimResult.trimmed}
|
|
1040
|
+
---
|
|
1041
|
+
|
|
1042
|
+
❗ THE ABOVE IS INCOMPLETE - the full result was too large for the context window.
|
|
1043
|
+
|
|
1044
|
+
RECOMMENDED ACTIONS:
|
|
1045
|
+
1. Use a different action with more specific filters to get smaller result sets
|
|
1046
|
+
2. Request data in batches or pages instead of all at once
|
|
1047
|
+
3. Ask the user to clarify scope to narrow the query
|
|
1048
|
+
4. If you need the full data, acknowledge the limitation and ask the user how to proceed
|
|
1049
|
+
|
|
1050
|
+
Please choose an alternative approach to complete your task.`
|
|
1051
|
+
};
|
|
1052
|
+
return {
|
|
1053
|
+
step: 'Retry',
|
|
1054
|
+
retryReason: 'Context length recovery - previous result too large',
|
|
1055
|
+
retryInstructions: 'The previous action result exceeded context limits. Use more specific filters or request data in smaller batches.',
|
|
1056
|
+
terminate: false,
|
|
1057
|
+
previousPayload: payload,
|
|
1058
|
+
newPayload: payload
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
900
1061
|
async processNextStep(nextStep, params, agentType, promptResult, currentPayload, currentStep) {
|
|
901
1062
|
const validatedNextStep = await this.validateNextStep(params, nextStep, currentPayload, this._agentRun, currentStep);
|
|
902
1063
|
const guardrailCheckedStep = await this.checkExecutionGuardrails(params, validatedNextStep, currentPayload, this._agentRun, currentStep);
|
|
@@ -1006,16 +1167,18 @@ class BaseAgent {
|
|
|
1006
1167
|
throw new Error(`Error executing actions: ${error.message}`);
|
|
1007
1168
|
}
|
|
1008
1169
|
}
|
|
1009
|
-
async ExecuteSubAgent(params, subAgentRequest, subAgent, stepEntity, payload) {
|
|
1170
|
+
async ExecuteSubAgent(params, subAgentRequest, subAgent, stepEntity, payload, contextMessage) {
|
|
1010
1171
|
try {
|
|
1011
1172
|
this.logStatus(`🤖 Executing sub-agent '${subAgentRequest.name}'`, true, params);
|
|
1012
1173
|
const runner = new AgentRunner_1.AgentRunner();
|
|
1013
|
-
const subAgentMessages = [
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1174
|
+
const subAgentMessages = [];
|
|
1175
|
+
if (contextMessage) {
|
|
1176
|
+
subAgentMessages.push(contextMessage);
|
|
1177
|
+
}
|
|
1178
|
+
subAgentMessages.push({
|
|
1179
|
+
role: 'user',
|
|
1180
|
+
content: subAgentRequest.message
|
|
1181
|
+
});
|
|
1019
1182
|
this.logStatus(`📨 Sub-agent message: "${subAgentRequest.message}"`, true, params);
|
|
1020
1183
|
if (subAgentRequest.templateParameters) {
|
|
1021
1184
|
this.logStatus(`📎 Template parameters: ${JSON.stringify(subAgentRequest.templateParameters)}`, true, params);
|
|
@@ -1134,6 +1297,25 @@ class BaseAgent {
|
|
|
1134
1297
|
ValueType: param.ValueType
|
|
1135
1298
|
};
|
|
1136
1299
|
}
|
|
1300
|
+
formatParamValueForMessage(value, maxLength = 100) {
|
|
1301
|
+
if (value === null || value === undefined) {
|
|
1302
|
+
return 'null';
|
|
1303
|
+
}
|
|
1304
|
+
let stringValue;
|
|
1305
|
+
if (typeof value === 'string') {
|
|
1306
|
+
stringValue = value;
|
|
1307
|
+
}
|
|
1308
|
+
else if (typeof value === 'object') {
|
|
1309
|
+
stringValue = JSON.stringify(value);
|
|
1310
|
+
}
|
|
1311
|
+
else {
|
|
1312
|
+
stringValue = String(value);
|
|
1313
|
+
}
|
|
1314
|
+
if (stringValue.length > maxLength) {
|
|
1315
|
+
return `\`${stringValue.substring(0, maxLength)}...\``;
|
|
1316
|
+
}
|
|
1317
|
+
return `\`${stringValue}\``;
|
|
1318
|
+
}
|
|
1137
1319
|
getAgentTypeName(typeID) {
|
|
1138
1320
|
const agentType = aiengine_1.AIEngine.Instance.AgentTypes.find(at => at.ID === typeID);
|
|
1139
1321
|
return agentType?.Name || 'Unknown';
|
|
@@ -1364,9 +1546,13 @@ class BaseAgent {
|
|
|
1364
1546
|
}
|
|
1365
1547
|
switch (previousDecision.step) {
|
|
1366
1548
|
case 'Retry':
|
|
1549
|
+
if (previousDecision.messageIndex !== undefined) {
|
|
1550
|
+
const currentStepCount = this._agentRun?.Steps?.length || 0;
|
|
1551
|
+
this.executeExpandMessageStep(previousDecision, params, currentStepCount);
|
|
1552
|
+
}
|
|
1367
1553
|
return await this.executePromptStep(params, config, previousDecision);
|
|
1368
1554
|
case 'Sub-Agent':
|
|
1369
|
-
return await this.
|
|
1555
|
+
return await this.processSubAgentStep(params, previousDecision);
|
|
1370
1556
|
case 'Actions':
|
|
1371
1557
|
return await this.executeActionsStep(params, config, previousDecision);
|
|
1372
1558
|
case 'Chat':
|
|
@@ -1466,6 +1652,25 @@ class BaseAgent {
|
|
|
1466
1652
|
stepEntity.TargetLogID = promptResult.promptRun.ID;
|
|
1467
1653
|
stepEntity.PromptRun = promptResult.promptRun;
|
|
1468
1654
|
}
|
|
1655
|
+
if (!promptResult.success) {
|
|
1656
|
+
await this.finalizeStepEntity(stepEntity, false, promptResult.errorMessage);
|
|
1657
|
+
const isFatal = this.isFatalPromptError(promptResult);
|
|
1658
|
+
const isContextOverflow = isFatal &&
|
|
1659
|
+
promptResult.chatResult?.errorInfo?.errorType === 'ContextLengthExceeded';
|
|
1660
|
+
if (isContextOverflow && !this._contextRecoveryAttempted) {
|
|
1661
|
+
this._contextRecoveryAttempted = true;
|
|
1662
|
+
this.logStatus(`⚠️ Context length exceeded - attempting recovery by trimming conversation`, true, params);
|
|
1663
|
+
return await this.attemptContextRecovery(params, payload, promptResult.errorMessage || 'Context length exceeded');
|
|
1664
|
+
}
|
|
1665
|
+
this.logStatus(`❌ Prompt execution failed: ${promptResult.errorMessage} (fatal: ${isFatal})`, true, params);
|
|
1666
|
+
return {
|
|
1667
|
+
errorMessage: promptResult.errorMessage || 'Prompt execution failed',
|
|
1668
|
+
step: 'Failed',
|
|
1669
|
+
terminate: isFatal,
|
|
1670
|
+
previousPayload: payload,
|
|
1671
|
+
newPayload: payload
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1469
1674
|
if (params.cancellationToken?.aborted) {
|
|
1470
1675
|
await this.finalizeStepEntity(stepEntity, false, 'Cancelled during prompt execution');
|
|
1471
1676
|
const cancelledResult = await this.createCancelledResult('Cancelled during prompt execution', params.contextUser);
|
|
@@ -1555,7 +1760,7 @@ class BaseAgent {
|
|
|
1555
1760
|
return errorNextStep;
|
|
1556
1761
|
}
|
|
1557
1762
|
}
|
|
1558
|
-
async
|
|
1763
|
+
async executeChildSubAgentStep(params, previousDecision) {
|
|
1559
1764
|
const subAgentRequest = previousDecision.subAgent;
|
|
1560
1765
|
if (params.cancellationToken?.aborted) {
|
|
1561
1766
|
throw new Error('Cancelled before sub-agent execution');
|
|
@@ -1631,7 +1836,7 @@ class BaseAgent {
|
|
|
1631
1836
|
}
|
|
1632
1837
|
}
|
|
1633
1838
|
stepEntity.PayloadAtStart = JSON.stringify(previousDecision.newPayload);
|
|
1634
|
-
const subAgentResult = await this.ExecuteSubAgent(params, subAgentRequest, subAgentEntity, stepEntity, scopedPayload);
|
|
1839
|
+
const subAgentResult = await this.ExecuteSubAgent(params, subAgentRequest, subAgentEntity, stepEntity, scopedPayload, undefined);
|
|
1635
1840
|
let mergedPayload = previousDecision.newPayload;
|
|
1636
1841
|
let currentStepPayloadChangeResult = undefined;
|
|
1637
1842
|
if (subAgentResult.success) {
|
|
@@ -1771,6 +1976,322 @@ class BaseAgent {
|
|
|
1771
1976
|
};
|
|
1772
1977
|
}
|
|
1773
1978
|
}
|
|
1979
|
+
async processSubAgentStep(params, previousDecision) {
|
|
1980
|
+
const subAgentRequest = previousDecision.subAgent;
|
|
1981
|
+
const name = subAgentRequest?.name;
|
|
1982
|
+
if (!name) {
|
|
1983
|
+
return {
|
|
1984
|
+
step: 'Failed',
|
|
1985
|
+
terminate: false,
|
|
1986
|
+
errorMessage: 'Sub-agent name is required',
|
|
1987
|
+
previousPayload: previousDecision?.newPayload,
|
|
1988
|
+
newPayload: previousDecision?.newPayload
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
const childAgents = aiengine_1.AIEngine.Instance.Agents.filter(a => a.ParentID === params.agent.ID &&
|
|
1992
|
+
a.Status === 'Active');
|
|
1993
|
+
const childAgent = childAgents.find(a => a.Name.trim().toLowerCase() === name.trim().toLowerCase());
|
|
1994
|
+
if (childAgent) {
|
|
1995
|
+
return await this.executeChildSubAgentStep(params, previousDecision);
|
|
1996
|
+
}
|
|
1997
|
+
const activeRelationships = aiengine_1.AIEngine.Instance.AgentRelationships.filter(ar => ar.AgentID === params.agent.ID &&
|
|
1998
|
+
ar.Status === 'Active');
|
|
1999
|
+
for (const relationship of activeRelationships) {
|
|
2000
|
+
const relatedAgent = aiengine_1.AIEngine.Instance.Agents.find(a => a.ID === relationship.SubAgentID &&
|
|
2001
|
+
a.Status === 'Active');
|
|
2002
|
+
if (relatedAgent && relatedAgent.Name.trim().toLowerCase() === name.trim().toLowerCase()) {
|
|
2003
|
+
return await this.executeRelatedSubAgentStep(params, previousDecision, relatedAgent, relationship);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
this.logError(`Sub-agent '${name}' not found or not active for agent '${params.agent.Name}'`, {
|
|
2007
|
+
agent: params.agent,
|
|
2008
|
+
category: 'SubAgentExecution'
|
|
2009
|
+
});
|
|
2010
|
+
return {
|
|
2011
|
+
step: 'Retry',
|
|
2012
|
+
terminate: false,
|
|
2013
|
+
errorMessage: `Sub-agent '${name}' not found or not active`,
|
|
2014
|
+
previousPayload: previousDecision?.newPayload,
|
|
2015
|
+
newPayload: previousDecision?.newPayload
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
async executeRelatedSubAgentStep(params, previousDecision, subAgentEntity, relationship) {
|
|
2019
|
+
const subAgentRequest = previousDecision.subAgent;
|
|
2020
|
+
if (params.cancellationToken?.aborted) {
|
|
2021
|
+
throw new Error('Cancelled before related sub-agent execution');
|
|
2022
|
+
}
|
|
2023
|
+
params.onProgress?.({
|
|
2024
|
+
step: 'subagent_execution',
|
|
2025
|
+
percentage: 60,
|
|
2026
|
+
message: this.formatHierarchicalMessage(`Delegating to ${subAgentRequest.name} agent`),
|
|
2027
|
+
metadata: {
|
|
2028
|
+
agentName: params.agent.Name,
|
|
2029
|
+
subAgentName: subAgentRequest.name,
|
|
2030
|
+
reason: subAgentRequest.message,
|
|
2031
|
+
relationshipType: 'related'
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
params.conversationMessages.push({
|
|
2035
|
+
role: 'assistant',
|
|
2036
|
+
content: `I'm delegating this task to the "${subAgentRequest.name}" agent.\n\nReason: ${subAgentRequest.message}`
|
|
2037
|
+
});
|
|
2038
|
+
const inputData = {
|
|
2039
|
+
agentName: params.agent.Name,
|
|
2040
|
+
subAgentName: subAgentRequest.name,
|
|
2041
|
+
message: subAgentRequest.message,
|
|
2042
|
+
terminateAfter: subAgentRequest.terminateAfter,
|
|
2043
|
+
conversationMessages: params.conversationMessages,
|
|
2044
|
+
parentAgentHierarchy: this._agentHierarchy,
|
|
2045
|
+
relationshipType: 'related'
|
|
2046
|
+
};
|
|
2047
|
+
const stepEntity = await this.createStepEntity('Sub-Agent', `Execute Related Sub-Agent: ${subAgentRequest.name}`, params.contextUser, subAgentEntity.ID, inputData, undefined, previousDecision.newPayload);
|
|
2048
|
+
this.incrementExecutionCount(subAgentEntity.ID);
|
|
2049
|
+
try {
|
|
2050
|
+
stepEntity.PayloadAtStart = JSON.stringify(previousDecision.newPayload);
|
|
2051
|
+
let initialSubAgentPayload = undefined;
|
|
2052
|
+
if (relationship.SubAgentInputMapping) {
|
|
2053
|
+
const mapped = this.applySubAgentInputMapping(previousDecision.newPayload, relationship.SubAgentInputMapping);
|
|
2054
|
+
if (mapped && Object.keys(mapped).length > 0) {
|
|
2055
|
+
initialSubAgentPayload = mapped;
|
|
2056
|
+
if (params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)()) {
|
|
2057
|
+
(0, core_1.LogStatus)(`Related sub-agent '${subAgentRequest.name}' receiving mapped payload: ${JSON.stringify(Object.keys(mapped))}`);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
let contextPaths = [];
|
|
2062
|
+
if (relationship.SubAgentContextPaths) {
|
|
2063
|
+
try {
|
|
2064
|
+
contextPaths = JSON.parse(relationship.SubAgentContextPaths);
|
|
2065
|
+
}
|
|
2066
|
+
catch (parseError) {
|
|
2067
|
+
(0, core_1.LogError)(`Failed to parse SubAgentContextPaths for sub-agent ${subAgentRequest.name}: ${parseError.message}`);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
const contextMessage = this.prepareRelatedSubAgentContextMessage(previousDecision.newPayload, contextPaths);
|
|
2071
|
+
if (contextMessage && (params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)())) {
|
|
2072
|
+
(0, core_1.LogStatus)(`Related sub-agent '${subAgentRequest.name}' receiving context from paths: ${contextPaths.join(', ')}`);
|
|
2073
|
+
}
|
|
2074
|
+
const subAgentResult = await this.ExecuteSubAgent(params, subAgentRequest, subAgentEntity, stepEntity, initialSubAgentPayload, contextMessage);
|
|
2075
|
+
let mergedPayload = previousDecision.newPayload;
|
|
2076
|
+
let currentStepPayloadChangeResult = undefined;
|
|
2077
|
+
if (subAgentResult.success && relationship.SubAgentOutputMapping) {
|
|
2078
|
+
const payloadChange = this.applySubAgentOutputMapping(subAgentResult.payload, previousDecision.newPayload, relationship.SubAgentOutputMapping);
|
|
2079
|
+
if (payloadChange && payloadChange.updateElements) {
|
|
2080
|
+
const mergeResult = this._payloadManager.applyAgentChangeRequest(previousDecision.newPayload, payloadChange, {
|
|
2081
|
+
validateChanges: true,
|
|
2082
|
+
logChanges: true,
|
|
2083
|
+
analyzeChanges: true,
|
|
2084
|
+
generateDiff: true,
|
|
2085
|
+
agentName: `${subAgentRequest.name} (related agent mapping)`,
|
|
2086
|
+
verbose: params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)()
|
|
2087
|
+
});
|
|
2088
|
+
mergedPayload = mergeResult.result;
|
|
2089
|
+
currentStepPayloadChangeResult = this.buildPayloadChangeResultSummary(mergeResult);
|
|
2090
|
+
if (mergeResult.warnings.length > 0 && (params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)())) {
|
|
2091
|
+
(0, core_1.LogStatus)(`Related sub-agent mapping warnings: ${mergeResult.warnings.join('; ')}`);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
const shouldTerminate = subAgentRequest.terminateAfter === true;
|
|
2096
|
+
const outputData = {
|
|
2097
|
+
subAgentResult: {
|
|
2098
|
+
success: subAgentResult.success,
|
|
2099
|
+
finalStep: subAgentResult.agentRun?.FinalStep,
|
|
2100
|
+
errorMessage: subAgentResult.agentRun?.ErrorMessage,
|
|
2101
|
+
stepCount: subAgentResult.agentRun?.Steps?.length || 0,
|
|
2102
|
+
hasMergedPayload: !!(relationship.SubAgentOutputMapping && mergedPayload !== previousDecision.newPayload)
|
|
2103
|
+
},
|
|
2104
|
+
shouldTerminate: shouldTerminate,
|
|
2105
|
+
nextStep: shouldTerminate ? 'success' : 'retry',
|
|
2106
|
+
...(currentStepPayloadChangeResult && {
|
|
2107
|
+
payloadChangeResult: currentStepPayloadChangeResult
|
|
2108
|
+
})
|
|
2109
|
+
};
|
|
2110
|
+
if (stepEntity) {
|
|
2111
|
+
stepEntity.PayloadAtEnd = JSON.stringify(mergedPayload);
|
|
2112
|
+
}
|
|
2113
|
+
await this.finalizeStepEntity(stepEntity, subAgentResult.success, subAgentResult.agentRun?.ErrorMessage, outputData);
|
|
2114
|
+
if (subAgentResult.agentRun?.FinalStep === 'Chat') {
|
|
2115
|
+
return {
|
|
2116
|
+
step: 'Chat',
|
|
2117
|
+
terminate: true,
|
|
2118
|
+
message: subAgentResult.agentRun?.Message || null,
|
|
2119
|
+
previousPayload: previousDecision?.newPayload,
|
|
2120
|
+
newPayload: previousDecision?.newPayload
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
const subAgentSummary = {
|
|
2124
|
+
agentName: params.agent.Name,
|
|
2125
|
+
subAgentName: subAgentRequest.name,
|
|
2126
|
+
success: subAgentResult.success,
|
|
2127
|
+
payload: subAgentResult.payload,
|
|
2128
|
+
errorMessage: subAgentResult.agentRun?.ErrorMessage
|
|
2129
|
+
};
|
|
2130
|
+
params.conversationMessages.push({
|
|
2131
|
+
role: 'user',
|
|
2132
|
+
content: `Related sub-agent "${subAgentRequest.name}" completed:\n${JSON.stringify(subAgentSummary, null, 2)}`
|
|
2133
|
+
});
|
|
2134
|
+
if (this._agentRun) {
|
|
2135
|
+
this._agentRun.FinalPayloadObject = mergedPayload;
|
|
2136
|
+
}
|
|
2137
|
+
return {
|
|
2138
|
+
...subAgentResult,
|
|
2139
|
+
step: subAgentResult.success ? 'Success' : 'Failed',
|
|
2140
|
+
terminate: shouldTerminate,
|
|
2141
|
+
previousPayload: previousDecision?.newPayload,
|
|
2142
|
+
newPayload: mergedPayload
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
catch (error) {
|
|
2146
|
+
const payload = stepEntity.PayloadAtEnd ? JSON.parse(stepEntity.PayloadAtEnd) : previousDecision?.newPayload || params.payload;
|
|
2147
|
+
stepEntity.PayloadAtEnd = payload ? JSON.stringify(payload) : null;
|
|
2148
|
+
await this.finalizeStepEntity(stepEntity, false, error.message);
|
|
2149
|
+
return {
|
|
2150
|
+
errorMessage: `Related sub-agent execution failed: ${error.message}`,
|
|
2151
|
+
step: 'Failed',
|
|
2152
|
+
terminate: false,
|
|
2153
|
+
previousPayload: payload,
|
|
2154
|
+
newPayload: payload
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
applySubAgentOutputMapping(subAgentResult, _parentPayload, mappingConfig) {
|
|
2159
|
+
try {
|
|
2160
|
+
const mapping = JSON.parse(mappingConfig);
|
|
2161
|
+
const updateObj = {};
|
|
2162
|
+
for (const [subAgentPath, parentPath] of Object.entries(mapping)) {
|
|
2163
|
+
let value;
|
|
2164
|
+
if (subAgentPath === '*') {
|
|
2165
|
+
value = subAgentResult;
|
|
2166
|
+
}
|
|
2167
|
+
else {
|
|
2168
|
+
value = this.getValueFromPath(subAgentResult, subAgentPath);
|
|
2169
|
+
}
|
|
2170
|
+
if (value !== undefined) {
|
|
2171
|
+
const pathParts = parentPath.split('.');
|
|
2172
|
+
let current = updateObj;
|
|
2173
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2174
|
+
const part = pathParts[i];
|
|
2175
|
+
if (!(part in current)) {
|
|
2176
|
+
current[part] = {};
|
|
2177
|
+
}
|
|
2178
|
+
current = current[part];
|
|
2179
|
+
}
|
|
2180
|
+
current[pathParts[pathParts.length - 1]] = value;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
if (Object.keys(updateObj).length === 0) {
|
|
2184
|
+
return null;
|
|
2185
|
+
}
|
|
2186
|
+
return {
|
|
2187
|
+
updateElements: updateObj
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
catch (error) {
|
|
2191
|
+
(0, core_1.LogError)(`Failed to parse SubAgentOutputMapping: ${error.message}`);
|
|
2192
|
+
return null;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
applySubAgentInputMapping(parentPayload, mappingConfig) {
|
|
2196
|
+
try {
|
|
2197
|
+
const mapping = JSON.parse(mappingConfig);
|
|
2198
|
+
const subAgentPayload = {};
|
|
2199
|
+
for (const [parentPath, subAgentPath] of Object.entries(mapping)) {
|
|
2200
|
+
let value;
|
|
2201
|
+
if (parentPath === '*') {
|
|
2202
|
+
value = parentPayload;
|
|
2203
|
+
}
|
|
2204
|
+
else {
|
|
2205
|
+
value = this.getValueFromPath(parentPayload, parentPath);
|
|
2206
|
+
}
|
|
2207
|
+
if (value !== undefined) {
|
|
2208
|
+
const pathParts = subAgentPath.split('.');
|
|
2209
|
+
let current = subAgentPayload;
|
|
2210
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2211
|
+
const part = pathParts[i];
|
|
2212
|
+
if (!(part in current)) {
|
|
2213
|
+
current[part] = {};
|
|
2214
|
+
}
|
|
2215
|
+
current = current[part];
|
|
2216
|
+
}
|
|
2217
|
+
current[pathParts[pathParts.length - 1]] = value;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
if (Object.keys(subAgentPayload).length === 0) {
|
|
2221
|
+
return null;
|
|
2222
|
+
}
|
|
2223
|
+
return subAgentPayload;
|
|
2224
|
+
}
|
|
2225
|
+
catch (error) {
|
|
2226
|
+
(0, core_1.LogError)(`Failed to parse SubAgentInputMapping: ${error.message}`);
|
|
2227
|
+
return null;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
prepareRelatedSubAgentContextMessage(parentPayload, contextPaths) {
|
|
2231
|
+
if (!contextPaths || contextPaths.length === 0) {
|
|
2232
|
+
return null;
|
|
2233
|
+
}
|
|
2234
|
+
if (contextPaths.includes('*')) {
|
|
2235
|
+
return {
|
|
2236
|
+
role: 'user',
|
|
2237
|
+
content: `Parent Agent Context:\n\n${JSON.stringify(parentPayload, null, 2)}`
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
const contextData = {};
|
|
2241
|
+
for (const path of contextPaths) {
|
|
2242
|
+
const value = this.getValueFromPath(parentPayload, path);
|
|
2243
|
+
if (value !== undefined) {
|
|
2244
|
+
contextData[path] = value;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (Object.keys(contextData).length === 0) {
|
|
2248
|
+
return null;
|
|
2249
|
+
}
|
|
2250
|
+
const contextLines = Object.entries(contextData).map(([key, value]) => {
|
|
2251
|
+
const valueStr = typeof value === 'object'
|
|
2252
|
+
? JSON.stringify(value, null, 2)
|
|
2253
|
+
: String(value);
|
|
2254
|
+
return `${key}:\n${valueStr}`;
|
|
2255
|
+
});
|
|
2256
|
+
return {
|
|
2257
|
+
role: 'user',
|
|
2258
|
+
content: `Parent Agent Context:\n\n${contextLines.join('\n\n')}`
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
getValueFromPath(obj, path) {
|
|
2262
|
+
const parts = path.split('.');
|
|
2263
|
+
let current = obj;
|
|
2264
|
+
for (const part of parts) {
|
|
2265
|
+
if (!part)
|
|
2266
|
+
continue;
|
|
2267
|
+
const arrayMatch = part.match(/^([^[]+)\[(\d+)\]$/);
|
|
2268
|
+
if (arrayMatch) {
|
|
2269
|
+
const arrayName = arrayMatch[1];
|
|
2270
|
+
const index = parseInt(arrayMatch[2], 10);
|
|
2271
|
+
if (current && typeof current === 'object' && arrayName in current) {
|
|
2272
|
+
current = current[arrayName];
|
|
2273
|
+
if (Array.isArray(current) && index >= 0 && index < current.length) {
|
|
2274
|
+
current = current[index];
|
|
2275
|
+
}
|
|
2276
|
+
else {
|
|
2277
|
+
return undefined;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
else {
|
|
2281
|
+
return undefined;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
else {
|
|
2285
|
+
if (current && typeof current === 'object' && part in current) {
|
|
2286
|
+
current = current[part];
|
|
2287
|
+
}
|
|
2288
|
+
else {
|
|
2289
|
+
return undefined;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
return current;
|
|
2294
|
+
}
|
|
1774
2295
|
async executeActionsStep(params, config, previousDecision) {
|
|
1775
2296
|
try {
|
|
1776
2297
|
const currentPayload = previousDecision?.newPayload || previousDecision?.previousPayload || params.payload;
|
|
@@ -1781,35 +2302,34 @@ class BaseAgent {
|
|
|
1781
2302
|
let progressMessage;
|
|
1782
2303
|
if (actions.length === 1) {
|
|
1783
2304
|
const aa = actions[0];
|
|
1784
|
-
progressMessage = `Executing
|
|
2305
|
+
progressMessage = `Executing **${aa.name}** action`;
|
|
1785
2306
|
if (aa.params && Object.keys(aa.params).length > 0) {
|
|
1786
2307
|
const paramsList = Object.entries(aa.params)
|
|
1787
2308
|
.map(([key, value]) => {
|
|
1788
|
-
const displayValue =
|
|
1789
|
-
|
|
1790
|
-
: String(value);
|
|
1791
|
-
return ` - \`${key}\`: ${displayValue}`;
|
|
2309
|
+
const displayValue = this.formatParamValueForMessage(value);
|
|
2310
|
+
return `• **${key}**: ${displayValue}`;
|
|
1792
2311
|
})
|
|
1793
2312
|
.join('\n');
|
|
1794
|
-
progressMessage +=
|
|
2313
|
+
progressMessage += ` with parameters:\n${paramsList}`;
|
|
2314
|
+
}
|
|
2315
|
+
else {
|
|
2316
|
+
progressMessage += '.';
|
|
1795
2317
|
}
|
|
1796
2318
|
}
|
|
1797
2319
|
else {
|
|
1798
|
-
progressMessage = `Executing
|
|
1799
|
-
|
|
1800
|
-
progressMessage += `\n\n• **${aa.name}**`;
|
|
2320
|
+
progressMessage = `Executing **${actions.length} actions** in parallel:\n\n` + actions.map((aa, index) => {
|
|
2321
|
+
let actionText = `${index + 1}. **${aa.name}**`;
|
|
1801
2322
|
if (aa.params && Object.keys(aa.params).length > 0) {
|
|
1802
2323
|
const paramsList = Object.entries(aa.params)
|
|
1803
2324
|
.map(([key, value]) => {
|
|
1804
|
-
const displayValue =
|
|
1805
|
-
|
|
1806
|
-
: String(value);
|
|
1807
|
-
return ` - \`${key}\`: ${displayValue}`;
|
|
2325
|
+
const displayValue = this.formatParamValueForMessage(value);
|
|
2326
|
+
return ` • **${key}**: ${displayValue}`;
|
|
1808
2327
|
})
|
|
1809
2328
|
.join('\n');
|
|
1810
|
-
|
|
2329
|
+
actionText += `\n${paramsList}`;
|
|
1811
2330
|
}
|
|
1812
|
-
|
|
2331
|
+
return actionText;
|
|
2332
|
+
}).join('\n\n');
|
|
1813
2333
|
}
|
|
1814
2334
|
params.onProgress?.({
|
|
1815
2335
|
step: 'action_execution',
|
|
@@ -1821,9 +2341,38 @@ class BaseAgent {
|
|
|
1821
2341
|
},
|
|
1822
2342
|
displayMode: 'live'
|
|
1823
2343
|
});
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2344
|
+
let actionMessage;
|
|
2345
|
+
if (actions.length === 1) {
|
|
2346
|
+
const aa = actions[0];
|
|
2347
|
+
actionMessage = `I'm executing the **${aa.name}** action`;
|
|
2348
|
+
if (aa.params && Object.keys(aa.params).length > 0) {
|
|
2349
|
+
const paramsList = Object.entries(aa.params)
|
|
2350
|
+
.map(([key, value]) => {
|
|
2351
|
+
const displayValue = this.formatParamValueForMessage(value);
|
|
2352
|
+
return `• **${key}**: ${displayValue}`;
|
|
2353
|
+
})
|
|
2354
|
+
.join('\n');
|
|
2355
|
+
actionMessage += ` with parameters:\n${paramsList}`;
|
|
2356
|
+
}
|
|
2357
|
+
else {
|
|
2358
|
+
actionMessage += '.';
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2362
|
+
actionMessage = `I'm executing **${actions.length} actions** in parallel:\n\n` + actions.map((aa, index) => {
|
|
2363
|
+
let actionText = `${index + 1}. **${aa.name}**`;
|
|
2364
|
+
if (aa.params && Object.keys(aa.params).length > 0) {
|
|
2365
|
+
const paramsList = Object.entries(aa.params)
|
|
2366
|
+
.map(([key, value]) => {
|
|
2367
|
+
const displayValue = this.formatParamValueForMessage(value);
|
|
2368
|
+
return ` • **${key}**: ${displayValue}`;
|
|
2369
|
+
})
|
|
2370
|
+
.join('\n');
|
|
2371
|
+
actionText += `\n${paramsList}`;
|
|
2372
|
+
}
|
|
2373
|
+
return actionText;
|
|
2374
|
+
}).join('\n\n');
|
|
2375
|
+
}
|
|
1827
2376
|
params.conversationMessages.push({
|
|
1828
2377
|
role: 'assistant',
|
|
1829
2378
|
content: actionMessage
|
|
@@ -1892,9 +2441,44 @@ class BaseAgent {
|
|
|
1892
2441
|
});
|
|
1893
2442
|
const failedActions = actionSummaries.filter(a => !a.success);
|
|
1894
2443
|
const resultsMessage = (failedActions.length > 0 ? `${failedActions.length} of ${actionSummaries.length} failed:` : `Action results:`) + `\n${JSON.stringify(actionSummaries, null, 2)}`;
|
|
2444
|
+
let metadata;
|
|
2445
|
+
const agentActionConfigs = actionResults
|
|
2446
|
+
.map(r => agentActions.find(aa => aa.ActionID === r.actionEntity.ID))
|
|
2447
|
+
.filter(aa => aa != null);
|
|
2448
|
+
if (agentActionConfigs.length > 0) {
|
|
2449
|
+
let minExpirationTurns = null;
|
|
2450
|
+
let expirationMode = 'None';
|
|
2451
|
+
let compactMode;
|
|
2452
|
+
let compactLength;
|
|
2453
|
+
let compactPromptId;
|
|
2454
|
+
for (const agentAction of agentActionConfigs) {
|
|
2455
|
+
if (agentAction.ResultExpirationTurns != null) {
|
|
2456
|
+
if (minExpirationTurns === null || agentAction.ResultExpirationTurns < minExpirationTurns) {
|
|
2457
|
+
minExpirationTurns = agentAction.ResultExpirationTurns;
|
|
2458
|
+
expirationMode = agentAction.ResultExpirationMode || 'None';
|
|
2459
|
+
compactMode = agentAction.CompactMode;
|
|
2460
|
+
compactLength = agentAction.CompactLength;
|
|
2461
|
+
compactPromptId = agentAction.CompactPromptID;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
if (minExpirationTurns !== null && expirationMode !== 'None') {
|
|
2466
|
+
const currentStepCount = this._agentRun?.Steps?.length || 0;
|
|
2467
|
+
metadata = {
|
|
2468
|
+
turnAdded: currentStepCount,
|
|
2469
|
+
messageType: 'action-result',
|
|
2470
|
+
expirationTurns: minExpirationTurns,
|
|
2471
|
+
expirationMode: expirationMode,
|
|
2472
|
+
compactMode: compactMode,
|
|
2473
|
+
compactLength: compactLength,
|
|
2474
|
+
compactPromptId: compactPromptId
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
1895
2478
|
params.conversationMessages.push({
|
|
1896
2479
|
role: 'user',
|
|
1897
|
-
content: resultsMessage
|
|
2480
|
+
content: resultsMessage,
|
|
2481
|
+
metadata: metadata
|
|
1898
2482
|
});
|
|
1899
2483
|
let finalPayload = currentPayload;
|
|
1900
2484
|
try {
|
|
@@ -1960,6 +2544,7 @@ class BaseAgent {
|
|
|
1960
2544
|
confidence: previousDecision.confidence,
|
|
1961
2545
|
previousPayload: previousDecision.previousPayload,
|
|
1962
2546
|
newPayload: previousDecision.newPayload || previousDecision.previousPayload,
|
|
2547
|
+
suggestedResponses: previousDecision.suggestedResponses
|
|
1963
2548
|
};
|
|
1964
2549
|
}
|
|
1965
2550
|
async createFailureResult(errorMessage, contextUser) {
|
|
@@ -2029,7 +2614,8 @@ class BaseAgent {
|
|
|
2029
2614
|
return {
|
|
2030
2615
|
success: finalStep.step === 'Success' || finalStep.step === 'Chat',
|
|
2031
2616
|
payload,
|
|
2032
|
-
agentRun: this._agentRun
|
|
2617
|
+
agentRun: this._agentRun,
|
|
2618
|
+
suggestedResponses: finalStep.suggestedResponses
|
|
2033
2619
|
};
|
|
2034
2620
|
}
|
|
2035
2621
|
calculateTokenStats() {
|
|
@@ -2090,6 +2676,236 @@ class BaseAgent {
|
|
|
2090
2676
|
}
|
|
2091
2677
|
return violations;
|
|
2092
2678
|
}
|
|
2679
|
+
async pruneAndCompactExpiredMessages(params, currentTurn) {
|
|
2680
|
+
const messagesToCompact = [];
|
|
2681
|
+
const messagesToRemove = [];
|
|
2682
|
+
for (let i = 0; i < params.conversationMessages.length; i++) {
|
|
2683
|
+
const msg = params.conversationMessages[i];
|
|
2684
|
+
if (!msg.metadata?.expirationTurns && msg.metadata?.expirationTurns !== 0) {
|
|
2685
|
+
continue;
|
|
2686
|
+
}
|
|
2687
|
+
if (msg.metadata.expirationMode === 'None') {
|
|
2688
|
+
continue;
|
|
2689
|
+
}
|
|
2690
|
+
const turnAdded = msg.metadata.turnAdded || 0;
|
|
2691
|
+
const turnsAlive = currentTurn - turnAdded;
|
|
2692
|
+
if (turnsAlive > msg.metadata.expirationTurns) {
|
|
2693
|
+
msg.metadata.isExpired = true;
|
|
2694
|
+
if (msg.metadata.expirationMode === 'Remove') {
|
|
2695
|
+
messagesToRemove.push(i);
|
|
2696
|
+
this.emitMessageLifecycleEvent({
|
|
2697
|
+
type: 'message-expired',
|
|
2698
|
+
turn: currentTurn,
|
|
2699
|
+
messageIndex: i,
|
|
2700
|
+
message: msg,
|
|
2701
|
+
reason: `Expired after ${turnsAlive} turns (limit: ${msg.metadata.expirationTurns})`
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
else if (msg.metadata.expirationMode === 'Compact') {
|
|
2705
|
+
if (msg.metadata.compactMode) {
|
|
2706
|
+
messagesToCompact.push({
|
|
2707
|
+
index: i,
|
|
2708
|
+
message: msg,
|
|
2709
|
+
metadata: {
|
|
2710
|
+
compactMode: msg.metadata.compactMode,
|
|
2711
|
+
compactLength: msg.metadata.compactLength || 500,
|
|
2712
|
+
compactPromptId: msg.metadata.compactPromptId || '',
|
|
2713
|
+
originalLength: typeof msg.content === 'string'
|
|
2714
|
+
? msg.content.length
|
|
2715
|
+
: JSON.stringify(msg.content).length
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
const preserveOriginal = params.messageExpirationOverride?.preserveOriginalContent !== false;
|
|
2723
|
+
for (const item of messagesToCompact) {
|
|
2724
|
+
const originalContent = item.message.content;
|
|
2725
|
+
const compacted = await this.compactMessage(item.message, item.metadata, params);
|
|
2726
|
+
const originalTokens = this.estimateTokens(originalContent);
|
|
2727
|
+
const compactedTokens = this.estimateTokens(compacted);
|
|
2728
|
+
const tokensSaved = originalTokens - compactedTokens;
|
|
2729
|
+
params.conversationMessages[item.index] = {
|
|
2730
|
+
...item.message,
|
|
2731
|
+
content: compacted,
|
|
2732
|
+
metadata: {
|
|
2733
|
+
...item.message.metadata,
|
|
2734
|
+
wasCompacted: true,
|
|
2735
|
+
originalContent: preserveOriginal ? originalContent : undefined,
|
|
2736
|
+
originalLength: item.metadata.originalLength,
|
|
2737
|
+
tokensSaved,
|
|
2738
|
+
canExpand: preserveOriginal
|
|
2739
|
+
}
|
|
2740
|
+
};
|
|
2741
|
+
this.emitMessageLifecycleEvent({
|
|
2742
|
+
type: 'message-compacted',
|
|
2743
|
+
turn: currentTurn,
|
|
2744
|
+
messageIndex: item.index,
|
|
2745
|
+
message: params.conversationMessages[item.index],
|
|
2746
|
+
reason: `Compacted using ${item.metadata.compactMode} (saved ${tokensSaved} tokens)`,
|
|
2747
|
+
tokensSaved
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
for (let i = messagesToRemove.length - 1; i >= 0; i--) {
|
|
2751
|
+
const index = messagesToRemove[i];
|
|
2752
|
+
const removed = params.conversationMessages.splice(index, 1)[0];
|
|
2753
|
+
this.emitMessageLifecycleEvent({
|
|
2754
|
+
type: 'message-removed',
|
|
2755
|
+
turn: currentTurn,
|
|
2756
|
+
messageIndex: index,
|
|
2757
|
+
message: removed,
|
|
2758
|
+
reason: 'Removed due to expiration'
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
if (params.verbose && (messagesToCompact.length > 0 || messagesToRemove.length > 0)) {
|
|
2762
|
+
const totalSaved = messagesToCompact.reduce((sum, item) => {
|
|
2763
|
+
const msg = params.conversationMessages[item.index];
|
|
2764
|
+
return sum + (msg.metadata?.tokensSaved || 0);
|
|
2765
|
+
}, 0);
|
|
2766
|
+
console.log(`[Turn ${currentTurn}] Message pruning: ` +
|
|
2767
|
+
`${messagesToCompact.length} compacted (saved ~${totalSaved} tokens), ` +
|
|
2768
|
+
`${messagesToRemove.length} removed`);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
async createCompactionStep(prompt, message, params) {
|
|
2772
|
+
if (!this._agentRun) {
|
|
2773
|
+
throw new Error('Cannot create compaction step: agent run not initialized');
|
|
2774
|
+
}
|
|
2775
|
+
const md = new core_1.Metadata();
|
|
2776
|
+
const step = await md.GetEntityObject('MJ: AI Agent Run Steps', params.contextUser);
|
|
2777
|
+
step.NewRecord();
|
|
2778
|
+
step.AgentRunID = this._agentRun.ID;
|
|
2779
|
+
step.StepType = 'Prompt';
|
|
2780
|
+
step.Status = 'Running';
|
|
2781
|
+
step.InputData = JSON.stringify({
|
|
2782
|
+
stepName: 'Message Compaction',
|
|
2783
|
+
description: `Compacting message using AI Summary (${message.metadata?.messageType || 'unknown'} from turn ${message.metadata?.turnAdded || 0})`,
|
|
2784
|
+
messageType: message.metadata?.messageType,
|
|
2785
|
+
turnAdded: message.metadata?.turnAdded,
|
|
2786
|
+
originalLength: message.metadata?.originalLength ||
|
|
2787
|
+
(typeof message.content === 'string' ? message.content.length : JSON.stringify(message.content).length),
|
|
2788
|
+
compactMode: 'AI Summary',
|
|
2789
|
+
promptId: prompt.ID,
|
|
2790
|
+
promptName: prompt.Name
|
|
2791
|
+
});
|
|
2792
|
+
await step.Save();
|
|
2793
|
+
return step;
|
|
2794
|
+
}
|
|
2795
|
+
async updateCompactionStep(step, result, message, params) {
|
|
2796
|
+
step.Status = result.success ? 'Completed' : 'Failed';
|
|
2797
|
+
const promptTokens = result.promptTokens || 0;
|
|
2798
|
+
const completionTokens = result.completionTokens || 0;
|
|
2799
|
+
step.OutputData = JSON.stringify({
|
|
2800
|
+
success: result.success,
|
|
2801
|
+
summaryLength: result.result?.summary?.length || 0,
|
|
2802
|
+
tokensSaved: message.metadata?.tokensSaved || 0,
|
|
2803
|
+
promptTokens,
|
|
2804
|
+
completionTokens,
|
|
2805
|
+
totalTokens: promptTokens + completionTokens,
|
|
2806
|
+
cost: result.cost
|
|
2807
|
+
});
|
|
2808
|
+
if (!result.success) {
|
|
2809
|
+
step.ErrorMessage = result.errorMessage || 'AI Summary compaction failed';
|
|
2810
|
+
}
|
|
2811
|
+
await step.Save();
|
|
2812
|
+
}
|
|
2813
|
+
async compactMessage(message, metadata, params) {
|
|
2814
|
+
const originalContent = typeof message.content === 'string'
|
|
2815
|
+
? message.content
|
|
2816
|
+
: JSON.stringify(message.content);
|
|
2817
|
+
switch (metadata.compactMode) {
|
|
2818
|
+
case 'First N Chars': {
|
|
2819
|
+
const length = metadata.compactLength;
|
|
2820
|
+
if (originalContent.length <= length) {
|
|
2821
|
+
return originalContent;
|
|
2822
|
+
}
|
|
2823
|
+
const truncated = originalContent.substring(0, length);
|
|
2824
|
+
return `${truncated}...\n\n[Compacted: showing first ${length} of ${originalContent.length} characters. Agent can request expansion if needed.]`;
|
|
2825
|
+
}
|
|
2826
|
+
case 'AI Summary': {
|
|
2827
|
+
try {
|
|
2828
|
+
const promptId = metadata.compactPromptId || this.getSystemDefaultCompactPromptId();
|
|
2829
|
+
const prompt = aiengine_1.AIEngine.Instance.Prompts.find(p => p.ID === promptId);
|
|
2830
|
+
if (!prompt) {
|
|
2831
|
+
console.warn(`Compact prompt ${promptId} not found, falling back to First N Chars`);
|
|
2832
|
+
return this.compactMessage(message, { ...metadata, compactMode: 'First N Chars' }, params);
|
|
2833
|
+
}
|
|
2834
|
+
const step = await this.createCompactionStep(prompt, message, params);
|
|
2835
|
+
const promptParams = new ai_core_plus_1.AIPromptParams();
|
|
2836
|
+
promptParams.prompt = prompt;
|
|
2837
|
+
promptParams.data = {
|
|
2838
|
+
originalContent,
|
|
2839
|
+
originalLength: metadata.originalLength,
|
|
2840
|
+
targetLength: metadata.compactLength || 500,
|
|
2841
|
+
messageType: message.metadata?.messageType || 'unknown',
|
|
2842
|
+
turnAdded: message.metadata?.turnAdded || 0
|
|
2843
|
+
};
|
|
2844
|
+
promptParams.contextUser = params.contextUser;
|
|
2845
|
+
const runner = new ai_prompts_1.AIPromptRunner();
|
|
2846
|
+
const result = await runner.ExecutePrompt(promptParams);
|
|
2847
|
+
await this.updateCompactionStep(step, result, message, params);
|
|
2848
|
+
if (!result.success || !result.result?.summary) {
|
|
2849
|
+
console.warn('AI summary failed, falling back to First N Chars');
|
|
2850
|
+
return this.compactMessage(message, { ...metadata, compactMode: 'First N Chars' }, params);
|
|
2851
|
+
}
|
|
2852
|
+
return `[AI Summary of ${metadata.originalLength} chars. Agent can request full expansion if needed.]\n\n${result.result.summary}`;
|
|
2853
|
+
}
|
|
2854
|
+
catch (error) {
|
|
2855
|
+
console.error('Error during AI summary:', error);
|
|
2856
|
+
return this.compactMessage(message, { ...metadata, compactMode: 'First N Chars' }, params);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
default:
|
|
2860
|
+
return originalContent;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
getSystemDefaultCompactPromptId() {
|
|
2864
|
+
const prompt = aiengine_1.AIEngine.Instance.Prompts.find(p => p.Name === 'Compact Agent Message');
|
|
2865
|
+
if (!prompt) {
|
|
2866
|
+
console.warn('System default compact prompt not found. Ensure "Compact Agent Message" prompt exists.');
|
|
2867
|
+
return '';
|
|
2868
|
+
}
|
|
2869
|
+
return prompt.ID;
|
|
2870
|
+
}
|
|
2871
|
+
estimateTokens(content) {
|
|
2872
|
+
const text = typeof content === 'string'
|
|
2873
|
+
? content
|
|
2874
|
+
: JSON.stringify(content);
|
|
2875
|
+
return Math.ceil(text.length / 4);
|
|
2876
|
+
}
|
|
2877
|
+
emitMessageLifecycleEvent(event) {
|
|
2878
|
+
if (this._messageLifecycleCallback) {
|
|
2879
|
+
this._messageLifecycleCallback(event);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
executeExpandMessageStep(request, params, currentTurn) {
|
|
2883
|
+
const messageIndex = request.messageIndex;
|
|
2884
|
+
const reason = request.expandReason;
|
|
2885
|
+
if (messageIndex === undefined || messageIndex < 0 || messageIndex >= params.conversationMessages.length) {
|
|
2886
|
+
console.warn(`Cannot expand message: index ${messageIndex} out of bounds`);
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
const message = params.conversationMessages[messageIndex];
|
|
2890
|
+
if (!message.metadata?.canExpand || !message.metadata?.originalContent) {
|
|
2891
|
+
console.warn(`Cannot expand message at index ${messageIndex}: not expandable or no original content`);
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
message.content = message.metadata.originalContent;
|
|
2895
|
+
message.metadata.wasCompacted = false;
|
|
2896
|
+
message.metadata.canExpand = false;
|
|
2897
|
+
delete message.metadata.originalContent;
|
|
2898
|
+
this.emitMessageLifecycleEvent({
|
|
2899
|
+
type: 'message-expanded',
|
|
2900
|
+
turn: currentTurn,
|
|
2901
|
+
messageIndex,
|
|
2902
|
+
message,
|
|
2903
|
+
reason: reason || 'Agent requested expansion'
|
|
2904
|
+
});
|
|
2905
|
+
if (params.verbose) {
|
|
2906
|
+
console.log(`[Turn ${currentTurn}] Expanded message at index ${messageIndex}`);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2093
2909
|
}
|
|
2094
2910
|
exports.BaseAgent = BaseAgent;
|
|
2095
2911
|
BaseAgent.MAX_VALIDATION_RETRIES = 10;
|