@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org
252 lines (226 loc) • 7.5 kB
text/typescript
import {
GenericPluginContext,
HederaTool,
BasePlugin,
HederaAgentKit,
} from 'hedera-agent-kit';
import {
IStateManager,
OpenConvaiState,
HCS10Builder,
RegisterAgentTool,
FindRegistrationsTool,
InitiateConnectionTool,
ListConnectionsTool,
SendMessageToConnectionTool,
CheckMessagesTool,
ConnectionMonitorTool,
ManageConnectionRequestsTool,
AcceptConnectionRequestTool,
RetrieveProfileTool,
ListUnapprovedConnectionRequestsTool,
RegisteredAgent,
} from '@hashgraphonline/standards-agent-kit';
import { HCS10Client } from '@hashgraphonline/standards-sdk';
import { PrivateKey } from 'node_modules/@hashgraph/sdk/lib/Mnemonic';
interface HCS10ClientManager {
initializeConnectionsManager(client: HCS10Client): void;
}
function hasInitializeConnectionsManager(
stateManager: IStateManager
): stateManager is IStateManager & HCS10ClientManager {
return (
typeof stateManager === 'object' &&
stateManager !== null &&
'initializeConnectionsManager' in stateManager &&
typeof stateManager.initializeConnectionsManager === 'function'
);
}
export class HCS10Plugin extends BasePlugin {
id = 'hcs-10';
name = 'HCS-10 Plugin';
description =
'HCS-10 agent tools for decentralized agent registration, connections, and messaging on Hedera';
version = '1.0.0';
author = 'Hashgraph Online';
namespace = 'hcs10';
private stateManager?: IStateManager;
private tools: any[] = [];
appConfig?: Record<string, unknown>;
override async initialize(context: GenericPluginContext): Promise<void> {
await super.initialize(context);
const hederaKit = context.config.hederaKit as HederaAgentKit;
if (!hederaKit) {
this.context.logger.warn(
'HederaKit not found in context. HCS-10 tools will not be available.'
);
return;
}
try {
this.stateManager =
(context.stateManager as IStateManager) ||
(context.config.stateManager as IStateManager) ||
(this.appConfig?.stateManager as IStateManager) ||
new OpenConvaiState();
const accountId = hederaKit.signer.getAccountId().toString();
const isBytesMode =
String(hederaKit.operationalMode || 'returnBytes') === 'returnBytes';
let inboundTopicId = '';
let outboundTopicId = '';
let operatorPrivateKeyRef: PrivateKey =
hederaKit.signer.getOperatorPrivateKey();
let operatorPrivateKeySerialized: string | undefined;
try {
const resolved =
typeof operatorPrivateKeyRef?.toString === 'function'
? operatorPrivateKeyRef.toString()
: '';
operatorPrivateKeySerialized = resolved;
const hcs10Client = new HCS10Client({
network: hederaKit.network as 'mainnet' | 'testnet',
operatorId: accountId,
operatorPrivateKey: operatorPrivateKeyRef,
logLevel: 'error',
});
const profileResponse = await hcs10Client.retrieveProfile(accountId);
if (profileResponse.success && profileResponse.topicInfo) {
inboundTopicId = profileResponse.topicInfo.inboundTopic;
outboundTopicId = profileResponse.topicInfo.outboundTopic;
}
} catch (profileError) {
this.context.logger.warn(
'Skipping profile topic discovery',
profileError
);
}
const agentRecord: Record<string, unknown> = {
name: `Agent ${accountId}`,
accountId: accountId,
inboundTopicId,
outboundTopicId,
};
if (!isBytesMode && operatorPrivateKeySerialized) {
agentRecord.privateKey = operatorPrivateKeySerialized;
}
this.stateManager.setCurrentAgent(
agentRecord as unknown as RegisteredAgent
);
this.context.logger.info(
`Set current agent: ${accountId} with topics ${inboundTopicId}/${outboundTopicId}`
);
if (
!isBytesMode &&
this.stateManager &&
!this.stateManager.getConnectionsManager()
) {
try {
const hcs10Client = new HCS10Client({
network: hederaKit.network as 'mainnet' | 'testnet',
operatorId: accountId,
operatorPrivateKey: operatorPrivateKeyRef ?? '',
logLevel: 'error',
});
if (hasInitializeConnectionsManager(this.stateManager)) {
this.stateManager.initializeConnectionsManager(hcs10Client);
} else {
this.context.logger.warn(
'StateManager does not support connection manager initialization'
);
}
this.context.logger.info(
'ConnectionsManager initialized in HCS10Plugin'
);
} catch (cmError) {
this.context.logger.warn(
'Could not initialize ConnectionsManager:',
cmError
);
}
}
this.initializeTools();
this.context.logger.info('HCS-10 Plugin initialized successfully');
} catch (error) {
this.context.logger.error('Failed to initialize HCS-10 plugin:', error);
}
}
private initializeTools(): void {
if (!this.stateManager) {
throw new Error('StateManager must be initialized before creating tools');
}
const hederaKit = this.context.config.hederaKit as HederaAgentKit;
if (!hederaKit) {
throw new Error('HederaKit not found in context config');
}
const hcs10Builder = new HCS10Builder(hederaKit, this.stateManager);
this.tools = [
new RegisterAgentTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new FindRegistrationsTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new RetrieveProfileTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new InitiateConnectionTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new ListConnectionsTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new SendMessageToConnectionTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new CheckMessagesTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new ConnectionMonitorTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new ManageConnectionRequestsTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new AcceptConnectionRequestTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
new ListUnapprovedConnectionRequestsTool({
hederaKit: hederaKit,
hcs10Builder: hcs10Builder,
logger: this.context.logger,
}),
];
}
getTools(): HederaTool[] {
return this.tools;
}
getStateManager(): IStateManager | undefined {
return this.stateManager;
}
override async cleanup(): Promise<void> {
this.tools = [];
delete this.stateManager;
if (this.context?.logger) {
this.context.logger.info('HCS-10 Plugin cleaned up');
}
}
}