@memberjunction/ai-agents 5.22.0 → 5.23.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 +4 -1
- package/dist/AgentRunner.d.ts +36 -0
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +70 -15
- package/dist/AgentRunner.js.map +1 -1
- package/dist/ClientToolRequestManager.d.ts +62 -0
- package/dist/ClientToolRequestManager.d.ts.map +1 -0
- package/dist/ClientToolRequestManager.js +107 -0
- package/dist/ClientToolRequestManager.js.map +1 -0
- package/dist/KnowledgeAgent.d.ts +5 -3
- package/dist/KnowledgeAgent.d.ts.map +1 -1
- package/dist/KnowledgeAgent.js +49 -58
- package/dist/KnowledgeAgent.js.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.d.ts +21 -10
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +15 -6
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +16 -2
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +28 -3
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +30 -0
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +273 -7
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/base-agent.js
CHANGED
|
@@ -24,6 +24,7 @@ import { AgentRunner } from './AgentRunner.js';
|
|
|
24
24
|
import { PayloadManager } from './PayloadManager.js';
|
|
25
25
|
import { ScratchpadManager } from './ScratchpadManager.js';
|
|
26
26
|
import { AgentDataPreloader } from './AgentDataPreloader.js';
|
|
27
|
+
import { ClientToolRequestManager } from './ClientToolRequestManager.js';
|
|
27
28
|
import { ConversationMessageResolver } from './utils/ConversationMessageResolver.js';
|
|
28
29
|
import _ from 'lodash';
|
|
29
30
|
/**
|
|
@@ -1673,6 +1674,9 @@ export class BaseAgent {
|
|
|
1673
1674
|
case 'While':
|
|
1674
1675
|
// While loops are valid - no additional validation needed
|
|
1675
1676
|
return nextStep;
|
|
1677
|
+
case 'ClientTools':
|
|
1678
|
+
// Client tools are valid - execution handled by executeClientToolsStep
|
|
1679
|
+
return nextStep;
|
|
1676
1680
|
default:
|
|
1677
1681
|
// if we get here, the next step is not recognized, we can return a retry step
|
|
1678
1682
|
this.logError(`Invalid next step '${nextStep.step}' for agent '${params.agent.Name}'`, {
|
|
@@ -2598,9 +2602,9 @@ export class BaseAgent {
|
|
|
2598
2602
|
? currentStepCount - msg.metadata.turnAdded
|
|
2599
2603
|
: 0,
|
|
2600
2604
|
tokens: this.estimateTokens(msg.content),
|
|
2601
|
-
|
|
2605
|
+
isToolResult: this.IsToolResultMessage(msg)
|
|
2602
2606
|
}))
|
|
2603
|
-
.filter(c => c.
|
|
2607
|
+
.filter(c => c.isToolResult && c.age >= minAge)
|
|
2604
2608
|
.sort((a, b) => b.age - a.age); // Oldest first
|
|
2605
2609
|
// Remove messages until we've saved enough
|
|
2606
2610
|
for (const candidate of candidates) {
|
|
@@ -2651,10 +2655,10 @@ export class BaseAgent {
|
|
|
2651
2655
|
? currentStepCount - msg.metadata.turnAdded
|
|
2652
2656
|
: 0,
|
|
2653
2657
|
tokens: this.estimateTokens(msg.content),
|
|
2654
|
-
|
|
2658
|
+
isToolResult: this.IsToolResultMessage(msg),
|
|
2655
2659
|
alreadyCompacted: msg.metadata?.wasCompacted === true
|
|
2656
2660
|
}))
|
|
2657
|
-
.filter(c => c.
|
|
2661
|
+
.filter(c => c.isToolResult && c.age >= minAge && !c.alreadyCompacted)
|
|
2658
2662
|
.sort((a, b) => b.age - a.age); // Oldest first
|
|
2659
2663
|
for (const candidate of candidates) {
|
|
2660
2664
|
if (tokensSaved >= tokensToSave)
|
|
@@ -2713,10 +2717,10 @@ export class BaseAgent {
|
|
|
2713
2717
|
message: msg,
|
|
2714
2718
|
index: index,
|
|
2715
2719
|
tokens: this.estimateTokens(msg.content),
|
|
2716
|
-
|
|
2720
|
+
isToolResult: this.IsToolResultMessage(msg),
|
|
2717
2721
|
alreadyCompacted: msg.metadata?.wasCompacted === true
|
|
2718
2722
|
}))
|
|
2719
|
-
.filter(c => c.
|
|
2723
|
+
.filter(c => c.isToolResult && !c.alreadyCompacted && c.tokens > 200)
|
|
2720
2724
|
.sort((a, b) => b.tokens - a.tokens); // Largest first
|
|
2721
2725
|
for (const candidate of candidates) {
|
|
2722
2726
|
if (tokensSaved >= tokensToSave)
|
|
@@ -3004,6 +3008,10 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3004
3008
|
const agentType = engine.AgentTypes.find(at => UUIDsEqual(at.ID, agent.TypeID));
|
|
3005
3009
|
const runtimePromptParamOverrides = extraData?.__agentTypePromptParams;
|
|
3006
3010
|
const agentTypePromptParams = this.buildAgentTypePromptParams(agentType, agent, runtimePromptParamOverrides);
|
|
3011
|
+
// Build client tool details for the prompt
|
|
3012
|
+
const clientToolDetails = this.buildClientToolPromptSection(agent, extraData);
|
|
3013
|
+
// Build app context section if provided in extraData
|
|
3014
|
+
const appContext = this.buildAppContextSection(extraData);
|
|
3007
3015
|
const contextData = {
|
|
3008
3016
|
agentName: agent.Name,
|
|
3009
3017
|
agentDescription: agent.Description,
|
|
@@ -3012,6 +3020,8 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3012
3020
|
subAgentDetails: this.formatSubAgentDetails(uniqueActiveSubAgents),
|
|
3013
3021
|
actionCount: activeActions.length,
|
|
3014
3022
|
actionDetails: this.formatActionDetails(activeActions),
|
|
3023
|
+
clientToolDetails: clientToolDetails,
|
|
3024
|
+
appContext: appContext,
|
|
3015
3025
|
};
|
|
3016
3026
|
// Build the final result with __agentTypePromptParams injected
|
|
3017
3027
|
// Note: extraData can override contextData properties, but __agentTypePromptParams
|
|
@@ -3023,7 +3033,11 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3023
3033
|
if (extraData) {
|
|
3024
3034
|
// Spread extraData but don't let it override __agentTypePromptParams
|
|
3025
3035
|
// (which was already built with runtime overrides included)
|
|
3026
|
-
|
|
3036
|
+
// Spread extraData into the result but exclude properties that were already
|
|
3037
|
+
// processed into formatted prompt sections above. Without this exclusion,
|
|
3038
|
+
// the raw objects from extraData would overwrite the formatted markdown strings
|
|
3039
|
+
// in contextData (e.g., appContext object would replace the markdown string).
|
|
3040
|
+
const { __agentTypePromptParams: _ignored, appContext: _ignoredAppContext, ...restExtraData } = extraData;
|
|
3027
3041
|
return {
|
|
3028
3042
|
...result,
|
|
3029
3043
|
...restExtraData
|
|
@@ -3501,6 +3515,111 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3501
3515
|
return lines.join('\n');
|
|
3502
3516
|
}).join('\n\n');
|
|
3503
3517
|
}
|
|
3518
|
+
/**
|
|
3519
|
+
* Build the client tool prompt section for system prompt injection.
|
|
3520
|
+
*
|
|
3521
|
+
* Tool sources (checked in order, all merged — first registration wins):
|
|
3522
|
+
* 1. Metadata tools from AI Agent Client Tools junction table
|
|
3523
|
+
* 2. Session-level enriched tools from ClientToolRequestManager (set by client SDK)
|
|
3524
|
+
* 3. Tools provided directly in extraData.clientTools (runtime override)
|
|
3525
|
+
*/
|
|
3526
|
+
buildClientToolPromptSection(agent, extraData) {
|
|
3527
|
+
const toolMap = new Map();
|
|
3528
|
+
// 1. Metadata tools from junction table (authoritative source)
|
|
3529
|
+
const engine = AIEngine.Instance;
|
|
3530
|
+
const metadataTools = engine.GetClientToolsForAgent(agent.ID);
|
|
3531
|
+
for (const tool of metadataTools) {
|
|
3532
|
+
toolMap.set(tool.Name, {
|
|
3533
|
+
Name: tool.Name,
|
|
3534
|
+
Description: tool.Description,
|
|
3535
|
+
InputSchema: tool.InputSchemaJSON ? JSON.parse(tool.InputSchemaJSON) : {},
|
|
3536
|
+
OutputSchema: tool.OutputSchemaJSON ? JSON.parse(tool.OutputSchemaJSON) : undefined,
|
|
3537
|
+
Category: tool.Category || undefined,
|
|
3538
|
+
DefaultTimeoutMs: tool.DefaultTimeoutMs || undefined
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
// 2. Session-level enriched tools (client SDK decorated tools)
|
|
3542
|
+
const sessionID = extraData?.sessionID;
|
|
3543
|
+
if (sessionID) {
|
|
3544
|
+
for (const tool of ClientToolRequestManager.Instance.GetSessionTools(sessionID)) {
|
|
3545
|
+
if (!toolMap.has(tool.Name)) {
|
|
3546
|
+
toolMap.set(tool.Name, tool);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
// 3. Runtime extraData override
|
|
3551
|
+
if (extraData?.clientTools) {
|
|
3552
|
+
for (const tool of extraData.clientTools) {
|
|
3553
|
+
if (!toolMap.has(tool.Name)) {
|
|
3554
|
+
toolMap.set(tool.Name, tool);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
const tools = Array.from(toolMap.values());
|
|
3559
|
+
if (tools.length === 0) {
|
|
3560
|
+
return ''; // No client tools available
|
|
3561
|
+
}
|
|
3562
|
+
const lines = [];
|
|
3563
|
+
lines.push('### Client Tools (execute in the user\'s browser)');
|
|
3564
|
+
lines.push('Client tools run in the user\'s browser and interact with the UI. Use these when you need');
|
|
3565
|
+
lines.push('to navigate the user somewhere, display a specific view, switch dashboard tabs, or show');
|
|
3566
|
+
lines.push('records. When you choose client tools, set nextStep.type to "ClientTools".');
|
|
3567
|
+
lines.push('');
|
|
3568
|
+
lines.push('NOTE: Do NOT use client tools for asking the user questions or collecting input.');
|
|
3569
|
+
lines.push('Use the "Chat" step for that. Client tools are for programmatic UI interaction only.');
|
|
3570
|
+
lines.push('');
|
|
3571
|
+
for (const tool of tools) {
|
|
3572
|
+
const categoryTag = tool.Category ? ` [${tool.Category}]` : '';
|
|
3573
|
+
lines.push(`- **${tool.Name}**${categoryTag}: ${tool.Description}`);
|
|
3574
|
+
// Show input parameters from InputSchema
|
|
3575
|
+
const props = tool.InputSchema?.properties;
|
|
3576
|
+
const required = tool.InputSchema?.required;
|
|
3577
|
+
if (props) {
|
|
3578
|
+
const paramParts = Object.entries(props).map(([name, schema]) => {
|
|
3579
|
+
const req = required?.includes(name) ? '\\*' : '';
|
|
3580
|
+
const desc = schema.description ? ` — ${schema.description}` : '';
|
|
3581
|
+
return `\`${name}\`${req}${desc}`;
|
|
3582
|
+
});
|
|
3583
|
+
lines.push(` Inputs: ${paramParts.join(', ')}`);
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
return lines.join('\n');
|
|
3587
|
+
}
|
|
3588
|
+
/**
|
|
3589
|
+
* Build the app context section for system prompt injection.
|
|
3590
|
+
* Reads the AppContextSnapshot from extraData.appContext and formats
|
|
3591
|
+
* it as a concise markdown section the LLM can reference.
|
|
3592
|
+
*/
|
|
3593
|
+
buildAppContextSection(extraData) {
|
|
3594
|
+
const ctx = extraData?.appContext;
|
|
3595
|
+
if (!ctx)
|
|
3596
|
+
return '';
|
|
3597
|
+
const app = ctx['App'];
|
|
3598
|
+
const activeNav = ctx['ActiveNavItem'];
|
|
3599
|
+
const otherNavs = ctx['OtherNavItems'];
|
|
3600
|
+
const user = ctx['User'];
|
|
3601
|
+
if (!app?.Name)
|
|
3602
|
+
return '';
|
|
3603
|
+
const lines = [];
|
|
3604
|
+
lines.push('### Current Application Context');
|
|
3605
|
+
lines.push(`The user is currently in the **${app.Name}** application${app.Description ? ` — ${app.Description}` : ''}.`);
|
|
3606
|
+
if (activeNav?.Name) {
|
|
3607
|
+
lines.push('');
|
|
3608
|
+
lines.push(`**Active view:** ${activeNav.Name}${activeNav.Description ? ` — ${activeNav.Description}` : ''}${activeNav.ResourceType ? ` (${activeNav.ResourceType})` : ''}`);
|
|
3609
|
+
}
|
|
3610
|
+
if (otherNavs && otherNavs.length > 0) {
|
|
3611
|
+
lines.push('');
|
|
3612
|
+
lines.push('**Other views available in this app:**');
|
|
3613
|
+
for (const nav of otherNavs) {
|
|
3614
|
+
lines.push(`- ${nav.Name}${nav.Description ? ` — ${nav.Description}` : ''}`);
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
if (user?.Name) {
|
|
3618
|
+
lines.push('');
|
|
3619
|
+
lines.push(`**User:** ${user.Name}${user.Roles?.length ? ` (Roles: ${user.Roles.join(', ')})` : ''}`);
|
|
3620
|
+
}
|
|
3621
|
+
return lines.join('\n');
|
|
3622
|
+
}
|
|
3504
3623
|
/**
|
|
3505
3624
|
* Formats a single action parameter as a compact inline string.
|
|
3506
3625
|
* Uses \* suffix for required, (array) when IsArray, and only shows
|
|
@@ -4247,6 +4366,10 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
4247
4366
|
return await this.processSubAgentStep(params, previousDecision, undefined, undefined, stepCount);
|
|
4248
4367
|
case 'Actions':
|
|
4249
4368
|
return await this.executeActionsStep(params, previousDecision, undefined, true, stepCount);
|
|
4369
|
+
// Type assertion required because 'ClientTools' is not yet in the DB StepType value list.
|
|
4370
|
+
// The LoopAgentType.DetermineNextStep() emits this value when the LLM chooses client tools.
|
|
4371
|
+
case 'ClientTools':
|
|
4372
|
+
return await this.executeClientToolsStep(params, config, previousDecision, stepCount);
|
|
4250
4373
|
case 'Chat':
|
|
4251
4374
|
return await this.executeChatStep(params, previousDecision);
|
|
4252
4375
|
case 'Success':
|
|
@@ -4620,6 +4743,17 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
4620
4743
|
else if (updatedNextStep.step === 'Success' || updatedNextStep.step === 'Failed') {
|
|
4621
4744
|
return { ...updatedNextStep, terminate: true };
|
|
4622
4745
|
}
|
|
4746
|
+
else if (updatedNextStep.step === 'ClientTools') {
|
|
4747
|
+
// ClientTools must return terminate: false so the main loop continues
|
|
4748
|
+
// to the next iteration where executeClientToolsStep actually dispatches
|
|
4749
|
+
// and awaits the tool. The LLM's original terminate intent is preserved in
|
|
4750
|
+
// terminateAfterExecution so executeClientToolsStep can honor it post-execution.
|
|
4751
|
+
return {
|
|
4752
|
+
...updatedNextStep,
|
|
4753
|
+
terminate: false,
|
|
4754
|
+
terminateAfterExecution: updatedNextStep.terminate
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4623
4757
|
else {
|
|
4624
4758
|
return { ...updatedNextStep, terminate: false };
|
|
4625
4759
|
}
|
|
@@ -5821,6 +5955,130 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
5821
5955
|
*
|
|
5822
5956
|
* @private
|
|
5823
5957
|
*/
|
|
5958
|
+
// ================================================================
|
|
5959
|
+
// Client Tools Step Execution
|
|
5960
|
+
// ================================================================
|
|
5961
|
+
/**
|
|
5962
|
+
* Execute client-side tools requested by the agent.
|
|
5963
|
+
* Sends each tool invocation via PubSub, awaits the client's response
|
|
5964
|
+
* (or timeout), then adds results to the conversation and continues.
|
|
5965
|
+
*/
|
|
5966
|
+
async executeClientToolsStep(params, config, previousDecision, stepCount = 0) {
|
|
5967
|
+
const clientTools = previousDecision.clientTools ?? [];
|
|
5968
|
+
if (clientTools.length === 0) {
|
|
5969
|
+
// No tools to execute — continue with next prompt
|
|
5970
|
+
return await this.executePromptStep(params, config, previousDecision, stepCount);
|
|
5971
|
+
}
|
|
5972
|
+
if (!params.sessionID) {
|
|
5973
|
+
// No session ID — can't communicate with client
|
|
5974
|
+
const errorMsg = 'Cannot execute client tools: no sessionID provided in ExecuteAgentParams';
|
|
5975
|
+
LogError(errorMsg);
|
|
5976
|
+
params.conversationMessages.push({
|
|
5977
|
+
role: 'user',
|
|
5978
|
+
content: `Client tool execution skipped: ${errorMsg}`
|
|
5979
|
+
});
|
|
5980
|
+
return await this.executePromptStep(params, config, previousDecision, stepCount);
|
|
5981
|
+
}
|
|
5982
|
+
const currentPayload = previousDecision?.newPayload || previousDecision?.previousPayload || params.payload;
|
|
5983
|
+
// Build assistant message describing the tool invocations
|
|
5984
|
+
const toolMessage = clientTools.length === 1
|
|
5985
|
+
? `I'm invoking the **${clientTools[0].Name}** client tool${clientTools[0].Description ? ` — ${clientTools[0].Description}` : ''}.`
|
|
5986
|
+
: `I'm invoking **${clientTools.length} client tools**:\n\n` +
|
|
5987
|
+
clientTools.map((t, i) => `${i + 1}. **${t.Name}**${t.Description ? ` — ${t.Description}` : ''}`).join('\n');
|
|
5988
|
+
params.conversationMessages.push({
|
|
5989
|
+
role: 'assistant',
|
|
5990
|
+
content: toolMessage
|
|
5991
|
+
});
|
|
5992
|
+
// Report progress
|
|
5993
|
+
params.onProgress?.({
|
|
5994
|
+
step: 'action_execution', // Reuse action_execution step type for progress reporting
|
|
5995
|
+
message: this.formatHierarchicalMessage(toolMessage),
|
|
5996
|
+
metadata: {
|
|
5997
|
+
toolCount: clientTools.length,
|
|
5998
|
+
toolNames: clientTools.map(t => t.Name),
|
|
5999
|
+
stepCount: stepCount + 1,
|
|
6000
|
+
hierarchicalStep: this.buildHierarchicalStep(stepCount + 1, this._parentStepCounts)
|
|
6001
|
+
},
|
|
6002
|
+
displayMode: 'live'
|
|
6003
|
+
});
|
|
6004
|
+
// Resolve default timeout: per-tool > params > agent config > 30s
|
|
6005
|
+
const defaultTimeout = params.clientToolTimeoutMs ?? 30_000;
|
|
6006
|
+
const results = [];
|
|
6007
|
+
const agentRunID = this._agentRun?.ID ?? 'unknown';
|
|
6008
|
+
// Execute tools sequentially (client may not support parallel UI operations)
|
|
6009
|
+
for (const tool of clientTools) {
|
|
6010
|
+
const stepEntity = await this.createStepEntity({
|
|
6011
|
+
// INTENTIONAL: We use 'Actions' as the DB step type because the MJ: AI Agent Run Steps
|
|
6012
|
+
// entity's StepType value list does not yet include 'ClientTools'. A future database
|
|
6013
|
+
// migration will add 'ClientTools' to the allowed values in the StepType CHECK constraint
|
|
6014
|
+
// and CodeGen will regenerate the types. Until then, client tool steps are recorded under
|
|
6015
|
+
// 'Actions' in the run history. The step name ("Client Tool: {name}") distinguishes them.
|
|
6016
|
+
stepType: 'Actions',
|
|
6017
|
+
stepName: `Client Tool: ${tool.Name}`,
|
|
6018
|
+
inputData: { toolName: tool.Name, params: tool.Params },
|
|
6019
|
+
contextUser: params.contextUser,
|
|
6020
|
+
payloadAtStart: currentPayload,
|
|
6021
|
+
payloadAtEnd: currentPayload
|
|
6022
|
+
});
|
|
6023
|
+
const timeoutMs = tool.TimeoutMs ?? defaultTimeout;
|
|
6024
|
+
const response = await ClientToolRequestManager.Instance.RequestClientTool(`ct_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, tool.Name, tool.Params, params.sessionID, agentRunID, timeoutMs, tool.Description);
|
|
6025
|
+
await this.finalizeStepEntity(stepEntity, response.Success, response.ErrorMessage, { result: response.Result });
|
|
6026
|
+
results.push({
|
|
6027
|
+
ToolName: tool.Name,
|
|
6028
|
+
Success: response.Success,
|
|
6029
|
+
Result: response.Result,
|
|
6030
|
+
ErrorMessage: response.ErrorMessage
|
|
6031
|
+
});
|
|
6032
|
+
}
|
|
6033
|
+
// Format results as conversation message
|
|
6034
|
+
const resultsMarkdown = this.formatClientToolResultsAsMarkdown(results);
|
|
6035
|
+
params.conversationMessages.push({
|
|
6036
|
+
role: 'user',
|
|
6037
|
+
content: resultsMarkdown,
|
|
6038
|
+
metadata: {
|
|
6039
|
+
turnAdded: this._promptTurnCount,
|
|
6040
|
+
messageType: 'client-tool-result'
|
|
6041
|
+
}
|
|
6042
|
+
});
|
|
6043
|
+
// If the LLM already declared taskComplete=true alongside the client tools,
|
|
6044
|
+
// honor that intent now that tools have executed — no need for another LLM call.
|
|
6045
|
+
if (previousDecision.terminateAfterExecution) {
|
|
6046
|
+
return {
|
|
6047
|
+
step: 'Success',
|
|
6048
|
+
terminate: true,
|
|
6049
|
+
message: previousDecision.message || 'Client tools executed successfully.',
|
|
6050
|
+
payloadChangeRequest: previousDecision.payloadChangeRequest,
|
|
6051
|
+
scratchpad: previousDecision.scratchpad,
|
|
6052
|
+
responseForm: previousDecision.responseForm,
|
|
6053
|
+
actionableCommands: previousDecision.actionableCommands,
|
|
6054
|
+
automaticCommands: previousDecision.automaticCommands
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
return await this.executePromptStep(params, config, previousDecision, stepCount);
|
|
6058
|
+
}
|
|
6059
|
+
/**
|
|
6060
|
+
* Format client tool results as a compact markdown summary for the conversation.
|
|
6061
|
+
*/
|
|
6062
|
+
formatClientToolResultsAsMarkdown(results) {
|
|
6063
|
+
const failedCount = results.filter(r => !r.Success).length;
|
|
6064
|
+
const header = failedCount > 0
|
|
6065
|
+
? `${failedCount} of ${results.length} client tool(s) failed:`
|
|
6066
|
+
: 'Client tool results:';
|
|
6067
|
+
const lines = results.map(r => {
|
|
6068
|
+
const icon = r.Success ? '✓' : '✗';
|
|
6069
|
+
let line = `${icon} **${r.ToolName}**: ${r.Success ? 'succeeded' : 'failed'}`;
|
|
6070
|
+
if (r.ErrorMessage)
|
|
6071
|
+
line += ` — ${r.ErrorMessage}`;
|
|
6072
|
+
if (r.Success && r.Result != null) {
|
|
6073
|
+
const resultStr = typeof r.Result === 'string' ? r.Result : JSON.stringify(r.Result);
|
|
6074
|
+
if (resultStr.length <= 500) {
|
|
6075
|
+
line += `\n Result: ${resultStr}`;
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
return line;
|
|
6079
|
+
});
|
|
6080
|
+
return `${header}\n${lines.join('\n')}`;
|
|
6081
|
+
}
|
|
5824
6082
|
async executeChatStep(params, previousDecision) {
|
|
5825
6083
|
const stepEntity = await this.createStepEntity({ stepType: 'Chat', stepName: 'User Interaction', contextUser: params.contextUser });
|
|
5826
6084
|
// Chat steps are successful - they indicate a need for user interaction
|
|
@@ -7165,6 +7423,14 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
7165
7423
|
* @param modelName - Optional model name for accurate tokenization
|
|
7166
7424
|
* @protected
|
|
7167
7425
|
*/
|
|
7426
|
+
/**
|
|
7427
|
+
* Returns true if the message is a tool result (action or client tool).
|
|
7428
|
+
* Used by recovery strategies to identify compactable result messages.
|
|
7429
|
+
*/
|
|
7430
|
+
IsToolResultMessage(msg) {
|
|
7431
|
+
const messageType = msg.metadata?.messageType;
|
|
7432
|
+
return messageType === 'action-result' || messageType === 'client-tool-result';
|
|
7433
|
+
}
|
|
7168
7434
|
estimateTokens(content, modelName) {
|
|
7169
7435
|
const text = typeof content === 'string'
|
|
7170
7436
|
? content
|