@hashgraphonline/conversational-agent 0.1.215 → 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 (123) hide show
  1. package/dist/cjs/conversational-agent.d.ts +8 -0
  2. package/dist/cjs/core/ToolRegistry.d.ts +130 -0
  3. package/dist/cjs/execution/ExecutionPipeline.d.ts +81 -0
  4. package/dist/cjs/forms/FormEngine.d.ts +121 -0
  5. package/dist/cjs/forms/form-generator.d.ts +39 -2
  6. package/dist/cjs/forms/types.d.ts +21 -2
  7. package/dist/cjs/index.cjs +1 -1
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.ts +3 -4
  10. package/dist/cjs/langchain/FormAwareAgentExecutor.d.ts +53 -4
  11. package/dist/cjs/langchain/FormValidatingToolWrapper.d.ts +43 -6
  12. package/dist/cjs/langchain-agent.d.ts +49 -0
  13. package/dist/cjs/memory/ContentStorage.d.ts +7 -0
  14. package/dist/cjs/memory/SmartMemoryManager.d.ts +1 -0
  15. package/dist/cjs/services/ContentStoreManager.d.ts +11 -1
  16. package/dist/cjs/utils/ResponseFormatter.d.ts +26 -0
  17. package/dist/esm/index.js +2 -6
  18. package/dist/esm/index12.js +1 -1
  19. package/dist/esm/index12.js.map +1 -1
  20. package/dist/esm/index14.js +23 -5
  21. package/dist/esm/index14.js.map +1 -1
  22. package/dist/esm/index15.js +25 -4
  23. package/dist/esm/index15.js.map +1 -1
  24. package/dist/esm/index16.js +4 -2
  25. package/dist/esm/index16.js.map +1 -1
  26. package/dist/esm/index17.js +2 -7
  27. package/dist/esm/index17.js.map +1 -1
  28. package/dist/esm/index18.js +292 -150
  29. package/dist/esm/index18.js.map +1 -1
  30. package/dist/esm/index19.js +158 -65
  31. package/dist/esm/index19.js.map +1 -1
  32. package/dist/esm/index20.js +94 -270
  33. package/dist/esm/index20.js.map +1 -1
  34. package/dist/esm/index21.js +1 -1
  35. package/dist/esm/index23.js +14 -0
  36. package/dist/esm/index23.js.map +1 -1
  37. package/dist/esm/index24.js +508 -12
  38. package/dist/esm/index24.js.map +1 -1
  39. package/dist/esm/index25.js +1 -1
  40. package/dist/esm/index25.js.map +1 -1
  41. package/dist/esm/index26.js +1 -1
  42. package/dist/esm/index26.js.map +1 -1
  43. package/dist/esm/index27.js +189 -128
  44. package/dist/esm/index27.js.map +1 -1
  45. package/dist/esm/index28.js +164 -45
  46. package/dist/esm/index28.js.map +1 -1
  47. package/dist/esm/index29.js +302 -24
  48. package/dist/esm/index29.js.map +1 -1
  49. package/dist/esm/index30.js +144 -80
  50. package/dist/esm/index30.js.map +1 -1
  51. package/dist/esm/index31.js +63 -7
  52. package/dist/esm/index31.js.map +1 -1
  53. package/dist/esm/index32.js +24 -236
  54. package/dist/esm/index32.js.map +1 -1
  55. package/dist/esm/index33.js +95 -0
  56. package/dist/esm/index33.js.map +1 -0
  57. package/dist/esm/index34.js +245 -0
  58. package/dist/esm/index34.js.map +1 -0
  59. package/dist/esm/index5.js.map +1 -1
  60. package/dist/esm/index6.js +61 -22
  61. package/dist/esm/index6.js.map +1 -1
  62. package/dist/esm/index8.js +653 -131
  63. package/dist/esm/index8.js.map +1 -1
  64. package/dist/types/conversational-agent.d.ts +8 -0
  65. package/dist/types/core/ToolRegistry.d.ts +130 -0
  66. package/dist/types/execution/ExecutionPipeline.d.ts +81 -0
  67. package/dist/types/forms/FormEngine.d.ts +121 -0
  68. package/dist/types/forms/form-generator.d.ts +39 -2
  69. package/dist/types/forms/types.d.ts +21 -2
  70. package/dist/types/index.d.ts +3 -4
  71. package/dist/types/langchain/FormAwareAgentExecutor.d.ts +53 -4
  72. package/dist/types/langchain/FormValidatingToolWrapper.d.ts +43 -6
  73. package/dist/types/langchain-agent.d.ts +49 -0
  74. package/dist/types/memory/ContentStorage.d.ts +7 -0
  75. package/dist/types/memory/SmartMemoryManager.d.ts +1 -0
  76. package/dist/types/services/ContentStoreManager.d.ts +11 -1
  77. package/dist/types/utils/ResponseFormatter.d.ts +26 -0
  78. package/package.json +13 -10
  79. package/src/config/system-message.ts +14 -0
  80. package/src/context/ReferenceContextManager.ts +1 -1
  81. package/src/conversational-agent.ts +91 -36
  82. package/src/core/ToolRegistry.ts +358 -0
  83. package/src/execution/ExecutionPipeline.ts +301 -0
  84. package/src/forms/FormEngine.ts +443 -0
  85. package/src/forms/field-type-registry.ts +1 -13
  86. package/src/forms/form-generator.ts +394 -237
  87. package/src/forms/types.ts +20 -3
  88. package/src/index.ts +6 -10
  89. package/src/langchain/FormAwareAgentExecutor.ts +653 -22
  90. package/src/langchain/FormValidatingToolWrapper.ts +216 -93
  91. package/src/langchain-agent.ts +924 -185
  92. package/src/mcp/ContentProcessor.ts +20 -4
  93. package/src/mcp/MCPClientManager.ts +1 -1
  94. package/src/mcp/adapters/langchain.ts +1 -1
  95. package/src/memory/ContentStorage.ts +25 -5
  96. package/src/memory/SmartMemoryManager.ts +27 -4
  97. package/src/memory/TokenCounter.ts +1 -1
  98. package/src/plugins/hbar/HbarPlugin.ts +0 -1
  99. package/src/scripts/test-external-tool-wrapper.ts +3 -12
  100. package/src/scripts/test-hedera-kit-wrapper.ts +6 -22
  101. package/src/scripts/test-inscribe-form-generation.ts +24 -42
  102. package/src/scripts/test-inscribe-wrapper-verification.ts +1 -7
  103. package/src/services/ContentStoreManager.ts +23 -9
  104. package/src/services/EntityResolver.ts +2 -9
  105. package/src/tools/EntityResolverTool.ts +5 -8
  106. package/src/utils/ResponseFormatter.ts +146 -0
  107. package/dist/cjs/examples/external-tool-wrapper-example.d.ts +0 -131
  108. package/dist/cjs/langchain/ContentAwareAgentExecutor.d.ts +0 -14
  109. package/dist/cjs/langchain/external-tool-wrapper.d.ts +0 -179
  110. package/dist/cjs/scripts/test-external-tool-wrapper.d.ts +0 -5
  111. package/dist/cjs/scripts/test-hedera-kit-wrapper.d.ts +0 -36
  112. package/dist/cjs/scripts/test-inscribe-form-generation.d.ts +0 -15
  113. package/dist/cjs/scripts/test-inscribe-wrapper-verification.d.ts +0 -13
  114. package/dist/types/examples/external-tool-wrapper-example.d.ts +0 -131
  115. package/dist/types/langchain/ContentAwareAgentExecutor.d.ts +0 -14
  116. package/dist/types/langchain/external-tool-wrapper.d.ts +0 -179
  117. package/dist/types/scripts/test-external-tool-wrapper.d.ts +0 -5
  118. package/dist/types/scripts/test-hedera-kit-wrapper.d.ts +0 -36
  119. package/dist/types/scripts/test-inscribe-form-generation.d.ts +0 -15
  120. package/dist/types/scripts/test-inscribe-wrapper-verification.d.ts +0 -13
  121. package/src/examples/external-tool-wrapper-example.ts +0 -227
  122. package/src/langchain/ContentAwareAgentExecutor.ts +0 -19
  123. package/src/langchain/external-tool-wrapper.ts +0 -486
