@hashgraphonline/conversational-agent 0.2.1 → 0.2.103

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 (75) hide show
  1. package/README.md +3 -3
  2. package/bin/conversational-agent-cli.js +0 -1
  3. package/dist/cjs/conversational-agent.d.ts +11 -1
  4. package/dist/cjs/index.cjs +1 -1
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/memory/smart-memory-manager.d.ts +7 -1
  7. package/dist/cjs/services/attachment-processor.d.ts +41 -0
  8. package/dist/cjs/services/index.d.ts +2 -0
  9. package/dist/cjs/services/parameter-service.d.ts +43 -0
  10. package/dist/cjs/tools/entity-resolver-tool.d.ts +5 -3
  11. package/dist/esm/index.js +9 -5
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/esm/index10.js +2 -2
  14. package/dist/esm/index18.js +64 -17
  15. package/dist/esm/index18.js.map +1 -1
  16. package/dist/esm/index21.js +1 -1
  17. package/dist/esm/index23.js +3 -3
  18. package/dist/esm/index24.js +20 -4
  19. package/dist/esm/index24.js.map +1 -1
  20. package/dist/esm/index29.js +248 -903
  21. package/dist/esm/index29.js.map +1 -1
  22. package/dist/esm/index30.js +98 -219
  23. package/dist/esm/index30.js.map +1 -1
  24. package/dist/esm/index31.js +833 -1085
  25. package/dist/esm/index31.js.map +1 -1
  26. package/dist/esm/index32.js +228 -115
  27. package/dist/esm/index32.js.map +1 -1
  28. package/dist/esm/index33.js +1197 -79
  29. package/dist/esm/index33.js.map +1 -1
  30. package/dist/esm/index34.js +119 -39
  31. package/dist/esm/index34.js.map +1 -1
  32. package/dist/esm/index35.js +103 -96
  33. package/dist/esm/index35.js.map +1 -1
  34. package/dist/esm/index36.js +46 -21
  35. package/dist/esm/index36.js.map +1 -1
  36. package/dist/esm/index37.js +107 -12
  37. package/dist/esm/index37.js.map +1 -1
  38. package/dist/esm/index38.js +21 -7
  39. package/dist/esm/index38.js.map +1 -1
  40. package/dist/esm/index39.js +11 -26
  41. package/dist/esm/index39.js.map +1 -1
  42. package/dist/esm/index40.js +6 -4
  43. package/dist/esm/index40.js.map +1 -1
  44. package/dist/esm/index41.js +5 -255
  45. package/dist/esm/index41.js.map +1 -1
  46. package/dist/esm/index42.js +213 -142
  47. package/dist/esm/index42.js.map +1 -1
  48. package/dist/esm/index43.js +174 -82
  49. package/dist/esm/index43.js.map +1 -1
  50. package/dist/esm/index44.js +95 -0
  51. package/dist/esm/index44.js.map +1 -0
  52. package/dist/esm/index45.js +30 -0
  53. package/dist/esm/index45.js.map +1 -0
  54. package/dist/esm/index5.js +2 -2
  55. package/dist/esm/index6.js +76 -6
  56. package/dist/esm/index6.js.map +1 -1
  57. package/dist/esm/index8.js +1 -1
  58. package/dist/types/conversational-agent.d.ts +11 -1
  59. package/dist/types/memory/smart-memory-manager.d.ts +7 -1
  60. package/dist/types/services/attachment-processor.d.ts +41 -0
  61. package/dist/types/services/index.d.ts +2 -0
  62. package/dist/types/services/parameter-service.d.ts +43 -0
  63. package/dist/types/tools/entity-resolver-tool.d.ts +5 -3
  64. package/package.json +2 -1
  65. package/src/conversational-agent.ts +97 -5
  66. package/src/langchain/form-aware-agent-executor.ts +1 -1
  67. package/src/langchain/langchain-agent.ts +36 -18
  68. package/src/memory/smart-memory-manager.ts +80 -27
  69. package/src/scripts/test-inscribe-form-generation.ts +3 -3
  70. package/src/scripts/test-inscribe-wrapper-verification.ts +3 -3
  71. package/src/services/attachment-processor.ts +163 -0
  72. package/src/services/content-store-manager.ts +32 -4
  73. package/src/services/index.ts +2 -0
  74. package/src/services/parameter-service.ts +430 -0
  75. package/src/tools/entity-resolver-tool.ts +12 -18
