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.

169 lines (168 loc) 6.87 kB
import { StructuredTool } from "@langchain/core/tools"; import { z } from "zod"; import { Logger } from "@hashgraphonline/standards-sdk"; class ConnectionTool extends StructuredTool { /** * @param client - Instance of HCS10Client. * @param stateManager - Instance of StateManager for shared state management. */ constructor({ client, stateManager, ...rest }) { super(rest); this.name = "monitor_connections"; this.description = "Starts passively LISTENING on the current agent's own inbound topic for INCOMING HCS-10 connection requests. Handles received requests automatically. Takes NO arguments. DO NOT use this to start a new connection TO someone else."; this.isMonitoring = false; this.monitoringTopic = null; this.schema = z.object({}); this.client = client; this.stateManager = stateManager; this.logger = Logger.getInstance({ module: "ConnectionTool", level: "info" }); } /** * Initiates the connection request monitoring process in the background. * Gets the inbound topic ID from the configured client. */ async _call() { let inboundTopicId; try { inboundTopicId = await this.client.getInboundTopicId(); } catch (error) { const errorMsg = `Error getting inbound topic ID for monitoring: ${error instanceof Error ? error.message : String(error)}`; this.logger.error(errorMsg); return errorMsg; } if (!inboundTopicId) { return "Error: Could not determine the inbound topic ID for the current agent."; } if (this.isMonitoring) { if (this.monitoringTopic === inboundTopicId) { return `Already monitoring topic ${inboundTopicId}.`; } else { return `Error: Already monitoring a different topic (${this.monitoringTopic}). Stop the current monitor first.`; } } this.isMonitoring = true; this.monitoringTopic = inboundTopicId; this.logger.info( `Initiating connection request monitoring for topic ${inboundTopicId}...` ); this.monitorIncomingRequests(inboundTopicId).catch((error) => { this.logger.error( `Monitoring loop for ${inboundTopicId} encountered an unrecoverable error:`, error ); this.isMonitoring = false; this.monitoringTopic = null; }); return `Started monitoring inbound topic ${inboundTopicId} for connection requests in the background.`; } /** * The core monitoring loop. */ async monitorIncomingRequests(inboundTopicId) { this.logger.info(`Monitoring inbound topic ${inboundTopicId}...`); let lastProcessedMessageSequence = 0; const processedRequestIds = /* @__PURE__ */ new Set(); while (this.isMonitoring && this.monitoringTopic === inboundTopicId) { try { const messagesResult = await this.client.getMessages(inboundTopicId); const allMessages = messagesResult.messages; const connectionRequests = allMessages.filter( (msg) => msg.op === "connection_request" && typeof msg.sequence_number === "number" // Keep filtering by sequence number if needed, or remove if checking existing confirmations is sufficient // msg.sequence_number > lastProcessedMessageSequence // Temporarily remove or adjust this if checking confirmations is the primary method ); for (const message of connectionRequests) { lastProcessedMessageSequence = Math.max( lastProcessedMessageSequence, message.sequence_number || 0 // Use 0 if sequence_number is undefined (though filter should prevent this) ); const connectionRequestId = message.sequence_number; if (!connectionRequestId) { continue; } const alreadyHandled = allMessages.some( (m) => m.op === "connection_created" && m.connection_id === connectionRequestId ); if (alreadyHandled) { this.logger.debug( `Connection request #${connectionRequestId} already handled (found connection_created). Skipping.` ); continue; } const senderOperatorId = message.operator_id || ""; const requestingAccountId = senderOperatorId.split("@")[1] || null; if (!requestingAccountId) { this.logger.warn( `Could not determine requesting account ID from operator_id '${senderOperatorId}' for request #${connectionRequestId}. Skipping.` ); continue; } if (processedRequestIds.has(connectionRequestId)) { this.logger.info( `Connection request #${connectionRequestId} already processed in this session. Skipping.` ); continue; } this.logger.info( `Processing connection request #${connectionRequestId} from account ${requestingAccountId}...` ); try { const confirmation = await this.client.handleConnectionRequest( inboundTopicId, requestingAccountId, connectionRequestId ); processedRequestIds.add(connectionRequestId); this.logger.info( `Connection confirmed for request #${connectionRequestId}. New connection topic: ${confirmation.connectionTopicId}` ); const newConnection = { targetAccountId: requestingAccountId, targetAgentName: `Agent ${requestingAccountId}`, targetInboundTopicId: "N/A", connectionTopicId: confirmation.connectionTopicId }; this.stateManager.addActiveConnection(newConnection); this.logger.info( `Added new active connection to ${requestingAccountId} state.` ); } catch (handleError) { this.logger.error( `Error handling connection request #${connectionRequestId} from ${requestingAccountId}:`, handleError ); } } } catch (error) { this.logger.error( `Error fetching or processing messages for topic ${inboundTopicId}:`, error ); } await new Promise((resolve) => setTimeout(resolve, 5e3)); } this.logger.info(`Monitoring loop stopped for topic ${inboundTopicId}.`); this.isMonitoring = false; this.monitoringTopic = null; } // Optional: Method to explicitly stop monitoring stopMonitoring() { if (this.isMonitoring) { this.logger.info( `Stopping monitoring for topic ${this.monitoringTopic}...` ); this.isMonitoring = false; this.monitoringTopic = null; } else { this.logger.info("Monitor is not currently running."); } } } export { ConnectionTool }; //# sourceMappingURL=standards-agent-kit.es5.js.map