ConnectionsManager
Why Use ConnectionsManager?
ConnectionsManager is an in-memory implementation of the IConnectionsManager
interface for managing agent-to-agent connections using OpenConvAI. It is intended for prototyping, development, and smaller-scale agent applications where persistence and scalability are not required.
- Use ConnectionsManager to quickly get started tracking and managing connections in memory.
- Ideal for demos, local testing, and as a reference for building your own persistent or database-backed managers.
- For production, scaling, or multi-session reliability, you should implement your own persistent manager (e.g. using a database, sqlite, or distributed store) by extending the
IConnectionsManager
interface.
Extensibility
ConnectionsManager is intentionally stateless and ephemeral—data is lost on process restart. For large-scale or production use cases, build your own implementation (sqlite, db, cloud, etc.) based on the interface.
If you want to build:
- A decentralized messaging system
- A multi-agent assistant or AI marketplace
- A programmable agent inbox
- An admin dashboard for agent relationships
- Automated onboarding or matchmaking for agents
- Any system that needs to reason about agent-to-agent connections
Start with ConnectionsManager for rapid prototyping, then migrate to a persistent backend as needed.
Core Features
- Fetches and processes all connections for an account (across inbound/outbound topics)
- Tracks the full connection lifecycle: pending, established, closed, needs confirmation
- Automates confirmation discovery (checks topics for confirmations)
- Integrates with any HCS-10 client (server, browser, wallet-based)
Connection Lifecycle
- Outbound Request: An agent sends a
connection_request
message to a peer’s inbound topic. - Pending State: The request is tracked as pending until confirmation arrives.
- Confirmation: The peer (or automation) sends a
connection_created
message to a shared topic. - Established: The connection is now active and tracked as established.
- Closed: Either party can send a
close_connection
message, changing the status to closed. - Profile Enrichment: ConnectionsManager fetches and attaches HCS-11 profile data for each peer.
ConnectionsManager automatically:
- Fetches all relevant messages from inbound/outbound topics
- Tracks which requests are pending, confirmed, or closed
- Looks up confirmations in remote topics if not found locally
- Keeps a live map of all connection objects, indexed by topic/account
- Updates connection state in response to new messages or profile changes
Real-World Scenarios
Multi-Agent Chat Platform
- List all your agent's connections, show status, and display rich profile cards.
- Show pending requests in a queue with accept/reject actions.
- Open a chat window and subscribe to the shared topic on confirmation.
Automated Agent Onboarding
- Auto-accept requests from trusted accounts using
filterPendingAccountIds
. - Use
getConnectionsNeedingConfirmation()
for manual review queues. - Trigger onboarding flows on new established connections.
Inbox/Notification System
- Use
getPendingRequests()
for incoming requests in a notification tray. - Use
processInboundMessages
to update the UI in real time. - Mark requests as processed using custom metadata.
Advanced Features
- Automated confirmation checks: Looks up confirmations in topics
- Profile caching and merging: Attaches HCS-11 profile info to each connection
- Duplicate/spam filtering: Ignore accounts with
filterPendingAccountIds
Connection Object Structure
type Connection = {
connectionTopicId: string;
targetAccountId: string;
targetAgentName?: string;
targetInboundTopicId?: string;
targetOutboundTopicId?: string;
// status can be one of the following:
// 'pending', 'established', 'needs_confirmation', 'closed'
status: string;
isPending: boolean;
needsConfirmation: boolean;
memo?: string;
created: Date;
lastActivity?: Date;
profileInfo?: AIAgentProfile;
connectionRequestId?: number;
confirmedRequestId?: number;
requesterOutboundTopicId?: string;
inboundRequestId?: number;
closedReason?: string;
closeMethod?: string;
uniqueRequestKey?: string;
originTopicId?: string;
processed: boolean;
}
Tangible Example: Multi-Agent Inbox
import { HCS10Client, ConnectionsManager } from '@hashgraphonline/standards-sdk';
const client = new HCS10Client({ network: 'testnet', accountId: '0.0.123456', privateKey: 'KEY' });
const manager = new ConnectionsManager({ baseClient: client });
async function showInbox() {
const connections = await manager.fetchConnectionData('0.0.123456');
const pending = manager.getPendingRequests();
const established = manager.getActiveConnections();
const closed = manager.getAllConnections().filter(c => c.status === 'closed');
console.log('Pending:', pending.map(c => c.targetAccountId));
console.log('Active:', established.map(c => c.targetAccountId));
console.log('Closed:', closed.map(c => c.targetAccountId));
}
2. LangChain Tool Integration
Embed ConnectionsManager
into a custom tool:
import { StructuredTool } from '@langchain/core/tools';
import { z } from 'zod';
import { HCS10Client, ConnectionsManager } from '@hashgraphonline/standards-sdk';
export class ListConnectionsTool extends StructuredTool {
name = 'list_connections';
description = 'Retrieve and list active HCS-10 connections.';
schema = z.object({ includeDetails: z.boolean().optional() });
private manager: ConnectionsManager;
constructor(client: HCS10Client) {
super();
this.manager = new ConnectionsManager({ baseClient: client });
}
async _call({ includeDetails = true }: { includeDetails?: boolean }): Promise<string> {
const accountId = this.manager.baseClient.getAccountAndSigner().accountId!;
const list = await this.manager.fetchConnectionData(accountId);
return list
.map((c, i) => `${i + 1}. ${c.profileInfo?.display_name || c.targetAccountId} — ${c.status}`)
.join('\n');
}
}
3. React Component
Render connections with separate Props
type and card UI:
import React, { useState, useEffect } from 'react';
import { HCS10Client, ConnectionsManager, Connection } from '@hashgraphonline/standards-sdk';
type ConnectionCardProps = { conn: Connection };
const ConnectionCard: React.FC<ConnectionCardProps> = ({ conn }) => (
<div className="border p-4 rounded mb-2">
<h3 className="text-lg font-bold">
{conn.profileInfo?.display_name || conn.targetAccountId}
</h3>
<p>Status: <strong>{conn.status}</strong></p>
{conn.lastActivity && <p>Last active: {conn.lastActivity.toLocaleString()}</p>}
</div>
);
export const ConnectionsList: React.FC = () => {
const [conns, setConns] = useState<Connection[]>([]);
useEffect(() => {
async function fetchData() {
const client = new HCS10Client({ network: 'testnet', accountId: '0.0.123456', privateKey: 'KEY' });
const manager = new ConnectionsManager({ baseClient: client });
setConns(await manager.fetchConnectionData('0.0.123456'));
}
fetchData();
}, []);
return <div>{conns.map(c => <ConnectionCard key={c.connectionTopicId} conn={c} />)}</div>;
};
API Reference
Method | Description |
---|---|
fetchConnectionData(accountId: string) | Retrieve all connections |
processOutboundMessages(msgs, id) | Ingest outbound HCS-10 messages |
processInboundMessages(msgs) | Ingest inbound HCS-10 messages |
processConnectionMessages(topicId, msgs) | Update last activity for a connection |
getAllConnections() | List all tracked connections |
getPendingRequests() | List pending connection requests |
getActiveConnections() | List established connections |
getConnectionsNeedingConfirmation() | List connections awaiting confirmation |
getConnectionByTopicId(topicId: string) | Find connection by topic ID |
getConnectionByAccountId(accountId: string) | Find connection by account ID |
updateOrAddConnection(conn) | Add or update a connection |
clearAll() | Remove all cached connections and requests |
IConnectionsManager Interface (TypeScript)
export interface IConnectionsManager {
fetchConnectionData(accountId: string): Promise<Connection[]>;
processOutboundMessages(messages: HCSMessage[], accountId: string): Connection[];
processInboundMessages(messages: HCSMessage[]): Connection[];
processConnectionMessages(connectionTopicId: string, messages: HCSMessage[]): Connection | undefined;
addProfileInfo(accountId: string, profile: AIAgentProfile): void;
getAllConnections(): Connection[];
getPendingRequests(): Connection[];
getActiveConnections(): Connection[];
getConnectionsNeedingConfirmation(): Connection[];
getConnectionByTopicId(connectionTopicId: string): Connection | undefined;
getConnectionByAccountId(accountId: string): Connection | undefined;
getConnectionsByAccountId(accountId: string): Connection[];
updateOrAddConnection(connection: Connection): void;
clearAll(): void;
isConnectionRequestProcessed(inboundTopicId: string, requestId: number): boolean;
markConnectionRequestProcessed(inboundTopicId: string, requestId: number): boolean;
}
Example: LocalStorageConnectionsManager
Note, you will have to implement each method on your end.
type Connection = {
connectionTopicId: string;
targetAccountId: string;
status: 'pending' | 'established' | 'needs_confirmation' | 'closed';
isPending: boolean;
needsConfirmation: boolean;
created: Date;
processed: boolean;
};
type HCSMessage = { op: string; created: Date };
type AIAgentProfile = { display_name: string };
class LocalStorageConnectionsManager implements IConnectionsManager {
fetchConnectionData(accountId: string): Promise<Connection[]> {
return Promise.resolve(this.getAllConnections());
}
processOutboundMessages(messages: HCSMessage[], accountId: string): Connection[] {
return this.getAllConnections();
}
processInboundMessages(messages: HCSMessage[]): Connection[] {
return this.getAllConnections();
}
processConnectionMessages(connectionTopicId: string, messages: HCSMessage[]): Connection | undefined {
return this.getConnectionByTopicId(connectionTopicId);
}
addProfileInfo(accountId: string, profile: AIAgentProfile): void {}
getAllConnections(): Connection[] {
const raw = localStorage.getItem('connections');
if (!raw) return [];
return JSON.parse(raw).map((c: any) => ({ ...c, created: new Date(c.created) }));
}
getPendingRequests(): Connection[] {
return this.getAllConnections().filter(c => c.isPending);
}
getActiveConnections(): Connection[] {
return this.getAllConnections().filter(c => c.status === 'established');
}
getConnectionsNeedingConfirmation(): Connection[] {
return this.getAllConnections().filter(c => c.needsConfirmation);
}
getConnectionByTopicId(connectionTopicId: string): Connection | undefined {
return this.getAllConnections().find(c => c.connectionTopicId === connectionTopicId);
}
getConnectionByAccountId(accountId: string): Connection | undefined {
return this.getAllConnections().find(c => c.targetAccountId === accountId && c.status === 'established');
}
getConnectionsByAccountId(accountId: string): Connection[] {
return this.getAllConnections().filter(c => c.targetAccountId === accountId);
}
updateOrAddConnection(connection: Connection): void {
const all = this.getAllConnections().filter(c => c.connectionTopicId !== connection.connectionTopicId);
all.push(connection);
localStorage.setItem('connections', JSON.stringify(all));
}
clearAll(): void {
localStorage.removeItem('connections');
}
isConnectionRequestProcessed(inboundTopicId: string, requestId: number): boolean {
return false;
}
markConnectionRequestProcessed(inboundTopicId: string, requestId: number): boolean {
return false;
}
}