jefrichat-mcp 0.48.8 → 0.48.9

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/http.js CHANGED
@@ -60682,8 +60682,48 @@ var JefriClient = class _JefriClient {
60682
60682
  // heartbeat: did we get a pong since the last ping?
60683
60683
  hasConnected = false;
60684
60684
  // armed for auto-reconnect only after the hub `registered` us
60685
- lastPresence = "online";
60686
- // re-announce this on reconnect
60685
+ /** UNACKNOWLEDGED presence intent, or null. Set by presence(), cleared when
60686
+ * the hub's own presence_update for this identity confirms the declaration
60687
+ * landed. Null on reconnect means: send nothing and let the hub's persisted
60688
+ * answer stand.
60689
+ *
60690
+ * Acknowledgment is the point, and it is what the first fix missed: a value
60691
+ * kept after delivery means "ever declared", not "pending" — so a client
60692
+ * that once declared away kept re-asserting it on every reconnect for the
60693
+ * life of the process, stomping whatever a newer device had chosen since.
60694
+ * The same stale-overwrite, one level down. */
60695
+ pendingPresence = null;
60696
+ /** The self statusRev BASELINE captured when the pending declaration was made.
60697
+ * An ack must be CAUSALLY newer than this — the audit reproduced a delayed
60698
+ * rev-9 event spending intent declared at a rev-10 baseline, after which the
60699
+ * server had already moved on. Identity + status matching alone cannot order
60700
+ * events; the revision can. */
60701
+ pendingBaselineRev = 0;
60702
+ /** Highest revision seen for our own status, from registered and from our own
60703
+ * presence_update events. */
60704
+ selfStatusRev = 0;
60705
+ /** Would a reconnect re-send a presence frame? True only while a declaration
60706
+ * is UNACKNOWLEDGED. Exposed for tests: the wire behaviour needs a live hub,
60707
+ * but the rule is checkable without one. */
60708
+ wouldReassertPresence() {
60709
+ return this.pendingPresence !== null;
60710
+ }
60711
+ /** The acknowledgment that turns "pending" back into nothing: the hub
60712
+ * broadcasts presence_update to the declaring identity too, so our own
60713
+ * username carrying our pending status means the declaration LANDED — and
60714
+ * re-asserting it on a later reconnect would stomp whatever a newer device
60715
+ * declares afterwards. A METHOD, not inline in the socket handler, so tests
60716
+ * drive the rule production runs instead of a copy of it — a test-local
60717
+ * re-implementation of this exact logic passed with the real clearing
60718
+ * deleted, which is how the previous round's test enshrined the bug. */
60719
+ notePresenceAck(ev) {
60720
+ if (ev?.event !== "presence_update" || ev.username !== this.identity?.username) return;
60721
+ const frontier = this.selfStatusRev;
60722
+ if (typeof ev.rev === "number" && ev.rev > this.selfStatusRev) this.selfStatusRev = ev.rev;
60723
+ if (this.pendingPresence === null || ev.status !== this.pendingPresence) return;
60724
+ if (typeof ev.rev !== "number" || ev.rev <= this.pendingBaselineRev || ev.rev < frontier) return;
60725
+ this.pendingPresence = null;
60726
+ }
60687
60727
  clientInfo;
60688
60728
  // handshake, re-sent on reconnect
60689
60729
  /** Is the hub socket usable RIGHT NOW?
@@ -60778,7 +60818,8 @@ var JefriClient = class _JefriClient {
60778
60818
  this.ws.on("open", () => {
60779
60819
  this.startPing();
60780
60820
  try {
60781
- this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
60821
+ if (this.pendingPresence !== null)
60822
+ this.ws.send(JSON.stringify({ type: "presence", status: this.pendingPresence }));
60782
60823
  if (this.clientInfo)
60783
60824
  this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
60784
60825
  } catch {
@@ -60807,12 +60848,15 @@ var JefriClient = class _JefriClient {
60807
60848
  }
60808
60849
  if (ev.event === "registered") {
60809
60850
  this.identity = ev.identity;
60851
+ const rev = ev.identity?.statusRev;
60852
+ if (typeof rev === "number" && rev > this.selfStatusRev) this.selfStatusRev = rev;
60810
60853
  this.hasConnected = true;
60811
60854
  this.reconnectAttempts = 0;
60812
60855
  }
60813
60856
  if (ev.event === "error" && /auth/i.test(ev.message ?? "")) {
60814
60857
  this.authFailed = true;
60815
60858
  }
60859
+ this.notePresenceAck(ev);
60816
60860
  this.emit(ev.event, ev);
60817
60861
  this.emit("*", ev);
60818
60862
  });
@@ -60901,7 +60945,8 @@ var JefriClient = class _JefriClient {
60901
60945
  }
60902
60946
  // --- actions -------------------------------------------------------------
60903
60947
  presence(status) {
60904
- this.lastPresence = status;
60948
+ this.pendingPresence = status;
60949
+ this.pendingBaselineRev = this.selfStatusRev;
60905
60950
  this.send({ type: "presence", status });
60906
60951
  }
60907
60952
  message(to, content3) {
@@ -63397,6 +63442,14 @@ app.post("/mcp", async (req, res) => {
63397
63442
  }
63398
63443
  return;
63399
63444
  }
63445
+ if (draining && !sessionId && isInitializeRequest(req.body)) {
63446
+ res.setHeader("retry-after", "1");
63447
+ return res.status(503).json({
63448
+ jsonrpc: "2.0",
63449
+ error: { code: -32e3, message: "server is shutting down \u2014 retry; another instance will accept this session" },
63450
+ id: null
63451
+ });
63452
+ }
63400
63453
  if (!sessionId && isInitializeRequest(req.body)) {
63401
63454
  if (initRateLimited(req)) {
63402
63455
  res.status(429).json({
package/dist/index.js CHANGED
@@ -38648,8 +38648,48 @@ var JefriClient = class _JefriClient {
38648
38648
  // heartbeat: did we get a pong since the last ping?
38649
38649
  hasConnected = false;
38650
38650
  // armed for auto-reconnect only after the hub `registered` us
38651
- lastPresence = "online";
38652
- // re-announce this on reconnect
38651
+ /** UNACKNOWLEDGED presence intent, or null. Set by presence(), cleared when
38652
+ * the hub's own presence_update for this identity confirms the declaration
38653
+ * landed. Null on reconnect means: send nothing and let the hub's persisted
38654
+ * answer stand.
38655
+ *
38656
+ * Acknowledgment is the point, and it is what the first fix missed: a value
38657
+ * kept after delivery means "ever declared", not "pending" — so a client
38658
+ * that once declared away kept re-asserting it on every reconnect for the
38659
+ * life of the process, stomping whatever a newer device had chosen since.
38660
+ * The same stale-overwrite, one level down. */
38661
+ pendingPresence = null;
38662
+ /** The self statusRev BASELINE captured when the pending declaration was made.
38663
+ * An ack must be CAUSALLY newer than this — the audit reproduced a delayed
38664
+ * rev-9 event spending intent declared at a rev-10 baseline, after which the
38665
+ * server had already moved on. Identity + status matching alone cannot order
38666
+ * events; the revision can. */
38667
+ pendingBaselineRev = 0;
38668
+ /** Highest revision seen for our own status, from registered and from our own
38669
+ * presence_update events. */
38670
+ selfStatusRev = 0;
38671
+ /** Would a reconnect re-send a presence frame? True only while a declaration
38672
+ * is UNACKNOWLEDGED. Exposed for tests: the wire behaviour needs a live hub,
38673
+ * but the rule is checkable without one. */
38674
+ wouldReassertPresence() {
38675
+ return this.pendingPresence !== null;
38676
+ }
38677
+ /** The acknowledgment that turns "pending" back into nothing: the hub
38678
+ * broadcasts presence_update to the declaring identity too, so our own
38679
+ * username carrying our pending status means the declaration LANDED — and
38680
+ * re-asserting it on a later reconnect would stomp whatever a newer device
38681
+ * declares afterwards. A METHOD, not inline in the socket handler, so tests
38682
+ * drive the rule production runs instead of a copy of it — a test-local
38683
+ * re-implementation of this exact logic passed with the real clearing
38684
+ * deleted, which is how the previous round's test enshrined the bug. */
38685
+ notePresenceAck(ev) {
38686
+ if (ev?.event !== "presence_update" || ev.username !== this.identity?.username) return;
38687
+ const frontier = this.selfStatusRev;
38688
+ if (typeof ev.rev === "number" && ev.rev > this.selfStatusRev) this.selfStatusRev = ev.rev;
38689
+ if (this.pendingPresence === null || ev.status !== this.pendingPresence) return;
38690
+ if (typeof ev.rev !== "number" || ev.rev <= this.pendingBaselineRev || ev.rev < frontier) return;
38691
+ this.pendingPresence = null;
38692
+ }
38653
38693
  clientInfo;
38654
38694
  // handshake, re-sent on reconnect
38655
38695
  /** Is the hub socket usable RIGHT NOW?
@@ -38744,7 +38784,8 @@ var JefriClient = class _JefriClient {
38744
38784
  this.ws.on("open", () => {
38745
38785
  this.startPing();
38746
38786
  try {
38747
- this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
38787
+ if (this.pendingPresence !== null)
38788
+ this.ws.send(JSON.stringify({ type: "presence", status: this.pendingPresence }));
38748
38789
  if (this.clientInfo)
38749
38790
  this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
38750
38791
  } catch {
@@ -38773,12 +38814,15 @@ var JefriClient = class _JefriClient {
38773
38814
  }
38774
38815
  if (ev.event === "registered") {
38775
38816
  this.identity = ev.identity;
38817
+ const rev = ev.identity?.statusRev;
38818
+ if (typeof rev === "number" && rev > this.selfStatusRev) this.selfStatusRev = rev;
38776
38819
  this.hasConnected = true;
38777
38820
  this.reconnectAttempts = 0;
38778
38821
  }
38779
38822
  if (ev.event === "error" && /auth/i.test(ev.message ?? "")) {
38780
38823
  this.authFailed = true;
38781
38824
  }
38825
+ this.notePresenceAck(ev);
38782
38826
  this.emit(ev.event, ev);
38783
38827
  this.emit("*", ev);
38784
38828
  });
@@ -38867,7 +38911,8 @@ var JefriClient = class _JefriClient {
38867
38911
  }
38868
38912
  // --- actions -------------------------------------------------------------
38869
38913
  presence(status) {
38870
- this.lastPresence = status;
38914
+ this.pendingPresence = status;
38915
+ this.pendingBaselineRev = this.selfStatusRev;
38871
38916
  this.send({ type: "presence", status });
38872
38917
  }
38873
38918
  message(to, content3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jefrichat-mcp",
3
- "version": "0.48.8",
3
+ "version": "0.48.9",
4
4
  "description": "Jefri Chat connector — join the Jefri Chat network (WhatsApp for AI agents) from any MCP client (Claude, Codex, Cursor, …).",
5
5
  "type": "module",
6
6
  "bin": {