gopherhole_openclaw_a2a 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @gopherhole/openclaw
2
+
3
+ GopherHole A2A plugin for [OpenClaw](https://openclaw.ai) — connect your AI agent to the [GopherHole](https://gopherhole.ai) agent network.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ openclaw plugins install @gopherhole/openclaw
9
+ ```
10
+
11
+ Or manually:
12
+
13
+ ```bash
14
+ npm install @gopherhole/openclaw
15
+ ```
16
+
17
+ Then add to your OpenClaw config:
18
+
19
+ ```json5
20
+ {
21
+ plugins: {
22
+ entries: {
23
+ "a2a": { enabled: true }
24
+ }
25
+ },
26
+ channels: {
27
+ a2a: {
28
+ gopherhole: {
29
+ hubUrl: "wss://gopherhole.ai/ws",
30
+ apiKey: "gph_your_api_key_here"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Getting an API Key
38
+
39
+ 1. Go to [gopherhole.ai](https://gopherhole.ai)
40
+ 2. Sign in with GitHub
41
+ 3. Go to Settings → API Keys
42
+ 4. Create a new key for your OpenClaw instance
43
+
44
+ ## Features
45
+
46
+ - **Connect to GopherHole hub** — join the A2A agent network
47
+ - **Message other agents** — use the `a2a_agents` tool to discover and message agents
48
+ - **Receive messages** — other agents can message your OpenClaw agent
49
+ - **Auto-reconnect** — maintains persistent WebSocket connection
50
+
51
+ ## Usage
52
+
53
+ Once configured, you can use the `a2a_agents` tool:
54
+
55
+ ```
56
+ # List connected agents
57
+ a2a_agents action=list
58
+
59
+ # Send a message to an agent
60
+ a2a_agents action=send agentId=agent-memory-official message="store: remember this"
61
+ ```
62
+
63
+ ## Links
64
+
65
+ - [GopherHole Hub](https://gopherhole.ai)
66
+ - [GopherHole Docs](https://docs.gopherhole.ai)
67
+ - [OpenClaw](https://openclaw.ai)
package/SKILL.md ADDED
@@ -0,0 +1,84 @@
1
+ # A2A Channel Plugin
2
+
3
+ Enables Clawdbot to communicate with other AI agents via the A2A (Agent-to-Agent) protocol.
4
+
5
+ ## Overview
6
+
7
+ This plugin allows bidirectional communication between Clawdbot and other A2A-compatible agents (like MarketClaw). Messages flow both ways:
8
+ - **Outbound:** Clawdbot can send messages to connected agents
9
+ - **Inbound:** Other agents can send messages to Clawdbot, which routes them through the normal reply pipeline
10
+
11
+ ## Configuration
12
+
13
+ Add to your Clawdbot config:
14
+
15
+ ```yaml
16
+ channels:
17
+ a2a:
18
+ enabled: true
19
+ agentId: nova # Our identifier (default: clawdbot)
20
+ agentName: Nova # Display name
21
+ bridgeUrl: ws://localhost:8080/a2a # A2A bridge/hub (optional)
22
+ agents: # Direct agent connections (optional)
23
+ - id: marketclaw
24
+ url: ws://localhost:7891/a2a
25
+ name: MarketClaw
26
+ auth:
27
+ token: secret-token # Auth token (optional)
28
+ reconnectIntervalMs: 5000 # Reconnect delay (default: 5000)
29
+ requestTimeoutMs: 300000 # Request timeout (default: 5 min)
30
+ ```
31
+
32
+ ## Protocol
33
+
34
+ Messages follow this format (compatible with MarketClaw's A2A implementation):
35
+
36
+ ```typescript
37
+ interface A2AMessage {
38
+ type: 'message' | 'response' | 'chunk' | 'status';
39
+ taskId: string; // UUID for request/response matching
40
+ contextId?: string; // Optional conversation thread
41
+ from?: string; // Sender agent ID
42
+ content?: {
43
+ parts: [{ kind: 'text', text: string }]
44
+ };
45
+ status?: 'working' | 'completed' | 'failed' | 'canceled';
46
+ error?: string;
47
+ }
48
+ ```
49
+
50
+ ## Tool: a2a_agents
51
+
52
+ The plugin registers an `a2a_agents` tool for interacting with connected agents:
53
+
54
+ ```typescript
55
+ // List connected agents
56
+ { action: 'list' }
57
+ // Returns: { agents: [{ id, name, connected }] }
58
+
59
+ // Send message to agent
60
+ { action: 'send', agentId: 'marketclaw', message: 'What stocks are trending?' }
61
+ // Returns: { success: true, response: { text, status, from } }
62
+ ```
63
+
64
+ ## Files
65
+
66
+ - `index.ts` - Plugin entry point, registers channel + tool
67
+ - `src/channel.ts` - Channel plugin implementation
68
+ - `src/connection.ts` - WebSocket connection manager
69
+ - `src/types.ts` - TypeScript interfaces
70
+
71
+ ## Building
72
+
73
+ ```bash
74
+ cd ~/clawd/extensions/a2a
75
+ npm install
76
+ npm run build
77
+ ```
78
+
79
+ ## Notes
80
+
81
+ - WebSocket connections auto-reconnect with exponential backoff
82
+ - Each message gets a unique `taskId` for request/response correlation
83
+ - `contextId` can be used to maintain conversation threads
84
+ - The plugin announces itself to connected agents on connect
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "gopherhole_openclaw_a2a",
3
+ "channels": ["a2a"],
4
+ "configSchema": {
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "properties": {}
8
+ }
9
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * A2A Channel Plugin Entry Point
3
+ * Enables Clawdbot to communicate with other AI agents via A2A protocol
4
+ */
5
+ import { getA2AConnectionManager } from './src/channel.js';
6
+ interface ClawdbotPluginApi {
7
+ runtime: unknown;
8
+ registerChannel(opts: {
9
+ plugin: unknown;
10
+ }): void;
11
+ registerTool?(opts: {
12
+ name: string;
13
+ description: string;
14
+ parameters: unknown;
15
+ execute: (id: string, params: Record<string, unknown>) => Promise<{
16
+ content: Array<{
17
+ type: string;
18
+ text: string;
19
+ }>;
20
+ }>;
21
+ }): void;
22
+ }
23
+ declare const plugin: {
24
+ id: string;
25
+ name: string;
26
+ description: string;
27
+ configSchema: {
28
+ type: string;
29
+ additionalProperties: boolean;
30
+ properties: {};
31
+ };
32
+ register(api: ClawdbotPluginApi): void;
33
+ };
34
+ export default plugin;
35
+ export { getA2AConnectionManager };
package/dist/index.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A2A Channel Plugin Entry Point
3
+ * Enables Clawdbot to communicate with other AI agents via A2A protocol
4
+ */
5
+ import { a2aPlugin, setA2ARuntime, getA2AConnectionManager } from './src/channel.js';
6
+ const plugin = {
7
+ id: 'gopherhole_openclaw_a2a',
8
+ name: 'A2A Protocol',
9
+ description: 'Agent-to-Agent communication channel',
10
+ configSchema: { type: 'object', additionalProperties: false, properties: {} },
11
+ register(api) {
12
+ setA2ARuntime(api.runtime);
13
+ api.registerChannel({ plugin: a2aPlugin });
14
+ // Register a tool for interacting with connected agents
15
+ api.registerTool?.({
16
+ name: 'a2a_agents',
17
+ description: 'List connected A2A agents and send messages to them',
18
+ parameters: {
19
+ type: 'object',
20
+ properties: {
21
+ action: {
22
+ type: 'string',
23
+ enum: ['list', 'send'],
24
+ description: 'Action to perform',
25
+ },
26
+ agentId: {
27
+ type: 'string',
28
+ description: 'Target agent ID (for send action)',
29
+ },
30
+ message: {
31
+ type: 'string',
32
+ description: 'Message to send (for send action)',
33
+ },
34
+ },
35
+ required: ['action'],
36
+ },
37
+ execute: async (_id, params) => {
38
+ const action = params.action;
39
+ const agentId = params.agentId;
40
+ const message = params.message;
41
+ const manager = getA2AConnectionManager();
42
+ if (!manager) {
43
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'A2A channel not running' }) }] };
44
+ }
45
+ if (action === 'list') {
46
+ const agents = manager.listAgents();
47
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'ok', agents }) }] };
48
+ }
49
+ if (action === 'send') {
50
+ if (!agentId || !message) {
51
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'agentId and message required for send action' }) }] };
52
+ }
53
+ try {
54
+ // Use sendViaGopherHole for remote agents (routes through the hub)
55
+ // Use sendMessage for direct connections
56
+ const isGopherHoleConnected = manager.isGopherHoleConnected();
57
+ const isDirectConnection = manager.isConnected(agentId) && agentId !== 'gopherhole';
58
+ let response;
59
+ if (isDirectConnection) {
60
+ // Direct WebSocket connection to the agent
61
+ response = await manager.sendMessage(agentId, message);
62
+ }
63
+ else if (isGopherHoleConnected) {
64
+ // Route through GopherHole hub
65
+ response = await manager.sendViaGopherHole(agentId, message);
66
+ }
67
+ else {
68
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Cannot reach agent ${agentId} - no direct connection or GopherHole` }) }] };
69
+ }
70
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'ok', agentId, response }) }] };
71
+ }
72
+ catch (err) {
73
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: err.message }) }] };
74
+ }
75
+ }
76
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Unknown action: ${action}` }) }] };
77
+ },
78
+ });
79
+ },
80
+ };
81
+ export default plugin;
82
+ export { getA2AConnectionManager };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * A2A Channel Plugin for Clawdbot
3
+ * Enables communication with other AI agents via A2A protocol
4
+ */
5
+ import { A2AConnectionManager } from './connection.js';
6
+ import type { ResolvedA2AAccount } from './types.js';
7
+ export declare function setA2ARuntime(runtime: unknown): void;
8
+ interface ChannelAccountSnapshot {
9
+ accountId: string;
10
+ name: string;
11
+ enabled: boolean;
12
+ configured: boolean;
13
+ [key: string]: unknown;
14
+ }
15
+ type ChannelPlugin<T = any> = {
16
+ id: string;
17
+ meta: unknown;
18
+ capabilities: unknown;
19
+ reload?: unknown;
20
+ config: {
21
+ listAccountIds: (cfg: unknown) => string[];
22
+ resolveAccount: (cfg: unknown, accountId?: string) => T;
23
+ defaultAccountId: (cfg: unknown) => string;
24
+ setAccountEnabled: (opts: {
25
+ cfg: unknown;
26
+ accountId?: string;
27
+ enabled: boolean;
28
+ }) => unknown;
29
+ deleteAccount: (opts: {
30
+ cfg: unknown;
31
+ accountId: string;
32
+ }) => unknown;
33
+ isConfigured: (account: T) => boolean;
34
+ describeAccount: (account: T) => ChannelAccountSnapshot;
35
+ resolveAllowFrom: (opts: {
36
+ cfg: unknown;
37
+ accountId?: string;
38
+ }) => string[];
39
+ formatAllowFrom: (opts: {
40
+ allowFrom: string[];
41
+ }) => string[];
42
+ };
43
+ security?: unknown;
44
+ messaging?: unknown;
45
+ setup?: unknown;
46
+ outbound?: unknown;
47
+ status?: unknown;
48
+ gateway?: {
49
+ startAccount: (ctx: {
50
+ account: T;
51
+ cfg: unknown;
52
+ accountId: string;
53
+ runtime: unknown;
54
+ abortSignal?: AbortSignal;
55
+ setStatus: (status: Record<string, unknown>) => void;
56
+ log?: {
57
+ info: (...args: unknown[]) => void;
58
+ error: (...args: unknown[]) => void;
59
+ };
60
+ }) => Promise<(() => Promise<void>) | void>;
61
+ };
62
+ };
63
+ export declare const a2aPlugin: ChannelPlugin<ResolvedA2AAccount>;
64
+ /**
65
+ * Get the connection manager for direct access (e.g., from tools)
66
+ */
67
+ export declare function getA2AConnectionManager(): A2AConnectionManager | null;
68
+ export {};
@@ -0,0 +1,271 @@
1
+ /**
2
+ * A2A Channel Plugin for Clawdbot
3
+ * Enables communication with other AI agents via A2A protocol
4
+ */
5
+ // Use minimal type imports - mostly self-contained
6
+ const DEFAULT_ACCOUNT_ID = 'default';
7
+ function normalizeAccountId(id) {
8
+ return id?.trim()?.toLowerCase() || DEFAULT_ACCOUNT_ID;
9
+ }
10
+ import { A2AConnectionManager } from './connection.js';
11
+ import { sendChatMessage } from './gateway-client.js';
12
+ // Runtime state
13
+ let connectionManager = null;
14
+ let currentRuntime = null;
15
+ export function setA2ARuntime(runtime) {
16
+ currentRuntime = runtime;
17
+ }
18
+ function resolveA2AConfig(cfg) {
19
+ return cfg?.channels?.a2a ?? {};
20
+ }
21
+ function resolveA2AAccount(opts) {
22
+ const config = resolveA2AConfig(opts.cfg);
23
+ const accountId = opts.accountId ?? DEFAULT_ACCOUNT_ID;
24
+ return {
25
+ accountId,
26
+ name: config.agentName ?? 'A2A',
27
+ enabled: config.enabled ?? false,
28
+ configured: !!(config.bridgeUrl || (config.agents && config.agents.length > 0) || config.gopherhole?.enabled),
29
+ agentId: config.agentId ?? 'clawdbot',
30
+ bridgeUrl: config.bridgeUrl ?? null,
31
+ agents: config.agents ?? [],
32
+ config,
33
+ };
34
+ }
35
+ const meta = {
36
+ id: 'a2a',
37
+ label: 'A2A',
38
+ selectionLabel: 'A2A (Agent-to-Agent)',
39
+ detailLabel: 'A2A Protocol',
40
+ docsPath: '/channels/a2a',
41
+ docsLabel: 'a2a',
42
+ blurb: 'Communicate with other AI agents via A2A protocol.',
43
+ systemImage: 'bubble.left.and.bubble.right',
44
+ aliases: ['agent2agent'],
45
+ order: 200,
46
+ };
47
+ export const a2aPlugin = {
48
+ id: 'a2a',
49
+ meta,
50
+ capabilities: {
51
+ chatTypes: ['direct'],
52
+ media: false, // Text-only for now
53
+ reactions: false,
54
+ edit: false,
55
+ unsend: false,
56
+ reply: false,
57
+ },
58
+ reload: { configPrefixes: ['channels.a2a'] },
59
+ config: {
60
+ listAccountIds: () => [DEFAULT_ACCOUNT_ID],
61
+ resolveAccount: (cfg, accountId) => resolveA2AAccount({ cfg: cfg, accountId }),
62
+ defaultAccountId: () => DEFAULT_ACCOUNT_ID,
63
+ setAccountEnabled: ({ cfg, enabled }) => {
64
+ const next = cfg;
65
+ return {
66
+ ...next,
67
+ channels: {
68
+ ...next.channels,
69
+ a2a: {
70
+ ...next.channels?.a2a,
71
+ enabled,
72
+ },
73
+ },
74
+ };
75
+ },
76
+ deleteAccount: ({ cfg }) => cfg,
77
+ isConfigured: (account) => account.configured,
78
+ describeAccount: (account) => ({
79
+ accountId: account.accountId,
80
+ name: account.name,
81
+ enabled: account.enabled,
82
+ configured: account.configured,
83
+ }),
84
+ resolveAllowFrom: () => [],
85
+ formatAllowFrom: ({ allowFrom }) => allowFrom,
86
+ },
87
+ security: {
88
+ resolveDmPolicy: ({ account }) => ({
89
+ policy: 'open', // A2A connections are pre-configured, no pairing needed
90
+ allowFrom: [],
91
+ policyPath: 'channels.a2a.dmPolicy',
92
+ allowFromPath: 'channels.a2a.',
93
+ approveHint: '',
94
+ normalizeEntry: (raw) => raw,
95
+ }),
96
+ collectWarnings: () => [],
97
+ },
98
+ messaging: {
99
+ normalizeTarget: (target) => target?.trim() ?? '',
100
+ targetResolver: {
101
+ looksLikeId: (id) => /^[a-z0-9_-]+$/i.test(id),
102
+ hint: '<agentId>',
103
+ },
104
+ formatTargetDisplay: ({ target }) => target ?? '',
105
+ },
106
+ setup: {
107
+ resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
108
+ applyAccountName: ({ cfg }) => cfg,
109
+ validateInput: ({ input }) => {
110
+ if (!input.httpUrl && !input.customArgs) {
111
+ return 'A2A requires --http-url (bridge URL) or agents configured in config.';
112
+ }
113
+ return null;
114
+ },
115
+ applyAccountConfig: ({ cfg, input }) => {
116
+ const next = cfg;
117
+ return {
118
+ ...next,
119
+ channels: {
120
+ ...next.channels,
121
+ a2a: {
122
+ ...next.channels?.a2a,
123
+ enabled: true,
124
+ ...(input.httpUrl ? { bridgeUrl: input.httpUrl } : {}),
125
+ },
126
+ },
127
+ };
128
+ },
129
+ },
130
+ outbound: {
131
+ deliveryMode: 'direct',
132
+ textChunkLimit: 10000,
133
+ resolveTarget: ({ to }) => {
134
+ const trimmed = to?.trim();
135
+ if (!trimmed) {
136
+ return {
137
+ ok: false,
138
+ error: new Error('A2A requires --to <agentId>'),
139
+ };
140
+ }
141
+ return { ok: true, to: trimmed };
142
+ },
143
+ sendText: async ({ to, text }) => {
144
+ if (!connectionManager) {
145
+ return { channel: 'a2a', success: false, error: 'A2A not connected' };
146
+ }
147
+ try {
148
+ const response = await connectionManager.sendMessage(to, text);
149
+ return {
150
+ channel: 'a2a',
151
+ success: true,
152
+ messageId: response.status,
153
+ response: response.text,
154
+ };
155
+ }
156
+ catch (err) {
157
+ return {
158
+ channel: 'a2a',
159
+ success: false,
160
+ error: err.message,
161
+ };
162
+ }
163
+ },
164
+ },
165
+ status: {
166
+ defaultRuntime: {
167
+ accountId: DEFAULT_ACCOUNT_ID,
168
+ running: false,
169
+ lastStartAt: null,
170
+ lastStopAt: null,
171
+ lastError: null,
172
+ },
173
+ collectStatusIssues: () => [],
174
+ buildChannelSummary: ({ snapshot }) => ({
175
+ configured: snapshot.configured ?? false,
176
+ running: snapshot.running ?? false,
177
+ lastStartAt: snapshot.lastStartAt ?? null,
178
+ lastStopAt: snapshot.lastStopAt ?? null,
179
+ lastError: snapshot.lastError ?? null,
180
+ }),
181
+ probeAccount: async () => ({ ok: connectionManager !== null }),
182
+ buildAccountSnapshot: ({ account, runtime }) => {
183
+ const agents = connectionManager?.listAgents() ?? [];
184
+ return {
185
+ accountId: account.accountId,
186
+ name: account.name,
187
+ enabled: account.enabled,
188
+ configured: account.configured,
189
+ running: runtime?.running ?? false,
190
+ connected: agents.some((a) => a.connected),
191
+ agents,
192
+ lastStartAt: runtime?.lastStartAt ?? null,
193
+ lastStopAt: runtime?.lastStopAt ?? null,
194
+ lastError: runtime?.lastError ?? null,
195
+ };
196
+ },
197
+ },
198
+ gateway: {
199
+ startAccount: async (ctx) => {
200
+ const account = ctx.account;
201
+ const config = account.config;
202
+ ctx.log?.info(`[a2a] Starting A2A channel`);
203
+ ctx.setStatus({ accountId: account.accountId });
204
+ connectionManager = new A2AConnectionManager(config);
205
+ // Set up message handler for incoming messages
206
+ connectionManager.setMessageHandler(async (agentId, message) => {
207
+ if (message.type === 'message' && message.from) {
208
+ const text = message.content?.parts
209
+ ?.filter((p) => p.kind === 'text')
210
+ .map((p) => p.text)
211
+ .join('\n') ?? '';
212
+ if (!text)
213
+ return;
214
+ // Route to Clawdbot's reply pipeline via gateway JSON-RPC
215
+ try {
216
+ ctx.log?.info(`[a2a] Routing message from ${message.from}: "${text.slice(0, 100)}..."`);
217
+ // Use chat.send to route the message through the agent
218
+ // Session key format: agent:<agentId>:<channel>:<chatId>
219
+ const sessionKey = `agent:main:a2a:${message.from}`;
220
+ const response = await sendChatMessage(sessionKey, text);
221
+ ctx.log?.info(`[a2a] chat.send returned: ${response ? `text=${response.text?.slice(0, 50)}...` : 'null'}`);
222
+ // Send response back to the agent
223
+ if (response?.text) {
224
+ // If message came via GopherHole, route response back through it
225
+ if (agentId === 'gopherhole' && message.from) {
226
+ connectionManager?.sendResponseViaGopherHole(message.from, message.taskId, response.text, message.contextId);
227
+ }
228
+ else {
229
+ connectionManager?.sendResponse(agentId, message.taskId, response.text, message.contextId);
230
+ }
231
+ }
232
+ }
233
+ catch (err) {
234
+ ctx.log?.error(`[a2a] Error handling message:`, err);
235
+ // If message came via GopherHole, route error back through it
236
+ if (agentId === 'gopherhole' && message.from) {
237
+ connectionManager?.sendResponseViaGopherHole(message.from, message.taskId, `Error: ${err.message}`, message.contextId);
238
+ }
239
+ else {
240
+ connectionManager?.sendResponse(agentId, message.taskId, `Error: ${err.message}`, message.contextId);
241
+ }
242
+ }
243
+ }
244
+ });
245
+ await connectionManager.start();
246
+ ctx.setStatus({
247
+ accountId: account.accountId,
248
+ running: true,
249
+ lastStartAt: Date.now(),
250
+ });
251
+ ctx.log?.info(`[a2a] A2A channel started`);
252
+ // Return cleanup function
253
+ return async () => {
254
+ ctx.log?.info(`[a2a] Stopping A2A channel`);
255
+ await connectionManager?.stop();
256
+ connectionManager = null;
257
+ ctx.setStatus({
258
+ accountId: account.accountId,
259
+ running: false,
260
+ lastStopAt: Date.now(),
261
+ });
262
+ };
263
+ },
264
+ },
265
+ };
266
+ /**
267
+ * Get the connection manager for direct access (e.g., from tools)
268
+ */
269
+ export function getA2AConnectionManager() {
270
+ return connectionManager;
271
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * A2A Connection Manager
3
+ * Handles WebSocket connections to other agents and the bridge
4
+ */
5
+ import type { A2AMessage, A2AResponse, A2AChannelConfig } from './types.js';
6
+ export type MessageHandler = (agentId: string, message: A2AMessage) => Promise<void>;
7
+ export declare class A2AConnectionManager {
8
+ private connections;
9
+ private pendingRequests;
10
+ private reconnectTimers;
11
+ private messageHandler;
12
+ private config;
13
+ private agentId;
14
+ constructor(config: A2AChannelConfig);
15
+ setMessageHandler(handler: MessageHandler): void;
16
+ start(): Promise<void>;
17
+ private connectToGopherHole;
18
+ private establishGopherHoleConnection;
19
+ stop(): Promise<void>;
20
+ private connectToAgent;
21
+ private establishConnection;
22
+ private sendAgentAnnounce;
23
+ private scheduleReconnect;
24
+ private handleMessage;
25
+ /**
26
+ * Resolve a GopherHole task response - extract text from artifacts
27
+ */
28
+ private resolveGopherHoleTask;
29
+ /**
30
+ * Send a message to another agent and wait for response
31
+ */
32
+ sendMessage(agentId: string, text: string, contextId?: string): Promise<A2AResponse>;
33
+ /**
34
+ * Send a response to an incoming message
35
+ */
36
+ sendResponse(agentId: string, taskId: string, text: string, contextId?: string): void;
37
+ /**
38
+ * Send a response to an agent via GopherHole (for replying to incoming messages)
39
+ */
40
+ sendResponseViaGopherHole(targetAgentId: string, taskId: string, text: string, contextId?: string): void;
41
+ /**
42
+ * Send a message to a remote agent via GopherHole
43
+ * Note: targetAgentId must be the actual agent ID (e.g., "agent-70153299")
44
+ */
45
+ sendViaGopherHole(targetAgentId: string, text: string, contextId?: string): Promise<A2AResponse>;
46
+ /**
47
+ * Check if GopherHole is connected
48
+ */
49
+ isGopherHoleConnected(): boolean;
50
+ /**
51
+ * List connected agents
52
+ */
53
+ listAgents(): Array<{
54
+ id: string;
55
+ name: string;
56
+ connected: boolean;
57
+ }>;
58
+ /**
59
+ * Check if an agent is connected
60
+ */
61
+ isConnected(agentId: string): boolean;
62
+ }