agent-comms 2.18.0 → 2.18.2

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.
@@ -15,7 +15,7 @@
15
15
  "url": "https://github.com/ExaDev/agent-comms.git"
16
16
  },
17
17
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
18
- "version": "2.18.0"
18
+ "version": "2.18.2"
19
19
  }
20
20
  ]
21
21
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-comms",
3
3
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
4
- "version": "2.18.0",
4
+ "version": "2.18.2",
5
5
  "author": {
6
6
  "name": "ExaDev"
7
7
  },
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white)](https://github.com/ExaDev/agent-comms)
4
4
  [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/agent-comms)
5
- [![version](https://img.shields.io/badge/version-2.18.0-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.18.0)
5
+ [![version](https://img.shields.io/badge/version-2.18.2-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.18.2)
6
6
  [![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/agent-comms/ci.yml?branch=main)](https://github.com/ExaDev/agent-comms/actions)
7
7
 
8
8
  Cross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.
@@ -0,0 +1,9 @@
1
+ export declare const INITIAL_RETRY_DELAY_MS = 15000;
2
+ export declare const MAX_RETRY_DELAY_MS = 120000;
3
+ export declare const RETRY_BACKOFF_MULTIPLIER = 2;
4
+ export declare const MAX_TOTAL_RETRY_MS: number;
5
+ /** The next delay in the doubling-with-cap backoff schedule, given the delay just used. */
6
+ export declare function nextRetryDelayMs(previousDelayMs: number): number;
7
+ /** Recognises a publish failure worth retrying (npm propagation lag, or a transient 5xx from the registry's own infrastructure) as opposed to any other failure -- a genuinely invalid publish request, for instance, which retrying can never fix. */
8
+ export declare function isRetryablePublishFailure(output: string): boolean;
9
+ //# sourceMappingURL=mcp-registry-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-registry-retry.d.ts","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AAEjD,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAKhE;AAiBD,uPAAuP;AACvP,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,27 @@
1
+ // Pure retry logic for scripts/publish-mcp-registry.ts, kept here rather than in the script itself so it's testable under src/test/ -- tsconfig.json's rootDir is src/, so a test there can't import from scripts/ (outside that root), while a script importing from src/ is the normal direction. The script itself keeps the actual Date.now()/spawnSync-driven retry loop, which is inherently imperative and time-based; this module holds only the two decisions that loop makes that are worth asserting deterministically.
2
+ // The MCP registry validates a publish against the npm registry, where the version semantic-release published seconds earlier may not be visible yet. The registry's own error text says to wait and retry, so retry that failure specifically instead of failing the release job on propagation lag.
3
+ //
4
+ // Confirmed insufficient in production (ExaDev/agent-comms, 2026-09-13, v2.18.0): the original fixed [15, 30, 60, 120] schedule (225s total) exhausted and failed the release job while npm's own CDN still hadn't propagated the just-published version -- a real, observed lag longer than that budget, not a hypothetical one. The release job carries no timeout-minutes of its own, so there's no tight external deadline forcing a small retry budget.
5
+ export const INITIAL_RETRY_DELAY_MS = 15_000;
6
+ export const MAX_RETRY_DELAY_MS = 120_000;
7
+ export const RETRY_BACKOFF_MULTIPLIER = 2;
8
+ export const MAX_TOTAL_RETRY_MS = 15 * 60 * 1000;
9
+ /** The next delay in the doubling-with-cap backoff schedule, given the delay just used. */
10
+ export function nextRetryDelayMs(previousDelayMs) {
11
+ return Math.min(previousDelayMs * RETRY_BACKOFF_MULTIPLIER, MAX_RETRY_DELAY_MS);
12
+ }
13
+ /** The MCP registry's own npm-propagation-lag error text -- the version semantic-release just published may not be visible to the registry's own npm lookup for a few seconds. */
14
+ function isNpmPropagationLag(output) {
15
+ return (output.includes("was not found") &&
16
+ output.includes("A newly published release can take a moment"));
17
+ }
18
+ // Confirmed as a second, distinct retryable failure mode in the same run this retry logic was first fixed for (ExaDev/agent-comms, 2026-09-13, v2.18.1): the registry returned a genuine HTTP 504 (a transient gateway timeout on the registry's own infrastructure, not anything to do with npm propagation), and the retry logic at the time only recognised the propagation-lag text above, so it failed the release job immediately instead of retrying a condition that resolved itself on a plain re-run seconds later. Any 5xx is the registry's own server failing to complete the request, not this side's request being wrong -- exactly the class of error a retry can plausibly fix, unlike a 4xx (this side asked for something invalid) other than the specific propagation-lag 404 already handled above.
19
+ const SERVER_ERROR_STATUS_PATTERN = /server returned status 5\d{2}\b/;
20
+ function isRetryableServerError(output) {
21
+ return SERVER_ERROR_STATUS_PATTERN.test(output);
22
+ }
23
+ /** Recognises a publish failure worth retrying (npm propagation lag, or a transient 5xx from the registry's own infrastructure) as opposed to any other failure -- a genuinely invalid publish request, for instance, which retrying can never fix. */
24
+ export function isRetryablePublishFailure(output) {
25
+ return isNpmPropagationLag(output) || isRetryableServerError(output);
26
+ }
27
+ //# sourceMappingURL=mcp-registry-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-registry-retry.js","sourceRoot":"","sources":["../../src/core/mcp-registry-retry.ts"],"names":[],"mappings":"AAAA,mgBAAmgB;AAEngB,sSAAsS;AACtS,EAAE;AACF,6bAA6b;AAC7b,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEjD,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,eAAuB;IACtD,OAAO,IAAI,CAAC,GAAG,CACb,eAAe,GAAG,wBAAwB,EAC1C,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED,kLAAkL;AAClL,SAAS,mBAAmB,CAAC,MAAc;IACzC,OAAO,CACL,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QAChC,MAAM,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,yxBAAyxB;AACzxB,MAAM,2BAA2B,GAAG,iCAAiC,CAAC;AAEtE,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,uPAAuP;AACvP,MAAM,UAAU,yBAAyB,CAAC,MAAc;IACtD,OAAO,mBAAmB,CAAC,MAAM,CAAC,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-registry-retry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-registry-retry.test.d.ts","sourceRoot":"","sources":["../../src/test/mcp-registry-retry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { INITIAL_RETRY_DELAY_MS, MAX_RETRY_DELAY_MS, MAX_TOTAL_RETRY_MS, isRetryablePublishFailure, nextRetryDelayMs, } from "../core/mcp-registry-retry.js";
4
+ describe("nextRetryDelayMs", () => {
5
+ it("doubles the previous delay", () => {
6
+ assert.equal(nextRetryDelayMs(INITIAL_RETRY_DELAY_MS), INITIAL_RETRY_DELAY_MS * 2);
7
+ });
8
+ it("caps at MAX_RETRY_DELAY_MS", () => {
9
+ assert.equal(nextRetryDelayMs(MAX_RETRY_DELAY_MS), MAX_RETRY_DELAY_MS);
10
+ assert.equal(nextRetryDelayMs(MAX_RETRY_DELAY_MS * 10), MAX_RETRY_DELAY_MS);
11
+ });
12
+ it("accumulates to comfortably exceed the previous, confirmed-insufficient 225-second retry budget (agent-comms v2.18.0's release job exhausted a fixed [15, 30, 60, 120] schedule while npm's own CDN still hadn't propagated the just-published version)", () => {
13
+ const previousBudgetMs = (15 + 30 + 60 + 120) * 1000;
14
+ let delayMs = INITIAL_RETRY_DELAY_MS;
15
+ let totalMs = 0;
16
+ while (totalMs < MAX_TOTAL_RETRY_MS) {
17
+ totalMs += delayMs;
18
+ delayMs = nextRetryDelayMs(delayMs);
19
+ }
20
+ assert.ok(totalMs > previousBudgetMs);
21
+ assert.ok(totalMs >= MAX_TOTAL_RETRY_MS);
22
+ });
23
+ });
24
+ describe("isRetryablePublishFailure", () => {
25
+ it("recognises the MCP registry's own npm-propagation-lag error text", () => {
26
+ const output = "registry validation failed for package 0 (agent-comms): NPM package 'agent-comms' exists, but version '2.18.0' was not found (status: 404). A newly published release can take a moment to appear on the registry. Wait and retry, or publish version '2.18.0' before registering it";
27
+ assert.equal(isRetryablePublishFailure(output), true);
28
+ });
29
+ it("recognises a transient 5xx from the registry's own infrastructure -- confirmed as a real, second retryable failure mode (ExaDev/agent-comms, 2026-09-13, v2.18.1): the registry returned a genuine HTTP 504 that resolved itself on a plain re-run seconds later, but the retry logic at the time only recognised the propagation-lag text above and failed the release job immediately instead of retrying it", () => {
30
+ const output = "Error: publish failed: server returned status 504: <html>";
31
+ assert.equal(isRetryablePublishFailure(output), true);
32
+ });
33
+ it("does not retry a 4xx other than the specific propagation-lag 404 -- a genuinely invalid publish request, which retrying can never fix", () => {
34
+ const output = "Error: publish failed: server returned status 400: Bad Request";
35
+ assert.equal(isRetryablePublishFailure(output), false);
36
+ });
37
+ it("does not misclassify an unrelated failure as retryable", () => {
38
+ assert.equal(isRetryablePublishFailure("permission denied"), false);
39
+ });
40
+ });
41
+ //# sourceMappingURL=mcp-registry-retry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-registry-retry.test.js","sourceRoot":"","sources":["../../src/test/mcp-registry-retry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,KAAK,CACV,gBAAgB,CAAC,sBAAsB,CAAC,EACxC,sBAAsB,GAAG,CAAC,CAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,kBAAkB,GAAG,EAAE,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wPAAwP,EAAE,GAAG,EAAE;QAChQ,MAAM,gBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;QACrD,IAAI,OAAO,GAAG,sBAAsB,CAAC;QACrC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,OAAO,GAAG,kBAAkB,EAAE,CAAC;YACpC,OAAO,IAAI,OAAO,CAAC;YACnB,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,CAAC,EAAE,CAAC,OAAO,GAAG,gBAAgB,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,kBAAkB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GACV,sRAAsR,CAAC;QACzR,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gZAAgZ,EAAE,GAAG,EAAE;QACxZ,MAAM,MAAM,GAAG,2DAA2D,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uIAAuI,EAAE,GAAG,EAAE;QAC/I,MAAM,MAAM,GACV,gEAAgE,CAAC;QACnE,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-comms",
3
- "version": "2.18.0",
3
+ "version": "2.18.2",
4
4
  "description": "Cross-harness communication mesh for LLM agents — rooms, DMs, presence, and real-time push delivery over TCP",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.33.0",