echoclaw-relay-agent 0.15.2 → 0.15.3

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.
@@ -311,7 +311,16 @@ export class RelayAgent extends EventEmitter {
311
311
  this.emit('pairing_code', receivedCode);
312
312
  });
313
313
  const result = await this.pairing.pairAsAgent(code);
314
- await this.onPaired(result, true); // fresh pairing — force identity exchange
314
+ try {
315
+ await this.onPaired(result, true); // fresh pairing — force identity exchange
316
+ }
317
+ catch (err) {
318
+ // Identity exchange failed on fresh pairing — tear down transport to
319
+ // prevent leaking an authenticated-but-unusable WebSocket, then re-throw.
320
+ // Do NOT emit('error') here — the throw propagates to start() caller.
321
+ this.transport?.disconnect();
322
+ throw err;
323
+ }
315
324
  }
316
325
  async resumeSession(session) {
317
326
  this.setStatus('connecting');
@@ -538,7 +547,14 @@ export class RelayAgent extends EventEmitter {
538
547
  await this._handleIdentityExchange(sessionData, freshPairing);
539
548
  }
540
549
  catch (err) {
541
- this.emit('error', new IdentityExchangeError(`Identity exchange failed: ${err instanceof Error ? err.message : err}`));
550
+ const ixErr = new IdentityExchangeError(`Identity exchange failed: ${err instanceof Error ? err.message : err}`);
551
+ if (freshPairing) {
552
+ // Fresh pairing MUST establish identity — re-throw to fail the
553
+ // connection; reconnect will retry with a new ECDH handshake.
554
+ throw ixErr;
555
+ }
556
+ // Resume/re-key: non-fatal — identity was already established previously.
557
+ this.emit('error', ixErr);
542
558
  }
543
559
  }
544
560
  // Persist session (with identity fields if exchange succeeded)
@@ -282,7 +282,16 @@ export class RelayClient extends EventEmitter {
282
282
  this.emit('pairing_code', code);
283
283
  });
284
284
  const result = await pairing.pairAsClient(); // no code = initiator mode
285
- await this.onPaired(result, 'paired');
285
+ try {
286
+ await this.onPaired(result, 'paired');
287
+ }
288
+ catch (err) {
289
+ // Identity exchange failed on fresh pairing — tear down transport to
290
+ // prevent leaking an authenticated-but-unusable WebSocket, then re-throw.
291
+ // Do NOT emit('error') here — the throw propagates to connect() caller.
292
+ this.transport?.disconnect();
293
+ throw err;
294
+ }
286
295
  }
287
296
  async resumeSession(session) {
288
297
  this.setStatus('connecting');
@@ -510,15 +519,22 @@ export class RelayClient extends EventEmitter {
510
519
  // On fresh pairing (event='paired'): always exchange, even if we had a previous peer
511
520
  // — this handles the case where user pairs with a NEW agent machine.
512
521
  // On resume/re-key (event='resumed'): only exchange if not already done.
513
- const shouldExchange = event === 'paired' || !this._identityExchanged;
522
+ const freshPairing = event === 'paired';
523
+ const shouldExchange = freshPairing || !this._identityExchanged;
514
524
  if (shouldExchange) {
515
525
  try {
516
- await this._performIdentityExchange(sessionData, event === 'paired');
526
+ await this._performIdentityExchange(sessionData, freshPairing);
517
527
  }
518
528
  catch (err) {
519
- // Identity exchange failure is non-fatal connection works without it,
520
- // but identity reconnect won't be available.
521
- this.emit('error', new IdentityExchangeError(`Identity exchange failed: ${err instanceof Error ? err.message : err}`));
529
+ const ixErr = new IdentityExchangeError(`Identity exchange failed: ${err instanceof Error ? err.message : err}`);
530
+ if (freshPairing) {
531
+ // Fresh pairing MUST establish identity without it, identity reconnect
532
+ // is permanently unavailable for this pairing. Re-throw to fail the
533
+ // connection; user can retry with a new pairing code.
534
+ throw ixErr;
535
+ }
536
+ // Resume/re-key: non-fatal — identity was already established previously.
537
+ this.emit('error', ixErr);
522
538
  }
523
539
  }
524
540
  // Persist session (with identity fields if exchange succeeded)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoclaw-relay-agent",
3
- "version": "0.15.2",
3
+ "version": "0.15.3",
4
4
  "description": "EchoClaw Relay Connection — E2E encrypted relay transport, pairing, and session management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",