@hashgraphonline/conversational-agent 0.1.218 → 0.1.220
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/cjs/base-agent.d.ts +2 -0
- package/dist/cjs/core/tool-registry.d.ts +7 -0
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/langchain/form-aware-agent-executor.d.ts +14 -3
- package/dist/cjs/langchain/form-validating-tool-wrapper.d.ts +81 -0
- package/dist/cjs/langchain/index.d.ts +1 -0
- package/dist/esm/index.js +7 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index10.js +2 -2
- package/dist/esm/index21.js +1 -1
- package/dist/esm/index23.js +3 -3
- package/dist/esm/index25.js +0 -4
- package/dist/esm/index25.js.map +1 -1
- package/dist/esm/index29.js +78 -9
- package/dist/esm/index29.js.map +1 -1
- package/dist/esm/index30.js +184 -1215
- package/dist/esm/index30.js.map +1 -1
- package/dist/esm/index31.js +1193 -116
- package/dist/esm/index31.js.map +1 -1
- package/dist/esm/index32.js +120 -99
- package/dist/esm/index32.js.map +1 -1
- package/dist/esm/index33.js +106 -38
- package/dist/esm/index33.js.map +1 -1
- package/dist/esm/index34.js +41 -98
- package/dist/esm/index34.js.map +1 -1
- package/dist/esm/index35.js +103 -21
- package/dist/esm/index35.js.map +1 -1
- package/dist/esm/index36.js +21 -12
- package/dist/esm/index36.js.map +1 -1
- package/dist/esm/index37.js +11 -6
- package/dist/esm/index37.js.map +1 -1
- package/dist/esm/index38.js +6 -4
- package/dist/esm/index38.js.map +1 -1
- package/dist/esm/index39.js +5 -224
- package/dist/esm/index39.js.map +1 -1
- package/dist/esm/index40.js +213 -142
- package/dist/esm/index40.js.map +1 -1
- package/dist/esm/index41.js +181 -24
- package/dist/esm/index41.js.map +1 -1
- package/dist/esm/index42.js +24 -89
- package/dist/esm/index42.js.map +1 -1
- package/dist/esm/index43.js +95 -0
- package/dist/esm/index43.js.map +1 -0
- package/dist/esm/index5.js +2 -2
- package/dist/esm/index6.js +3 -7
- package/dist/esm/index6.js.map +1 -1
- package/dist/esm/index7.js.map +1 -1
- package/dist/esm/index8.js +1 -1
- package/dist/types/base-agent.d.ts +2 -0
- package/dist/types/core/tool-registry.d.ts +7 -0
- package/dist/types/langchain/form-aware-agent-executor.d.ts +14 -3
- package/dist/types/langchain/form-validating-tool-wrapper.d.ts +81 -0
- package/dist/types/langchain/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/base-agent.ts +4 -0
- package/src/conversational-agent.ts +3 -14
- package/src/core/tool-registry.ts +45 -1
- package/src/langchain/form-aware-agent-executor.ts +219 -80
- package/src/langchain/form-validating-tool-wrapper.ts +356 -0
- package/src/langchain/index.ts +1 -0
- package/src/langchain/langchain-agent.ts +24 -104
- package/src/services/formatters/format-converter-registry.ts +0 -5
package/dist/esm/index30.js
CHANGED
|
@@ -1,1282 +1,251 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
const meta = metadata;
|
|
22
|
-
if (!("hashLinkBlock" in meta) || !meta.hashLinkBlock || typeof meta.hashLinkBlock !== "object") {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
const block = meta.hashLinkBlock;
|
|
26
|
-
return "blockId" in block && "hashLink" in block && "template" in block && "attributes" in block && typeof block.blockId === "string" && typeof block.hashLink === "string" && typeof block.template === "string" && typeof block.attributes === "object";
|
|
27
|
-
}
|
|
28
|
-
class LangChainAgent extends BaseAgent {
|
|
29
|
-
constructor() {
|
|
30
|
-
super(...arguments);
|
|
31
|
-
this.systemMessage = "";
|
|
32
|
-
this.mcpConnectionStatus = /* @__PURE__ */ new Map();
|
|
33
|
-
}
|
|
34
|
-
addToolRawToMemory(name, payload) {
|
|
35
|
-
try {
|
|
36
|
-
const content = `[tool-raw:${name}] ${payload}`;
|
|
37
|
-
this.smartMemory.addMessage(new SystemMessage(content));
|
|
38
|
-
} catch {
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
persistToolRaw(toolName, output) {
|
|
42
|
-
try {
|
|
43
|
-
let payload = "";
|
|
44
|
-
if (typeof output === "string") {
|
|
45
|
-
payload = this.isJSON(output) ? output : JSON.stringify({ output });
|
|
46
|
-
} else if (output !== void 0) {
|
|
47
|
-
try {
|
|
48
|
-
payload = JSON.stringify(output);
|
|
49
|
-
} catch {
|
|
50
|
-
payload = String(output);
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
payload = JSON.stringify({ observation: null });
|
|
54
|
-
}
|
|
55
|
-
this.addToolRawToMemory(toolName, payload);
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
persistIntermediateSteps(steps) {
|
|
60
|
-
if (!steps || !Array.isArray(steps)) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
for (const step of steps) {
|
|
65
|
-
const name = step?.action?.tool || "unknown";
|
|
66
|
-
const obs = step?.observation;
|
|
67
|
-
this.persistToolRaw(name, obs);
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
1
|
+
import { StructuredTool } from "@langchain/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { Logger } from "@hashgraphonline/standards-sdk";
|
|
4
|
+
import { isFormValidatable } from "@hashgraphonline/standards-agent-kit";
|
|
5
|
+
class FormValidatingToolWrapper extends StructuredTool {
|
|
6
|
+
constructor(originalTool, formGenerator, config = {}) {
|
|
7
|
+
super();
|
|
8
|
+
this.originalTool = originalTool;
|
|
9
|
+
this.formGenerator = formGenerator;
|
|
10
|
+
this.validationConfig = config;
|
|
11
|
+
this.logger = new Logger({ module: "FormValidatingToolWrapper" });
|
|
12
|
+
this.name = originalTool.name;
|
|
13
|
+
this.description = originalTool.description;
|
|
14
|
+
this.schema = originalTool.schema;
|
|
15
|
+
this.logger.info(`🔧 FormValidatingToolWrapper created for tool: ${this.name}`, {
|
|
16
|
+
originalToolName: originalTool.name,
|
|
17
|
+
originalToolType: originalTool.constructor.name,
|
|
18
|
+
wrapperType: this.constructor.name
|
|
19
|
+
});
|
|
71
20
|
}
|
|
72
21
|
/**
|
|
73
|
-
*
|
|
22
|
+
* Validate the input against the schema
|
|
74
23
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const allTools = this.toolRegistry.getAllRegistryEntries();
|
|
89
|
-
for (const entry of allTools) {
|
|
90
|
-
const tool = entry.tool;
|
|
91
|
-
const name = tool.name.toLowerCase();
|
|
92
|
-
const desc = tool.description?.toLowerCase() || "";
|
|
93
|
-
if (name.includes("inscribe") || name.includes("hashinal") || desc.includes("inscribe") || desc.includes("hashinal")) {
|
|
94
|
-
return tool;
|
|
24
|
+
validateInput(input) {
|
|
25
|
+
try {
|
|
26
|
+
this.schema.parse(input);
|
|
27
|
+
return { isValid: true };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof z.ZodError) {
|
|
30
|
+
const errors = error.errors.filter((err) => {
|
|
31
|
+
const fieldName = err.path[0];
|
|
32
|
+
return !this.validationConfig.skipFields?.includes(fieldName);
|
|
33
|
+
}).map((err) => `${err.path.join(".")}: ${err.message}`);
|
|
34
|
+
return { isValid: false, errors };
|
|
95
35
|
}
|
|
36
|
+
return { isValid: false, errors: ["Validation failed"] };
|
|
96
37
|
}
|
|
97
|
-
return null;
|
|
98
38
|
}
|
|
99
39
|
/**
|
|
100
|
-
*
|
|
40
|
+
* Gets the shape keys from the schema if it's a ZodObject
|
|
101
41
|
*/
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
sessionId: `session-${Date.now()}`,
|
|
106
|
-
timestamp: Date.now()
|
|
107
|
-
};
|
|
108
|
-
const result = await this.executionPipeline.execute(
|
|
109
|
-
toolName,
|
|
110
|
-
parameters,
|
|
111
|
-
sessionContext
|
|
112
|
-
);
|
|
113
|
-
if (!result.success) {
|
|
114
|
-
throw new Error(result.error || "Pipeline execution failed");
|
|
115
|
-
}
|
|
116
|
-
return result.output;
|
|
117
|
-
}
|
|
118
|
-
const entry = this.toolRegistry.getTool(toolName);
|
|
119
|
-
if (!entry) {
|
|
120
|
-
throw new Error(`Tool not found: ${toolName}`);
|
|
42
|
+
getSchemaShape() {
|
|
43
|
+
if (this.isZodObject(this.schema)) {
|
|
44
|
+
return Object.keys(this.schema.shape);
|
|
121
45
|
}
|
|
122
|
-
|
|
123
|
-
if (this.pendingParameterPreprocessingCallback) {
|
|
124
|
-
this.logger.info(
|
|
125
|
-
"Applying parameter preprocessing in executeToolDirect",
|
|
126
|
-
{
|
|
127
|
-
toolName,
|
|
128
|
-
hasCallback: true,
|
|
129
|
-
parameterKeys: Object.keys(parameters)
|
|
130
|
-
}
|
|
131
|
-
);
|
|
132
|
-
try {
|
|
133
|
-
processedParameters = await this.pendingParameterPreprocessingCallback(
|
|
134
|
-
toolName,
|
|
135
|
-
parameters
|
|
136
|
-
);
|
|
137
|
-
if (JSON.stringify(processedParameters) !== JSON.stringify(parameters)) {
|
|
138
|
-
this.logger.info("Parameters preprocessed successfully", {
|
|
139
|
-
toolName,
|
|
140
|
-
originalKeys: Object.keys(parameters),
|
|
141
|
-
processedKeys: Object.keys(processedParameters),
|
|
142
|
-
changes: Object.keys(processedParameters).filter(
|
|
143
|
-
(key) => processedParameters[key] !== parameters[key]
|
|
144
|
-
)
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
this.logger.warn(
|
|
149
|
-
"Parameter preprocessing failed, using original parameters",
|
|
150
|
-
{
|
|
151
|
-
toolName,
|
|
152
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
processedParameters = parameters;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const mergedArgs = { ...processedParameters, renderForm: false };
|
|
159
|
-
if (entry.wrapper) {
|
|
160
|
-
const maybeWrapper = entry.tool;
|
|
161
|
-
if (maybeWrapper.originalTool?.call) {
|
|
162
|
-
return await maybeWrapper.originalTool.call(mergedArgs);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return await entry.tool.call(mergedArgs);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Create a standard ChatResponse from tool output
|
|
169
|
-
*/
|
|
170
|
-
createToolResponse(toolOutput) {
|
|
171
|
-
return {
|
|
172
|
-
output: toolOutput,
|
|
173
|
-
message: toolOutput,
|
|
174
|
-
notes: []
|
|
175
|
-
};
|
|
46
|
+
return [];
|
|
176
47
|
}
|
|
177
48
|
/**
|
|
178
|
-
*
|
|
49
|
+
* Executes the wrapped tool's original implementation directly, bypassing wrapper logic.
|
|
179
50
|
*/
|
|
180
|
-
async
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (message.includes("TOOL_EXECUTION")) {
|
|
185
|
-
const parsed = JSON.parse(message);
|
|
186
|
-
if (parsed.type === "TOOL_EXECUTION") {
|
|
187
|
-
isToolExecution = true;
|
|
188
|
-
toolExecutionData = parsed;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} catch {
|
|
51
|
+
async executeOriginal(input, runManager) {
|
|
52
|
+
const tool = this.originalTool;
|
|
53
|
+
if ("_call" in tool && typeof tool._call === "function") {
|
|
54
|
+
return tool._call(input, runManager);
|
|
192
55
|
}
|
|
193
|
-
if (
|
|
194
|
-
return
|
|
56
|
+
if ("call" in tool && typeof tool.call === "function") {
|
|
57
|
+
return tool.call(input, runManager);
|
|
195
58
|
}
|
|
196
|
-
|
|
197
|
-
const params = toolExecutionData.parameters || {};
|
|
198
|
-
const toolName = toolExecutionData.toolName;
|
|
199
|
-
if (toolName) {
|
|
200
|
-
const toolOutput = await this.executeToolDirect(toolName, params);
|
|
201
|
-
try {
|
|
202
|
-
const payload = this.isJSON(toolOutput) ? toolOutput : JSON.stringify({ output: toolOutput });
|
|
203
|
-
this.addToolRawToMemory(toolName, payload);
|
|
204
|
-
} catch {
|
|
205
|
-
}
|
|
206
|
-
return this.createToolResponse(toolOutput);
|
|
207
|
-
}
|
|
208
|
-
} catch {
|
|
209
|
-
}
|
|
210
|
-
const formSubmission = {
|
|
211
|
-
formId: toolExecutionData.formId,
|
|
212
|
-
toolName: toolExecutionData.toolName || "",
|
|
213
|
-
parameters: toolExecutionData.parameters || {},
|
|
214
|
-
timestamp: Date.now()
|
|
215
|
-
};
|
|
216
|
-
if (this.executor && "processFormSubmission" in this.executor && typeof this.executor.processFormSubmission === "function") {
|
|
217
|
-
return this.processFormSubmission(formSubmission, context);
|
|
218
|
-
}
|
|
219
|
-
return null;
|
|
59
|
+
throw new Error("Original tool has no callable implementation");
|
|
220
60
|
}
|
|
221
61
|
/**
|
|
222
|
-
*
|
|
62
|
+
* Provides access to the wrapped tool instance for executors that want to bypass the wrapper.
|
|
223
63
|
*/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
const toolLineMatch = message.match(/Tool:\s*(.+)/);
|
|
230
|
-
const argsLineIndex = message.indexOf("Arguments:");
|
|
231
|
-
if (toolLineMatch && argsLineIndex !== -1) {
|
|
232
|
-
const toolName = toolLineMatch[1].trim();
|
|
233
|
-
const argsText = message.slice(argsLineIndex + "Arguments:".length).trim();
|
|
234
|
-
let args = {};
|
|
235
|
-
try {
|
|
236
|
-
args = JSON.parse(argsText);
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
const toolOutput = await this.executeToolDirect(toolName, args);
|
|
240
|
-
try {
|
|
241
|
-
const payload = this.isJSON(toolOutput) ? toolOutput : JSON.stringify({ output: toolOutput });
|
|
242
|
-
this.addToolRawToMemory(toolName, payload);
|
|
243
|
-
} catch {
|
|
244
|
-
}
|
|
245
|
-
return this.createToolResponse(toolOutput);
|
|
246
|
-
}
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
|
-
return null;
|
|
64
|
+
getOriginalTool() {
|
|
65
|
+
return this.originalTool;
|
|
250
66
|
}
|
|
251
67
|
/**
|
|
252
|
-
*
|
|
68
|
+
* Checks if tool implements FormValidatable method
|
|
253
69
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
try {
|
|
259
|
-
const trimmed = message.trim();
|
|
260
|
-
if (!(trimmed.startsWith("{") && trimmed.endsWith("}")) && !(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
const obj = JSON.parse(trimmed);
|
|
264
|
-
const formId = obj["formId"];
|
|
265
|
-
const toolName = obj["toolName"] || "";
|
|
266
|
-
const parameters = obj["parameters"] || {};
|
|
267
|
-
if (formId && this.executor && "processFormSubmission" in this.executor && typeof this.executor.processFormSubmission === "function") {
|
|
268
|
-
return this.processFormSubmission(
|
|
269
|
-
{ formId, toolName, parameters, timestamp: Date.now() },
|
|
270
|
-
context
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
if (toolName) {
|
|
274
|
-
const toolOutput = await this.executeToolDirect(toolName, parameters);
|
|
275
|
-
try {
|
|
276
|
-
const payload = this.isJSON(toolOutput) ? toolOutput : JSON.stringify({ output: toolOutput });
|
|
277
|
-
this.addToolRawToMemory(toolName, payload);
|
|
278
|
-
} catch {
|
|
279
|
-
}
|
|
280
|
-
return this.createToolResponse(toolOutput);
|
|
281
|
-
}
|
|
282
|
-
} catch {
|
|
283
|
-
}
|
|
284
|
-
return null;
|
|
70
|
+
hasFormValidatableMethod(tool, methodName) {
|
|
71
|
+
return tool !== null && typeof tool === "object" && methodName in tool && typeof tool[methodName] === "function";
|
|
285
72
|
}
|
|
286
73
|
/**
|
|
287
|
-
*
|
|
74
|
+
* Expose FormValidatable methods by delegating to the underlying tool when available.
|
|
288
75
|
*/
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
return
|
|
292
|
-
}
|
|
293
|
-
try {
|
|
294
|
-
const tool = this.getInscriptionTool();
|
|
295
|
-
if (!tool) {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
const idMatch = message.match(/content-ref:([A-Za-z0-9_\-]+)/i) || message.match(/content-ref:([^\s)]+)/i);
|
|
299
|
-
const contentRef = idMatch && idMatch[1] ? `content-ref:${idMatch[1]}` : message.match(/content-ref:[^\s)]+/i)?.[0] || void 0;
|
|
300
|
-
const args = contentRef ? { contentRef, renderForm: true, withHashLinkBlocks: true } : { renderForm: true, withHashLinkBlocks: true };
|
|
301
|
-
const toolOutput = await tool.call(args);
|
|
302
|
-
let parsed;
|
|
303
|
-
try {
|
|
304
|
-
parsed = typeof toolOutput === "string" ? JSON.parse(toolOutput) : toolOutput;
|
|
305
|
-
} catch {
|
|
306
|
-
}
|
|
307
|
-
if (parsed && parsed["requiresForm"] && parsed["formMessage"]) {
|
|
308
|
-
const pending = /* @__PURE__ */ new Map();
|
|
309
|
-
const originalInput = {
|
|
310
|
-
input: message,
|
|
311
|
-
chat_history: this.smartMemory.getMessages()
|
|
312
|
-
};
|
|
313
|
-
const formMessage = parsed["formMessage"];
|
|
314
|
-
pending.set(formMessage.id, {
|
|
315
|
-
toolName: tool.name,
|
|
316
|
-
originalInput,
|
|
317
|
-
originalToolInput: args,
|
|
318
|
-
schema: null
|
|
319
|
-
});
|
|
320
|
-
const maybeRestore = this.executor;
|
|
321
|
-
if (typeof maybeRestore.restorePendingForms === "function") {
|
|
322
|
-
maybeRestore.restorePendingForms(pending);
|
|
323
|
-
}
|
|
324
|
-
const outputMsg = parsed["message"] || "Please complete the form to continue.";
|
|
325
|
-
return {
|
|
326
|
-
output: outputMsg,
|
|
327
|
-
message: outputMsg,
|
|
328
|
-
notes: [],
|
|
329
|
-
requiresForm: true,
|
|
330
|
-
formMessage
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
76
|
+
getFormSchema() {
|
|
77
|
+
if (this.hasFormValidatableMethod(this.originalTool, "getFormSchema")) {
|
|
78
|
+
return this.originalTool.getFormSchema();
|
|
334
79
|
}
|
|
335
|
-
return
|
|
80
|
+
return this.schema;
|
|
336
81
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
async processExecutorResult(result) {
|
|
341
|
-
let outputStr = "";
|
|
342
|
-
if (typeof result.output === "string") {
|
|
343
|
-
outputStr = result.output;
|
|
344
|
-
} else if (result.output) {
|
|
345
|
-
try {
|
|
346
|
-
outputStr = JSON.stringify(result.output);
|
|
347
|
-
} catch {
|
|
348
|
-
outputStr = String(result.output);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
let response = {
|
|
352
|
-
output: outputStr,
|
|
353
|
-
message: outputStr,
|
|
354
|
-
notes: [],
|
|
355
|
-
intermediateSteps: result.intermediateSteps
|
|
356
|
-
};
|
|
357
|
-
if (result.requiresForm && result.formMessage) {
|
|
358
|
-
response.formMessage = result.formMessage;
|
|
359
|
-
response.requiresForm = true;
|
|
360
|
-
}
|
|
361
|
-
if (result.intermediateSteps && Array.isArray(result.intermediateSteps)) {
|
|
362
|
-
const toolCalls = result.intermediateSteps.map(
|
|
363
|
-
(step, index) => ({
|
|
364
|
-
id: `call_${index}`,
|
|
365
|
-
name: step.action?.tool || "unknown",
|
|
366
|
-
args: step.action?.toolInput || {},
|
|
367
|
-
output: typeof step.observation === "string" ? step.observation : JSON.stringify(step.observation)
|
|
368
|
-
})
|
|
369
|
-
);
|
|
370
|
-
if (toolCalls.length > 0) {
|
|
371
|
-
response.tool_calls = toolCalls;
|
|
372
|
-
}
|
|
373
|
-
this.persistIntermediateSteps(
|
|
374
|
-
result.intermediateSteps
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
const parsedSteps = result?.intermediateSteps?.[0]?.observation;
|
|
378
|
-
if (parsedSteps && typeof parsedSteps === "string" && this.isJSON(parsedSteps)) {
|
|
379
|
-
try {
|
|
380
|
-
const parsed = JSON.parse(parsedSteps);
|
|
381
|
-
if (ResponseFormatter.isInscriptionResponse(parsed)) {
|
|
382
|
-
const formattedMessage = ResponseFormatter.formatInscriptionResponse(parsed);
|
|
383
|
-
response.output = formattedMessage;
|
|
384
|
-
response.message = formattedMessage;
|
|
385
|
-
if (parsed.inscription) {
|
|
386
|
-
response.inscription = parsed.inscription;
|
|
387
|
-
}
|
|
388
|
-
if (parsed.metadata) {
|
|
389
|
-
response.metadata = {
|
|
390
|
-
...response.metadata,
|
|
391
|
-
...parsed.metadata
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
} else {
|
|
395
|
-
response = { ...response, ...parsed };
|
|
396
|
-
}
|
|
397
|
-
const blockMetadata = this.processHashLinkBlocks(parsed);
|
|
398
|
-
if (blockMetadata.hashLinkBlock) {
|
|
399
|
-
response.metadata = {
|
|
400
|
-
...response.metadata,
|
|
401
|
-
...blockMetadata
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
} catch (error) {
|
|
405
|
-
this.logger.error("Error parsing intermediate steps:", error);
|
|
406
|
-
}
|
|
82
|
+
getEssentialFields() {
|
|
83
|
+
if (this.hasFormValidatableMethod(this.originalTool, "getEssentialFields")) {
|
|
84
|
+
return this.originalTool.getEssentialFields();
|
|
407
85
|
}
|
|
408
|
-
|
|
409
|
-
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
isFieldEmpty(fieldName, value) {
|
|
89
|
+
if (this.hasFormValidatableMethod(this.originalTool, "isFieldEmpty")) {
|
|
90
|
+
return this.originalTool.isFieldEmpty(fieldName, value);
|
|
410
91
|
}
|
|
411
|
-
if (
|
|
412
|
-
|
|
92
|
+
if (value === void 0 || value === null || value === "") {
|
|
93
|
+
return true;
|
|
413
94
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
if (tokenUsage) {
|
|
417
|
-
response.tokenUsage = tokenUsage;
|
|
418
|
-
response.cost = calculateTokenCostSync(tokenUsage);
|
|
419
|
-
}
|
|
95
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
96
|
+
return true;
|
|
420
97
|
}
|
|
421
|
-
|
|
422
|
-
response.metadata = {
|
|
423
|
-
...response.metadata,
|
|
424
|
-
memoryStats: {
|
|
425
|
-
activeMessages: finalMemoryStats.totalActiveMessages,
|
|
426
|
-
tokenUsage: finalMemoryStats.currentTokenCount,
|
|
427
|
-
maxTokens: finalMemoryStats.maxTokens,
|
|
428
|
-
usagePercentage: finalMemoryStats.usagePercentage
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
this.logger.info("LangChainAgent.chat returning response:", response);
|
|
432
|
-
return response;
|
|
98
|
+
return false;
|
|
433
99
|
}
|
|
434
100
|
/**
|
|
435
|
-
*
|
|
436
|
-
*/
|
|
437
|
-
/**
|
|
438
|
-
* Loads context messages into memory, merging with existing messages
|
|
101
|
+
* Calculates which fields are missing from the input
|
|
439
102
|
*/
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const existingMessages = this.smartMemory.getMessages();
|
|
445
|
-
const existingContent = new Set(
|
|
446
|
-
existingMessages.map((m) => `${m.constructor.name}:${m.content}`)
|
|
447
|
-
);
|
|
448
|
-
for (const msg of context.messages) {
|
|
449
|
-
let messageClass;
|
|
450
|
-
let content;
|
|
451
|
-
if (msg instanceof HumanMessage || msg instanceof AIMessage || msg instanceof SystemMessage) {
|
|
452
|
-
messageClass = msg.constructor;
|
|
453
|
-
content = msg.content;
|
|
454
|
-
} else if (msg && typeof msg === "object" && "content" in msg && "type" in msg) {
|
|
455
|
-
content = String(msg.content);
|
|
456
|
-
const type = String(msg.type);
|
|
457
|
-
if (type === "human") messageClass = HumanMessage;
|
|
458
|
-
else if (type === "ai") messageClass = AIMessage;
|
|
459
|
-
else if (type === "system") messageClass = SystemMessage;
|
|
460
|
-
else continue;
|
|
461
|
-
} else {
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
const key = `${messageClass.name}:${content}`;
|
|
465
|
-
if (!existingContent.has(key)) {
|
|
466
|
-
this.smartMemory.addMessage(new messageClass(content));
|
|
467
|
-
existingContent.add(key);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
async boot() {
|
|
472
|
-
this.logger.info("🚨🚨🚨 LANGCHAIN AGENT BOOT METHOD CALLED 🚨🚨🚨");
|
|
473
|
-
if (this.initialized) {
|
|
474
|
-
this.logger.warn("Agent already initialized");
|
|
475
|
-
return;
|
|
103
|
+
calculateMissingFields(input, isCustom) {
|
|
104
|
+
const missingFields = /* @__PURE__ */ new Set();
|
|
105
|
+
if (!isCustom) {
|
|
106
|
+
return missingFields;
|
|
476
107
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.toolRegistry = new ToolRegistry(this.logger);
|
|
483
|
-
const allTools = this.agentKit.getAggregatedLangChainTools();
|
|
484
|
-
this.logger.info("=== TOOL REGISTRATION START ===");
|
|
485
|
-
this.logger.info(
|
|
486
|
-
"All tools from agentKit:",
|
|
487
|
-
allTools.map((t) => t.name)
|
|
488
|
-
);
|
|
489
|
-
const filteredTools = this.filterTools(allTools);
|
|
490
|
-
this.logger.info(
|
|
491
|
-
"Filtered tools for registration:",
|
|
492
|
-
filteredTools.map((t) => t.name)
|
|
493
|
-
);
|
|
494
|
-
for (const tool of filteredTools) {
|
|
495
|
-
this.logger.info(`🔧 Registering tool: ${tool.name}`);
|
|
496
|
-
const options = {};
|
|
497
|
-
const name = tool.name.toLowerCase();
|
|
498
|
-
const desc = tool.description?.toLowerCase() || "";
|
|
499
|
-
if (tool.name === "hedera-hts-mint-nft") {
|
|
500
|
-
const originalCall = tool.call.bind(tool);
|
|
501
|
-
tool.call = async (args) => {
|
|
502
|
-
if (args.metaOptions && typeof args.metaOptions === "object") {
|
|
503
|
-
const metaOptions = args.metaOptions;
|
|
504
|
-
if (metaOptions.transactionMemo) {
|
|
505
|
-
console.warn(
|
|
506
|
-
"🚨 WORKAROUND: Stripping transactionMemo from hedera-hts-mint-nft to avoid bug",
|
|
507
|
-
{ originalMemo: metaOptions.transactionMemo }
|
|
508
|
-
);
|
|
509
|
-
delete metaOptions.transactionMemo;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
return originalCall(args);
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
if (name.includes("inscribe") || name.includes("hashinal") || desc.includes("inscribe") || desc.includes("hashinal")) {
|
|
516
|
-
options.forceWrapper = true;
|
|
517
|
-
options.metadata = {
|
|
518
|
-
category: "core",
|
|
519
|
-
version: "1.0.0",
|
|
520
|
-
dependencies: []
|
|
521
|
-
};
|
|
522
|
-
this.logger.info(`🎯 CRITICAL TOOL DEBUG - ${tool.name} schema:`, {
|
|
523
|
-
hasSchema: !!tool.schema,
|
|
524
|
-
schemaType: tool.schema?.constructor?.name,
|
|
525
|
-
hasRenderConfig: !!tool.schema?._renderConfig,
|
|
526
|
-
renderConfig: tool.schema?._renderConfig
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
this.toolRegistry.registerTool(tool, options);
|
|
530
|
-
}
|
|
531
|
-
this.tools = this.toolRegistry.getAllTools();
|
|
532
|
-
this.logger.info(`🚀 TOOLS REGISTERED: ${this.tools.length} tools`);
|
|
533
|
-
const stats = this.toolRegistry.getStatistics();
|
|
534
|
-
this.logger.info("📊 Tool Registry Statistics:", {
|
|
535
|
-
total: stats.totalTools,
|
|
536
|
-
wrapped: stats.wrappedTools,
|
|
537
|
-
unwrapped: stats.unwrappedTools,
|
|
538
|
-
categories: stats.categoryCounts,
|
|
539
|
-
priorities: stats.priorityCounts
|
|
540
|
-
});
|
|
541
|
-
const inscriptionTool = this.getInscriptionTool();
|
|
542
|
-
if (inscriptionTool) {
|
|
543
|
-
const entry = this.toolRegistry.getTool(inscriptionTool.name);
|
|
544
|
-
if (entry) {
|
|
545
|
-
this.logger.info(
|
|
546
|
-
`✅ Inscription tool registered: ${inscriptionTool.name}`
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
const toolNames = this.toolRegistry.getToolNames();
|
|
551
|
-
const uniqueNames = new Set(toolNames);
|
|
552
|
-
if (toolNames.length !== uniqueNames.size) {
|
|
553
|
-
this.logger.error("DUPLICATE TOOL NAMES DETECTED in registry!");
|
|
554
|
-
const duplicates = toolNames.filter(
|
|
555
|
-
(name, index) => toolNames.indexOf(name) !== index
|
|
556
|
-
);
|
|
557
|
-
throw new Error(
|
|
558
|
-
`Duplicate tool names detected: ${duplicates.join(", ")}`
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
if (this.config.mcp?.servers && this.config.mcp.servers.length > 0) {
|
|
562
|
-
if (this.config.mcp.autoConnect !== false) {
|
|
563
|
-
await this.initializeMCP();
|
|
564
|
-
} else {
|
|
565
|
-
this.logger.info(
|
|
566
|
-
"MCP servers configured but autoConnect=false, skipping synchronous connection"
|
|
567
|
-
);
|
|
568
|
-
this.mcpManager = new MCPClientManager(this.logger);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
this.smartMemory = new SmartMemoryManager({
|
|
572
|
-
modelName,
|
|
573
|
-
maxTokens: 9e4,
|
|
574
|
-
reserveTokens: 1e4,
|
|
575
|
-
storageLimit: 1e3
|
|
576
|
-
});
|
|
577
|
-
this.logger.info("SmartMemoryManager initialized:", {
|
|
578
|
-
modelName,
|
|
579
|
-
toolsCount: this.tools.length,
|
|
580
|
-
maxTokens: 9e4,
|
|
581
|
-
reserveTokens: 1e4
|
|
582
|
-
});
|
|
583
|
-
this.formEngine = new FormEngine(this.logger);
|
|
584
|
-
this.executionPipeline = new ExecutionPipeline(
|
|
585
|
-
this.toolRegistry,
|
|
586
|
-
this.formEngine,
|
|
587
|
-
this.smartMemory,
|
|
588
|
-
this.logger
|
|
589
|
-
);
|
|
590
|
-
this.systemMessage = this.buildSystemPrompt();
|
|
591
|
-
this.smartMemory.setSystemPrompt(this.systemMessage);
|
|
592
|
-
await this.createExecutor();
|
|
593
|
-
this.initialized = true;
|
|
594
|
-
this.logger.info("LangChain Hedera agent initialized with ToolRegistry");
|
|
595
|
-
} catch (error) {
|
|
596
|
-
this.logger.error("Failed to initialize agent:", error);
|
|
597
|
-
throw error;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
async chat(message, context) {
|
|
601
|
-
if (!this.initialized || !this.executor || !this.smartMemory) {
|
|
602
|
-
throw new Error("Agent not initialized. Call boot() first.");
|
|
603
|
-
}
|
|
604
|
-
try {
|
|
605
|
-
const toolExecutionResult = await this.handleToolExecution(
|
|
606
|
-
message,
|
|
607
|
-
context
|
|
608
|
-
);
|
|
609
|
-
if (toolExecutionResult) {
|
|
610
|
-
return toolExecutionResult;
|
|
108
|
+
const essentialFields = this.getEssentialFields();
|
|
109
|
+
for (const fieldName of essentialFields) {
|
|
110
|
+
const value = input[fieldName];
|
|
111
|
+
if (this.isFieldEmpty(fieldName, value)) {
|
|
112
|
+
missingFields.add(fieldName);
|
|
611
113
|
}
|
|
612
|
-
const directToolResult = await this.handleDirectToolExecution(message);
|
|
613
|
-
if (directToolResult) {
|
|
614
|
-
return directToolResult;
|
|
615
|
-
}
|
|
616
|
-
const jsonToolResult = await this.handleJsonToolCalls(message, context);
|
|
617
|
-
if (jsonToolResult) {
|
|
618
|
-
return jsonToolResult;
|
|
619
|
-
}
|
|
620
|
-
const contentRefResult = await this.handleContentRefMessages(message);
|
|
621
|
-
if (contentRefResult) {
|
|
622
|
-
return contentRefResult;
|
|
623
|
-
}
|
|
624
|
-
this.logger.info("LangChainAgent.chat called with:", {
|
|
625
|
-
message,
|
|
626
|
-
contextLength: context?.messages?.length || 0
|
|
627
|
-
});
|
|
628
|
-
this.loadContextMessages(context);
|
|
629
|
-
this.smartMemory.addMessage(new HumanMessage(message));
|
|
630
|
-
const memoryStats = this.smartMemory.getMemoryStats();
|
|
631
|
-
this.logger.info("Memory stats before execution:", {
|
|
632
|
-
totalMessages: memoryStats.totalActiveMessages,
|
|
633
|
-
currentTokens: memoryStats.currentTokenCount,
|
|
634
|
-
maxTokens: memoryStats.maxTokens,
|
|
635
|
-
usagePercentage: memoryStats.usagePercentage,
|
|
636
|
-
toolsCount: this.tools.length
|
|
637
|
-
});
|
|
638
|
-
const currentMessages = this.smartMemory.getMessages();
|
|
639
|
-
this.logger.info("Current messages in memory:", {
|
|
640
|
-
count: currentMessages.length
|
|
641
|
-
});
|
|
642
|
-
try {
|
|
643
|
-
const instr = currentMessages.map((m) => String(m.content || "")).filter(
|
|
644
|
-
(c) => typeof c === "string" && (c.includes("[instruction:") || c.includes("[tool-next-steps:"))
|
|
645
|
-
);
|
|
646
|
-
if (instr.length > 0) {
|
|
647
|
-
this.logger.info("Instruction/next-steps messages in memory:", {
|
|
648
|
-
messages: instr
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
} catch {
|
|
652
|
-
}
|
|
653
|
-
const result = await this.executor.invoke({
|
|
654
|
-
input: message,
|
|
655
|
-
chat_history: currentMessages
|
|
656
|
-
});
|
|
657
|
-
this.logger.info("LangChainAgent executor result:", result);
|
|
658
|
-
return this.processExecutorResult(result);
|
|
659
|
-
} catch (error) {
|
|
660
|
-
this.logger.error("LangChainAgent.chat error:", error);
|
|
661
|
-
return this.handleError(error);
|
|
662
114
|
}
|
|
663
|
-
|
|
664
|
-
async shutdown() {
|
|
665
|
-
if (this.mcpManager) {
|
|
666
|
-
await this.mcpManager.disconnectAll();
|
|
667
|
-
}
|
|
668
|
-
if (this.smartMemory) {
|
|
669
|
-
this.smartMemory.dispose();
|
|
670
|
-
this.smartMemory = void 0;
|
|
671
|
-
}
|
|
672
|
-
if (this.toolRegistry) {
|
|
673
|
-
this.toolRegistry.clear();
|
|
674
|
-
}
|
|
675
|
-
this.executor = void 0;
|
|
676
|
-
this.agentKit = void 0;
|
|
677
|
-
this.tools = [];
|
|
678
|
-
this.initialized = false;
|
|
679
|
-
this.logger.info("Agent cleaned up");
|
|
680
|
-
}
|
|
681
|
-
switchMode(mode) {
|
|
682
|
-
if (this.config.execution) {
|
|
683
|
-
this.config.execution.operationalMode = mode;
|
|
684
|
-
} else {
|
|
685
|
-
this.config.execution = { operationalMode: mode };
|
|
686
|
-
}
|
|
687
|
-
if (this.agentKit) {
|
|
688
|
-
this.agentKit.operationalMode = mode;
|
|
689
|
-
}
|
|
690
|
-
this.systemMessage = this.buildSystemPrompt();
|
|
691
|
-
this.logger.info(`Operational mode switched to: ${mode}`);
|
|
692
|
-
}
|
|
693
|
-
getUsageStats() {
|
|
694
|
-
if (!this.tokenTracker) {
|
|
695
|
-
return {
|
|
696
|
-
promptTokens: 0,
|
|
697
|
-
completionTokens: 0,
|
|
698
|
-
totalTokens: 0,
|
|
699
|
-
cost: { totalCost: 0 }
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
const usage = this.tokenTracker.getTotalTokenUsage();
|
|
703
|
-
const cost = calculateTokenCostSync(usage);
|
|
704
|
-
return { ...usage, cost };
|
|
705
|
-
}
|
|
706
|
-
getUsageLog() {
|
|
707
|
-
if (!this.tokenTracker) {
|
|
708
|
-
return [];
|
|
709
|
-
}
|
|
710
|
-
return this.tokenTracker.getTokenUsageHistory().map((usage) => ({
|
|
711
|
-
...usage,
|
|
712
|
-
cost: calculateTokenCostSync(usage)
|
|
713
|
-
}));
|
|
714
|
-
}
|
|
715
|
-
clearUsageStats() {
|
|
716
|
-
if (this.tokenTracker) {
|
|
717
|
-
this.tokenTracker.reset();
|
|
718
|
-
this.logger.info("Usage statistics cleared");
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
getMCPConnectionStatus() {
|
|
722
|
-
return new Map(this.mcpConnectionStatus);
|
|
115
|
+
return missingFields;
|
|
723
116
|
}
|
|
724
117
|
/**
|
|
725
|
-
*
|
|
118
|
+
* Creates a form message with optional JSON schema
|
|
726
119
|
*/
|
|
727
|
-
async
|
|
728
|
-
this.
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
"🔥 LangChainAgent.processFormSubmission - After initialization check"
|
|
120
|
+
async createFormMessage(schema, input, missingFields) {
|
|
121
|
+
let formMessage = await this.formGenerator.generateFormFromSchema(
|
|
122
|
+
schema,
|
|
123
|
+
input,
|
|
124
|
+
{
|
|
125
|
+
toolName: this.name,
|
|
126
|
+
toolDescription: this.description
|
|
127
|
+
},
|
|
128
|
+
missingFields
|
|
737
129
|
);
|
|
738
|
-
|
|
739
|
-
this.logger.info(
|
|
740
|
-
"🔥 LangChainAgent.processFormSubmission - About to log submission info"
|
|
741
|
-
);
|
|
742
|
-
this.logger.info("Processing form submission:", {
|
|
743
|
-
formId: submission.formId,
|
|
744
|
-
toolName: submission.toolName,
|
|
745
|
-
parameterKeys: Object.keys(submission.parameters || {}),
|
|
746
|
-
hasParameters: !!submission.parameters,
|
|
747
|
-
parametersType: typeof submission.parameters,
|
|
748
|
-
parametersIsNull: submission.parameters === null,
|
|
749
|
-
parametersIsUndefined: submission.parameters === void 0,
|
|
750
|
-
hasContext: !!submission.context
|
|
751
|
-
});
|
|
752
|
-
this.logger.info(
|
|
753
|
-
"🔥 LangChainAgent.processFormSubmission - After submission info logged"
|
|
754
|
-
);
|
|
755
|
-
if (!submission.parameters || typeof submission.parameters !== "object") {
|
|
756
|
-
this.logger.error("Invalid form submission parameters:", {
|
|
757
|
-
parameters: submission.parameters,
|
|
758
|
-
type: typeof submission.parameters
|
|
759
|
-
});
|
|
760
|
-
const errorInfo = JSON.stringify(submission, null, 2);
|
|
761
|
-
return this.handleError(
|
|
762
|
-
new Error(`Invalid form submission parameters: ${errorInfo}`)
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
this.logger.info(
|
|
766
|
-
"🔥 LangChainAgent.processFormSubmission - Parameters validated"
|
|
767
|
-
);
|
|
768
|
-
this.loadContextMessages(context);
|
|
769
|
-
this.logger.info(
|
|
770
|
-
"🔥 LangChainAgent.processFormSubmission - Context loaded"
|
|
771
|
-
);
|
|
130
|
+
if (this.isZodObject(schema)) {
|
|
772
131
|
try {
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
parameters: submission.parameters || {}
|
|
783
|
-
};
|
|
784
|
-
this.logger.info(
|
|
785
|
-
"🔥 LangChainAgent.processFormSubmission - Safe submission created"
|
|
786
|
-
);
|
|
787
|
-
this.logger.info("About to call executor.processFormSubmission with:", {
|
|
788
|
-
formId: safeSubmission.formId,
|
|
789
|
-
toolName: safeSubmission.toolName,
|
|
790
|
-
parameterKeys: Object.keys(safeSubmission.parameters),
|
|
791
|
-
parameterCount: Object.keys(safeSubmission.parameters).length
|
|
792
|
-
});
|
|
793
|
-
const result = await this.executor.processFormSubmission(safeSubmission);
|
|
794
|
-
this.logger.info("🔍 DEBUG: Raw result from FormAwareAgentExecutor:", {
|
|
795
|
-
hasResult: !!result,
|
|
796
|
-
resultKeys: result ? Object.keys(result) : [],
|
|
797
|
-
hasMetadata: !!result?.metadata,
|
|
798
|
-
metadataKeys: result?.metadata ? Object.keys(result.metadata) : [],
|
|
799
|
-
hasHashLinkBlock: hasHashLinkBlock(result?.metadata),
|
|
800
|
-
hashLinkBlockContent: hasHashLinkBlock(result?.metadata) ? result.metadata.hashLinkBlock : void 0
|
|
801
|
-
});
|
|
802
|
-
if (result?.metadata) {
|
|
803
|
-
this.logger.info(
|
|
804
|
-
"🔍 DEBUG: Full metadata from executor:",
|
|
805
|
-
JSON.stringify(result.metadata)
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
const preservedMetadata = result?.metadata ? { ...result.metadata } : {};
|
|
809
|
-
this.logger.info("Executor processFormSubmission result:", {
|
|
810
|
-
hasResult: !!result,
|
|
811
|
-
hasOutput: !!result.output,
|
|
812
|
-
hasError: !!result.error,
|
|
813
|
-
hasMetadata: !!result.metadata,
|
|
814
|
-
outputType: typeof result.output
|
|
815
|
-
});
|
|
816
|
-
try {
|
|
817
|
-
const maybeRaw = result.rawToolOutput;
|
|
818
|
-
const toolName = result.toolName || "unknown";
|
|
819
|
-
if (typeof maybeRaw === "string" && maybeRaw.trim().length > 0) {
|
|
820
|
-
const payload = this.isJSON(maybeRaw) ? maybeRaw : JSON.stringify({ output: maybeRaw });
|
|
821
|
-
this.addToolRawToMemory(toolName, payload);
|
|
822
|
-
}
|
|
823
|
-
} catch {
|
|
824
|
-
}
|
|
825
|
-
let outputMessage = "Form processed successfully.";
|
|
826
|
-
if (typeof result.output === "string") {
|
|
827
|
-
outputMessage = result.output;
|
|
828
|
-
} else if (result.output) {
|
|
829
|
-
try {
|
|
830
|
-
outputMessage = JSON.stringify(result.output);
|
|
831
|
-
} catch {
|
|
832
|
-
outputMessage = String(result.output);
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
let response = {
|
|
836
|
-
output: outputMessage,
|
|
837
|
-
message: outputMessage,
|
|
838
|
-
notes: [],
|
|
839
|
-
intermediateSteps: result.intermediateSteps
|
|
840
|
-
};
|
|
841
|
-
if (result.metadata) {
|
|
842
|
-
response.metadata = {
|
|
843
|
-
...response.metadata,
|
|
844
|
-
...result.metadata
|
|
132
|
+
const { jsonSchema, uiSchema } = this.formGenerator.generateJsonSchemaForm(
|
|
133
|
+
schema,
|
|
134
|
+
input,
|
|
135
|
+
missingFields
|
|
136
|
+
);
|
|
137
|
+
formMessage = {
|
|
138
|
+
...formMessage,
|
|
139
|
+
jsonSchema,
|
|
140
|
+
uiSchema
|
|
845
141
|
};
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
metadataKeys: response.metadata ? Object.keys(response.metadata) : [],
|
|
849
|
-
hasHashLinkBlock: hasHashLinkBlock(response.metadata),
|
|
850
|
-
hashLinkBlockContent: hasHashLinkBlock(response.metadata) ? response.metadata.hashLinkBlock : void 0
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
if (result.requiresForm && result.formMessage) {
|
|
854
|
-
response.formMessage = result.formMessage;
|
|
855
|
-
response.requiresForm = true;
|
|
856
|
-
}
|
|
857
|
-
if (result.intermediateSteps && Array.isArray(result.intermediateSteps)) {
|
|
858
|
-
const toolCalls = result.intermediateSteps.map(
|
|
859
|
-
(step, index) => {
|
|
860
|
-
const name = step?.action?.tool || "unknown";
|
|
861
|
-
const args = step?.action?.toolInput || {};
|
|
862
|
-
const obs = step?.observation;
|
|
863
|
-
let output = "";
|
|
864
|
-
if (typeof obs === "string") {
|
|
865
|
-
output = obs;
|
|
866
|
-
} else if (obs && typeof obs === "object") {
|
|
867
|
-
try {
|
|
868
|
-
output = JSON.stringify(obs);
|
|
869
|
-
} catch {
|
|
870
|
-
output = String(obs);
|
|
871
|
-
}
|
|
872
|
-
} else if (obs !== void 0) {
|
|
873
|
-
output = String(obs);
|
|
874
|
-
}
|
|
875
|
-
return { id: `call_${index}`, name, args, output };
|
|
876
|
-
}
|
|
877
|
-
);
|
|
878
|
-
if (toolCalls.length > 0) {
|
|
879
|
-
response.tool_calls = toolCalls;
|
|
880
|
-
}
|
|
881
|
-
this.persistIntermediateSteps(
|
|
882
|
-
result.intermediateSteps
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
const parsedSteps = result?.intermediateSteps?.[0]?.observation;
|
|
886
|
-
if (parsedSteps && typeof parsedSteps === "string" && this.isJSON(parsedSteps)) {
|
|
887
|
-
try {
|
|
888
|
-
const parsed = JSON.parse(parsedSteps);
|
|
889
|
-
response = { ...response, ...parsed };
|
|
890
|
-
const blockMetadata = this.processHashLinkBlocks(parsed);
|
|
891
|
-
if (blockMetadata.hashLinkBlock) {
|
|
892
|
-
response.metadata = {
|
|
893
|
-
...response.metadata,
|
|
894
|
-
...blockMetadata
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
} catch (error) {
|
|
898
|
-
this.logger.error("Error parsing intermediate steps:", error);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
if (response.output) {
|
|
902
|
-
this.smartMemory.addMessage(new AIMessage(response.output));
|
|
903
|
-
}
|
|
904
|
-
if (this.tokenTracker) {
|
|
905
|
-
const tokenUsage = this.tokenTracker.getLatestTokenUsage();
|
|
906
|
-
if (tokenUsage) {
|
|
907
|
-
response.tokenUsage = tokenUsage;
|
|
908
|
-
response.cost = calculateTokenCostSync(tokenUsage);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
const finalMemoryStats = this.smartMemory.getMemoryStats();
|
|
912
|
-
this.logger.info("🔍 DEBUG: Metadata before memoryStats merge:", {
|
|
913
|
-
hasMetadata: !!response.metadata,
|
|
914
|
-
metadataKeys: response.metadata ? Object.keys(response.metadata) : [],
|
|
915
|
-
hasHashLinkBlock: hasHashLinkBlock(response.metadata)
|
|
916
|
-
});
|
|
917
|
-
response.metadata = {
|
|
918
|
-
...preservedMetadata,
|
|
919
|
-
...response.metadata,
|
|
920
|
-
memoryStats: {
|
|
921
|
-
activeMessages: finalMemoryStats.totalActiveMessages,
|
|
922
|
-
tokenUsage: finalMemoryStats.currentTokenCount,
|
|
923
|
-
maxTokens: finalMemoryStats.maxTokens,
|
|
924
|
-
usagePercentage: finalMemoryStats.usagePercentage
|
|
925
|
-
}
|
|
926
|
-
};
|
|
927
|
-
this.logger.info("🔍 DEBUG: Final response metadata before return:", {
|
|
928
|
-
hasMetadata: !!response.metadata,
|
|
929
|
-
metadataKeys: response.metadata ? Object.keys(response.metadata) : [],
|
|
930
|
-
hasHashLinkBlock: hasHashLinkBlock(response.metadata),
|
|
931
|
-
fullMetadata: response.metadata
|
|
932
|
-
});
|
|
933
|
-
if (hasHashLinkBlock(preservedMetadata) && !hasHashLinkBlock(response.metadata)) {
|
|
934
|
-
this.logger.error(
|
|
935
|
-
"❌ CRITICAL: HashLink metadata was lost during processing!"
|
|
936
|
-
);
|
|
937
|
-
this.logger.error(
|
|
938
|
-
"Original metadata had hashLinkBlock:",
|
|
939
|
-
preservedMetadata.hashLinkBlock
|
|
940
|
-
);
|
|
941
|
-
this.logger.error("Final metadata missing hashLinkBlock");
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.logger.warn("Failed to generate JSON Schema for RJSF:", error);
|
|
942
144
|
}
|
|
943
|
-
return response;
|
|
944
|
-
} catch (error) {
|
|
945
|
-
this.logger.error("Form submission processing error:", error);
|
|
946
|
-
return this.handleError(error);
|
|
947
145
|
}
|
|
146
|
+
formMessage.partialInput = input;
|
|
147
|
+
return formMessage;
|
|
948
148
|
}
|
|
949
149
|
/**
|
|
950
|
-
*
|
|
150
|
+
* Type guard to check if a schema is a ZodObject
|
|
951
151
|
*/
|
|
952
|
-
|
|
953
|
-
|
|
152
|
+
isZodObject(schema) {
|
|
153
|
+
const def = schema._def;
|
|
154
|
+
return !!(def && def.typeName === "ZodObject");
|
|
954
155
|
}
|
|
955
156
|
/**
|
|
956
|
-
*
|
|
157
|
+
* Check if we should generate a form for this tool invocation
|
|
957
158
|
*/
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const corePlugins = getAllHederaCorePlugins();
|
|
963
|
-
const extensionPlugins = this.config.extensions?.plugins || [];
|
|
964
|
-
const plugins = [...corePlugins, ...extensionPlugins];
|
|
965
|
-
const operationalMode = this.config.execution?.operationalMode || "returnBytes";
|
|
966
|
-
const modelName = this.config.ai?.modelName || "gpt-4o";
|
|
967
|
-
return new HederaAgentKit(
|
|
968
|
-
this.config.signer,
|
|
969
|
-
{ plugins },
|
|
970
|
-
operationalMode,
|
|
971
|
-
this.config.execution?.userAccountId,
|
|
972
|
-
this.config.execution?.scheduleUserTransactionsInBytesMode ?? false,
|
|
973
|
-
void 0,
|
|
974
|
-
modelName,
|
|
975
|
-
this.config.extensions?.mirrorConfig,
|
|
976
|
-
this.config.debug?.silent ?? false
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
async createExecutor() {
|
|
980
|
-
const existingPendingForms = this.executor?.getPendingForms() || /* @__PURE__ */ new Map();
|
|
981
|
-
let llm;
|
|
982
|
-
if (this.config.ai?.provider && this.config.ai.provider.getModel) {
|
|
983
|
-
llm = this.config.ai.provider.getModel();
|
|
984
|
-
} else if (this.config.ai?.llm) {
|
|
985
|
-
llm = this.config.ai.llm;
|
|
986
|
-
} else {
|
|
987
|
-
const apiKey = this.config.ai?.apiKey || process.env.OPENAI_API_KEY;
|
|
988
|
-
if (!apiKey) {
|
|
989
|
-
throw new Error("OpenAI API key required");
|
|
990
|
-
}
|
|
991
|
-
const modelName = this.config.ai?.modelName || "gpt-4o-mini";
|
|
992
|
-
const isGPT5Model = modelName.toLowerCase().includes("gpt-5") || modelName.toLowerCase().includes("gpt5");
|
|
993
|
-
llm = new ChatOpenAI({
|
|
994
|
-
apiKey,
|
|
995
|
-
modelName,
|
|
996
|
-
callbacks: this.tokenTracker ? [this.tokenTracker] : [],
|
|
997
|
-
...isGPT5Model ? { temperature: 1 } : {}
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
const prompt = ChatPromptTemplate.fromMessages([
|
|
1001
|
-
["system", this.systemMessage],
|
|
1002
|
-
new MessagesPlaceholder("chat_history"),
|
|
1003
|
-
["human", "{input}"],
|
|
1004
|
-
new MessagesPlaceholder("agent_scratchpad")
|
|
1005
|
-
]);
|
|
1006
|
-
const langchainTools = this.tools;
|
|
1007
|
-
const inscriptionTool = this.getInscriptionTool();
|
|
1008
|
-
if (inscriptionTool) {
|
|
1009
|
-
const entry = this.toolRegistry.getTool(inscriptionTool.name);
|
|
1010
|
-
if (entry) {
|
|
1011
|
-
this.logger.info(
|
|
1012
|
-
`✅ Inscription tool registered: ${inscriptionTool.name}`
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
const stats = this.toolRegistry.getStatistics();
|
|
1017
|
-
this.logger.info("🛡️ TOOL SECURITY REPORT:", {
|
|
1018
|
-
totalTools: stats.totalTools,
|
|
1019
|
-
wrappedTools: stats.wrappedTools,
|
|
1020
|
-
unwrappedTools: stats.unwrappedTools,
|
|
1021
|
-
categories: stats.categoryCounts,
|
|
1022
|
-
priorities: stats.priorityCounts
|
|
159
|
+
shouldGenerateForm(input) {
|
|
160
|
+
this.logger.info(`shouldGenerateForm called for ${this.name}/${this.originalTool.name}`, {
|
|
161
|
+
input,
|
|
162
|
+
hasCustomValidation: !!this.validationConfig.customValidation
|
|
1023
163
|
});
|
|
1024
|
-
this.
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
164
|
+
if (this.validationConfig.customValidation) {
|
|
165
|
+
const result = !this.validationConfig.customValidation(input);
|
|
166
|
+
this.logger.info(`Custom validation result: ${result}`);
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
if (isFormValidatable(this.originalTool)) {
|
|
170
|
+
this.logger.info(`Tool ${this.originalTool.name} implements FormValidatable, using custom logic`);
|
|
171
|
+
return this.originalTool.shouldGenerateForm(input);
|
|
172
|
+
}
|
|
173
|
+
this.logger.info(`Tool ${this.originalTool.name} using schema validation only`);
|
|
174
|
+
const validation = this.validateInput(input);
|
|
175
|
+
this.logger.info(`Schema validation for ${this.originalTool.name}:`, {
|
|
176
|
+
isValid: validation.isValid,
|
|
177
|
+
errors: validation.errors
|
|
1037
178
|
});
|
|
1038
|
-
|
|
1039
|
-
this.executor.setParameterPreprocessingCallback(
|
|
1040
|
-
this.pendingParameterPreprocessingCallback
|
|
1041
|
-
);
|
|
1042
|
-
this.logger.info(
|
|
1043
|
-
"Parameter preprocessing callback re-applied to new executor",
|
|
1044
|
-
{ hasCallback: true }
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
if (existingPendingForms.size > 0) {
|
|
1048
|
-
this.logger.info(
|
|
1049
|
-
`Restoring ${existingPendingForms.size} pending forms to new executor`
|
|
1050
|
-
);
|
|
1051
|
-
this.executor.restorePendingForms(existingPendingForms);
|
|
1052
|
-
}
|
|
1053
|
-
this.logger.info("FormAwareAgentExecutor initialization complete");
|
|
179
|
+
return !validation.isValid;
|
|
1054
180
|
}
|
|
1055
181
|
/**
|
|
1056
|
-
*
|
|
182
|
+
* Checks if input has bypass flags that skip form generation
|
|
1057
183
|
*/
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
if (this.executor) {
|
|
1061
|
-
this.executor.setParameterPreprocessingCallback(callback);
|
|
1062
|
-
this.logger.info("Parameter preprocessing callback configured", {
|
|
1063
|
-
hasCallback: !!callback
|
|
1064
|
-
});
|
|
1065
|
-
} else {
|
|
1066
|
-
this.logger.warn(
|
|
1067
|
-
"Cannot set parameter preprocessing callback: executor not initialized"
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
handleError(error) {
|
|
1072
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1073
|
-
this.logger.error("Chat error:", error);
|
|
1074
|
-
let tokenUsage;
|
|
1075
|
-
let cost;
|
|
1076
|
-
if (this.tokenTracker) {
|
|
1077
|
-
tokenUsage = this.tokenTracker.getLatestTokenUsage();
|
|
1078
|
-
if (tokenUsage) {
|
|
1079
|
-
cost = calculateTokenCostSync(tokenUsage);
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
let userFriendlyMessage = errorMessage;
|
|
1083
|
-
let userFriendlyOutput = errorMessage;
|
|
1084
|
-
if (errorMessage.includes("429")) {
|
|
1085
|
-
if (errorMessage.includes("quota")) {
|
|
1086
|
-
userFriendlyMessage = "API quota exceeded. Please check your OpenAI billing and usage limits.";
|
|
1087
|
-
userFriendlyOutput = "I'm currently unable to respond because the API quota has been exceeded. Please check your OpenAI account billing and usage limits, then try again.";
|
|
1088
|
-
} else {
|
|
1089
|
-
userFriendlyMessage = ERROR_MESSAGES.TOO_MANY_REQUESTS;
|
|
1090
|
-
userFriendlyOutput = ERROR_MESSAGES.RATE_LIMITED;
|
|
1091
|
-
}
|
|
1092
|
-
} else if (errorMessage.includes("401") || errorMessage.includes("unauthorized")) {
|
|
1093
|
-
userFriendlyMessage = "API authentication failed. Please check your API key configuration.";
|
|
1094
|
-
userFriendlyOutput = "There's an issue with the API authentication. Please check your OpenAI API key configuration in settings.";
|
|
1095
|
-
} else if (errorMessage.includes("timeout")) {
|
|
1096
|
-
userFriendlyMessage = "Request timed out. Please try again.";
|
|
1097
|
-
userFriendlyOutput = "The request took too long to process. Please try again.";
|
|
1098
|
-
} else if (errorMessage.includes("network") || errorMessage.includes("fetch")) {
|
|
1099
|
-
userFriendlyMessage = "Network error. Please check your internet connection and try again.";
|
|
1100
|
-
userFriendlyOutput = "There was a network error. Please check your internet connection and try again.";
|
|
1101
|
-
} else if (errorMessage.includes("400")) {
|
|
1102
|
-
userFriendlyMessage = errorMessage;
|
|
1103
|
-
userFriendlyOutput = errorMessage;
|
|
1104
|
-
}
|
|
1105
|
-
const errorResponse = {
|
|
1106
|
-
output: userFriendlyOutput,
|
|
1107
|
-
message: userFriendlyMessage,
|
|
1108
|
-
error: errorMessage,
|
|
1109
|
-
notes: []
|
|
1110
|
-
};
|
|
1111
|
-
if (tokenUsage) {
|
|
1112
|
-
errorResponse.tokenUsage = tokenUsage;
|
|
1113
|
-
}
|
|
1114
|
-
if (cost) {
|
|
1115
|
-
errorResponse.cost = cost;
|
|
1116
|
-
}
|
|
1117
|
-
return errorResponse;
|
|
1118
|
-
}
|
|
1119
|
-
async initializeMCP() {
|
|
1120
|
-
this.mcpManager = new MCPClientManager(this.logger);
|
|
1121
|
-
for (const serverConfig of this.config.mcp.servers) {
|
|
1122
|
-
if (serverConfig.autoConnect === false) {
|
|
1123
|
-
this.logger.info(
|
|
1124
|
-
`Skipping MCP server ${serverConfig.name} (autoConnect=false)`
|
|
1125
|
-
);
|
|
1126
|
-
continue;
|
|
1127
|
-
}
|
|
1128
|
-
const status = await this.mcpManager.connectServer(serverConfig);
|
|
1129
|
-
if (status.connected) {
|
|
1130
|
-
this.logger.info(
|
|
1131
|
-
`Connected to MCP server ${status.serverName} with ${status.tools.length} tools`
|
|
1132
|
-
);
|
|
1133
|
-
for (const mcpTool of status.tools) {
|
|
1134
|
-
const langchainTool = convertMCPToolToLangChain(
|
|
1135
|
-
mcpTool,
|
|
1136
|
-
this.mcpManager,
|
|
1137
|
-
serverConfig
|
|
1138
|
-
);
|
|
1139
|
-
this.toolRegistry.registerTool(langchainTool, {
|
|
1140
|
-
metadata: {
|
|
1141
|
-
category: "mcp",
|
|
1142
|
-
version: "1.0.0",
|
|
1143
|
-
dependencies: [serverConfig.name]
|
|
1144
|
-
}
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
this.tools = this.toolRegistry.getAllTools();
|
|
1148
|
-
} else {
|
|
1149
|
-
this.logger.error(
|
|
1150
|
-
`Failed to connect to MCP server ${status.serverName}: ${status.error}`
|
|
1151
|
-
);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
184
|
+
hasFormBypassFlags(input) {
|
|
185
|
+
return input.__fromForm === true || input.renderForm === false;
|
|
1154
186
|
}
|
|
1155
187
|
/**
|
|
1156
|
-
*
|
|
188
|
+
* Override _call to intercept tool execution
|
|
1157
189
|
*/
|
|
1158
|
-
async
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
}
|
|
1165
|
-
this.logger.info(
|
|
1166
|
-
`Starting background MCP server connections for ${this.config.mcp.servers.length} servers...`
|
|
1167
|
-
);
|
|
1168
|
-
this.config.mcp.servers.forEach((serverConfig) => {
|
|
1169
|
-
this.connectServerInBackground(serverConfig);
|
|
190
|
+
async _call(input, runManager) {
|
|
191
|
+
this.logger.info(`🚨🚨🚨 FormValidatingToolWrapper._call INTERCEPTING ${this.name} 🚨🚨🚨`, {
|
|
192
|
+
input,
|
|
193
|
+
inputKeys: Object.keys(input),
|
|
194
|
+
schemaShape: this.getSchemaShape(),
|
|
195
|
+
stackTrace: new Error().stack?.split("\n").slice(0, 5)
|
|
1170
196
|
});
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
197
|
+
const inputRecord = input;
|
|
198
|
+
if (this.hasFormBypassFlags(inputRecord)) {
|
|
199
|
+
this.logger.info("Bypassing form generation and executing original tool due to submission flags");
|
|
200
|
+
return this.executeOriginal(inputRecord, runManager);
|
|
201
|
+
}
|
|
202
|
+
const shouldGenerate = this.shouldGenerateForm(input);
|
|
203
|
+
this.logger.info(`FormValidatingToolWrapper decision for ${this.name}:`, {
|
|
204
|
+
shouldGenerateForm: shouldGenerate,
|
|
205
|
+
toolName: this.name,
|
|
206
|
+
originalToolName: this.originalTool.name
|
|
207
|
+
});
|
|
208
|
+
if (shouldGenerate) {
|
|
209
|
+
this.logger.info(`Generating form for incomplete input in ${this.name}`);
|
|
1179
210
|
try {
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1182
|
-
this.
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
);
|
|
1193
|
-
this.toolRegistry.registerTool(langchainTool, {
|
|
1194
|
-
metadata: {
|
|
1195
|
-
category: "mcp",
|
|
1196
|
-
version: "1.0.0",
|
|
1197
|
-
dependencies: [serverConfig.name]
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
|
-
this.tools = this.toolRegistry.getAllTools();
|
|
1202
|
-
if (this.initialized && this.executor) {
|
|
1203
|
-
this.logger.info(
|
|
1204
|
-
`Recreating executor with ${this.tools.length} total tools`
|
|
1205
|
-
);
|
|
1206
|
-
await this.createExecutor();
|
|
1207
|
-
}
|
|
1208
|
-
} else {
|
|
1209
|
-
this.logger.error(
|
|
1210
|
-
`Failed to connect to MCP server ${status.serverName}: ${status.error}`
|
|
1211
|
-
);
|
|
1212
|
-
}
|
|
1213
|
-
} catch (error) {
|
|
1214
|
-
this.logger.error(
|
|
1215
|
-
`Background connection failed for MCP server ${serverName}:`,
|
|
1216
|
-
error
|
|
1217
|
-
);
|
|
1218
|
-
this.mcpConnectionStatus.set(serverName, {
|
|
1219
|
-
connected: false,
|
|
1220
|
-
serverName,
|
|
1221
|
-
tools: [],
|
|
1222
|
-
error: error instanceof Error ? error.message : "Connection failed"
|
|
211
|
+
const isCustom = isFormValidatable(this.originalTool);
|
|
212
|
+
const schemaToUse = isCustom ? this.getFormSchema() : this.schema;
|
|
213
|
+
const missingFields = this.calculateMissingFields(
|
|
214
|
+
input,
|
|
215
|
+
isCustom
|
|
216
|
+
);
|
|
217
|
+
const schemaFields = this.isZodObject(schemaToUse) ? Object.keys(schemaToUse.shape) : [];
|
|
218
|
+
this.logger.info(`Using ${isCustom ? "CUSTOM" : "DEFAULT"} schema for form generation`, {
|
|
219
|
+
toolName: this.originalTool.name,
|
|
220
|
+
schemaType: schemaToUse.constructor?.name,
|
|
221
|
+
schemaFields,
|
|
222
|
+
isCustomSchema: isCustom
|
|
1223
223
|
});
|
|
224
|
+
const formMessage = await this.createFormMessage(
|
|
225
|
+
schemaToUse,
|
|
226
|
+
input,
|
|
227
|
+
missingFields
|
|
228
|
+
);
|
|
229
|
+
const result = {
|
|
230
|
+
requiresForm: true,
|
|
231
|
+
formMessage,
|
|
232
|
+
message: `Please complete the form to provide the required information for ${this.name}.`
|
|
233
|
+
};
|
|
234
|
+
this.logger.info(`FormValidatingToolWrapper returning form result for ${this.name}`);
|
|
235
|
+
return JSON.stringify(result);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
this.logger.error("Failed to generate form:", error);
|
|
1224
238
|
}
|
|
1225
|
-
}, 1e3);
|
|
1226
|
-
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Detects and processes HashLink blocks from tool responses
|
|
1229
|
-
* @param parsedResponse - The parsed JSON response from a tool
|
|
1230
|
-
* @returns Metadata object containing hashLinkBlock if detected
|
|
1231
|
-
*/
|
|
1232
|
-
processHashLinkBlocks(parsedResponse) {
|
|
1233
|
-
try {
|
|
1234
|
-
const responseRecord = parsedResponse;
|
|
1235
|
-
if (parsedResponse && typeof parsedResponse === "object" && responseRecord.hashLinkBlock && typeof responseRecord.hashLinkBlock === "object") {
|
|
1236
|
-
const block = responseRecord.hashLinkBlock;
|
|
1237
|
-
if (block.blockId && block.hashLink && block.template && block.attributes && typeof block.blockId === "string" && typeof block.hashLink === "string" && typeof block.template === "string" && typeof block.attributes === "object") {
|
|
1238
|
-
this.logger.info("HashLink block detected:", {
|
|
1239
|
-
blockId: block.blockId,
|
|
1240
|
-
hashLink: block.hashLink,
|
|
1241
|
-
template: block.template,
|
|
1242
|
-
attributeKeys: Object.keys(block.attributes)
|
|
1243
|
-
});
|
|
1244
|
-
return {
|
|
1245
|
-
hashLinkBlock: {
|
|
1246
|
-
blockId: block.blockId,
|
|
1247
|
-
hashLink: block.hashLink,
|
|
1248
|
-
template: block.template,
|
|
1249
|
-
attributes: block.attributes
|
|
1250
|
-
}
|
|
1251
|
-
};
|
|
1252
|
-
} else {
|
|
1253
|
-
this.logger.warn("Invalid HashLink block structure detected:", block);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
} catch (error) {
|
|
1257
|
-
this.logger.error("Error processing HashLink blocks:", error);
|
|
1258
|
-
}
|
|
1259
|
-
return {};
|
|
1260
|
-
}
|
|
1261
|
-
/**
|
|
1262
|
-
* Check if a string is valid JSON
|
|
1263
|
-
*/
|
|
1264
|
-
isJSON(str) {
|
|
1265
|
-
if (typeof str !== "string") return false;
|
|
1266
|
-
const trimmed = str.trim();
|
|
1267
|
-
if (!trimmed) return false;
|
|
1268
|
-
if (!(trimmed.startsWith("{") && trimmed.endsWith("}")) && !(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
1269
|
-
return false;
|
|
1270
|
-
}
|
|
1271
|
-
try {
|
|
1272
|
-
JSON.parse(trimmed);
|
|
1273
|
-
return true;
|
|
1274
|
-
} catch {
|
|
1275
|
-
return false;
|
|
1276
239
|
}
|
|
240
|
+
this.logger.info(`FormValidatingToolWrapper passing through to original tool ${this.name}`);
|
|
241
|
+
return this.executeOriginal(input, runManager);
|
|
1277
242
|
}
|
|
1278
243
|
}
|
|
244
|
+
function wrapToolWithFormValidation(tool, formGenerator, config = {}) {
|
|
245
|
+
return new FormValidatingToolWrapper(tool, formGenerator, config);
|
|
246
|
+
}
|
|
1279
247
|
export {
|
|
1280
|
-
|
|
248
|
+
FormValidatingToolWrapper,
|
|
249
|
+
wrapToolWithFormValidation
|
|
1281
250
|
};
|
|
1282
251
|
//# sourceMappingURL=index30.js.map
|