@@ -19,6 +19,8 @@ export interface EntityAssociation {
19
19
  createdAt: Date;
20
20
  /** Transaction ID that created this entity */
21
21
  transactionId?: string;
22
+ /** Optional session identifier to scope associations */
23
+ sessionId?: string;
22
24
  }
23
25
 
24
26
  /**
@@ -381,7 +383,8 @@ export class SmartMemoryManager {
381
383
  entityId: string,
382
384
  entityName: string,
383
385
  entityType: string,
384
- transactionId?: string
386
+ transactionId?: string,
387
+ sessionId?: string
385
388
  ): void {
386
389
  try {
387
390
  if (
@@ -410,7 +413,7 @@ export class SmartMemoryManager {
410
413
 
411
414
  const sanitizedEntityId = entityId.trim();
412
415
  const sanitizedEntityName = entityName.trim().substring(0, 100);
413
- const sanitizedEntityType = entityType.trim().toLowerCase();
416
+ const sanitizedEntityType = this.normalizeEntityType(entityType);
414
417
 
415
418
  let usageHint = '';
416
419
  if (sanitizedEntityType === 'tokenid') {
@@ -433,7 +436,7 @@ export class SmartMemoryManager {
433
436
  createdAt: new Date(),
434
437
  isEntityAssociation: true,
435
438
  ...(usageHint ? { usage: usageHint } : {}),
436
- ...(sanitizedEntityType === 'topicid'
439
+ ...(sanitizedEntityType === 'topicId'
437
440
  ? { hrl: `hcs://1/${sanitizedEntityId}` }
438
441
  : {}),
439
442
  ...(transactionId !== undefined &&
@@ -441,6 +444,7 @@ export class SmartMemoryManager {
441
444
  transactionId.trim() !== ''
442
445
  ? { transactionId: transactionId.trim() }
443
446
  : {}),
447
+ ...(sessionId && sessionId.trim() !== '' ? { sessionId: sessionId.trim() } : {}),
444
448
  };
445
449
 
446
450
  const content = JSON.stringify(association);
@@ -462,6 +466,7 @@ export class SmartMemoryManager {
462
466
  entityName: sanitizedEntityName,
463
467
  entityType: sanitizedEntityType,
464
468
  isEntityAssociation: true,
469
+ ...(sessionId && sessionId.trim() !== '' ? { sessionId: sessionId.trim() } : {}),
465
470
  },
466
471
  };
467
472
 
@@ -480,6 +485,43 @@ export class SmartMemoryManager {
480
485
  }
481
486
  }
482
487
 
488
+ /**
489
+ * Normalize various type aliases to canonical EntityFormat strings using a registry.
490
+ */
491
+ private normalizeEntityType(input: string): string {
492
+ const raw = (input || '').trim();
493
+ if (raw.length === 0) {
494
+ return '';
495
+ }
496
+
497
+ const key = raw.replace(/[^a-z]/gi, '').toLowerCase();
498
+
499
+ const REGISTRY: Record<string, string> = {
500
+ topic: 'topicId',
501
+ topicid: 'topicId',
502
+ token: 'tokenId',
503
+ tokenid: 'tokenId',
504
+ account: 'accountId',
505
+ accountid: 'accountId',
506
+ contract: 'contractId',
507
+ contractid: 'contractId',
508
+ file: 'fileId',
509
+ fileid: 'fileId',
510
+ schedule: 'scheduleId',
511
+ scheduleid: 'scheduleId',
512
+ };
513
+
514
+ if (Object.prototype.hasOwnProperty.call(REGISTRY, key)) {
515
+ return REGISTRY[key];
516
+ }
517
+
518
+ if (/^[a-z]+Id$/.test(raw)) {
519
+ return raw;
520
+ }
521
+
522
+ return raw;
523
+ }
524
+
483
525
  /**
484
526
  * Resolve entity references from natural language queries
485
527
  * @param query - Search query (entity name or natural language reference)
@@ -611,19 +653,15 @@ export class SmartMemoryManager {
611
653
  */
