relay-claude-channel 0.3.16 → 0.3.17

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.
@@ -8,7 +8,7 @@
8
8
  "plugins": [
9
9
  {
10
10
  "name": "relay",
11
- "version": "0.3.16",
11
+ "version": "0.3.17",
12
12
  "source": "./plugin",
13
13
  "description": "Send and receive Relay messages from Claude Code. Nothing is lost if a reply is sent twice.",
14
14
  "category": "productivity",
@@ -3,7 +3,7 @@
3
3
  "name": "relay",
4
4
  "displayName": "Relay Messenger",
5
5
  "description": "Relay Messenger channel for Claude Code with durable acknowledged delivery, explicit Read receipts, and retry-safe replies.",
6
- "version": "0.3.16",
6
+ "version": "0.3.17",
7
7
  "author": {
8
8
  "name": "Relay Messenger",
9
9
  "url": "https://relayapp.im"
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "relaySdk": {
9
9
  "package": "@relaymessenger/sdk",
10
- "version": "0.5.4",
11
- "integrity": "sha512-MwQKqzkvKMfK0uHRkR7TkGErE2RMAMcqWts4bGodyYwTSpXV3NRw13H9CgYpPjqt25uyCYNA29Tz2PCxreZ/ig==",
10
+ "version": "0.5.5",
11
+ "integrity": "sha512-LnCzswBvfcruckCJ1z8Hr1agjM0EXmPsZ6olD+9g+I6WVwoY4TNCeXMdDeJvqAdEYo1AXp4vaoi9tO2EH+DSIg==",
12
12
  "workspaceOpenapiSha256": "79bd85b0150ef45ea4bbe5f498507dd86d784e7fbf6c3b299a5a81db091aacdd"
13
13
  },
14
14
  "claudeCode": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relay-claude-channel",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Relay Messenger channel for Claude Code over Relay v1 WebSocket delivery.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@modelcontextprotocol/sdk": "1.30.0",
43
- "@relaymessenger/sdk": "0.5.4"
43
+ "@relaymessenger/sdk": "0.5.5"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "26.6.2",
@@ -3,7 +3,7 @@
3
3
  "name": "relay",
4
4
  "displayName": "Relay Messenger",
5
5
  "description": "Relay Messenger channel for Claude Code with durable acknowledged delivery, explicit Read receipts, and retry-safe replies.",
6
- "version": "0.3.16",
6
+ "version": "0.3.17",
7
7
  "author": {
8
8
  "name": "Relay Messenger",
9
9
  "url": "https://relayapp.im"
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "relaySdk": {
9
9
  "package": "@relaymessenger/sdk",
10
- "version": "0.5.4",
11
- "integrity": "sha512-MwQKqzkvKMfK0uHRkR7TkGErE2RMAMcqWts4bGodyYwTSpXV3NRw13H9CgYpPjqt25uyCYNA29Tz2PCxreZ/ig==",
10
+ "version": "0.5.5",
11
+ "integrity": "sha512-LnCzswBvfcruckCJ1z8Hr1agjM0EXmPsZ6olD+9g+I6WVwoY4TNCeXMdDeJvqAdEYo1AXp4vaoi9tO2EH+DSIg==",
12
12
  "workspaceOpenapiSha256": "79bd85b0150ef45ea4bbe5f498507dd86d784e7fbf6c3b299a5a81db091aacdd"
13
13
  },
14
14
  "claudeCode": {
@@ -21084,6 +21084,8 @@ var WebSocketStoppedError = class extends Error {
21084
21084
  this.closeCode = closeCode;
21085
21085
  }
21086
21086
  };
21087
+ var UnknownStatusWebSocketError = class extends Error {
21088
+ };
21087
21089
  var DurableApplicationError = class extends Error {
21088
21090
  sequence;
21089
21091
  closeCode = CLIENT_CLOSE_DURABLE_ACCEPTANCE;
@@ -21094,14 +21096,25 @@ var DurableApplicationError = class extends Error {
21094
21096
  };
21095
21097
  var RetryableWebSocketError = class extends Error {
21096
21098
  closeCode;
21097
- constructor(message, closeCode = CLIENT_CLOSE_RECONNECT) {
21099
+ retryAfterMs;
21100
+ constructor(message, closeCode = CLIENT_CLOSE_RECONNECT, retryAfterMs) {
21098
21101
  super(message);
21099
21102
  this.closeCode = closeCode;
21103
+ this.retryAfterMs = retryAfterMs;
21100
21104
  }
21101
21105
  };
21102
- var upgradeResponseBody = (status, statusMessage, textBody) => {
21106
+ var AGENT_TOKEN_REFUSED_CODE = 2004;
21107
+ var agentTokenRefused = (detail, options2 = {}) => new RelayAPIError(`Relay refused this Agent Token (${detail}): it is invalid or was revoked, so Relay will not reconnect with it. Create a new Agent Token in Relay Console, or run npx relaymessenger connect , then start again.`, {
21108
+ status: 401,
21109
+ code: options2.code ?? AGENT_TOKEN_REFUSED_CODE,
21110
+ ...options2.traceId === void 0 ? {} : { traceId: options2.traceId },
21111
+ ...options2.body === void 0 ? {} : { body: options2.body }
21112
+ });
21113
+ var upgradeResponseBody = (status, statusMessage, textBody, retryAfterHeader) => {
21103
21114
  let body = textBody;
21104
21115
  let message;
21116
+ let code;
21117
+ let retryAfter = Number(retryAfterHeader ?? NaN);
21105
21118
  let traceId;
21106
21119
  try {
21107
21120
  const parsed = textBody ? JSON.parse(textBody) : void 0;
@@ -21112,9 +21125,22 @@ var upgradeResponseBody = (status, statusMessage, textBody) => {
21112
21125
  if (isRecord2(parsed.error) && typeof parsed.error.message === "string") {
21113
21126
  message = parsed.error.message;
21114
21127
  }
21128
+ if (isRecord2(parsed.error) && typeof parsed.error.code === "number") {
21129
+ code = parsed.error.code;
21130
+ }
21131
+ if (isRecord2(parsed.error) && typeof parsed.error.retry_after === "number") {
21132
+ retryAfter = parsed.error.retry_after;
21133
+ }
21115
21134
  }
21116
21135
  } catch {
21117
21136
  }
21137
+ if (status === 401) {
21138
+ return agentTokenRefused(`HTTP 401${message ? `: ${message}` : ""}`, {
21139
+ ...code === void 0 ? {} : { code },
21140
+ ...traceId === void 0 ? {} : { traceId },
21141
+ body
21142
+ });
21143
+ }
21118
21144
  if (status === 409) {
21119
21145
  return new RelayWebhookConfiguredError(message ?? "This Agent delivers by webhook; delete its webhook subscription to use the WebSocket.", {
21120
21146
  ...traceId === void 0 ? {} : { traceId },
@@ -21123,7 +21149,10 @@ var upgradeResponseBody = (status, statusMessage, textBody) => {
21123
21149
  }
21124
21150
  const fallback = status > 0 ? `Relay WebSocket upgrade failed with HTTP ${status}${statusMessage ? ` ${statusMessage}` : ""}.` : "Relay WebSocket upgrade failed before receiving an HTTP status.";
21125
21151
  const errorMessage = message ?? fallback;
21126
- return status === 429 || status >= 500 || status === 0 ? new RetryableWebSocketError(errorMessage) : new WebSocketStoppedError(errorMessage);
21152
+ if (status === 429) {
21153
+ return new RetryableWebSocketError(errorMessage, CLIENT_CLOSE_RECONNECT, Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : void 0);
21154
+ }
21155
+ return status >= 500 || status === 0 ? new RetryableWebSocketError(errorMessage) : new WebSocketStoppedError(errorMessage);
21127
21156
  };
21128
21157
  var deriveWebSocketURL = (baseURL, observe = false) => {
21129
21158
  let url;
@@ -21166,6 +21195,7 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21166
21195
  Authorization: `Bearer ${agentToken}`
21167
21196
  }
21168
21197
  });
21198
+ const statusUnknown = typeof socket.on !== "function";
21169
21199
  let chain = Promise.resolve();
21170
21200
  let settled = false;
21171
21201
  let ready = false;
@@ -21241,6 +21271,8 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21241
21271
  };
21242
21272
  const onUnexpectedResponse = (_request, response) => {
21243
21273
  const chunks = [];
21274
+ const header = response.headers?.["retry-after"];
21275
+ const retryAfterHeader = Array.isArray(header) ? header[0] : header;
21244
21276
  let bytes = 0;
21245
21277
  response.setEncoding?.("utf8");
21246
21278
  response.on("data", (chunk) => {
@@ -21251,10 +21283,10 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21251
21283
  chunks.push(value.slice(0, Math.max(0, 65536 - (bytes - value.length))));
21252
21284
  });
21253
21285
  response.on("end", () => {
21254
- finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join("")));
21286
+ finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join(""), retryAfterHeader));
21255
21287
  });
21256
21288
  response.on("error", () => {
21257
- finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join("")));
21289
+ finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join(""), retryAfterHeader));
21258
21290
  });
