@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.
250 lines (249 loc) • 8.68 kB
JavaScript
import { updateEnvFile } from "./standards-agent-kit.es42.js";
import { Logger, ConnectionsManager } from "@hashgraphonline/standards-sdk";
class OpenConvaiState {
/**
* Creates a new OpenConvaiState instance
* @param options - Options for environment variable persistence
*/
constructor(options) {
this.currentAgent = null;
this.connectionMessageTimestamps = {};
this.connectionsManager = null;
this.defaultEnvFilePath = options?.defaultEnvFilePath;
this.defaultPrefix = options?.defaultPrefix ?? "TODD";
const shouldSilence = options?.disableLogging || process.env.DISABLE_LOGGING === "true";
this.logger = new Logger({ module: "OpenConvaiState", silent: shouldSilence });
if (options?.baseClient) {
this.initializeConnectionsManager(options.baseClient);
}
}
/**
* Initializes the ConnectionsManager
* @param baseClient - HCS10BaseClient instance to use
*/
initializeConnectionsManager(baseClient) {
if (!this.connectionsManager) {
this.logger.debug("Initializing ConnectionsManager");
this.connectionsManager = new ConnectionsManager({
baseClient,
logLevel: "error"
});
} else {
this.logger.debug("ConnectionsManager already initialized");
}
return this.connectionsManager;
}
/**
* Gets the ConnectionsManager instance
* @returns The ConnectionsManager instance, or null if not initialized
*/
getConnectionsManager() {
return this.connectionsManager;
}
/**
* Sets the current active agent and clears any previous connection data.
* This should be called when switching between agents.
*/
setCurrentAgent(agent) {
this.currentAgent = agent;
this.connectionMessageTimestamps = {};
if (this.connectionsManager) {
this.connectionsManager.clearAll();
}
}
/**
* Returns the currently active agent or null if none is set.
*/
getCurrentAgent() {
return this.currentAgent;
}
/**
* Adds a new connection to the active connections list.
* Ensures no duplicates are added based on connectionTopicId.
* Initializes timestamp tracking for the connection.
*/
addActiveConnection(connection) {
if (!this.connectionsManager) {
this.logger.error(
"ConnectionsManager not initialized. Call initializeConnectionsManager before adding connections."
);
throw new Error(
"ConnectionsManager not initialized. Call initializeConnectionsManager before adding connections."
);
}
const sdkConnection = {
connectionTopicId: connection.connectionTopicId,
targetAccountId: connection.targetAccountId,
targetAgentName: connection.targetAgentName,
targetInboundTopicId: connection.targetInboundTopicId,
status: this.convertConnectionStatus(connection.status || "established"),
isPending: connection.isPending || false,
needsConfirmation: connection.needsConfirmation || false,
created: connection.created || /* @__PURE__ */ new Date(),
lastActivity: connection.lastActivity,
profileInfo: connection.profileInfo,
connectionRequestId: connection.connectionRequestId,
processed: true
};
this.connectionsManager.updateOrAddConnection(sdkConnection);
this.initializeTimestampIfNeeded(connection.connectionTopicId);
}
/**
* Updates an existing connection or adds it if not found.
* Preserves existing properties when updating by merging objects.
*/
updateOrAddConnection(connection) {
this.addActiveConnection(connection);
}
/**
* Returns a copy of all active connections.
*/
listConnections() {
if (!this.connectionsManager) {
this.logger.debug(
"ConnectionsManager not initialized, returning empty connections list"
);
return [];
}
return this.connectionsManager.getAllConnections().map((conn) => this.convertToActiveConnection(conn));
}
/**
* Finds a connection by its identifier, which can be:
* - A 1-based index as displayed in the connection list
* - A target account ID string
* - A connection topic ID string
*/
getConnectionByIdentifier(identifier) {
if (!this.connectionsManager) {
return void 0;
}
const connections = this.listConnections();
const numericIndex = parseInt(identifier) - 1;
if (!isNaN(numericIndex) && numericIndex >= 0 && numericIndex < connections.length) {
return connections[numericIndex];
}
const byTopicId = this.connectionsManager.getConnectionByTopicId(identifier);
if (byTopicId) {
return this.convertToActiveConnection(byTopicId);
}
const byAccountId = this.connectionsManager.getConnectionByAccountId(identifier);
if (byAccountId) {
return this.convertToActiveConnection(byAccountId);
}
return void 0;
}
/**
* Gets the last processed message timestamp for a connection.
* Returns 0 if no timestamp has been recorded.
*/
getLastTimestamp(connectionTopicId) {
return this.connectionMessageTimestamps[connectionTopicId] || 0;
}
/**
* Updates the last processed message timestamp for a connection,
* but only if the new timestamp is more recent than the existing one.
*/
updateTimestamp(connectionTopicId, timestampNanos) {
if (!(connectionTopicId in this.connectionMessageTimestamps)) {
this.connectionMessageTimestamps[connectionTopicId] = timestampNanos;
return;
}
const currentTimestamp = this.connectionMessageTimestamps[connectionTopicId];
if (timestampNanos > currentTimestamp) {
this.connectionMessageTimestamps[connectionTopicId] = timestampNanos;
}
}
/**
* Helper method to initialize timestamp tracking for a connection
* if it doesn't already exist.
*/
initializeTimestampIfNeeded(connectionTopicId) {
if (!(connectionTopicId in this.connectionMessageTimestamps)) {
this.connectionMessageTimestamps[connectionTopicId] = Date.now() * 1e6;
}
}
/**
* Converts ConnectionStatus to SDK status format
*/
convertConnectionStatus(status) {
switch (status) {
case "pending":
return "pending";
case "established":
return "established";
case "needs confirmation":
return "needs_confirmation";
default:
return "established";
}
}
/**
* Converts SDK Connection to ActiveConnection
*/
convertToActiveConnection(conn) {
return {
targetAccountId: conn.targetAccountId,
targetAgentName: conn.targetAgentName || `Agent ${conn.targetAccountId}`,
targetInboundTopicId: conn.targetInboundTopicId || "",
connectionTopicId: conn.connectionTopicId,
status: this.convertToStateStatus(conn.status),
created: conn.created,
lastActivity: conn.lastActivity,
isPending: conn.isPending,
needsConfirmation: conn.needsConfirmation,
profileInfo: conn.profileInfo,
connectionRequestId: conn.connectionRequestId
};
}
/**
* Converts SDK status to state status format
*/
convertToStateStatus(status) {
switch (status) {
case "pending":
return "pending";
case "established":
return "established";
case "needs_confirmation":
return "needs confirmation";
case "closed":
return "established";
// Mapping closed to established for compatibility
default:
return "unknown";
}
}
/**
* Persists agent data to environment variables
* @param agent - The agent data to persist
* @param options - Environment file persistence options
*/
async persistAgentData(agent, options) {
if (options?.type && options.type !== "env-file") {
throw new Error(
`Unsupported persistence type: ${options.type}. Only 'env-file' is supported.`
);
}
const envFilePath = options?.envFilePath || this.defaultEnvFilePath || process.env.ENV_FILE_PATH || ".env";
const prefix = options?.prefix || this.defaultPrefix;
if (!agent.accountId || !agent.inboundTopicId || !agent.outboundTopicId) {
throw new Error("Agent data incomplete, cannot persist to environment");
}
const updates = {
[`${prefix}_ACCOUNT_ID`]: agent.accountId,
[`${prefix}_INBOUND_TOPIC_ID`]: agent.inboundTopicId,
[`${prefix}_OUTBOUND_TOPIC_ID`]: agent.outboundTopicId
};
if (agent.privateKey) {
updates[`${prefix}_PRIVATE_KEY`] = agent.privateKey;
}
if (agent.profileTopicId) {
updates[`${prefix}_PROFILE_TOPIC_ID`] = agent.profileTopicId;
}
await updateEnvFile(envFilePath, updates);
}
}
export {
OpenConvaiState
};
//# sourceMappingURL=standards-agent-kit.es38.js.map