@hashgraphonline/conversational-agent 0.1.214 → 0.1.217

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.
Files changed (170) hide show
  1. package/cli/dist/CLIApp.d.ts +9 -0
  2. package/cli/dist/CLIApp.js +127 -0
  3. package/cli/dist/LocalConversationalAgent.d.ts +37 -0
  4. package/cli/dist/LocalConversationalAgent.js +58 -0
  5. package/cli/dist/app.d.ts +16 -0
  6. package/cli/dist/app.js +13 -0
  7. package/cli/dist/cli.d.ts +2 -0
  8. package/cli/dist/cli.js +51 -0
  9. package/cli/dist/components/AppContainer.d.ts +16 -0
  10. package/cli/dist/components/AppContainer.js +24 -0
  11. package/cli/dist/components/AppScreens.d.ts +2 -0
  12. package/cli/dist/components/AppScreens.js +259 -0
  13. package/cli/dist/components/ChatScreen.d.ts +15 -0
  14. package/cli/dist/components/ChatScreen.js +39 -0
  15. package/cli/dist/components/DebugLoadingScreen.d.ts +5 -0
  16. package/cli/dist/components/DebugLoadingScreen.js +31 -0
  17. package/cli/dist/components/LoadingScreen.d.ts +2 -0
  18. package/cli/dist/components/LoadingScreen.js +16 -0
  19. package/cli/dist/components/LoadingScreenDebug.d.ts +5 -0
  20. package/cli/dist/components/LoadingScreenDebug.js +27 -0
  21. package/cli/dist/components/MCPConfigScreen.d.ts +28 -0
  22. package/cli/dist/components/MCPConfigScreen.js +168 -0
  23. package/cli/dist/components/ScreenRouter.d.ts +12 -0
  24. package/cli/dist/components/ScreenRouter.js +22 -0
  25. package/cli/dist/components/SetupScreen.d.ts +15 -0
  26. package/cli/dist/components/SetupScreen.js +65 -0
  27. package/cli/dist/components/SingleLoadingScreen.d.ts +5 -0
  28. package/cli/dist/components/SingleLoadingScreen.js +27 -0
  29. package/cli/dist/components/StatusBadge.d.ts +7 -0
  30. package/cli/dist/components/StatusBadge.js +28 -0
  31. package/cli/dist/components/TerminalWindow.d.ts +8 -0
  32. package/cli/dist/components/TerminalWindow.js +24 -0
  33. package/cli/dist/components/WelcomeScreen.d.ts +11 -0
  34. package/cli/dist/components/WelcomeScreen.js +47 -0
  35. package/cli/dist/context/AppContext.d.ts +68 -0
  36. package/cli/dist/context/AppContext.js +363 -0
  37. package/cli/dist/hooks/useInitializeAgent.d.ts +19 -0
  38. package/cli/dist/hooks/useInitializeAgent.js +28 -0
  39. package/cli/dist/hooks/useStableState.d.ts +38 -0
  40. package/cli/dist/hooks/useStableState.js +68 -0
  41. package/cli/dist/managers/AgentManager.d.ts +57 -0
  42. package/cli/dist/managers/AgentManager.js +119 -0
  43. package/cli/dist/managers/ConfigManager.d.ts +53 -0
  44. package/cli/dist/managers/ConfigManager.js +173 -0
  45. package/cli/dist/types.d.ts +31 -0
  46. package/cli/dist/types.js +19 -0
  47. package/dist/cjs/base-agent.d.ts +2 -0
  48. package/dist/cjs/conversational-agent.d.ts +8 -0
  49. package/dist/cjs/core/ToolRegistry.d.ts +130 -0
  50. package/dist/cjs/execution/ExecutionPipeline.d.ts +81 -0
  51. package/dist/cjs/forms/FormEngine.d.ts +121 -0
  52. package/dist/cjs/forms/field-type-registry.d.ts +51 -0
  53. package/dist/cjs/forms/form-generator.d.ts +123 -0
  54. package/dist/cjs/forms/index.d.ts +2 -0
  55. package/dist/cjs/forms/types.d.ts +108 -0
  56. package/dist/cjs/index.cjs +1 -1
  57. package/dist/cjs/index.cjs.map +1 -1
  58. package/dist/cjs/index.d.ts +5 -0
  59. package/dist/cjs/langchain/FormAwareAgentExecutor.d.ts +108 -0
  60. package/dist/cjs/langchain/FormValidatingToolWrapper.d.ts +81 -0
  61. package/dist/cjs/langchain-agent.d.ts +65 -0
  62. package/dist/cjs/memory/ContentStorage.d.ts +7 -0
  63. package/dist/cjs/memory/SmartMemoryManager.d.ts +1 -0
  64. package/dist/cjs/services/ContentStoreManager.d.ts +11 -1
  65. package/dist/cjs/utils/ResponseFormatter.d.ts +26 -0
  66. package/dist/esm/index.js +8 -1
  67. package/dist/esm/index.js.map +1 -1
  68. package/dist/esm/index12.js +1 -1
  69. package/dist/esm/index12.js.map +1 -1
  70. package/dist/esm/index14.js +23 -5
  71. package/dist/esm/index14.js.map +1 -1
  72. package/dist/esm/index15.js +25 -4
  73. package/dist/esm/index15.js.map +1 -1
  74. package/dist/esm/index16.js +4 -2
  75. package/dist/esm/index16.js.map +1 -1
  76. package/dist/esm/index17.js +2 -7
  77. package/dist/esm/index17.js.map +1 -1
  78. package/dist/esm/index18.js +609 -36
  79. package/dist/esm/index18.js.map +1 -1
  80. package/dist/esm/index19.js +229 -84
  81. package/dist/esm/index19.js.map +1 -1
  82. package/dist/esm/index20.js +111 -17
  83. package/dist/esm/index20.js.map +1 -1
  84. package/dist/esm/index21.js +44 -7
  85. package/dist/esm/index21.js.map +1 -1
  86. package/dist/esm/index22.js +86 -157
  87. package/dist/esm/index22.js.map +1 -1
  88. package/dist/esm/index23.js +32 -150
  89. package/dist/esm/index23.js.map +1 -1
  90. package/dist/esm/index24.js +746 -80
  91. package/dist/esm/index24.js.map +1 -1
  92. package/dist/esm/index25.js +154 -45
  93. package/dist/esm/index25.js.map +1 -1
  94. package/dist/esm/index26.js +149 -24
  95. package/dist/esm/index26.js.map +1 -1
  96. package/dist/esm/index27.js +196 -217
  97. package/dist/esm/index27.js.map +1 -1
  98. package/dist/esm/index28.js +187 -0
  99. package/dist/esm/index28.js.map +1 -0
  100. package/dist/esm/index29.js +308 -0
  101. package/dist/esm/index29.js.map +1 -0
  102. package/dist/esm/index30.js +159 -0
  103. package/dist/esm/index30.js.map +1 -0
  104. package/dist/esm/index31.js +68 -0
  105. package/dist/esm/index31.js.map +1 -0
  106. package/dist/esm/index32.js +30 -0
  107. package/dist/esm/index32.js.map +1 -0
  108. package/dist/esm/index33.js +95 -0
  109. package/dist/esm/index33.js.map +1 -0
  110. package/dist/esm/index34.js +245 -0
  111. package/dist/esm/index34.js.map +1 -0
  112. package/dist/esm/index5.js +2 -2
  113. package/dist/esm/index5.js.map +1 -1
  114. package/dist/esm/index6.js +68 -25
  115. package/dist/esm/index6.js.map +1 -1
  116. package/dist/esm/index7.js.map +1 -1
  117. package/dist/esm/index8.js +744 -70
  118. package/dist/esm/index8.js.map +1 -1
  119. package/dist/types/base-agent.d.ts +2 -0
  120. package/dist/types/conversational-agent.d.ts +8 -0
  121. package/dist/types/core/ToolRegistry.d.ts +130 -0
  122. package/dist/types/execution/ExecutionPipeline.d.ts +81 -0
  123. package/dist/types/forms/FormEngine.d.ts +121 -0
  124. package/dist/types/forms/field-type-registry.d.ts +51 -0
  125. package/dist/types/forms/form-generator.d.ts +123 -0
  126. package/dist/types/forms/index.d.ts +2 -0
  127. package/dist/types/forms/types.d.ts +108 -0
  128. package/dist/types/index.d.ts +5 -0
  129. package/dist/types/langchain/FormAwareAgentExecutor.d.ts +108 -0
  130. package/dist/types/langchain/FormValidatingToolWrapper.d.ts +81 -0
  131. package/dist/types/langchain-agent.d.ts +65 -0
  132. package/dist/types/memory/ContentStorage.d.ts +7 -0
  133. package/dist/types/memory/SmartMemoryManager.d.ts +1 -0
  134. package/dist/types/services/ContentStoreManager.d.ts +11 -1
  135. package/dist/types/utils/ResponseFormatter.d.ts +26 -0
  136. package/package.json +35 -34
  137. package/src/base-agent.ts +2 -0
  138. package/src/config/system-message.ts +14 -0
  139. package/src/context/ReferenceContextManager.ts +1 -1
  140. package/src/conversational-agent.ts +95 -38
  141. package/src/core/ToolRegistry.ts +358 -0
  142. package/src/execution/ExecutionPipeline.ts +301 -0
  143. package/src/forms/FormEngine.ts +443 -0
  144. package/src/forms/field-type-registry.ts +203 -0
  145. package/src/forms/form-generator.ts +841 -0
  146. package/src/forms/index.ts +2 -0
  147. package/src/forms/types.ts +125 -0
  148. package/src/index.ts +9 -0
  149. package/src/langchain/FormAwareAgentExecutor.ts +971 -0
  150. package/src/langchain/FormValidatingToolWrapper.ts +355 -0
  151. package/src/langchain-agent.ts +1034 -87
  152. package/src/mcp/ContentProcessor.ts +20 -4
  153. package/src/mcp/MCPClientManager.ts +1 -1
  154. package/src/mcp/adapters/langchain.ts +1 -1
  155. package/src/memory/ContentStorage.ts +25 -5
  156. package/src/memory/SmartMemoryManager.ts +27 -4
  157. package/src/memory/TokenCounter.ts +1 -1
  158. package/src/plugins/hbar/HbarPlugin.ts +0 -1
  159. package/src/scripts/test-external-tool-wrapper.ts +103 -0
  160. package/src/scripts/test-hedera-kit-wrapper.ts +265 -0
  161. package/src/scripts/test-inscribe-form-generation.ts +494 -0
  162. package/src/scripts/test-inscribe-wrapper-verification.ts +220 -0
  163. package/src/services/ContentStoreManager.ts +23 -9
  164. package/src/services/EntityResolver.ts +2 -9
  165. package/src/tools/EntityResolverTool.ts +5 -8
  166. package/src/utils/ResponseFormatter.ts +146 -0
  167. package/cli/readme.md +0 -181
  168. package/dist/cjs/langchain/ContentAwareAgentExecutor.d.ts +0 -14
  169. package/dist/types/langchain/ContentAwareAgentExecutor.d.ts +0 -14
  170. package/src/langchain/ContentAwareAgentExecutor.ts +0 -19