21259
21291
  };
21260
21292
  const onMessage = (message) => {
@@ -21297,6 +21329,9 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21297
21329
  if (parsed.reason === "heartbeat_timeout" || parsed.reason === "restart") {
21298
21330
  throw new RetryableWebSocketError(parsed.reason === "restart" ? "Relay WebSocket is restarting." : "Relay WebSocket heartbeat timed out.");
21299
21331
  }
21332
+ if (parsed.reason === "revoked") {
21333
+ throw agentTokenRefused("disconnect: revoked");
21334
+ }
21300
21335
  if (parsed.reason === "webhook_configured") {
21301
21336
  throw new RelayWebhookConfiguredError("Webhook delivery is now configured for this Agent.", { code: 4410 });
21302
21337
  }
@@ -21397,7 +21432,8 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21397
21432
  if (settled)
21398
21433
  return;
21399
21434
  stopReceiving();
21400
- socket.close(error2 instanceof WebSocketStoppedError ? error2.closeCode : error2 instanceof WebSocketProtocolError ? error2.closeCode : error2 instanceof RetryableWebSocketError ? error2.closeCode : CLIENT_CLOSE_DURABLE_ACCEPTANCE, error2 instanceof WebSocketStoppedError ? "Relay stopped this consumer" : error2 instanceof WebSocketProtocolError ? "protocol error" : error2 instanceof RetryableWebSocketError ? "Relay requested reconnect" : error2 instanceof DurableApplicationError && error2.sequence !== void 0 ? `durable application failed at sequence ${error2.sequence}` : "durable application failed");
21435
+ const refused = error2 instanceof RelayAPIError && error2.status === 401;
21436
+ socket.close(refused ? 4401 : error2 instanceof WebSocketStoppedError ? error2.closeCode : error2 instanceof WebSocketProtocolError ? error2.closeCode : error2 instanceof RetryableWebSocketError ? error2.closeCode : CLIENT_CLOSE_DURABLE_ACCEPTANCE, refused || error2 instanceof WebSocketStoppedError ? "Relay stopped this consumer" : error2 instanceof WebSocketProtocolError ? "protocol error" : error2 instanceof RetryableWebSocketError ? "Relay requested reconnect" : error2 instanceof DurableApplicationError && error2.sequence !== void 0 ? `durable application failed at sequence ${error2.sequence}` : "durable application failed");
21401
21437
  finish(error2);
21402
21438
  });
