gopherhole_openclaw_a2a 0.4.7 → 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.
@@ -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
- const response = await sendChatMessage(sessionKey, a2aContext);
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);
@@ -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
  */
@@ -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
  */
@@ -7,6 +7,7 @@
7
7
  * resolve when we get state: "final".
8
8
  */
9
9
  export declare const GATEWAY_PROTOCOL_VERSION = 4;
10
+ export declare const CHAT_INACTIVITY_TIMEOUT_MS: number;
10
11
  export declare function connectToGateway(port?: number): Promise<void>;
11
12
  export declare function callGateway(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
12
13
  /**
@@ -20,6 +20,14 @@ let handshakeComplete = false;
20
20
  // protocol bump fails a focused compatibility test instead of silently
21
21
  // breaking inbound A2A replies at runtime.
22
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
+ }
23
31
  function getGatewayToken() {
24
32
  // Try the current OpenClaw config location first, then the legacy
25
33
  // Clawdbot path as a fallback for users still on older installs.
@@ -65,7 +73,7 @@ export async function connectToGateway(port = 18789) {
65
73
  client: {
66
74
  id: 'gateway-client',
67
75
  displayName: 'A2A Channel Plugin',
68
- version: '0.4.7',
76
+ version: '0.4.8',
69
77
  platform: process.platform,
70
78
  mode: 'backend',
71
79
  },
@@ -174,6 +182,9 @@ function handleChatEvent(payload) {
174
182
  // Not a chat we're tracking
175
183
  return;
176
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);
177
188
  // Detailed logging for debugging relay issues
178
189
  console.log(`[a2a] Chat event: runId=${payload.runId}, state=${payload.state}, seq=${payload.seq}, role=${payload.message?.role}`);
179
190
  if (payload.state === 'delta' || payload.state === 'final') {
@@ -238,7 +249,7 @@ export async function callGateway(method, params, timeoutMs = 300000) {
238
249
  * Send a chat message and wait for the full response.
239
250
  * chat.send is non-blocking - returns runId immediately, response streams via events.
240
251
  */
241
- export async function sendChatMessage(sessionKey, message, timeoutMs = 300000) {
252
+ export async function sendChatMessage(sessionKey, message, timeoutMs = CHAT_INACTIVITY_TIMEOUT_MS) {
242
253
  if (!ws || !connected || !handshakeComplete) {
243
254
  await connectToGateway();
244
255
  }
@@ -256,16 +267,15 @@ export async function sendChatMessage(sessionKey, message, timeoutMs = 300000) {
256
267
  }
257
268
  // Now wait for chat events to complete
258
269
  return new Promise((resolve, reject) => {
259
- const timeout = setTimeout(() => {
260
- pendingChats.delete(result.runId);
261
- reject(new Error('Chat response timeout'));
262
- }, timeoutMs);
263
- pendingChats.set(result.runId, {
270
+ const pending = {
264
271
  resolve,
265
272
  reject,
266
- timeout,
273
+ timeout: undefined,
267
274
  latestText: '',
268
- });
275
+ timeoutMs,
276
+ };
277
+ pendingChats.set(result.runId, pending);
278
+ refreshChatTimeout(result.runId, pending);
269
279
  });
270
280
  }
271
281
  export function disconnectFromGateway() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gopherhole_openclaw_a2a",
3
- "version": "0.4.7",
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.7.5",
38
+ "@gopherhole/sdk": "^0.9.1",
39
39
  "uuid": "^10.0.0"
40
40
  },
41
41
  "devDependencies": {