@@ -0,0 +1,494 @@
1
+ /**
2
+ * Test script to verify that InscribeHashinalTool generates a form when attributes field is missing
3
+ *
4
+ * This script tests the end-to-end form generation functionality for the InscribeHashinalTool
5
+ * when required fields are missing from the input.
6
+ *
7
+ * Expected behavior:
8
+ * 1. Tool should be wrapped with FormValidatingToolWrapper (due to extendZodSchema)
9
+ * 2. When attributes field is missing, validation should fail
10
+ * 3. A form should be generated containing the missing attributes field
11
+ * 4. Tool should NOT execute directly but return form generation response
12
+ *
13
+ * Run with: pnpm tsx src/scripts/test-inscribe-form-generation.ts
14
+ */
15
+
16
+ import dotenv from 'dotenv';
17
+ import { ConversationalAgent } from '../conversational-agent';
18
+ import type { ChatResponse } from '../base-agent';
19
+ import type { FormMessage } from '../forms/types';
20
+
21
+ dotenv.config();
22
+
23
+ interface TestResult {
24
+ success: boolean;
25
+ message: string;
26
+ details?: Record<string, unknown>;
27
+ }
28
+
29
+ interface FormGenerationTestResult {
30
+ isFormGenerated: boolean;
31
+ hasAttributesField: boolean;
32
+ formMessage?: FormMessage;
33
+ originalResponse?: ChatResponse;
34
+ }
35
+
36
+ /**
37
+ * Validates that required environment variables are present
38
+ */
39
+ function validateEnvironment(): TestResult {
40
+ const required = [
41
+ 'HEDERA_OPERATOR_ID',
42
+ 'HEDERA_OPERATOR_KEY',
43
+ 'OPENAI_API_KEY'
44
+ ];
45
+
46
+ const missing = required.filter(key => !process.env[key]);
47
+
48
+ if (missing.length > 0) {
49
+ return {
50
+ success: false,
51
+ message: `Missing required environment variables: ${missing.join(', ')}`
52
+ };
53
+ }
54
+
55
+ return {
56
+ success: true,
57
+ message: 'Environment validation passed'
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Creates and initializes a conversational agent for testing
63
+ */
64
+ async function createTestAgent(): Promise<ConversationalAgent> {
65
+ const options = {
66
+ accountId: process.env.HEDERA_OPERATOR_ID!,
67
+ privateKey: process.env.HEDERA_OPERATOR_KEY!,
68
+ network: (process.env.HEDERA_NETWORK as any) || 'testnet',
69
+ openAIApiKey: process.env.OPENAI_API_KEY!,
70
+ openAIModelName: 'gpt-4o-mini',
71
+ verbose: true, // Enable verbose for debugging
72
+ disableLogging: false, // Enable logging for debugging
73
+ entityMemoryEnabled: false, // Disable for testing
74
+ };
75
+
76
+ const agent = new ConversationalAgent(options);
77
+ await agent.initialize();
78
+
79
+ return agent;
80
+ }
81
+
82
+ /**
83
+ * Test the FormValidatingToolWrapper directly by accessing the underlying tools
84
+ */
85
+ async function testDirectToolAccess(agent: ConversationalAgent): Promise<FormGenerationTestResult> {
86
+ console.log('šŸ”§ Testing direct tool access...');
87
+
88
+ try {
89
+ const underlyingAgent = agent.getAgent();
90
+ const tools = (underlyingAgent as any).tools;
91
+
92
+ console.log('Available tools:', tools.map((t: any) => t.name));
93
+
94
+ const inscribeHashinalTool = tools.find((t: any) => t.name === 'inscribeHashinal');
95
+
96
+ if (!inscribeHashinalTool) {
97
+ console.log('āŒ InscribeHashinal tool not found in tools array');
98
+ return {
99
+ isFormGenerated: false,
100
+ hasAttributesField: false
101
+ };
102
+ }
103
+
104
+ console.log('āœ… Found InscribeHashinal tool:', {
105
+ name: inscribeHashinalTool.name,
106
+ type: inscribeHashinalTool.constructor.name,
107
+ isFormValidatingWrapper: inscribeHashinalTool.constructor.name === 'FormValidatingToolWrapper'
108
+ });
109
+
110
+ const incompleteInput = {
111
+ url: "https://example.com/test-image.png",
112
+ name: "Test NFT",
113
+ description: "A test NFT for form testing"
114
+ };
115
+
116
+ console.log('🧪 Calling tool directly with incomplete input...');
117
+ const result = await (inscribeHashinalTool as any)._call(incompleteInput);
118
+
119
+ console.log('šŸ“‹ Direct tool call result:', {
120
+ resultType: typeof result,
121
+ resultLength: result?.length || 0,
122
+ resultPreview: result?.substring(0, 200) + '...'
123
+ });
124
+
125
+ try {
126
+ const parsed = JSON.parse(result);
127
+ if (parsed.requiresForm && parsed.formMessage) {
128
+ console.log('šŸŽ‰ Form generated by direct tool call!');
129
+ const formMessage = parsed.formMessage;
130
+
131
+ const formConfig = (formMessage as any).formConfig;
132
+ const attributesField = formConfig?.fields?.find(
133
+ (field: any) => field.name === 'attributes'
134
+ );
135
+
136
+ const hasAttributesField = !!attributesField;
137
+
138
+ console.log('šŸ“ Form analysis (direct call):', {
139
+ formId: (formMessage as any).id,
140
+ toolName: (formMessage as any).toolName,
141
+ fieldCount: formConfig?.fields?.length || 0,
142
+ hasAttributesField,
143
+ attributesFieldType: attributesField?.type,
144
+ fieldsPresent: formConfig?.fields?.map((f: any) => f.name) || []
145
+ });
146
+
147
+ return {
148
+ isFormGenerated: true,
149
+ hasAttributesField,
150
+ formMessage,
151
+ };
152
+ }
153
+ } catch {
154
+ console.log('Result is not JSON or does not contain form data');
155
+ }
156
+
157
+ return {
158
+ isFormGenerated: false,
159
+ hasAttributesField: false
160
+ };
161
+
162
+ } catch (error) {
163
+ console.error('āŒ Error during direct tool test:', error);
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Tests that the InscribeHashinalTool is properly wrapped and generates forms for missing fields
170
+ */
171
+ async function testInscribeFormGeneration(agent: ConversationalAgent): Promise<FormGenerationTestResult> {
172
+ console.log('🧪 Testing form generation for InscribeHashinalTool...');
173
+
174
+ const testMessage = `
175
+ I need to inscribe a hashinal NFT. Please call the inscribeHashinal tool with this data:
176
+ {
177
+ "url": "https://example.com/test-image.png",
178
+ "name": "Test NFT",
179
+ "description": "A test NFT for form testing"
180
+ }
181
+
182
+ Do not ask any questions, just call the tool now with those parameters.
183
+ `;
184
+
185
+ try {
186
+ console.log('šŸ“¤ Sending test message to agent...');
187
+ const response = await agent.processMessage(testMessage);
188
+
189
+ console.log('šŸ“‹ Response received:', {
190
+ requiresForm: response.requiresForm,
191
+ hasFormMessage: !!response.formMessage,
192
+ outputLength: response.output?.length || 0,
193
+ toolCallCount: response.tool_calls?.length || 0,
194
+ hasError: !!response.error
195
+ });
196
+
197
+ if (response.tool_calls && response.tool_calls.length > 0) {
198
+ console.log('šŸ”§ Tool calls made:',
199
+ response.tool_calls.map(call => ({
200
+ name: call.name,
201
+ hasArgs: !!call.args,
202
+ argKeys: call.args ? Object.keys(call.args) : [],
203
+ output: call.output?.substring(0, 100) + '...'
204
+ }))
205
+ );
206
+ }
207
+
208
+ if (response.tool_calls && response.tool_calls.length > 0) {
209
+ for (const toolCall of response.tool_calls) {
210
+ if (toolCall.name === 'inscribeHashinal' && toolCall.output) {
211
+ try {
212
+ const parsed = JSON.parse(toolCall.output);
213
+ if (parsed.requiresForm && parsed.formMessage) {
214
+ console.log('šŸ“ Found form in tool call output!');
215
+ const formMessage = parsed.formMessage;
216
+
217
+ const formConfig = (formMessage as any).formConfig;
218
+ const attributesField = formConfig?.fields?.find(
219
+ (field: any) => field.name === 'attributes'
220
+ );
221
+
222
+ const hasAttributesField = !!attributesField;
223
+
224
+ console.log('šŸ“ Form analysis:', {
225
+ formId: (formMessage as any).id,
226
+ toolName: (formMessage as any).toolName,
227
+ fieldCount: formConfig?.fields?.length || 0,
228
+ hasAttributesField,
229
+ attributesFieldType: attributesField?.type,
230
+ fieldsPresent: formConfig?.fields?.map((f: any) => f.name) || []
231
+ });
232
+
233
+ return {
234
+ isFormGenerated: true,
235
+ hasAttributesField,
236
+ formMessage,
237
+ originalResponse: response
238
+ };
239
+ }
240
+ } catch {
241
+ console.log('Could not parse tool output as JSON:', parseError);
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ if (response.requiresForm && response.formMessage) {
248
+ const formMessage = response.formMessage;
249
+
250
+ const formConfig = (formMessage as any).formConfig;
251
+ const attributesField = formConfig?.fields?.find(
252
+ (field: any) => field.name === 'attributes'
253
+ );
254
+
255
+ const hasAttributesField = !!attributesField;
256
+
257
+ console.log('šŸ“ Form analysis (top-level):', {
258
+ formId: (formMessage as any).id,
259
+ toolName: (formMessage as any).toolName,
260
+ fieldCount: formConfig?.fields?.length || 0,
261
+ hasAttributesField,
262
+ attributesFieldType: attributesField?.type,
263
+ fieldsPresent: formConfig?.fields?.map((f: any) => f.name) || []
264
+ });
265
+
266
+ return {
267
+ isFormGenerated: true,
268
+ hasAttributesField,
269
+ formMessage: formMessage as FormMessage,
270
+ originalResponse: response
271
+ };
272
+ } else {
273
+ console.log('āš ļø No form was generated in response');
274
+ console.log('Response details:', {
275
+ output: response.output?.substring(0, 200) + '...',
276
+ toolCalls: response.tool_calls?.length || 0,
277
+ error: response.error
278
+ });
279
+
280
+ return {
281
+ isFormGenerated: false,
282
+ hasAttributesField: false,
283
+ originalResponse: response
284
+ };
285
+ }
286
+ } catch (error) {
287
+ console.error('āŒ Error during form generation test:', error);
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Verifies that the tool wrapper is working correctly by checking response structure
294
+ */
295
+ function analyzeFormGenerationResponse(result: FormGenerationTestResult): TestResult {
296
+ if (!result.isFormGenerated) {
297
+ return {
298
+ success: false,
299
+ message: 'Form was not generated when attributes field was missing',
300
+ details: {
301
+ responseHadForm: result.isFormGenerated,
302
+ originalOutput: result.originalResponse?.output?.substring(0, 200)
303
+ }
304
+ };
305
+ }
306
+
307
+ if (!result.hasAttributesField) {
308
+ return {
309
+ success: false,
310
+ message: 'Form was generated but does not contain attributes field',
311
+ details: {
312
+ formFields: result.formMessage?.formConfig.fields.map(f => f.name) || [],
313
+ formId: result.formMessage?.id
314
+ }
315
+ };
316
+ }
317
+
318
+ if (result.formMessage?.toolName !== 'inscribeHashinal') {
319
+ return {
320
+ success: false,
321
+ message: 'Form was generated for wrong tool',
322
+ details: {
323
+ expectedTool: 'inscribeHashinal',
324
+ actualTool: result.formMessage?.toolName
325
+ }
326
+ };
327
+ }
328
+
329
+ return {
330
+ success: true,
331
+ message: 'Form generation test passed successfully',
332
+ details: {
333
+ formGenerated: true,
334
+ hasAttributesField: true,
335
+ toolName: result.formMessage?.toolName,
336
+ fieldCount: result.formMessage?.formConfig.fields.length,
337
+ formId: result.formMessage?.id
338
+ }
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Validates that the form contains expected structure and required elements
344
+ */
345
+ function validateFormStructure(formMessage: FormMessage): TestResult {
346
+ const issues: string[] = [];
347
+
348
+ if (!formMessage.id) {
349
+ issues.push('Form missing unique ID');
350
+ }
351
+
352
+ if (!formMessage.formConfig) {
353
+ issues.push('Form missing configuration');
354
+ }
355
+
356
+ if (!formMessage.formConfig.fields || formMessage.formConfig.fields.length === 0) {
357
+ issues.push('Form has no fields');
358
+ }
359
+
360
+ if (!formMessage.formConfig.title) {
361
+ issues.push('Form missing title');
362
+ }
363
+
364
+ const fieldNames = formMessage.formConfig.fields.map(f => f.name);
365
+ const essentialFields = ['attributes'];
366
+ const missingEssential = essentialFields.filter(field => !fieldNames.includes(field));
367
+
368
+ if (missingEssential.length > 0) {
369
+ issues.push(`Form missing essential fields: ${missingEssential.join(', ')}`);
370
+ }
371
+
372
+ const attributesField = formMessage.formConfig.fields.find(f => f.name === 'attributes');
373
+ if (attributesField) {
374
+ if (attributesField.type !== 'array') {
375
+ issues.push(`Attributes field has wrong type: ${attributesField.type}, expected: array`);
376
+ }
377
+
378
+ if (!attributesField.required) {
379
+ issues.push('Attributes field should be marked as required');
380
+ }
381
+ }
382
+
383
+ if (issues.length > 0) {
384
+ return {
385
+ success: false,
386
+ message: `Form structure validation failed: ${issues.join(', ')}`,
387
+ details: {
388
+ issues,
389
+ formStructure: {
390
+ id: formMessage.id,
391
+ title: formMessage.formConfig.title,
392
+ fieldCount: formMessage.formConfig.fields.length,
393
+ fields: formMessage.formConfig.fields.map(f => ({
394
+ name: f.name,
395
+ type: f.type,
396
+ required: f.required
397
+ }))
398
+ }
399
+ }
400
+ };
401
+ }
402
+
403
+ return {
404
+ success: true,
405
+ message: 'Form structure validation passed',
406
+ details: {
407
+ formId: formMessage.id,
408
+ title: formMessage.formConfig.title,
409
+ fieldCount: formMessage.formConfig.fields.length,
410
+ attributesFieldFound: true
411
+ }
412
+ };
413
+ }
414
+
415
+ /**
416
+ * Main test execution function
417
+ */
418
+ async function runTest(): Promise<void> {
419
+ console.log('šŸš€ Starting InscribeHashinalTool form generation test\n');
420
+
421
+ try {
422
+
423
+ console.log('šŸ“‹ Step 1: Validating environment...');
424
+ const envResult = validateEnvironment();
425
+ if (!envResult.success) {
426
+ console.error('āŒ', envResult.message);
427
+ process.exit(1);
428
+ }
429
+ console.log('āœ…', envResult.message, '\n');
430
+
431
+ console.log('šŸ“‹ Step 2: Initializing conversational agent...');
432
+ const agent = await createTestAgent();
433
+ console.log('āœ… Agent initialized successfully\n');
434
+
435
+ console.log('šŸ“‹ Step 3a: Testing direct tool access...');
436
+ const directResult = await testDirectToolAccess(agent);
437
+
438
+ console.log('šŸ“‹ Step 3b: Testing InscribeHashinalTool form generation via conversation...');
439
+ const formResult = directResult.isFormGenerated ? directResult : await testInscribeFormGeneration(agent);
440
+
441
+ console.log('šŸ“‹ Step 4: Analyzing form generation results...');
442
+ const analysisResult = analyzeFormGenerationResponse(formResult);
443
+
444
+ if (!analysisResult.success) {
445
+ console.error('āŒ', analysisResult.message);
446
+ if (analysisResult.details) {
447
+ console.log('Details:', JSON.stringify(analysisResult.details, null, 2));
448
+ }
449
+ process.exit(1);
450
+ }
451
+ console.log('āœ…', analysisResult.message);
452
+
453
+ if (formResult.formMessage) {
454
+ console.log('šŸ“‹ Step 5: Validating form structure...');
455
+ const structureResult = validateFormStructure(formResult.formMessage);
456
+
457
+ if (!structureResult.success) {
458
+ console.error('āŒ', structureResult.message);
459
+ if (structureResult.details) {
460
+ console.log('Details:', JSON.stringify(structureResult.details, null, 2));
461
+ }
462
+ process.exit(1);
463
+ }
464
+ console.log('āœ…', structureResult.message);
465
+ }
466
+
467
+ console.log('\nšŸŽ‰ All tests passed successfully!');
468
+ console.log('\nšŸ“Š Test Summary:');
469
+ console.log('āœ… InscribeHashinalTool is properly wrapped with FormValidatingToolWrapper');
470
+ console.log('āœ… Form is generated when attributes field is missing');
471
+ console.log('āœ… Generated form contains attributes field for user input');
472
+ console.log('āœ… Form structure is valid and complete');
473
+
474
+ if (analysisResult.details) {
475
+ console.log('\nšŸ“‹ Final Details:');
476
+ console.log(` Form ID: ${analysisResult.details.formId}`);
477
+ console.log(` Tool Name: ${analysisResult.details.toolName}`);
478
+ console.log(` Field Count: ${analysisResult.details.fieldCount}`);
479
+ console.log(` Has Attributes Field: ${analysisResult.details.hasAttributesField}`);
480
+ }
481
+
482
+ await agent.cleanup();
483
+
484
+ } catch (error) {
485
+ console.error('āŒ Test failed with error:', error);
486
+ console.error('Error details:', error instanceof Error ? error.stack : String(error));
487
+ process.exit(1);
488
+ }
489
+ }
490
+
491
+ runTest().catch((error) => {
492
+ console.error('Unhandled test error:', error);
493
+ process.exit(1);
494
+ });
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Simplified test script to verify that InscribeHashinalTool is wrapped with FormValidatingToolWrapper
3
+ *
4
+ * This script verifies the fix for isZodObjectLike detection in langchain-agent.ts
5
+ *
6
+ * Expected behavior:
7
+ * 1. InscribeHashinalTool should be detected as having extendZodSchema with render config
8
+ * 2. Tool should be wrapped with FormValidatingToolWrapper during initialization
9
+ * 3. Wrapper type should be confirmed in the tools array
10
+ *
11
+ * Run with: pnpm tsx src/scripts/test-inscribe-wrapper-verification.ts
12
+ */
13
+
14
+ import dotenv from 'dotenv';
15
+ import { ConversationalAgent } from '../conversational-agent';
16
+
17
+ dotenv.config();
18
+
19
+ interface WrapperTestResult {
20
+ success: boolean;
21
+ message: string;
22
+ details: {
23
+ toolFound: boolean;
24
+ isWrapped: boolean;
25
+ wrapperType?: string;
26
+ hasRenderConfig?: boolean;
27
+ toolsCount: number;
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Validates that required environment variables are present
33
+ */
34
+ function validateEnvironment(): { success: boolean; message: string } {
35
+ const required = [
36
+ 'HEDERA_OPERATOR_ID',
37
+ 'HEDERA_OPERATOR_KEY',
38
+ 'OPENAI_API_KEY'
39
+ ];
40
+
41
+ const missing = required.filter(key => !process.env[key]);
42
+
43
+ if (missing.length > 0) {
44
+ return {
45
+ success: false,
46
+ message: `Missing required environment variables: ${missing.join(', ')}`
47
+ };
48
+ }
49
+
50
+ return {
51
+ success: true,
52
+ message: 'Environment validation passed'
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Creates and initializes a conversational agent for testing
58
+ */
59
+ async function createTestAgent(): Promise<ConversationalAgent> {
60
+ const options = {
61
+ accountId: process.env.HEDERA_OPERATOR_ID!,
62
+ privateKey: process.env.HEDERA_OPERATOR_KEY!,
63
+ network: (process.env.HEDERA_NETWORK as any) || 'testnet',
64
+ openAIApiKey: process.env.OPENAI_API_KEY!,
65
+ openAIModelName: 'gpt-4o-mini',
66
+ verbose: false, // Reduce verbosity
67
+ disableLogging: true, // Disable logs
68
+ entityMemoryEnabled: false, // Disable for testing
69
+ };
70
+
71
+ const agent = new ConversationalAgent(options);
72
+ await agent.initialize();
73
+
74
+ return agent;
75
+ }
76
+
77
+ /**
78
+ * Test that the InscribeHashinalTool is properly wrapped with FormValidatingToolWrapper
79
+ */
80
+ async function testInscribeHashinalWrapper(agent: ConversationalAgent): Promise<WrapperTestResult> {
81
+ console.log('šŸ” Checking InscribeHashinalTool wrapper status...');
82
+
83
+ try {
84
+ const underlyingAgent = agent.getAgent();
85
+ const tools = (underlyingAgent as any).tools;
86
+
87
+ const toolsCount = tools.length;
88
+ console.log(`šŸ“Š Total tools loaded: ${toolsCount}`);
89
+
90
+ const inscribeHashinalTool = tools.find((t: any) => t.name === 'inscribeHashinal');
91
+
92
+ if (!inscribeHashinalTool) {
93
+ return {
94
+ success: false,
95
+ message: 'InscribeHashinal tool not found in tools array',
96
+ details: {
97
+ toolFound: false,
98
+ isWrapped: false,
99
+ toolsCount
100
+ }
101
+ };
102
+ }
103
+
104
+ const toolType = inscribeHashinalTool.constructor.name;
105
+ const isFormValidatingWrapper = toolType === 'FormValidatingToolWrapper';
106
+ const hasRenderConfig = !!(inscribeHashinalTool.schema as any)?._renderConfig;
107
+
108
+ console.log('šŸ”§ InscribeHashinal tool analysis:', {
109
+ name: inscribeHashinalTool.name,
110
+ type: toolType,
111
+ isFormValidatingWrapper,
112
+ hasRenderConfig,
113
+ hasSchema: !!inscribeHashinalTool.schema
114
+ });
115
+
116
+ if (isFormValidatingWrapper) {
117
+ return {
118
+ success: true,
119
+ message: 'InscribeHashinalTool is properly wrapped with FormValidatingToolWrapper',
120
+ details: {
121
+ toolFound: true,
122
+ isWrapped: true,
123
+ wrapperType: toolType,
124
+ hasRenderConfig,
125
+ toolsCount
126
+ }
127
+ };
128
+ } else {
129
+ return {
130
+ success: false,
131
+ message: `InscribeHashinalTool is not wrapped. Tool type: ${toolType}`,
132
+ details: {
133
+ toolFound: true,
134
+ isWrapped: false,
135
+ wrapperType: toolType,
136
+ hasRenderConfig,
137
+ toolsCount
138
+ }
139
+ };
140
+ }
141
+
142
+ } catch (error) {
143
+ console.error('āŒ Error during wrapper test:', error);
144
+ return {
145
+ success: false,
146
+ message: `Error during wrapper test: ${error instanceof Error ? error.message : String(error)}`,
147
+ details: {
148
+ toolFound: false,
149
+ isWrapped: false,
150
+ toolsCount: 0
151
+ }
152
+ };
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Main test execution function
158
+ */
159
+ async function runTest(): Promise<void> {
160
+ console.log('šŸš€ Starting InscribeHashinalTool wrapper verification test\n');
161
+
162
+ try {
163
+
164
+ console.log('šŸ“‹ Step 1: Validating environment...');
165
+ const envResult = validateEnvironment();
166
+ if (!envResult.success) {
167
+ console.error('āŒ', envResult.message);
168
+ process.exit(1);
169
+ }
170
+ console.log('āœ…', envResult.message, '\n');
171
+
172
+ console.log('šŸ“‹ Step 2: Initializing conversational agent...');
173
+ const agent = await createTestAgent();
174
+ console.log('āœ… Agent initialized successfully\n');
175
+
176
+ console.log('šŸ“‹ Step 3: Testing InscribeHashinalTool wrapper...');
177
+ const wrapperResult = await testInscribeHashinalWrapper(agent);
178
+
179
+ if (wrapperResult.success) {
180
+ console.log('āœ…', wrapperResult.message);
181
+ console.log('\nšŸ“Š Test Results:');
182
+ console.log(` Tool Found: ${wrapperResult.details.toolFound}`);
183
+ console.log(` Is Wrapped: ${wrapperResult.details.isWrapped}`);
184
+ console.log(` Wrapper Type: ${wrapperResult.details.wrapperType}`);
185
+ console.log(` Has Render Config: ${wrapperResult.details.hasRenderConfig}`);
186
+ console.log(` Total Tools: ${wrapperResult.details.toolsCount}`);
187
+
188
+ console.log('\nšŸŽ‰ SUCCESS: InscribeHashinalTool form generation fix is working!');
189
+ console.log('\nšŸ“ What this means:');
190
+ console.log('āœ… isZodObjectLike detection fixed for tools with extendZodSchema');
191
+ console.log('āœ… InscribeHashinalTool properly wrapped with FormValidatingToolWrapper');
192
+ console.log('āœ… Form will be generated when attributes field is missing');
193
+ console.log('āœ… Users will get form UI instead of validation errors');
194
+
195
+ } else {
196
+ console.error('āŒ', wrapperResult.message);
197
+ console.log('\nšŸ“Š Test Results:');
198
+ console.log(` Tool Found: ${wrapperResult.details.toolFound}`);
199
+ console.log(` Is Wrapped: ${wrapperResult.details.isWrapped}`);
200
+ console.log(` Wrapper Type: ${wrapperResult.details.wrapperType || 'N/A'}`);
201
+ console.log(` Has Render Config: ${wrapperResult.details.hasRenderConfig || false}`);
202
+ console.log(` Total Tools: ${wrapperResult.details.toolsCount}`);
203
+
204
+ console.log('\nāŒ FAILURE: Fix may not be working correctly');
205
+ process.exit(1);
206
+ }
207
+
208
+ await agent.cleanup();
209
+
210
+ } catch (error) {
211
+ console.error('āŒ Test failed with error:', error);
212
+ console.error('Error details:', error instanceof Error ? error.stack : String(error));
213
+ process.exit(1);
214
+ }
215
+ }
216
+
217
+ runTest().catch((error) => {
218
+ console.error('Unhandled test error:', error);
219
+ process.exit(1);
220
+ });