21403
21439
  };
@@ -21418,12 +21454,16 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21418
21454
  }));
21419
21455
  return;
21420
21456
  }
21457
+ if (event.code === 4401) {
21458
+ finish(agentTokenRefused(`close 4401${event.reason ? `: ${event.reason}` : ""}`));
21459
+ return;
21460
+ }
21421
21461
  if (event.code !== void 0 && event.code >= 4400 && event.code <= 4499 && event.code !== 4408) {
21422
21462
  finish(new WebSocketStoppedError(`Relay WebSocket closed permanently (${event.code}): ${event.reason ?? "server policy changed"}.`, event.code));
21423
21463
  return;
21424
21464
  }
21425
21465
  if (!ready) {
21426
- finish(new Error(`Relay WebSocket closed before ready (${event.code ?? 1006}): ${event.reason ?? "connection ended"}.`));
21466
+ finish(new (statusUnknown ? UnknownStatusWebSocketError : Error)(`Relay WebSocket closed before ready (${event.code ?? 1006}): ${event.reason ?? "connection ended"}.`));
21427
21467
  return;
21428
21468
  }
21429
21469
  finish();
@@ -21435,7 +21475,7 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21435
21475
  void drained().then(() => finish());
21436
21476
  return;
21437
21477
  }
21438
- const error2 = new Error("Relay WebSocket connection failed.");
21478
+ const error2 = new (statusUnknown && !ready ? UnknownStatusWebSocketError : Error)("Relay WebSocket connection failed.");
21439
21479
  void drained().then(() => finish(error2));
