@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,841 @@
1
+ import { z, ZodError } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import type {
4
+ FormConfig,
5
+ FormField,
6
+ ValidationError,
7
+ FormMessage,
8
+ FormFieldType,
9
+ } from './types';
10
+ import {
11
+ extractRenderConfigs,
12
+ generateFieldOrdering,
13
+ type RenderConfigSchema,
14
+ type ZodSchemaWithRender,
15
+ type ExtractedRenderConfig,
16
+ } from '@hashgraphonline/standards-agent-kit';
17
+ import { Logger } from '@hashgraphonline/standards-sdk';
18
+ import { fieldTypeRegistry } from './field-type-registry';
19
+
20
+ type FieldPriority = 'essential' | 'common' | 'advanced' | 'expert';
21
+
22
+ /**
23
+ * Generates forms from Zod validation failures
24
+ */
25
+ export class FormGenerator {
26
+ private logger: Logger;
27
+
28
+ constructor() {
29
+ this.logger = new Logger({ module: 'FormGenerator' });
30
+ }
31
+
32
+ /**
33
+ * Creates a form message from a Zod validation error
34
+ * @param error The Zod validation error
35
+ * @param schema The original Zod schema
36
+ * @param toolName Name of the tool that failed validation
37
+ * @param originalPrompt The user's original request
38
+ * @returns FormMessage to send to the chat UI
39
+ */
40
+ generateFormFromError(
41
+ error: ZodError,
42
+ schema: z.ZodSchema,
43
+ toolName: string,
44
+ originalPrompt: string
45
+ ): FormMessage {
46
+ const validationErrors = this.extractValidationErrors(error);
47
+ const missingFields = this.identifyMissingFields(validationErrors, schema);
48
+ const formConfig = this.createFormConfig(schema, missingFields, toolName);
49
+
50
+ return {
51
+ type: 'form',
52
+ id: `form_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
53
+ formConfig,
54
+ originalPrompt,
55
+ toolName,
56
+ validationErrors,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Generates a form from a schema and partial input
62
+ * @param schema The Zod schema to generate a form from
63
+ * @param partialInput Any partial input already provided
64
+ * @param context Additional context about the tool
65
+ * @param preCalculatedMissingFields Optional pre-calculated missing fields set. If undefined, includes all fields from schema.
66
+ * @returns FormMessage to send to the chat UI
67
+ */
68
+ async generateFormFromSchema(
69
+ schema: z.ZodSchema,
70
+ partialInput: unknown,
71
+ context: { toolName: string; toolDescription?: string },
72
+ preCalculatedMissingFields?: Set<string>
73
+ ): Promise<FormMessage> {
74
+ let missingFields: Set<string>;
75
+
76
+ this.logger.info(`🏁 FormGenerator.generateFormFromSchema starting`, {
77
+ toolName: context.toolName,
78
+ partialInput,
79
+ hasSchema: !!schema,
80
+ hasShape: !!(schema && (schema as any).shape),
81
+ hasPreCalculatedFields: preCalculatedMissingFields !== undefined,
82
+ preCalculatedFieldsSize: preCalculatedMissingFields?.size || 0,
83
+ });
84
+
85
+ if (preCalculatedMissingFields !== undefined) {
86
+ missingFields = preCalculatedMissingFields;
87
+ this.logger.info(`📋 Using pre-calculated missing fields`, {
88
+ missingFieldsCount: missingFields.size,
89
+ missingFields: Array.from(missingFields),
90
+ });
91
+ } else {
92
+ missingFields = new Set<string>();
93
+
94
+ const zodObject = this.extractZodObject(schema);
95
+ if (zodObject) {
96
+ const shape = zodObject.shape;
97
+ for (const fieldName of Object.keys(shape)) {
98
+ missingFields.add(fieldName);
99
+ this.logger.info(
100
+ `⭐ Including all fields from focused schema: ${fieldName}`
101
+ );
102
+ }
103
+ }
104
+
105
+ this.logger.info(`📋 Using ALL fields from focused schema`, {
106
+ totalFields: zodObject ? Object.keys(zodObject.shape).length : 0,
107
+ missingFieldsCount: missingFields.size,
108
+ missingFields: Array.from(missingFields),
109
+ });
110
+ }
111
+
112
+ const formConfig = this.createFormConfig(
113
+ schema,
114
+ missingFields,
115
+ context.toolName,
116
+ preCalculatedMissingFields
117
+ );
118
+
119
+ return {
120
+ type: 'form',
121
+ id: `form_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
122
+ formConfig,
123
+ originalPrompt: context.toolDescription || `Complete ${context.toolName}`,
124
+ toolName: context.toolName,
125
+ validationErrors: [],
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Extracts validation errors from ZodError
131
+ */
132
+ private extractValidationErrors(error: ZodError): ValidationError[] {
133
+ return error.issues.map((issue) => ({
134
+ path: issue.path.map((p) => String(p)),
135
+ message: issue.message,
136
+ code: issue.code,
137
+ }));
138
+ }
139
+
140
+ /**
141
+ * Identifies which fields are missing or invalid from validation errors
142
+ */
143
+ private identifyMissingFields(
144
+ errors: ValidationError[],
145
+ _schema: z.ZodSchema
146
+ ): Set<string> {
147
+ const missingFields = new Set<string>();
148
+
149
+ errors.forEach((error) => {
150
+ const fieldPath = error.path.join('.');
151
+ if (fieldPath) {
152
+ missingFields.add(fieldPath);
153
+ }
154
+ });
155
+
156
+ return missingFields;
157
+ }
158
+
159
+ /**
160
+ * Creates form configuration from schema
161
+ */
162
+ private createFormConfig(
163
+ schema: z.ZodSchema,
164
+ missingFields: Set<string>,
165
+ toolName: string,
166
+ preCalculatedMissingFields?: Set<string>
167
+ ): FormConfig {
168
+ const extractedConfig = this.extractRenderConfigsSafely(schema);
169
+ const fieldOrdering = this.generateFieldOrderingSafely(schema);
170
+ const fields = this.generateFormFields(
171
+ schema,
172
+ extractedConfig,
173
+ missingFields,
174
+ fieldOrdering,
175
+ preCalculatedMissingFields
176
+ );
177
+
178
+ return {
179
+ title: this.generateFormTitle(toolName),
180
+ description: this.generateFormDescription(toolName, missingFields.size),
181
+ fields,
182
+ submitLabel: 'Continue',
183
+ cancelLabel: 'Cancel',
184
+ metadata: {
185
+ toolName,
186
+ missingFieldCount: missingFields.size,
187
+ },
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Safely extracts render configs from schema
193
+ */
194
+ private extractRenderConfigsSafely(
195
+ schema: z.ZodSchema
196
+ ): ExtractedRenderConfig {
197
+ try {
198
+ if (typeof extractRenderConfigs === 'function') {
199
+ return extractRenderConfigs(schema as unknown as ZodSchemaWithRender);
200
+ }
201
+ } catch (error) {
202
+ this.logger.warn('Could not extract render configs:', error);
203
+ }
204
+ return {
205
+ fields: {},
206
+ groups: {},
207
+ order: [],
208
+ metadata: {},
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Safely generates field ordering from schema
214
+ */
215
+ private generateFieldOrderingSafely(schema: z.ZodSchema): {
216
+ sections: Array<{ fields: string[] }>;
217
+ } {
218
+ try {
219
+ if (typeof generateFieldOrdering === 'function') {
220
+ const ordering = generateFieldOrdering(
221
+ schema as unknown as ZodSchemaWithRender
222
+ );
223
+
224
+ const sections = Object.values(ordering.sections).map((section) => ({
225
+ fields: section.fields,
226
+ }));
227
+ return { sections };
228
+ }
229
+ } catch (error) {
230
+ this.logger.warn('Could not generate field ordering:', error);
231
+ }
232
+ return { sections: [] };
233
+ }
234
+
235
+ /**
236
+ * Determines field priority for progressive disclosure
237
+ */
238
+ private getFieldPriority(
239
+ name: string,
240
+ renderConfig?: RenderConfigSchema,
241
+ isRequired?: boolean
242
+ ): FieldPriority {
243
+ if (renderConfig?.ui?.priority) {
244
+ const priority = renderConfig.ui.priority as string;
245
+ if (['essential', 'common', 'advanced', 'expert'].includes(priority)) {
246
+ return priority as FieldPriority;
247
+ }
248
+ }
249
+
250
+ if (isRequired === true) {
251
+ return 'essential';
252
+ }
253
+
254
+ const ui = renderConfig?.ui as Record<string, unknown> | undefined;
255
+ if (ui?.advanced === true) {
256
+ return 'advanced';
257
+ }
258
+
259
+ if (ui?.expert === true) {
260
+ return 'expert';
261
+ }
262
+
263
+ return 'common';
264
+ }
265
+
266
+ /**
267
+ * Determines which fields should be included in the form
268
+ */
269
+ private determineFieldsToInclude(
270
+ schema: z.ZodSchema,
271
+ missingFields: Set<string>,
272
+ preCalculatedMissingFields?: Set<string>
273
+ ): Set<string> {
274
+ const fieldsToInclude = new Set<string>();
275
+
276
+ if (preCalculatedMissingFields === undefined) {
277
+ this.logger.info(
278
+ `⭐ Focused schema mode - including ALL fields from schema`
279
+ );
280
+ const allSchemaFields = this.extractFieldsFromSchema(schema);
281
+ allSchemaFields.forEach((fieldName) => {
282
+ fieldsToInclude.add(fieldName);
283
+ this.logger.info(`✅ Including focused schema field: ${fieldName}`);
284
+ });
285
+ } else if (preCalculatedMissingFields.size > 0) {
286
+ this.logger.info(
287
+ `📋 Using ONLY pre-calculated missing fields (${preCalculatedMissingFields.size} fields)`,
288
+ { fields: Array.from(preCalculatedMissingFields) }
289
+ );
290
+ preCalculatedMissingFields.forEach((fieldName) => {
291
+ fieldsToInclude.add(fieldName);
292
+ this.logger.info(`✅ Including pre-calculated field: ${fieldName}`);
293
+ });
294
+ } else {
295
+ this.logger.info(
296
+ '⚠️ No pre-calculated fields, falling back to schema analysis'
297
+ );
298
+ this.includeRequiredMissingFields(schema, missingFields, fieldsToInclude);
299
+ }
300
+
301
+ return fieldsToInclude;
302
+ }
303
+
304
+ /**
305
+ * Includes required fields that are missing
306
+ */
307
+ private includeRequiredMissingFields(
308
+ schema: z.ZodSchema,
309
+ missingFields: Set<string>,
310
+ fieldsToInclude: Set<string>
311
+ ): void {
312
+ const allSchemaFields = this.extractFieldsFromSchema(schema);
313
+ allSchemaFields.forEach((fieldName) => {
314
+ const isRequired = this.isFieldRequired(schema, fieldName);
315
+ const isMissing = missingFields.has(fieldName);
316
+ const shouldInclude = isMissing && isRequired;
317
+
318
+ this.logger.info(`🔍 FormGenerator field inclusion check: ${fieldName}`, {
319
+ isRequired,
320
+ isMissing,
321
+ shouldInclude,
322
+ });
323
+
324
+ if (shouldInclude) {
325
+ fieldsToInclude.add(fieldName);
326
+ }
327
+ });
328
+ }
329
+
330
+ /**
331
+ * Creates form fields from ordered field names
332
+ */
333
+ private createOrderedFields(
334
+ fieldsToInclude: Set<string>,
335
+ fieldOrdering: { sections: Array<{ fields: string[] }> },
336
+ extractedConfig: ExtractedRenderConfig,
337
+ schema: z.ZodSchema
338
+ ): FormField[] {
339
+ const fields: FormField[] = [];
340
+ const processedFields = new Set<string>();
341
+
342
+ if (fieldOrdering.sections.length > 0) {
343
+ const orderedFieldNames = fieldOrdering.sections.flatMap((s) => s.fields);
344
+ orderedFieldNames.forEach((fieldName) => {
345
+ if (fieldsToInclude.has(fieldName) && !processedFields.has(fieldName)) {
346
+ const field = this.createFormField(
347
+ fieldName,
348
+ extractedConfig.fields[fieldName],
349
+ schema,
350
+ fieldName
351
+ );
352
+ if (field) {
353
+ fields.push(field);
354
+ processedFields.add(fieldName);
355
+ }
356
+ }
357
+ });
358
+ }
359
+
360
+ fieldsToInclude.forEach((fieldName) => {
361
+ if (!processedFields.has(fieldName)) {
362
+ const field = this.createFormField(
363
+ fieldName,
364
+ extractedConfig.fields[fieldName],
365
+ schema,
366
+ fieldName
367
+ );
368
+ if (field) {
369
+ fields.push(field);
370
+ }
371
+ }
372
+ });
373
+
374
+ return fields;
375
+ }
376
+
377
+ /**
378
+ * Generates form fields from schema and validation errors
379
+ */
380
+ private generateFormFields(
381
+ schema: z.ZodSchema,
382
+ extractedConfig: ExtractedRenderConfig,
383
+ missingFields: Set<string>,
384
+ fieldOrdering: { sections: Array<{ fields: string[] }> },
385
+ preCalculatedMissingFields?: Set<string>
386
+ ): FormField[] {
387
+ const fieldsToInclude = this.determineFieldsToInclude(
388
+ schema,
389
+ missingFields,
390
+ preCalculatedMissingFields
391
+ );
392
+
393
+ let fields = this.createOrderedFields(
394
+ fieldsToInclude,
395
+ fieldOrdering,
396
+ extractedConfig,
397
+ schema
398
+ );
399
+
400
+ if (fields.length === 0 && missingFields.size > 0) {
401
+ fields = Array.from(missingFields)
402
+ .map((fieldName) =>
403
+ this.createFormField(
404
+ fieldName,
405
+ extractedConfig.fields[fieldName],
406
+ schema,
407
+ fieldName
408
+ )
409
+ )
410
+ .filter(
411
+ (field): field is FormField => field !== null && field !== undefined
412
+ );
413
+ }
414
+
415
+ return fields;
416
+ }
417
+
418
+ /**
419
+ * Creates a single form field
420
+ */
421
+ private createFormField(
422
+ fieldName: string,
423
+ renderConfig?: RenderConfigSchema,
424
+ schema?: z.ZodSchema,
425
+ fieldPath?: string
426
+ ): FormField {
427
+ const type = this.mapFieldType(renderConfig?.fieldType, schema, fieldPath);
428
+ const isRequired = this.isFieldRequired(schema, fieldPath || fieldName);
429
+
430
+ const field: FormField = {
431
+ name: fieldName,
432
+ label: renderConfig?.ui?.label || this.humanizeFieldName(fieldName),
433
+ type,
434
+ required: isRequired,
435
+ priority: this.getFieldPriority(fieldName, renderConfig, isRequired),
436
+ };
437
+
438
+ if (renderConfig) {
439
+ field.renderConfig = renderConfig;
440
+ }
441
+
442
+ if (renderConfig?.ui?.placeholder) {
443
+ field.placeholder = renderConfig.ui.placeholder;
444
+ }
445
+
446
+ if (renderConfig?.ui?.helpText) {
447
+ field.helpText = renderConfig.ui.helpText;
448
+ }
449
+
450
+ if (renderConfig?.constraints) {
451
+ const validation: Record<string, unknown> = {};
452
+ if (renderConfig.constraints.min !== undefined)
453
+ validation.min = renderConfig.constraints.min;
454
+ if (renderConfig.constraints.max !== undefined)
455
+ validation.max = renderConfig.constraints.max;
456
+ if (renderConfig.constraints.minLength !== undefined)
457
+ validation.minLength = renderConfig.constraints.minLength;
458
+ if (renderConfig.constraints.maxLength !== undefined)
459
+ validation.maxLength = renderConfig.constraints.maxLength;
460
+ if (renderConfig.constraints.pattern !== undefined)
461
+ validation.pattern = renderConfig.constraints.pattern;
462
+
463
+ if (Object.keys(validation).length > 0) {
464
+ field.validation = validation;
465
+ }
466
+ }
467
+
468
+ if (renderConfig?.options) {
469
+ field.options = renderConfig.options.map((opt) => ({
470
+ value: String(opt.value),
471
+ label: opt.label,
472
+ ...(opt.disabled !== undefined && { disabled: opt.disabled }),
473
+ }));
474
+ }
475
+
476
+ return field;
477
+ }
478
+
479
+ /**
480
+ * Maps render config field type to form field type with fallback inference
481
+ */
482
+ private mapFieldType(
483
+ fieldType?: string,
484
+ schema?: z.ZodSchema,
485
+ fieldPath?: string
486
+ ): FormFieldType {
487
+ if (!fieldType && schema && fieldPath) {
488
+ const inferredType = this.inferTypeFromSchema(schema, fieldPath);
489
+ if (inferredType) {
490
+ return inferredType;
491
+ }
492
+ }
493
+
494
+ if (!fieldType && fieldPath) {
495
+ const registryType = fieldTypeRegistry.detectType(fieldPath);
496
+ if (registryType) {
497
+ return registryType;
498
+ }
499
+ }
500
+
501
+ if (!fieldType) {
502
+ return 'text';
503
+ }
504
+
505
+ const normalizedType = fieldType.toLowerCase();
506
+
507
+ if (['text', 'string'].includes(normalizedType)) return 'text';
508
+ if (['number', 'integer', 'float', 'decimal'].includes(normalizedType))
509
+ return 'number';
510
+ if (['select', 'enum', 'dropdown'].includes(normalizedType))
511
+ return 'select';
512
+ if (['checkbox', 'boolean', 'bool'].includes(normalizedType))
513
+ return 'checkbox';
514
+ if (['textarea', 'longtext', 'multiline'].includes(normalizedType))
515
+ return 'textarea';
516
+ if (['file', 'upload', 'attachment'].includes(normalizedType))
517
+ return 'file';
518
+ if (['array', 'list'].includes(normalizedType)) return 'array';
519
+ if (['object', 'json'].includes(normalizedType)) return 'object';
520
+ if (['currency', 'money', 'price'].includes(normalizedType))
521
+ return 'currency';
522
+ if (['percentage', 'percent'].includes(normalizedType)) return 'percentage';
523
+
524
+ return 'text';
525
+ }
526
+
527
+ /**
528
+ * Converts field name to human-readable label
529
+ */
530
+ private humanizeFieldName(fieldName: string): string {
531
+ return fieldName
532
+ .replace(/([A-Z])/g, ' $1')
533
+ .replace(/_/g, ' ')
534
+ .replace(/\./g, ' ')
535
+ .trim()
536
+ .split(' ')
537
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
538
+ .join(' ');
539
+ }
540
+
541
+ /**
542
+ * Generates a title for the form
543
+ */
544
+ private generateFormTitle(toolName: string): string {
545
+ const cleanName = toolName
546
+ .replace(/Tool$/, '')
547
+ .replace(/Hedera/g, '')
548
+ .replace(/([A-Z])/g, ' $1')
549
+ .trim();
550
+
551
+ return `Complete ${cleanName} Information`;
552
+ }
553
+
554
+ /**
555
+ * Safely extracts ZodObject from a schema, returns null if not an object schema
556
+ */
557
+ private extractZodObject(
558
+ schema: z.ZodSchema
559
+ ): z.ZodObject<z.ZodRawShape> | null {
560
+ try {
561
+ const def = (schema as z.ZodType)._def as { typeName?: string };
562
+ if (def && def.typeName === 'ZodObject') {
563
+ return schema as z.ZodObject<z.ZodRawShape>;
564
+ }
565
+ } catch (error) {
566
+ this.logger.debug('Could not extract ZodObject from schema:', error);
567
+ }
568
+ return null;
569
+ }
570
+
571
+ /**
572
+ * Extracts field names from Zod schema structure
573
+ */
574
+ private extractFieldsFromSchema(schema: z.ZodSchema): string[] {
575
+ const fields: string[] = [];
576
+
577
+ const zodObject = this.extractZodObject(schema);
578
+ if (zodObject) {
579
+ fields.push(...Object.keys(zodObject.shape));
580
+ return fields;
581
+ }
582
+
583
+ try {
584
+ const def = (schema as z.ZodType)._def as {
585
+ typeName?: string;
586
+ options?: z.ZodType[];
587
+ };
588
+ if (def && def.typeName === 'ZodUnion' && def.options) {
589
+ const firstOption = def.options[0];
590
+ const firstOptionObject = this.extractZodObject(firstOption);
591
+ if (firstOptionObject) {
592
+ fields.push(...Object.keys(firstOptionObject.shape));
593
+ }
594
+ }
595
+ } catch (error) {
596
+ this.logger.debug(
597
+ 'Could not extract fields from schema structure:',
598
+ error
599
+ );
600
+ }
601
+
602
+ return fields;
603
+ }
604
+
605
+ /**
606
+ * Infers field type from Zod schema
607
+ */
608
+ private inferTypeFromSchema(
609
+ schema: z.ZodSchema,
610
+ fieldPath: string
611
+ ): FormFieldType | null {
612
+ try {
613
+ const zodObject = this.extractZodObject(schema);
614
+ if (!zodObject) return null;
615
+
616
+ const shape = zodObject.shape;
617
+ if (!shape) return null;
618
+
619
+ let fieldSchema = shape[fieldPath] as z.ZodType | undefined;
620
+ if (!fieldSchema) return null;
621
+
622
+ const fieldDef = fieldSchema._def as {
623
+ typeName?: string;
624
+ innerType?: z.ZodType;
625
+ };
626
+ if (
627
+ fieldDef &&
628
+ fieldDef.typeName === 'ZodOptional' &&
629
+ fieldDef.innerType
630
+ ) {
631
+ fieldSchema = fieldDef.innerType;
632
+ }
633
+
634
+ if (!fieldSchema || !fieldSchema._def) return null;
635
+
636
+ const typeDef = fieldSchema._def as { typeName?: string };
637
+ const fieldTypeName = typeDef.typeName;
638
+ const lowerPath = fieldPath.toLowerCase();
639
+
640
+ switch (fieldTypeName) {
641
+ case 'ZodString':
642
+ if (lowerPath.includes('memo') || lowerPath.includes('description')) {
643
+ return 'textarea';
644
+ }
645
+ return 'text';
646
+ case 'ZodNumber':
647
+ if (lowerPath.includes('percent')) {
648
+ return 'percentage';
649
+ }
650
+ if (lowerPath.includes('price') || lowerPath.includes('cost')) {
651
+ return 'currency';
652
+ }
653
+ return 'number';
654
+ case 'ZodBoolean':
655
+ return 'checkbox';
656
+ case 'ZodEnum':
657
+ case 'ZodNativeEnum':
658
+ return 'select';
659
+ case 'ZodArray':
660
+ return 'array';
661
+ case 'ZodObject':
662
+ return 'object';
663
+ default:
664
+ return 'text';
665
+ }
666
+ } catch (error) {
667
+ this.logger.debug('Could not infer type from schema:', error);
668
+ }
669
+ return null;
670
+ }
671
+
672
+ /**
673
+ * Determines if a field is required based on the Zod schema
674
+ */
675
+ private isFieldRequired(schema?: z.ZodSchema, fieldPath?: string): boolean {
676
+ if (!schema || !fieldPath) {
677
+ return false;
678
+ }
679
+
680
+ try {
681
+ const zodObject = this.extractZodObject(schema);
682
+ if (!zodObject) return false;
683
+
684
+ const shape = zodObject.shape;
685
+ if (!shape || !shape[fieldPath]) return false;
686
+
687
+ const fieldSchema = shape[fieldPath] as z.ZodType;
688
+ if (!fieldSchema || !fieldSchema._def) return true;
689
+
690
+ const fieldDef = fieldSchema._def as {
691
+ typeName?: string;
692
+ defaultValue?: unknown;
693
+ };
694
+ const typeName = fieldDef.typeName;
695
+
696
+ if (typeName === 'ZodOptional') {
697
+ return false;
698
+ }
699
+
700
+ if (typeName === 'ZodDefault') {
701
+ return false;
702
+ }
703
+
704
+ if (fieldDef.defaultValue !== undefined) {
705
+ return false;
706
+ }
707
+
708
+ return true;
709
+ } catch (error) {
710
+ this.logger.debug(
711
+ `Could not determine if field ${fieldPath} is required:`,
712
+ error
713
+ );
714
+ }
715
+
716
+ return false;
717
+ }
718
+
719
+ /**
720
+ * Generates a description for the form
721
+ */
722
+ private generateFormDescription(
723
+ toolName: string,
724
+ fieldCount: number
725
+ ): string {
726
+ if (fieldCount === 0) {
727
+ return 'Please provide the required information to continue with your request.';
728
+ }
729
+
730
+ return `Please provide the following ${fieldCount} required field${
731
+ fieldCount !== 1 ? 's' : ''
732
+ } to continue with your request.`;
733
+ }
734
+
735
+ /**
736
+ * Generates JSON Schema and uiSchema from a Zod schema for use with @rjsf/core
737
+ * @param zodSchema The Zod schema to convert
738
+ * @param partialInput Existing input data to filter out fields that already have values
739
+ * @param missingFields Set of fields that are missing and should be shown
740
+ * @returns Object containing jsonSchema and uiSchema
741
+ */
742
+ public generateJsonSchemaForm(
743
+ zodSchema: z.ZodObject<z.ZodRawShape>,
744
+ partialInput?: Record<string, unknown>,
745
+ missingFields?: Set<string>
746
+ ) {
747
+ const fullJsonSchema = zodToJsonSchema(zodSchema, {
748
+ target: 'jsonSchema7',
749
+ });
750
+
751
+ const uiSchema: Record<string, Record<string, unknown>> = {};
752
+
753
+ let jsonSchema = fullJsonSchema;
754
+ if (missingFields && missingFields.size > 0) {
755
+ const fullSchemaAsObject = fullJsonSchema as {
756
+ properties?: Record<string, unknown>;
757
+ required?: string[];
758
+ [key: string]: unknown;
759
+ };
760
+ if (
761
+ fullSchemaAsObject.properties &&
762
+ typeof fullSchemaAsObject.properties === 'object'
763
+ ) {
764
+ const filteredSchema = {
765
+ ...fullSchemaAsObject,
766
+ type: 'object' as const,
767
+ properties: {} as Record<string, unknown>,
768
+ required: [] as string[],
769
+ };
770
+
771
+ let fieldsAdded = 0;
772
+ missingFields.forEach((fieldName) => {
773
+ if (
774
+ fullSchemaAsObject.properties &&
775
+ fullSchemaAsObject.properties[fieldName]
776
+ ) {
777
+ filteredSchema.properties[fieldName] =
778
+ fullSchemaAsObject.properties[fieldName];
779
+ fieldsAdded++;
780
+ }
781
+ });
782
+
783
+ if (Array.isArray(fullSchemaAsObject.required)) {
784
+ filteredSchema.required = fullSchemaAsObject.required.filter(
785
+ (field: string) => missingFields.has(field)
786
+ );
787
+ }
788
+
789
+ if (fieldsAdded > 0) {
790
+ jsonSchema = filteredSchema;
791
+ }
792
+ }
793
+ }
794
+
795
+ const fieldNames = this.extractFieldsFromSchema(zodSchema);
796
+
797
+ fieldNames.forEach((fieldName) => {
798
+ const isRequired = this.isFieldRequired(zodSchema, fieldName);
799
+ const priority = this.getFieldPriority(fieldName, undefined, isRequired);
800
+ const lower = fieldName.toLowerCase();
801
+
802
+ if (
803
+ lower === 'attributes' ||
804
+ lower === 'metadata' ||
805
+ lower === 'properties'
806
+ ) {
807
+ uiSchema[fieldName] = {
808
+ 'ui:options': {
809
+ collapsible: true,
810
+ collapsed: true,
811
+ },
812
+ };
813
+ }
814
+
815
+ switch (priority) {
816
+ case 'essential':
817
+ if (isRequired) {
818
+ uiSchema[fieldName] = {
819
+ ...uiSchema[fieldName],
820
+ 'ui:help': 'Required field',
821
+ };
822
+ }
823
+ break;
824
+ case 'advanced':
825
+ case 'expert':
826
+ uiSchema[fieldName] = {
827
+ ...uiSchema[fieldName],
828
+ 'ui:options': {
829
+ ...(uiSchema[fieldName]?.['ui:options'] as
830
+ | Record<string, unknown>
831
+ | undefined),
832
+ collapsed: true,
833
+ },
834
+ };
835
+ break;
836
+ }
837
+ });
838
+
839
+ return { jsonSchema, uiSchema };
840
+ }
841
+ }