612
654
  getEntityAssociations(entityType?: string): EntityAssociation[] {
613
655
  try {
614
- const sanitizedEntityType = entityType
615
- ? entityType.trim().toLowerCase()
616
- : undefined;
656
+ const rawFilter = entityType ? entityType.trim() : undefined;
657
+ const filterCanonical = rawFilter ? this.normalizeEntityType(rawFilter) : undefined;
617
658
 
618
- if (
619
- entityType &&
620
- (!sanitizedEntityType || sanitizedEntityType.length === 0)
621
- ) {
659
+ if (entityType && (!rawFilter || rawFilter.length === 0)) {
622
660
  return [];
623
661
  }
624
662
 
625
663
  const SEARCH_ANY_ENTITY = 'entityId';
626
- const searchQuery = sanitizedEntityType || SEARCH_ANY_ENTITY;
664
+ const searchQuery = filterCanonical || SEARCH_ANY_ENTITY;
627
665
  const searchResults = this._contentStorage.searchMessages(searchQuery, {
628
666
  caseSensitive: false,
629
667
  limit: 100,
@@ -638,10 +676,7 @@ export class SmartMemoryManager {
638
676
  const parsed = JSON.parse(content);
639
677
 
640
678
  if (parsed.entityId && parsed.entityName && parsed.entityType) {
641
- if (
642
- sanitizedEntityType &&
643
- parsed.entityType !== sanitizedEntityType
644
- ) {
679
+ if (filterCanonical && parsed.entityType !== filterCanonical) {
645
680
  continue;
646
681
  }
647
682
 
@@ -667,18 +702,36 @@ export class SmartMemoryManager {
667
702
  }
668
703
  }
669
704
 
670
- const results = associations
671
- .filter(
672
- (assoc, index, arr) =>
673
- arr.findIndex((a) => a.entityId === assoc.entityId) === index
674
- )
675
- .sort((a, b): number => {
676
- const getTime = (d: Date | string): number =>
677
- d instanceof Date ? d.getTime() : new Date(d).getTime();
678
- const aTime = getTime(a.createdAt);
679
- const bTime = getTime(b.createdAt);
680
- return bTime - aTime;
681
- });
705
+ // Merge duplicates by entityId, preferring the newest and one that carries transactionId
706
+ const mergedById = new Map<string, EntityAssociation>();
707
+ const getTime = (d: Date | string): number =>
708
+ d instanceof Date ? d.getTime() : new Date(d).getTime();
709
+
710
+ for (const assoc of associations) {
711
+ const existing = mergedById.get(assoc.entityId);
712
+ if (!existing) {
713
+ mergedById.set(assoc.entityId, assoc);
714
+ continue;
715
+ }
716
+
717
+ const existingTime = getTime(existing.createdAt);
718
+ const currentTime = getTime(assoc.createdAt);
719
+
720
+ const preferCurrent =
721
+ currentTime > existingTime ||
722
+ (!!assoc.transactionId && !existing.transactionId);
723
+
724
+ if (preferCurrent) {
725
+ mergedById.set(assoc.entityId, {
726
+ ...existing,
727
+ ...assoc,
728
+ });
729
+ }
730
+ }
731
+
732
+ const results = Array.from(mergedById.values()).sort((a, b) =>
733
+ getTime(b.createdAt) - getTime(a.createdAt)
734
+ );
682
735
 
683
736
  return results;
684
737
  } catch (error) {
@@ -69,9 +69,9 @@ async function createTestAgent(): Promise<ConversationalAgent> {
69
69
  network: (process.env.HEDERA_NETWORK as 'testnet' | 'mainnet') || 'testnet',
70
70
  openAIApiKey: process.env.OPENAI_API_KEY!,
71
71
  openAIModelName: 'gpt-4o-mini',
72
- verbose: true, // Enable verbose for debugging
73
- disableLogging: false, // Enable logging for debugging
74
- entityMemoryEnabled: false, // Disable for testing
72
+ verbose: true,
73
+ disableLogging: false,
74
+ entityMemoryEnabled: false,
75
75
  };
76
76
 
77
77
  const agent = new ConversationalAgent(options);
@@ -64,9 +64,9 @@ async function createTestAgent(): Promise<ConversationalAgent> {
64
64
  network: (process.env.HEDERA_NETWORK as 'testnet' | 'mainnet') || 'testnet',
65
65
  openAIApiKey: process.env.OPENAI_API_KEY!,
66
66
  openAIModelName: 'gpt-4o-mini',
67
- verbose: false, // Reduce verbosity
68
- disableLogging: true, // Disable logs
69
- entityMemoryEnabled: false, // Disable for testing
67
+ verbose: false,
68
+ disableLogging: true,
69
+ entityMemoryEnabled: false,
70
70
  };
71
71
 
72
72
  const agent = new ConversationalAgent(options);
@@ -0,0 +1,163 @@
1
+ import { Logger } from '@hashgraphonline/standards-sdk';
2
+ import type { ContentStoreManager as PackageContentStoreManager } from './content-store-manager';
3
+
4
+ export interface AttachmentData {
5
+ name: string;
6
+ data: string;
7
+ type: string;
8
+ size: number;
9
+ }
10
+
11
+ type ContentStoreManager = PackageContentStoreManager;
12
+
13
+ /**
14
+ * Utility for processing file attachments and content references
15
+ */
16
+ export class AttachmentProcessor {
17
+ private logger: Logger;
18
+
19
+ constructor() {
20
+ this.logger = new Logger({ module: 'AttachmentProcessor' });
21
+ }
22
+
23
+ /**
24
+ * Process attachments and create content references
25
+ */
26
+ async processAttachments(
27
+ content: string,
28
+ attachments: AttachmentData[],
29
+ contentStoreManager?: ContentStoreManager
30
+ ): Promise<string> {
31
+ if (attachments.length === 0) {
32
+ return content;
33
+ }
34
+
35
+ this.logger.info('Processing attachments with content reference system:', {
36
+ attachmentCount: attachments.length,
37
+ totalSize: attachments.reduce((sum, att) => sum + att.size, 0),
38
+ });
39
+
40
+ if (contentStoreManager && contentStoreManager.isInitialized()) {
41
+ return this.processWithContentStore(content, attachments, contentStoreManager);
42
+ } else {
43
+ this.logger.warn('Content storage not available, creating simple file references');
44
+ return this.processWithSimpleReferences(content, attachments);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Process attachments using content store manager
50
+ */
51
+ private async processWithContentStore(
52
+ content: string,
53
+ attachments: AttachmentData[],
54
+ contentStoreManager: ContentStoreManager
55
+ ): Promise<string> {
56
+ const contentReferences: string[] = [];
57
+
58
+ for (const attachment of attachments) {
59
+ try {
60
+ const base64Data = attachment.data.includes('base64,')
61
+ ? attachment.data.split('base64,')[1]
62
+ : attachment.data;
63
+ const buffer = Buffer.from(base64Data, 'base64');
64
+
65
+ const contentRef = await contentStoreManager.storeContentIfLarge(
66
+ buffer,
67
+ {
68
+ mimeType: attachment.type,
69
+ source: 'user_upload',
70
+ fileName: attachment.name,
71
+ tags: ['attachment', 'user_file'],
72
+ }
73
+ );
74
+
75
+ if (contentRef) {
76
+ if (attachment.type.startsWith('image/')) {
77
+ contentReferences.push(
78
+ `[Image File: ${attachment.name}] (content-ref:${contentRef.referenceId})`
79
+ );
80
+ } else {
81
+ contentReferences.push(
82
+ `[File: ${attachment.name}] (content-ref:${contentRef.referenceId})`
83
+ );
84
+ }
85
+ } else {
86
+ contentReferences.push(
87
+ this.createInlineReference(attachment, base64Data)
88
+ );
89
+ }
90
+ } catch (error) {
91
+ this.logger.error('Failed to process attachment:', {
92
+ fileName: attachment.name,
93
+ error: error instanceof Error ? error.message : 'Unknown error',
94
+ });
95
+ contentReferences.push(
96
+ `[File: ${attachment.name} - Error processing file: ${
97
+ error instanceof Error ? error.message : 'Unknown error'
98
+ }]`
99
+ );
100
+ }
101
+ }
102
+
103
+ const fileList = this.createFileList(attachments);
104
+ return content
105
+ ? `${content}\n\nAttached files:\n${fileList}\n\n${contentReferences.join('\n')}`
106
+ : `Attached files:\n${fileList}\n\n${contentReferences.join('\n')}`;
107
+ }
108
+
109
+ /**
110
+ * Process attachments with simple file references
111
+ */
112
+ private processWithSimpleReferences(content: string, attachments: AttachmentData[]): string {
113
+ const fileReferences = attachments.map((attachment) => {
114
+ const sizeStr = this.formatFileSize(attachment.size);
115
+
116
+ if (attachment.type.startsWith('image/')) {
117
+ return `📎 Image: ${attachment.name} (${sizeStr}, ${attachment.type})`;
118
+ } else {
119
+ return `📎 File: ${attachment.name} (${sizeStr}, ${attachment.type})`;
120
+ }
121
+ });
122
+
123
+ return content
124
+ ? `${content}\n\nAttached files:\n${fileReferences.join('\n')}`
125
+ : `Attached files:\n${fileReferences.join('\n')}`;
126
+ }
127
+
128
+ /**
129
+ * Create inline reference for small files
130
+ */
131
+ private createInlineReference(attachment: AttachmentData, base64Data: string): string {
132
+ if (attachment.size < 50000) {
133
+ if (attachment.type.startsWith('image/')) {
134
+ return `![${attachment.name}](data:${attachment.type};base64,${base64Data})`;
135
+ } else {
136
+ return `[File: ${attachment.name} (${this.formatFileSize(attachment.size)})]\nContent: ${base64Data}`;
137
+ }
138
+ } else {
139
+ return `[File: ${attachment.name} (${this.formatFileSize(attachment.size)}) - Content too large to include inline]`;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Create formatted file list
145
+ */
146
+ private createFileList(attachments: AttachmentData[]): string {
147
+ return attachments
148
+ .map((file) => {
149
+ const sizeStr = this.formatFileSize(file.size);
150
+ return `📎 ${file.name} (${sizeStr})`;
151
+ })
152
+ .join('\n');
153
+ }
154
+
155
+ /**
156
+ * Format file size for display
157
+ */
158
+ private formatFileSize(size: number): string {
159
+ return size >= 1024 * 1024
160
+ ? `${(size / (1024 * 1024)).toFixed(1)}MB`
161
+ : `${(size / 1024).toFixed(1)}KB`;
162
+ }
163
+ }
@@ -165,8 +165,26 @@ export class ContentStoreManager {
165
165
  }
166
166
 
167
167
  try {
168
- await ContentStoreService.setInstance(this.adapter);
169
- ContentResolverRegistry.register(this.resolver);
168
+ if (
169
+ ContentStoreService &&
170
+ typeof (ContentStoreService as unknown as { setInstance?: Function }).setInstance === 'function'
171
+ ) {
172
+ await (ContentStoreService as unknown as { setInstance: (adapter: unknown) => Promise<void> }).setInstance(
173
+ this.adapter
174
+ );
175
+ } else {
176
+ this.logger.warn('ContentStoreService.setInstance is unavailable; skipping registration');
177
+ }
178
+ if (
179
+ ContentResolverRegistry &&
180
+ typeof (ContentResolverRegistry as unknown as { register?: Function }).register === 'function'
181
+ ) {
182
+ (ContentResolverRegistry as unknown as { register: (resolver: unknown) => void }).register(
183
+ this.resolver
184
+ );
185
+ } else {
186
+ this.logger.warn('ContentResolverRegistry.register is unavailable; skipping registration');
187
+ }
170
188
  this.isRegistered = true;
171
189
  this.logger.info(
172
190
  'ContentStoreManager initialized and registered for cross-package access'
@@ -236,8 +254,18 @@ export class ContentStoreManager {
236
254
  async dispose(): Promise<void> {
237
255
  if (this.isRegistered) {
238
256
  this.contentStorage.dispose();
239
- ContentStoreService.dispose();
240
- ContentResolverRegistry.unregister();
257
+ if (
258
+ ContentStoreService &&
259
+ typeof (ContentStoreService as unknown as { dispose?: Function }).dispose === 'function'
260
+ ) {
261
+ (ContentStoreService as unknown as { dispose: () => void }).dispose();
262
+ }
263
+ if (
264
+ ContentResolverRegistry &&
265
+ typeof (ContentResolverRegistry as unknown as { unregister?: Function }).unregister === 'function'
266
+ ) {
267
+ (ContentResolverRegistry as unknown as { unregister: () => void }).unregister();
268
+ }
241
269
  this.isRegistered = false;
242
270
  this.logger.info('ContentStoreManager disposed and unregistered');
243
271
  }
@@ -1,3 +1,5 @@
1
1
  export * from './entity-resolver';
2
2
  export * from './content-store-manager';
3
3
  export * from './formatters';
4
+ export * from './parameter-service';
5
+ export * from './attachment-processor';