21440
21480
  };
21441
21481
  const onAbort = () => {
@@ -21455,6 +21495,26 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21455
21495
  else
21456
21496
  options2.signal?.addEventListener("abort", onAbort, { once: true });
21457
21497
  });
21498
+ var refusedToken = async (baseURL, agentToken, signal) => {
21499
+ try {
21500
+ const url = new URL(baseURL);
21501
+ url.pathname = "/v1/me";
21502
+ url.search = "";
21503
+ url.hash = "";
21504
+ const response = await fetch(url, {
21505
+ headers: { Authorization: `Bearer ${agentToken}` },
21506
+ ...signal ? { signal } : {}
21507
+ });
21508
+ if (response.status !== 401) {
21509
+ await response.body?.cancel();
21510
+ return void 0;
21511
+ }
21512
+ const answer = upgradeResponseBody(401, void 0, await response.text());
21513
+ return answer instanceof RelayAPIError ? answer : void 0;
21514
+ } catch {
21515
+ return void 0;
21516
+ }
21517
+ };
21458
21518
  var runWebSocket = async (baseURL, agentToken, options2) => {
21459
21519
  if (options2.observe !== void 0 && typeof options2.observe !== "boolean") {
21460
21520
  throw new TypeError("WebSocket observe must be a boolean when provided.");
@@ -21483,6 +21543,7 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21483
21543
  report(new RelayUnknownEventTypeError(eventType, sequence));
21484
21544
  };
21485
21545
  while (!options2.signal?.aborted) {
21546
+ let retryAfterMs = 0;
21486
21547
  options2.onConnectionState?.("connecting");
21487
21548
  try {
21488
21549
  await runConnection(url, agentToken, options2, Constructor, (frame) => {
@@ -21494,13 +21555,19 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21494
21555
  failing = false;
21495
21556
  attempt = 0;
21496
21557
  });
21497
- } catch (error2) {
21558
+ } catch (caught) {
21559
+ if (options2.signal?.aborted)
21560
+ return;
21561
+ const error2 = caught instanceof UnknownStatusWebSocketError ? await refusedToken(baseURL, agentToken, options2.signal) ?? caught : caught;
21498
21562
  if (options2.signal?.aborted)
21499
21563
  return;
21500
21564
  report(error2);
21501
21565
  if (error2 instanceof DurableApplicationError)
21502
21566
  failing = true;
21503
- if (error2 instanceof WebSocketStoppedError || error2 instanceof WebSocketProtocolError || error2 instanceof RelayWebhookConfiguredError)
21567
+ if (error2 instanceof RetryableWebSocketError && error2.retryAfterMs !== void 0) {
21568
+ retryAfterMs = error2.retryAfterMs;
21569
+ }
21570
+ if (error2 instanceof WebSocketStoppedError || error2 instanceof WebSocketProtocolError || error2 instanceof RelayAPIError && !error2.retryable)
21504
21571
  throw error2;
21505
21572
  attempt += 1;
21506
21573
  } finally {
@@ -21509,7 +21576,7 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21509
21576
  if (options2.signal?.aborted)
21510
21577
  return;
21511
21578
  const ceiling = Math.min(maximum, minimum * 2 ** Math.max(0, attempt - 1));
21512
- await wait(Math.floor(random() * ceiling), options2.signal);
21579
+ await wait(Math.max(retryAfterMs, Math.floor(random() * ceiling)), options2.signal);
21513
21580
  }
21514
21581
  };
21515
21582
 
@@ -24844,7 +24911,7 @@ var RelayStateStore = class {
24844
24911
  };
24845
24912
 
24846
24913
  // server.ts
24847
- var VERSION = true ? "0.3.16" : createRequire(import.meta.url)("./package.json").version;
24914
+ var VERSION = true ? "0.3.17" : createRequire(import.meta.url)("./package.json").version;
24848
24915
  if (process.argv.includes("--version")) {
24849
24916
  process.stdout.write(`${VERSION}
24850
24917
  `);
@@ -21084,6 +21084,8 @@ var WebSocketStoppedError = class extends Error {
21084
21084
  this.closeCode = closeCode;
21085
21085
  }
21086
21086
  };
21087
+ var UnknownStatusWebSocketError = class extends Error {
21088
+ };
21087
21089
  var DurableApplicationError = class extends Error {
21088
21090
  sequence;
21089
21091
  closeCode = CLIENT_CLOSE_DURABLE_ACCEPTANCE;
@@ -21094,14 +21096,25 @@ var DurableApplicationError = class extends Error {
21094
21096
  };
21095
21097
  var RetryableWebSocketError = class extends Error {
21096
21098
  closeCode;
21097
- constructor(message, closeCode = CLIENT_CLOSE_RECONNECT) {
21099
+ retryAfterMs;
21100
+ constructor(message, closeCode = CLIENT_CLOSE_RECONNECT, retryAfterMs) {
21098
21101
  super(message);
21099
21102
  this.closeCode = closeCode;
21103
+ this.retryAfterMs = retryAfterMs;
21100
21104
  }
21101
21105
  };
21102
- var upgradeResponseBody = (status, statusMessage, textBody) => {
21106
+ var AGENT_TOKEN_REFUSED_CODE = 2004;
21107
+ var agentTokenRefused = (detail, options2 = {}) => new RelayAPIError(`Relay refused this Agent Token (${detail}): it is invalid or was revoked, so Relay will not reconnect with it. Create a new Agent Token in Relay Console, or run npx relaymessenger connect , then start again.`, {
21108
+ status: 401,
21109
+ code: options2.code ?? AGENT_TOKEN_REFUSED_CODE,
21110
+ ...options2.traceId === void 0 ? {} : { traceId: options2.traceId },
21111
+ ...options2.body === void 0 ? {} : { body: options2.body }
21112
+ });
21113
+ var upgradeResponseBody = (status, statusMessage, textBody, retryAfterHeader) => {
21103
21114
  let body = textBody;
21104
21115
  let message;
21116
+ let code;
21117
+ let retryAfter = Number(retryAfterHeader ?? NaN);
21105
21118
  let traceId;
21106
21119
  try {
21107
21120
  const parsed = textBody ? JSON.parse(textBody) : void 0;
@@ -21112,9 +21125,22 @@ var upgradeResponseBody = (status, statusMessage, textBody) => {
21112
21125
  if (isRecord2(parsed.error) && typeof parsed.error.message === "string") {
21113
21126
  message = parsed.error.message;
21114
21127
  }
21128
+ if (isRecord2(parsed.error) && typeof parsed.error.code === "number") {
21129
+ code = parsed.error.code;
21130
+ }
21131
+ if (isRecord2(parsed.error) && typeof parsed.error.retry_after === "number") {
21132
+ retryAfter = parsed.error.retry_after;
21133
+ }
21115
21134
  }
21116
21135
  } catch {
21117
21136
  }
21137
+ if (status === 401) {
21138
+ return agentTokenRefused(`HTTP 401${message ? `: ${message}` : ""}`, {
21139
+ ...code === void 0 ? {} : { code },
21140
+ ...traceId === void 0 ? {} : { traceId },
21141
+ body
21142
+ });
21143
+ }
21118
21144
  if (status === 409) {
21119
21145
  return new RelayWebhookConfiguredError(message ?? "This Agent delivers by webhook; delete its webhook subscription to use the WebSocket.", {
21120
21146
  ...traceId === void 0 ? {} : { traceId },
@@ -21123,7 +21149,10 @@ var upgradeResponseBody = (status, statusMessage, textBody) => {
21123
21149
  }
21124
21150
  const fallback = status > 0 ? `Relay WebSocket upgrade failed with HTTP ${status}${statusMessage ? ` ${statusMessage}` : ""}.` : "Relay WebSocket upgrade failed before receiving an HTTP status.";
21125
21151
  const errorMessage = message ?? fallback;
21126
- return status === 429 || status >= 500 || status === 0 ? new RetryableWebSocketError(errorMessage) : new WebSocketStoppedError(errorMessage);
21152
+ if (status === 429) {
21153
+ return new RetryableWebSocketError(errorMessage, CLIENT_CLOSE_RECONNECT, Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : void 0);
21154
+ }
21155
+ return status >= 500 || status === 0 ? new RetryableWebSocketError(errorMessage) : new WebSocketStoppedError(errorMessage);
21127
21156
  };
21128
21157
  var deriveWebSocketURL = (baseURL, observe = false) => {
21129
21158
  let url;
@@ -21166,6 +21195,7 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21166
21195
  Authorization: `Bearer ${agentToken}`
21167
21196
  }
21168
21197
  });
21198
+ const statusUnknown = typeof socket.on !== "function";
21169
21199
  let chain = Promise.resolve();
21170
21200
  let settled = false;
21171
21201
  let ready = false;
@@ -21241,6 +21271,8 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21241
21271
  };
21242
21272
  const onUnexpectedResponse = (_request, response) => {
21243
21273
  const chunks = [];
21274
+ const header = response.headers?.["retry-after"];
21275
+ const retryAfterHeader = Array.isArray(header) ? header[0] : header;
21244
21276
  let bytes = 0;
21245
21277
  response.setEncoding?.("utf8");
21246
21278
  response.on("data", (chunk) => {
@@ -21251,10 +21283,10 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21251
21283
  chunks.push(value.slice(0, Math.max(0, 65536 - (bytes - value.length))));
21252
21284
  });
21253
21285
  response.on("end", () => {
21254
- finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join("")));
21286
+ finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join(""), retryAfterHeader));
21255
21287
  });
21256
21288
  response.on("error", () => {
21257
- finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join("")));
21289
+ finish(upgradeResponseBody(response.statusCode ?? 0, response.statusMessage, chunks.join(""), retryAfterHeader));
21258
21290
  });
