@memberjunction/ai-agents 3.4.0 → 4.0.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/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/services/AgentEmbeddingService.d.ts +16 -0
- package/dist/services/AgentEmbeddingService.d.ts.map +1 -0
- package/dist/services/AgentEmbeddingService.js +158 -0
- package/dist/services/AgentEmbeddingService.js.map +1 -0
- package/dist/types/AgentMatchResult.d.ts +18 -0
- package/dist/types/AgentMatchResult.d.ts.map +1 -0
- package/dist/types/AgentMatchResult.js +3 -0
- package/dist/types/AgentMatchResult.js.map +1 -0
- 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,41 +1,104 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Implementation of the Flow Agent Type for deterministic workflow execution.
|
|
3
|
+
*
|
|
4
|
+
* The FlowAgentType enables agents to execute predefined workflows using a directed graph
|
|
5
|
+
* of steps and conditional paths. Unlike LoopAgentType which makes decisions based on
|
|
6
|
+
* LLM output, FlowAgentType follows a deterministic execution path based on boolean
|
|
7
|
+
* expressions evaluated against the current payload and step results.
|
|
8
|
+
*
|
|
9
|
+
* @module @memberjunction/ai-agents
|
|
10
|
+
* @author MemberJunction.com
|
|
11
|
+
* @since 2.76.0
|
|
12
|
+
*/
|
|
2
13
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
14
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
15
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
16
|
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
17
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
18
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
import { RegisterClass, SafeExpressionEvaluator } from '@memberjunction/global';
|
|
20
|
+
import { BaseAgentType } from './base-agent-type.js';
|
|
21
|
+
import { LogError, LogStatus, LogStatusEx, IsVerboseLoggingEnabled } from '@memberjunction/core';
|
|
22
|
+
import { AIEngine } from '@memberjunction/aiengine';
|
|
23
|
+
import { ActionEngineServer } from '@memberjunction/actions';
|
|
24
|
+
import { PayloadManager } from '../PayloadManager.js';
|
|
25
|
+
import { ConversationMessageResolver } from '../utils/ConversationMessageResolver.js';
|
|
26
|
+
/**
|
|
27
|
+
* Flow execution state that tracks the progress through the workflow.
|
|
28
|
+
* This is maintained by the Flow Agent Type during execution.
|
|
29
|
+
*
|
|
30
|
+
* Note: Loop iteration tracking is now handled by BaseAgent._iterationContext.
|
|
31
|
+
* FlowExecutionState only tracks flow-specific navigation state.
|
|
32
|
+
*/
|
|
33
|
+
export class FlowExecutionState {
|
|
18
34
|
constructor(agentId) {
|
|
35
|
+
/** Set of completed step IDs */
|
|
19
36
|
this.completedStepIds = new Set();
|
|
37
|
+
/** Map of step results by step ID */
|
|
20
38
|
this.stepResults = new Map();
|
|
39
|
+
/** Ordered list of step IDs in execution order */
|
|
21
40
|
this.executionPath = [];
|
|
22
41
|
this.agentId = agentId;
|
|
23
42
|
}
|
|
24
43
|
}
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Implementation of the Flow Agent Type pattern.
|
|
46
|
+
*
|
|
47
|
+
* This agent type enables deterministic workflow execution where agents follow
|
|
48
|
+
* predefined paths through a graph of steps. Key features:
|
|
49
|
+
* - Graph-based workflow definition
|
|
50
|
+
* - Conditional path evaluation using safe boolean expressions
|
|
51
|
+
* - Support for Actions, Sub-Agents, and Prompts as step types
|
|
52
|
+
* - Deterministic execution with optional AI-driven decision points
|
|
53
|
+
*
|
|
54
|
+
* @class FlowAgentType
|
|
55
|
+
* @extends BaseAgentType
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // Flow agents execute steps based on graph structure
|
|
60
|
+
* const flowAgent = new FlowAgentType();
|
|
61
|
+
* const nextStep = await flowAgent.DetermineNextStep(promptResult, payload);
|
|
62
|
+
*
|
|
63
|
+
* // Steps are determined by evaluating path conditions
|
|
64
|
+
* // Path: "payload.status == 'approved' && payload.amount < 1000"
|
|
65
|
+
* // Leads to: "Auto-Approve" step
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
let FlowAgentType = class FlowAgentType extends BaseAgentType {
|
|
27
69
|
constructor() {
|
|
28
70
|
super(...arguments);
|
|
29
|
-
this._evaluator = new
|
|
30
|
-
this._payloadManager = new
|
|
71
|
+
this._evaluator = new SafeExpressionEvaluator();
|
|
72
|
+
this._payloadManager = new PayloadManager();
|
|
31
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Handles the initialization of the flow agent state that is specialized, additional state information
|
|
76
|
+
* specific to the Flow Agent Type
|
|
77
|
+
* @param params
|
|
78
|
+
* @returns
|
|
79
|
+
*/
|
|
32
80
|
async InitializeAgentTypeState(params) {
|
|
33
81
|
const flowState = new FlowExecutionState(params.agent.ID);
|
|
34
82
|
return flowState;
|
|
35
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Determines the next step based on the flow graph structure.
|
|
86
|
+
*
|
|
87
|
+
* For Flow agents, the next step is determined by:
|
|
88
|
+
* 1. Finding the current position in the graph
|
|
89
|
+
* 2. Evaluating conditions on outgoing paths
|
|
90
|
+
* 3. Following the highest priority valid path
|
|
91
|
+
* 4. Using prompt results only when current step type is 'Prompt'
|
|
92
|
+
*
|
|
93
|
+
* @param {AIPromptRunResult | null} promptResult - Result from prompt execution (null for non-prompt steps)
|
|
94
|
+
* @param {ExecuteAgentParams} params - The full execution parameters
|
|
95
|
+
*
|
|
96
|
+
* @returns {Promise<BaseAgentNextStep<P>>} The next step to execute
|
|
97
|
+
*/
|
|
36
98
|
async DetermineNextStep(promptResult, params, payload, agentTypeState) {
|
|
37
99
|
try {
|
|
38
100
|
const flowState = agentTypeState;
|
|
101
|
+
// If no current step, this should have been handled by DetermineInitialStep
|
|
39
102
|
if (!flowState.currentStepId) {
|
|
40
103
|
const startingSteps = await this.getStartingSteps(params.agent.ID);
|
|
41
104
|
if (startingSteps.length === 0) {
|
|
@@ -43,26 +106,41 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
43
106
|
errorMessage: 'No starting steps defined for flow agent'
|
|
44
107
|
});
|
|
45
108
|
}
|
|
109
|
+
// For now, execute the first starting step
|
|
110
|
+
// Future enhancement: support parallel starting steps
|
|
46
111
|
return await this.createStepForFlowNode(params, startingSteps[0], payload, flowState);
|
|
47
112
|
}
|
|
113
|
+
// Get current step to check if it was a Prompt step
|
|
48
114
|
const currentStep = await this.getStepById(flowState.currentStepId);
|
|
115
|
+
// If current step was a Prompt, update payload with the prompt result
|
|
49
116
|
if (currentStep?.StepType === 'Prompt' && promptResult) {
|
|
117
|
+
// Parse the prompt result as JSON
|
|
50
118
|
const promptResponse = this.parseJSONResponse(promptResult);
|
|
51
119
|
if (promptResponse) {
|
|
120
|
+
// Check if the prompt response contains a Chat step request
|
|
121
|
+
// This handles the case where a Prompt step wants to return a message to the user
|
|
52
122
|
if (promptResponse.nextStep?.type === 'Chat' ||
|
|
53
123
|
(promptResponse.taskComplete && promptResponse.message)) {
|
|
124
|
+
// Return a Chat step to bubble the message back to the user
|
|
54
125
|
return this.createNextStep('Chat', {
|
|
55
126
|
message: promptResponse.message || 'Response from flow prompt step',
|
|
56
127
|
reasoning: promptResponse.reasoning,
|
|
57
128
|
confidence: promptResponse.confidence,
|
|
58
|
-
terminate: true,
|
|
129
|
+
terminate: true, // Always terminate for chat responses
|
|
59
130
|
newPayload: payload,
|
|
60
131
|
previousPayload: payload
|
|
61
132
|
});
|
|
62
133
|
}
|
|
134
|
+
// Deep merge the prompt response into the current payload
|
|
135
|
+
// This preserves existing nested properties while adding/updating from the prompt
|
|
136
|
+
// Example: If payload has {decision: {Y: 4, Z: 2}} and prompt returns {decision: {x: "string"}},
|
|
137
|
+
// the result will be {decision: {x: "string", Y: 4, Z: 2}} instead of losing Y and Z
|
|
63
138
|
const mergedPayload = this._payloadManager.deepMerge(payload, promptResponse);
|
|
139
|
+
// Copy merged result back to payload reference (modifying in place for consistency)
|
|
64
140
|
Object.assign(payload, mergedPayload);
|
|
65
141
|
}
|
|
142
|
+
// Store the prompt step result so path conditions can access it
|
|
143
|
+
// Create a result object with Success: true (prompt executed successfully)
|
|
66
144
|
const promptStepResult = {
|
|
67
145
|
Success: true,
|
|
68
146
|
step: 'Success',
|
|
@@ -70,30 +148,39 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
70
148
|
rawResult: promptResult
|
|
71
149
|
};
|
|
72
150
|
flowState.stepResults.set(flowState.currentStepId, promptStepResult);
|
|
151
|
+
// Add to execution path if not already present
|
|
73
152
|
if (!flowState.executionPath.includes(flowState.currentStepId)) {
|
|
74
153
|
flowState.executionPath.push(flowState.currentStepId);
|
|
75
154
|
}
|
|
155
|
+
// Mark step as completed
|
|
76
156
|
flowState.completedStepIds.add(flowState.currentStepId);
|
|
77
157
|
}
|
|
158
|
+
// Find valid paths from current step
|
|
159
|
+
// Pass params to enable data/context access in path conditions (Phase 1)
|
|
78
160
|
const paths = await this.getValidPaths(flowState.currentStepId, payload, flowState, params);
|
|
79
161
|
if (paths.length === 0) {
|
|
162
|
+
// No valid paths - flow is complete
|
|
80
163
|
return this.createSuccessStep({
|
|
81
164
|
message: 'Flow completed - no more paths to follow'
|
|
82
165
|
});
|
|
83
166
|
}
|
|
167
|
+
// Get the destination step for the highest priority path
|
|
84
168
|
const nextStep = await this.getStepById(paths[0].DestinationStepID);
|
|
85
169
|
if (!nextStep) {
|
|
86
170
|
return this.createNextStep('Failed', {
|
|
87
171
|
errorMessage: `Destination step not found: ${paths[0].DestinationStepID}`
|
|
88
172
|
});
|
|
89
173
|
}
|
|
174
|
+
// Check if the step is active
|
|
90
175
|
if (nextStep.Status !== 'Active') {
|
|
176
|
+
// Step is disabled or pending - find next valid path
|
|
91
177
|
for (let i = 1; i < paths.length; i++) {
|
|
92
178
|
const alternateStep = await this.getStepById(paths[i].DestinationStepID);
|
|
93
179
|
if (alternateStep && alternateStep.Status === 'Active') {
|
|
94
180
|
return await this.createStepForFlowNode(params, alternateStep, payload, flowState);
|
|
95
181
|
}
|
|
96
182
|
}
|
|
183
|
+
// No active steps found in any path
|
|
97
184
|
return this.createNextStep('Failed', {
|
|
98
185
|
errorMessage: `No active steps found. Step '${nextStep.Name}' has status: ${nextStep.Status}`
|
|
99
186
|
});
|
|
@@ -101,12 +188,19 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
101
188
|
return await this.createStepForFlowNode(params, nextStep, payload, flowState);
|
|
102
189
|
}
|
|
103
190
|
catch (error) {
|
|
104
|
-
|
|
191
|
+
LogError(`Error in FlowAgentType.DetermineNextStep: ${error.message}`);
|
|
105
192
|
return this.createNextStep('Failed', {
|
|
106
193
|
errorMessage: `Flow execution error: ${error.message}`
|
|
107
194
|
});
|
|
108
195
|
}
|
|
109
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Injects flow-specific context into the prompt parameters.
|
|
199
|
+
*
|
|
200
|
+
* @param {P} payload - The payload to inject
|
|
201
|
+
* @param {AIPromptParams} prompt - The prompt parameters to update
|
|
202
|
+
* @param {object} agentInfo - Agent identification info
|
|
203
|
+
*/
|
|
110
204
|
async InjectPayload(payload, agentState, prompt, agentInfo) {
|
|
111
205
|
if (!prompt) {
|
|
112
206
|
throw new Error('Prompt parameters are required for payload injection');
|
|
@@ -114,7 +208,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
114
208
|
if (!prompt.data) {
|
|
115
209
|
prompt.data = {};
|
|
116
210
|
}
|
|
117
|
-
|
|
211
|
+
// Inject standard payload
|
|
212
|
+
prompt.data[BaseAgentType.CURRENT_PAYLOAD_PLACEHOLDER] = payload || {};
|
|
213
|
+
// Add flow-specific context from our state tracking
|
|
118
214
|
if (agentInfo.agentRunId) {
|
|
119
215
|
const flowState = agentState;
|
|
120
216
|
if (agentState) {
|
|
@@ -127,28 +223,66 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
127
223
|
}
|
|
128
224
|
}
|
|
129
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Gets all starting steps for an agent
|
|
228
|
+
*
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
130
231
|
async getStartingSteps(agentId) {
|
|
131
|
-
const steps =
|
|
232
|
+
const steps = AIEngine.Instance.GetAgentSteps(agentId, 'Active');
|
|
132
233
|
return steps.filter(step => step.StartingStep).sort((a, b) => a.Name.localeCompare(b.Name));
|
|
133
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Gets a step by ID
|
|
237
|
+
*
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
134
240
|
async getStepById(stepId) {
|
|
135
|
-
return
|
|
241
|
+
return AIEngine.Instance.GetAgentStepByID(stepId);
|
|
136
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Gets a step by name within an agent
|
|
245
|
+
*
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
137
248
|
async getStepByName(agentId, stepName) {
|
|
138
|
-
const steps =
|
|
249
|
+
const steps = AIEngine.Instance.GetAgentSteps(agentId);
|
|
139
250
|
return steps.find(step => step.Name === stepName) || null;
|
|
140
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Gets all paths from a step and evaluates their conditions.
|
|
254
|
+
*
|
|
255
|
+
* The evaluation context includes:
|
|
256
|
+
* - payload: The current agent payload (persistent state)
|
|
257
|
+
* - stepResult: Result from the last executed step
|
|
258
|
+
* - flowContext: Flow execution metadata (current step, completed steps, path)
|
|
259
|
+
* - data: Transient template data from ExecuteAgentParams (NEW in Phase 1)
|
|
260
|
+
* - context: Runtime context from ExecuteAgentParams (NEW in Phase 1)
|
|
261
|
+
*
|
|
262
|
+
* This allows path conditions to access runtime parameters like:
|
|
263
|
+
* - `data.userApproval === true`
|
|
264
|
+
* - `context.environment === 'production'`
|
|
265
|
+
* - `data.retryCount < 3 && payload.status === 'pending'`
|
|
266
|
+
*
|
|
267
|
+
* @private
|
|
268
|
+
*/
|
|
141
269
|
async getValidPaths(stepId, payload, flowState, params) {
|
|
142
|
-
|
|
270
|
+
// Load all paths from this step
|
|
271
|
+
const allPaths = AIEngine.Instance.GetPathsFromStep(stepId)
|
|
143
272
|
.sort((a, b) => b.Priority - a.Priority);
|
|
144
273
|
const validPaths = [];
|
|
274
|
+
// Evaluate each path's condition
|
|
145
275
|
for (const path of allPaths) {
|
|
276
|
+
// If no condition, path is always valid
|
|
146
277
|
if (!path.Condition) {
|
|
147
278
|
validPaths.push(path);
|
|
148
279
|
continue;
|
|
149
280
|
}
|
|
150
281
|
try {
|
|
282
|
+
// Create enhanced evaluation context
|
|
283
|
+
// Phase 1: Read-only access to data and context for path conditions
|
|
151
284
|
const context = {
|
|
285
|
+
// Existing context properties
|
|
152
286
|
payload,
|
|
153
287
|
stepResult: this.getLastStepResult(flowState),
|
|
154
288
|
flowContext: {
|
|
@@ -157,6 +291,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
157
291
|
executionPath: flowState.executionPath,
|
|
158
292
|
stepCount: flowState.completedStepIds.size
|
|
159
293
|
},
|
|
294
|
+
// NEW: Map params.data and params.context directly
|
|
295
|
+
// These enable deterministic routing based on runtime state
|
|
296
|
+
// without polluting the persistent payload
|
|
160
297
|
data: params.data || {},
|
|
161
298
|
context: params.context || {}
|
|
162
299
|
};
|
|
@@ -165,41 +302,69 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
165
302
|
validPaths.push(path);
|
|
166
303
|
}
|
|
167
304
|
else if (!evalResult.success) {
|
|
168
|
-
|
|
305
|
+
// Only log errors when evaluation failed, not when condition is simply false
|
|
306
|
+
LogError(`Path condition failed: ${path.Condition}\n${evalResult.error}`);
|
|
169
307
|
}
|
|
170
308
|
}
|
|
171
309
|
catch (error) {
|
|
172
|
-
|
|
310
|
+
LogError(`Failed to evaluate path condition: ${error.message}`);
|
|
173
311
|
}
|
|
174
312
|
}
|
|
313
|
+
// If no valid paths with conditions, include paths with priority <= 0 (defaults)
|
|
175
314
|
if (validPaths.length === 0) {
|
|
176
315
|
return allPaths.filter(p => p.Priority <= 0 && !p.Condition);
|
|
177
316
|
}
|
|
317
|
+
// Sort by priority (already sorted by query, but re-sort valid subset)
|
|
178
318
|
return validPaths.sort((a, b) => b.Priority - a.Priority);
|
|
179
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Gets the last step result from the flow context
|
|
322
|
+
*
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
180
325
|
getLastStepResult(flowState) {
|
|
181
326
|
if (flowState.executionPath.length === 0)
|
|
182
327
|
return null;
|
|
183
328
|
const lastStepId = flowState.executionPath[flowState.executionPath.length - 1];
|
|
184
329
|
return flowState.stepResults.get(lastStepId) || null;
|
|
185
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Helper to set a value on a target object, supporting array append syntax.
|
|
333
|
+
* If key ends with '[]', the value is pushed to an array (auto-initialized if needed).
|
|
334
|
+
*
|
|
335
|
+
* @private
|
|
336
|
+
*/
|
|
186
337
|
setMappedValue(target, key, value) {
|
|
187
338
|
const isArrayAppend = key.endsWith('[]');
|
|
188
339
|
const actualKey = isArrayAppend ? key.slice(0, -2) : key;
|
|
189
340
|
if (isArrayAppend) {
|
|
341
|
+
// Initialize array if it doesn't exist
|
|
190
342
|
if (!(actualKey in target)) {
|
|
191
343
|
target[actualKey] = [];
|
|
192
344
|
}
|
|
345
|
+
// Validate it's actually an array
|
|
193
346
|
if (!Array.isArray(target[actualKey])) {
|
|
194
347
|
throw new Error(`Cannot append to '${actualKey}': target is not an array. ` +
|
|
195
348
|
`Use '${actualKey}' without [] suffix for property update.`);
|
|
196
349
|
}
|
|
350
|
+
// Append the value
|
|
197
351
|
target[actualKey].push(value);
|
|
198
352
|
}
|
|
199
353
|
else {
|
|
354
|
+
// Standard property assignment
|
|
200
355
|
target[actualKey] = value;
|
|
201
356
|
}
|
|
202
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Applies action output mapping to update the payload and extract special fields.
|
|
360
|
+
*
|
|
361
|
+
* Special fields ($message, $reasoning, $confidence) are prefixed with $ and are not
|
|
362
|
+
* added to the payload. Instead, they are stored separately in FlowExecutionState
|
|
363
|
+
* for use in the final response.
|
|
364
|
+
*
|
|
365
|
+
* @private
|
|
366
|
+
* @returns Object with payloadChange and specialFields
|
|
367
|
+
*/
|
|
203
368
|
applyActionOutputMapping(actionResult, _payload, mappingConfig) {
|
|
204
369
|
try {
|
|
205
370
|
const mapping = JSON.parse(mappingConfig);
|
|
@@ -211,10 +376,12 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
211
376
|
value = actionResult;
|
|
212
377
|
}
|
|
213
378
|
else if (outputParam.includes('.')) {
|
|
379
|
+
// Support dot notation for nested property access (e.g., "AgentSpec.ID")
|
|
214
380
|
const parts = outputParam.split('.');
|
|
215
381
|
value = actionResult;
|
|
216
382
|
for (const part of parts) {
|
|
217
383
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
384
|
+
// Case-insensitive lookup at each level
|
|
218
385
|
const actualKey = Object.keys(value).find(key => key.toLowerCase() === part.toLowerCase());
|
|
219
386
|
value = actualKey ? value[actualKey] : undefined;
|
|
220
387
|
}
|
|
@@ -225,12 +392,15 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
225
392
|
}
|
|
226
393
|
}
|
|
227
394
|
else {
|
|
395
|
+
// Simple case-insensitive lookup for non-dotted output parameters
|
|
228
396
|
const actualKey = Object.keys(actionResult).find(key => key.toLowerCase() === outputParam.toLowerCase());
|
|
229
397
|
value = actualKey ? actionResult[actualKey] : undefined;
|
|
230
398
|
}
|
|
231
399
|
if (value !== undefined) {
|
|
400
|
+
// Check if this is a special field mapping (starts with $)
|
|
232
401
|
if (payloadPath.startsWith('$')) {
|
|
233
402
|
const fieldName = payloadPath.substring(1).toLowerCase();
|
|
403
|
+
// Store in special fields instead of payload
|
|
234
404
|
if (fieldName === 'message' && typeof value === 'string') {
|
|
235
405
|
specialFields.message = value;
|
|
236
406
|
}
|
|
@@ -240,21 +410,26 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
240
410
|
else if (fieldName === 'confidence' && typeof value === 'number') {
|
|
241
411
|
specialFields.confidence = value;
|
|
242
412
|
}
|
|
413
|
+
// Ignore unknown special fields with warning
|
|
243
414
|
else {
|
|
244
|
-
|
|
415
|
+
LogError(`Unknown special field in ActionOutputMapping: ${payloadPath}. Valid special fields are: $message, $reasoning, $confidence`);
|
|
245
416
|
}
|
|
246
417
|
}
|
|
247
418
|
else {
|
|
419
|
+
// Regular payload mapping
|
|
420
|
+
// Parse the path and build nested object
|
|
248
421
|
const pathParts = payloadPath.split('.');
|
|
249
422
|
let current = updateObj;
|
|
250
423
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
251
424
|
const part = pathParts[i];
|
|
425
|
+
// Remove [] suffix for intermediate path parts
|
|
252
426
|
const cleanPart = part.endsWith('[]') ? part.slice(0, -2) : part;
|
|
253
427
|
if (!(cleanPart in current)) {
|
|
254
428
|
current[cleanPart] = {};
|
|
255
429
|
}
|
|
256
430
|
current = current[cleanPart];
|
|
257
431
|
}
|
|
432
|
+
// Use helper to support array append on final path part
|
|
258
433
|
this.setMappedValue(current, pathParts[pathParts.length - 1], value);
|
|
259
434
|
}
|
|
260
435
|
}
|
|
@@ -268,26 +443,41 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
268
443
|
};
|
|
269
444
|
}
|
|
270
445
|
catch (error) {
|
|
271
|
-
|
|
446
|
+
LogError(`Failed to parse ActionOutputMapping: ${error.message}`);
|
|
272
447
|
return { payloadChange: null };
|
|
273
448
|
}
|
|
274
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Creates a BaseAgentNextStep for a flow node.
|
|
452
|
+
*
|
|
453
|
+
* If the node is in agentTypeParams.skipSteps, the step is marked as skipped
|
|
454
|
+
* and we immediately evaluate paths to find the next step.
|
|
455
|
+
*
|
|
456
|
+
* @private
|
|
457
|
+
*/
|
|
275
458
|
async createStepForFlowNode(params, node, payload, flowState) {
|
|
459
|
+
// Check if this step should be skipped
|
|
276
460
|
const flowParams = params.agentTypeParams;
|
|
277
461
|
if (flowParams?.skipSteps && flowParams.skipSteps.some(s => s.ID === node.ID)) {
|
|
278
|
-
|
|
462
|
+
LogStatus(`Flow Agent: Skipping step '${node.Name}' (via agentTypeParams.skipSteps)`);
|
|
463
|
+
// Update flow state to mark this as current step (for path evaluation)
|
|
279
464
|
flowState.currentStepId = node.ID;
|
|
465
|
+
// Mark the step as completed (skipped)
|
|
280
466
|
flowState.completedStepIds.add(node.ID);
|
|
281
467
|
flowState.executionPath.push(node.ID);
|
|
468
|
+
// Store a skip marker as the step result
|
|
282
469
|
flowState.stepResults.set(node.ID, { skipped: true, stepName: node.Name });
|
|
470
|
+
// Find the next step by evaluating paths from this skipped step
|
|
283
471
|
const paths = await this.getValidPaths(node.ID, payload, flowState, params);
|
|
284
472
|
if (paths.length === 0) {
|
|
473
|
+
// No more paths - flow is complete
|
|
285
474
|
return this.createSuccessStep({
|
|
286
475
|
message: `Flow completed after skipping step '${node.Name}' - no more paths to follow`,
|
|
287
476
|
newPayload: payload,
|
|
288
477
|
previousPayload: payload
|
|
289
478
|
});
|
|
290
479
|
}
|
|
480
|
+
// Get the destination step for the highest priority path
|
|
291
481
|
const nextStep = await this.getStepById(paths[0].DestinationStepID);
|
|
292
482
|
if (!nextStep) {
|
|
293
483
|
return this.createNextStep('Failed', {
|
|
@@ -296,8 +486,10 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
296
486
|
previousPayload: payload
|
|
297
487
|
});
|
|
298
488
|
}
|
|
489
|
+
// Recursively create the step for the next node (which may also be skipped)
|
|
299
490
|
return await this.createStepForFlowNode(params, nextStep, payload, flowState);
|
|
300
491
|
}
|
|
492
|
+
// Update flow state to mark this as current step
|
|
301
493
|
flowState.currentStepId = node.ID;
|
|
302
494
|
switch (node.StepType) {
|
|
303
495
|
case 'Action':
|
|
@@ -308,6 +500,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
308
500
|
previousPayload: payload
|
|
309
501
|
});
|
|
310
502
|
}
|
|
503
|
+
// Need to get the action name
|
|
311
504
|
const actionName = await this.getActionName(node.ActionID);
|
|
312
505
|
if (!actionName) {
|
|
313
506
|
return this.createNextStep('Failed', {
|
|
@@ -316,15 +509,18 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
316
509
|
previousPayload: payload
|
|
317
510
|
});
|
|
318
511
|
}
|
|
512
|
+
// Check if we have output mapping configured
|
|
319
513
|
const baseStep = this.createNextStep('Actions', {
|
|
320
514
|
actions: [{
|
|
321
515
|
name: actionName,
|
|
322
|
-
params: {}
|
|
516
|
+
params: {} // Future: support parameter mapping
|
|
323
517
|
}],
|
|
324
518
|
terminate: false,
|
|
325
519
|
newPayload: payload,
|
|
326
520
|
previousPayload: payload
|
|
327
521
|
});
|
|
522
|
+
// Store the mapping config in a special property for later use
|
|
523
|
+
// Always set stepId for action steps so PreProcessActionStep can access step entity
|
|
328
524
|
baseStep.stepId = node.ID;
|
|
329
525
|
if (node.ActionOutputMapping) {
|
|
330
526
|
baseStep.actionOutputMapping = node.ActionOutputMapping;
|
|
@@ -338,6 +534,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
338
534
|
previousPayload: payload
|
|
339
535
|
});
|
|
340
536
|
}
|
|
537
|
+
// Need to get the sub-agent name
|
|
341
538
|
const subAgentName = await this.getAgentName(node.SubAgentID);
|
|
342
539
|
if (!subAgentName) {
|
|
343
540
|
return this.createNextStep('Failed', {
|
|
@@ -346,18 +543,25 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
346
543
|
previousPayload: payload
|
|
347
544
|
});
|
|
348
545
|
}
|
|
546
|
+
// Use node description to define the sub-agent's task
|
|
547
|
+
// The full conversation history is preserved in params.conversationMessages
|
|
548
|
+
// and will be available to the sub-agent through BaseAgent.ExecuteSubAgent()
|
|
549
|
+
// Propagate context to sub-agent so it has access to runtime configuration,
|
|
550
|
+
// API keys, environment settings, etc. from the parent agent
|
|
349
551
|
return this.createNextStep('Sub-Agent', {
|
|
350
552
|
subAgent: {
|
|
351
553
|
name: subAgentName,
|
|
352
554
|
message: node.Description || '',
|
|
353
555
|
terminateAfter: false,
|
|
354
|
-
context: params.context
|
|
556
|
+
context: params.context // Propagate runtime context to sub-agent
|
|
355
557
|
},
|
|
356
558
|
terminate: false,
|
|
357
559
|
newPayload: payload,
|
|
358
560
|
previousPayload: payload
|
|
359
561
|
});
|
|
360
562
|
case 'Prompt':
|
|
563
|
+
// For prompt steps, we use Retry with a special marker
|
|
564
|
+
// The base agent will need to handle this case
|
|
361
565
|
if (!node.PromptID) {
|
|
362
566
|
return this.createNextStep('Failed', {
|
|
363
567
|
errorMessage: `Prompt step '${node.Name}' has no PromptID`,
|
|
@@ -369,6 +573,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
369
573
|
step: 'Retry',
|
|
370
574
|
message: node.Description || 'Executing prompt step for flow decision',
|
|
371
575
|
terminate: false,
|
|
576
|
+
// Special marker for flow prompt steps
|
|
372
577
|
flowPromptStepId: node.PromptID,
|
|
373
578
|
newPayload: payload,
|
|
374
579
|
previousPayload: payload
|
|
@@ -386,79 +591,137 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
386
591
|
});
|
|
387
592
|
}
|
|
388
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Gets action name by ID
|
|
596
|
+
*
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
389
599
|
async getActionName(actionId) {
|
|
390
|
-
const action =
|
|
600
|
+
const action = ActionEngineServer.Instance.Actions.find(a => a.ID === actionId);
|
|
391
601
|
return action?.Name || null;
|
|
392
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* Gets agent name by ID
|
|
605
|
+
*
|
|
606
|
+
* @private
|
|
607
|
+
*/
|
|
393
608
|
async getAgentName(agentId) {
|
|
394
|
-
const agent =
|
|
609
|
+
const agent = AIEngine.Instance.Agents.find(a => a.ID === agentId);
|
|
395
610
|
return agent?.Name || null;
|
|
396
611
|
}
|
|
612
|
+
/**
|
|
613
|
+
* Processes action results and applies output mapping if configured
|
|
614
|
+
* This should be called by BaseAgent after action execution
|
|
615
|
+
*
|
|
616
|
+
* @public
|
|
617
|
+
*/
|
|
397
618
|
processActionResult(actionResult, _stepId, outputMapping, currentPayload) {
|
|
398
619
|
if (!outputMapping) {
|
|
399
620
|
return null;
|
|
400
621
|
}
|
|
401
622
|
const result = this.applyActionOutputMapping(actionResult, currentPayload || {}, outputMapping);
|
|
623
|
+
// Note: Special fields are ignored in this legacy method
|
|
624
|
+
// Use PostProcessActionStep for full special field support
|
|
402
625
|
return result.payloadChange;
|
|
403
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Override of BaseAgentType's PreProcessActionStep to handle Flow-specific input mapping.
|
|
629
|
+
*
|
|
630
|
+
* This method checks if the current step has action input mapping configured
|
|
631
|
+
* and applies it to map payload values or static values to action input parameters.
|
|
632
|
+
*
|
|
633
|
+
* @override
|
|
634
|
+
* @param {AgentAction[]} actions - The actions that will be executed (modified in place)
|
|
635
|
+
* @param {P} currentPayload - The current payload
|
|
636
|
+
* @param {BaseAgentNextStep<P>} currentStep - The current step being executed
|
|
637
|
+
*
|
|
638
|
+
* @returns {Promise<void>} Actions are modified in place
|
|
639
|
+
*
|
|
640
|
+
* @since 2.76.0
|
|
641
|
+
*/
|
|
404
642
|
async PreProcessActionStep(actions, currentPayload, agentTypeState, currentStep, params) {
|
|
643
|
+
// Try to find the flow state and use its payload if available
|
|
405
644
|
const stepMetadata = currentStep;
|
|
406
645
|
const stepId = stepMetadata.stepId;
|
|
407
646
|
if (!stepId || actions.length === 0) {
|
|
647
|
+
// No step ID or no actions to process
|
|
408
648
|
return;
|
|
409
649
|
}
|
|
410
|
-
|
|
650
|
+
// Get the AIAgentStep from cached metadata
|
|
651
|
+
const stepEntity = AIEngine.Instance.GetAgentStepByID(stepId);
|
|
411
652
|
if (!stepEntity) {
|
|
412
|
-
|
|
653
|
+
LogError(`Failed to find AIAgentStep for input mapping: ${stepId}`);
|
|
413
654
|
return;
|
|
414
655
|
}
|
|
415
656
|
if (!stepEntity.ActionInputMapping) {
|
|
657
|
+
// No input mapping configured
|
|
416
658
|
return;
|
|
417
659
|
}
|
|
660
|
+
// For flow agents, we currently only support single action steps with input mapping
|
|
661
|
+
// Future enhancement: support mapping to multiple actions
|
|
418
662
|
if (actions.length > 1) {
|
|
419
|
-
|
|
663
|
+
LogError('Flow agent action input mapping currently only supports single action steps');
|
|
420
664
|
return;
|
|
421
665
|
}
|
|
422
666
|
try {
|
|
667
|
+
// Parse the input mapping configuration
|
|
668
|
+
// Expected format: { "paramName": "payload.path.to.value" | "static:value" | 123 | true }
|
|
423
669
|
let inputMapping;
|
|
424
670
|
if (typeof stepEntity.ActionInputMapping === 'string') {
|
|
671
|
+
// ActionInputMapping is a JSON string - parse it
|
|
425
672
|
inputMapping = JSON.parse(stepEntity.ActionInputMapping);
|
|
426
673
|
}
|
|
427
674
|
else if (typeof stepEntity.ActionInputMapping === 'object' && stepEntity.ActionInputMapping !== null) {
|
|
675
|
+
// ActionInputMapping is already an object - use it directly
|
|
428
676
|
inputMapping = stepEntity.ActionInputMapping;
|
|
429
677
|
}
|
|
430
678
|
else {
|
|
431
|
-
|
|
679
|
+
// ActionInputMapping is null or invalid
|
|
680
|
+
LogError(`Invalid ActionInputMapping format for step ${stepEntity.Name}: ${stepEntity.ActionInputMapping}`);
|
|
432
681
|
return;
|
|
433
682
|
}
|
|
434
683
|
const action = actions[0];
|
|
684
|
+
// Initialize params if not present
|
|
435
685
|
if (!action.params) {
|
|
436
686
|
action.params = {};
|
|
437
687
|
}
|
|
688
|
+
// Apply each mapping
|
|
438
689
|
for (const [paramName, mappingValue] of Object.entries(inputMapping)) {
|
|
690
|
+
// Use recursive resolution to handle nested objects, arrays, and primitive values
|
|
439
691
|
const resolvedValue = this.resolveNestedValue(mappingValue, currentPayload, params);
|
|
692
|
+
// Set the parameter value
|
|
440
693
|
action.params[paramName] = resolvedValue;
|
|
441
694
|
}
|
|
442
|
-
if (
|
|
695
|
+
if (IsVerboseLoggingEnabled()) {
|
|
443
696
|
console.log(`Applied action input mapping for step ${stepEntity.Name}:`, action.params);
|
|
444
697
|
}
|
|
445
698
|
}
|
|
446
699
|
catch (error) {
|
|
447
|
-
|
|
700
|
+
LogError(`Failed to apply action input mapping: ${error.message}`);
|
|
448
701
|
}
|
|
449
702
|
}
|
|
703
|
+
/**
|
|
704
|
+
* Helper method to get a value from a nested object path
|
|
705
|
+
* Supports both dot notation (obj.prop) and array indexing (arr[0])
|
|
706
|
+
*
|
|
707
|
+
* @private
|
|
708
|
+
*/
|
|
450
709
|
getValueFromPath(obj, path) {
|
|
451
710
|
const parts = path.split('.');
|
|
452
711
|
let current = obj;
|
|
453
712
|
for (const part of parts) {
|
|
454
713
|
if (!part)
|
|
455
714
|
continue;
|
|
715
|
+
// Check if this part contains array indexing like "arrayName[0]"
|
|
456
716
|
const arrayMatch = part.match(/^([^[]+)\[(\d+)\]$/);
|
|
457
717
|
if (arrayMatch) {
|
|
718
|
+
// Extract array name and index
|
|
458
719
|
const arrayName = arrayMatch[1];
|
|
459
720
|
const index = parseInt(arrayMatch[2], 10);
|
|
721
|
+
// Navigate to the array
|
|
460
722
|
if (current && typeof current === 'object' && arrayName in current) {
|
|
461
723
|
current = current[arrayName];
|
|
724
|
+
// Access the array element
|
|
462
725
|
if (Array.isArray(current) && index >= 0 && index < current.length) {
|
|
463
726
|
current = current[index];
|
|
464
727
|
}
|
|
@@ -471,6 +734,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
471
734
|
}
|
|
472
735
|
}
|
|
473
736
|
else {
|
|
737
|
+
// Regular property access
|
|
474
738
|
if (current && typeof current === 'object' && part in current) {
|
|
475
739
|
current = current[part];
|
|
476
740
|
}
|
|
@@ -481,11 +745,25 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
481
745
|
}
|
|
482
746
|
return current;
|
|
483
747
|
}
|
|
748
|
+
/**
|
|
749
|
+
* Recursively resolves payload, static, data, context, and conversation references in nested objects.
|
|
750
|
+
*
|
|
751
|
+
* Supported prefixes:
|
|
752
|
+
* - `payload.path.to.value` - reads from persistent payload
|
|
753
|
+
* - `static:value` - literal static value
|
|
754
|
+
* - `data.path.to.value` - reads from params.data (template data)
|
|
755
|
+
* - `context.path.to.value` - reads from params.context (runtime context, e.g., API keys, environment)
|
|
756
|
+
* - `conversation[N].content` - reads from conversation messages
|
|
757
|
+
*
|
|
758
|
+
* @private
|
|
759
|
+
*/
|
|
484
760
|
resolveNestedValue(value, currentPayload, params) {
|
|
485
761
|
if (typeof value === 'string') {
|
|
762
|
+
// Handle string values with payload/static/data/context/conversation resolution (case-insensitive)
|
|
486
763
|
const trimmedValue = value.trim();
|
|
487
|
-
|
|
488
|
-
|
|
764
|
+
// Check for conversation message references
|
|
765
|
+
if (ConversationMessageResolver.isConversationReference(trimmedValue) && params?.conversationMessages) {
|
|
766
|
+
return ConversationMessageResolver.resolve(trimmedValue, params.conversationMessages);
|
|
489
767
|
}
|
|
490
768
|
else if (trimmedValue.toLowerCase().startsWith('static:')) {
|
|
491
769
|
return value.substring(value.indexOf(':') + 1);
|
|
@@ -501,6 +779,8 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
501
779
|
return this.getValueFromPath(params.data, path);
|
|
502
780
|
}
|
|
503
781
|
else if (trimmedValue.toLowerCase().startsWith('context.') && params?.context) {
|
|
782
|
+
// NEW: Support context. prefix for accessing runtime context
|
|
783
|
+
// This allows action input mappings to reference API keys, environment settings, etc.
|
|
504
784
|
const pathStart = value.indexOf('.') + 1;
|
|
505
785
|
const path = value.substring(pathStart);
|
|
506
786
|
return this.getValueFromPath(params.context, path);
|
|
@@ -510,9 +790,11 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
510
790
|
}
|
|
511
791
|
}
|
|
512
792
|
else if (Array.isArray(value)) {
|
|
793
|
+
// Handle arrays - recursively resolve each element
|
|
513
794
|
return value.map(item => this.resolveNestedValue(item, currentPayload, params));
|
|
514
795
|
}
|
|
515
796
|
else if (value && typeof value === 'object') {
|
|
797
|
+
// Handle objects - recursively resolve each property
|
|
516
798
|
const resolvedObj = {};
|
|
517
799
|
for (const [key, val] of Object.entries(value)) {
|
|
518
800
|
resolvedObj[key] = this.resolveNestedValue(val, currentPayload, params);
|
|
@@ -520,22 +802,43 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
520
802
|
return resolvedObj;
|
|
521
803
|
}
|
|
522
804
|
else {
|
|
805
|
+
// Handle primitives (numbers, booleans, null, undefined)
|
|
523
806
|
return value;
|
|
524
807
|
}
|
|
525
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* Override of BaseAgentType's PostProcessActionStep to handle Flow-specific logic.
|
|
811
|
+
*
|
|
812
|
+
* This method checks if the current step has action output mapping configured
|
|
813
|
+
* and applies it to update the payload accordingly.
|
|
814
|
+
*
|
|
815
|
+
* @override
|
|
816
|
+
* @param {ActionResult[]} actionResults - The results from action execution
|
|
817
|
+
* @param {AgentAction[]} actions - The actions that were executed
|
|
818
|
+
* @param {P} currentPayload - The current payload
|
|
819
|
+
* @param {BaseAgentNextStep<P>} currentStep - The current step being executed
|
|
820
|
+
*
|
|
821
|
+
* @returns {Promise<AgentPayloadChangeRequest<P> | null>} Payload changes from output mapping
|
|
822
|
+
*/
|
|
526
823
|
async PostProcessActionStep(actionResults, actions, currentPayload, agentTypeState, currentStep) {
|
|
824
|
+
// Check if this step has action output mapping configured
|
|
527
825
|
const stepMetadata = currentStep;
|
|
528
826
|
const outputMapping = stepMetadata.actionOutputMapping;
|
|
529
827
|
const stepId = stepMetadata.stepId;
|
|
530
828
|
if (!outputMapping || !stepId || actionResults.length === 0) {
|
|
829
|
+
// No mapping configured or no results to process
|
|
531
830
|
return null;
|
|
532
831
|
}
|
|
832
|
+
// For flow agents, we currently only support single action steps with output mapping
|
|
833
|
+
// Future enhancement: support mapping from multiple actions
|
|
533
834
|
if (actionResults.length > 1) {
|
|
534
|
-
|
|
835
|
+
LogError('Flow agent action output mapping currently only supports single action steps');
|
|
535
836
|
return null;
|
|
536
837
|
}
|
|
838
|
+
// Extract output parameters from the action result
|
|
537
839
|
const actionResult = actionResults[0];
|
|
538
840
|
const outputParams = {};
|
|
841
|
+
// Filter for output parameters (Type === 'Output' or 'Both')
|
|
539
842
|
if (actionResult.Params) {
|
|
540
843
|
for (const param of actionResult.Params) {
|
|
541
844
|
if (param.Type === 'Output' || param.Type === 'Both') {
|
|
@@ -543,61 +846,114 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
543
846
|
}
|
|
544
847
|
}
|
|
545
848
|
}
|
|
849
|
+
// Apply the mapping using our existing method
|
|
546
850
|
const result = this.applyActionOutputMapping(outputParams, currentPayload, outputMapping);
|
|
547
851
|
const payloadChange = result.payloadChange;
|
|
852
|
+
// Store special fields in flow state for later use
|
|
548
853
|
const flowState = agentTypeState;
|
|
549
854
|
if (result.specialFields) {
|
|
550
855
|
if (!flowState.specialFields) {
|
|
551
856
|
flowState.specialFields = {};
|
|
552
857
|
}
|
|
858
|
+
// Merge special fields (later values override earlier ones)
|
|
553
859
|
Object.assign(flowState.specialFields, result.specialFields);
|
|
554
860
|
}
|
|
861
|
+
// Update flow state with the modified payload
|
|
862
|
+
// This ensures the payload persists when DetermineNextStep is called again
|
|
555
863
|
if (payloadChange && payloadChange.updateElements) {
|
|
556
864
|
const existingPayload = currentPayload || {};
|
|
865
|
+
// Apply the payload change using PayloadManager's merge capabilities
|
|
557
866
|
const mergeResult = this._payloadManager.applyAgentChangeRequest(existingPayload, payloadChange, {
|
|
558
867
|
logChanges: false,
|
|
559
|
-
verbose:
|
|
868
|
+
verbose: IsVerboseLoggingEnabled()
|
|
560
869
|
});
|
|
870
|
+
// Log any warnings if present
|
|
561
871
|
if (mergeResult.warnings && mergeResult.warnings.length > 0) {
|
|
562
|
-
|
|
872
|
+
LogError(`Warnings during payload merge in flow state: ${mergeResult.warnings.join(', ')}`);
|
|
563
873
|
}
|
|
564
874
|
}
|
|
565
875
|
return payloadChange;
|
|
566
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* Determines the initial step for flow agent types.
|
|
879
|
+
*
|
|
880
|
+
* Flow agents look up their configured starting step instead of executing a prompt.
|
|
881
|
+
* If agentTypeParams.startAtStep is provided, the flow will begin at that step
|
|
882
|
+
* instead of the configured entry point.
|
|
883
|
+
*
|
|
884
|
+
* @param {ExecuteAgentParams} params - The full execution parameters
|
|
885
|
+
* @returns {Promise<BaseAgentNextStep<P> | null>} The initial step to execute, or null if flow context not ready
|
|
886
|
+
*
|
|
887
|
+
* @override
|
|
888
|
+
* @since 2.76.0
|
|
889
|
+
*/
|
|
567
890
|
async DetermineInitialStep(params, payload, agentTypeState) {
|
|
568
891
|
const flowState = agentTypeState;
|
|
569
892
|
const payloadToUse = payload || {};
|
|
893
|
+
// Check for startAtStep in agentTypeParams (FlowAgentExecuteParams)
|
|
570
894
|
const flowParams = params.agentTypeParams;
|
|
571
895
|
if (flowParams?.startAtStep) {
|
|
572
|
-
|
|
896
|
+
// Validate the step belongs to this agent
|
|
897
|
+
const agentSteps = AIEngine.Instance.GetAgentSteps(flowState.agentId);
|
|
573
898
|
const validStep = agentSteps.find(s => s.ID === flowParams.startAtStep.ID);
|
|
574
899
|
if (!validStep) {
|
|
575
900
|
return this.createNextStep('Failed', {
|
|
576
901
|
errorMessage: `startAtStep '${flowParams.startAtStep.Name}' (ID: ${flowParams.startAtStep.ID}) does not belong to agent '${params.agent.Name}'`
|
|
577
902
|
});
|
|
578
903
|
}
|
|
579
|
-
|
|
904
|
+
LogStatus(`Flow Agent: Starting at step '${validStep.Name}' (override via agentTypeParams.startAtStep)`);
|
|
580
905
|
return await this.createStepForFlowNode(params, validStep, payloadToUse, flowState);
|
|
581
906
|
}
|
|
907
|
+
// Default behavior: start at the configured entry point
|
|
582
908
|
const startingSteps = await this.getStartingSteps(flowState.agentId);
|
|
583
909
|
if (startingSteps.length === 0) {
|
|
584
910
|
return this.createNextStep('Failed', {
|
|
585
911
|
errorMessage: 'No starting steps defined for flow agent'
|
|
586
912
|
});
|
|
587
913
|
}
|
|
914
|
+
// Execute the first starting step
|
|
915
|
+
// Future enhancement: support parallel starting steps
|
|
588
916
|
return await this.createStepForFlowNode(params, startingSteps[0], payloadToUse, flowState);
|
|
589
917
|
}
|
|
918
|
+
/**
|
|
919
|
+
* Pre-processes steps for flow agent types.
|
|
920
|
+
*
|
|
921
|
+
* For Flow agents, 'Retry' after actions means evaluate paths from the current step
|
|
922
|
+
* and continue the flow based on the updated payload. The only exception is when
|
|
923
|
+
* executing a Prompt step within the flow, which should execute normally.
|
|
924
|
+
*
|
|
925
|
+
* Also, 'Success' steps after sub-agents need to be evaluated in the same way as Retry after actions
|
|
926
|
+
*
|
|
927
|
+
* @param {ExecuteAgentParams} params - The full execution parameters
|
|
928
|
+
* @param {BaseAgentNextStep} step - The retry step that was returned
|
|
929
|
+
* @param {P} payload - The current payload
|
|
930
|
+
* @param {ATS} agentTypeState - The current agent type state
|
|
931
|
+
* @returns {Promise<BaseAgentNextStep<P> | null>} The next flow step, or null for prompt execution
|
|
932
|
+
*
|
|
933
|
+
* @override
|
|
934
|
+
* @since 2.76.0
|
|
935
|
+
*/
|
|
590
936
|
async PreProcessNextStep(params, step, payload, agentTypeState) {
|
|
937
|
+
// we only want to do special processing for retry, success, or failed steps
|
|
938
|
+
// Failed steps need to be evaluated for conditional failure paths
|
|
591
939
|
if (step.step !== 'Retry' && step.step !== 'Success' && step.step !== 'Failed') {
|
|
940
|
+
// Not a retry, success, or failed step - use default processing
|
|
592
941
|
return null;
|
|
593
942
|
}
|
|
943
|
+
// Check if this is a special flow prompt step marker
|
|
594
944
|
const flowStep = step;
|
|
595
945
|
if (flowStep.flowPromptStepId) {
|
|
946
|
+
// This is a prompt step in the flow, let it execute normally
|
|
596
947
|
return null;
|
|
597
948
|
}
|
|
949
|
+
// Get the updated payload (after action execution)
|
|
598
950
|
const payloadFromStep = payload || step.newPayload || params.payload || {};
|
|
599
951
|
const flowState = agentTypeState;
|
|
952
|
+
// CRITICAL FIX: Don't overwrite the flow state's payload if it already has accumulated data
|
|
953
|
+
// The retry step only contains the action's output mapping result, not the full payload
|
|
954
|
+
// If flow state already has a payload with more data, keep it
|
|
600
955
|
let currentPayload = payloadFromStep;
|
|
956
|
+
// We should have a current step ID from the flow state
|
|
601
957
|
if (!flowState.currentStepId) {
|
|
602
958
|
return this.createNextStep('Failed', {
|
|
603
959
|
errorMessage: 'No current step in flow state after action execution',
|
|
@@ -605,15 +961,23 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
605
961
|
previousPayload: currentPayload
|
|
606
962
|
});
|
|
607
963
|
}
|
|
964
|
+
// Store the step result so path conditions can access it via stepResult
|
|
965
|
+
// The step parameter contains the result from the just-completed step (Sub-Agent, Action, or Prompt)
|
|
608
966
|
flowState.stepResults.set(flowState.currentStepId, step);
|
|
967
|
+
// Add to execution path if not already present
|
|
609
968
|
if (!flowState.executionPath.includes(flowState.currentStepId)) {
|
|
610
969
|
flowState.executionPath.push(flowState.currentStepId);
|
|
611
970
|
}
|
|
971
|
+
// Mark step as completed
|
|
612
972
|
flowState.completedStepIds.add(flowState.currentStepId);
|
|
973
|
+
// Find valid paths from current step using updated payload
|
|
974
|
+
// Pass params to enable data/context access in path conditions (Phase 1)
|
|
613
975
|
const paths = await this.getValidPaths(flowState.currentStepId, currentPayload, flowState, params);
|
|
614
976
|
if (paths.length === 0) {
|
|
977
|
+
// No valid paths found
|
|
978
|
+
// If the previous step failed, propagate the failure with its error message
|
|
615
979
|
if (step.step === 'Failed') {
|
|
616
|
-
|
|
980
|
+
LogStatusEx({
|
|
617
981
|
message: `⚠️ Flow Agent: Step failed with no recovery path. Error: ${step.errorMessage || 'No error message'}`,
|
|
618
982
|
verboseOnly: false
|
|
619
983
|
});
|
|
@@ -624,10 +988,12 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
624
988
|
terminate: true
|
|
625
989
|
});
|
|
626
990
|
}
|
|
627
|
-
|
|
991
|
+
// Otherwise, flow completed successfully
|
|
992
|
+
LogStatusEx({
|
|
628
993
|
message: '✅ Flow Agent: Flow completed successfully - no more paths to follow',
|
|
629
994
|
verboseOnly: true
|
|
630
995
|
});
|
|
996
|
+
// Include special fields from action output mappings if available
|
|
631
997
|
const successStep = this.createSuccessStep({
|
|
632
998
|
message: flowState.specialFields?.message || 'Flow completed - no more paths to follow',
|
|
633
999
|
reasoning: flowState.specialFields?.reasoning,
|
|
@@ -637,24 +1003,28 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
637
1003
|
});
|
|
638
1004
|
return successStep;
|
|
639
1005
|
}
|
|
1006
|
+
// Get the destination step for the highest priority path
|
|
640
1007
|
const nextStep = await this.getStepById(paths[0].DestinationStepID);
|
|
641
1008
|
if (!nextStep) {
|
|
642
1009
|
return this.createNextStep('Failed', {
|
|
643
1010
|
errorMessage: `Destination step not found: ${paths[0].DestinationStepID}`
|
|
644
1011
|
});
|
|
645
1012
|
}
|
|
1013
|
+
// Log when we're navigating to a recovery path after a failure
|
|
646
1014
|
if (step.step === 'Failed') {
|
|
647
|
-
|
|
1015
|
+
LogStatusEx({
|
|
648
1016
|
message: `🔄 Flow Agent: Failure detected, navigating to recovery path: '${nextStep.Name}'`,
|
|
649
1017
|
verboseOnly: false
|
|
650
1018
|
});
|
|
651
1019
|
}
|
|
1020
|
+
// Check if the step is active
|
|
652
1021
|
if (nextStep.Status !== 'Active') {
|
|
1022
|
+
// Try alternate paths
|
|
653
1023
|
for (let i = 1; i < paths.length; i++) {
|
|
654
1024
|
const alternateStep = await this.getStepById(paths[i].DestinationStepID);
|
|
655
1025
|
if (alternateStep && alternateStep.Status === 'Active') {
|
|
656
1026
|
if (step.step === 'Failed') {
|
|
657
|
-
|
|
1027
|
+
LogStatusEx({
|
|
658
1028
|
message: `🔄 Flow Agent: Using alternate recovery path: '${alternateStep.Name}'`,
|
|
659
1029
|
verboseOnly: false
|
|
660
1030
|
});
|
|
@@ -668,23 +1038,44 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
668
1038
|
previousPayload: currentPayload
|
|
669
1039
|
});
|
|
670
1040
|
}
|
|
1041
|
+
// Create the next step based on the flow node
|
|
671
1042
|
return await this.createStepForFlowNode(params, nextStep, currentPayload, flowState);
|
|
672
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Gets the prompt to use for a specific step.
|
|
1046
|
+
* Flow agents may use different prompts for different steps in the flow.
|
|
1047
|
+
*
|
|
1048
|
+
* @param {ExecuteAgentParams} params - The execution parameters
|
|
1049
|
+
* @param {AgentConfiguration} config - The loaded agent configuration
|
|
1050
|
+
* @param {BaseAgentNextStep | null} previousDecision - The previous step decision that may contain flow prompt info
|
|
1051
|
+
* @returns {Promise<AIPromptEntityExtended | null>} Custom prompt for flow steps, or default config.childPrompt
|
|
1052
|
+
*
|
|
1053
|
+
* @override
|
|
1054
|
+
* @since 2.76.0
|
|
1055
|
+
*/
|
|
673
1056
|
async GetPromptForStep(params, config, payload, agentTypeState, previousDecision) {
|
|
1057
|
+
// Check if this is a flow prompt step with a specific prompt ID
|
|
674
1058
|
const flowDecision = previousDecision;
|
|
675
1059
|
if (flowDecision?.flowPromptStepId) {
|
|
676
1060
|
const promptId = flowDecision.flowPromptStepId;
|
|
677
|
-
|
|
1061
|
+
// Get the specific prompt from AIEngine (avoids database hit)
|
|
1062
|
+
const promptEntity = AIEngine.Instance.Prompts.find(p => p.ID === promptId);
|
|
678
1063
|
if (promptEntity) {
|
|
679
1064
|
return promptEntity;
|
|
680
1065
|
}
|
|
681
1066
|
else {
|
|
682
|
-
|
|
1067
|
+
LogError(`Failed to find flow prompt with ID: ${promptId} in AIEngine`);
|
|
1068
|
+
// Fall back to default
|
|
683
1069
|
return config.childPrompt || null;
|
|
684
1070
|
}
|
|
685
1071
|
}
|
|
1072
|
+
// For non-flow-prompt steps, use the default prompt
|
|
686
1073
|
return config.childPrompt || null;
|
|
687
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Converts a Flow Agent ForEach step into universal ForEachOperation format
|
|
1077
|
+
* that BaseAgent can execute
|
|
1078
|
+
*/
|
|
688
1079
|
async convertForEachStepToOperation(node, payload, flowState, params) {
|
|
689
1080
|
if (!node.Configuration) {
|
|
690
1081
|
return this.createNextStep('Failed', {
|
|
@@ -693,7 +1084,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
693
1084
|
previousPayload: payload
|
|
694
1085
|
});
|
|
695
1086
|
}
|
|
1087
|
+
// Parse base configuration from AIAgentStep
|
|
696
1088
|
const baseConfig = JSON.parse(node.Configuration);
|
|
1089
|
+
// Build universal ForEachOperation
|
|
697
1090
|
const forEach = {
|
|
698
1091
|
collectionPath: baseConfig.collectionPath,
|
|
699
1092
|
itemVariable: baseConfig.itemVariable,
|
|
@@ -704,8 +1097,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
704
1097
|
executionMode: baseConfig.executionMode,
|
|
705
1098
|
maxConcurrency: baseConfig.maxConcurrency
|
|
706
1099
|
};
|
|
1100
|
+
// Add action or subAgent based on LoopBodyType (using cached engine data - no await needed)
|
|
707
1101
|
if (node.LoopBodyType === 'Action') {
|
|
708
|
-
const action =
|
|
1102
|
+
const action = AIEngine.Instance.Actions.find(a => a.ID === node.ActionID);
|
|
709
1103
|
if (!action) {
|
|
710
1104
|
return this.createNextStep('Failed', {
|
|
711
1105
|
errorMessage: `Action not found for loop body: ${node.ActionID}`,
|
|
@@ -720,7 +1114,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
720
1114
|
};
|
|
721
1115
|
}
|
|
722
1116
|
else if (node.LoopBodyType === 'Sub-Agent') {
|
|
723
|
-
const subAgent =
|
|
1117
|
+
const subAgent = AIEngine.Instance.Agents.find(a => a.ID === node.SubAgentID);
|
|
724
1118
|
if (!subAgent) {
|
|
725
1119
|
return this.createNextStep('Failed', {
|
|
726
1120
|
errorMessage: `Sub-Agent not found for loop body: ${node.SubAgentID}`,
|
|
@@ -732,7 +1126,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
732
1126
|
name: subAgent.Name,
|
|
733
1127
|
message: node.Description || `Execute sub-agent: ${subAgent.Name}`,
|
|
734
1128
|
templateParameters: {},
|
|
735
|
-
context: params.context
|
|
1129
|
+
context: params.context // Propagate runtime context to sub-agent in loop
|
|
736
1130
|
};
|
|
737
1131
|
}
|
|
738
1132
|
else if (node.LoopBodyType === 'Prompt') {
|
|
@@ -742,16 +1136,22 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
742
1136
|
previousPayload: payload
|
|
743
1137
|
});
|
|
744
1138
|
}
|
|
1139
|
+
// Store stepId in flow state for later (when loop completes, we need to navigate paths)
|
|
745
1140
|
flowState.currentStepId = node.ID;
|
|
1141
|
+
// Return ForEach decision for BaseAgent to execute
|
|
746
1142
|
return {
|
|
747
1143
|
step: 'ForEach',
|
|
748
1144
|
forEach,
|
|
749
1145
|
terminate: false,
|
|
750
1146
|
newPayload: payload,
|
|
751
1147
|
previousPayload: payload,
|
|
752
|
-
agentTypeData: { stepId: node.ID }
|
|
1148
|
+
agentTypeData: { stepId: node.ID } // Flow needs to remember which step this is
|
|
753
1149
|
};
|
|
754
1150
|
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Converts a Flow Agent While step into universal WhileOperation format
|
|
1153
|
+
* that BaseAgent can execute
|
|
1154
|
+
*/
|
|
755
1155
|
async convertWhileStepToOperation(node, payload, flowState, params) {
|
|
756
1156
|
if (!node.Configuration) {
|
|
757
1157
|
return this.createNextStep('Failed', {
|
|
@@ -760,7 +1160,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
760
1160
|
previousPayload: payload
|
|
761
1161
|
});
|
|
762
1162
|
}
|
|
1163
|
+
// Parse base configuration
|
|
763
1164
|
const baseConfig = JSON.parse(node.Configuration);
|
|
1165
|
+
// Build universal WhileOperation
|
|
764
1166
|
const whileOp = {
|
|
765
1167
|
condition: baseConfig.condition,
|
|
766
1168
|
itemVariable: baseConfig.itemVariable,
|
|
@@ -768,8 +1170,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
768
1170
|
continueOnError: baseConfig.continueOnError,
|
|
769
1171
|
delayBetweenIterationsMs: baseConfig.delayBetweenIterationsMs
|
|
770
1172
|
};
|
|
1173
|
+
// Add action or subAgent based on LoopBodyType
|
|
771
1174
|
if (node.LoopBodyType === 'Action') {
|
|
772
|
-
const action =
|
|
1175
|
+
const action = AIEngine.Instance.Actions.find(a => a.ID === node.ActionID);
|
|
773
1176
|
if (!action) {
|
|
774
1177
|
return this.createNextStep('Failed', {
|
|
775
1178
|
errorMessage: `Action not found for loop body: ${node.ActionID}`,
|
|
@@ -784,7 +1187,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
784
1187
|
};
|
|
785
1188
|
}
|
|
786
1189
|
else if (node.LoopBodyType === 'Sub-Agent') {
|
|
787
|
-
const subAgent =
|
|
1190
|
+
const subAgent = AIEngine.Instance.Agents.find(a => a.ID === node.SubAgentID);
|
|
788
1191
|
if (!subAgent) {
|
|
789
1192
|
return this.createNextStep('Failed', {
|
|
790
1193
|
errorMessage: `Sub-Agent not found for loop body: ${node.SubAgentID}`,
|
|
@@ -796,7 +1199,7 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
796
1199
|
name: subAgent.Name,
|
|
797
1200
|
message: node.Description || `Execute sub-agent: ${subAgent.Name}`,
|
|
798
1201
|
templateParameters: {},
|
|
799
|
-
context: params.context
|
|
1202
|
+
context: params.context // Propagate runtime context to sub-agent in loop
|
|
800
1203
|
};
|
|
801
1204
|
}
|
|
802
1205
|
else if (node.LoopBodyType === 'Prompt') {
|
|
@@ -806,7 +1209,9 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
806
1209
|
previousPayload: payload
|
|
807
1210
|
});
|
|
808
1211
|
}
|
|
1212
|
+
// Store stepId for later path navigation
|
|
809
1213
|
flowState.currentStepId = node.ID;
|
|
1214
|
+
// Return While decision for BaseAgent to execute
|
|
810
1215
|
return {
|
|
811
1216
|
step: 'While',
|
|
812
1217
|
while: whileOp,
|
|
@@ -816,31 +1221,63 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
816
1221
|
agentTypeData: { stepId: node.ID }
|
|
817
1222
|
};
|
|
818
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Flow agents don't need loop results injected as messages - they navigate paths
|
|
1226
|
+
* @override
|
|
1227
|
+
*/
|
|
819
1228
|
get InjectLoopResultsAsMessage() {
|
|
820
1229
|
return false;
|
|
821
1230
|
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Flow agents don't require agent-level prompts - they use step-level prompts exclusively
|
|
1233
|
+
* @override
|
|
1234
|
+
*/
|
|
822
1235
|
get RequiresAgentLevelPrompts() {
|
|
823
1236
|
return false;
|
|
824
1237
|
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Provides Flow-specific guidance for prompt configuration
|
|
1240
|
+
* @override
|
|
1241
|
+
*/
|
|
825
1242
|
GetPromptConfigurationGuidance() {
|
|
826
1243
|
return ` - Flow agents use step-level prompts (StepType="Prompt" with PromptID)\n` +
|
|
827
1244
|
` - Prompts should be configured in agent steps, not AI Agent Prompts relationship\n` +
|
|
828
1245
|
` - Verify that prompts exist in AI Prompts table and are active`;
|
|
829
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Flow agents should NEVER fall back to prompt execution for Success/Failed steps.
|
|
1249
|
+
* Flow agents are deterministic and driven by their graph structure (steps and paths).
|
|
1250
|
+
* When a step completes (Success or Failed), the agent should terminate rather than
|
|
1251
|
+
* retry with prompts.
|
|
1252
|
+
*
|
|
1253
|
+
* @override
|
|
1254
|
+
* @since 2.113.0
|
|
1255
|
+
*/
|
|
830
1256
|
async HandleStepFallback(step, config, params, payload, agentTypeState) {
|
|
1257
|
+
// Flow agents NEVER fall back to prompt execution
|
|
1258
|
+
// They are driven entirely by their graph structure (steps and paths)
|
|
1259
|
+
// Success/Failed steps should terminate, not retry prompts
|
|
831
1260
|
if (step.step === 'Success' || step.step === 'Failed') {
|
|
1261
|
+
// Use spread to preserve all properties and just override terminate
|
|
832
1262
|
return {
|
|
833
1263
|
...step,
|
|
834
1264
|
terminate: true
|
|
835
1265
|
};
|
|
836
1266
|
}
|
|
1267
|
+
// For other step types, use default behavior (should not reach here)
|
|
837
1268
|
return null;
|
|
838
1269
|
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Flow agents apply ActionOutputMapping after each iteration
|
|
1272
|
+
* @override
|
|
1273
|
+
*/
|
|
839
1274
|
AfterLoopIteration(iterationResult) {
|
|
840
1275
|
const loopContext = iterationResult.loopContext;
|
|
1276
|
+
// Only apply for actions with output mapping
|
|
841
1277
|
if (!iterationResult.actionResults || !loopContext.actionOutputMapping) {
|
|
842
1278
|
return null;
|
|
843
1279
|
}
|
|
1280
|
+
// Extract output parameters
|
|
844
1281
|
const outputParams = {};
|
|
845
1282
|
if (iterationResult.actionResults[0]?.Params) {
|
|
846
1283
|
for (const param of iterationResult.actionResults[0].Params) {
|
|
@@ -849,22 +1286,25 @@ let FlowAgentType = class FlowAgentType extends base_agent_type_1.BaseAgentType
|
|
|
849
1286
|
}
|
|
850
1287
|
}
|
|
851
1288
|
}
|
|
1289
|
+
// Replace iteration variables in output mapping paths before applying
|
|
852
1290
|
let resolvedOutputMapping = loopContext.actionOutputMapping;
|
|
853
1291
|
const indexVar = loopContext.indexVariable || 'index';
|
|
854
1292
|
const itemVar = loopContext.itemVariable || 'item';
|
|
1293
|
+
// Replace [index] with [0], [1], etc.
|
|
855
1294
|
resolvedOutputMapping = resolvedOutputMapping.replace(new RegExp(`\\[${indexVar}\\]`, 'g'), `[${iterationResult.index}]`);
|
|
1295
|
+
// Apply output mapping with resolved paths
|
|
856
1296
|
const result = this.applyActionOutputMapping(outputParams, iterationResult.currentPayload, resolvedOutputMapping);
|
|
1297
|
+
// Note: Special fields are not supported in loop iterations
|
|
1298
|
+
// They only apply to final flow steps
|
|
857
1299
|
if (result.payloadChange?.updateElements) {
|
|
1300
|
+
// Deep merge to preserve existing payload structure
|
|
858
1301
|
return this._payloadManager.deepMerge(iterationResult.currentPayload, result.payloadChange.updateElements);
|
|
859
1302
|
}
|
|
860
1303
|
return null;
|
|
861
1304
|
}
|
|
862
1305
|
};
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
(0, global_1.RegisterClass)(base_agent_type_1.BaseAgentType, "FlowAgentType")
|
|
1306
|
+
FlowAgentType = __decorate([
|
|
1307
|
+
RegisterClass(BaseAgentType, "FlowAgentType")
|
|
866
1308
|
], FlowAgentType);
|
|
867
|
-
|
|
868
|
-
}
|
|
869
|
-
exports.LoadFlowAgentType = LoadFlowAgentType;
|
|
1309
|
+
export { FlowAgentType };
|
|
870
1310
|
//# sourceMappingURL=flow-agent-type.js.map
|