@diegoaltoworks/chatter 0.62.0 → 0.63.0

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.
@@ -90,6 +90,15 @@ export interface LeaseGatedConnectDeps {
90
90
  waitMs?: number;
91
91
  schedule?: (fn: () => void, ms: number) => void;
92
92
  logger?: Logger;
93
+ /**
94
+ * Recovery for a `connect` that threw, replacing the default (release the
95
+ * session, re-check after `waitMs`). The channel injects one so a thrown
96
+ * connect backs off on the same failure counter a closed connection uses
97
+ * instead of re-attempting at a flat interval. Whatever it does, it must
98
+ * end with another attempt scheduled: a session whose lease is held with
99
+ * no live socket and nothing pending never recovers on its own.
100
+ */
101
+ onConnectFailed?: (sessionId: string, error: unknown) => void;
93
102
  }
94
103
  /**
95
104
  * Acquire `sessionId`'s lease before connecting — two instances must never
@@ -99,6 +108,11 @@ export interface LeaseGatedConnectDeps {
99
108
  * heartbeat ever finds the lease gone (this instance went stale and another
100
109
  * took over), the session is torn down and this re-enters the same
101
110
  * wait-and-retry loop rather than staying connected alongside the new holder.
111
+ *
112
+ * Every failed connect attempt lands back here too, whether it threw on its
113
+ * first try or on a reconnect: this loop is the only way a session recovers,
114
+ * so an attempt that fails without leaving a socket behind (no socket, no
115
+ * future "close" event) must re-enter it rather than stop at a log line.
102
116
  */
103
117
  export declare function acquireSessionLease(sessionId: string, deps: LeaseGatedConnectDeps): Promise<void>;
104
118
  /** Sender-registry name for a session — the default session stays unprefixed so single-number deployments see a plain `"whatsapp"` name. */
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/channel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/channel.ts"],"names":[],"mappings":"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"}
@@ -360,11 +360,17 @@ async function shutdownWaSessions(sessions) {
360
360
  })
361
361
  );
362
362
  }