21259
21291
  };
21260
21292
  const onMessage = (message) => {
@@ -21297,6 +21329,9 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21297
21329
  if (parsed.reason === "heartbeat_timeout" || parsed.reason === "restart") {
21298
21330
  throw new RetryableWebSocketError(parsed.reason === "restart" ? "Relay WebSocket is restarting." : "Relay WebSocket heartbeat timed out.");
21299
21331
  }
21332
+ if (parsed.reason === "revoked") {
21333
+ throw agentTokenRefused("disconnect: revoked");
21334
+ }
21300
21335
  if (parsed.reason === "webhook_configured") {
21301
21336
  throw new RelayWebhookConfiguredError("Webhook delivery is now configured for this Agent.", { code: 4410 });
21302
21337
  }
@@ -21397,7 +21432,8 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21397
21432
  if (settled)
21398
21433
  return;
21399
21434
  stopReceiving();
21400
- socket.close(error2 instanceof WebSocketStoppedError ? error2.closeCode : error2 instanceof WebSocketProtocolError ? error2.closeCode : error2 instanceof RetryableWebSocketError ? error2.closeCode : CLIENT_CLOSE_DURABLE_ACCEPTANCE, error2 instanceof WebSocketStoppedError ? "Relay stopped this consumer" : error2 instanceof WebSocketProtocolError ? "protocol error" : error2 instanceof RetryableWebSocketError ? "Relay requested reconnect" : error2 instanceof DurableApplicationError && error2.sequence !== void 0 ? `durable application failed at sequence ${error2.sequence}` : "durable application failed");
21435
+ const refused = error2 instanceof RelayAPIError && error2.status === 401;
21436
+ socket.close(refused ? 4401 : error2 instanceof WebSocketStoppedError ? error2.closeCode : error2 instanceof WebSocketProtocolError ? error2.closeCode : error2 instanceof RetryableWebSocketError ? error2.closeCode : CLIENT_CLOSE_DURABLE_ACCEPTANCE, refused || error2 instanceof WebSocketStoppedError ? "Relay stopped this consumer" : error2 instanceof WebSocketProtocolError ? "protocol error" : error2 instanceof RetryableWebSocketError ? "Relay requested reconnect" : error2 instanceof DurableApplicationError && error2.sequence !== void 0 ? `durable application failed at sequence ${error2.sequence}` : "durable application failed");
21401
21437
  finish(error2);
21402
21438
  });
