@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.
138 lines (120 loc) • 5.17 kB
text/typescript
import { StructuredTool, ToolParams } from '@langchain/core/tools';
import { z } from 'zod';
import { HCS10Client } from '../hcs10/HCS10Client';
import { IStateManager, ActiveConnection } from '../state/state-types';
import { Logger } from '@hashgraphonline/standards-sdk';
export interface InitiateConnectionToolParams extends ToolParams {
hcsClient: HCS10Client;
stateManager: IStateManager;
}
/**
* A tool to actively START a NEW HCS-10 connection TO a target agent.
* Requires the target agent's account ID.
* It retrieves their profile, sends a connection request, waits for confirmation, and stores the connection using the provided stateManager.
* Use this tool ONLY to actively INITIATE an OUTGOING connection.
*/
export class InitiateConnectionTool extends StructuredTool {
name = 'initiate_connection';
description =
'Actively STARTS a NEW HCS-10 connection TO a specific target agent identified by their account ID. Requires the targetAccountId parameter. Use this ONLY to INITIATE an OUTGOING connection request.';
schema = z.object({
targetAccountId: z
.string()
.describe(
'The Hedera account ID (e.g., 0.0.12345) of the agent you want to connect with.'
)
});
private hcsClient: HCS10Client;
private stateManager: IStateManager;
private logger: Logger;
constructor({
hcsClient,
stateManager,
...rest
}: InitiateConnectionToolParams) {
super(rest);
this.hcsClient = hcsClient;
this.stateManager = stateManager;
this.logger = Logger.getInstance({ module: 'InitiateConnectionTool' });
}
protected async _call({
targetAccountId
}: z.infer<this['schema']>): Promise<string> {
const currentAgent = this.stateManager.getCurrentAgent();
if (!currentAgent) {
return 'Error: Cannot initiate connection. No agent is currently active. Please register or select an agent first.';
}
this.logger.info(
`Attempting connection from ${currentAgent.accountId} to ${targetAccountId}`
);
try {
this.logger.debug(`Retrieving profile for ${targetAccountId}...`);
const targetProfile = await this.hcsClient.getAgentProfile(
targetAccountId
);
if (!targetProfile?.topicInfo?.inboundTopic) {
return `Error: Could not retrieve profile or find inbound topic ID for target agent ${targetAccountId}. They might not be registered or have a public profile.`;
}
const targetInboundTopicId = targetProfile.topicInfo.inboundTopic;
const targetAgentName =
targetProfile.profile.name || `Agent ${targetAccountId}`;
const requestResult = await this.hcsClient.submitConnectionRequest(
targetInboundTopicId,
currentAgent.name
);
const sequenceNumberLong = requestResult?.topicSequenceNumber;
if (!sequenceNumberLong) {
throw new Error('Connection request sequence number not found.');
}
let connectionRequestId: number;
try {
connectionRequestId = sequenceNumberLong.toNumber();
if (isNaN(connectionRequestId)) {
throw new Error('Converted sequence number is NaN.');
}
} catch (conversionError) {
throw new Error(
`Failed to convert connection request sequence number: ${conversionError}`
);
}
const confirmationTimeoutMs = 60000;
const delayMs = 2000;
const maxAttempts = Math.ceil(confirmationTimeoutMs / delayMs);
const confirmationResult =
await this.hcsClient.waitForConnectionConfirmation(
targetInboundTopicId,
connectionRequestId,
maxAttempts,
delayMs
);
if (!confirmationResult?.connectionTopicId) {
return `Error: Connection confirmation not received from ${targetAccountId} (for request ${connectionRequestId}) within ${
confirmationTimeoutMs / 1000
} seconds.`;
}
const connectionTopicId = confirmationResult.connectionTopicId;
this.logger.info(`Connection confirmed! Topic ID: ${connectionTopicId}`);
const newConnection: ActiveConnection = {
targetAccountId: targetAccountId,
targetAgentName: targetAgentName,
targetInboundTopicId: targetInboundTopicId,
connectionTopicId: connectionTopicId,
};
this.stateManager.addActiveConnection(newConnection);
const connections = this.stateManager.listConnections();
const addedEntry = connections.find(
(c) => c.connectionTopicId === connectionTopicId
);
const localConnectionId = addedEntry
? connections.indexOf(addedEntry) + 1
: null;
const idString = localConnectionId ? `#${localConnectionId}` : '';
return `Successfully established connection ${idString} with ${targetAgentName} (${targetAccountId}). Connection Topic: ${connectionTopicId}. You can now send messages using this connection.`;
} catch (error) {
this.logger.error(`Connection initiation failed: ${error}`);
return `Error initiating connection with ${targetAccountId}: ${
error instanceof Error ? error.message : String(error)
}`;
}
}
}