363
- async function loseSession(sessionId, sessions) {
363
+ function claimSession(sessionId, sessions) {
364
364
  const handle = sessions.get(sessionId);
365
- if (!handle) return;
365
+ if (!handle) return void 0;
366
366
  sessions.delete(sessionId);
367
+ return handle;
368
+ }
369
+ async function loseSession(sessionId, sessions) {
370
+ const handle = claimSession(sessionId, sessions);
371
+ if (!handle) return false;
367
372
  await shutdownWaSessions(/* @__PURE__ */ new Map([[sessionId, handle]]));
373
+ return true;
368
374
  }
369
375
  async function acquireSessionLease(sessionId, deps) {
370
376
  const { leaseStore, instanceId, sessions, isStopped, connect } = deps;
@@ -400,7 +406,9 @@ async function acquireSessionLease(sessionId, deps) {
400
406
  void leaseStore.tryAcquire(sessionId, instanceId, now(), staleMs).then((stillHeld) => {
401
407
  if (stillHeld || isStopped()) return;
402
408
  logger.error(`WhatsApp[${sessionId}]: lost the lease to another instance \u2014 disconnecting`);
403
- void loseSession(sessionId, sessions).then(retry);
409
+ void loseSession(sessionId, sessions).then((lost) => {
410
+ if (lost) retry();
411
+ });
404
412
  }).catch((error) => logger.warn(`WhatsApp[${sessionId}]: lease heartbeat failed:`, error));
405
413
  }, heartbeatMs);
406
414
  if (typeof heartbeat === "object" && "unref" in heartbeat) heartbeat.unref();
@@ -412,6 +420,10 @@ async function acquireSessionLease(sessionId, deps) {
412
420
  try {
413
421
  await connect(sessionId);
414
422
  } catch (error) {
423
+ if (deps.onConnectFailed) {
424
+ deps.onConnectFailed(sessionId, error);
425
+ return;
426
+ }
415
427
  logger.error(`WhatsApp[${sessionId}]: connect failed right after acquiring the lease:`, error);
416
428
  await loseSession(sessionId, sessions);
417
429
  retry();
@@ -458,6 +470,50 @@ function createWhatsAppChannel(config) {
458
470
  const authStore = config.authStore ?? createTursoWaAuthKV(deps.db);
459
471
  senders = deps.senders;
460
472
  await loadBaileys2();
473
+ const leaseDeps = {
474
+ leaseStore,
475
+ instanceId,
476
+ sessions,
477
+ isStopped: () => stopped,
478
+ connect,
479
+ now,
480
+ staleMs,
481
+ heartbeatMs,
482
+ waitMs,
483
+ schedule,
484
+ logger: log,
485
+ onConnectFailed: recoverFromFailedConnect
486
+ };
487
+ function nextRetryDelayMs(sessionId) {
488
+ const failures = failureCounts.get(sessionId) ?? 0;
489
+ failureCounts.set(sessionId, failures + 1);
490
+ return reconnectDelayMs(failures);
491
+ }
492
+ function attemptConnect(sessionId) {
493
+ void connect(sessionId).catch((error) => recoverFromFailedConnect(sessionId, error));
494
+ }
495
+ function recoverFromFailedConnect(sessionId, error) {
496
+ if (stopped) return;
497
+ const handle = claimSession(sessionId, sessions);
498
+ if (!handle) return;
499
+ const delay = nextRetryDelayMs(sessionId);
500
+ log.error(
501
+ `WhatsApp[${sessionId}]: connect attempt failed, releasing the lease and retrying in ${Math.round(delay / 1e3)}s:`,
502
+ error
503
+ );
504
+ schedule(
505
+ () => void acquireSessionLease(sessionId, leaseDeps).catch(
506
+ (retryError) => log.error(`WhatsApp[${sessionId}]: lease re-check failed:`, retryError)
507
+ ),
508
+ delay
509
+ );
510
+ void shutdownWaSessions(/* @__PURE__ */ new Map([[sessionId, handle]])).catch(
511
+ (teardownError) => log.warn(
512
+ `WhatsApp[${sessionId}]: teardown after a failed connect failed:`,
513
+ teardownError
514
+ )
515
+ );
516
+ }
461
517
  async function connect(sessionId) {
462
518
  if (stopped || !sessions.has(sessionId)) return;
463
519
  const baileys = await loadBaileys2();
@@ -476,7 +532,7 @@ function createWhatsAppChannel(config) {
476
532
  log.warn(
477
533
  `WhatsApp[${sessionId}]: no paired session found \u2014 pair it first (see docs/channels.md). Re-checking in 60s.`
478
534
  );
479
- schedule(() => void connect(sessionId).catch(() => void 0), 6e4);
535
+ schedule(() => attemptConnect(sessionId), 6e4);
480
536
  return;
481
537
  }
482
538
  const { version } = await baileys.fetchLatestBaileysVersion();
@@ -542,17 +598,11 @@ function createWhatsAppChannel(config) {
542
598
  return;
543
599
  }
544
600
  if (!stopped && sessions.has(sessionId)) {
545
- const delay = reconnectDelayMs(failureCounts.get(sessionId) ?? 0);
546
- failureCounts.set(sessionId, (failureCounts.get(sessionId) ?? 0) + 1);
601
+ const delay = nextRetryDelayMs(sessionId);
547
602
  log.warn(
548
603
  `WhatsApp[${sessionId}]: connection closed (${statusCode ?? "?"}), reconnecting in ${Math.round(delay / 1e3)}s...`
549
604
  );
550
- schedule(
551
- () => void connect(sessionId).catch(
552
- (error) => log.error(`WhatsApp[${sessionId}]: reconnect failed:`, error)
553
- ),
554
- delay
555
- );
605
+ schedule(() => attemptConnect(sessionId), delay);
556
606
  }
557
607
  }
558
608
  });
@@ -570,19 +620,9 @@ function createWhatsAppChannel(config) {
570
620
  }
571
621
  }
572
622
  for (const sessionId of sessionIds) {
573
- void acquireSessionLease(sessionId, {
574
- leaseStore,
575
- instanceId,
576
- sessions,
577
- isStopped: () => stopped,
578
- connect,
579
- now,
580
- staleMs,
581
- heartbeatMs,
582
- waitMs,
583
- schedule,
584
- logger: log
585
- }).catch((error) => log.error(`WhatsApp[${sessionId}]: failed to start:`, error));
623
+ void acquireSessionLease(sessionId, leaseDeps).catch(
624
+ (error) => log.error(`WhatsApp[${sessionId}]: failed to start:`, error)
625
+ );
586
626
  }
587
627
  },