21403
21439
  };
@@ -21418,12 +21454,16 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21418
21454
  }));
21419
21455
  return;
21420
21456
  }
21457
+ if (event.code === 4401) {
21458
+ finish(agentTokenRefused(`close 4401${event.reason ? `: ${event.reason}` : ""}`));
21459
+ return;
21460
+ }
21421
21461
  if (event.code !== void 0 && event.code >= 4400 && event.code <= 4499 && event.code !== 4408) {
21422
21462
  finish(new WebSocketStoppedError(`Relay WebSocket closed permanently (${event.code}): ${event.reason ?? "server policy changed"}.`, event.code));
21423
21463
  return;
21424
21464
  }
21425
21465
  if (!ready) {
21426
- finish(new Error(`Relay WebSocket closed before ready (${event.code ?? 1006}): ${event.reason ?? "connection ended"}.`));
21466
+ finish(new (statusUnknown ? UnknownStatusWebSocketError : Error)(`Relay WebSocket closed before ready (${event.code ?? 1006}): ${event.reason ?? "connection ended"}.`));
21427
21467
  return;
21428
21468
  }
21429
21469
  finish();
@@ -21435,7 +21475,7 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21435
21475
  void drained().then(() => finish());
21436
21476
  return;
21437
21477
  }
21438
- const error2 = new Error("Relay WebSocket connection failed.");
21478
+ const error2 = new (statusUnknown && !ready ? UnknownStatusWebSocketError : Error)("Relay WebSocket connection failed.");
21439
21479
  void drained().then(() => finish(error2));
