@clawchatsai/connector 0.1.6 → 0.1.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server/gateway.js +27 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawchatsai/connector",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "ClawChats OpenClaw plugin — P2P tunnel + local API bridge",
6
6
  "main": "dist/index.js",
package/server/gateway.js CHANGED
@@ -22,6 +22,10 @@ export class GatewayClient {
22
22
  this.streamState = new Map();
23
23
  this.activityLogs = new Map();
24
24
  this._pendingTitleGens = new Map();
25
+ // Runs we've already synthesized a streaming-end{reason:'error'} for
26
+ // (agent lifecycle:error emitted but no chat state:error ever will). Prevents
27
+ // double-fires across retries and cross-path dedup with handleChatEvent.
28
+ this._syntheticErrorRuns = new Set();
25
29
 
26
30
  // On startup: mark any pending activity log messages from previous crashed/restarted sessions
27
31
  // as interrupted. Without this, stale pending=true rows survive gateway restarts and cause
@@ -402,6 +406,29 @@ export class GatewayClient {
402
406
  log.finalSteps = cleanSteps;
403
407
  log.finalSummary = generateActivitySummary(log.steps);
404
408
  // Note: activityLogs entry is kept until _popActivityLogForSession cleans it up
409
+ //
410
+ // Fallback: if the gateway never emitted any chat.* events for this session
411
+ // (e.g. pre-reply provider error on a non-webchat surface where the chat
412
+ // broadcast is gated off upstream), handleChatEvent won't fire a
413
+ // streaming-end and the UI stays locked on "thinking…" forever. Synthesize
414
+ // one here so the frontend's existing error path (`reason: 'error'`) runs.
415
+ if (data?.phase === 'error' && !this.streamState.has(sessionKey) && !this._syntheticErrorRuns.has(runId)) {
416
+ this._syntheticErrorRuns.add(runId);
417
+ const parsed = parseSessionKey(sessionKey);
418
+ if (parsed) {
419
+ this.broadcastToBrowsers(JSON.stringify({
420
+ type: 'clawchats',
421
+ event: 'streaming-end',
422
+ threadId: parsed.threadId,
423
+ workspace: parsed.workspace,
424
+ reason: 'error',
425
+ errorMessage: data?.error || 'Agent failed before reply',
426
+ }));
427
+ }
428
+ // Bound the set — auto-evict after the retry window so long-lived
429
+ // processes don't leak. 5 min is far longer than any retry chain.
430
+ setTimeout(() => this._syntheticErrorRuns.delete(runId), 5 * 60 * 1000);
431
+ }
405
432
  }
406
433
  }
407
434