@@ -1,4 +1,5 @@
1
1
  import { z, ZodError } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
2
3
  import type {
3
4
  FormConfig,
4
5
  FormField,
@@ -16,6 +17,8 @@ import {
16
17
  import { Logger } from '@hashgraphonline/standards-sdk';
17
18
  import { fieldTypeRegistry } from './field-type-registry';
18
19
 
20
+ type FieldPriority = 'essential' | 'common' | 'advanced' | 'expert';
21
+
19
22
  /**
20
23
  * Generates forms from Zod validation failures
21
24
  */
@@ -59,32 +62,58 @@ export class FormGenerator {
59
62
  * @param schema The Zod schema to generate a form from
60
63
  * @param partialInput Any partial input already provided
61
64
  * @param context Additional context about the tool
65
+ * @param preCalculatedMissingFields Optional pre-calculated missing fields set. If undefined, includes all fields from schema.
62
66
  * @returns FormMessage to send to the chat UI
63
67
  */
64
68
  async generateFormFromSchema(
65
69
  schema: z.ZodSchema,
66
- partialInput: any,
67
- context: { toolName: string; toolDescription?: string }
70
+ partialInput: unknown,
71
+ context: { toolName: string; toolDescription?: string },
72
+ preCalculatedMissingFields?: Set<string>
68
73
  ): Promise<FormMessage> {
69
- const missingFields = new Set<string>();
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>();
70
93
 
71
- // Identify which fields are missing from the input
72
- if (schema && (schema as any).shape) {
73
- const shape = (schema as any).shape;
74
- for (const fieldName of Object.keys(shape)) {
75
- if (
76
- partialInput[fieldName] === undefined ||
77
- partialInput[fieldName] === ''
78
- ) {
94
+ const zodObject = this.extractZodObject(schema);
95
+ if (zodObject) {
96
+ const shape = zodObject.shape;
97
+ for (const fieldName of Object.keys(shape)) {
79
98
  missingFields.add(fieldName);
99
+ this.logger.info(
100
+ `⭐ Including all fields from focused schema: ${fieldName}`
101
+ );
80
102
  }
81
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
+ });
82
110
  }
83
111
 
84
112
  const formConfig = this.createFormConfig(
85
113
  schema,
86
114
  missingFields,
87
- context.toolName
115
+ context.toolName,
116
+ preCalculatedMissingFields
88
117
  );
89
118
 
90
119
  return {
@@ -113,7 +142,7 @@ export class FormGenerator {
113
142
  */
114
143
  private identifyMissingFields(
115
144
  errors: ValidationError[],
116
- schema: z.ZodSchema
145
+ _schema: z.ZodSchema
117
146
  ): Set<string> {
118
147
  const missingFields = new Set<string>();
119
148
 
@@ -133,7 +162,8 @@ export class FormGenerator {
133
162
  private createFormConfig(
134
163
  schema: z.ZodSchema,
135
164
  missingFields: Set<string>,
136
- toolName: string
165
+ toolName: string,
166
+ preCalculatedMissingFields?: Set<string>
137
167
  ): FormConfig {
138
168
  const extractedConfig = this.extractRenderConfigsSafely(schema);
139
169
  const fieldOrdering = this.generateFieldOrderingSafely(schema);
@@ -141,7 +171,8 @@ export class FormGenerator {
141
171
  schema,
142
172
  extractedConfig,
143
173
  missingFields,
144
- fieldOrdering
174
+ fieldOrdering,
175
+ preCalculatedMissingFields
145
176
  );
146
177
 
147
178
  return {
@@ -165,7 +196,7 @@ export class FormGenerator {
165
196
  ): ExtractedRenderConfig {
166
197
  try {
167
198
  if (typeof extractRenderConfigs === 'function') {
168
- return extractRenderConfigs(schema as ZodSchemaWithRender);
199
+ return extractRenderConfigs(schema as unknown as ZodSchemaWithRender);
169
200
  }
170
201
  } catch (error) {
171
202
  this.logger.warn('Could not extract render configs:', error);
@@ -186,8 +217,10 @@ export class FormGenerator {
186
217
  } {
187
218
  try {
188
219
  if (typeof generateFieldOrdering === 'function') {
189
- const ordering = generateFieldOrdering(schema as ZodSchemaWithRender);
190
- // Convert to expected format
220
+ const ordering = generateFieldOrdering(
221
+ schema as unknown as ZodSchemaWithRender
222
+ );
223
+
191
224
  const sections = Object.values(ordering.sections).map((section) => ({
192
225
  fields: section.fields,
193
226
  }));
@@ -200,78 +233,116 @@ export class FormGenerator {
200
233
  }
201
234
 
202
235
  /**
203
- * Generates form fields from schema and validation errors
236
+ * Determines field priority for progressive disclosure
204
237
  */
205
- private generateFormFields(
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(
206
270
  schema: z.ZodSchema,
207
- extractedConfig: ExtractedRenderConfig,
208
271
  missingFields: Set<string>,
209
- fieldOrdering: { sections: Array<{ fields: string[] }> }
210
- ): FormField[] {
211
- const fields: FormField[] = [];
272
+ preCalculatedMissingFields?: Set<string>
273
+ ): Set<string> {
274
+ const fieldsToInclude = new Set<string>();
212
275
 
213
- // Determine field priority for progressive disclosure
214
- const getFieldPriority = (
215
- name: string
216
- ): 'essential' | 'common' | 'advanced' | 'expert' => {
217
- const essentialFields = [
218
- 'tokenSymbol',
219
- 'treasuryAccountId',
220
- 'maxSupply',
221
- 'tokenName',
222
- 'name',
223
- 'description',
224
- 'contentRef',
225
- ];
226
- const commonFields = ['creator', 'type', 'fileStandard', 'supplyType'];
227
- const expertFields = [
228
- 'apiKey',
229
- 'chunkSize',
230
- 'timeoutMs',
231
- 'quoteOnly',
232
- 'waitForConfirmation',
233
- ];
234
-
235
- if (essentialFields.includes(name)) return 'essential';
236
- if (commonFields.includes(name)) return 'common';
237
- if (expertFields.includes(name)) return 'expert';
238
-
239
- // Keys and technical fields are advanced
240
- if (
241
- name.toLowerCase().includes('key') ||
242
- name.includes('Period') ||
243
- name.includes('Fees') ||
244
- name.includes('autoRenew')
245
- ) {
246
- return 'advanced';
247
- }
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
+ }
248
300
 
249
- return 'advanced'; // Default to advanced for unknown fields
250
- };
301
+ return fieldsToInclude;
302
+ }
251
303
 
252
- // For progressive disclosure, include only required fields and essential fields that are missing
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 {
253
312
  const allSchemaFields = this.extractFieldsFromSchema(schema);
254
- const fieldsToInclude = new Set<string>();
255
-
256
313
  allSchemaFields.forEach((fieldName) => {
257
- const priority = getFieldPriority(fieldName);
258
314
  const isRequired = this.isFieldRequired(schema, fieldName);
315
+ const isMissing = missingFields.has(fieldName);
316
+ const shouldInclude = isMissing && isRequired;
259
317
 
260
- // Include field if it's missing and required, or if it's essential (so users see key fields)
261
- if (
262
- (missingFields.has(fieldName) && isRequired) ||
263
- priority === 'essential'
264
- ) {
318
+ this.logger.info(`🔍 FormGenerator field inclusion check: ${fieldName}`, {
319
+ isRequired,
320
+ isMissing,
321
+ shouldInclude,
322
+ });
323
+
324
+ if (shouldInclude) {
265
325
  fieldsToInclude.add(fieldName);
266
326
  }
267
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>();
268
341
 
269
- // If we have field ordering from render configs, use that first
270
342
  if (fieldOrdering.sections.length > 0) {
271
343
  const orderedFieldNames = fieldOrdering.sections.flatMap((s) => s.fields);
272
-
273
344
  orderedFieldNames.forEach((fieldName) => {
274
- if (fieldsToInclude.has(fieldName)) {
345
+ if (fieldsToInclude.has(fieldName) && !processedFields.has(fieldName)) {
275
346
  const field = this.createFormField(
276
347
  fieldName,
277
348
  extractedConfig.fields[fieldName],
@@ -280,15 +351,14 @@ export class FormGenerator {
280
351
  );
281
352
  if (field) {
282
353
  fields.push(field);
354
+ processedFields.add(fieldName);
283
355
  }
284
356
  }
285
357
  });
286
358
  }
287
359
 
288
- // Add any remaining fields not in ordering
289
360
  fieldsToInclude.forEach((fieldName) => {
290
- // Don't duplicate fields already added
291
- if (!fields.some((f) => f.name === fieldName)) {
361
+ if (!processedFields.has(fieldName)) {
292
362
  const field = this.createFormField(
293
363
  fieldName,
294
364
  extractedConfig.fields[fieldName],
@@ -301,19 +371,45 @@ export class FormGenerator {
301
371
  }
302
372
  });
303
373
 
304
- // If still no fields (shouldn't happen), fall back to missing fields
305
- if (fields.length === 0) {
306
- missingFields.forEach((fieldName) => {
307
- const field = this.createFormField(
308
- fieldName,
309
- extractedConfig.fields[fieldName],
310
- schema,
311
- fieldName
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
312
412
  );
313
- if (field) {
314
- fields.push(field);
315
- }
316
- });
317
413
  }
318
414
 
319
415
  return fields;
@@ -329,47 +425,6 @@ export class FormGenerator {
329
425
  fieldPath?: string
330
426
  ): FormField {
331
427
  const type = this.mapFieldType(renderConfig?.fieldType, schema, fieldPath);
332
-
333
- // Determine field priority for progressive disclosure
334
- const getFieldPriority = (
335
- name: string
336
- ): 'essential' | 'common' | 'advanced' | 'expert' => {
337
- const essentialFields = [
338
- 'tokenSymbol',
339
- 'treasuryAccountId',
340
- 'maxSupply',
341
- 'tokenName',
342
- 'name',
343
- 'description',
344
- 'contentRef',
345
- ];
346
- const commonFields = ['creator', 'type', 'fileStandard', 'supplyType'];
347
- const expertFields = [
348
- 'apiKey',
349
- 'chunkSize',
350
- 'timeoutMs',
351
- 'quoteOnly',
352
- 'waitForConfirmation',
353
- ];
354
-
355
- if (essentialFields.includes(name)) return 'essential';
356
- if (commonFields.includes(name)) return 'common';
357
- if (expertFields.includes(name)) return 'expert';
358
-
359
- // Keys and technical fields are advanced
360
- if (
361
- name.toLowerCase().includes('key') ||
362
- name.includes('Period') ||
363
- name.includes('Fees') ||
364
- name.includes('autoRenew')
365
- ) {
366
- return 'advanced';
367
- }
368
-
369
- return 'advanced'; // Default to advanced for unknown fields
370
- };
371
-
372
- // Determine if field is required from schema
373
428
  const isRequired = this.isFieldRequired(schema, fieldPath || fieldName);
374
429
 
375
430
  const field: FormField = {
@@ -377,7 +432,7 @@ export class FormGenerator {
377
432
  label: renderConfig?.ui?.label || this.humanizeFieldName(fieldName),
378
433
  type,
379
434
  required: isRequired,
380
- priority: getFieldPriority(fieldName),
435
+ priority: this.getFieldPriority(fieldName, renderConfig, isRequired),
381
436
  };
382
437
 
383
438
  if (renderConfig) {
@@ -429,7 +484,6 @@ export class FormGenerator {
429
484
  schema?: z.ZodSchema,
430
485
  fieldPath?: string
431
486
  ): FormFieldType {
432
- // First try schema inference if no explicit fieldType
433
487
  if (!fieldType && schema && fieldPath) {
434
488
  const inferredType = this.inferTypeFromSchema(schema, fieldPath);
435
489
  if (inferredType) {
@@ -437,7 +491,6 @@ export class FormGenerator {
437
491
  }
438
492
  }
439
493
 
440
- // If no fieldType and schema inference failed, use registry pattern detection
441
494
  if (!fieldType && fieldPath) {
442
495
  const registryType = fieldTypeRegistry.detectType(fieldPath);
443
496
  if (registryType) {
@@ -498,24 +551,45 @@ export class FormGenerator {
498
551
  return `Complete ${cleanName} Information`;
499
552
  }
500
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
+
501
571
  /**
502
572
  * Extracts field names from Zod schema structure
503
573
  */
504
574
  private extractFieldsFromSchema(schema: z.ZodSchema): string[] {
505
575
  const fields: string[] = [];
506
576
 
577
+ const zodObject = this.extractZodObject(schema);
578
+ if (zodObject) {
579
+ fields.push(...Object.keys(zodObject.shape));
580
+ return fields;
581
+ }
582
+
507
583
  try {
508
- if (schema && (schema as any)._def) {
509
- const def = (schema as any)._def;
510
-
511
- if (def.typeName === 'ZodObject' && def.shape) {
512
- fields.push(...Object.keys(def.shape));
513
- } else if (def.typeName === 'ZodUnion' && def.options) {
514
- // For unions, try to extract from the first option
515
- const firstOption = def.options[0];
516
- if (firstOption && firstOption._def && firstOption._def.shape) {
517
- fields.push(...Object.keys(firstOption._def.shape));
518
- }
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));
519
593
  }
520
594
  }
521
595
  } catch (error) {
@@ -536,67 +610,58 @@ export class FormGenerator {
536
610
  fieldPath: string
537
611
  ): FormFieldType | null {
538
612
  try {
539
- const def = (schema as any)._def;
613
+ const zodObject = this.extractZodObject(schema);
614
+ if (!zodObject) return null;
540
615
 
541
- if (def && def.typeName === 'ZodObject') {
542
- let shape;
543
- if (typeof def.shape === 'function') {
544
- shape = def.shape();
545
- } else {
546
- shape = def.shape;
547
- }
616
+ const shape = zodObject.shape;
617
+ if (!shape) return null;
548
618
 
549
- if (shape) {
550
- let fieldSchema = shape[fieldPath];
619
+ let fieldSchema = shape[fieldPath] as z.ZodType | undefined;
620
+ if (!fieldSchema) return null;
551
621
 
552
- // Handle optional fields by unwrapping ZodOptional
553
- if (
554
- fieldSchema &&
555
- fieldSchema._def &&
556
- fieldSchema._def.typeName === 'ZodOptional'
557
- ) {
558
- fieldSchema = fieldSchema._def.innerType;
559
- }
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;
560
635
 
561
- if (fieldSchema && fieldSchema._def) {
562
- const fieldTypeName = fieldSchema._def.typeName;
563
-
564
- switch (fieldTypeName) {
565
- case 'ZodString':
566
- // Check for special string patterns
567
- if (
568
- fieldPath.toLowerCase().includes('memo') ||
569
- fieldPath.toLowerCase().includes('description')
570
- ) {
571
- return 'textarea';
572
- }
573
- return 'text';
574
- case 'ZodNumber':
575
- // Check for special number patterns
576
- if (fieldPath.toLowerCase().includes('percent')) {
577
- return 'percentage';
578
- }
579
- if (
580
- fieldPath.toLowerCase().includes('price') ||
581
- fieldPath.toLowerCase().includes('cost')
582
- ) {
583
- return 'currency';
584
- }
585
- return 'number';
586
- case 'ZodBoolean':
587
- return 'checkbox';
588
- case 'ZodEnum':
589
- case 'ZodNativeEnum':
590
- return 'select';
591
- case 'ZodArray':
592
- return 'array';
593
- case 'ZodObject':
594
- return 'object';
595
- default:
596
- return 'text';
597
- }
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';
598
644
  }
599
- }
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';
600
665
  }
601
666
  } catch (error) {
602
667
  this.logger.debug('Could not infer type from schema:', error);
@@ -609,53 +674,38 @@ export class FormGenerator {
609
674
  */
610
675
  private isFieldRequired(schema?: z.ZodSchema, fieldPath?: string): boolean {
611
676
  if (!schema || !fieldPath) {
612
- return false; // If we can't determine, assume optional
677
+ return false;
613
678
  }
614
679
 
615
680
  try {
616
- const def = (schema as any)._def;
681
+ const zodObject = this.extractZodObject(schema);
682
+ if (!zodObject) return false;
617
683
 
618
- if (def && def.typeName === 'ZodObject') {
619
- let shape;
620
- if (typeof def.shape === 'function') {
621
- shape = def.shape();
622
- } else {
623
- shape = def.shape;
624
- }
684
+ const shape = zodObject.shape;
685
+ if (!shape || !shape[fieldPath]) return false;
625
686
 
626
- if (shape && shape[fieldPath]) {
627
- const fieldSchema = shape[fieldPath];
687
+ const fieldSchema = shape[fieldPath] as z.ZodType;
688
+ if (!fieldSchema || !fieldSchema._def) return true;
628
689
 
629
- // Check if field is wrapped in ZodOptional
630
- if (
631
- fieldSchema &&
632
- fieldSchema._def &&
633
- fieldSchema._def.typeName === 'ZodOptional'
634
- ) {
635
- return false; // Optional field
636
- }
690
+ const fieldDef = fieldSchema._def as {
691
+ typeName?: string;
692
+ defaultValue?: unknown;
693
+ };
694
+ const typeName = fieldDef.typeName;
637
695
 
638
- // Check if field has a default value (making it effectively optional)
639
- if (
640
- fieldSchema &&
641
- fieldSchema._def &&
642
- fieldSchema._def.defaultValue !== undefined
643
- ) {
644
- return false; // Has default, so optional
645
- }
696
+ if (typeName === 'ZodOptional') {
697
+ return false;
698
+ }
646
699
 
647
- // Check for ZodDefault wrapper
648
- if (
649
- fieldSchema &&
650
- fieldSchema._def &&
651
- fieldSchema._def.typeName === 'ZodDefault'
652
- ) {
653
- return false; // Has default, so optional
654
- }
700
+ if (typeName === 'ZodDefault') {
701
+ return false;
702
+ }
655
703
 
656
- return true; // Required field
657
- }
704
+ if (fieldDef.defaultValue !== undefined) {
705
+ return false;
658
706
  }
707
+
708
+ return true;
659
709
  } catch (error) {
660
710
  this.logger.debug(
661
711
  `Could not determine if field ${fieldPath} is required:`,
@@ -663,7 +713,7 @@ export class FormGenerator {
663
713
  );
664
714
  }
665
715
 
666
- return false; // Default to optional if we can't determine
716
+ return false;
667
717
  }
668
718
 
669
719
  /**
@@ -681,4 +731,111 @@ export class FormGenerator {
681
731
  fieldCount !== 1 ? 's' : ''
682
732
  } to continue with your request.`;
683
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
+ }
684
841
  }