gopherhole_openclaw_a2a 0.4.6 → 0.4.8
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/dist/src/channel.js +14 -1
- package/dist/src/connection.d.ts +1 -0
- package/dist/src/connection.js +11 -0
- package/dist/src/gateway-client.d.ts +3 -1
- package/dist/src/gateway-client.js +26 -12
- package/package.json +3 -3
package/dist/src/channel.js
CHANGED
|
@@ -261,7 +261,20 @@ export const a2aPlugin = {
|
|
|
261
261
|
a2aLog.messageProcessing(message.taskId, sessionKey);
|
|
262
262
|
// Add A2A context so the agent knows to relay its full response
|
|
263
263
|
const a2aContext = `[A2A Request from agent "${message.from}"]\n\n${text}\n\n[Note: Your complete response will be sent back to the requesting agent. Include all relevant information in your reply.]`;
|
|
264
|
-
|
|
264
|
+
// A2A status updates are task-level keepalives. Publish one now and
|
|
265
|
+
// periodically while OpenClaw is working so callers do not mistake
|
|
266
|
+
// a long tool run for a dead connection.
|
|
267
|
+
connectionManager?.sendTaskStatusViaGopherHole(message.taskId, 'OpenClaw is working');
|
|
268
|
+
const keepalive = setInterval(() => {
|
|
269
|
+
connectionManager?.sendTaskStatusViaGopherHole(message.taskId, 'OpenClaw is still working');
|
|
270
|
+
}, 30_000);
|
|
271
|
+
let response;
|
|
272
|
+
try {
|
|
273
|
+
response = await sendChatMessage(sessionKey, a2aContext);
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
clearInterval(keepalive);
|
|
277
|
+
}
|
|
265
278
|
// Log captured response
|
|
266
279
|
if (response?.text) {
|
|
267
280
|
a2aLog.responseCaptured(message.taskId, response.text);
|
package/dist/src/connection.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare class A2AConnectionManager {
|
|
|
44
44
|
* Uses SDK's respond() method to complete the original task
|
|
45
45
|
*/
|
|
46
46
|
sendResponseViaGopherHole(_targetAgentId: string, taskId: string, text: string, _contextId?: string): void;
|
|
47
|
+
sendTaskStatusViaGopherHole(taskId: string, message?: string): void;
|
|
47
48
|
/**
|
|
48
49
|
* Legacy alias for sendPartsViaGopherHole with text-only
|
|
49
50
|
*/
|
package/dist/src/connection.js
CHANGED
|
@@ -271,6 +271,17 @@ export class A2AConnectionManager {
|
|
|
271
271
|
console.error('[a2a] Error details:', err);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
+
sendTaskStatusViaGopherHole(taskId, message) {
|
|
275
|
+
if (!this.gopherhole || !this.connected || !taskId || taskId.startsWith('gph-')) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
this.gopherhole.updateTaskStatus(taskId, 'working', message);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
console.error('[a2a] Failed to publish task status:', err.message);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
274
285
|
/**
|
|
275
286
|
* Legacy alias for sendPartsViaGopherHole with text-only
|
|
276
287
|
*/
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gateway WebSocket Client
|
|
3
|
-
* Sends messages to the
|
|
3
|
+
* Sends messages to the OpenClaw gateway via its WebSocket protocol
|
|
4
4
|
*
|
|
5
5
|
* chat.send is non-blocking - it returns a runId immediately and
|
|
6
6
|
* streams the response via chat events. We accumulate deltas and
|
|
7
7
|
* resolve when we get state: "final".
|
|
8
8
|
*/
|
|
9
|
+
export declare const GATEWAY_PROTOCOL_VERSION = 4;
|
|
10
|
+
export declare const CHAT_INACTIVITY_TIMEOUT_MS: number;
|
|
9
11
|
export declare function connectToGateway(port?: number): Promise<void>;
|
|
10
12
|
export declare function callGateway(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
11
13
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gateway WebSocket Client
|
|
3
|
-
* Sends messages to the
|
|
3
|
+
* Sends messages to the OpenClaw gateway via its WebSocket protocol
|
|
4
4
|
*
|
|
5
5
|
* chat.send is non-blocking - it returns a runId immediately and
|
|
6
6
|
* streams the response via chat events. We accumulate deltas and
|
|
@@ -16,6 +16,18 @@ let pendingRequests = new Map();
|
|
|
16
16
|
let pendingChats = new Map(); // keyed by runId
|
|
17
17
|
let connected = false;
|
|
18
18
|
let handshakeComplete = false;
|
|
19
|
+
// OpenClaw 2026.7+ requires gateway protocol v4. Keep this explicit so a
|
|
20
|
+
// protocol bump fails a focused compatibility test instead of silently
|
|
21
|
+
// breaking inbound A2A replies at runtime.
|
|
22
|
+
export const GATEWAY_PROTOCOL_VERSION = 4;
|
|
23
|
+
export const CHAT_INACTIVITY_TIMEOUT_MS = 30 * 60 * 1000;
|
|
24
|
+
function refreshChatTimeout(runId, pending) {
|
|
25
|
+
clearTimeout(pending.timeout);
|
|
26
|
+
pending.timeout = setTimeout(() => {
|
|
27
|
+
pendingChats.delete(runId);
|
|
28
|
+
pending.reject(new Error('Chat response inactivity timeout'));
|
|
29
|
+
}, pending.timeoutMs);
|
|
30
|
+
}
|
|
19
31
|
function getGatewayToken() {
|
|
20
32
|
// Try the current OpenClaw config location first, then the legacy
|
|
21
33
|
// Clawdbot path as a fallback for users still on older installs.
|
|
@@ -56,12 +68,12 @@ export async function connectToGateway(port = 18789) {
|
|
|
56
68
|
id: connectId,
|
|
57
69
|
method: 'connect',
|
|
58
70
|
params: {
|
|
59
|
-
minProtocol:
|
|
60
|
-
maxProtocol:
|
|
71
|
+
minProtocol: GATEWAY_PROTOCOL_VERSION,
|
|
72
|
+
maxProtocol: GATEWAY_PROTOCOL_VERSION,
|
|
61
73
|
client: {
|
|
62
74
|
id: 'gateway-client',
|
|
63
75
|
displayName: 'A2A Channel Plugin',
|
|
64
|
-
version: '0.
|
|
76
|
+
version: '0.4.8',
|
|
65
77
|
platform: process.platform,
|
|
66
78
|
mode: 'backend',
|
|
67
79
|
},
|
|
@@ -170,6 +182,9 @@ function handleChatEvent(payload) {
|
|
|
170
182
|
// Not a chat we're tracking
|
|
171
183
|
return;
|
|
172
184
|
}
|
|
185
|
+
// Any chat event proves the OpenClaw run is alive. Treat the timeout as an
|
|
186
|
+
// inactivity deadline rather than a fixed whole-task deadline.
|
|
187
|
+
refreshChatTimeout(payload.runId, pending);
|
|
173
188
|
// Detailed logging for debugging relay issues
|
|
174
189
|
console.log(`[a2a] Chat event: runId=${payload.runId}, state=${payload.state}, seq=${payload.seq}, role=${payload.message?.role}`);
|
|
175
190
|
if (payload.state === 'delta' || payload.state === 'final') {
|
|
@@ -234,7 +249,7 @@ export async function callGateway(method, params, timeoutMs = 300000) {
|
|
|
234
249
|
* Send a chat message and wait for the full response.
|
|
235
250
|
* chat.send is non-blocking - returns runId immediately, response streams via events.
|
|
236
251
|
*/
|
|
237
|
-
export async function sendChatMessage(sessionKey, message, timeoutMs =
|
|
252
|
+
export async function sendChatMessage(sessionKey, message, timeoutMs = CHAT_INACTIVITY_TIMEOUT_MS) {
|
|
238
253
|
if (!ws || !connected || !handshakeComplete) {
|
|
239
254
|
await connectToGateway();
|
|
240
255
|
}
|
|
@@ -252,16 +267,15 @@ export async function sendChatMessage(sessionKey, message, timeoutMs = 300000) {
|
|
|
252
267
|
}
|
|
253
268
|
// Now wait for chat events to complete
|
|
254
269
|
return new Promise((resolve, reject) => {
|
|
255
|
-
const
|
|
256
|
-
pendingChats.delete(result.runId);
|
|
257
|
-
reject(new Error('Chat response timeout'));
|
|
258
|
-
}, timeoutMs);
|
|
259
|
-
pendingChats.set(result.runId, {
|
|
270
|
+
const pending = {
|
|
260
271
|
resolve,
|
|
261
272
|
reject,
|
|
262
|
-
timeout,
|
|
273
|
+
timeout: undefined,
|
|
263
274
|
latestText: '',
|
|
264
|
-
|
|
275
|
+
timeoutMs,
|
|
276
|
+
};
|
|
277
|
+
pendingChats.set(result.runId, pending);
|
|
278
|
+
refreshChatTimeout(result.runId, pending);
|
|
265
279
|
});
|
|
266
280
|
}
|
|
267
281
|
export function disconnectFromGateway() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gopherhole_openclaw_a2a",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "GopherHole A2A plugin for OpenClaw - connect your AI agent to the GopherHole network",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gopherhole/sdk": "^0.
|
|
38
|
+
"@gopherhole/sdk": "^0.9.1",
|
|
39
39
|
"uuid": "^10.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -53,4 +53,4 @@
|
|
|
53
53
|
"README.md",
|
|
54
54
|
"SKILL.md"
|
|
55
55
|
]
|
|
56
|
-
}
|
|
56
|
+
}
|