@hashgraphonline/conversational-agent 0.2.1 → 0.2.101

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 (63) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/conversational-agent.d.ts +11 -1
  3. package/dist/cjs/index.cjs +1 -1
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/services/attachment-processor.d.ts +41 -0
  6. package/dist/cjs/services/index.d.ts +2 -0
  7. package/dist/cjs/services/parameter-service.d.ts +43 -0
  8. package/dist/cjs/tools/entity-resolver-tool.d.ts +5 -3
  9. package/dist/esm/index.js +9 -5
  10. package/dist/esm/index.js.map +1 -1
  11. package/dist/esm/index10.js +2 -2
  12. package/dist/esm/index21.js +1 -1
  13. package/dist/esm/index23.js +3 -3
  14. package/dist/esm/index24.js +20 -4
  15. package/dist/esm/index24.js.map +1 -1
  16. package/dist/esm/index29.js +248 -903
  17. package/dist/esm/index29.js.map +1 -1
  18. package/dist/esm/index30.js +98 -219
  19. package/dist/esm/index30.js.map +1 -1
  20. package/dist/esm/index31.js +834 -1085
  21. package/dist/esm/index31.js.map +1 -1
  22. package/dist/esm/index32.js +228 -115
  23. package/dist/esm/index32.js.map +1 -1
  24. package/dist/esm/index33.js +1185 -79
  25. package/dist/esm/index33.js.map +1 -1
  26. package/dist/esm/index34.js +119 -39
  27. package/dist/esm/index34.js.map +1 -1
  28. package/dist/esm/index35.js +103 -96
  29. package/dist/esm/index35.js.map +1 -1
  30. package/dist/esm/index36.js +46 -21
  31. package/dist/esm/index36.js.map +1 -1
  32. package/dist/esm/index37.js +107 -12
  33. package/dist/esm/index37.js.map +1 -1
  34. package/dist/esm/index38.js +21 -7
  35. package/dist/esm/index38.js.map +1 -1
  36. package/dist/esm/index39.js +4 -26
  37. package/dist/esm/index39.js.map +1 -1
  38. package/dist/esm/index40.js +11 -4
  39. package/dist/esm/index40.js.map +1 -1
  40. package/dist/esm/index41.js +1 -1
  41. package/dist/esm/index43.js +24 -89
  42. package/dist/esm/index43.js.map +1 -1
  43. package/dist/esm/index44.js +10 -0
  44. package/dist/esm/index44.js.map +1 -0
  45. package/dist/esm/index45.js +95 -0
  46. package/dist/esm/index45.js.map +1 -0
  47. package/dist/esm/index5.js +2 -2
  48. package/dist/esm/index6.js +76 -6
  49. package/dist/esm/index6.js.map +1 -1
  50. package/dist/esm/index8.js +1 -1
  51. package/dist/types/conversational-agent.d.ts +11 -1
  52. package/dist/types/services/attachment-processor.d.ts +41 -0
  53. package/dist/types/services/index.d.ts +2 -0
  54. package/dist/types/services/parameter-service.d.ts +43 -0
  55. package/dist/types/tools/entity-resolver-tool.d.ts +5 -3
  56. package/package.json +2 -1
  57. package/src/conversational-agent.ts +97 -5
  58. package/src/langchain/langchain-agent.ts +9 -1
  59. package/src/services/attachment-processor.ts +163 -0
  60. package/src/services/content-store-manager.ts +32 -4
  61. package/src/services/index.ts +2 -0
  62. package/src/services/parameter-service.ts +430 -0
  63. package/src/tools/entity-resolver-tool.ts +12 -18
@@ -1,964 +1,309 @@
1
- import { AgentExecutor } from "langchain/agents";
2
- import { z, ZodError } from "zod";
3
- import { FormGenerator } from "./index10.js";
4
- import { FormEngine } from "./index11.js";
5
1
  import { Logger } from "@hashgraphonline/standards-sdk";
