@hashgraphonline/standards-agent-kit 0.2.108 → 0.2.110

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashgraphonline/standards-agent-kit",
3
- "version": "0.2.108",
3
+ "version": "0.2.110",
4
4
  "description": "A modular SDK for building on-chain autonomous agents using Hashgraph Online Standards, including HCS-10 for agent discovery and communication.",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/standards-agent-kit.cjs",
@@ -89,7 +89,7 @@
89
89
  },
90
90
  "dependencies": {
91
91
  "@hashgraph/sdk": "^2.69.0",
92
- "@hashgraphonline/standards-sdk": "0.0.162",
92
+ "@hashgraphonline/standards-sdk": "0.0.165",
93
93
  "@langchain/community": "^0.3.49",
94
94
  "@langchain/core": "^0.3.66",
95
95
  "@langchain/openai": "^0.6.3",
@@ -7,8 +7,8 @@ import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';
7
7
  * Schema for inscribing from buffer
8
8
  */
9
9
  const inscribeFromBufferSchema = z.object({
10
- base64Data: z.string().describe('Base64 encoded content to inscribe'),
11
- fileName: z.string().describe('Name for the inscribed content'),
10
+ base64Data: z.string().min(1, 'Base64 data cannot be empty').describe('Base64 encoded content to inscribe. Must contain valid, non-empty content (minimum 10 bytes after decoding).'),
11
+ fileName: z.string().min(1, 'File name cannot be empty').describe('Name for the inscribed content. Required for all inscriptions.'),
12
12
  mimeType: z
13
13
  .string()
14
14
  .optional()
@@ -53,7 +53,7 @@ const inscribeFromBufferSchema = z.object({
53
53
  */
54
54
  export class InscribeFromBufferTool extends BaseInscriberQueryTool<typeof inscribeFromBufferSchema> {
55
55
  name = 'inscribeFromBuffer';
56
- description = 'Inscribe content from a buffer/base64 data to the Hedera network. Useful for inscribing content that has been read into memory, including files accessed through MCP filesystem tools.';
56
+ description = 'Inscribe content from a buffer/base64 data to the Hedera network. IMPORTANT: Only use this tool when you have actual content to inscribe. The base64Data must contain valid, non-empty content (minimum 10 bytes). Useful for inscribing content that has been read into memory, including files accessed through MCP filesystem tools. Always verify the content exists and is meaningful before attempting inscription.';
57
57
 
58
58
  get specificInputSchema() {
59
59
  return inscribeFromBufferSchema;
@@ -70,15 +70,44 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<typeof inscri
70
70
 
71
71
  if (!params.base64Data || params.base64Data.trim() === '') {
72
72
  console.log(`[InscribeFromBufferTool] ERROR: No data provided`);
73
- throw new Error('No data provided. Cannot inscribe empty content.');
73
+ throw new Error('No data provided. Cannot inscribe empty content. Please provide valid base64 encoded data.');
74
74
  }
75
75
 
76
- const buffer = Buffer.from(params.base64Data, 'base64');
76
+ if (!params.fileName || params.fileName.trim() === '') {
77
+ console.log(`[InscribeFromBufferTool] ERROR: No fileName provided`);
78
+ throw new Error('No fileName provided. A valid fileName is required for inscription.');
79
+ }
80
+
81
+ let buffer: Buffer;
82
+ try {
83
+ buffer = Buffer.from(params.base64Data, 'base64');
84
+ } catch (error) {
85
+ console.log(`[InscribeFromBufferTool] ERROR: Invalid base64 data`);
86
+ throw new Error('Invalid base64 data provided. Please ensure the data is properly base64 encoded.');
87
+ }
88
+
77
89
  console.log(`[InscribeFromBufferTool] Buffer length after conversion: ${buffer.length}`);
78
90
 
79
91
  if (buffer.length === 0) {
80
92
  console.log(`[InscribeFromBufferTool] ERROR: Buffer is empty after conversion`);
81
- throw new Error('Buffer is empty. Cannot inscribe empty content.');
93
+ throw new Error('Buffer is empty after base64 conversion. The provided data appears to be invalid or empty.');
94
+ }
95
+
96
+ if (buffer.length < 10) {
97
+ console.log(`[InscribeFromBufferTool] WARNING: Buffer is very small (${buffer.length} bytes)`);
98
+ console.log(`[InscribeFromBufferTool] Buffer content preview: ${buffer.toString('utf8', 0, Math.min(buffer.length, 50))}`);
99
+ throw new Error(`Buffer content is too small (${buffer.length} bytes). This may indicate empty or invalid content. Please verify the source data contains actual content.`);
100
+ }
101
+
102
+ const isValidBase64 = /^[A-Za-z0-9+/]*={0,2}$/.test(params.base64Data);
103
+ if (!isValidBase64) {
104
+ console.log(`[InscribeFromBufferTool] ERROR: Invalid base64 format`);
105
+ throw new Error('Invalid base64 format. The data does not appear to be properly base64 encoded.');
106
+ }
107
+
108
+ if (buffer.toString('utf8', 0, Math.min(buffer.length, 100)).trim() === '') {
109
+ console.log(`[InscribeFromBufferTool] ERROR: Buffer contains only whitespace or empty content`);
110
+ throw new Error('Buffer contains only whitespace or empty content. Cannot inscribe meaningless data.');
82
111
  }
83
112
 
84
113
  const options: InscriptionOptions = {
@@ -9,7 +9,7 @@ import * as path from 'path';
9
9
  * Schema for inscribing from file
10
10
  */
11
11
  const inscribeFromFileSchema = z.object({
12
- filePath: z.string().describe('The file path of the content to inscribe'),
12
+ filePath: z.string().min(1, 'File path cannot be empty').describe('The file path of the content to inscribe. Must point to a valid, non-empty file.'),
13
13
  mode: z
14
14
  .enum(['file', 'hashinal'])
15
15
  .optional()
@@ -49,7 +49,7 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
49
49
  > {
50
50
  name = 'inscribeFromFile';
51
51
  description =
52
- 'Inscribe content from a local file to the Hedera network using a file path. For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead.';
52
+ 'Inscribe content from a local file to the Hedera network using a file path. IMPORTANT: Only use this tool when you have a valid file path to actual content. The file must exist and contain meaningful data (minimum 10 bytes). For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead.';
53
53
 
54
54
  get specificInputSchema(): typeof inscribeFromFileSchema {
55
55
  return inscribeFromFileSchema;
@@ -76,19 +76,46 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
76
76
 
77
77
  if (stats.size === 0) {
78
78
  throw new Error(
79
- `File "${params.filePath}" is empty. Cannot inscribe empty files.`
79
+ `File "${params.filePath}" is empty (0 bytes). Cannot inscribe empty files.`
80
80
  );
81
81
  }
82
82
 
83
+ if (stats.size < 10) {
84
+ throw new Error(
85
+ `File "${params.filePath}" is too small (${stats.size} bytes). Files must contain at least 10 bytes of meaningful content.`
86
+ );
87
+ }
88
+
89
+ if (stats.size > 100 * 1024 * 1024) {
90
+ console.log(`[InscribeFromFileTool] WARNING: Large file detected (${(stats.size / (1024 * 1024)).toFixed(2)} MB)`);
91
+ }
92
+
83
93
  this.logger?.info('Reading file content...');
84
94
  fileContent = await fs.readFile(params.filePath);
85
95
  this.logger?.info(`Read ${fileContent.length} bytes from file`);
86
96
 
87
97
  if (!fileContent || fileContent.length === 0) {
88
98
  throw new Error(
89
- `File "${params.filePath}" has no content. Cannot inscribe empty files.`
99
+ `File "${params.filePath}" has no content after reading. Cannot inscribe empty files.`
100
+ );
101
+ }
102
+
103
+ if (fileContent.length < 10) {
104
+ throw new Error(
105
+ `File "${params.filePath}" content is too small (${fileContent.length} bytes). Files must contain at least 10 bytes of meaningful content.`
90
106
  );
91
107
  }
108
+
109
+ const fileName = path.basename(params.filePath);
110
+ const mimeType = this.getMimeType(fileName);
111
+ if (mimeType.startsWith('text/') || mimeType === 'application/json') {
112
+ const textContent = fileContent.toString('utf8', 0, Math.min(fileContent.length, 1000));
113
+ if (textContent.trim() === '') {
114
+ throw new Error(
115
+ `File "${params.filePath}" contains only whitespace or empty content. Cannot inscribe meaningless data.`
116
+ );
117
+ }
118
+ }
92
119
  } catch (error) {
93
120
  if (error instanceof Error) {
94
121
  if (error.message.includes('ENOENT')) {
@@ -42,7 +42,7 @@ const inscribeFromUrlSchema = z.object({
42
42
  */
43
43
  export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeFromUrlSchema> {
44
44
  name = 'inscribeFromUrl';
45
- description = 'Inscribe content from a URL to the Hedera network';
45
+ description = 'Inscribe content from a URL to the Hedera network. IMPORTANT: Only use this tool when you have a valid URL pointing to actual content. The URL must be accessible and return meaningful data. The tool will validate that the content exists and is not empty before inscription.';
46
46
 
47
47
  get specificInputSchema() {
48
48
  return inscribeFromUrlSchema;
@@ -52,6 +52,68 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
52
52
  params: z.infer<typeof inscribeFromUrlSchema>,
53
53
  _runManager?: CallbackManagerForToolRun
54
54
  ): Promise<unknown> {
55
+ console.log(`[DEBUG] InscribeFromUrlTool.executeQuery called with URL: ${params.url}`);
56
+
57
+ if (!params.url || params.url.trim() === '') {
58
+ throw new Error('URL cannot be empty. Please provide a valid URL.');
59
+ }
60
+
61
+ try {
62
+ const urlObj = new URL(params.url);
63
+ if (!urlObj.protocol || !urlObj.host) {
64
+ throw new Error('Invalid URL format. Please provide a complete URL with protocol (http/https).');
65
+ }
66
+ if (!['http:', 'https:'].includes(urlObj.protocol)) {
67
+ throw new Error('Only HTTP and HTTPS URLs are supported for inscription.');
68
+ }
69
+ } catch (error) {
70
+ throw new Error(`Invalid URL: ${params.url}. Please provide a valid URL.`);
71
+ }
72
+
73
+ console.log(`[InscribeFromUrlTool] Validating URL content before inscription...`);
74
+ try {
75
+ const controller = new AbortController();
76
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
77
+
78
+ try {
79
+ const response = await fetch(params.url, {
80
+ method: 'HEAD',
81
+ signal: controller.signal,
82
+ });
83
+
84
+ clearTimeout(timeoutId);
85
+
86
+ if (!response.ok) {
87
+ throw new Error(`URL returned error status ${response.status}: ${response.statusText}. Cannot inscribe content from inaccessible URLs.`);
88
+ }
89
+
90
+ const contentLength = response.headers.get('content-length');
91
+ if (contentLength && parseInt(contentLength) === 0) {
92
+ throw new Error('URL returns empty content (Content-Length: 0). Cannot inscribe empty content.');
93
+ }
94
+
95
+ if (contentLength && parseInt(contentLength) < 10) {
96
+ throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);
97
+ }
98
+
99
+ const contentType = response.headers.get('content-type');
100
+ console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || 'unknown'}`);
101
+ } catch (fetchError) {
102
+ clearTimeout(timeoutId);
103
+ throw fetchError;
104
+ }
105
+ } catch (error) {
106
+ if (error instanceof Error) {
107
+ if (error.name === 'AbortError') {
108
+ console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);
109
+ } else if (error.message.includes('URL returned error') || error.message.includes('empty content') || error.message.includes('too small')) {
110
+ throw error;
111
+ } else {
112
+ console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);
113
+ }
114
+ }
115
+ }
116
+
55
117
  const options: InscriptionOptions = {
56
118
  mode: params.mode,
57
119
  metadata: params.metadata,