UNPKG

@hashgraphonline/standards-agent-kit

Version:

A modular SDK for building on-chain autonomous agents using Hashgraph Online Standards, including HCS-10 for agent discovery and communication.

143 lines (122 loc) 5.01 kB
// src/tools/send-message-to-connection-tool.ts import { StructuredTool, ToolParams } from '@langchain/core/tools'; import { z } from 'zod'; import { HCS10Client } from '../hcs10/HCS10Client'; import { IStateManager } from '../state/state-types'; import { Logger } from '@hashgraphonline/standards-sdk'; // Assuming logger utility export interface SendMessageToConnectionToolParams extends ToolParams { hcsClient: HCS10Client; stateManager: IStateManager; } /** * A tool to send a message to an agent over an established HCS-10 connection. */ export class SendMessageToConnectionTool extends StructuredTool { name = 'send_message_to_connection'; description = "Sends a text message to another agent using an existing active connection. Identify the target agent using their account ID (e.g., 0.0.12345) or the connection number shown in 'list_connections'. Return back the reply from the target agent if possible"; schema = z.object({ targetIdentifier: z .string() .describe( "The account ID (e.g., 0.0.12345) of the target agent OR the connection number (e.g., '1', '2') from the 'list_connections' tool." ), message: z.string().describe('The text message content to send.'), disableMonitoring: z.boolean().optional().default(false), }); private hcsClient: HCS10Client; private stateManager: IStateManager; private logger: Logger; constructor({ hcsClient, stateManager, ...rest }: SendMessageToConnectionToolParams) { super(rest); this.hcsClient = hcsClient; this.stateManager = stateManager; this.logger = Logger.getInstance({ module: 'SendMessageToConnectionTool' }); } protected async _call({ targetIdentifier, message, disableMonitoring, }: z.infer<this['schema']>): Promise<string> { const currentAgent = this.stateManager.getCurrentAgent(); if (!currentAgent) { return 'Error: Cannot send message. No agent is currently active. Please register or select an agent first.'; } const connection = this.stateManager.getConnectionByIdentifier(targetIdentifier); if (!connection) { return `Error: Could not find an active connection matching identifier "${targetIdentifier}". Use 'list_connections' to see active connections.`; } const connectionTopicId = connection.connectionTopicId; const targetAgentName = connection.targetAgentName; // Construct the sender's operator ID const operatorId = `${currentAgent.inboundTopicId}@${currentAgent.accountId}`; this.logger.info( `Sending message from ${operatorId} to ${targetAgentName} (${connection.targetAccountId}) via connection topic ${connectionTopicId}` ); try { // Call sendMessage with correct arguments const sequenceNumber = await this.hcsClient.sendMessage( connectionTopicId, message, // Message content as 'data' `Agent message from ${currentAgent.name}` // Optional memo ); if (!sequenceNumber) { throw new Error('Failed to send message'); } if (!disableMonitoring) { return `Message sent to ${targetAgentName} (${connection.targetAccountId}) via connection ${connectionTopicId}. Sequence Number: ${sequenceNumber}`; } const replyBack = await this.monitorResponses( connectionTopicId, operatorId, sequenceNumber ); if (replyBack) { this.logger.info(`Got reply from ${targetAgentName}`, replyBack); // Format the return string clearly as an observation/reply return `Received reply from ${targetAgentName}: ${replyBack}`; } // Return message based on the status string if no reply was received/awaited return `Message sent to ${targetAgentName} (${connection.targetAccountId}) via connection ${connectionTopicId}. Sequence Number: ${sequenceNumber}`; } catch (error) { this.logger.error( `Failed to send message via connection ${connectionTopicId}: ${error}` ); return `Error sending message to ${targetAgentName}: ${ error instanceof Error ? error.message : String(error) }`; } } private async monitorResponses( topicId: string, operatorId: string, sequenceNumber: number ): Promise<string | null> { const maxAttempts = 30; const attempts = 0; while (attempts < maxAttempts) { try { const messages = await this.hcsClient.getMessageStream(topicId); for (const message of messages.messages) { if ( message.sequence_number < sequenceNumber || message.operator_id === operatorId ) { continue; } const content = await this.hcsClient.getMessageContent(message.data || ''); return content; } } catch (error) { this.logger.error(`Error monitoring responses: ${error}`); } await new Promise((resolve) => setTimeout(resolve, 4000)); } return null; } }