588
628
  async stop() {
@@ -295,11 +295,17 @@ async function shutdownWaSessions(sessions) {
295
295
  })
296
296
  );
297
297
  }
298
- async function loseSession(sessionId, sessions) {
298
+ function claimSession(sessionId, sessions) {
299
299
  const handle = sessions.get(sessionId);
300
- if (!handle) return;
300
+ if (!handle) return void 0;
301
301
  sessions.delete(sessionId);
302
+ return handle;
303
+ }
304
+ async function loseSession(sessionId, sessions) {
305
+ const handle = claimSession(sessionId, sessions);
306
+ if (!handle) return false;
302
307
  await shutdownWaSessions(/* @__PURE__ */ new Map([[sessionId, handle]]));
308
+ return true;
303
309
  }
304
310
  async function acquireSessionLease(sessionId, deps) {
305
311
  const { leaseStore, instanceId, sessions, isStopped, connect } = deps;
@@ -335,7 +341,9 @@ async function acquireSessionLease(sessionId, deps) {
335
341
  void leaseStore.tryAcquire(sessionId, instanceId, now(), staleMs).then((stillHeld) => {
336
342
  if (stillHeld || isStopped()) return;
337
343
  logger.error(`WhatsApp[${sessionId}]: lost the lease to another instance \u2014 disconnecting`);
338
- void loseSession(sessionId, sessions).then(retry);
344
+ void loseSession(sessionId, sessions).then((lost) => {
345
+ if (lost) retry();
346
+ });
339
347
  }).catch((error) => logger.warn(`WhatsApp[${sessionId}]: lease heartbeat failed:`, error));
340
348
  }, heartbeatMs);
341
349
  if (typeof heartbeat === "object" && "unref" in heartbeat) heartbeat.unref();
@@ -347,6 +355,10 @@ async function acquireSessionLease(sessionId, deps) {
347
355
  try {
348
356
  await connect(sessionId);
349
357
  } catch (error) {
358
+ if (deps.onConnectFailed) {
359
+ deps.onConnectFailed(sessionId, error);
360
+ return;
361
+ }
350
362
  logger.error(`WhatsApp[${sessionId}]: connect failed right after acquiring the lease:`, error);
351
363
  await loseSession(sessionId, sessions);
352
364
  retry();
@@ -393,6 +405,50 @@ function createWhatsAppChannel(config) {
393
405
  const authStore = config.authStore ?? createTursoWaAuthKV(deps.db);
394
406
  senders = deps.senders;
395
407
  await loadBaileys2();
408
+ const leaseDeps = {
409
+ leaseStore,
410
+ instanceId,
411
+ sessions,
412
+ isStopped: () => stopped,
413
+ connect,
414
+ now,
415
+ staleMs,
416
+ heartbeatMs,
417
+ waitMs,
418
+ schedule,
419
+ logger: log,
420
+ onConnectFailed: recoverFromFailedConnect
421
+ };
422
+ function nextRetryDelayMs(sessionId) {
423
+ const failures = failureCounts.get(sessionId) ?? 0;
424
+ failureCounts.set(sessionId, failures + 1);
425
+ return reconnectDelayMs(failures);
426
+ }
427
+ function attemptConnect(sessionId) {
428
+ void connect(sessionId).catch((error) => recoverFromFailedConnect(sessionId, error));
429
+ }
430
+ function recoverFromFailedConnect(sessionId, error) {
431
+ if (stopped) return;
432
+ const handle = claimSession(sessionId, sessions);
433
+ if (!handle) return;
434
+ const delay = nextRetryDelayMs(sessionId);
435
+ log.error(
436
+ `WhatsApp[${sessionId}]: connect attempt failed, releasing the lease and retrying in ${Math.round(delay / 1e3)}s:`,
437
+ error
438
+ );
439
+ schedule(
440
+ () => void acquireSessionLease(sessionId, leaseDeps).catch(
441
+ (retryError) => log.error(`WhatsApp[${sessionId}]: lease re-check failed:`, retryError)
442
+ ),
443
+ delay
444
+ );
445
+ void shutdownWaSessions(/* @__PURE__ */ new Map([[sessionId, handle]])).catch(
446
+ (teardownError) => log.warn(
447
+ `WhatsApp[${sessionId}]: teardown after a failed connect failed:`,
448
+ teardownError
449
+ )
450
+ );
451
+ }
396
452
  async function connect(sessionId) {
397
453
  if (stopped || !sessions.has(sessionId)) return;
398
454
  const baileys = await loadBaileys2();
@@ -411,7 +467,7 @@ function createWhatsAppChannel(config) {
411
467
  log.warn(
412
468
  `WhatsApp[${sessionId}]: no paired session found \u2014 pair it first (see docs/channels.md). Re-checking in 60s.`
413
469
  );
414
- schedule(() => void connect(sessionId).catch(() => void 0), 6e4);
470
+ schedule(() => attemptConnect(sessionId), 6e4);
415
471
  return;
416
472
  }
417
473
  const { version } = await baileys.fetchLatestBaileysVersion();
@@ -477,17 +533,11 @@ function createWhatsAppChannel(config) {
477
533
  return;
478
534
  }
479
535
  if (!stopped && sessions.has(sessionId)) {
480
- const delay = reconnectDelayMs(failureCounts.get(sessionId) ?? 0);
481
- failureCounts.set(sessionId, (failureCounts.get(sessionId) ?? 0) + 1);
536
+ const delay = nextRetryDelayMs(sessionId);
482
537
  log.warn(
483
538
  `WhatsApp[${sessionId}]: connection closed (${statusCode ?? "?"}), reconnecting in ${Math.round(delay / 1e3)}s...`
484
539
  );
485
- schedule(
486
- () => void connect(sessionId).catch(
487
- (error) => log.error(`WhatsApp[${sessionId}]: reconnect failed:`, error)
488
- ),
489
- delay
490
- );
540
+ schedule(() => attemptConnect(sessionId), delay);
491
541
  }
492
542
  }
493
543
  });
@@ -505,19 +555,9 @@ function createWhatsAppChannel(config) {
505
555
  }
506
556
  }
507
557
  for (const sessionId of sessionIds) {
508
- void acquireSessionLease(sessionId, {
509
- leaseStore,
510
- instanceId,
511
- sessions,
512
- isStopped: () => stopped,
513
- connect,
514
- now,
515
- staleMs,
516
- heartbeatMs,
517
- waitMs,
518
- schedule,
519
- logger: log
520
- }).catch((error) => log.error(`WhatsApp[${sessionId}]: failed to start:`, error));
558
+ void acquireSessionLease(sessionId, leaseDeps).catch(
559
+ (error) => log.error(`WhatsApp[${sessionId}]: failed to start:`, error)
560
+ );
521
561
  }
522
562
  },
523
563
  async stop() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegoaltoworks/chatter",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "description": "Embeddable AI chatbot framework with RAG, authentication, and customizable widgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -201,14 +201,14 @@
201
201
  "@biomejs/biome": "2.5.8",
202
202
  "@commitlint/cli": "21.2.2",
203
203
  "@commitlint/config-conventional": "21.2.2",
204
- "@hono/node-server": "2.1.0",
204
+ "@hono/node-server": "2.1.1",
205
205
  "@libsql/client": "0.17.4",
206
206
  "@modelcontextprotocol/sdk": "1.30.0",
207
207
  "@types/bun": "1.3.14",
208
208
  "@types/node": "26.2.0",
209
209
  "@whiskeysockets/baileys": "7.0.0-rc14",
210
210
  "esbuild": "0.28.2",
211
- "hono": "4.13.1",
211
+ "hono": "4.13.2",
212
212
  "husky": "9.1.7",
213
213
  "lint-staged": "17.3.0",
214
214
  "openai": "7.4.0",