6
- import { isFormValidatable } from "@hashgraphonline/standards-agent-kit";
7
- import { ResponseFormatter } from "./index33.js";
8
- const globalPendingForms = /* @__PURE__ */ new Map();
9
- class FormAwareAgentExecutor extends AgentExecutor {
10
- constructor(...args) {
11
- super(...args);
12
- this.pendingForms = /* @__PURE__ */ new Map();
13
- this.formGenerator = new FormGenerator();
14
- this.formEngine = new FormEngine(
15
- new Logger({ module: "FormAwareAgentExecutor.FormEngine" })
16
- );
17
- this.formLogger = new Logger({ module: "FormAwareAgentExecutor" });
18
- this.parameterPreprocessingCallback = void 0;
2
+ import { EntityFormat } from "./index26.js";
3
+ import "./index28.js";
4
+ class ParameterService {
5
+ constructor(formatConverterRegistry, networkType) {
6
+ this.logger = new Logger({ module: "ParameterService" });
7
+ this.formatConverterRegistry = formatConverterRegistry;
8
+ this.networkType = networkType;
19
9
  }
20
10
  /**
21
- * Type guard to check if a Zod type is a ZodObject
11
+ * Unified preprocessing entrypoint (DRY):
12
+ * - Optional AI-driven resolution via provided entityResolver
13
+ * - Deterministic post-pass for safe format enforcement
22
14
  */
23
- isZodObject(schema) {
24
- return schema instanceof z.ZodObject;
25
- }
26
- /**
27
- * Type guard to check if metadata has hashLinkBlock
28
- */
29
- hasHashLinkBlock(metadata) {
30
- return typeof metadata === "object" && metadata !== null && "hashLinkBlock" in metadata && typeof metadata.hashLinkBlock === "object" && metadata.hashLinkBlock !== null;
31
- }
32
- /**
33
- * Set parameter preprocessing callback
34
- */
35
- setParameterPreprocessingCallback(callback) {
36
- this.parameterPreprocessingCallback = callback;
37
- }
38
- /**
39
- * BULLETPROOF TOOL INTERCEPTION
40
- * Override the single-step execution to intercept tool calls BEFORE LangChain processes them
41
- */
42
- async _takeNextStep(nameToolMap, inputs, intermediateSteps, runManager, config) {
43
- this.formLogger.info("🛡️ BULLETPROOF INTERCEPTION: _takeNextStep called", {
44
- availableTools: Object.keys(nameToolMap),
45
- inputKeys: Object.keys(inputs)
46
- });
47
- const result = await this.agent.plan(
48
- intermediateSteps,
49
- inputs,
50
- runManager?.getChild()
51
- );
52
- if ("returnValues" in result) {
53
- this.formLogger.info("Agent returned finish action, passing through");
54
- return result;
55
- }
56
- const action = result;
57
- const toolName = action.tool;
58
- const toolInput = action.toolInput;
59
- this.formLogger.info(`🎯 INTERCEPTING TOOL CALL: ${toolName}`, {
60
- toolInput,
61
- hasInNameToolMap: toolName in nameToolMap,
62
- toolInputKeys: Object.keys(toolInput || {})
63
- });
64
- const tool = nameToolMap[toolName] || this.tools.find((t) => t.name === toolName);
65
- if (!tool) {
66
- this.formLogger.error(`Tool ${toolName} not found in registry`);
67
- throw new Error(`Tool "${toolName}" not found`);
68
- }
69
- let shouldGenerateForm = false;
70
- if (isFormValidatable(tool)) {
71
- this.formLogger.info(
72
- `🔍 Tool ${toolName} implements FormValidatable, checking shouldGenerateForm()`,
73
- {
74
- toolInput
75
- }
76
- );
77
- try {
78
- shouldGenerateForm = tool.shouldGenerateForm(toolInput);
79
- this.formLogger.info(
80
- `FormValidatable.shouldGenerateForm() result: ${shouldGenerateForm}`,
81
- {
82
- toolName,
83
- toolInput
84
- }
85
- );
86
- } catch (error) {
87
- this.formLogger.error(
88
- `Error calling shouldGenerateForm() on ${toolName}:`,
89
- error
90
- );
91
- shouldGenerateForm = false;
92
- }
93
- }
94
- if (shouldGenerateForm) {
95
- this.formLogger.info(`🚨 FORM GENERATION TRIGGERED for ${toolName}`);
15
+ async preprocessParameters(toolName, parameters, entities = [], options) {
16
+ const sessionId = options?.sessionId;
17
+ const entityResolver = options?.entityResolver;
18
+ const preferences = options?.preferences;
19
+ let working = { ...parameters };
20
+ if (entityResolver && entities.length > 0) {
96
21
  try {
97
- let schemaToUse;
98
- let isFocusedSchema = false;
99
- if (isFormValidatable(tool)) {
100
- this.formLogger.info(
101
- `🎯 Tool ${toolName} is FormValidatable, attempting to get focused schema`
102
- );
103
- try {
104
- const focusedSchema = tool.getFormSchema();
105
- if (focusedSchema) {
106
- schemaToUse = focusedSchema;
107
- isFocusedSchema = true;
108
- this.formLogger.info(
109
- `✅ Successfully obtained focused schema for ${toolName}`
110
- );
111
- } else {
112
- this.formLogger.warn(
113
- `getFormSchema() returned null/undefined for ${toolName}, using default schema`
114
- );
115
- schemaToUse = tool.schema;
116
- isFocusedSchema = false;
117
- }
118
- } catch (error) {
119
- this.formLogger.error(
120
- `Failed to get focused schema from ${toolName}:`,
121
- error
22
+ this.logger.info("AI-driven preprocessing phase", {
23
+ toolName,
24
+ entityCount: entities.length,
25
+ sessionId
26
+ });
27
+ const aiProcessed = { ...working };
28
+ for (const [paramName, paramValue] of Object.entries(working)) {
29
+ if (typeof paramValue === "string") {
30
+ const resolved = await entityResolver.resolveReferences(
31
+ paramValue,
32
+ entities
122
33
  );
123
- this.formLogger.info(
124
- `Falling back to default schema for ${toolName}`
34
+ const converted = await this.convertParameterEntities(
35
+ resolved,
36
+ entities,
37
+ preferences
125
38
  );
126
- schemaToUse = tool.schema;
127
- isFocusedSchema = false;
128
- }
129
- } else {
130
- this.formLogger.info(
131
- `Tool ${toolName} is not FormValidatable, using default schema`
132
- );
133
- schemaToUse = tool.schema;
134
- isFocusedSchema = false;
135
- }
136
- let schemaFieldCount = "unknown";
137
- try {
138
- if (this.isZodObject(schemaToUse)) {
139
- const zodObject = schemaToUse;
140
- const shape = zodObject.shape;
141
- if (shape && typeof shape === "object") {
142
- schemaFieldCount = Object.keys(shape).length.toString();
143
- }
144
- }
145
- } catch {
146
- }
147
- this.formLogger.info(
148
- `📋 Generating form with ${isFocusedSchema ? "FOCUSED" : "DEFAULT"} schema`,
149
- {
150
- toolName,
151
- schemaType: schemaToUse?.constructor?.name,
152
- estimatedFieldCount: schemaFieldCount,
153
- isFocusedSchema
154
- }
155
- );
156
- let missingFields;
157
- if (isFocusedSchema) {
158
- this.formLogger.info(
159
- `⭐ Using focused schema - letting FormGenerator determine fields from schema`
160
- );
161
- missingFields = void 0;
162
- } else {
163
- missingFields = /* @__PURE__ */ new Set();
164
- if (this.isZodObject(schemaToUse)) {
165
- const zodObject = schemaToUse;
166
- const shape = zodObject.shape || {};
167
- for (const fieldName of Object.keys(shape)) {
168
- const value = (toolInput || {})[fieldName];
169
- const isEmpty = isFormValidatable(tool) && tool.isFieldEmpty ? tool.isFieldEmpty(fieldName, value) : value === void 0 || value === "" || value === null || Array.isArray(value) && value.length === 0;
170
- const isRequired = this.isFieldRequired(schemaToUse, fieldName);
171
- const isEssential = isFormValidatable(tool) && tool.getEssentialFields ? tool.getEssentialFields().includes(fieldName) : false;
172
- this.formLogger.info(`🔍 Field analysis: ${fieldName}`, {
173
- value,
174
- isEmpty,
175
- isRequired,
176
- isEssential,
177
- willAddToMissingFields: isEmpty && (isRequired || isEssential)
178
- });
179
- if (isEmpty && (isRequired || isEssential)) {
180
- missingFields.add(fieldName);
39
+ aiProcessed[paramName] = converted;
40
+ } else if (Array.isArray(paramValue)) {
41
+ const out = [];
42
+ for (const item of paramValue) {
43
+ if (typeof item === "string") {
44
+ const resolved = await entityResolver.resolveReferences(
45
+ item,
46
+ entities
47
+ );
48
+ const converted = await this.convertParameterEntities(
49
+ resolved,
50
+ entities,
51
+ preferences
52
+ );
53
+ out.push(converted);
54
+ } else {
55
+ out.push(item);
181
56
  }
182
57
  }
183
- }
184
- this.formLogger.info(`📋 Missing fields analysis complete`, {
185
- totalFields: this.isZodObject(schemaToUse) ? Object.keys(schemaToUse.shape).length : 0,
186
- missingFieldsCount: missingFields.size,
187
- missingFields: Array.from(missingFields)
188
- });
189
- }
190
- const formMessage = await this.formGenerator.generateFormFromSchema(
191
- schemaToUse,
192
- toolInput,
193
- {
194
- toolName,
195
- toolDescription: tool.description
196
- },
197
- missingFields
198
- // Pass undefined for focused schemas, Set<string> for others
199
- );
200
- if (this.isZodObject(schemaToUse)) {
201
- try {
202
- const { jsonSchema, uiSchema } = this.formGenerator.generateJsonSchemaForm(
203
- schemaToUse,
204
- toolInput,
205
- missingFields
206
- );
207
- formMessage.jsonSchema = jsonSchema;
208
- formMessage.uiSchema = uiSchema;
209
- } catch (error) {
210
- this.formLogger.warn(
211
- "Failed to generate JSON Schema for RJSF:",
212
- error
213
- );
58
+ aiProcessed[paramName] = out;
214
59
  }
215
60
  }
216
- formMessage.partialInput = toolInput;
217
- const formData = {
218
- toolName,
219
- originalInput: inputs,
220
- originalToolInput: toolInput,
221
- schema: schemaToUse,
222
- toolRef: tool,
223
- originalToolRef: tool.originalTool
224
- };
225
- this.pendingForms.set(formMessage.id, formData);
226
- globalPendingForms.set(formMessage.id, formData);
227
- this.formLogger.info(`✅ FORM INTERCEPT SUCCESS for ${toolName}`);
228
- const formResult = {
229
- requiresForm: true,
230
- formMessage
231
- };
232
- return [
233
- {
234
- action,
235
- observation: JSON.stringify(formResult)
236
- }
237
- ];
61
+ working = aiProcessed;
238
62
  } catch (error) {
239
- this.formLogger.error(`Form generation failed for ${toolName}:`, error);
240
- }
241
- }
242
- this.formLogger.info(
243
- `⚪ Passing through to normal tool execution for ${toolName}`
244
- );
245
- if (this.parameterPreprocessingCallback && toolInput) {
246
- this.formLogger.info(
247
- `🔄 Applying parameter preprocessing for ${toolName}`
248
- );
249
- try {
250
- const preprocessedInput = await this.parameterPreprocessingCallback(
251
- toolName,
252
- toolInput
253
- );
254
- if (preprocessedInput && typeof preprocessedInput === "object" && "__requestForm" in preprocessedInput) {
255
- const rf = preprocessedInput.__requestForm;
256
- const formId = rf.id || `form_${Date.now()}_${Math.random().toString(36).slice(2)}`;
257
- const formMessage = {
258
- type: "form",
259
- id: formId,
260
- originalPrompt: "Parameter validation required",
261
- toolName,
262
- formConfig: {
263
- title: rf.title || "Complete required parameters",
264
- description: rf.description || "One or more parameters require confirmation. Please review and submit.",
265
- submitLabel: rf.submitLabel || "Continue",
266
- fields: (rf.fields || []).map((f) => {
267
- const allowedTypes = [
268
- "text",
269
- "number",
270
- "select",
271
- "checkbox",
272
- "textarea"
273
- ];
274
- const resolvedType = allowedTypes.includes(
275
- f.type
276
- ) ? f.type : "text";
277
- return {
278
- name: f.name,
279
- label: f.label,
280
- type: resolvedType,
281
- required: f.required ?? true,
282
- options: f.options
283
- };
284
- })
285
- }
286
- };
287
- const resolvedSchema = isFormValidatable(tool) ? (() => {
288
- try {
289
- const s = tool.getFormSchema();
290
- return s || tool.schema;
291
- } catch {
292
- return tool.schema;
293
- }
294
- })() : tool.schema;
295
- this.pendingForms.set(formId, {
296
- toolName,
297
- originalInput: inputs,
298
- originalToolInput: toolInput,
299
- schema: resolvedSchema,
300
- toolRef: tool,
301
- originalToolRef: tool.originalTool
302
- });
303
- globalPendingForms.set(formId, {
63
+ const message = error instanceof Error ? error.message : "unknown";
64
+ this.logger.warn(
65
+ "AI phase failed; continuing with deterministic pass",
66
+ {
304
67
  toolName,
305
- originalInput: inputs,
306
- originalToolInput: toolInput,
307
- schema: resolvedSchema
308
- });
309
- return [
310
- {
311
- action,
312
- observation: JSON.stringify({ requiresForm: true, formMessage })
313
- }
314
- ];
315
- }
316
- if (JSON.stringify(preprocessedInput) !== JSON.stringify(toolInput)) {
317
- this.formLogger.info(`📝 Parameters preprocessed for ${toolName}:`, {
318
- original: Object.keys(toolInput),
319
- preprocessed: Object.keys(preprocessedInput),
320
- hasChanges: true
321
- });
322
- try {
323
- action.toolInput = preprocessedInput;
324
- } catch {
68
+ error: message
325
69
  }
326
- } else {
327
- this.formLogger.debug(`No parameter changes needed for ${toolName}`);
328
- }
329
- } catch (preprocessError) {
330
- this.formLogger.warn(
331
- `Parameter preprocessing failed for ${toolName}, using original parameters:`,
332
- preprocessError
333
70
  );
334
71
  }
335
72
  }
336
- return super._takeNextStep(
337
- nameToolMap,
338
- inputs,
339
- intermediateSteps,
340
- runManager,
341
- config
342
- );
343
- }
344
- /**
345
- * Helper to determine if a field is required in the schema
346
- */
347
- isFieldRequired(schema, fieldPath) {
348
- if (!schema || !fieldPath) {
349
- return false;
350
- }
351
- try {
352
- const obj = schema;
353
- const def = obj._def;
354
- if (!def || def.typeName !== "ZodObject") {
355
- return false;
356
- }
357
- const rawShape = typeof def.shape === "function" ? def.shape() : def.shape;
358
- if (!rawShape || typeof rawShape !== "object") {
359
- return false;
360
- }
361
- const shape = rawShape;
362
- const fieldSchema = shape[fieldPath];
363
- if (!fieldSchema) {
364
- return false;
365
- }
366
- const unwrapOptional = (s) => {
367
- const inner = s._def;
368
- if (inner && inner.typeName === "ZodOptional" && inner.innerType) {
369
- return inner.innerType;
370
- }
371
- return s;
372
- };
373
- const unwrapped = unwrapOptional(fieldSchema);
374
- const fdef = unwrapped._def;
375
- if (!fdef) {
376
- return true;
377
- }
378
- if (fdef.typeName === "ZodOptional" || fdef.typeName === "ZodDefault") {
379
- return false;
380
- }
381
- if (fdef.defaultValue !== void 0) {
382
- return false;
383
- }
384
- return true;
385
- } catch (error) {
386
- this.formLogger.debug(
387
- `Could not determine if field ${fieldPath} is required:`,
388
- error
389
- );
390
- }
391
- return false;
392
- }
393
- /**
394
- * Override _call to intercept Zod validation errors at the execution level
395
- */
396
- async _call(inputs) {
397
73
  try {
398
- const result = await super._call(inputs);
399
- if (result.intermediateSteps && Array.isArray(result.intermediateSteps)) {
400
- for (const step of result.intermediateSteps) {
401
- if (step.observation) {
402
- try {
403
- const parsed = typeof step.observation === "string" ? JSON.parse(step.observation) : step.observation;
404
- if (parsed.requiresForm && parsed.formMessage) {
405
- this.formLogger.info("Tool requested form generation", {
406
- toolName: step.action?.tool,
407
- hasForm: true
408
- });
409
- const actionToolName = step.action?.tool || "unknown";
410
- const toolInstance = this.tools.find(
411
- (t) => t.name === actionToolName
412
- );
413
- const originalToolCandidate = toolInstance || {};
414
- const pf = {
415
- toolName: actionToolName,
416
- originalInput: inputs,
417
- originalToolInput: step.action?.toolInput,
418
- schema: null,
419
- toolRef: toolInstance,
420
- originalToolRef: originalToolCandidate?.originalTool
421
- };
422
- this.pendingForms.set(parsed.formMessage.id, pf);
423
- globalPendingForms.set(parsed.formMessage.id, pf);
424
- return {
425
- ...result,
426
- requiresForm: true,
427
- formMessage: parsed.formMessage,
428
- output: parsed.message || "Please complete the form to continue."
429
- };
430
- }
431
- if (parsed.hashLinkBlock || parsed.success && parsed.inscription && parsed.hashLinkBlock) {
432
- this.formLogger.info("Tool returned HashLink blocks", {
433
- toolName: step.action?.tool,
434
- hasHashLink: true,
435
- blockId: parsed.hashLinkBlock?.blockId
436
- });
437
- const hashLinkResponse = this.processHashLinkResponse(parsed);
438
- return {
439
- ...result,
440
- hasHashLinkBlocks: true,
441
- hashLinkBlock: hashLinkResponse.hashLinkBlock,
442
- output: hashLinkResponse.message
443
- };
444
- }
445
- } catch {
74
+ const processed = { ...working };
75
+ for (const [paramName, paramValue] of Object.entries(working)) {
76
+ if (typeof paramValue === "string") {
77
+ const converted = await this.convertParameterEntities(
78
+ paramValue,
79
+ entities,
80
+ preferences
81
+ );
82
+ processed[paramName] = converted;
83
+ } else if (Array.isArray(paramValue)) {
84
+ const out = [];
85
+ for (const item of paramValue) {
86
+ if (typeof item === "string") {
87
+ const converted = await this.convertParameterEntities(
88
+ item,
89
+ entities,
90
+ preferences
91
+ );
92
+ out.push(converted);
93
+ } else {
94
+ out.push(item);
446
95
  }
447
96
  }
97
+ processed[paramName] = out;
448
98
  }
449
99
  }
450
- return result;
451
- } catch (error) {
452
- if (error instanceof ZodError) {
453
- this.formLogger.info("Intercepted ZodError during agent execution");
454
- return this.handleValidationError(error, inputs, []);
455
- }
456
- throw error;
100
+ working = processed;
101
+ } catch (e) {
102
+ this.logger.warn("Deterministic post-pass failed", {
103
+ toolName,
104
+ error: e instanceof Error ? e.message : "unknown"
105
+ });
457
106
  }
107
+ return working;
458
108
  }
459
109
  /**
460
- * Handles Zod validation errors by generating forms
110
+ * Attach unified preprocessing callback directly to the agent.
461
111
  */
462
- async handleValidationError(error, inputs, intermediateSteps) {
463
- this.formLogger.info("Zod validation error detected, generating form", {
464
- errorIssues: error.issues.length,
465
- inputKeys: Object.keys(inputs)
466
- });
467
- let toolInfo = this.extractToolInfoFromError(
468
- error,
469
- inputs,
470
- intermediateSteps
471
- );
472
- if (!toolInfo) {
473
- this.formLogger.warn(
474
- "Could not extract tool info from validation error, trying fallback detection"
475
- );
476
- const fallbackTool = this.detectToolFromErrorContext(error);
477
- if (!fallbackTool) {
478
- this.formLogger.error(
479
- "No tool detected for form generation, rethrowing error"
112
+ attachToAgent(agent, deps) {
113
+ const getSessionId = deps?.getSessionId ?? (() => null);
114
+ const getEntities = deps?.getEntities ?? (async () => []);
115
+ const entityResolver = deps?.entityResolver;
116
+ const maybe = agent;
117
+ const attach = (target) => {
118
+ const t = target;
119
+ if (typeof t.setParameterPreprocessingCallback === "function") {
120
+ t.setParameterPreprocessingCallback(
121
+ async (toolName, parameters) => {
122
+ const sessionId = getSessionId();
123
+ const entities = await getEntities(sessionId);
124
+ const opts = {};
125
+ if (entityResolver) {
126
+ opts.entityResolver = entityResolver;
127
+ }
128
+ if (sessionId) {
129
+ opts.sessionId = sessionId;
130
+ }
131
+ return this.preprocessParameters(toolName, parameters, entities, opts);
132
+ }
480
133
  );
481
- throw error;
134
+ this.logger.info("Parameter preprocessing callback attached");
135
+ return true;
482
136
  }
483
- toolInfo = fallbackTool;
484
- }
485
- this.formLogger.info("Generating form for tool:", {
486
- toolName: toolInfo.toolName,
487
- hasSchema: !!toolInfo.schema
488
- });
489
- const formMessage = this.formGenerator.generateFormFromError(
490
- error,
491
- toolInfo.schema,
492
- toolInfo.toolName,
493
- inputs.input || ""
494
- );
495
- this.pendingForms.set(formMessage.id, {
496
- toolName: toolInfo.toolName,
497
- originalInput: inputs,
498
- schema: toolInfo.schema
499
- });
500
- globalPendingForms.set(formMessage.id, {
501
- toolName: toolInfo.toolName,
502
- originalInput: inputs,
503
- schema: toolInfo.schema
504
- });
505
- return {
506
- output: this.formatFormResponse(formMessage),
507
- formMessage,
508
- requiresForm: true,
509
- intermediateSteps: intermediateSteps || []
137
+ return false;
510
138
  };
511
- }
512
- /**
513
- * Get a copy of pending forms for preservation during executor recreation
514
- */
515
- getPendingForms() {
516
- return new Map(this.pendingForms);
517
- }
518
- /**
519
- * Restore pending forms from a previous executor instance
520
- */
521
- restorePendingForms(forms) {
522
- for (const [formId, formData] of forms) {
523
- this.pendingForms.set(formId, formData);
139
+ if (!attach(agent) && typeof maybe.getAgent === "function") {
140
+ const underlying = maybe.getAgent();
141
+ if (underlying) {
142
+ void attach(underlying);
143
+ }
524
144
  }
525
145
  }
526
146
  /**
527
- * Processes form submission and continues tool execution
147
+ * Preprocess tool parameters by applying format conversions based on tool's entity resolution preferences
528
148
  */
529
- async processFormSubmission(submission) {
530
- this.formLogger.info(
531
- "🚀 FormAwareAgentExecutor.processFormSubmission called!",
532
- {
533
- submissionFormId: submission.formId,
534
- submissionToolName: submission.toolName
535
- }
536
- );
537
- if (!submission) {
538
- throw new Error("Form submission is null or undefined");
539
- }
540
- if (!submission.formId) {
541
- throw new Error("Form submission missing formId");
542
- }
543
- if (!submission.parameters || submission.parameters === null || typeof submission.parameters !== "object" || Array.isArray(submission.parameters)) {
544
- throw new Error(
545
- `Form submission parameters are invalid: ${typeof submission.parameters}, isNull: ${submission.parameters === null}, isArray: ${Array.isArray(
546
- submission.parameters
547
- )}, parameters: ${JSON.stringify(submission.parameters)}`
548
- );
549
- }
550
- this.formLogger.info("Processing form submission:", {
551
- formId: submission.formId,
552
- toolName: submission.toolName,
553
- parameterKeys: Object.keys(submission.parameters),
554
- parametersType: typeof submission.parameters,
555
- parametersIsNull: submission.parameters === null,
556
- parametersIsUndefined: submission.parameters === void 0,
557
- hasContext: !!submission.context
558
- });
559
- let pendingForm = this.pendingForms.get(submission.formId);
560
- if (!pendingForm) {
561
- pendingForm = globalPendingForms.get(submission.formId);
562
- if (!pendingForm) {
563
- throw new Error(`No pending form found for ID: ${submission.formId}`);
564
- }
565
- }
566
- this.pendingForms.delete(submission.formId);
567
- globalPendingForms.delete(submission.formId);
568
- const tool = pendingForm.toolRef || this.tools.find((t) => t.name === pendingForm.toolName);
569
- if (!tool) {
570
- throw new Error(
571
- `Tool not found for form submission: ${pendingForm.toolName}`
572
- );
573
- }
574
- let baseToolInput = {};
575
- try {
576
- if (pendingForm.originalToolInput && typeof pendingForm.originalToolInput === "object") {
577
- baseToolInput = {
578
- ...pendingForm.originalToolInput
579
- };
580
- }
581
- } catch (error) {
582
- this.formLogger.warn(
583
- "Failed to extract base tool input, using empty object:",
584
- error
585
- );
586
- baseToolInput = {};
587
- }
588
- let submissionData = {};
589
- try {
590
- if (submission.parameters && typeof submission.parameters === "object") {
591
- submissionData = {
592
- ...submission.parameters
593
- };
594
- }
595
- } catch (error) {
596
- this.formLogger.warn(
597
- "Failed to extract submission parameters, using empty object:",
598
- error
599
- );
600
- submissionData = {};
601
- }
602
- const mergedToolInput = {};
603
- try {
604
- Object.keys(baseToolInput).forEach((key) => {
605
- const value = baseToolInput[key];
606
- if (value !== void 0 && value !== null) {
607
- mergedToolInput[key] = value;
608
- }
609
- });
610
- Object.keys(submissionData).forEach((key) => {
611
- const value = submissionData[key];
612
- if (value !== void 0 && value !== null) {
613
- mergedToolInput[key] = value;
614
- }
615
- });
616
- mergedToolInput.renderForm = false;
617
- mergedToolInput.__fromForm = true;
618
- this.formLogger.info("Successfully merged tool input:", {
619
- baseKeys: Object.keys(baseToolInput),
620
- submissionKeys: Object.keys(submissionData),
621
- mergedKeys: Object.keys(mergedToolInput)
622
- });
623
- } catch (error) {
624
- this.formLogger.error("Failed to merge tool input data:", error);
625
- throw new Error(
626
- `Failed to merge tool input data: ${error instanceof Error ? error.message : "Unknown error"}`
627
- );
628
- }
149
+ async preprocessToolParameters(toolName, parameters, entities, sessionId) {
629
150
  try {
630
- const maybeWrapper = tool;
631
- let toolOutput;
632
- if (typeof maybeWrapper.executeOriginal === "function") {
633
- toolOutput = await maybeWrapper.executeOriginal(mergedToolInput);
634
- } else if (typeof maybeWrapper.getOriginalTool === "function") {
635
- const ot = maybeWrapper.getOriginalTool();
636
- const otCall = ot;
637
- if (ot && typeof otCall._call === "function") {
638
- toolOutput = await otCall._call(mergedToolInput);
639
- } else if (ot && typeof otCall.call === "function") {
640
- toolOutput = await otCall.call(mergedToolInput);
641
- } else {
642
- const tcall = tool;
643
- if (typeof tcall.call === "function") {
644
- toolOutput = await tcall.call(mergedToolInput);
645
- } else {
646
- throw new Error(
647
- "No callable tool implementation found for form submission"
648
- );
151
+ if (!entities || entities.length === 0) {
152
+ this.logger.info(
153
+ "Tool parameter preprocessing skipped - no entities provided:",
154
+ {
155
+ toolName,
156
+ originalParams: Object.keys(parameters)
649
157
  }
650
- }
651
- } else if (maybeWrapper.originalTool && typeof maybeWrapper.originalTool._call === "function") {
652
- toolOutput = await maybeWrapper.originalTool._call(mergedToolInput);
653
- } else if (maybeWrapper.originalTool && typeof maybeWrapper.originalTool.call === "function") {
654
- toolOutput = await maybeWrapper.originalTool.call(mergedToolInput);
655
- } else if (typeof tool.call === "function") {
656
- toolOutput = await tool.call(mergedToolInput);
657
- } else {
658
- throw new Error(
659
- "No callable tool implementation found for form submission"
660
158
  );
159
+ return parameters;
661
160
  }
662
- let responseMetadata = {};
663
- let formattedOutput;
664
- try {
665
- const parsed = JSON.parse(toolOutput);
666
- this.formLogger.info(
667
- "✅ METADATA EXTRACTION: Successfully parsed JSON",
668
- {
669
- jsonKeys: Object.keys(parsed),
670
- hasHashLinkBlock: !!parsed.hashLinkBlock
161
+ const processedParameters = { ...parameters };
162
+ const preferences = void 0;
163
+ let hasChanges = false;
164
+ for (const [paramName, paramValue] of Object.entries(parameters)) {
165
+ if (typeof paramValue === "string") {
166
+ const convertedValue = await this.convertParameterEntities(
167
+ paramValue,
168
+ entities,
169
+ preferences
170
+ );
171
+ if (convertedValue !== paramValue) {
172
+ processedParameters[paramName] = convertedValue;
173
+ hasChanges = true;
174
+ this.logger.info("Parameter entity conversion applied:", {
175
+ toolName,
176
+ paramName,
177
+ original: paramValue,
178
+ converted: convertedValue
179
+ });
671
180
  }
672
- );
673
- if (parsed && typeof parsed === "object") {
674
- if (ResponseFormatter.isHashLinkResponse(parsed)) {
675
- this.formLogger.info(
676
- "🔗 HASHLINK DETECTED: Processing HashLink response separately to preserve metadata"
677
- );
678
- responseMetadata = {
679
- ...responseMetadata,
680
- hashLinkBlock: parsed.hashLinkBlock
681
- };
682
- formattedOutput = ResponseFormatter.formatHashLinkResponse(parsed);
683
- this.formLogger.info(
684
- "🔗 METADATA PRESERVED: HashLink metadata extracted for component rendering",
685
- {
686
- blockId: this.hasHashLinkBlock(responseMetadata) ? responseMetadata.hashLinkBlock.blockId : void 0,
687
- hasTemplate: this.hasHashLinkBlock(responseMetadata) ? !!responseMetadata.hashLinkBlock.template : false
181
+ } else if (Array.isArray(paramValue)) {
182
+ const originalArray = paramValue;
183
+ const convertedArray = [];
184
+ let arrayChanged = false;
185
+ for (const item of originalArray) {
186
+ if (typeof item === "string") {
187
+ const convertedItem = await this.convertParameterEntities(
188
+ item,
189
+ entities,
190
+ preferences
191
+ );
192
+ convertedArray.push(convertedItem);
193
+ if (convertedItem !== item) {
194
+ arrayChanged = true;
195
+ this.logger.info("Parameter array item conversion applied:", {
196
+ toolName,
197
+ paramName,
198
+ original: item,
199
+ converted: convertedItem
200
+ });
688
201
  }
689
- );
690
- } else {
691
- formattedOutput = ResponseFormatter.formatResponse(toolOutput);
692
- responseMetadata = {
693
- ...responseMetadata,
694
- hashLinkBlock: parsed.hashLinkBlock
695
- };
202
+ } else {
203
+ convertedArray.push(item);
204
+ }
696
205
  }
697
- } else {
698
- formattedOutput = ResponseFormatter.formatResponse(toolOutput);
699
- }
700
- } catch (error) {
701
- this.formLogger.warn(
702
- "❌ METADATA EXTRACTION: Tool output is not JSON",
703
- {
704
- error: error instanceof Error ? error.message : "unknown error",
705
- outputSample: typeof toolOutput === "string" ? toolOutput.substring(0, 200) : "not-string"
206
+ if (arrayChanged) {
207
+ processedParameters[paramName] = convertedArray;
208
+ hasChanges = true;
706
209
  }
707
- );
708
- formattedOutput = ResponseFormatter.formatResponse(toolOutput);
210
+ }
709
211
  }
710
- return {
711
- output: formattedOutput,
712
- formCompleted: true,
713
- originalFormId: submission.formId,
714
- intermediateSteps: [],
715
- metadata: responseMetadata,
716
- rawToolOutput: toolOutput,
717
- toolName: pendingForm.toolName
718
- };
212
+ this.logger.info("Tool parameter preprocessing completed:", {
213
+ toolName,
214
+ originalParams: Object.keys(parameters),
215
+ hasChanges,
216
+ sessionId
217
+ });
218
+ return processedParameters;
719
219
  } catch (error) {
720
- if (error instanceof ZodError) {
721
- return this.handleValidationError(error, mergedToolInput, []);
722
- }
723
- throw error;
220
+ this.logger.warn("Tool parameter preprocessing failed:", {
221
+ toolName,
222
+ error: error instanceof Error ? error.message : "Unknown error"
223
+ });
224
+ return parameters;
724
225
  }
725
226
  }
726
227
  /**
727
- * Extracts tool information from the execution context
228
+ * Convert entity references in a parameter value based on tool preferences
728
229
  */
729
- extractToolInfoFromError(error, inputs, intermediateSteps) {
730
- try {
731
- if (intermediateSteps.length > 0) {
732
- const lastStep = intermediateSteps[intermediateSteps.length - 1];
733
- if (lastStep.action && lastStep.action.tool) {
734
- const tool = this.tools.find((t) => t.name === lastStep.action.tool);
735
- if (tool && "schema" in tool) {
736
- this.formLogger.info(
737
- "Found tool from intermediate steps:",
738
- lastStep.action.tool
739
- );
740
- return {
741
- toolName: lastStep.action.tool,
742
- schema: tool.schema
743
- };
744
- }
745
- }
230
+ async convertParameterEntities(parameterValue, entities, preferences) {
231
+ let convertedValue = parameterValue;
232
+ for (const entity of entities) {
233
+ const containsEntityId = convertedValue.includes(entity.entityId);
234
+ const containsEntityName = convertedValue.includes(entity.entityName);
235
+ if (!containsEntityId && !containsEntityName) {
236
+ continue;
746
237
  }
747
- const inputSteps = inputs.intermediateSteps || [];
748
- if (inputSteps.length > 0) {
749
- const lastStep = inputSteps[inputSteps.length - 1];
750
- let action;
751
- if (Array.isArray(lastStep) && lastStep.length > 0) {
752
- action = lastStep[0];
753
- } else if (lastStep.action) {
754
- action = lastStep.action;
755
- } else {
756
- action = lastStep;
238
+ let targetFormat = null;
239
+ if (entity.entityType === EntityFormat.TOPIC_ID) {
240
+ if (preferences?.inscription === "hrl" || preferences?.topic === "hrl") {
241
+ targetFormat = EntityFormat.HRL;
242
+ } else if (preferences?.inscription === "topicId" || preferences?.topic === "topicId") {
243
+ targetFormat = EntityFormat.TOPIC_ID;
757
244
  }
758
- if (action && action.tool) {
759
- const tool = this.tools.find((t) => t.name === action.tool);
760
- if (tool && "schema" in tool) {
761
- this.formLogger.info("Found tool from input steps:", action.tool);
762
- return {
763
- toolName: action.tool,
764
- schema: tool.schema
765
- };
766
- }
245
+ } else if (entity.entityType === EntityFormat.TOKEN_ID) {
246
+ if (preferences?.token === "tokenId") {
247
+ targetFormat = EntityFormat.TOKEN_ID;
248
+ } else if (preferences?.token === "symbol") {
249
+ targetFormat = EntityFormat.SYMBOL;
767
250
  }
768
- }
769
- const toolFromContext = this.findToolFromContext(inputs);
770
- if (toolFromContext) {
771
- this.formLogger.info(
772
- "Found tool from context:",
773
- toolFromContext.toolName
774
- );
775
- return toolFromContext;
776
- }
777
- return null;
778
- } catch (err) {
779
- this.formLogger.error("Error extracting tool info:", err);
780
- return null;
781
- }
782
- }
783
- /**
784
- * Attempts to find tool from execution context
785
- */
786
- findToolFromContext(inputs) {
787
- const inputText = inputs.input || "";
788
- for (const tool of this.tools) {
789
- const keywords = this.extractToolKeywords(tool.name);
790
- if (keywords.some(
791
- (keyword) => inputText.toLowerCase().includes(keyword.toLowerCase())
792
- )) {
793
- if ("schema" in tool) {
794
- return {
795
- toolName: tool.name,
796
- schema: tool.schema
797
- };
251
+ } else if (entity.entityType === EntityFormat.ACCOUNT_ID) {
252
+ if (preferences?.account === "accountId" || preferences?.supplyKey === "accountId" || preferences?.adminKey === "accountId") {
253
+ targetFormat = EntityFormat.ACCOUNT_ID;
254
+ } else if (preferences?.account === "alias") {
255
+ targetFormat = EntityFormat.ALIAS;
798
256
  }
799
257
  }
800
- }
801
- return null;
802
- }
803
- /**
804
- * Additional fallback to detect tool from error context
805
- */
806
- detectToolFromErrorContext(error) {
807
- const errorPaths = error.issues.map((issue) => issue.path.join("."));
808
- for (const tool of this.tools) {
809
- if ("schema" in tool) {
810
- const toolSchema = tool.schema;
811
- if (this.schemaMatchesErrorPaths(toolSchema, errorPaths)) {
812
- this.formLogger.info(
813
- "Detected tool from error path analysis:",
814
- tool.name
815
- );
816
- return {
817
- toolName: tool.name,
818
- schema: toolSchema
258
+ if (targetFormat) {
259
+ try {
260
+ const context = {
261
+ networkType: this.networkType,
262
+ sessionId: "unknown"
819
263
  };
264
+ if (preferences) {
265
+ context.toolPreferences = preferences;
266
+ }
267
+ const convertedEntityValue = await this.formatConverterRegistry.convertEntity(
268
+ entity.entityId,
269
+ targetFormat,
270
+ context
271
+ );
272
+ if (containsEntityId) {
273
+ convertedValue = convertedValue.replace(
274
+ new RegExp(`\\b${entity.entityId.replace(/\./g, "\\.")}\\b`, "g"),
275
+ convertedEntityValue
276
+ );
277
+ }
278
+ if (containsEntityName) {
279
+ convertedValue = convertedValue.replace(
280
+ new RegExp(
281
+ `\\b${entity.entityName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
282
+ "g"
283
+ ),
284
+ convertedEntityValue
285
+ );
286
+ }
287
+ this.logger.info("Applied format conversion to parameter:", {
288
+ entityId: entity.entityId,
289
+ entityType: entity.entityType,
290
+ targetFormat,
291
+ convertedValue: convertedEntityValue,
292
+ parameterValue: convertedValue
293
+ });
294
+ } catch (error) {
295
+ this.logger.warn("Format conversion failed for parameter:", {
296
+ entityId: entity.entityId,
297
+ targetFormat,
298
+ error: error instanceof Error ? error.message : "Unknown error"
299
+ });
820
300
  }
821
301
  }
822
302
  }
823
- return null;
824
- }
825
- /**
826
- * Checks if a schema structure matches error paths
827
- */
828
- schemaMatchesErrorPaths(schema, errorPaths) {
829
- const schemaRecord = schema;
830
- if (!schemaRecord || !schemaRecord._def) return false;
831
- try {
832
- const def = schemaRecord._def;
833
- if (def.typeName === "ZodObject") {
834
- const shape = def.shape;
835
- const schemaKeys = Object.keys(shape || {});
836
- return errorPaths.some((path) => {
837
- const topLevelKey = path.split(".")[0];
838
- return schemaKeys.includes(topLevelKey);
839
- });
840
- }
841
- } catch (err) {
842
- this.formLogger.debug("Error analyzing schema structure:", err);
843
- }
844
- return false;
845
- }
846
- /**
847
- * Extracts keywords from tool name for matching
848
- */
849
- extractToolKeywords(toolName) {
850
- const words = toolName.replace(/([A-Z])/g, " $1").toLowerCase().split(/[\s_-]+/).filter((w) => w.length > 2);
851
- return [...words, toolName.toLowerCase()];
852
- }
853
- /**
854
- * Formats the form message for display
855
- */
856
- formatFormResponse(formMessage) {
857
- const fieldCount = formMessage.formConfig.fields.length;
858
- const fieldList = formMessage.formConfig.fields.slice(0, 3).map((f) => `• ${f.label}`).join("\n");
859
- return `I need some additional information to complete your request.
860
-
861
- ${formMessage.formConfig.description}
862
-
863
- Required fields:
864
- ${fieldList}${fieldCount > 3 ? `
865
- ... and ${fieldCount - 3} more` : ""}
866
-
867
- Please fill out the form below to continue.`;
868
- }
869
- /**
870
- * Check if there are pending forms
871
- */
872
- hasPendingForms() {
873
- return this.pendingForms.size > 0;
874
- }
875
- /**
876
- * Get information about pending forms
877
- */
878
- getPendingFormsInfo() {
879
- return Array.from(this.pendingForms.entries()).map(([formId, info]) => ({
880
- formId,
881
- toolName: info.toolName
882
- }));
883
- }
884
- /**
885
- * Processes HashLink block responses from tools
886
- */
887
- processHashLinkResponse(toolResponse) {
888
- try {
889
- let hashLinkBlock;
890
- if (toolResponse.hashLinkBlock) {
891
- hashLinkBlock = toolResponse.hashLinkBlock;
892
- } else if (toolResponse.success && toolResponse.inscription && toolResponse.hashLinkBlock) {
893
- hashLinkBlock = toolResponse.hashLinkBlock;
894
- }
895
- if (!hashLinkBlock) {
896
- throw new Error("HashLink block data not found in response");
897
- }
898
- if (!hashLinkBlock.blockId || !hashLinkBlock.hashLink || !hashLinkBlock.attributes) {
899
- throw new Error(
900
- "Invalid HashLink block structure - missing required fields"
901
- );
902
- }
903
- let message = "Content processed successfully!";
904
- if (toolResponse.success && toolResponse.inscription) {
905
- const inscription = toolResponse.inscription;
906
- const metadata = toolResponse.metadata || {};
907
- message = `✅ ${inscription.standard} Hashinal inscription completed!
908
-
909
- `;
910
- if (metadata.name) {
911
- message += `**${metadata.name}**
912
- `;
913
- }
914
- if (metadata.description) {
915
- message += `${metadata.description}
916
-
917
- `;
918
- }
919
- message += `📍 **Topic ID:** ${inscription.topicId}
920
- `;
921
- message += `🔗 **HRL:** ${inscription.hrl}
922
- `;
923
- if (inscription.cdnUrl) {
924
- message += `🌐 **CDN URL:** ${inscription.cdnUrl}
925
- `;
926
- }
927
- if (metadata.creator) {
928
- message += `👤 **Creator:** ${metadata.creator}
929
- `;
930
- }
931
- }
932
- this.formLogger.info("Processed HashLink response", {
933
- blockId: hashLinkBlock.blockId,
934
- hashLink: hashLinkBlock.hashLink,
935
- hasTemplate: !!hashLinkBlock.template,
936
- attributeCount: Object.keys(hashLinkBlock.attributes || {}).length
937
- });
938
- return {
939
- hasHashLinkBlocks: true,
940
- hashLinkBlock,
941
- message
942
- };
943
- } catch (error) {
944
- this.formLogger.error("Error processing HashLink response:", error);
945
- return {
946
- hasHashLinkBlocks: false,
947
- message: "Content processed, but interactive display is not available."
948
- };
949
- }
950
- }
951
- /**
952
- * Get FormEngine statistics for debugging
953
- */
954
- getFormEngineStatistics() {
955
- return {
956
- strategies: this.formEngine.getRegisteredStrategies(),
957
- middleware: this.formEngine.getRegisteredMiddleware()
958
- };
303
+ return convertedValue;
959
304
  }
960
305
  }
961
306
  export {
962
- FormAwareAgentExecutor
307
+ ParameterService
963
308
  };
964
309
  //# sourceMappingURL=index29.js.map