21440
21480
  };
21441
21481
  const onAbort = () => {
@@ -21455,6 +21495,26 @@ var runConnection = (url, agentToken, options2, Constructor, onReady, onUnknownE
21455
21495
  else
21456
21496
  options2.signal?.addEventListener("abort", onAbort, { once: true });
21457
21497
  });
21498
+ var refusedToken = async (baseURL, agentToken, signal) => {
21499
+ try {
21500
+ const url = new URL(baseURL);
21501
+ url.pathname = "/v1/me";
21502
+ url.search = "";
21503
+ url.hash = "";
21504
+ const response = await fetch(url, {
21505
+ headers: { Authorization: `Bearer ${agentToken}` },
21506
+ ...signal ? { signal } : {}
21507
+ });
21508
+ if (response.status !== 401) {
21509
+ await response.body?.cancel();
21510
+ return void 0;
21511
+ }
21512
+ const answer = upgradeResponseBody(401, void 0, await response.text());
21513
+ return answer instanceof RelayAPIError ? answer : void 0;
21514
+ } catch {
21515
+ return void 0;
21516
+ }
21517
+ };
21458
21518
  var runWebSocket = async (baseURL, agentToken, options2) => {
21459
21519
  if (options2.observe !== void 0 && typeof options2.observe !== "boolean") {
21460
21520
  throw new TypeError("WebSocket observe must be a boolean when provided.");
@@ -21483,6 +21543,7 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21483
21543
  report(new RelayUnknownEventTypeError(eventType, sequence));
21484
21544
  };
21485
21545
  while (!options2.signal?.aborted) {
21546
+ let retryAfterMs = 0;
21486
21547
  options2.onConnectionState?.("connecting");
21487
21548
  try {
21488
21549
  await runConnection(url, agentToken, options2, Constructor, (frame) => {
@@ -21494,13 +21555,19 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21494
21555
  failing = false;
21495
21556
  attempt = 0;
21496
21557
  });
21497
- } catch (error2) {
21558
+ } catch (caught) {
21559
+ if (options2.signal?.aborted)
21560
+ return;
21561
+ const error2 = caught instanceof UnknownStatusWebSocketError ? await refusedToken(baseURL, agentToken, options2.signal) ?? caught : caught;
21498
21562
  if (options2.signal?.aborted)
21499
21563
  return;
21500
21564
  report(error2);
21501
21565
  if (error2 instanceof DurableApplicationError)
21502
21566
  failing = true;
21503
- if (error2 instanceof WebSocketStoppedError || error2 instanceof WebSocketProtocolError || error2 instanceof RelayWebhookConfiguredError)
21567
+ if (error2 instanceof RetryableWebSocketError && error2.retryAfterMs !== void 0) {
21568
+ retryAfterMs = error2.retryAfterMs;
21569
+ }
21570
+ if (error2 instanceof WebSocketStoppedError || error2 instanceof WebSocketProtocolError || error2 instanceof RelayAPIError && !error2.retryable)
21504
21571
  throw error2;
21505
21572
  attempt += 1;
21506
21573
  } finally {
@@ -21509,7 +21576,7 @@ var runWebSocket = async (baseURL, agentToken, options2) => {
21509
21576
  if (options2.signal?.aborted)
21510
21577
  return;
21511
21578
  const ceiling = Math.min(maximum, minimum * 2 ** Math.max(0, attempt - 1));
21512
- await wait(Math.floor(random() * ceiling), options2.signal);
21579
+ await wait(Math.max(retryAfterMs, Math.floor(random() * ceiling)), options2.signal);
21513
21580
  }
21514
21581
  };
21515
21582
 
@@ -24844,7 +24911,7 @@ var RelayStateStore = class {
24844
24911
  };
24845
24912
 
24846
24913
  // server.ts
24847
- var VERSION = true ? "0.3.16" : createRequire(import.meta.url)("./package.json").version;
24914
+ var VERSION = true ? "0.3.17" : createRequire(import.meta.url)("./package.json").version;
24848
24915
  if (process.argv.includes("--version")) {
24849
24916
  process.stdout.write(`${VERSION}
24850
24917
  `);