@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
text/typescript
// 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;
}
}