@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
JavaScript
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