@prompd/cli 0.5.0-beta.2 → 0.5.0-beta.4
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/commands/package.d.ts.map +1 -1
- package/dist/commands/package.js +85 -25
- package/dist/commands/package.js.map +1 -1
- package/dist/lib/compiler/stages/template.d.ts +6 -0
- package/dist/lib/compiler/stages/template.d.ts.map +1 -1
- package/dist/lib/compiler/stages/template.js +49 -0
- package/dist/lib/compiler/stages/template.js.map +1 -1
- package/dist/lib/workflowExecutor.d.ts +4 -0
- package/dist/lib/workflowExecutor.d.ts.map +1 -1
- package/dist/lib/workflowExecutor.js +680 -278
- package/dist/lib/workflowExecutor.js.map +1 -1
- package/dist/lib/workflowTypes.d.ts +1 -1
- package/dist/lib/workflowTypes.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -21,6 +21,169 @@ const path_1 = require("path");
|
|
|
21
21
|
const vm_1 = __importDefault(require("vm"));
|
|
22
22
|
const workflowParser_1 = require("./workflowParser");
|
|
23
23
|
const memoryBackend_1 = require("./memoryBackend");
|
|
24
|
+
/**
|
|
25
|
+
* Node types that represent callable tools.
|
|
26
|
+
* Must match TOOL_NODE_TYPES in the frontend's workflowTypes.ts.
|
|
27
|
+
*/
|
|
28
|
+
const TOOL_NODE_TYPES = new Set([
|
|
29
|
+
'tool', 'mcp-tool', 'web-search', 'skill', 'api',
|
|
30
|
+
'command', 'code', 'claude-code', 'database-query',
|
|
31
|
+
]);
|
|
32
|
+
/** Node types allowed as children in tool containers (tool-call-router, chat-agent) */
|
|
33
|
+
const TOOL_CONTAINER_CHILD_TYPES = new Set([
|
|
34
|
+
...TOOL_NODE_TYPES, 'tool-call-parser',
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* Convert any tool-like workflow node to an AgentTool definition.
|
|
38
|
+
* Handles all node types that extend BaseToolNodeData (tool, mcp-tool, web-search,
|
|
39
|
+
* skill, command, code, claude-code, database-query, api).
|
|
40
|
+
*/
|
|
41
|
+
function nodeToAgentTool(toolNode) {
|
|
42
|
+
const data = toolNode.data;
|
|
43
|
+
const nodeType = toolNode.type || 'tool';
|
|
44
|
+
// Read BaseToolNodeData fields (all tool-like nodes have these)
|
|
45
|
+
const toolName = data.toolName || data.label || nodeType;
|
|
46
|
+
const description = data.description || `Tool: ${toolName}`;
|
|
47
|
+
const parameterSchema = data.parameterSchema;
|
|
48
|
+
// Determine AgentTool toolType based on node type
|
|
49
|
+
let agentToolType = 'function';
|
|
50
|
+
let originalToolType = nodeType;
|
|
51
|
+
if (nodeType === 'tool') {
|
|
52
|
+
// ToolNode has its own toolType field
|
|
53
|
+
const tt = data.toolType || 'function';
|
|
54
|
+
originalToolType = tt;
|
|
55
|
+
agentToolType = tt === 'http' ? 'http' : tt === 'mcp' ? 'mcp' : 'function';
|
|
56
|
+
}
|
|
57
|
+
else if (nodeType === 'mcp-tool') {
|
|
58
|
+
agentToolType = 'mcp';
|
|
59
|
+
}
|
|
60
|
+
else if (nodeType === 'api') {
|
|
61
|
+
agentToolType = 'http';
|
|
62
|
+
}
|
|
63
|
+
else if (nodeType === 'command' || nodeType === 'claude-code') {
|
|
64
|
+
agentToolType = 'command';
|
|
65
|
+
}
|
|
66
|
+
else if (nodeType === 'code') {
|
|
67
|
+
agentToolType = 'code';
|
|
68
|
+
}
|
|
69
|
+
else if (nodeType === 'web-search') {
|
|
70
|
+
agentToolType = 'web-search';
|
|
71
|
+
}
|
|
72
|
+
else if (nodeType === 'database-query') {
|
|
73
|
+
agentToolType = 'database-query';
|
|
74
|
+
}
|
|
75
|
+
// skill → 'function' (default)
|
|
76
|
+
// Auto-generate parameterSchema for node types that have known inputs
|
|
77
|
+
// but no explicit schema set by the user
|
|
78
|
+
let resolvedParameters = parameterSchema;
|
|
79
|
+
if (!resolvedParameters || !resolvedParameters.properties || Object.keys(resolvedParameters.properties).length === 0) {
|
|
80
|
+
if (nodeType === 'web-search') {
|
|
81
|
+
resolvedParameters = {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
query: { type: 'string', description: 'The search query to look up on the web' },
|
|
85
|
+
},
|
|
86
|
+
required: ['query'],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
else if (nodeType === 'database-query') {
|
|
90
|
+
const dbData = data;
|
|
91
|
+
// For MongoDB: the LLM provides a JSON query document and optionally a collection
|
|
92
|
+
// For SQL: the LLM provides the SQL query and optionally parameters
|
|
93
|
+
// The node's configured values serve as defaults
|
|
94
|
+
resolvedParameters = {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
query: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: dbData.collection
|
|
100
|
+
? `Query to execute. For MongoDB, provide a JSON filter document (e.g. {"name": "John"}). Default collection: ${dbData.collection}`
|
|
101
|
+
: 'SQL query or database command to execute',
|
|
102
|
+
},
|
|
103
|
+
...(dbData.collection ? {
|
|
104
|
+
collection: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: `MongoDB collection name (default: ${dbData.collection})`,
|
|
107
|
+
},
|
|
108
|
+
} : {}),
|
|
109
|
+
},
|
|
110
|
+
required: ['query'],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else if (nodeType === 'command') {
|
|
114
|
+
resolvedParameters = {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
input: { type: 'string', description: 'Input to pass to the command' },
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
else if (nodeType === 'api') {
|
|
122
|
+
resolvedParameters = {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
input: { type: 'string', description: 'Input data for the API request' },
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const agentTool = {
|
|
131
|
+
name: toolName,
|
|
132
|
+
description,
|
|
133
|
+
toolType: agentToolType,
|
|
134
|
+
parameters: resolvedParameters,
|
|
135
|
+
_toolNodeId: toolNode.id,
|
|
136
|
+
_originalToolType: originalToolType,
|
|
137
|
+
};
|
|
138
|
+
// Add type-specific config
|
|
139
|
+
if (nodeType === 'tool') {
|
|
140
|
+
const toolData = data;
|
|
141
|
+
if (toolData.toolType === 'http') {
|
|
142
|
+
agentTool.httpConfig = {
|
|
143
|
+
method: toolData.httpMethod || 'GET',
|
|
144
|
+
url: toolData.httpUrl || '',
|
|
145
|
+
headers: toolData.httpHeaders,
|
|
146
|
+
bodyTemplate: toolData.httpBody,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
else if (toolData.toolType === 'mcp') {
|
|
150
|
+
agentTool.mcpConfig = {
|
|
151
|
+
serverUrl: toolData.mcpServerUrl,
|
|
152
|
+
serverName: toolData.mcpServerName,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (nodeType === 'mcp-tool') {
|
|
157
|
+
const mcpData = data;
|
|
158
|
+
agentTool.mcpConfig = {
|
|
159
|
+
serverUrl: mcpData.serverConfig?.serverUrl,
|
|
160
|
+
serverName: mcpData.serverConfig?.serverName,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
else if (nodeType === 'api') {
|
|
164
|
+
const apiData = data;
|
|
165
|
+
agentTool.httpConfig = {
|
|
166
|
+
method: apiData.method || 'GET',
|
|
167
|
+
url: apiData.url || '',
|
|
168
|
+
headers: apiData.headers,
|
|
169
|
+
bodyTemplate: apiData.body,
|
|
170
|
+
};
|
|
171
|
+
// Carry connectionId so execution can resolve baseUrl from the http-api connection
|
|
172
|
+
if (data.connectionId) {
|
|
173
|
+
agentTool._connectionId = data.connectionId;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (nodeType === 'command') {
|
|
177
|
+
const cmdData = data;
|
|
178
|
+
agentTool.commandConfig = {
|
|
179
|
+
executable: cmdData.command || '',
|
|
180
|
+
args: cmdData.args?.join(' '),
|
|
181
|
+
cwd: cmdData.cwd,
|
|
182
|
+
requiresApproval: cmdData.requiresApproval,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return agentTool;
|
|
186
|
+
}
|
|
24
187
|
/**
|
|
25
188
|
* Send error notification to webhook endpoint
|
|
26
189
|
* This is fire-and-forget - errors are logged but don't affect execution
|
|
@@ -3640,6 +3803,321 @@ async function emitAgentCheckpoint(event, options, workflowFile) {
|
|
|
3640
3803
|
* history is captured and included in the output for downstream analysis.
|
|
3641
3804
|
* Use a Checkpoint node connected to the agent output to inspect agent state.
|
|
3642
3805
|
*/
|
|
3806
|
+
/**
|
|
3807
|
+
* Build memory tools based on the docked memory node's mode.
|
|
3808
|
+
*
|
|
3809
|
+
* When a memory node is docked to an agent's 'memory' handle, the tools
|
|
3810
|
+
* adapt to the memory mode:
|
|
3811
|
+
* - kv: memory_get, memory_set, memory_delete, memory_list
|
|
3812
|
+
* - conversation: memory_get_history, memory_append, memory_clear_history
|
|
3813
|
+
* - cache: memory_get (with TTL info), memory_set (with TTL), memory_delete, memory_list
|
|
3814
|
+
*
|
|
3815
|
+
* When no memory node is docked, defaults to KV tools for backward compatibility.
|
|
3816
|
+
* Scope and namespace are pre-filled from the memory node config to simplify LLM usage.
|
|
3817
|
+
*/
|
|
3818
|
+
function buildMemoryTools(agentNodeId, workflowFile) {
|
|
3819
|
+
// Find docked memory node
|
|
3820
|
+
let memoryMode = 'kv';
|
|
3821
|
+
let defaultScope = 'workflow';
|
|
3822
|
+
let defaultNamespace = '';
|
|
3823
|
+
let defaultConversationId = 'default';
|
|
3824
|
+
let maxMessages = 0;
|
|
3825
|
+
if (workflowFile) {
|
|
3826
|
+
const dockedMemoryNode = workflowFile.nodes.find(n => {
|
|
3827
|
+
const nodeData = n.data;
|
|
3828
|
+
return n.type === 'memory' &&
|
|
3829
|
+
nodeData.dockedTo?.nodeId === agentNodeId &&
|
|
3830
|
+
nodeData.dockedTo?.handleId === 'memory';
|
|
3831
|
+
});
|
|
3832
|
+
// Also check for edge-connected memory nodes (non-docked)
|
|
3833
|
+
const edgeConnectedMemoryNode = !dockedMemoryNode ? workflowFile.nodes.find(n => {
|
|
3834
|
+
if (n.type !== 'memory')
|
|
3835
|
+
return false;
|
|
3836
|
+
return workflowFile.edges.some(e => e.source === agentNodeId && e.sourceHandle === 'memory' && e.target === n.id);
|
|
3837
|
+
}) : undefined;
|
|
3838
|
+
const memoryNode = dockedMemoryNode || edgeConnectedMemoryNode;
|
|
3839
|
+
if (memoryNode) {
|
|
3840
|
+
const memData = memoryNode.data;
|
|
3841
|
+
memoryMode = memData.mode || 'kv';
|
|
3842
|
+
defaultScope = memData.scope || 'workflow';
|
|
3843
|
+
defaultNamespace = memData.namespace || '';
|
|
3844
|
+
if (memoryMode === 'conversation') {
|
|
3845
|
+
defaultConversationId = memData.conversationId || 'default';
|
|
3846
|
+
maxMessages = memData.maxMessages ?? 0;
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
const scopeDescription = defaultScope === 'execution'
|
|
3851
|
+
? 'execution: data is cleared when this workflow run ends'
|
|
3852
|
+
: defaultScope === 'global'
|
|
3853
|
+
? 'global: data shared across all workflows'
|
|
3854
|
+
: 'workflow: data persists across executions of this workflow';
|
|
3855
|
+
switch (memoryMode) {
|
|
3856
|
+
case 'conversation': {
|
|
3857
|
+
return [
|
|
3858
|
+
{
|
|
3859
|
+
name: 'memory_get_history',
|
|
3860
|
+
description: `Retrieve conversation history. Returns an array of messages with role and content.${maxMessages > 0 ? ` Sliding window: last ${maxMessages} messages.` : ''}`,
|
|
3861
|
+
toolType: 'function',
|
|
3862
|
+
parameters: {
|
|
3863
|
+
type: 'object',
|
|
3864
|
+
properties: {
|
|
3865
|
+
conversation_id: {
|
|
3866
|
+
type: 'string',
|
|
3867
|
+
description: `Conversation thread ID (default: "${defaultConversationId}")`,
|
|
3868
|
+
},
|
|
3869
|
+
scope: {
|
|
3870
|
+
type: 'string',
|
|
3871
|
+
enum: ['workflow', 'global'],
|
|
3872
|
+
description: scopeDescription,
|
|
3873
|
+
},
|
|
3874
|
+
namespace: {
|
|
3875
|
+
type: 'string',
|
|
3876
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
3877
|
+
},
|
|
3878
|
+
},
|
|
3879
|
+
required: [],
|
|
3880
|
+
},
|
|
3881
|
+
},
|
|
3882
|
+
{
|
|
3883
|
+
name: 'memory_append',
|
|
3884
|
+
description: 'Append a message to conversation history. The message is added with a role and content.',
|
|
3885
|
+
toolType: 'function',
|
|
3886
|
+
parameters: {
|
|
3887
|
+
type: 'object',
|
|
3888
|
+
properties: {
|
|
3889
|
+
role: {
|
|
3890
|
+
type: 'string',
|
|
3891
|
+
enum: ['user', 'assistant', 'system'],
|
|
3892
|
+
description: 'The role of the message sender',
|
|
3893
|
+
},
|
|
3894
|
+
content: {
|
|
3895
|
+
type: 'string',
|
|
3896
|
+
description: 'The message content to append',
|
|
3897
|
+
},
|
|
3898
|
+
conversation_id: {
|
|
3899
|
+
type: 'string',
|
|
3900
|
+
description: `Conversation thread ID (default: "${defaultConversationId}")`,
|
|
3901
|
+
},
|
|
3902
|
+
scope: {
|
|
3903
|
+
type: 'string',
|
|
3904
|
+
enum: ['workflow', 'global'],
|
|
3905
|
+
description: scopeDescription,
|
|
3906
|
+
},
|
|
3907
|
+
namespace: {
|
|
3908
|
+
type: 'string',
|
|
3909
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
3910
|
+
},
|
|
3911
|
+
},
|
|
3912
|
+
required: ['role', 'content'],
|
|
3913
|
+
},
|
|
3914
|
+
},
|
|
3915
|
+
{
|
|
3916
|
+
name: 'memory_clear_history',
|
|
3917
|
+
description: 'Clear all messages from a conversation history.',
|
|
3918
|
+
toolType: 'function',
|
|
3919
|
+
parameters: {
|
|
3920
|
+
type: 'object',
|
|
3921
|
+
properties: {
|
|
3922
|
+
conversation_id: {
|
|
3923
|
+
type: 'string',
|
|
3924
|
+
description: `Conversation thread ID (default: "${defaultConversationId}")`,
|
|
3925
|
+
},
|
|
3926
|
+
scope: {
|
|
3927
|
+
type: 'string',
|
|
3928
|
+
enum: ['workflow', 'global'],
|
|
3929
|
+
description: scopeDescription,
|
|
3930
|
+
},
|
|
3931
|
+
namespace: {
|
|
3932
|
+
type: 'string',
|
|
3933
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
3934
|
+
},
|
|
3935
|
+
},
|
|
3936
|
+
required: [],
|
|
3937
|
+
},
|
|
3938
|
+
},
|
|
3939
|
+
];
|
|
3940
|
+
}
|
|
3941
|
+
case 'cache': {
|
|
3942
|
+
return [
|
|
3943
|
+
{
|
|
3944
|
+
name: 'memory_get',
|
|
3945
|
+
description: 'Retrieve a cached value. Returns null if expired or not found.',
|
|
3946
|
+
toolType: 'function',
|
|
3947
|
+
parameters: {
|
|
3948
|
+
type: 'object',
|
|
3949
|
+
properties: {
|
|
3950
|
+
key: { type: 'string', description: 'The cache key to retrieve' },
|
|
3951
|
+
scope: {
|
|
3952
|
+
type: 'string',
|
|
3953
|
+
enum: ['workflow', 'global'],
|
|
3954
|
+
description: scopeDescription,
|
|
3955
|
+
},
|
|
3956
|
+
namespace: {
|
|
3957
|
+
type: 'string',
|
|
3958
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
3959
|
+
},
|
|
3960
|
+
},
|
|
3961
|
+
required: ['key'],
|
|
3962
|
+
},
|
|
3963
|
+
},
|
|
3964
|
+
{
|
|
3965
|
+
name: 'memory_set',
|
|
3966
|
+
description: 'Store a value in cache with optional TTL (time-to-live in seconds).',
|
|
3967
|
+
toolType: 'function',
|
|
3968
|
+
parameters: {
|
|
3969
|
+
type: 'object',
|
|
3970
|
+
properties: {
|
|
3971
|
+
key: { type: 'string', description: 'The cache key' },
|
|
3972
|
+
value: { type: 'string', description: 'The value to cache (any JSON-serializable data)' },
|
|
3973
|
+
ttl: { type: 'number', description: 'Time-to-live in seconds (0 = no expiration)' },
|
|
3974
|
+
scope: {
|
|
3975
|
+
type: 'string',
|
|
3976
|
+
enum: ['workflow', 'global'],
|
|
3977
|
+
description: scopeDescription,
|
|
3978
|
+
},
|
|
3979
|
+
namespace: {
|
|
3980
|
+
type: 'string',
|
|
3981
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
3982
|
+
},
|
|
3983
|
+
},
|
|
3984
|
+
required: ['key', 'value'],
|
|
3985
|
+
},
|
|
3986
|
+
},
|
|
3987
|
+
{
|
|
3988
|
+
name: 'memory_delete',
|
|
3989
|
+
description: 'Delete a cached value.',
|
|
3990
|
+
toolType: 'function',
|
|
3991
|
+
parameters: {
|
|
3992
|
+
type: 'object',
|
|
3993
|
+
properties: {
|
|
3994
|
+
key: { type: 'string', description: 'The cache key to delete' },
|
|
3995
|
+
scope: {
|
|
3996
|
+
type: 'string',
|
|
3997
|
+
enum: ['workflow', 'global'],
|
|
3998
|
+
description: scopeDescription,
|
|
3999
|
+
},
|
|
4000
|
+
namespace: {
|
|
4001
|
+
type: 'string',
|
|
4002
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
4003
|
+
},
|
|
4004
|
+
},
|
|
4005
|
+
required: ['key'],
|
|
4006
|
+
},
|
|
4007
|
+
},
|
|
4008
|
+
{
|
|
4009
|
+
name: 'memory_list',
|
|
4010
|
+
description: 'List all cache keys in a namespace.',
|
|
4011
|
+
toolType: 'function',
|
|
4012
|
+
parameters: {
|
|
4013
|
+
type: 'object',
|
|
4014
|
+
properties: {
|
|
4015
|
+
scope: {
|
|
4016
|
+
type: 'string',
|
|
4017
|
+
enum: ['workflow', 'global'],
|
|
4018
|
+
description: scopeDescription,
|
|
4019
|
+
},
|
|
4020
|
+
namespace: {
|
|
4021
|
+
type: 'string',
|
|
4022
|
+
description: `Namespace for isolation (default: "${defaultNamespace || 'default'}")`,
|
|
4023
|
+
},
|
|
4024
|
+
},
|
|
4025
|
+
required: [],
|
|
4026
|
+
},
|
|
4027
|
+
},
|
|
4028
|
+
];
|
|
4029
|
+
}
|
|
4030
|
+
case 'kv':
|
|
4031
|
+
default: {
|
|
4032
|
+
return [
|
|
4033
|
+
{
|
|
4034
|
+
name: 'memory_get',
|
|
4035
|
+
description: 'Retrieve a value from workflow or global memory. Use this to recall information stored in previous workflow executions.',
|
|
4036
|
+
toolType: 'function',
|
|
4037
|
+
parameters: {
|
|
4038
|
+
type: 'object',
|
|
4039
|
+
properties: {
|
|
4040
|
+
scope: {
|
|
4041
|
+
type: 'string',
|
|
4042
|
+
enum: ['workflow', 'global'],
|
|
4043
|
+
description: scopeDescription,
|
|
4044
|
+
},
|
|
4045
|
+
namespace: {
|
|
4046
|
+
type: 'string',
|
|
4047
|
+
description: `Namespace to organize related data (default: "${defaultNamespace || 'default'}")`,
|
|
4048
|
+
},
|
|
4049
|
+
key: { type: 'string', description: 'The key to retrieve' },
|
|
4050
|
+
},
|
|
4051
|
+
required: ['key'],
|
|
4052
|
+
},
|
|
4053
|
+
},
|
|
4054
|
+
{
|
|
4055
|
+
name: 'memory_set',
|
|
4056
|
+
description: 'Store a value in workflow or global memory for future executions.',
|
|
4057
|
+
toolType: 'function',
|
|
4058
|
+
parameters: {
|
|
4059
|
+
type: 'object',
|
|
4060
|
+
properties: {
|
|
4061
|
+
scope: {
|
|
4062
|
+
type: 'string',
|
|
4063
|
+
enum: ['workflow', 'global'],
|
|
4064
|
+
description: scopeDescription,
|
|
4065
|
+
},
|
|
4066
|
+
namespace: {
|
|
4067
|
+
type: 'string',
|
|
4068
|
+
description: `Namespace to organize related data (default: "${defaultNamespace || 'default'}")`,
|
|
4069
|
+
},
|
|
4070
|
+
key: { type: 'string', description: 'The key to store under' },
|
|
4071
|
+
value: { type: 'string', description: 'The value to store (any JSON-serializable data)' },
|
|
4072
|
+
},
|
|
4073
|
+
required: ['key', 'value'],
|
|
4074
|
+
},
|
|
4075
|
+
},
|
|
4076
|
+
{
|
|
4077
|
+
name: 'memory_delete',
|
|
4078
|
+
description: 'Delete a value from workflow or global memory.',
|
|
4079
|
+
toolType: 'function',
|
|
4080
|
+
parameters: {
|
|
4081
|
+
type: 'object',
|
|
4082
|
+
properties: {
|
|
4083
|
+
scope: {
|
|
4084
|
+
type: 'string',
|
|
4085
|
+
enum: ['workflow', 'global'],
|
|
4086
|
+
description: scopeDescription,
|
|
4087
|
+
},
|
|
4088
|
+
namespace: {
|
|
4089
|
+
type: 'string',
|
|
4090
|
+
description: `Namespace containing the key (default: "${defaultNamespace || 'default'}")`,
|
|
4091
|
+
},
|
|
4092
|
+
key: { type: 'string', description: 'The key to delete' },
|
|
4093
|
+
},
|
|
4094
|
+
required: ['key'],
|
|
4095
|
+
},
|
|
4096
|
+
},
|
|
4097
|
+
{
|
|
4098
|
+
name: 'memory_list',
|
|
4099
|
+
description: 'List all keys in a namespace for workflow or global memory.',
|
|
4100
|
+
toolType: 'function',
|
|
4101
|
+
parameters: {
|
|
4102
|
+
type: 'object',
|
|
4103
|
+
properties: {
|
|
4104
|
+
scope: {
|
|
4105
|
+
type: 'string',
|
|
4106
|
+
enum: ['workflow', 'global'],
|
|
4107
|
+
description: scopeDescription,
|
|
4108
|
+
},
|
|
4109
|
+
namespace: {
|
|
4110
|
+
type: 'string',
|
|
4111
|
+
description: `Namespace to list keys from (default: "${defaultNamespace || 'default'}")`,
|
|
4112
|
+
},
|
|
4113
|
+
},
|
|
4114
|
+
required: [],
|
|
4115
|
+
},
|
|
4116
|
+
},
|
|
4117
|
+
];
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
3643
4121
|
async function executeAgentNode(node, context, options, trace, state, workflowFile, memoryBackend) {
|
|
3644
4122
|
const data = node.data;
|
|
3645
4123
|
// Check if workflow is in debug mode (from execution options)
|
|
@@ -3665,38 +4143,11 @@ async function executeAgentNode(node, context, options, trace, state, workflowFi
|
|
|
3665
4143
|
const toolRouterNode = workflowFile.nodes.find(n => n.id === toolsEdge.target && n.type === 'tool-call-router');
|
|
3666
4144
|
if (toolRouterNode) {
|
|
3667
4145
|
connectedToolRouterNodeId = toolRouterNode.id;
|
|
3668
|
-
// Find all
|
|
3669
|
-
const childToolNodes = workflowFile.nodes.filter(n => n.parentId === toolRouterNode.id && n.type
|
|
3670
|
-
// Convert
|
|
4146
|
+
// Find all tool-like nodes that are children of this Tool Router
|
|
4147
|
+
const childToolNodes = workflowFile.nodes.filter(n => n.parentId === toolRouterNode.id && TOOL_CONTAINER_CHILD_TYPES.has(n.type || ''));
|
|
4148
|
+
// Convert tool nodes to AgentTool format
|
|
3671
4149
|
for (const toolNode of childToolNodes) {
|
|
3672
|
-
|
|
3673
|
-
// Map toolType: command -> function (we'll handle it specially during execution)
|
|
3674
|
-
// Map toolType: code -> function (same handling)
|
|
3675
|
-
const agentToolType = toolData.toolType === 'http' ? 'http' :
|
|
3676
|
-
toolData.toolType === 'mcp' ? 'mcp' :
|
|
3677
|
-
'function'; // command, code, and function all map to 'function'
|
|
3678
|
-
const agentTool = {
|
|
3679
|
-
name: toolData.toolName,
|
|
3680
|
-
description: toolData.description || `Tool: ${toolData.toolName}`,
|
|
3681
|
-
toolType: agentToolType,
|
|
3682
|
-
parameters: toolData.parameterSchema,
|
|
3683
|
-
// Store original data for execution routing
|
|
3684
|
-
httpConfig: toolData.toolType === 'http' ? {
|
|
3685
|
-
method: toolData.httpMethod || 'GET',
|
|
3686
|
-
url: toolData.httpUrl || '',
|
|
3687
|
-
headers: toolData.httpHeaders,
|
|
3688
|
-
bodyTemplate: toolData.httpBody,
|
|
3689
|
-
} : undefined,
|
|
3690
|
-
mcpConfig: toolData.toolType === 'mcp' ? {
|
|
3691
|
-
serverUrl: toolData.mcpServerUrl,
|
|
3692
|
-
serverName: toolData.mcpServerName,
|
|
3693
|
-
} : undefined,
|
|
3694
|
-
};
|
|
3695
|
-
agentTool.
|
|
3696
|
-
_toolNodeId = toolNode.id;
|
|
3697
|
-
agentTool.
|
|
3698
|
-
_originalToolType = toolData.toolType;
|
|
3699
|
-
collectedTools.push(agentTool);
|
|
4150
|
+
collectedTools.push(nodeToAgentTool(toolNode));
|
|
3700
4151
|
}
|
|
3701
4152
|
addTraceEntry(trace, {
|
|
3702
4153
|
type: 'debug_step',
|
|
@@ -3706,116 +4157,14 @@ async function executeAgentNode(node, context, options, trace, state, workflowFi
|
|
|
3706
4157
|
message: `Collected ${childToolNodes.length} tools from Tool Router '${toolRouterNode.data.label}'`,
|
|
3707
4158
|
data: {
|
|
3708
4159
|
toolRouterNodeId: toolRouterNode.id,
|
|
3709
|
-
collectedToolNames: childToolNodes.map(n => n.data.toolName),
|
|
4160
|
+
collectedToolNames: childToolNodes.map(n => n.data.toolName || n.data.label),
|
|
3710
4161
|
},
|
|
3711
4162
|
}, options);
|
|
3712
4163
|
}
|
|
3713
4164
|
}
|
|
3714
4165
|
}
|
|
3715
|
-
// Add memory tools to
|
|
3716
|
-
const memoryTools =
|
|
3717
|
-
{
|
|
3718
|
-
name: 'memory_get',
|
|
3719
|
-
description: 'Retrieve a value from workflow or global memory. Use this to recall information stored in previous workflow executions.',
|
|
3720
|
-
toolType: 'function',
|
|
3721
|
-
parameters: {
|
|
3722
|
-
type: 'object',
|
|
3723
|
-
properties: {
|
|
3724
|
-
scope: {
|
|
3725
|
-
type: 'string',
|
|
3726
|
-
enum: ['workflow', 'global'],
|
|
3727
|
-
description: 'workflow: data persists across executions of this workflow; global: data shared across all workflows'
|
|
3728
|
-
},
|
|
3729
|
-
namespace: {
|
|
3730
|
-
type: 'string',
|
|
3731
|
-
description: 'Namespace to organize related data (e.g., "user_preferences", "session_data")'
|
|
3732
|
-
},
|
|
3733
|
-
key: {
|
|
3734
|
-
type: 'string',
|
|
3735
|
-
description: 'The key to retrieve'
|
|
3736
|
-
}
|
|
3737
|
-
},
|
|
3738
|
-
required: ['scope', 'namespace', 'key']
|
|
3739
|
-
}
|
|
3740
|
-
},
|
|
3741
|
-
{
|
|
3742
|
-
name: 'memory_set',
|
|
3743
|
-
description: 'Store a value in workflow or global memory for future executions. Use this to remember information across workflow runs.',
|
|
3744
|
-
toolType: 'function',
|
|
3745
|
-
parameters: {
|
|
3746
|
-
type: 'object',
|
|
3747
|
-
properties: {
|
|
3748
|
-
scope: {
|
|
3749
|
-
type: 'string',
|
|
3750
|
-
enum: ['workflow', 'global'],
|
|
3751
|
-
description: 'workflow: data persists across executions of this workflow; global: data shared across all workflows'
|
|
3752
|
-
},
|
|
3753
|
-
namespace: {
|
|
3754
|
-
type: 'string',
|
|
3755
|
-
description: 'Namespace to organize related data (e.g., "user_preferences", "session_data")'
|
|
3756
|
-
},
|
|
3757
|
-
key: {
|
|
3758
|
-
type: 'string',
|
|
3759
|
-
description: 'The key to store under'
|
|
3760
|
-
},
|
|
3761
|
-
value: {
|
|
3762
|
-
type: 'string',
|
|
3763
|
-
description: 'The value to store (any JSON-serializable data)'
|
|
3764
|
-
},
|
|
3765
|
-
ttl: {
|
|
3766
|
-
type: 'number',
|
|
3767
|
-
description: 'Optional: Time-to-live in seconds (for cache mode only)'
|
|
3768
|
-
}
|
|
3769
|
-
},
|
|
3770
|
-
required: ['scope', 'namespace', 'key', 'value']
|
|
3771
|
-
}
|
|
3772
|
-
},
|
|
3773
|
-
{
|
|
3774
|
-
name: 'memory_delete',
|
|
3775
|
-
description: 'Delete a value from workflow or global memory.',
|
|
3776
|
-
toolType: 'function',
|
|
3777
|
-
parameters: {
|
|
3778
|
-
type: 'object',
|
|
3779
|
-
properties: {
|
|
3780
|
-
scope: {
|
|
3781
|
-
type: 'string',
|
|
3782
|
-
enum: ['workflow', 'global'],
|
|
3783
|
-
description: 'workflow or global scope'
|
|
3784
|
-
},
|
|
3785
|
-
namespace: {
|
|
3786
|
-
type: 'string',
|
|
3787
|
-
description: 'Namespace containing the key'
|
|
3788
|
-
},
|
|
3789
|
-
key: {
|
|
3790
|
-
type: 'string',
|
|
3791
|
-
description: 'The key to delete'
|
|
3792
|
-
}
|
|
3793
|
-
},
|
|
3794
|
-
required: ['scope', 'namespace', 'key']
|
|
3795
|
-
}
|
|
3796
|
-
},
|
|
3797
|
-
{
|
|
3798
|
-
name: 'memory_list',
|
|
3799
|
-
description: 'List all keys in a namespace for workflow or global memory.',
|
|
3800
|
-
toolType: 'function',
|
|
3801
|
-
parameters: {
|
|
3802
|
-
type: 'object',
|
|
3803
|
-
properties: {
|
|
3804
|
-
scope: {
|
|
3805
|
-
type: 'string',
|
|
3806
|
-
enum: ['workflow', 'global'],
|
|
3807
|
-
description: 'workflow or global scope'
|
|
3808
|
-
},
|
|
3809
|
-
namespace: {
|
|
3810
|
-
type: 'string',
|
|
3811
|
-
description: 'Namespace to list keys from'
|
|
3812
|
-
}
|
|
3813
|
-
},
|
|
3814
|
-
required: ['scope', 'namespace']
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
|
-
];
|
|
3818
|
-
// Add memory tools to collected tools
|
|
4166
|
+
// Add memory tools adapted to the docked memory node's mode (kv/conversation/cache)
|
|
4167
|
+
const memoryTools = buildMemoryTools(node.id, workflowFile);
|
|
3819
4168
|
collectedTools.push(...memoryTools);
|
|
3820
4169
|
// Resolve user prompt with template expressions
|
|
3821
4170
|
let userPrompt = data.userPrompt || '{{ input }}';
|
|
@@ -3965,7 +4314,9 @@ async function executeAgentNode(node, context, options, trace, state, workflowFi
|
|
|
3965
4314
|
}
|
|
3966
4315
|
// Tool call found - execute it
|
|
3967
4316
|
totalToolCalls++;
|
|
3968
|
-
|
|
4317
|
+
// Normalize tool name: strip 'functions.' prefix that some LLMs add (e.g. "functions.web_search" → "web_search")
|
|
4318
|
+
const rawToolName = toolCallResult.toolName;
|
|
4319
|
+
const toolName = rawToolName.startsWith('functions.') ? rawToolName.slice('functions.'.length) : rawToolName;
|
|
3969
4320
|
const toolParams = toolCallResult.toolParameters || {};
|
|
3970
4321
|
// Add assistant message with tool call to history
|
|
3971
4322
|
conversationHistory.push({
|
|
@@ -4663,158 +5014,25 @@ Analyze the input above. Return a JSON object:
|
|
|
4663
5014
|
}
|
|
4664
5015
|
// Collect tools from child nodes or inline tools
|
|
4665
5016
|
let collectedTools = [...(data.tools || [])];
|
|
4666
|
-
// Find tool nodes that are children of this chat-agent node
|
|
5017
|
+
// Find tool-like nodes that are children of this chat-agent node
|
|
4667
5018
|
if (workflowFile) {
|
|
4668
|
-
const childToolNodes = workflowFile.nodes.filter(n => n.parentId === node.id && n.type
|
|
5019
|
+
const childToolNodes = workflowFile.nodes.filter(n => n.parentId === node.id && TOOL_CONTAINER_CHILD_TYPES.has(n.type || ''));
|
|
4669
5020
|
for (const toolNode of childToolNodes) {
|
|
4670
|
-
|
|
4671
|
-
const agentToolType = toolData.toolType === 'http' ? 'http' :
|
|
4672
|
-
toolData.toolType === 'mcp' ? 'mcp' : 'function';
|
|
4673
|
-
const agentTool = {
|
|
4674
|
-
name: toolData.toolName,
|
|
4675
|
-
description: toolData.description || `Tool: ${toolData.toolName}`,
|
|
4676
|
-
toolType: agentToolType,
|
|
4677
|
-
parameters: toolData.parameterSchema,
|
|
4678
|
-
httpConfig: toolData.toolType === 'http' ? {
|
|
4679
|
-
method: toolData.httpMethod || 'GET',
|
|
4680
|
-
url: toolData.httpUrl || '',
|
|
4681
|
-
headers: toolData.httpHeaders,
|
|
4682
|
-
bodyTemplate: toolData.httpBody,
|
|
4683
|
-
} : undefined,
|
|
4684
|
-
mcpConfig: toolData.toolType === 'mcp' ? {
|
|
4685
|
-
serverUrl: toolData.mcpServerUrl,
|
|
4686
|
-
serverName: toolData.mcpServerName,
|
|
4687
|
-
} : undefined,
|
|
4688
|
-
};
|
|
4689
|
-
agentTool._toolNodeId = toolNode.id;
|
|
4690
|
-
agentTool._originalToolType = toolData.toolType;
|
|
4691
|
-
collectedTools.push(agentTool);
|
|
5021
|
+
collectedTools.push(nodeToAgentTool(toolNode));
|
|
4692
5022
|
}
|
|
4693
5023
|
// Also check for connected tool-call-router
|
|
4694
5024
|
if (data.toolRouterNodeId) {
|
|
4695
5025
|
const toolRouterNode = workflowFile.nodes.find(n => n.id === data.toolRouterNodeId && n.type === 'tool-call-router');
|
|
4696
5026
|
if (toolRouterNode) {
|
|
4697
|
-
const routerChildTools = workflowFile.nodes.filter(n => n.parentId === toolRouterNode.id && n.type
|
|
5027
|
+
const routerChildTools = workflowFile.nodes.filter(n => n.parentId === toolRouterNode.id && TOOL_CONTAINER_CHILD_TYPES.has(n.type || ''));
|
|
4698
5028
|
for (const toolNode of routerChildTools) {
|
|
4699
|
-
|
|
4700
|
-
const agentToolType = toolData.toolType === 'http' ? 'http' :
|
|
4701
|
-
toolData.toolType === 'mcp' ? 'mcp' : 'function';
|
|
4702
|
-
const agentTool = {
|
|
4703
|
-
name: toolData.toolName,
|
|
4704
|
-
description: toolData.description || `Tool: ${toolData.toolName}`,
|
|
4705
|
-
toolType: agentToolType,
|
|
4706
|
-
parameters: toolData.parameterSchema,
|
|
4707
|
-
};
|
|
4708
|
-
agentTool._toolNodeId = toolNode.id;
|
|
4709
|
-
collectedTools.push(agentTool);
|
|
5029
|
+
collectedTools.push(nodeToAgentTool(toolNode));
|
|
4710
5030
|
}
|
|
4711
5031
|
}
|
|
4712
5032
|
}
|
|
4713
5033
|
}
|
|
4714
|
-
// Add memory tools to
|
|
4715
|
-
const memoryTools =
|
|
4716
|
-
{
|
|
4717
|
-
name: 'memory_get',
|
|
4718
|
-
description: 'Retrieve a value from workflow or global memory. Use this to recall information stored in previous workflow executions.',
|
|
4719
|
-
toolType: 'function',
|
|
4720
|
-
parameters: {
|
|
4721
|
-
type: 'object',
|
|
4722
|
-
properties: {
|
|
4723
|
-
scope: {
|
|
4724
|
-
type: 'string',
|
|
4725
|
-
enum: ['workflow', 'global'],
|
|
4726
|
-
description: 'workflow: data persists across executions of this workflow; global: data shared across all workflows'
|
|
4727
|
-
},
|
|
4728
|
-
namespace: {
|
|
4729
|
-
type: 'string',
|
|
4730
|
-
description: 'Namespace to organize related data (e.g., "user_preferences", "session_data")'
|
|
4731
|
-
},
|
|
4732
|
-
key: {
|
|
4733
|
-
type: 'string',
|
|
4734
|
-
description: 'The key to retrieve'
|
|
4735
|
-
}
|
|
4736
|
-
},
|
|
4737
|
-
required: ['scope', 'namespace', 'key']
|
|
4738
|
-
}
|
|
4739
|
-
},
|
|
4740
|
-
{
|
|
4741
|
-
name: 'memory_set',
|
|
4742
|
-
description: 'Store a value in workflow or global memory for future executions. Use this to remember information across workflow runs.',
|
|
4743
|
-
toolType: 'function',
|
|
4744
|
-
parameters: {
|
|
4745
|
-
type: 'object',
|
|
4746
|
-
properties: {
|
|
4747
|
-
scope: {
|
|
4748
|
-
type: 'string',
|
|
4749
|
-
enum: ['workflow', 'global'],
|
|
4750
|
-
description: 'workflow: data persists across executions of this workflow; global: data shared across all workflows'
|
|
4751
|
-
},
|
|
4752
|
-
namespace: {
|
|
4753
|
-
type: 'string',
|
|
4754
|
-
description: 'Namespace to organize related data (e.g., "user_preferences", "session_data")'
|
|
4755
|
-
},
|
|
4756
|
-
key: {
|
|
4757
|
-
type: 'string',
|
|
4758
|
-
description: 'The key to store under'
|
|
4759
|
-
},
|
|
4760
|
-
value: {
|
|
4761
|
-
type: 'string',
|
|
4762
|
-
description: 'The value to store (any JSON-serializable data)'
|
|
4763
|
-
},
|
|
4764
|
-
ttl: {
|
|
4765
|
-
type: 'number',
|
|
4766
|
-
description: 'Optional: Time-to-live in seconds (for cache mode only)'
|
|
4767
|
-
}
|
|
4768
|
-
},
|
|
4769
|
-
required: ['scope', 'namespace', 'key', 'value']
|
|
4770
|
-
}
|
|
4771
|
-
},
|
|
4772
|
-
{
|
|
4773
|
-
name: 'memory_delete',
|
|
4774
|
-
description: 'Delete a value from workflow or global memory.',
|
|
4775
|
-
toolType: 'function',
|
|
4776
|
-
parameters: {
|
|
4777
|
-
type: 'object',
|
|
4778
|
-
properties: {
|
|
4779
|
-
scope: {
|
|
4780
|
-
type: 'string',
|
|
4781
|
-
enum: ['workflow', 'global'],
|
|
4782
|
-
description: 'workflow or global scope'
|
|
4783
|
-
},
|
|
4784
|
-
namespace: {
|
|
4785
|
-
type: 'string',
|
|
4786
|
-
description: 'Namespace containing the key'
|
|
4787
|
-
},
|
|
4788
|
-
key: {
|
|
4789
|
-
type: 'string',
|
|
4790
|
-
description: 'The key to delete'
|
|
4791
|
-
}
|
|
4792
|
-
},
|
|
4793
|
-
required: ['scope', 'namespace', 'key']
|
|
4794
|
-
}
|
|
4795
|
-
},
|
|
4796
|
-
{
|
|
4797
|
-
name: 'memory_list',
|
|
4798
|
-
description: 'List all keys in a namespace for workflow or global memory.',
|
|
4799
|
-
toolType: 'function',
|
|
4800
|
-
parameters: {
|
|
4801
|
-
type: 'object',
|
|
4802
|
-
properties: {
|
|
4803
|
-
scope: {
|
|
4804
|
-
type: 'string',
|
|
4805
|
-
enum: ['workflow', 'global'],
|
|
4806
|
-
description: 'workflow or global scope'
|
|
4807
|
-
},
|
|
4808
|
-
namespace: {
|
|
4809
|
-
type: 'string',
|
|
4810
|
-
description: 'Namespace to list keys from'
|
|
4811
|
-
}
|
|
4812
|
-
},
|
|
4813
|
-
required: ['scope', 'namespace']
|
|
4814
|
-
}
|
|
4815
|
-
}
|
|
4816
|
-
];
|
|
4817
|
-
// Add memory tools to collected tools
|
|
5034
|
+
// Add memory tools adapted to the docked memory node's mode (kv/conversation/cache)
|
|
5035
|
+
const memoryTools = buildMemoryTools(node.id, workflowFile);
|
|
4818
5036
|
collectedTools.push(...memoryTools);
|
|
4819
5037
|
// Debug: log collected tools
|
|
4820
5038
|
console.log(`[ChatAgentNode ${node.id}] Collected ${collectedTools.length} tools:`, collectedTools.map(t => t.name));
|
|
@@ -5043,7 +5261,9 @@ Analyze the input above. Return a JSON object:
|
|
|
5043
5261
|
}
|
|
5044
5262
|
// Tool call found
|
|
5045
5263
|
totalToolCalls++;
|
|
5046
|
-
|
|
5264
|
+
// Normalize tool name: strip 'functions.' prefix that some LLMs add
|
|
5265
|
+
const rawToolName = toolCallResult.toolName;
|
|
5266
|
+
const toolName = rawToolName.startsWith('functions.') ? rawToolName.slice('functions.'.length) : rawToolName;
|
|
5047
5267
|
const toolParams = toolCallResult.toolParameters || {};
|
|
5048
5268
|
// Log the tool call with parameters
|
|
5049
5269
|
addTraceEntry(trace, {
|
|
@@ -5565,6 +5785,102 @@ async function executeAgentTool(tool, params, context, options, nodeId, trace, w
|
|
|
5565
5785
|
const keys = await memoryBackend.list(scope, namespace);
|
|
5566
5786
|
return { keys, count: keys.length };
|
|
5567
5787
|
}
|
|
5788
|
+
// Conversation memory tools
|
|
5789
|
+
case 'memory_get_history': {
|
|
5790
|
+
const conversationId = params.conversation_id || 'default';
|
|
5791
|
+
const convKey = `__conv__${conversationId}`;
|
|
5792
|
+
const resolvedScope = scope || 'workflow';
|
|
5793
|
+
const resolvedNamespace = namespace || '';
|
|
5794
|
+
addTraceEntry(trace, {
|
|
5795
|
+
type: 'debug_step',
|
|
5796
|
+
nodeId,
|
|
5797
|
+
nodeName: tool.name,
|
|
5798
|
+
nodeType: 'agent',
|
|
5799
|
+
message: `Getting conversation history: ${resolvedScope}:${resolvedNamespace}:${convKey}`,
|
|
5800
|
+
data: { scope: resolvedScope, namespace: resolvedNamespace, conversationId },
|
|
5801
|
+
}, options);
|
|
5802
|
+
const stored = await memoryBackend.get(resolvedScope, resolvedNamespace, convKey);
|
|
5803
|
+
const messages = Array.isArray(stored) ? stored : [];
|
|
5804
|
+
return { messages, messageCount: messages.length, conversationId };
|
|
5805
|
+
}
|
|
5806
|
+
case 'memory_append': {
|
|
5807
|
+
const conversationId = params.conversation_id || 'default';
|
|
5808
|
+
const convKey = `__conv__${conversationId}`;
|
|
5809
|
+
const role = params.role;
|
|
5810
|
+
const content = params.content;
|
|
5811
|
+
const resolvedScope = scope || 'workflow';
|
|
5812
|
+
const resolvedNamespace = namespace || '';
|
|
5813
|
+
addTraceEntry(trace, {
|
|
5814
|
+
type: 'debug_step',
|
|
5815
|
+
nodeId,
|
|
5816
|
+
nodeName: tool.name,
|
|
5817
|
+
nodeType: 'agent',
|
|
5818
|
+
message: `Appending ${role} message to conversation ${conversationId}`,
|
|
5819
|
+
data: { scope: resolvedScope, namespace: resolvedNamespace, conversationId, role },
|
|
5820
|
+
}, options);
|
|
5821
|
+
// Load existing conversation
|
|
5822
|
+
const stored = await memoryBackend.get(resolvedScope, resolvedNamespace, convKey);
|
|
5823
|
+
const messages = Array.isArray(stored) ? stored : [];
|
|
5824
|
+
// Append new message
|
|
5825
|
+
const message = { role, content, timestamp: Date.now() };
|
|
5826
|
+
messages.push(message);
|
|
5827
|
+
// Apply max messages limit if configured via the docked memory node
|
|
5828
|
+
// The buildMemoryTools function embeds maxMessages in the tool description
|
|
5829
|
+
// but we also check the docked memory node directly
|
|
5830
|
+
if (workflowFile) {
|
|
5831
|
+
const dockedMemoryNode = workflowFile.nodes.find(n => {
|
|
5832
|
+
const nd = n.data;
|
|
5833
|
+
return n.type === 'memory' &&
|
|
5834
|
+
nd.dockedTo?.nodeId === nodeId &&
|
|
5835
|
+
nd.dockedTo?.handleId === 'memory';
|
|
5836
|
+
});
|
|
5837
|
+
if (dockedMemoryNode) {
|
|
5838
|
+
const memData = dockedMemoryNode.data;
|
|
5839
|
+
const maxMessages = memData.maxMessages ?? 0;
|
|
5840
|
+
if (maxMessages > 0) {
|
|
5841
|
+
const includeSystem = memData.includeSystemInWindow ?? true;
|
|
5842
|
+
if (includeSystem) {
|
|
5843
|
+
while (messages.length > maxMessages) {
|
|
5844
|
+
messages.shift();
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
else {
|
|
5848
|
+
const systemMsgs = messages.filter(m => m.role === 'system');
|
|
5849
|
+
const nonSystemMsgs = messages.filter(m => m.role !== 'system');
|
|
5850
|
+
while (nonSystemMsgs.length > maxMessages) {
|
|
5851
|
+
nonSystemMsgs.shift();
|
|
5852
|
+
}
|
|
5853
|
+
// Rebuild preserving order
|
|
5854
|
+
messages.length = 0;
|
|
5855
|
+
messages.push(...systemMsgs, ...nonSystemMsgs);
|
|
5856
|
+
messages.sort((a, b) => a.timestamp - b.timestamp);
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
// Store back
|
|
5862
|
+
await memoryBackend.set(resolvedScope, resolvedNamespace, convKey, messages);
|
|
5863
|
+
return { success: true, conversationId, messageCount: messages.length, appended: message };
|
|
5864
|
+
}
|
|
5865
|
+
case 'memory_clear_history': {
|
|
5866
|
+
const conversationId = params.conversation_id || 'default';
|
|
5867
|
+
const convKey = `__conv__${conversationId}`;
|
|
5868
|
+
const resolvedScope = scope || 'workflow';
|
|
5869
|
+
const resolvedNamespace = namespace || '';
|
|
5870
|
+
addTraceEntry(trace, {
|
|
5871
|
+
type: 'debug_step',
|
|
5872
|
+
nodeId,
|
|
5873
|
+
nodeName: tool.name,
|
|
5874
|
+
nodeType: 'agent',
|
|
5875
|
+
message: `Clearing conversation history: ${conversationId}`,
|
|
5876
|
+
data: { scope: resolvedScope, namespace: resolvedNamespace, conversationId },
|
|
5877
|
+
}, options);
|
|
5878
|
+
// Get count before clearing
|
|
5879
|
+
const stored = await memoryBackend.get(resolvedScope, resolvedNamespace, convKey);
|
|
5880
|
+
const count = Array.isArray(stored) ? stored.length : 0;
|
|
5881
|
+
await memoryBackend.delete(resolvedScope, resolvedNamespace, convKey);
|
|
5882
|
+
return { success: true, conversationId, clearedCount: count };
|
|
5883
|
+
}
|
|
5568
5884
|
default:
|
|
5569
5885
|
throw new Error(`Unknown memory tool: ${tool.name}`);
|
|
5570
5886
|
}
|
|
@@ -5629,6 +5945,7 @@ async function executeAgentTool(tool, params, context, options, nodeId, trace, w
|
|
|
5629
5945
|
...tool.httpConfig.headers,
|
|
5630
5946
|
},
|
|
5631
5947
|
body,
|
|
5948
|
+
connectionId: tool._connectionId,
|
|
5632
5949
|
},
|
|
5633
5950
|
};
|
|
5634
5951
|
const result = await options.onToolCall(toolCallRequest);
|
|
@@ -5729,6 +6046,91 @@ async function executeAgentTool(tool, params, context, options, nodeId, trace, w
|
|
|
5729
6046
|
}
|
|
5730
6047
|
return applyOutputTransform(result.result, tool._toolNodeId);
|
|
5731
6048
|
}
|
|
6049
|
+
case 'command':
|
|
6050
|
+
case 'web-search':
|
|
6051
|
+
case 'database-query': {
|
|
6052
|
+
// Route through onToolCall callback for Electron-side execution
|
|
6053
|
+
if (!options.onToolCall) {
|
|
6054
|
+
throw new Error(`Tool '${tool.name}' (type: ${tool.toolType}) requires onToolCall callback`);
|
|
6055
|
+
}
|
|
6056
|
+
addTraceEntry(trace, {
|
|
6057
|
+
type: 'debug_step',
|
|
6058
|
+
nodeId,
|
|
6059
|
+
nodeName: tool.name,
|
|
6060
|
+
nodeType: 'agent',
|
|
6061
|
+
message: `Executing ${tool.toolType} tool '${tool.name}' via onToolCall`,
|
|
6062
|
+
data: { toolType: tool.toolType, params },
|
|
6063
|
+
}, options);
|
|
6064
|
+
const toolCallRequest = {
|
|
6065
|
+
nodeId,
|
|
6066
|
+
toolName: tool.name,
|
|
6067
|
+
toolType: tool.toolType,
|
|
6068
|
+
parameters: params,
|
|
6069
|
+
};
|
|
6070
|
+
// Add type-specific config from the source tool node
|
|
6071
|
+
if (tool._toolNodeId && workflowFile) {
|
|
6072
|
+
const toolNode = workflowFile.nodes.find(n => n.id === tool._toolNodeId);
|
|
6073
|
+
if (toolNode) {
|
|
6074
|
+
if (tool.toolType === 'command') {
|
|
6075
|
+
const toolData = toolNode.data;
|
|
6076
|
+
let commandArgs = toolData.commandArgs || '';
|
|
6077
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6078
|
+
commandArgs = commandArgs.replace(new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'), String(value));
|
|
6079
|
+
}
|
|
6080
|
+
toolCallRequest.commandConfig = {
|
|
6081
|
+
executable: toolData.commandExecutable || '',
|
|
6082
|
+
args: commandArgs,
|
|
6083
|
+
cwd: toolData.commandCwd,
|
|
6084
|
+
requiresApproval: toolData.commandRequiresApproval,
|
|
6085
|
+
};
|
|
6086
|
+
}
|
|
6087
|
+
else if (tool.toolType === 'web-search') {
|
|
6088
|
+
const toolData = toolNode.data;
|
|
6089
|
+
const baseData = toolNode.data;
|
|
6090
|
+
// LLM sends query via params (e.g. { query: "...", input: "..." })
|
|
6091
|
+
const searchQuery = params.query || params.input || params.search_query || '';
|
|
6092
|
+
toolCallRequest.webSearchConfig = {
|
|
6093
|
+
query: searchQuery,
|
|
6094
|
+
resultCount: toolData.resultCount || 5,
|
|
6095
|
+
// Pass inline config if set, otherwise pass connectionId for resolution
|
|
6096
|
+
connectionConfig: toolData.provider ? {
|
|
6097
|
+
provider: toolData.provider,
|
|
6098
|
+
apiKey: toolData.apiKey,
|
|
6099
|
+
instanceUrl: toolData.instanceUrl,
|
|
6100
|
+
} : undefined,
|
|
6101
|
+
connectionId: baseData.connectionId,
|
|
6102
|
+
};
|
|
6103
|
+
}
|
|
6104
|
+
else if (tool.toolType === 'database-query') {
|
|
6105
|
+
const toolData = toolNode.data;
|
|
6106
|
+
// LLM may override query via params, otherwise use node's configured query
|
|
6107
|
+
const query = params.query || toolData.query || '';
|
|
6108
|
+
toolCallRequest.databaseConfig = {
|
|
6109
|
+
connectionId: toolData.connectionId,
|
|
6110
|
+
queryType: toolData.queryType || 'select',
|
|
6111
|
+
query,
|
|
6112
|
+
parameters: params.parameters || toolData.parameters,
|
|
6113
|
+
collection: params.collection || toolData.collection,
|
|
6114
|
+
maxRows: toolData.maxRows,
|
|
6115
|
+
timeoutMs: toolData.timeoutMs,
|
|
6116
|
+
};
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
const result = await options.onToolCall(toolCallRequest);
|
|
6121
|
+
if (!result.success) {
|
|
6122
|
+
throw new Error(result.error || `Tool '${tool.name}' execution failed`);
|
|
6123
|
+
}
|
|
6124
|
+
addTraceEntry(trace, {
|
|
6125
|
+
type: 'debug_step',
|
|
6126
|
+
nodeId,
|
|
6127
|
+
nodeName: tool.name,
|
|
6128
|
+
nodeType: 'agent',
|
|
6129
|
+
message: `${tool.toolType} tool '${tool.name}' completed`,
|
|
6130
|
+
data: { success: result.success, output: result.result },
|
|
6131
|
+
}, options);
|
|
6132
|
+
return applyOutputTransform(result.result, tool._toolNodeId);
|
|
6133
|
+
}
|
|
5732
6134
|
case 'workflow': {
|
|
5733
6135
|
// Workflow tools are not yet supported in agent context
|
|
5734
6136
|
throw new Error(`Workflow tools are not yet supported in agent nodes. Tool: ${tool.name}`);
|