commonswarm 0.1.72 → 0.1.73

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.
Files changed (2) hide show
  1. package/cswarm.cjs +1238 -273
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -1149,41 +1149,77 @@ async function requestSuccessor(options) {
1149
1149
  let body = {};
1150
1150
  try {
1151
1151
  const text = await response.text();
1152
- if (text) body = JSON.parse(text);
1152
+ if (text) {
1153
+ const parsed = JSON.parse(text);
1154
+ if (options.listenerMode && (!parsed || typeof parsed !== "object" || Array.isArray(parsed))) {
1155
+ throw new RenewalMalformedResponseError("renewal response was not an object");
1156
+ }
1157
+ body = parsed;
1158
+ }
1153
1159
  } catch {
1154
- body = {};
1160
+ if (!options.listenerMode) {
1161
+ body = {};
1162
+ } else {
1163
+ throw new RenewalMalformedResponseError("renewal response was not valid JSON");
1164
+ }
1155
1165
  }
1156
- const principalId = typeof body.principal_id === "string" && UUID_RE5.test(body.principal_id) ? body.principal_id.toLowerCase() : null;
1157
- if (response.status === 400 || response.status === 404) {
1158
- throw new RenewalUnsupported(
1159
- "this deployment does not offer credential renewal yet, so a credential here still has to be re-issued by hand when it expires"
1160
- );
1166
+ if (options.listenerMode && response.status !== 401 && response.status !== 403 && body.principal_id !== void 0 && body.principal_id !== null && (typeof body.principal_id !== "string" || !UUID_RE5.test(body.principal_id))) {
1167
+ throw new RenewalMalformedResponseError("renewal response carried a malformed principal_id");
1161
1168
  }
1169
+ const principalId = typeof body.principal_id === "string" && UUID_RE5.test(body.principal_id) ? body.principal_id.toLowerCase() : null;
1162
1170
  if (response.status === 401 || response.status === 403) {
1171
+ if (options.listenerMode) {
1172
+ if (response.status === 401 && body.error === "unauthenticated") {
1173
+ throw new RenewalCredentialCheckError(response.status, "unauthenticated");
1174
+ }
1175
+ throw new RenewalOutcomeUnknown(`renewal command answered HTTP ${response.status}`);
1176
+ }
1163
1177
  const named = typeof body.reason === "string" && REVOCATION_REASONS.has(body.reason) ? body.reason : null;
1164
1178
  if (named !== null) throw new RenewalRevoked(named, REVOKED_MESSAGE);
1165
- const expiresAt2 = options.expiresAt ?? null;
1166
- if (expiresAt2 !== null && now() >= expiresAt2) {
1179
+ if (options.expiresAt !== null && options.expiresAt !== void 0 && now() >= options.expiresAt) {
1167
1180
  throw new RenewalRevoked("predecessor_expired_local", LOCALLY_EXPIRED_MESSAGE);
1168
1181
  }
1169
1182
  throw new RenewalRevoked("forbidden", UNEXPLAINED_REFUSAL_MESSAGE);
1170
1183
  }
1171
1184
  if (response.status === 426) {
1172
- const minimum = typeof body.min_client_version === "string" ? body.min_client_version : null;
1173
- throw new RenewalRefused(
1174
- response.status,
1175
- "upgrade_required",
1176
- `This copy of cswarm is older than the deployment accepts${minimum === null ? "" : ` (minimum ${minimum})`}. Update cswarm; until then this credential cannot renew itself.`
1185
+ if (!options.listenerMode) {
1186
+ const minimum = typeof body.min_client_version === "string" ? body.min_client_version : null;
1187
+ throw new RenewalRefused(
1188
+ 426,
1189
+ "upgrade_required",
1190
+ `This copy of cswarm is older than the deployment accepts${minimum === null ? "" : ` (minimum ${minimum})`}. Update cswarm; until then this credential cannot renew itself.`
1191
+ );
1192
+ }
1193
+ if (body.error !== "upgrade_required" || typeof body.min_client_version !== "string") {
1194
+ throw new RenewalOutcomeUnknown("renewal command answered an unrecognized HTTP 426");
1195
+ }
1196
+ throw new RenewalUpgradeRequiredError(body.min_client_version, options.listenerMode);
1197
+ }
1198
+ if (!options.listenerMode && (response.status === 400 || response.status === 404)) {
1199
+ throw new RenewalUnsupported(
1200
+ "this deployment does not offer credential renewal yet, so a credential here still has to be re-issued by hand when it expires"
1177
1201
  );
1178
1202
  }
1179
1203
  if (!response.ok) {
1180
- throw new RenewalRefused(
1181
- response.status,
1182
- typeof body.error === "string" ? body.error : "unknown",
1183
- `The deployment did not renew this credential (HTTP ${response.status}).`
1184
- );
1204
+ if (!options.listenerMode) {
1205
+ throw new RenewalRefused(
1206
+ response.status,
1207
+ typeof body.error === "string" ? body.error : "unknown",
1208
+ `The deployment did not renew this credential (HTTP ${response.status}).`
1209
+ );
1210
+ }
1211
+ throw new RenewalOutcomeUnknown(`renewal command answered HTTP ${response.status}`);
1212
+ }
1213
+ if (options.listenerMode && body.status !== "accepted" && body.status !== "rejected") {
1214
+ throw new RenewalMalformedResponseError("renewal response did not carry a command status");
1215
+ }
1216
+ if (options.listenerMode && body.status === "accepted" && body.ok !== true) {
1217
+ throw new RenewalMalformedResponseError("renewal response did not confirm acceptance");
1185
1218
  }
1186
1219
  if (body.status === "rejected") {
1220
+ if (options.listenerMode && typeof body.reason !== "string") {
1221
+ throw new RenewalMalformedResponseError("renewal rejection did not name a reason");
1222
+ }
1187
1223
  const reason = typeof body.reason === "string" ? body.reason : "unknown";
1188
1224
  if (reason === "renewal_idle_suspended" || reason === "renewal_grant_suspended") {
1189
1225
  throw new RenewalSuspended(
@@ -1231,57 +1267,91 @@ async function requestSuccessor(options) {
1231
1267
  reason === "renewal_device_unavailable" ? "The standing grant is device-bound, but this renewal carried no device identity. Ask a workspace owner to revoke this grant and mint a new credential on the intended device." : "The standing grant is bound to another device, so CommonSwarm refused renewal. Ask a workspace owner to revoke this grant and mint a new credential on the intended device."
1232
1268
  );
1233
1269
  }
1234
- throw new RenewalRefused(
1235
- 200,
1236
- reason,
1237
- `The deployment refused to renew this credential (${reason}).`
1238
- );
1270
+ if (!options.listenerMode) {
1271
+ throw new RenewalRefused(
1272
+ 200,
1273
+ reason,
1274
+ `The deployment refused to renew this credential (${reason}).`
1275
+ );
1276
+ }
1277
+ throw new RenewalMalformedResponseError("renewal rejection named an unknown reason");
1239
1278
  }
1240
- const token = typeof body.agent_token === "string" ? body.agent_token : "";
1241
- if (!token) {
1242
- return null;
1279
+ if (options.listenerMode && body.agent_token !== void 0 && typeof body.agent_token !== "string") {
1280
+ throw new RenewalMalformedResponseError("renewal response carried a malformed agent_token");
1243
1281
  }
1244
- if (!AGENT_TOKEN_RE3.test(token)) {
1245
- throw new RenewalRefused(
1282
+ const token = typeof body.agent_token === "string" ? body.agent_token : "";
1283
+ if (!options.listenerMode && !token) return null;
1284
+ if (token && !AGENT_TOKEN_RE3.test(token)) {
1285
+ if (!options.listenerMode) throw new RenewalRefused(
1246
1286
  response.status,
1247
1287
  "malformed_successor",
1248
1288
  "The deployment returned a credential that is not shaped like one. It was not stored."
1249
1289
  );
1290
+ throw new RenewalMalformedResponseError(
1291
+ "The deployment returned a credential that is not shaped like one. It was not stored."
1292
+ );
1250
1293
  }
1251
1294
  const tokenId = typeof body.token_id === "string" ? body.token_id : "";
1252
1295
  const runId = typeof body.run_id === "string" ? body.run_id : "";
1253
1296
  if (!UUID_RE5.test(tokenId) || !UUID_RE5.test(runId) || principalId === null) {
1254
- throw new RenewalRefused(
1297
+ if (!options.listenerMode) throw new RenewalRefused(
1255
1298
  response.status,
1256
1299
  "incomplete_successor",
1257
1300
  "A successor credential was issued but the deployment did not name its principal, run, or token. It was not stored; ask an owner to revoke it."
1258
1301
  );
1302
+ throw new RenewalMalformedResponseError(
1303
+ "A successor credential was issued but the deployment did not name its principal, run, or token. It was not stored; ask an owner to revoke it."
1304
+ );
1305
+ }
1306
+ const parsedIssuedAt = timestamp(body.issued_at);
1307
+ if (options.listenerMode && body.issued_at !== void 0 && parsedIssuedAt === null) {
1308
+ throw new RenewalMalformedResponseError("renewal response carried a malformed issued_at");
1259
1309
  }
1260
- const issuedAt = timestamp(body.issued_at) ?? now();
1310
+ const issuedAt = parsedIssuedAt ?? now();
1261
1311
  const expiresAt = timestamp(body.expires_at);
1262
1312
  if (expiresAt === null) {
1263
- throw new RenewalRefused(
1313
+ if (!options.listenerMode) throw new RenewalRefused(
1264
1314
  response.status,
1265
1315
  "successor_expiry_missing",
1266
1316
  "A successor credential was issued without an expiry. It was not stored, because a credential whose lifetime is unknown cannot be renewed on time."
1267
1317
  );
1318
+ throw new RenewalMalformedResponseError(
1319
+ "A successor credential was issued without an expiry. It was not stored, because a credential whose lifetime is unknown cannot be renewed on time."
1320
+ );
1268
1321
  }
1269
1322
  if (expiresAt - issuedAt > AGENT_TOKEN_MAX_TTL_MS) {
1270
- throw new RenewalRefused(
1323
+ if (!options.listenerMode) throw new RenewalRefused(
1271
1324
  response.status,
1272
1325
  "successor_ttl_too_long",
1273
1326
  "The deployment issued a successor credential that lasts longer than eight hours. cswarm refused to store it. Agent credentials stay short on purpose; renewal is what makes that survivable."
1274
1327
  );
1328
+ throw new RenewalMalformedResponseError(
1329
+ "The deployment issued a successor credential that lasts longer than eight hours. cswarm refused to store it. Agent credentials stay short on purpose; renewal is what makes that survivable."
1330
+ );
1331
+ }
1332
+ const horizonExpiresAt = timestamp(body.horizon_expires_at);
1333
+ if (options.listenerMode && body.horizon_expires_at !== void 0 && body.horizon_expires_at !== null && horizonExpiresAt === null) {
1334
+ throw new RenewalMalformedResponseError("renewal response carried a malformed horizon_expires_at");
1335
+ }
1336
+ const successorsRemaining = count(body.successors_remaining);
1337
+ if (options.listenerMode && body.successors_remaining !== void 0 && body.successors_remaining !== null && successorsRemaining === null) {
1338
+ throw new RenewalMalformedResponseError("renewal response carried a malformed successors_remaining");
1275
1339
  }
1276
1340
  let wake;
1277
1341
  try {
1278
1342
  wake = parseOptionalWakeHint(body.wake);
1279
1343
  } catch {
1280
- throw new RenewalRefused(
1344
+ if (!options.listenerMode) throw new RenewalRefused(
1281
1345
  response.status,
1282
1346
  "malformed_wake",
1283
1347
  "The deployment returned a successor credential with a malformed wake hint. It was not stored."
1284
1348
  );
1349
+ throw new RenewalMalformedResponseError(
1350
+ "The deployment returned a successor credential with a malformed wake hint. It was not stored."
1351
+ );
1352
+ }
1353
+ if (!token) {
1354
+ return null;
1285
1355
  }
1286
1356
  return {
1287
1357
  token,
@@ -1290,12 +1360,12 @@ async function requestSuccessor(options) {
1290
1360
  runId: runId.toLowerCase(),
1291
1361
  issuedAt,
1292
1362
  expiresAt,
1293
- horizonExpiresAt: timestamp(body.horizon_expires_at),
1294
- successorsRemaining: count(body.successors_remaining),
1363
+ horizonExpiresAt,
1364
+ successorsRemaining,
1295
1365
  ...wake === void 0 ? {} : { wake }
1296
1366
  };
1297
1367
  }
1298
- var import_node_crypto4, AGENT_TOKEN_DEFAULT_TTL_MS, AGENT_TOKEN_MAX_TTL_MS, RENEWAL_HORIZON_DEFAULT_MS, RENEWAL_HORIZON_MAX_MS, RENEWAL_LEAD_FRACTION, RENEWAL_LEAD_FLOOR_MS, RENEWAL_LEAD_CEILING_MS, RENEWAL_PENDING_RECOVERY_MS, RENEW_TIMEOUT_MS, UUID_RE5, AGENT_TOKEN_RE3, RenewalReauthorisationRequired, RenewalRevoked, RenewalSuspended, RenewalUnsupported, RenewalSuperseded, RenewalOutcomeUnknown, RenewalRefused, REVOCATION_REASONS_LIST, REVOCATION_REASONS, REVOKED_MESSAGE, LOCALLY_EXPIRED_MESSAGE, UNEXPLAINED_REFUSAL_MESSAGE, AgentCredentialSession;
1368
+ var import_node_crypto4, AGENT_TOKEN_DEFAULT_TTL_MS, AGENT_TOKEN_MAX_TTL_MS, RENEWAL_HORIZON_DEFAULT_MS, RENEWAL_HORIZON_MAX_MS, RENEWAL_LEAD_FRACTION, RENEWAL_LEAD_FLOOR_MS, RENEWAL_LEAD_CEILING_MS, RENEWAL_PENDING_RECOVERY_MS, RENEW_TIMEOUT_MS, UUID_RE5, AGENT_TOKEN_RE3, RenewalReauthorisationRequired, RenewalRevoked, RenewalSuspended, RenewalUnsupported, RenewalSuperseded, RenewalOutcomeUnknown, RenewalMalformedResponseError, RenewalCredentialCheckError, RenewalRetryError, RenewalRefused, RenewalUpgradeRequiredError, RENEWAL_UPGRADE_LISTENER_ACTION, RENEWAL_UPGRADE_COMMAND_ACTION, REVOCATION_REASONS_LIST, REVOCATION_REASONS, REVOKED_MESSAGE, LOCALLY_EXPIRED_MESSAGE, UNEXPLAINED_REFUSAL_MESSAGE, AgentCredentialSession;
1299
1369
  var init_renewal = __esm({
1300
1370
  "src/cloud/renewal.ts"() {
1301
1371
  "use strict";
@@ -1358,6 +1428,28 @@ var init_renewal = __esm({
1358
1428
  super(message);
1359
1429
  }
1360
1430
  };
1431
+ RenewalMalformedResponseError = class extends RenewalOutcomeUnknown {
1432
+ name = "RenewalMalformedResponseError";
1433
+ };
1434
+ RenewalCredentialCheckError = class extends Error {
1435
+ constructor(status, code) {
1436
+ super(`renewal command refused the credential (${code})`);
1437
+ this.status = status;
1438
+ this.code = code;
1439
+ }
1440
+ status;
1441
+ code;
1442
+ name = "RenewalCredentialCheckError";
1443
+ };
1444
+ RenewalRetryError = class extends Error {
1445
+ constructor(expiresAt) {
1446
+ super("credential renewal is retrying");
1447
+ this.expiresAt = expiresAt;
1448
+ }
1449
+ expiresAt;
1450
+ name = "RenewalRetryError";
1451
+ code = "renewal_retry";
1452
+ };
1361
1453
  RenewalRefused = class extends Error {
1362
1454
  constructor(status, code, message) {
1363
1455
  super(message);
@@ -1368,6 +1460,14 @@ var init_renewal = __esm({
1368
1460
  code;
1369
1461
  name = "RenewalRefused";
1370
1462
  };
1463
+ RenewalUpgradeRequiredError = class extends RenewalRefused {
1464
+ name = "RenewalUpgradeRequiredError";
1465
+ constructor(minimum, listenerMode = false) {
1466
+ super(426, "upgrade_required", `This copy of cswarm is older than the deployment accepts (minimum ${minimum}). ${listenerMode ? RENEWAL_UPGRADE_LISTENER_ACTION : RENEWAL_UPGRADE_COMMAND_ACTION}`);
1467
+ }
1468
+ };
1469
+ RENEWAL_UPGRADE_LISTENER_ACTION = "Update cswarm, then restart the listener.";
1470
+ RENEWAL_UPGRADE_COMMAND_ACTION = "Update cswarm, then run the command again.";
1371
1471
  REVOCATION_REASONS_LIST = [
1372
1472
  "renewal_lineage_revoked",
1373
1473
  "renewal_grant_revoked",
@@ -1476,6 +1576,14 @@ var init_renewal = __esm({
1476
1576
  get expiry() {
1477
1577
  return this.expiresAt;
1478
1578
  }
1579
+ get renewalDue() {
1580
+ return this.due();
1581
+ }
1582
+ /** Next renewal boundary, or null when this session cannot keep a successor. */
1583
+ get renewalAt() {
1584
+ if (this.unsupported || this.options.store === null || this.expiresAt === null) return null;
1585
+ return renewalDueAt(this.issuedAt, this.expiresAt);
1586
+ }
1479
1587
  /**
1480
1588
  * ★ RENEWAL REQUIRES SOMEWHERE TO KEEP THE SUCCESSOR, AND THAT IS A SAFETY RULE, NOT A
1481
1589
  * CONVENIENCE. A successful renewal SUPERSEDES the predecessor server-side — the fence
@@ -1485,10 +1593,8 @@ var init_renewal = __esm({
1485
1593
  * bricked by the feature meant to keep it alive. So with no store, this never renews.
1486
1594
  */
1487
1595
  due() {
1488
- if (this.unsupported) return false;
1489
- if (this.options.store === null) return false;
1490
- if (this.expiresAt === null) return false;
1491
- return this.clock() >= renewalDueAt(this.issuedAt, this.expiresAt);
1596
+ const at = this.renewalAt;
1597
+ return at !== null && this.clock() >= at;
1492
1598
  }
1493
1599
  expired() {
1494
1600
  return this.expiresAt !== null && this.clock() >= this.expiresAt;
@@ -1497,9 +1603,9 @@ var init_renewal = __esm({
1497
1603
  * The credential to present, renewed first if it is close to expiring.
1498
1604
  *
1499
1605
  * Renewal happens AHEAD of expiry rather than on a 401, so the common path never shows a
1500
- * person a failure. A refusal while the predecessor is still live is therefore a warning
1501
- * and the command proceeds; it becomes fatal only when there is nothing valid left to
1502
- * present, which is the one case where continuing would fail anyway.
1606
+ * person a failure. A known one-shot 401/403 refusal is fatal with the D-004/D-011
1607
+ * remedy; an unknown outcome may use a still-live predecessor. The listener retries
1608
+ * unknown outcomes and samples recognized authentication refusals.
1503
1609
  */
1504
1610
  async bearer() {
1505
1611
  if (!this.due()) return this.token;
@@ -1516,17 +1622,32 @@ var init_renewal = __esm({
1516
1622
  }
1517
1623
  return this.token;
1518
1624
  }
1519
- if (this.expired() || error2 instanceof RenewalRevoked) throw error2;
1625
+ if (error2 instanceof RenewalCredentialCheckError && this.expired()) {
1626
+ throw new RenewalRevoked(
1627
+ "predecessor_expired_local",
1628
+ "The current credential expired before it could be renewed. Ask whoever set this agent up for a new credential."
1629
+ );
1630
+ }
1631
+ if (error2 instanceof RenewalCredentialCheckError || error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalSuspended || error2 instanceof RenewalRevoked) throw error2;
1632
+ if (this.options.listenerMode && error2 instanceof RenewalUpgradeRequiredError) throw error2;
1633
+ const retryableRenewalOutcome = error2 instanceof RenewalOutcomeUnknown || error2 instanceof RenewalUnsupported || error2 instanceof RenewalRefused;
1634
+ if (this.options.listenerMode && retryableRenewalOutcome && this.expired()) {
1635
+ throw new RenewalRevoked(
1636
+ "predecessor_expired_local",
1637
+ "The current credential expired before it could be renewed. Ask whoever set this agent up for a new credential."
1638
+ );
1639
+ }
1640
+ if (this.options.listenerMode && retryableRenewalOutcome) {
1641
+ throw new RenewalRetryError(this.expiresAt);
1642
+ }
1643
+ if (this.options.listenerMode) throw error2;
1644
+ if (this.expired()) throw error2;
1520
1645
  if (error2 instanceof RenewalUnsupported) {
1521
1646
  this.unsupported = true;
1522
1647
  this.warn(`${error2.message}.`);
1523
- } else if (error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalSuspended) {
1524
- throw error2;
1525
- } else {
1526
- this.warn(
1527
- `${error2.message}. The credential in hand is still valid, so this command went ahead; renewal is retried on the next one.`
1528
- );
1529
- }
1648
+ } else this.warn(
1649
+ `${error2.message}. The credential in hand is still valid, so this command went ahead; renewal is retried on the next one.`
1650
+ );
1530
1651
  }
1531
1652
  return this.token;
1532
1653
  }
@@ -1568,9 +1689,8 @@ var init_renewal = __esm({
1568
1689
  workspaceId: this.options.workspaceId,
1569
1690
  predecessor: this.token,
1570
1691
  commandId,
1571
- // Only so a 401 on an already-expired credential is reported as expiry, not
1572
- // revocation (D-004). Never sent on the wire.
1573
1692
  expiresAt: this.expiresAt,
1693
+ listenerMode: this.options.listenerMode === true,
1574
1694
  ...this.options.fetcher ? { fetcher: this.options.fetcher } : {},
1575
1695
  now: this.clock
1576
1696
  });
@@ -5109,22 +5229,22 @@ var init_error_envelope = __esm({
5109
5229
  function parseSignalAttachments(value, options = {}) {
5110
5230
  if (options.enabled === false || value === void 0) return [];
5111
5231
  if (!Array.isArray(value) || value.length > SIGNAL_ATTACHMENT_MAX) {
5112
- throw new Error("signal read returned malformed attachments");
5232
+ throw new SignalAttachmentMalformedError("signal read returned malformed attachments");
5113
5233
  }
5114
5234
  const attachments = [];
5115
5235
  const seen = /* @__PURE__ */ new Set();
5116
5236
  for (const valueAtPosition of value) {
5117
5237
  if (!valueAtPosition || typeof valueAtPosition !== "object" || Array.isArray(valueAtPosition)) {
5118
- throw new Error("signal read returned a malformed attachment");
5238
+ throw new SignalAttachmentMalformedError("signal read returned a malformed attachment");
5119
5239
  }
5120
5240
  const row = valueAtPosition;
5121
5241
  if (typeof row.file_id !== "string" || !UUID_RE9.test(row.file_id) || typeof row.version_n !== "number" || !Number.isSafeInteger(row.version_n) || row.version_n < 1 || typeof row.name !== "string" || row.name.length < 1 || row.name.length > 255 || typeof row.content_type !== "string" || row.content_type.length < 1 || typeof row.size_bytes !== "number" || !Number.isSafeInteger(row.size_bytes) || row.size_bytes < 0) {
5122
- throw new Error("signal read returned malformed attachment metadata");
5242
+ throw new SignalAttachmentMalformedError("signal read returned malformed attachment metadata");
5123
5243
  }
5124
5244
  const fileId = row.file_id.toLowerCase();
5125
5245
  const key2 = `${fileId}:${row.version_n}`;
5126
5246
  if (seen.has(key2)) {
5127
- throw new Error("signal read returned duplicate attachment metadata");
5247
+ throw new SignalAttachmentMalformedError("signal read returned duplicate attachment metadata");
5128
5248
  }
5129
5249
  seen.add(key2);
5130
5250
  attachments.push({
@@ -5152,12 +5272,15 @@ function formatAttachmentSize(bytes) {
5152
5272
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
5153
5273
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
5154
5274
  }
5155
- var SIGNAL_ATTACHMENT_MAX, UUID_RE9;
5275
+ var SIGNAL_ATTACHMENT_MAX, UUID_RE9, SignalAttachmentMalformedError;
5156
5276
  var init_attachments = __esm({
5157
5277
  "src/cloud/attachments.ts"() {
5158
5278
  "use strict";
5159
5279
  SIGNAL_ATTACHMENT_MAX = 8;
5160
5280
  UUID_RE9 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
5281
+ SignalAttachmentMalformedError = class extends Error {
5282
+ name = "SignalAttachmentMalformedError";
5283
+ };
5161
5284
  }
5162
5285
  });
5163
5286
 
@@ -5169,13 +5292,13 @@ function plainTransportError(failureCode = "no_response") {
5169
5292
  return error2;
5170
5293
  }
5171
5294
  function plainMalformedError(message) {
5172
- const error2 = new Error(message);
5295
+ const error2 = new SignalMalformedError(message);
5173
5296
  plainMalformedErrors.add(error2);
5174
5297
  return error2;
5175
5298
  }
5176
5299
  function checkedUuid2(value, field) {
5177
5300
  if (typeof value !== "string" || !UUID_RE10.test(value)) {
5178
- throw new Error(`signal read returned a malformed ${field}`);
5301
+ throw new SignalMalformedError(`signal read returned a malformed ${field}`);
5179
5302
  }
5180
5303
  return value.toLowerCase();
5181
5304
  }
@@ -5184,20 +5307,20 @@ function checkedNullableUuid(value, field) {
5184
5307
  }
5185
5308
  function checkedBoolean(value, field) {
5186
5309
  if (typeof value !== "boolean") {
5187
- throw new Error(`signal read returned a malformed ${field}`);
5310
+ throw new SignalMalformedError(`signal read returned a malformed ${field}`);
5188
5311
  }
5189
5312
  return value;
5190
5313
  }
5191
5314
  function checkedTimestamp(value, field) {
5192
5315
  if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
5193
- throw new Error(`signal read returned a malformed ${field}`);
5316
+ throw new SignalMalformedError(`signal read returned a malformed ${field}`);
5194
5317
  }
5195
5318
  return value;
5196
5319
  }
5197
5320
  function deliveryCapabilityMarker(row, key2) {
5198
5321
  if (row[key2] === void 0) return false;
5199
5322
  if (row[key2] !== 1) {
5200
- throw new Error("signal read returned a malformed delivery capability marker");
5323
+ throw new SignalMalformedError("signal read returned a malformed delivery capability marker");
5201
5324
  }
5202
5325
  return true;
5203
5326
  }
@@ -5222,30 +5345,30 @@ function pendingDeliveryCountOf(body, capabilities) {
5222
5345
  if (!capabilities.deliveryClaim && !capabilities.deliveryAck) return null;
5223
5346
  const value = body.pending_delivery_count;
5224
5347
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
5225
- throw new Error("signal read returned a malformed pending_delivery_count");
5348
+ throw new SignalMalformedError("signal read returned a malformed pending_delivery_count");
5226
5349
  }
5227
5350
  return value;
5228
5351
  }
5229
5352
  function parseSignalRecipients(value) {
5230
5353
  if (value === void 0) return {};
5231
5354
  if (!Array.isArray(value)) {
5232
- throw new Error("signal read returned a malformed recipients list");
5355
+ throw new SignalMalformedError("signal read returned a malformed recipients list");
5233
5356
  }
5234
5357
  const recipients = [];
5235
5358
  const seenPositions = /* @__PURE__ */ new Set();
5236
5359
  const seenIds = /* @__PURE__ */ new Set();
5237
5360
  for (const entry of value) {
5238
5361
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
5239
- throw new Error("signal read returned a malformed recipients list");
5362
+ throw new SignalMalformedError("signal read returned a malformed recipients list");
5240
5363
  }
5241
5364
  const row = entry;
5242
5365
  const keys = Object.keys(row).sort();
5243
5366
  if (keys.length !== 3 || keys[0] !== "id" || keys[1] !== "kind" || keys[2] !== "position" || typeof row.kind !== "string" || !SIGNAL_RECIPIENT_KINDS.has(row.kind) || typeof row.position !== "number" || !Number.isSafeInteger(row.position) || row.position < 0) {
5244
- throw new Error("signal read returned a malformed recipients list");
5367
+ throw new SignalMalformedError("signal read returned a malformed recipients list");
5245
5368
  }
5246
5369
  const id = checkedUuid2(row.id, "recipients[].id");
5247
5370
  if (seenPositions.has(row.position) || seenIds.has(id)) {
5248
- throw new Error("signal read returned a repeated recipient");
5371
+ throw new SignalMalformedError("signal read returned a repeated recipient");
5249
5372
  }
5250
5373
  seenPositions.add(row.position);
5251
5374
  seenIds.add(id);
@@ -5265,18 +5388,18 @@ function signalAddressesAgent(signal, principalId) {
5265
5388
  }
5266
5389
  function parseSignalRecord(value, options = {}) {
5267
5390
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5268
- throw new Error("signal read returned a malformed row");
5391
+ throw new SignalMalformedError("signal read returned a malformed row");
5269
5392
  }
5270
5393
  const row = value;
5271
5394
  if (typeof row.from_kind !== "string" || !["user", "agent"].includes(row.from_kind) || typeof row.kind !== "string" || !SIGNAL_KINDS.has(row.kind) || typeof row.body !== "string" || row.body.length < 1 || !(row.about === null || typeof row.about === "string")) {
5272
- throw new Error("signal read returned malformed signal data");
5395
+ throw new SignalMalformedError("signal read returned malformed signal data");
5273
5396
  }
5274
5397
  let senderOwnerRelation = "unknown";
5275
5398
  if (row.sender_owner_relation !== void 0) {
5276
5399
  if (typeof row.sender_owner_relation !== "string" || !SENDER_OWNER_RELATIONS.has(
5277
5400
  row.sender_owner_relation
5278
5401
  )) {
5279
- throw new Error(
5402
+ throw new SignalMalformedError(
5280
5403
  "signal read returned a malformed sender_owner_relation"
5281
5404
  );
5282
5405
  }
@@ -5352,7 +5475,7 @@ function parseSignalRows(rows3, options) {
5352
5475
  const parsed = error2 instanceof Error ? error2 : new Error(String(error2));
5353
5476
  options.onMalformedRow?.(index, parsed);
5354
5477
  if (malformedRows > options.maxMalformedRows) {
5355
- throw new Error(
5478
+ throw new SignalMalformedError(
5356
5479
  `signal read returned too many malformed rows (more than ${options.maxMalformedRows})`
5357
5480
  );
5358
5481
  }
@@ -5480,7 +5603,7 @@ function classifySignalReadFailure(error2) {
5480
5603
  if (error2 instanceof SignalTransportError) {
5481
5604
  return { code: "no_response", httpStatus: null, errorConstructor: null };
5482
5605
  }
5483
- if (error2 instanceof SignalMalformedError || error2 instanceof Error && plainMalformedErrors.has(error2)) {
5606
+ if (error2 instanceof SignalMalformedError || error2 instanceof SignalAttachmentMalformedError || error2 instanceof Error && plainMalformedErrors.has(error2)) {
5484
5607
  return {
5485
5608
  code: "malformed_response",
5486
5609
  httpStatus: null,
@@ -5501,14 +5624,18 @@ function isRestartableReadError(error2) {
5501
5624
  if (isTransportFollowMessage(error2)) return true;
5502
5625
  const http = followHttpDetails(error2);
5503
5626
  if (http !== null) {
5504
- return http.status === 429 || http.status >= 500;
5627
+ if (isConfirmedCredentialHttpFailure(
5628
+ http.status,
5629
+ followErrorEnvelope(error2).error
5630
+ )) {
5631
+ return false;
5632
+ }
5633
+ return http.status === 429 || http.status >= 500 || http.status === 401 || http.status === 403;
5505
5634
  }
5506
5635
  return false;
5507
5636
  }
5508
5637
  function isMalformedFollowMessage(error2) {
5509
- if (error2 instanceof SignalMalformedError) return true;
5510
- if (!(error2 instanceof Error)) return false;
5511
- return error2.message.startsWith("signal read returned a malformed") || error2.message === "signal read returned malformed JSON" || error2.message === "signal read returned malformed signal data" || error2.message === "signal read returned a malformed row";
5638
+ return error2 instanceof SignalMalformedError || error2 instanceof SignalAttachmentMalformedError || error2 instanceof Error && plainMalformedErrors.has(error2);
5512
5639
  }
5513
5640
  function checkedLimit(value) {
5514
5641
  const limit = value ?? 50;
@@ -5796,17 +5923,17 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
5796
5923
  }
5797
5924
  function parseAgentMemberRow(value) {
5798
5925
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5799
- throw new Error("member read returned a malformed agent row");
5926
+ throw new SignalMalformedError("member read returned a malformed agent row");
5800
5927
  }
5801
5928
  const row = value;
5802
5929
  if (typeof row.name !== "string") {
5803
- throw new Error("member read returned a malformed agent name");
5930
+ throw new SignalMalformedError("member read returned a malformed agent name");
5804
5931
  }
5805
5932
  if (row.model !== void 0 && row.model !== null && typeof row.model !== "string") {
5806
- throw new Error("member read returned a malformed agent model");
5933
+ throw new SignalMalformedError("member read returned a malformed agent model");
5807
5934
  }
5808
5935
  if (row.generation !== void 0 && row.generation !== null && (typeof row.generation !== "number" || !Number.isSafeInteger(row.generation) || row.generation < 1)) {
5809
- throw new Error("member read returned a malformed agent generation");
5936
+ throw new SignalMalformedError("member read returned a malformed agent generation");
5810
5937
  }
5811
5938
  return {
5812
5939
  ...row.model === void 0 ? {} : { model: row.model },
@@ -5818,11 +5945,11 @@ function parseAgentMemberRow(value) {
5818
5945
  }
5819
5946
  function parseMemberRow(value) {
5820
5947
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5821
- throw new Error("member read returned a malformed row");
5948
+ throw new SignalMalformedError("member read returned a malformed row");
5822
5949
  }
5823
5950
  const row = value;
5824
5951
  if (typeof row.display_name !== "string") {
5825
- throw new Error("member read returned a malformed display name");
5952
+ throw new SignalMalformedError("member read returned a malformed display name");
5826
5953
  }
5827
5954
  return {
5828
5955
  user_id: checkedUuid2(row.user_id, "member user_id"),
@@ -5831,15 +5958,15 @@ function parseMemberRow(value) {
5831
5958
  }
5832
5959
  function parseAgentIdentity(value) {
5833
5960
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5834
- throw new Error("member read returned a malformed credential identity");
5961
+ throw new SignalMalformedError("member read returned a malformed credential identity");
5835
5962
  }
5836
5963
  const row = value;
5837
5964
  if (row.credential_valid !== true) {
5838
- throw new Error("member read returned a malformed credential validity");
5965
+ throw new SignalMalformedError("member read returned a malformed credential validity");
5839
5966
  }
5840
5967
  const name = row.workspace_name;
5841
5968
  if (name !== void 0 && name !== null && typeof name !== "string") {
5842
- throw new Error("member read returned a malformed workspace name");
5969
+ throw new SignalMalformedError("member read returned a malformed workspace name");
5843
5970
  }
5844
5971
  return {
5845
5972
  credential_valid: true,
@@ -5870,27 +5997,27 @@ async function readAgentSignalDirectory(target2, token, workspaceId2, fetcherOrO
5870
5997
  } catch (error2) {
5871
5998
  if (error2 instanceof SignalReadTimeoutError) {
5872
5999
  if (options.deadlineMs !== void 0) throw error2;
5873
- throw new Error("member read could not reach the cloud service");
6000
+ throw new SignalTransportError("member read could not reach the cloud service");
5874
6001
  }
5875
6002
  throw error2;
5876
6003
  }
5877
6004
  if (result === null) {
5878
- throw new Error("member read could not reach the cloud service");
6005
+ throw new SignalTransportError("member read could not reach the cloud service");
5879
6006
  }
5880
6007
  const { response, body } = result;
5881
6008
  if (!response.ok) {
5882
6009
  throwSignalHttp(response, body, "member read failed");
5883
6010
  }
5884
6011
  if (!body || typeof body !== "object" || Array.isArray(body)) {
5885
- throw new Error("member read returned malformed JSON");
6012
+ throw new SignalMalformedError("member read returned malformed JSON");
5886
6013
  }
5887
6014
  const payload = body;
5888
6015
  if (!Array.isArray(payload.members)) {
5889
- throw new Error("member read returned malformed JSON");
6016
+ throw new SignalMalformedError("member read returned malformed JSON");
5890
6017
  }
5891
6018
  const agentsRaw = payload.agents;
5892
6019
  const agents = agentsRaw === void 0 ? [] : Array.isArray(agentsRaw) ? agentsRaw.map(parseAgentMemberRow) : (() => {
5893
- throw new Error("member read returned malformed agents");
6020
+ throw new SignalMalformedError("member read returned malformed agents");
5894
6021
  })();
5895
6022
  return {
5896
6023
  members: payload.members.map(parseMemberRow),
@@ -6241,12 +6368,26 @@ function resolveRefusalToleranceMs(raw, warn = () => {
6241
6368
  }
6242
6369
  return parsed;
6243
6370
  }
6371
+ function isConfirmedCredentialLossCode(code, surface = "read") {
6372
+ if (typeof code !== "string") return false;
6373
+ const set = surface === "command" ? COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODE_SET : READ_CONFIRMED_CREDENTIAL_LOSS_CODE_SET;
6374
+ return set.has(code);
6375
+ }
6376
+ function isConfirmedCredentialHttpFailure(status, code, surface = "read") {
6377
+ return (status === 401 || status === 403) && isConfirmedCredentialLossCode(code, surface);
6378
+ }
6244
6379
  function isRetryableFollowError(error2) {
6380
+ const http = followHttpDetails(error2);
6381
+ if (http !== null && (http.status === 401 || http.status === 403) && !isConfirmedCredentialHttpFailure(
6382
+ http.status,
6383
+ followErrorEnvelope(error2).error
6384
+ )) {
6385
+ return true;
6386
+ }
6245
6387
  if (serverRefusedRetry(followErrorEnvelope(error2))) return false;
6246
6388
  if (error2 instanceof SignalHostPortsExhaustedError) return true;
6247
6389
  if (error2 instanceof SignalReadTimeoutError) return true;
6248
6390
  if (isTransportFollowMessage(error2)) return true;
6249
- const http = followHttpDetails(error2);
6250
6391
  if (http) return http.status === 429 || http.status >= 500;
6251
6392
  return false;
6252
6393
  }
@@ -6254,18 +6395,27 @@ function isFatalFollowError(error2) {
6254
6395
  if (isMalformedFollowMessage(error2)) return true;
6255
6396
  const http = followHttpDetails(error2);
6256
6397
  if (!http) return false;
6257
- return http.status === 400 || http.status === 401 || http.status === 403 || http.status === 404 || http.status === 426 || http.status >= 400 && http.status < 500 && http.status !== 429;
6398
+ if (http.status === 401 || http.status === 403) {
6399
+ return isConfirmedCredentialHttpFailure(
6400
+ http.status,
6401
+ followErrorEnvelope(error2).error
6402
+ );
6403
+ }
6404
+ return http.status === 400 || http.status === 404 || http.status === 426 || http.status >= 400 && http.status < 500 && http.status !== 429;
6258
6405
  }
6259
6406
  function isFollowCredentialFailure(error2) {
6260
6407
  if (!(error2 instanceof Error)) return false;
6261
6408
  const http = followHttpDetails(error2);
6262
6409
  if (http !== null) {
6263
- return http.status === 401 || http.status === 403;
6410
+ return isConfirmedCredentialHttpFailure(
6411
+ http.status,
6412
+ followErrorEnvelope(error2).error
6413
+ );
6264
6414
  }
6265
6415
  if (error2.name === "RenewalReauthorisationRequired" || error2.name === "RenewalRevoked" || error2.name === "RenewalSuspended") {
6266
6416
  return true;
6267
6417
  }
6268
- return /secret is absent/i.test(error2.message);
6418
+ return error2 instanceof LocalCredentialSecretAbsentError;
6269
6419
  }
6270
6420
  function followRetryReason(error2) {
6271
6421
  if (error2 instanceof SignalReadTimeoutError) return "idle_deadline";
@@ -6472,7 +6622,7 @@ async function runInboxFollow(options) {
6472
6622
  }
6473
6623
  }
6474
6624
  }
6475
- var UUID_RE10, SIGNAL_KINDS, SIGNAL_BODY_DISPLAY_MAX, SIGNAL_ABOUT_DISPLAY_MAX, SIGNAL_READ_TIMEOUT_MS, SignalReadTimeoutError, SignalHostPortsExhaustedError, SIGNAL_WAIT_MIN_SECONDS, SIGNAL_WAIT_MAX_SECONDS, SIGNAL_WAIT_POLL_MS, SIGNAL_FOLLOW_POLL_MS, SIGNAL_FOLLOW_BACKOFF_INITIAL_MS, SIGNAL_FOLLOW_BACKOFF_MAX_MS, SIGNAL_FOLLOW_SEEN_MAX, SIGNAL_FOLLOW_POST_EMIT_MS, SIGNAL_FOLLOW_PAGE_LIMIT, SignalHttpError, SignalTransportError, SignalMalformedError, plainHttpRetryAfterMs, plainHttpStatus, plainHttpEnvelope, plainTransportErrors, plainTransportFailureCodes, plainMalformedErrors, SENDER_OWNER_RELATIONS, SIGNAL_RECIPIENT_KINDS, READ_RETRY_ATTEMPTS, READ_RETRY_BASE_MS, SIGNAL_STATUS_UNAVAILABLE_MESSAGE, ASK_WAIT_TIMEOUT_MESSAGE, BoundedSignalIdSet, DEFAULT_REFUSAL_TOLERANCE_MS, MAX_REFUSAL_TOLERANCE_MS;
6625
+ var UUID_RE10, SIGNAL_KINDS, SIGNAL_BODY_DISPLAY_MAX, SIGNAL_ABOUT_DISPLAY_MAX, SIGNAL_READ_TIMEOUT_MS, SignalReadTimeoutError, SignalHostPortsExhaustedError, SIGNAL_WAIT_MIN_SECONDS, SIGNAL_WAIT_MAX_SECONDS, SIGNAL_WAIT_POLL_MS, SIGNAL_FOLLOW_POLL_MS, SIGNAL_FOLLOW_BACKOFF_INITIAL_MS, SIGNAL_FOLLOW_BACKOFF_MAX_MS, SIGNAL_FOLLOW_SEEN_MAX, SIGNAL_FOLLOW_POST_EMIT_MS, SIGNAL_FOLLOW_PAGE_LIMIT, SignalHttpError, SignalTransportError, LocalCredentialSecretAbsentError, ListenerCredentialStateMismatchError, SignalMalformedError, plainHttpRetryAfterMs, plainHttpStatus, plainHttpEnvelope, plainTransportErrors, plainTransportFailureCodes, plainMalformedErrors, SENDER_OWNER_RELATIONS, SIGNAL_RECIPIENT_KINDS, READ_RETRY_ATTEMPTS, READ_RETRY_BASE_MS, SIGNAL_STATUS_UNAVAILABLE_MESSAGE, ASK_WAIT_TIMEOUT_MESSAGE, BoundedSignalIdSet, DEFAULT_REFUSAL_TOLERANCE_MS, MAX_REFUSAL_TOLERANCE_MS, CONFIRMED_CREDENTIAL_LOSS_CODES, COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES, READ_CONFIRMED_CREDENTIAL_LOSS_CODE_SET, COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODE_SET;
6476
6626
  var init_signals = __esm({
6477
6627
  "src/cloud/signals.ts"() {
6478
6628
  "use strict";
@@ -6529,6 +6679,19 @@ var init_signals = __esm({
6529
6679
  this.name = "SignalTransportError";
6530
6680
  }
6531
6681
  };
6682
+ LocalCredentialSecretAbsentError = class extends Error {
6683
+ constructor(message = "agent credential secret is absent") {
6684
+ super(message);
6685
+ this.name = "LocalCredentialSecretAbsentError";
6686
+ }
6687
+ };
6688
+ ListenerCredentialStateMismatchError = class extends LocalCredentialSecretAbsentError {
6689
+ code = "local_credential_state_mismatch";
6690
+ constructor() {
6691
+ super("listener credential state did not preserve the live credential");
6692
+ this.name = "ListenerCredentialStateMismatchError";
6693
+ }
6694
+ };
6532
6695
  SignalMalformedError = class extends Error {
6533
6696
  constructor(message) {
6534
6697
  super(message);
@@ -6584,6 +6747,19 @@ var init_signals = __esm({
6584
6747
  };
6585
6748
  DEFAULT_REFUSAL_TOLERANCE_MS = 6e4;
6586
6749
  MAX_REFUSAL_TOLERANCE_MS = 10 * 6e4;
6750
+ CONFIRMED_CREDENTIAL_LOSS_CODES = Object.freeze([
6751
+ "unauthenticated",
6752
+ "forbidden"
6753
+ ]);
6754
+ COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES = Object.freeze([
6755
+ "unauthenticated"
6756
+ ]);
6757
+ READ_CONFIRMED_CREDENTIAL_LOSS_CODE_SET = new Set(
6758
+ CONFIRMED_CREDENTIAL_LOSS_CODES
6759
+ );
6760
+ COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODE_SET = new Set(
6761
+ COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES
6762
+ );
6587
6763
  }
6588
6764
  });
6589
6765
 
@@ -24589,7 +24765,7 @@ function observationCommandId(signalId) {
24589
24765
  }
24590
24766
  function checkedUuid3(value, field) {
24591
24767
  if (typeof value !== "string" || !UUID_RE11.test(value)) {
24592
- throw new DeliveryProtocolError(
24768
+ throw new DeliveryMalformedResponseError(
24593
24769
  `delivery response returned a malformed ${field}`
24594
24770
  );
24595
24771
  }
@@ -24621,13 +24797,13 @@ function daysInMonth(year, month) {
24621
24797
  }
24622
24798
  function checkedRfc3339Timestamp(value, field) {
24623
24799
  if (typeof value !== "string") {
24624
- throw new DeliveryProtocolError(
24800
+ throw new DeliveryMalformedResponseError(
24625
24801
  `delivery response returned a malformed ${field}`
24626
24802
  );
24627
24803
  }
24628
24804
  const match = RFC3339_TIMESTAMP_RE.exec(value);
24629
24805
  if (!match) {
24630
- throw new DeliveryProtocolError(
24806
+ throw new DeliveryMalformedResponseError(
24631
24807
  `delivery response returned a malformed ${field}`
24632
24808
  );
24633
24809
  }
@@ -24638,7 +24814,7 @@ function checkedRfc3339Timestamp(value, field) {
24638
24814
  const minute = parseInt(match[5], 10);
24639
24815
  const second = parseInt(match[6], 10);
24640
24816
  if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
24641
- throw new DeliveryProtocolError(
24817
+ throw new DeliveryMalformedResponseError(
24642
24818
  `delivery response returned a malformed ${field}`
24643
24819
  );
24644
24820
  }
@@ -24646,13 +24822,13 @@ function checkedRfc3339Timestamp(value, field) {
24646
24822
  const offsetHour = Math.abs(parseInt(match[7], 10));
24647
24823
  const offsetMin = parseInt(match[8], 10);
24648
24824
  if (offsetHour > 23 || offsetMin < 0 || offsetMin > 59) {
24649
- throw new DeliveryProtocolError(
24825
+ throw new DeliveryMalformedResponseError(
24650
24826
  `delivery response returned a malformed ${field}`
24651
24827
  );
24652
24828
  }
24653
24829
  }
24654
24830
  if (!Number.isFinite(Date.parse(value))) {
24655
- throw new DeliveryProtocolError(
24831
+ throw new DeliveryMalformedResponseError(
24656
24832
  `delivery response returned a malformed ${field}`
24657
24833
  );
24658
24834
  }
@@ -24660,14 +24836,14 @@ function checkedRfc3339Timestamp(value, field) {
24660
24836
  }
24661
24837
  function checkedLiveLease(leasedUntil, now) {
24662
24838
  if (Date.parse(leasedUntil) <= now()) {
24663
- throw new DeliveryProtocolError(
24839
+ throw new DeliveryMalformedResponseError(
24664
24840
  "delivery claim response returned an already expired lease"
24665
24841
  );
24666
24842
  }
24667
24843
  }
24668
24844
  function checkedRelation(value) {
24669
24845
  if (typeof value !== "string" || !SENDER_OWNER_RELATIONS2.has(value)) {
24670
- throw new DeliveryProtocolError(
24846
+ throw new DeliveryMalformedResponseError(
24671
24847
  "delivery response returned a malformed sender_owner_relation"
24672
24848
  );
24673
24849
  }
@@ -24675,7 +24851,7 @@ function checkedRelation(value) {
24675
24851
  }
24676
24852
  function checkedNonNegativeCount(value, field) {
24677
24853
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
24678
- throw new DeliveryProtocolError(
24854
+ throw new DeliveryMalformedResponseError(
24679
24855
  `delivery response returned a malformed ${field}`
24680
24856
  );
24681
24857
  }
@@ -24683,14 +24859,14 @@ function checkedNonNegativeCount(value, field) {
24683
24859
  }
24684
24860
  function checkedClaimCapabilities(value) {
24685
24861
  if (!value || typeof value !== "object" || Array.isArray(value)) {
24686
- throw new DeliveryProtocolError(
24862
+ throw new DeliveryMalformedResponseError(
24687
24863
  "delivery claim response is missing delivery capabilities"
24688
24864
  );
24689
24865
  }
24690
24866
  const row = value;
24691
24867
  for (const marker of ["delivery_claim", "delivery_ack", "sender_owner_relation"]) {
24692
24868
  if (row[marker] !== 1) {
24693
- throw new DeliveryProtocolError(
24869
+ throw new DeliveryMalformedResponseError(
24694
24870
  `delivery claim response is missing the ${marker} capability`
24695
24871
  );
24696
24872
  }
@@ -24700,7 +24876,7 @@ function checkedClaimCapabilities(value) {
24700
24876
  function checkedOptionalUuidArray(value, field) {
24701
24877
  if (value === void 0) return;
24702
24878
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !UUID_RE11.test(item))) {
24703
- throw new DeliveryProtocolError(
24879
+ throw new DeliveryMalformedResponseError(
24704
24880
  `delivery response returned a malformed ${field}`
24705
24881
  );
24706
24882
  }
@@ -24708,7 +24884,7 @@ function checkedOptionalUuidArray(value, field) {
24708
24884
  function checkedOptionalArray(value, field) {
24709
24885
  if (value === void 0) return;
24710
24886
  if (!Array.isArray(value)) {
24711
- throw new DeliveryProtocolError(
24887
+ throw new DeliveryMalformedResponseError(
24712
24888
  `delivery response returned a malformed ${field}`
24713
24889
  );
24714
24890
  }
@@ -24718,7 +24894,7 @@ function checkedRecipientSlot(row) {
24718
24894
  const hasCount = Object.hasOwn(row, "recipient_count");
24719
24895
  if (!hasPosition && !hasCount) return { position: null, count: null };
24720
24896
  if (!hasPosition || !hasCount) {
24721
- throw new DeliveryProtocolError(
24897
+ throw new DeliveryMalformedResponseError(
24722
24898
  "delivery claim response returned a recipient position without its count"
24723
24899
  );
24724
24900
  }
@@ -24728,7 +24904,7 @@ function checkedRecipientSlot(row) {
24728
24904
  );
24729
24905
  const count2 = checkedNonNegativeCount(row.recipient_count, "recipient_count");
24730
24906
  if (count2 < 1 || position >= count2) {
24731
- throw new DeliveryProtocolError(
24907
+ throw new DeliveryMalformedResponseError(
24732
24908
  "delivery claim response returned a recipient position outside its set"
24733
24909
  );
24734
24910
  }
@@ -24736,7 +24912,7 @@ function checkedRecipientSlot(row) {
24736
24912
  }
24737
24913
  function parseDeliveryRow(value, expected, index, now) {
24738
24914
  if (!value || typeof value !== "object" || Array.isArray(value)) {
24739
- throw new DeliveryProtocolError(
24915
+ throw new DeliveryMalformedResponseError(
24740
24916
  "delivery claim response returned a malformed delivery row"
24741
24917
  );
24742
24918
  }
@@ -24745,22 +24921,22 @@ function parseDeliveryRow(value, expected, index, now) {
24745
24921
  try {
24746
24922
  signal = parseSignalRecord(row.signal);
24747
24923
  } catch {
24748
- throw new DeliveryProtocolError(
24924
+ throw new DeliveryMalformedResponseError(
24749
24925
  `delivery claim response returned a malformed signal at ${index}`
24750
24926
  );
24751
24927
  }
24752
24928
  if (signal.workspace_id !== expected.workspaceId) {
24753
- throw new DeliveryProtocolError(
24929
+ throw new DeliveryMalformedResponseError(
24754
24930
  "delivery claim response returned a signal for another workspace"
24755
24931
  );
24756
24932
  }
24757
24933
  if (signal.to_agent !== expected.principalId) {
24758
- throw new DeliveryProtocolError(
24934
+ throw new DeliveryMalformedResponseError(
24759
24935
  "delivery claim response returned a signal addressed to another agent"
24760
24936
  );
24761
24937
  }
24762
24938
  if (!DELIVERY_KINDS.has(signal.kind)) {
24763
- throw new DeliveryProtocolError(
24939
+ throw new DeliveryMalformedResponseError(
24764
24940
  "delivery claim response returned a non-direct signal kind"
24765
24941
  );
24766
24942
  }
@@ -24781,11 +24957,11 @@ function parseDeliveryRow(value, expected, index, now) {
24781
24957
  }
24782
24958
  function parseClaimSuccess(body, expected, now) {
24783
24959
  if (!body || typeof body !== "object" || Array.isArray(body)) {
24784
- throw new DeliveryProtocolError("delivery claim response was not an object");
24960
+ throw new DeliveryMalformedResponseError("delivery claim response was not an object");
24785
24961
  }
24786
24962
  const row = body;
24787
24963
  if (row.status !== "accepted" || row.ok !== true) {
24788
- throw new DeliveryProtocolError(
24964
+ throw new DeliveryMalformedResponseError(
24789
24965
  "delivery claim response did not report accepted ok"
24790
24966
  );
24791
24967
  }
@@ -24793,7 +24969,7 @@ function parseClaimSuccess(body, expected, now) {
24793
24969
  checkedOptionalUuidArray(row.event_ids, "event_ids");
24794
24970
  checkedOptionalArray(row.events, "events");
24795
24971
  if (!Array.isArray(row.deliveries)) {
24796
- throw new DeliveryProtocolError(
24972
+ throw new DeliveryMalformedResponseError(
24797
24973
  "delivery claim response is missing its deliveries array"
24798
24974
  );
24799
24975
  }
@@ -24804,20 +24980,20 @@ function parseClaimSuccess(body, expected, now) {
24804
24980
  const leaseIds = /* @__PURE__ */ new Set();
24805
24981
  for (const delivery of deliveries) {
24806
24982
  if (signalIds.has(delivery.signal.id)) {
24807
- throw new DeliveryProtocolError(
24983
+ throw new DeliveryMalformedResponseError(
24808
24984
  "delivery claim response repeats a signal id"
24809
24985
  );
24810
24986
  }
24811
24987
  signalIds.add(delivery.signal.id);
24812
24988
  if (leaseIds.has(delivery.leaseId)) {
24813
- throw new DeliveryProtocolError(
24989
+ throw new DeliveryMalformedResponseError(
24814
24990
  "delivery claim response repeats a lease id"
24815
24991
  );
24816
24992
  }
24817
24993
  leaseIds.add(delivery.leaseId);
24818
24994
  }
24819
24995
  if (deliveries.length > 1) {
24820
- throw new DeliveryProtocolError(
24996
+ throw new DeliveryMalformedResponseError(
24821
24997
  "delivery claim response returned more than one delivery"
24822
24998
  );
24823
24999
  }
@@ -24830,7 +25006,7 @@ function parseClaimSuccess(body, expected, now) {
24830
25006
  "terminal_delivery_failure_count"
24831
25007
  );
24832
25008
  if (deliveries.length > pendingDeliveryCount) {
24833
- throw new DeliveryProtocolError(
25009
+ throw new DeliveryMalformedResponseError(
24834
25010
  "delivery claim response returned more deliveries than its pending count"
24835
25011
  );
24836
25012
  }
@@ -24838,7 +25014,7 @@ function parseClaimSuccess(body, expected, now) {
24838
25014
  try {
24839
25015
  wake = parseOptionalWakeHint(row.wake);
24840
25016
  } catch {
24841
- throw new DeliveryProtocolError("delivery claim response wake field is malformed");
25017
+ throw new DeliveryMalformedResponseError("delivery claim response wake field is malformed");
24842
25018
  }
24843
25019
  return {
24844
25020
  capabilities,
@@ -24850,25 +25026,25 @@ function parseClaimSuccess(body, expected, now) {
24850
25026
  }
24851
25027
  function parseAckSuccess(body, expected) {
24852
25028
  if (!body || typeof body !== "object" || Array.isArray(body)) {
24853
- throw new DeliveryProtocolError(
25029
+ throw new DeliveryMalformedResponseError(
24854
25030
  "delivery acknowledgement response was not an object"
24855
25031
  );
24856
25032
  }
24857
25033
  const row = body;
24858
25034
  if (row.status !== "accepted" || row.ok !== true) {
24859
- throw new DeliveryProtocolError(
25035
+ throw new DeliveryMalformedResponseError(
24860
25036
  "delivery acknowledgement response did not report accepted ok"
24861
25037
  );
24862
25038
  }
24863
25039
  checkedOptionalUuidArray(row.event_ids, "event_ids");
24864
25040
  checkedOptionalArray(row.events, "events");
24865
25041
  if (row.signal_id !== expected.signalId) {
24866
- throw new DeliveryProtocolError(
25042
+ throw new DeliveryMalformedResponseError(
24867
25043
  "delivery acknowledgement response echoed a different signal id"
24868
25044
  );
24869
25045
  }
24870
25046
  if (row.outcome !== expected.outcome) {
24871
- throw new DeliveryProtocolError(
25047
+ throw new DeliveryMalformedResponseError(
24872
25048
  "delivery acknowledgement response echoed a different outcome"
24873
25049
  );
24874
25050
  }
@@ -24912,18 +25088,23 @@ function assertAckRequest(request) {
24912
25088
  }
24913
25089
  function boundedDeliveryErrorCode(body) {
24914
25090
  if (!body || typeof body !== "object" || Array.isArray(body)) {
24915
- return DELIVERY_UNKNOWN_ERROR_CODE;
25091
+ return null;
24916
25092
  }
24917
25093
  const error2 = body.error;
24918
25094
  if (typeof error2 === "string" && SERVER_ERROR_CODES_SET.has(error2)) {
24919
25095
  return error2;
24920
25096
  }
24921
- return DELIVERY_UNKNOWN_ERROR_CODE;
25097
+ return null;
24922
25098
  }
24923
25099
  function refusal(response, text) {
24924
25100
  let code = DELIVERY_UNKNOWN_ERROR_CODE;
25101
+ let recognizedEnvelope = false;
24925
25102
  try {
24926
- code = boundedDeliveryErrorCode(JSON.parse(text));
25103
+ const recognizedCode = boundedDeliveryErrorCode(JSON.parse(text));
25104
+ if (recognizedCode !== null) {
25105
+ code = recognizedCode;
25106
+ recognizedEnvelope = true;
25107
+ }
24927
25108
  } catch {
24928
25109
  }
24929
25110
  const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
@@ -24931,19 +25112,25 @@ function refusal(response, text) {
24931
25112
  response.status,
24932
25113
  code,
24933
25114
  `delivery command failed (HTTP ${response.status}): ${code}`,
24934
- retryAfterMs
25115
+ retryAfterMs,
25116
+ recognizedEnvelope
24935
25117
  );
24936
25118
  }
24937
25119
  function successBody(response, text, verb) {
25120
+ let body;
24938
25121
  try {
24939
- return JSON.parse(text);
25122
+ body = JSON.parse(text);
24940
25123
  } catch {
24941
- throw new DeliveryProtocolError(
25124
+ throw new DeliveryResponseError(
24942
25125
  `${verb} response was not JSON (HTTP ${response.status})`
24943
25126
  );
24944
25127
  }
25128
+ if (!body || typeof body !== "object" || Array.isArray(body) || body.status !== "accepted" || body.ok !== true) {
25129
+ throw new DeliveryResponseError(`${verb} response did not carry an accepted envelope`);
25130
+ }
25131
+ return body;
24945
25132
  }
24946
- var UUID_RE11, RFC3339_TIMESTAMP_RE, DELIVERY_KINDS, SENDER_OWNER_RELATIONS2, DELIVERY_ACK_OUTCOMES, DELIVERY_HANDLED_OUTCOMES, DELIVERY_PROVIDER_PROVEN_OUTCOMES, DELIVERY_REQUEST_TIMEOUT_MS, COMMAND_ID_VALIDATOR_RE, FAILED_TERMINAL_CODES_SET, SERVER_ERROR_CODES_SET, DELIVERY_FAILED_TERMINAL_CODES, DELIVERY_SERVER_ERROR_CODES, DELIVERY_UNKNOWN_ERROR_CODE, DeliveryTransportError, DeliveryHttpError, DeliveryProtocolError, DeliveryCommandClient;
25133
+ var UUID_RE11, RFC3339_TIMESTAMP_RE, DELIVERY_KINDS, SENDER_OWNER_RELATIONS2, DELIVERY_ACK_OUTCOMES, DELIVERY_HANDLED_OUTCOMES, DELIVERY_PROVIDER_PROVEN_OUTCOMES, DELIVERY_REQUEST_TIMEOUT_MS, COMMAND_ID_VALIDATOR_RE, FAILED_TERMINAL_CODES_SET, H0_SEAT_CLAIM_REFUSED_CODE, H0_SEAT_LISTENER_STOP_SENTENCE, DELIVERY_FAILED_TERMINAL_CODES, DELIVERY_SESSION_PROOF_CODES, DELIVERY_SERVER_ERROR_CODES, SERVER_ERROR_CODES_SET, DELIVERY_UNKNOWN_ERROR_CODE, DeliveryTransportError, DeliveryHttpError, DeliveryProtocolError, DeliveryResponseError, DeliveryMalformedResponseError, DeliveryCommandClient;
24947
25134
  var init_delivery = __esm({
24948
25135
  "src/cloud/delivery.ts"() {
24949
25136
  "use strict";
@@ -24984,26 +25171,20 @@ var init_delivery = __esm({
24984
25171
  "host_session_failed",
24985
25172
  "credential_unavailable"
24986
25173
  ]);
24987
- SERVER_ERROR_CODES_SET = /* @__PURE__ */ new Set([
24988
- "unauthenticated",
24989
- "fresh_auth_required",
24990
- "invalid_request",
24991
- "payload_too_large",
24992
- "forbidden",
24993
- "delivery_unavailable",
24994
- "delivery_ack_conflict",
24995
- "command_id_conflict",
24996
- "rate_limited",
24997
- "upgrade_required",
24998
- "temporarily_unavailable",
24999
- "internal_error"
25000
- ]);
25174
+ H0_SEAT_CLAIM_REFUSED_CODE = "h0_seat_uses_poll";
25175
+ H0_SEAT_LISTENER_STOP_SENTENCE = "This seat receives messages through the h0 poll. The listener has stopped; no further listener action is needed for this seat";
25001
25176
  DELIVERY_FAILED_TERMINAL_CODES = Object.freeze([
25002
25177
  "provider_refused",
25003
25178
  "local_effect_failed",
25004
25179
  "host_session_failed",
25005
25180
  "credential_unavailable"
25006
25181
  ]);
25182
+ DELIVERY_SESSION_PROOF_CODES = Object.freeze([
25183
+ "session_proof_missing",
25184
+ "session_proof_invalid",
25185
+ "session_expired",
25186
+ "session_conflict"
25187
+ ]);
25007
25188
  DELIVERY_SERVER_ERROR_CODES = Object.freeze([
25008
25189
  "unauthenticated",
25009
25190
  "fresh_auth_required",
@@ -25012,12 +25193,18 @@ var init_delivery = __esm({
25012
25193
  "forbidden",
25013
25194
  "delivery_unavailable",
25014
25195
  "delivery_ack_conflict",
25196
+ "delivery_not_surfaced",
25015
25197
  "command_id_conflict",
25016
25198
  "rate_limited",
25017
25199
  "upgrade_required",
25018
25200
  "temporarily_unavailable",
25019
- "internal_error"
25201
+ "internal_error",
25202
+ ...DELIVERY_SESSION_PROOF_CODES,
25203
+ H0_SEAT_CLAIM_REFUSED_CODE
25020
25204
  ]);
25205
+ SERVER_ERROR_CODES_SET = new Set(
25206
+ DELIVERY_SERVER_ERROR_CODES
25207
+ );
25021
25208
  DELIVERY_UNKNOWN_ERROR_CODE = "unknown_error";
25022
25209
  DeliveryTransportError = class extends Error {
25023
25210
  constructor(message) {
@@ -25026,16 +25213,18 @@ var init_delivery = __esm({
25026
25213
  }
25027
25214
  };
25028
25215
  DeliveryHttpError = class extends Error {
25029
- constructor(status, code, message, retryAfterMs = null) {
25216
+ constructor(status, code, message, retryAfterMs = null, recognizedEnvelope = true) {
25030
25217
  super(message);
25031
25218
  this.status = status;
25032
25219
  this.code = code;
25033
25220
  this.retryAfterMs = retryAfterMs;
25221
+ this.recognizedEnvelope = recognizedEnvelope;
25034
25222
  this.name = "DeliveryHttpError";
25035
25223
  }
25036
25224
  status;
25037
25225
  code;
25038
25226
  retryAfterMs;
25227
+ recognizedEnvelope;
25039
25228
  };
25040
25229
  DeliveryProtocolError = class extends Error {
25041
25230
  constructor(message) {
@@ -25043,6 +25232,18 @@ var init_delivery = __esm({
25043
25232
  this.name = "DeliveryProtocolError";
25044
25233
  }
25045
25234
  };
25235
+ DeliveryResponseError = class extends DeliveryProtocolError {
25236
+ constructor(message) {
25237
+ super(message);
25238
+ this.name = "DeliveryResponseError";
25239
+ }
25240
+ };
25241
+ DeliveryMalformedResponseError = class extends DeliveryResponseError {
25242
+ constructor(message) {
25243
+ super(message);
25244
+ this.name = "DeliveryMalformedResponseError";
25245
+ }
25246
+ };
25046
25247
  DeliveryCommandClient = class {
25047
25248
  constructor(target2, fetcher = fetch, options = {}) {
25048
25249
  this.target = target2;
@@ -46583,6 +46784,9 @@ function formatIdlePollDuration(ms) {
46583
46784
  if (ms % 1e3 === 0) return `${ms / 1e3}s`;
46584
46785
  throw new Error("idle poll duration must be a whole number of seconds");
46585
46786
  }
46787
+ function formatIdleWaitDuration(ms) {
46788
+ return formatIdlePollDuration(Math.ceil(ms / 1e3) * 1e3);
46789
+ }
46586
46790
  function idlePollDurationExamples() {
46587
46791
  const midMs = Math.min(IDLE_POLL_MAX_MS, IDLE_POLL_DEFAULT_MS * 2);
46588
46792
  const labels = [];
@@ -46631,7 +46835,7 @@ function nextIdlePollMs(baseMs, emptyStreak, maxMs = IDLE_POLL_MAX_MS) {
46631
46835
  return Math.min(maxMs, grown);
46632
46836
  }
46633
46837
  function idlePollStatusSentence(currentMs) {
46634
- return `Current idle poll interval: ${formatIdlePollDuration(currentMs)}.`;
46838
+ return `Current idle poll interval: ${formatIdleWaitDuration(currentMs)}.`;
46635
46839
  }
46636
46840
  function idlePollHelpSentence(defaultMs = IDLE_POLL_DEFAULT_MS) {
46637
46841
  return `listen start --poll-interval sets how long the listener waits after an empty claim (default ${formatIdlePollDuration(defaultMs)}). A whole number plus s or m (for example ${idlePollDurationHint()}), ${idlePollBoundSentence()}. Empty polls double that wait up to ${IDLE_POLL_MAX_LABEL}; any delivery resets it to the configured interval.`;
@@ -46699,13 +46903,13 @@ function listenerWakePersistWorthy(previous, next, lastPersistMs, nowMs) {
46699
46903
  function listenerWakeStatusSentence(wake, pollIntervalMs, lastWakeLabel) {
46700
46904
  if (wake.mode === LISTENER_WAKE_MODE_PUSH) {
46701
46905
  const last = lastWakeLabel === null ? "no wake yet" : `last wake ${lastWakeLabel}`;
46702
- return `${LISTENER_WAKE_MODE_PUSH} (Realtime), ${last}, reconcile every ${formatIdlePollDuration(LISTENER_RECONCILE_POLL_MS)}.`;
46906
+ return `${LISTENER_WAKE_MODE_PUSH} (Realtime), ${last}, reconcile every ${formatIdleWaitDuration(pollIntervalMs)}.`;
46703
46907
  }
46704
46908
  if (wake.errorCode === WAKE_ERROR_CODE_WAKE_BUDGET) {
46705
- return `Subscribed; claims paused until the minute clears (${WAKE_ERROR_CODE_WAKE_BUDGET}); polling every ${formatIdlePollDuration(pollIntervalMs)} meanwhile.`;
46909
+ return `Subscribed; claims paused until the minute clears (${WAKE_ERROR_CODE_WAKE_BUDGET}); polling every ${formatIdleWaitDuration(pollIntervalMs)} meanwhile.`;
46706
46910
  }
46707
46911
  const code = wake.errorCode ?? "disconnected";
46708
- return `${LISTENER_WAKE_MODE_POLL} every ${formatIdlePollDuration(pollIntervalMs)}. Realtime not connected (${code}).`;
46912
+ return `${LISTENER_WAKE_MODE_POLL} every ${formatIdleWaitDuration(pollIntervalMs)}. Realtime not connected (${code}).`;
46709
46913
  }
46710
46914
  function createWakeSubscriber(options) {
46711
46915
  return new WakeSubscriber(options);
@@ -50845,12 +51049,15 @@ __export(cli_exports, {
50845
51049
  agentToolsForTransport: () => agentToolsForTransport,
50846
51050
  clampTurnBudgetToCredential: () => clampTurnBudgetToCredential,
50847
51051
  claudeUserPromptHookSnippet: () => claudeUserPromptHookSnippet,
51052
+ collectListenerAttendanceEvidence: () => collectListenerAttendanceEvidence,
50848
51053
  describeAudience: () => describeAudience,
50849
51054
  formatBodySourceConflict: () => formatBodySourceConflict,
50850
51055
  formatBodySourceMissing: () => formatBodySourceMissing,
50851
51056
  formatBodyUsage: () => formatBodyUsage,
50852
51057
  formatOrList: () => formatOrList,
50853
51058
  isCliMain: () => isCliMain,
51059
+ isFollowRenewalCredentialFailure: () => isFollowRenewalCredentialFailure,
51060
+ listenerAttendanceProjectDirectory: () => listenerAttendanceProjectDirectory,
50854
51061
  listenerFailureMessage: () => listenerFailureMessage,
50855
51062
  listenerHostLimits: () => listenerHostLimits,
50856
51063
  listenerMainHostLimits: () => listenerMainHostLimits,
@@ -50858,6 +51065,8 @@ __export(cli_exports, {
50858
51065
  listenerPollIntervalMs: () => listenerPollIntervalMs,
50859
51066
  listenerProviderInstallEvidence: () => listenerProviderInstallEvidence,
50860
51067
  listenerRouteConfiguration: () => listenerRouteConfiguration,
51068
+ listenerSettingsHookInstalled: () => listenerSettingsHookInstalled,
51069
+ listenerStartPendingMessage: () => listenerStartPendingMessage,
50861
51070
  listenerStatusJson: () => listenerStatusJson,
50862
51071
  messageFormatAdvisory: () => messageFormatAdvisory,
50863
51072
  postSignalAllowedFlags: () => postSignalAllowedFlags,
@@ -57436,6 +57645,7 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
57436
57645
  init_idle_poll();
57437
57646
  init_wake2();
57438
57647
  var LISTENER_PAGE_LIMIT = 100;
57648
+ var LISTENER_CLAIM_REFUSALS_BEFORE_READ = 3;
57439
57649
  var LISTENER_IDLE_POLL_MS = IDLE_POLL_DEFAULT_MS;
57440
57650
  var LISTENER_IDLE_POLL_MAX_MS = IDLE_POLL_MAX_MS;
57441
57651
  var LISTENER_DELIVERY_SAFETY_MARGIN_MS = 3e4;
@@ -57446,8 +57656,71 @@ var LISTENER_DELIVERY_HOLD_BUDGET_MS = LISTENER_PROMPT_TIMEOUT_MS;
57446
57656
  var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
57447
57657
  var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
57448
57658
  var LISTENER_HOST_PORTS_PROBE_MS = 6e4;
57659
+ var CREDENTIAL_LOSS_CONFIRM_MIN_CHECKS = 3;
57660
+ var CREDENTIAL_LOSS_CONFIRM_INTERVAL_MS = 5 * 6e4;
57661
+ var RENEWAL_WINDOW_RETRY_MS = 3e4;
57662
+ var LISTENER_REQUEST_WAIT_FLOOR_MS = 1e3;
57663
+ var RENEWAL_PENDING_WRITE_ALLOWANCE_MS = 5e3;
57664
+ var RENEWAL_SERVER_CLOCK_LEAD_ALLOWANCE_MS = 3e4;
57665
+ var RENEWAL_EXPIRY_HEADROOM_MS = 8e3;
57666
+ var RENEWAL_WINDOW_EXPIRY_MARGIN_MS = 2 * RENEW_TIMEOUT_MS + RENEWAL_SERVER_CLOCK_LEAD_ALLOWANCE_MS + 2 * LISTENER_REQUEST_WAIT_FLOOR_MS + 2 * RENEWAL_PENDING_WRITE_ALLOWANCE_MS + RENEWAL_EXPIRY_HEADROOM_MS;
57667
+ var CREDENTIAL_LOSS_CONFIRM_WINDOW_MS = (CREDENTIAL_LOSS_CONFIRM_MIN_CHECKS - 1) * CREDENTIAL_LOSS_CONFIRM_INTERVAL_MS;
57668
+ var READ_FATAL_ANSWERS = Object.freeze({
57669
+ credentialCodes: CONFIRMED_CREDENTIAL_LOSS_CODES,
57670
+ configurationCode: "delivery_configuration_missing",
57671
+ refusals: Object.freeze([
57672
+ [400, "invalid_request"],
57673
+ [404, "channel_not_found"]
57674
+ ])
57675
+ });
57676
+ var COMMAND_FATAL_ANSWERS = Object.freeze({
57677
+ credentialCodes: COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES,
57678
+ h0FenceCode: H0_SEAT_CLAIM_REFUSED_CODE,
57679
+ refusals: Object.freeze([
57680
+ [400, "invalid_request"],
57681
+ [409, "command_id_conflict"],
57682
+ [409, "delivery_ack_conflict"],
57683
+ [409, "delivery_not_surfaced"],
57684
+ [413, "payload_too_large"],
57685
+ // command/index.ts version gate: the compiled client version cannot change on retry.
57686
+ [426, "upgrade_required"],
57687
+ [403, H0_SEAT_CLAIM_REFUSED_CODE]
57688
+ ])
57689
+ });
57690
+ function exactRefusal(set, status, code) {
57691
+ return code !== null && set.some(([s, c]) => s === status && c === code);
57692
+ }
57693
+ function fatalReadRefusal(error2) {
57694
+ const http = followHttpDetails(error2);
57695
+ return http !== null && exactRefusal(
57696
+ READ_FATAL_ANSWERS.refusals,
57697
+ http.status,
57698
+ followErrorEnvelope(error2).error
57699
+ );
57700
+ }
57701
+ function fatalCommandRefusal(error2) {
57702
+ return error2.recognizedEnvelope && exactRefusal(
57703
+ COMMAND_FATAL_ANSWERS.refusals,
57704
+ error2.status,
57705
+ error2.code
57706
+ );
57707
+ }
57708
+ var ListenerH0SeatError = class extends Error {
57709
+ code = H0_SEAT_CLAIM_REFUSED_CODE;
57710
+ constructor() {
57711
+ super(H0_SEAT_LISTENER_STOP_SENTENCE);
57712
+ this.name = "ListenerH0SeatError";
57713
+ }
57714
+ };
57449
57715
  var UUID_RE18 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
57450
- var ListenerCapabilityError = class extends Error {
57716
+ var ListenerCapabilityError = class _ListenerCapabilityError extends Error {
57717
+ static CODES = Object.freeze([
57718
+ "sender_relation_capability_missing",
57719
+ "cursor_capability_missing",
57720
+ "delivery_capability_inconsistent",
57721
+ "delivery_configuration_missing"
57722
+ ]);
57723
+ static READ_EDGE_CODES = _ListenerCapabilityError.CODES.filter((code) => code !== "delivery_configuration_missing");
57451
57724
  code;
57452
57725
  constructor(code, message) {
57453
57726
  super(message);
@@ -57455,6 +57728,9 @@ var ListenerCapabilityError = class extends Error {
57455
57728
  this.code = code;
57456
57729
  }
57457
57730
  };
57731
+ var ListenerLeaseResponseError = class extends Error {
57732
+ name = "ListenerLeaseResponseError";
57733
+ };
57458
57734
  var NullListenerModel = class {
57459
57735
  async start() {
57460
57736
  throw new Error("listener never starts a model");
@@ -57472,39 +57748,98 @@ function isRestartableListenerStop(stop) {
57472
57748
  return isRestartableRuntimeError(stop.error);
57473
57749
  }
57474
57750
  function isRestartableRuntimeError(error2) {
57751
+ if (error2 instanceof ListenerH0SeatError || error2 instanceof DeliveryHttpError && error2.recognizedEnvelope && error2.code === COMMAND_FATAL_ANSWERS.h0FenceCode) {
57752
+ return false;
57753
+ }
57475
57754
  if (error2 instanceof DeliveryTransportError) return true;
57476
57755
  if (error2 instanceof DeliveryHttpError) {
57477
- return error2.status === 429 || error2.status >= 500;
57756
+ if (isConfirmedCredentialHttpFailure(error2.status, error2.code, "command")) {
57757
+ return false;
57758
+ }
57759
+ return !fatalCommandRefusal(error2);
57478
57760
  }
57761
+ if (error2 instanceof DeliveryResponseError) return true;
57762
+ if (error2 instanceof DeliveryMalformedResponseError) return true;
57763
+ if (error2 instanceof ListenerLeaseResponseError) return true;
57479
57764
  if (error2 instanceof DeliveryProtocolError) return false;
57765
+ if (error2 instanceof RenewalRetryError) return true;
57480
57766
  if (error2 instanceof CommandTransportError) return true;
57481
57767
  if (error2 instanceof CommandHttpError) {
57482
- return error2.status === 429 || error2.status >= 500;
57768
+ if (isConfirmedCredentialHttpFailure(error2.status, error2.code, "command")) {
57769
+ return false;
57770
+ }
57771
+ return error2.status === 429 || error2.status >= 500 || error2.status === 401 || error2.status === 403;
57483
57772
  }
57484
57773
  if (error2 instanceof AcpHostError) return TRANSIENT_ACP_CODES.has(error2.code);
57485
- if (error2 instanceof ListenerCapabilityError) return false;
57774
+ if (error2 instanceof ListenerCapabilityError) {
57775
+ return error2.code !== READ_FATAL_ANSWERS.configurationCode;
57776
+ }
57486
57777
  if (error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalRevoked) {
57487
57778
  return false;
57488
57779
  }
57489
- return isRestartableReadError(error2);
57780
+ return !fatalReadRefusal(error2) && (isRestartableReadError(error2) || isForeignReadResponseFailure(error2));
57781
+ }
57782
+ function isForeignReadResponseFailure(error2) {
57783
+ if (error2 instanceof ListenerCapabilityError) {
57784
+ return error2.code !== READ_FATAL_ANSWERS.configurationCode;
57785
+ }
57786
+ if (classifySignalReadFailure(error2).code === "malformed_response") return true;
57787
+ const http = followHttpDetails(error2);
57788
+ return http !== null && !fatalReadRefusal(error2) && !isConfirmedCredentialHttpFailure(http.status, followErrorEnvelope(error2).error, "read");
57490
57789
  }
57491
57790
  function isAbort(error2) {
57492
57791
  return error2 instanceof Error && error2.name === "AbortError";
57493
57792
  }
57494
- function isCredentialLoss(error2) {
57495
- if (error2 instanceof CommandHttpError) {
57496
- return error2.status === 401 || error2.status === 403;
57497
- }
57793
+ function isLocalCredentialLoss(error2) {
57498
57794
  if (error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalRevoked) {
57499
57795
  return true;
57500
57796
  }
57797
+ if (followHttpDetails(error2) !== null || error2 instanceof CommandHttpError || error2 instanceof DeliveryHttpError || error2 instanceof SignalHttpError) {
57798
+ return false;
57799
+ }
57501
57800
  return isFollowCredentialFailure(error2);
57502
57801
  }
57503
- function isDeliveryCredentialLoss(error2) {
57504
- return isCredentialLoss(error2) || error2 instanceof DeliveryHttpError && (error2.status === 401 || error2.status === 403);
57802
+ function isServerConfirmedCredentialLoss(error2) {
57803
+ if (error2 instanceof RenewalCredentialCheckError) {
57804
+ return isConfirmedCredentialHttpFailure(error2.status, error2.code, "command");
57805
+ }
57806
+ if (error2 instanceof CommandHttpError || error2 instanceof DeliveryHttpError) {
57807
+ return isConfirmedCredentialHttpFailure(error2.status, error2.code, "command");
57808
+ }
57809
+ if (error2 instanceof SignalHttpError) {
57810
+ return isConfirmedCredentialHttpFailure(
57811
+ error2.status,
57812
+ error2.envelope.error,
57813
+ "read"
57814
+ );
57815
+ }
57816
+ return isFollowCredentialFailure(error2) && followErrorEnvelope(error2).error !== null;
57817
+ }
57818
+ function isH0SeatClaimRefusal(error2) {
57819
+ return error2 instanceof DeliveryHttpError && error2.recognizedEnvelope && error2.status === 403 && error2.code === COMMAND_FATAL_ANSWERS.h0FenceCode;
57820
+ }
57821
+ function isForeignDeliveryHttpResponse(error2) {
57822
+ return !error2.recognizedEnvelope;
57823
+ }
57824
+ function deliveryRetryCode(error2) {
57825
+ if (error2 instanceof RenewalRetryError) return error2.code;
57826
+ if (error2 instanceof DeliveryResponseError) return "malformed_response";
57827
+ if (error2 instanceof DeliveryHttpError) {
57828
+ return isForeignDeliveryHttpResponse(error2) ? `http_${error2.status}` : error2.code;
57829
+ }
57830
+ return "delivery_unreachable";
57505
57831
  }
57506
57832
  function isRetryableDeliveryError(error2) {
57507
- return error2 instanceof DeliveryTransportError || error2 instanceof DeliveryHttpError && (error2.status === 429 || error2.status >= 500);
57833
+ if (error2 instanceof RenewalRetryError) return true;
57834
+ if (error2 instanceof DeliveryTransportError) return true;
57835
+ if (error2 instanceof DeliveryResponseError) return true;
57836
+ if (error2 instanceof DeliveryMalformedResponseError) return true;
57837
+ if (!(error2 instanceof DeliveryHttpError)) return false;
57838
+ if (fatalCommandRefusal(error2)) return false;
57839
+ if (isConfirmedCredentialHttpFailure(error2.status, error2.code, "command")) {
57840
+ return false;
57841
+ }
57842
+ return true;
57508
57843
  }
57509
57844
  function deliveryRetryDelay(attempt, error2, random) {
57510
57845
  const exponent = Math.min(20, Math.max(0, attempt - 1));
@@ -57512,7 +57847,7 @@ function deliveryRetryDelay(attempt, error2, random) {
57512
57847
  LISTENER_DELIVERY_RETRY_MAX_MS,
57513
57848
  LISTENER_DELIVERY_RETRY_INITIAL_MS * 2 ** exponent
57514
57849
  );
57515
- const jitter = Math.floor(Math.max(0, Math.min(1, random())) * ceiling);
57850
+ const jitter = Math.floor((0.5 + Math.max(0, Math.min(1, random())) * 0.5) * ceiling);
57516
57851
  const retryAfter = error2 instanceof DeliveryHttpError && error2.status === 429 ? error2.retryAfterMs ?? 0 : 0;
57517
57852
  return Math.max(jitter, retryAfter);
57518
57853
  }
@@ -57592,13 +57927,13 @@ async function defaultSleep(ms, signal) {
57592
57927
  function requireCapabilities(page) {
57593
57928
  if (!page.capabilities.senderOwnerRelation) {
57594
57929
  throw new ListenerCapabilityError(
57595
- "sender_relation_capability_missing",
57930
+ ListenerCapabilityError.CODES[0],
57596
57931
  "the read service does not prove sender ownership; refusing to wake a model"
57597
57932
  );
57598
57933
  }
57599
57934
  if (!page.capabilities.cursorAfter || page.legacyCursorFallback) {
57600
57935
  throw new ListenerCapabilityError(
57601
- "cursor_capability_missing",
57936
+ ListenerCapabilityError.CODES[1],
57602
57937
  "the read service does not support lossless ascending inbox pages; refusing to wake a model"
57603
57938
  );
57604
57939
  }
@@ -57607,13 +57942,13 @@ function classifyDeliveryMode(page, durableConfigured) {
57607
57942
  const { deliveryClaim, deliveryAck } = page.capabilities;
57608
57943
  if (deliveryClaim && !deliveryAck) {
57609
57944
  throw new ListenerCapabilityError(
57610
- "delivery_capability_inconsistent",
57945
+ ListenerCapabilityError.CODES[2],
57611
57946
  "the read service delivery capability is inconsistent"
57612
57947
  );
57613
57948
  }
57614
57949
  if ((deliveryClaim || deliveryAck) && !durableConfigured) {
57615
57950
  throw new ListenerCapabilityError(
57616
- "delivery_configuration_missing",
57951
+ ListenerCapabilityError.CODES[3],
57617
57952
  "durable delivery configuration is required by the read service"
57618
57953
  );
57619
57954
  }
@@ -57687,7 +58022,36 @@ async function closeBeforeStart(model, error2) {
57687
58022
  }
57688
58023
  async function runListenerRuntime(options) {
57689
58024
  const now = options.now ?? Date.now;
57690
- const sleep2 = options.sleep ?? defaultSleep;
58025
+ const rawSleep = options.sleep ?? defaultSleep;
58026
+ let waitGeneration = 0;
58027
+ const renewalDeadline = () => {
58028
+ const expiry = options.credentialSession.expiry;
58029
+ return expiry === null || expiry === void 0 ? null : expiry - RENEWAL_WINDOW_EXPIRY_MARGIN_MS;
58030
+ };
58031
+ const renewalWaitBoundary = () => {
58032
+ const expiry = options.credentialSession.expiry;
58033
+ if (expiry === null || expiry === void 0 || now() >= expiry) return null;
58034
+ const dueAt = options.credentialSession.renewalAt;
58035
+ if (dueAt === null) return null;
58036
+ if (dueAt !== void 0 && now() < dueAt) return dueAt;
58037
+ if (dueAt === void 0 && options.credentialSession.renewalDue === false) return null;
58038
+ return renewalDeadline();
58039
+ };
58040
+ const capWaitMs = (ms) => {
58041
+ const boundary = renewalWaitBoundary();
58042
+ const wait = boundary === null ? ms : Math.min(ms, Math.max(0, boundary - now()));
58043
+ return Math.max(LISTENER_REQUEST_WAIT_FLOOR_MS, wait);
58044
+ };
58045
+ const mayWaitForWake = () => {
58046
+ const boundary = renewalWaitBoundary();
58047
+ return boundary === null || now() < boundary;
58048
+ };
58049
+ const sleep2 = async (ms, signal) => {
58050
+ const capped = capWaitMs(ms);
58051
+ if (capped <= 0) return;
58052
+ await rawSleep(capped, signal);
58053
+ if (!signal?.aborted) waitGeneration += 1;
58054
+ };
57691
58055
  const random = options.random ?? Math.random;
57692
58056
  const pageLimit = options.pageLimit ?? LISTENER_PAGE_LIMIT;
57693
58057
  const pollMs = options.pollMs ?? LISTENER_IDLE_POLL_MS;
@@ -57696,9 +58060,29 @@ async function runListenerRuntime(options) {
57696
58060
  const deferOverChars = options.deferOverChars ?? null;
57697
58061
  const deliveryHoldBudgetMs = options.deliveryHoldBudgetMs ?? LISTENER_DELIVERY_HOLD_BUDGET_MS;
57698
58062
  const abort = options.signal;
58063
+ let lastRequestWaitGeneration = -1;
58064
+ let requestGate = Promise.resolve();
58065
+ const paceRequest = async () => {
58066
+ const preceding = requestGate;
58067
+ let release;
58068
+ requestGate = new Promise((resolve7) => {
58069
+ release = resolve7;
58070
+ });
58071
+ await preceding;
58072
+ try {
58073
+ if (lastRequestWaitGeneration === waitGeneration) {
58074
+ await rawSleep(LISTENER_REQUEST_WAIT_FLOOR_MS, abort);
58075
+ if (!abort?.aborted) waitGeneration += 1;
58076
+ }
58077
+ if (abort?.aborted) throw new DOMException("listener stopped", "AbortError");
58078
+ lastRequestWaitGeneration = waitGeneration;
58079
+ } finally {
58080
+ release();
58081
+ }
58082
+ };
57699
58083
  const idleSleep = async (hadDelivery) => {
57700
58084
  if (hadDelivery) emptyIdleStreak = 0;
57701
- const intervalMs = nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
58085
+ const intervalMs = capWaitMs(nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS));
57702
58086
  if (!hadDelivery) emptyIdleStreak += 1;
57703
58087
  options.onEvent?.({
57704
58088
  type: "idle_poll",
@@ -57853,6 +58237,7 @@ async function runListenerRuntime(options) {
57853
58237
  let stop;
57854
58238
  let wakeSubscriber = options.wake ?? null;
57855
58239
  let reconcileDueAt = now();
58240
+ let plannedWakeUntil = null;
57856
58241
  const ensureWake = () => {
57857
58242
  if (wakeSubscriber === null) {
57858
58243
  wakeSubscriber = options.createWake ? options.createWake(options.target) : createWakeSubscriber({ target: options.target, now });
@@ -57880,6 +58265,102 @@ async function runListenerRuntime(options) {
57880
58265
  }
57881
58266
  return nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
57882
58267
  };
58268
+ const wakeWaitUntil = () => Math.max(
58269
+ now() + LISTENER_REQUEST_WAIT_FLOOR_MS,
58270
+ Math.min(reconcileDueAt, now() + waitCapMs(), renewalWaitBoundary() ?? Infinity)
58271
+ );
58272
+ let credentialWindow = null;
58273
+ const confirmedLossCode = (error2) => {
58274
+ if (error2 instanceof DeliveryHttpError || error2 instanceof CommandHttpError || error2 instanceof RenewalCredentialCheckError) {
58275
+ const code2 = error2.code;
58276
+ if (typeof code2 === "string" && /^[a-z0-9_-]{1,96}$/.test(code2)) return code2;
58277
+ }
58278
+ const code = followErrorEnvelope(error2).error;
58279
+ if (typeof code === "string" && /^[a-z0-9_-]{1,96}$/.test(code)) return code;
58280
+ return "unauthenticated";
58281
+ };
58282
+ const emitCredentialCheck = (delayMs) => {
58283
+ if (credentialWindow === null) return;
58284
+ options.onEvent?.({
58285
+ type: "credential_check",
58286
+ stopAt: new Date(credentialWindow.stopAtMs).toISOString(),
58287
+ checks: credentialWindow.checks,
58288
+ code: credentialWindow.code,
58289
+ edge: credentialWindow.edge,
58290
+ ...delayMs > 0 ? { nextAttemptAt: new Date(now() + delayMs).toISOString() } : {},
58291
+ ...options.credentialSession.expiry == null ? {} : { renewalExpiresAt: new Date(options.credentialSession.expiry).toISOString() },
58292
+ ts: eventTime(now)
58293
+ });
58294
+ };
58295
+ const projectCredentialStopAt = (atMs) => {
58296
+ if (credentialWindow === null) return atMs;
58297
+ const remaining = Math.max(
58298
+ 0,
58299
+ CREDENTIAL_LOSS_CONFIRM_MIN_CHECKS - credentialWindow.checks
58300
+ );
58301
+ return Math.max(
58302
+ credentialWindow.startedAtMs + CREDENTIAL_LOSS_CONFIRM_WINDOW_MS,
58303
+ atMs + remaining * credentialCheckDelayMs(atMs)
58304
+ );
58305
+ };
58306
+ const credentialCheckDelayMs = (atMs) => {
58307
+ const expiry = options.credentialSession.expiry;
58308
+ return expiry !== null && expiry !== void 0 && atMs < expiry ? RENEWAL_WINDOW_RETRY_MS : CREDENTIAL_LOSS_CONFIRM_INTERVAL_MS;
58309
+ };
58310
+ const clearCredentialWindow = () => {
58311
+ if (credentialWindow === null) return;
58312
+ credentialWindow = null;
58313
+ options.onEvent?.({
58314
+ type: "credential_check_cleared",
58315
+ ts: eventTime(now)
58316
+ });
58317
+ };
58318
+ const holdCredentialWindow = async (kind, error2) => {
58319
+ const atMs = now();
58320
+ const currentExpiry = options.credentialSession.expiry;
58321
+ if (currentExpiry !== null && currentExpiry !== void 0 && atMs >= currentExpiry) {
58322
+ return { reason: "credential", error: new RenewalRevoked(
58323
+ "predecessor_expired_local",
58324
+ "The current credential expired before it could be renewed. Ask whoever set this agent up for a new credential."
58325
+ ) };
58326
+ }
58327
+ if (kind === "confirmed") {
58328
+ if (credentialWindow === null) {
58329
+ credentialWindow = {
58330
+ startedAtMs: atMs,
58331
+ checks: 1,
58332
+ stopAtMs: atMs + CREDENTIAL_LOSS_CONFIRM_WINDOW_MS,
58333
+ code: confirmedLossCode(error2),
58334
+ edge: error2 instanceof DeliveryHttpError || error2 instanceof CommandHttpError || error2 instanceof RenewalCredentialCheckError ? "command" : "read"
58335
+ };
58336
+ } else {
58337
+ credentialWindow.checks += 1;
58338
+ credentialWindow.code = confirmedLossCode(error2);
58339
+ credentialWindow.edge = error2 instanceof DeliveryHttpError || error2 instanceof CommandHttpError || error2 instanceof RenewalCredentialCheckError ? "command" : "read";
58340
+ credentialWindow.stopAtMs = projectCredentialStopAt(atMs);
58341
+ }
58342
+ const window2 = credentialWindow;
58343
+ if (window2.checks >= CREDENTIAL_LOSS_CONFIRM_MIN_CHECKS && atMs >= window2.startedAtMs + CREDENTIAL_LOSS_CONFIRM_WINDOW_MS) {
58344
+ window2.stopAtMs = atMs;
58345
+ emitCredentialCheck(0);
58346
+ return { reason: "credential", error: asError(error2) };
58347
+ }
58348
+ } else if (credentialWindow !== null) {
58349
+ credentialWindow.stopAtMs = Math.max(
58350
+ credentialWindow.stopAtMs,
58351
+ projectCredentialStopAt(atMs)
58352
+ );
58353
+ } else {
58354
+ return "continue";
58355
+ }
58356
+ const delayMs = credentialCheckDelayMs(atMs);
58357
+ emitCredentialCheck(capWaitMs(delayMs));
58358
+ await sleep2(delayMs, abort);
58359
+ if (abort?.aborted) return { reason: "cancelled" };
58360
+ return "continue";
58361
+ };
58362
+ let ackAttempt = 0;
58363
+ let ackReadDue = false;
57883
58364
  const sendPreparedAck = async (active) => {
57884
58365
  if (active.phase !== "ack_pending" || active.signalId === null || active.leaseId === null || active.leasedUntil === null || active.ack === null) {
57885
58366
  return {
@@ -57893,10 +58374,11 @@ async function runListenerRuntime(options) {
57893
58374
  } catch (error2) {
57894
58375
  return { reason: "fatal", error: asError(error2) };
57895
58376
  }
57896
- let attempt = 0;
57897
58377
  while (true) {
57898
58378
  try {
58379
+ if (options.credentialSession.renewalDue === true) await paceRequest();
57899
58380
  const credential = await options.credentialSession.bearer();
58381
+ await paceRequest();
57900
58382
  await deliveryClient.ackAgentDelivery({
57901
58383
  workspaceId: options.workspaceId,
57902
58384
  credential,
@@ -57909,6 +58391,9 @@ async function runListenerRuntime(options) {
57909
58391
  });
57910
58392
  await options.deliveryJournal.clearActive(eventTime(now));
57911
58393
  after = null;
58394
+ ackAttempt = 0;
58395
+ clearCredentialWindow();
58396
+ options.onEvent?.({ type: "ack_retry_cleared", ts: eventTime(now) });
57912
58397
  options.onEvent?.({
57913
58398
  type: "delivery_ack",
57914
58399
  signalId: active.signalId,
@@ -57922,37 +58407,75 @@ async function runListenerRuntime(options) {
57922
58407
  try {
57923
58408
  await options.deliveryJournal.clearActive(eventTime(now));
57924
58409
  after = null;
58410
+ options.onEvent?.({ type: "ack_retry_cleared", ts: eventTime(now) });
57925
58411
  return null;
57926
58412
  } catch (clearError) {
57927
58413
  return { reason: "fatal", error: asError(clearError) };
57928
58414
  }
57929
58415
  }
57930
58416
  if (abort?.aborted) return { reason: "cancelled" };
57931
- if (isDeliveryCredentialLoss(error2)) {
58417
+ if (isH0SeatClaimRefusal(error2)) {
58418
+ return { reason: "fatal", error: new ListenerH0SeatError() };
58419
+ }
58420
+ if (isLocalCredentialLoss(error2)) {
57932
58421
  return { reason: "credential", error: asError(error2) };
57933
58422
  }
58423
+ if (isServerConfirmedCredentialLoss(error2)) {
58424
+ const decided = await holdCredentialWindow("confirmed", error2);
58425
+ if (decided !== "continue") return decided;
58426
+ continue;
58427
+ }
58428
+ if (credentialWindow !== null && isRetryableDeliveryError(error2)) {
58429
+ const decided = await holdCredentialWindow("transient", error2);
58430
+ if (decided !== "continue") return decided;
58431
+ continue;
58432
+ }
57934
58433
  if (!isRetryableDeliveryError(error2)) {
57935
58434
  return { reason: "fatal", error: asError(error2) };
57936
58435
  }
57937
- attempt += 1;
57938
- await sleep2(deliveryRetryDelay(attempt, error2, random), abort);
58436
+ ackAttempt += 1;
58437
+ const delayMs = capWaitMs(deliveryRetryDelay(ackAttempt, error2, random));
58438
+ options.onEvent?.({
58439
+ type: "ack_retry",
58440
+ code: deliveryRetryCode(error2),
58441
+ attempt: ackAttempt,
58442
+ delayMs,
58443
+ ...error2 instanceof RenewalRetryError && error2.expiresAt !== null ? { renewalExpiresAt: new Date(error2.expiresAt).toISOString() } : {},
58444
+ ts: eventTime(now)
58445
+ });
58446
+ await sleep2(delayMs, abort);
57939
58447
  if (abort?.aborted) return { reason: "cancelled" };
58448
+ if (ackAttempt % LISTENER_CLAIM_REFUSALS_BEFORE_READ === 0) {
58449
+ ackReadDue = true;
58450
+ return null;
58451
+ }
57940
58452
  }
57941
58453
  }
57942
58454
  };
57943
58455
  try {
58456
+ let forceRead = false;
58457
+ let claimRefusals = 0;
58458
+ let deliveryAttempt = 0;
57944
58459
  while (true) {
57945
58460
  if (abort?.aborted) {
57946
58461
  stop = { reason: "cancelled" };
57947
58462
  break;
57948
58463
  }
57949
58464
  let skipRead = false;
57950
- if (ready && deliveryMode === "durable_claim" && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
57951
- const until = Math.min(reconcileDueAt, now() + waitCapMs());
58465
+ if (ready && !forceRead && deliveryMode === "durable_claim" && wakeSubscriber !== null && wakeSubscriber.hasTopic && mayWaitForWake()) {
58466
+ const until = Math.max(
58467
+ now() + LISTENER_REQUEST_WAIT_FLOOR_MS,
58468
+ plannedWakeUntil ?? wakeWaitUntil()
58469
+ );
58470
+ plannedWakeUntil = null;
58471
+ const wakeWaitStartedAt = now();
57952
58472
  const reason = await wakeSubscriber.next({
57953
58473
  until,
57954
58474
  ...abort ? { signal: abort } : {}
57955
58475
  });
58476
+ const remainingFloorMs = LISTENER_REQUEST_WAIT_FLOOR_MS - (now() - wakeWaitStartedAt);
58477
+ if (remainingFloorMs > 0 && !abort?.aborted) await rawSleep(remainingFloorMs, abort);
58478
+ if (!abort?.aborted) waitGeneration += 1;
57956
58479
  emitWake();
57957
58480
  if (abort?.aborted) {
57958
58481
  stop = { reason: "cancelled" };
@@ -57969,11 +58492,17 @@ async function runListenerRuntime(options) {
57969
58492
  skipRead = true;
57970
58493
  }
57971
58494
  }
58495
+ } else {
58496
+ plannedWakeUntil = null;
57972
58497
  }
57973
58498
  let page = null;
58499
+ forceRead = false;
58500
+ ackReadDue = false;
57974
58501
  if (skipRead) {
57975
58502
  } else try {
58503
+ if (options.credentialSession.renewalDue === true) await paceRequest();
57976
58504
  const token = await options.credentialSession.bearer();
58505
+ await paceRequest();
57977
58506
  page = await readPage({
57978
58507
  token,
57979
58508
  after,
@@ -57990,9 +58519,14 @@ async function runListenerRuntime(options) {
57990
58519
  }
57991
58520
  });
57992
58521
  requireCapabilities(page);
58522
+ if (page.rawCount >= pageLimit && page.nextCursor === null) {
58523
+ throw new SignalMalformedError(
58524
+ "the read service returned a full page without a safe cursor"
58525
+ );
58526
+ }
57993
58527
  applyWakeHint(page.wake);
57994
58528
  emitWake();
57995
- if (ready && readEpisodeStartedAtMs !== null) {
58529
+ if (readEpisodeStartedAtMs !== null) {
57996
58530
  const recoveredAtMs = now();
57997
58531
  options.onEvent?.({
57998
58532
  type: "read_recovered",
@@ -58005,6 +58539,7 @@ async function runListenerRuntime(options) {
58005
58539
  readEpisodeAttempts = 0;
58006
58540
  }
58007
58541
  const nextMode = classifyDeliveryMode(page, durableConfigured);
58542
+ clearCredentialWindow();
58008
58543
  if (nextMode !== deliveryMode) {
58009
58544
  deliveryMode = nextMode;
58010
58545
  options.onEvent?.({
@@ -58020,32 +58555,52 @@ async function runListenerRuntime(options) {
58020
58555
  stop = { reason: "cancelled" };
58021
58556
  break;
58022
58557
  }
58023
- if (isCredentialLoss(error2)) {
58558
+ if (isLocalCredentialLoss(error2)) {
58024
58559
  stop = { reason: "credential", error: asError(error2) };
58025
58560
  break;
58026
58561
  }
58562
+ if (isServerConfirmedCredentialLoss(error2)) {
58563
+ const decided = await holdCredentialWindow("confirmed", error2);
58564
+ if (decided !== "continue") {
58565
+ stop = decided;
58566
+ break;
58567
+ }
58568
+ forceRead = true;
58569
+ continue;
58570
+ }
58027
58571
  const failure = classifySignalReadFailure(error2);
58028
- if (isRetryableFollowError(error2) || failure.code === "aborted" || failure.code === "host_ports_exhausted") {
58572
+ const transientRead = error2 instanceof RenewalRetryError || !fatalReadRefusal(error2) && (isRetryableFollowError(error2) || isForeignReadResponseFailure(error2) || failure.code === "aborted" || failure.code === "host_ports_exhausted");
58573
+ if (credentialWindow !== null && transientRead) {
58574
+ const decided = await holdCredentialWindow("transient", error2);
58575
+ if (decided !== "continue") {
58576
+ stop = decided;
58577
+ break;
58578
+ }
58579
+ forceRead = true;
58580
+ continue;
58581
+ }
58582
+ if (transientRead) {
58029
58583
  readAttempt += 1;
58030
- const delayMs = failure.code === "host_ports_exhausted" ? LISTENER_HOST_PORTS_PROBE_MS : nextFollowBackoffMs(readAttempt, null, random);
58031
- if (ready) {
58032
- const failedAtMs = now();
58033
- if (readEpisodeStartedAtMs === null) {
58034
- readEpisodeStartedAtMs = failedAtMs;
58035
- readEpisodeAttempts = 0;
58036
- }
58037
- readEpisodeAttempts += 1;
58038
- options.onEvent?.({
58039
- type: "read_retry",
58040
- attempt: readAttempt,
58041
- episodeAttempt: readEpisodeAttempts,
58042
- episodeStartedAt: new Date(readEpisodeStartedAtMs).toISOString(),
58043
- failure,
58044
- delayMs,
58045
- ts: new Date(failedAtMs).toISOString()
58046
- });
58584
+ const delayMs = capWaitMs(failure.code === "host_ports_exhausted" ? LISTENER_HOST_PORTS_PROBE_MS : nextFollowBackoffMs(readAttempt, null, random));
58585
+ const failedAtMs = now();
58586
+ if (readEpisodeStartedAtMs === null) {
58587
+ readEpisodeStartedAtMs = failedAtMs;
58588
+ readEpisodeAttempts = 0;
58047
58589
  }
58590
+ readEpisodeAttempts += 1;
58591
+ options.onEvent?.({
58592
+ type: "read_retry",
58593
+ attempt: readAttempt,
58594
+ episodeAttempt: readEpisodeAttempts,
58595
+ episodeStartedAt: new Date(readEpisodeStartedAtMs).toISOString(),
58596
+ failure,
58597
+ code: error2 instanceof RenewalRetryError ? error2.code : error2 instanceof ListenerCapabilityError ? error2.code : failure.code === "http_status" ? `http_${failure.httpStatus}` : failure.code,
58598
+ ...error2 instanceof RenewalRetryError && error2.expiresAt !== null ? { renewalExpiresAt: new Date(error2.expiresAt).toISOString() } : {},
58599
+ delayMs,
58600
+ ts: new Date(failedAtMs).toISOString()
58601
+ });
58048
58602
  await sleep2(delayMs, abort);
58603
+ forceRead = true;
58049
58604
  continue;
58050
58605
  }
58051
58606
  stop = { reason: "fatal", error: asError(error2) };
@@ -58065,7 +58620,9 @@ async function runListenerRuntime(options) {
58065
58620
  void (async () => {
58066
58621
  let declared = false;
58067
58622
  try {
58623
+ if (options.credentialSession.renewalDue === true) await paceRequest();
58068
58624
  const credential = await options.credentialSession.bearer();
58625
+ await paceRequest();
58069
58626
  const outcome = await declareAgentModel(options.target, {
58070
58627
  workspaceId: options.workspaceId,
58071
58628
  model: declaredLabel,
@@ -58108,6 +58665,10 @@ async function runListenerRuntime(options) {
58108
58665
  stop = { reason: "cancelled" };
58109
58666
  break;
58110
58667
  }
58668
+ if (ackReadDue) {
58669
+ forceRead = true;
58670
+ continue;
58671
+ }
58111
58672
  await idleSleep(true);
58112
58673
  continue;
58113
58674
  }
@@ -58152,6 +58713,7 @@ async function runListenerRuntime(options) {
58152
58713
  stop = ackStop;
58153
58714
  break;
58154
58715
  }
58716
+ if (ackReadDue) forceRead = true;
58155
58717
  continue;
58156
58718
  } catch (error2) {
58157
58719
  stop = { reason: "fatal", error: asError(error2) };
@@ -58216,11 +58778,12 @@ async function runListenerRuntime(options) {
58216
58778
  }
58217
58779
  }
58218
58780
  let result = null;
58219
- let deliveryAttempt = 0;
58220
58781
  while (result === null && !stop) {
58221
58782
  try {
58222
58783
  await journal.recordClaimAttempt(eventTime(now));
58784
+ if (options.credentialSession.renewalDue === true) await paceRequest();
58223
58785
  const credential = await options.credentialSession.bearer();
58786
+ await paceRequest();
58224
58787
  result = await deliveryClient.claimAgentInbox({
58225
58788
  workspaceId: options.workspaceId,
58226
58789
  credential,
@@ -58234,32 +58797,79 @@ async function runListenerRuntime(options) {
58234
58797
  stop = { reason: "cancelled" };
58235
58798
  break;
58236
58799
  }
58237
- if (isDeliveryCredentialLoss(error2)) {
58800
+ if (isH0SeatClaimRefusal(error2)) {
58801
+ stop = { reason: "fatal", error: new ListenerH0SeatError() };
58802
+ break;
58803
+ }
58804
+ if (isLocalCredentialLoss(error2)) {
58238
58805
  stop = { reason: "credential", error: asError(error2) };
58239
58806
  break;
58240
58807
  }
58808
+ if (isServerConfirmedCredentialLoss(error2)) {
58809
+ const decided = await holdCredentialWindow("confirmed", error2);
58810
+ if (decided !== "continue") {
58811
+ stop = decided;
58812
+ break;
58813
+ }
58814
+ continue;
58815
+ }
58241
58816
  if (error2 instanceof DeliveryHttpError && error2.code === "rate_limited") {
58242
58817
  wakeSubscriber?.markRateLimited(now());
58243
58818
  emitWake();
58244
58819
  }
58245
- if (!isRetryableDeliveryError(error2)) {
58820
+ const retryableClaim = isRetryableDeliveryError(error2);
58821
+ const delayMs = capWaitMs(retryableClaim ? credentialWindow !== null ? RENEWAL_WINDOW_RETRY_MS : deliveryRetryDelay(deliveryAttempt + 1, error2, random) : 0);
58822
+ if (retryableClaim) {
58823
+ claimRefusals += 1;
58824
+ options.onEvent?.({
58825
+ type: "claim_retry",
58826
+ code: deliveryRetryCode(error2),
58827
+ attempts: claimRefusals,
58828
+ delayMs: credentialWindow !== null ? capWaitMs(options.credentialSession.expiry == null ? CREDENTIAL_LOSS_CONFIRM_INTERVAL_MS : RENEWAL_WINDOW_RETRY_MS) : delayMs,
58829
+ ...error2 instanceof RenewalRetryError && error2.expiresAt !== null ? { renewalExpiresAt: new Date(error2.expiresAt).toISOString() } : {},
58830
+ ts: eventTime(now)
58831
+ });
58832
+ }
58833
+ if (credentialWindow !== null && retryableClaim) {
58834
+ const decided = await holdCredentialWindow("transient", error2);
58835
+ if (decided !== "continue") {
58836
+ stop = decided;
58837
+ break;
58838
+ }
58839
+ if (claimRefusals >= LISTENER_CLAIM_REFUSALS_BEFORE_READ) {
58840
+ forceRead = true;
58841
+ break;
58842
+ }
58843
+ continue;
58844
+ }
58845
+ if (!retryableClaim) {
58246
58846
  stop = { reason: "fatal", error: asError(error2) };
58247
58847
  break;
58248
58848
  }
58249
58849
  deliveryAttempt += 1;
58250
- const delayMs = deliveryRetryDelay(deliveryAttempt, error2, random);
58251
58850
  await sleep2(delayMs, abort);
58252
58851
  if (abort?.aborted) {
58253
58852
  stop = { reason: "cancelled" };
58254
58853
  break;
58255
58854
  }
58855
+ if (claimRefusals >= LISTENER_CLAIM_REFUSALS_BEFORE_READ) {
58856
+ forceRead = true;
58857
+ break;
58858
+ }
58256
58859
  }
58257
58860
  }
58258
58861
  if (stop) break;
58862
+ if (forceRead) continue;
58259
58863
  if (result === null) {
58260
58864
  stop = { reason: "fatal", error: new Error("delivery claim did not settle") };
58261
58865
  break;
58262
58866
  }
58867
+ if (claimRefusals > 0) {
58868
+ claimRefusals = 0;
58869
+ deliveryAttempt = 0;
58870
+ options.onEvent?.({ type: "claim_retry_cleared", ts: eventTime(now) });
58871
+ }
58872
+ clearCredentialWindow();
58263
58873
  applyWakeHint(result.wake);
58264
58874
  if (!skipRead && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
58265
58875
  wakeSubscriber.noteReconcile(now());
@@ -58288,7 +58898,7 @@ async function runListenerRuntime(options) {
58288
58898
  if (active.phase === "leased") {
58289
58899
  stop = {
58290
58900
  reason: "fatal",
58291
- error: new Error("delivery claim replay did not return the stored lease")
58901
+ error: new ListenerLeaseResponseError("delivery claim replay did not return the stored lease")
58292
58902
  };
58293
58903
  break;
58294
58904
  }
@@ -58300,12 +58910,14 @@ async function runListenerRuntime(options) {
58300
58910
  }
58301
58911
  if (wakeSubscriber !== null && wakeSubscriber.hasTopic) {
58302
58912
  const snap = wakeSubscriber.snapshot(now());
58303
- const intervalMs = snap.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
58913
+ plannedWakeUntil = wakeWaitUntil();
58914
+ const intervalMs = plannedWakeUntil - now();
58304
58915
  if (snap.mode === LISTENER_WAKE_MODE_PUSH) emptyIdleStreak = 0;
58305
58916
  else emptyIdleStreak += 1;
58306
58917
  options.onEvent?.({
58307
58918
  type: "idle_poll",
58308
58919
  intervalMs,
58920
+ pushReconcileWait: snap.mode === LISTENER_WAKE_MODE_PUSH && !skipRead,
58309
58921
  ts: eventTime(now)
58310
58922
  });
58311
58923
  emitWake();
@@ -58322,14 +58934,14 @@ async function runListenerRuntime(options) {
58322
58934
  });
58323
58935
  const leasedUntilMs = Date.parse(claimed.leasedUntil);
58324
58936
  if (!Number.isFinite(leasedUntilMs) || leasedUntilMs > now() + LISTENER_DELIVERY_MAX_LEASE_MS + LISTENER_LEASE_CLOCK_SKEW_ALLOWANCE_MS) {
58325
- stop = { reason: "fatal", error: new Error("delivery lease deadline is invalid") };
58937
+ stop = { reason: "fatal", error: new ListenerLeaseResponseError("delivery lease deadline is invalid") };
58326
58938
  break;
58327
58939
  }
58328
58940
  if (active.phase === "leased") {
58329
58941
  if (!exactRecoveredLease(active, claimed)) {
58330
58942
  stop = {
58331
58943
  reason: "fatal",
58332
- error: new Error("delivery claim replay does not match the stored lease")
58944
+ error: new ListenerLeaseResponseError("delivery claim replay does not match the stored lease")
58333
58945
  };
58334
58946
  break;
58335
58947
  }
@@ -58401,8 +59013,20 @@ async function runListenerRuntime(options) {
58401
59013
  } catch (error2) {
58402
59014
  if (abort?.aborted) {
58403
59015
  stop = { reason: "cancelled" };
58404
- } else if (isCredentialLoss(error2)) {
59016
+ } else if (isLocalCredentialLoss(error2)) {
58405
59017
  stop = { reason: "credential", error: asError(error2) };
59018
+ } else if (isServerConfirmedCredentialLoss(error2)) {
59019
+ const decided = await holdCredentialWindow("confirmed", error2);
59020
+ if (decided !== "continue") stop = decided;
59021
+ else {
59022
+ continue;
59023
+ }
59024
+ } else if (credentialWindow !== null && (isRetryableFollowError(error2) || isRetryableDeliveryError(error2))) {
59025
+ const decided = await holdCredentialWindow("transient", error2);
59026
+ if (decided !== "continue") stop = decided;
59027
+ else {
59028
+ continue;
59029
+ }
58406
59030
  } else if (isAbort(error2)) {
58407
59031
  stop = { reason: "cancelled" };
58408
59032
  } else {
@@ -58430,6 +59054,7 @@ async function runListenerRuntime(options) {
58430
59054
  stop = ackStop;
58431
59055
  break;
58432
59056
  }
59057
+ if (ackReadDue) forceRead = true;
58433
59058
  } catch (error2) {
58434
59059
  stop = { reason: "fatal", error: asError(error2) };
58435
59060
  break;
@@ -58490,10 +59115,28 @@ async function runListenerRuntime(options) {
58490
59115
  stop = { reason: "cancelled" };
58491
59116
  break;
58492
59117
  }
58493
- if (isCredentialLoss(error2)) {
59118
+ if (isLocalCredentialLoss(error2)) {
58494
59119
  stop = { reason: "credential", error: asError(error2) };
58495
59120
  break;
58496
59121
  }
59122
+ if (isServerConfirmedCredentialLoss(error2)) {
59123
+ const decided = await holdCredentialWindow("confirmed", error2);
59124
+ if (decided !== "continue") {
59125
+ stop = decided;
59126
+ break;
59127
+ }
59128
+ stop = { reason: "cancelled" };
59129
+ break;
59130
+ }
59131
+ if (credentialWindow !== null && (isRetryableFollowError(error2) || isRetryableDeliveryError(error2))) {
59132
+ const decided = await holdCredentialWindow("transient", error2);
59133
+ if (decided !== "continue") {
59134
+ stop = decided;
59135
+ break;
59136
+ }
59137
+ stop = { reason: "cancelled" };
59138
+ break;
59139
+ }
58497
59140
  if (isAbort(error2)) {
58498
59141
  stop = { reason: "cancelled" };
58499
59142
  break;
@@ -58502,18 +59145,13 @@ async function runListenerRuntime(options) {
58502
59145
  break;
58503
59146
  }
58504
59147
  }
59148
+ if (stop?.reason === "cancelled" && abort?.aborted !== true && credentialWindow !== null) {
59149
+ stop = void 0;
59150
+ continue;
59151
+ }
58505
59152
  if (stop) break;
58506
59153
  const fullPage = page.rawCount >= pageLimit;
58507
59154
  if (fullPage) {
58508
- if (page.nextCursor === null) {
58509
- stop = {
58510
- reason: "fatal",
58511
- error: new Error(
58512
- "the read service returned a full page without a safe cursor"
58513
- )
58514
- };
58515
- break;
58516
- }
58517
59155
  after = page.nextCursor;
58518
59156
  continue;
58519
59157
  }
@@ -58882,6 +59520,19 @@ var MAX_CONTROL_BYTES = 8 * 1024;
58882
59520
  var CONTROL_TIMEOUT_MS = 2e3;
58883
59521
  var START_LOCK_WAIT_MS = 2e3;
58884
59522
  var START_LOCK_STALE_MS = 1e4;
59523
+ var LISTENER_RUNNING_STATES = [
59524
+ "starting",
59525
+ "ready",
59526
+ "credential_check",
59527
+ "claim_retry",
59528
+ "ack_retry",
59529
+ "stopping"
59530
+ ];
59531
+ var LISTENER_STATUS_STATES = [
59532
+ ...LISTENER_RUNNING_STATES,
59533
+ "stopped",
59534
+ "failed"
59535
+ ];
58885
59536
  var LISTENER_DELIVERY_FAILING_THRESHOLD = 3;
58886
59537
  var ListenerAlreadyRunningError = class extends Error {
58887
59538
  constructor() {
@@ -58962,7 +59613,16 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
58962
59613
  "activityPublishFailures",
58963
59614
  "activityLastErrorCode",
58964
59615
  "idlePollMs",
58965
- "wake"
59616
+ "pushReconcileWaitMs",
59617
+ "wake",
59618
+ "nextAttemptAt",
59619
+ "credentialStopAt",
59620
+ "renewalExpiresAt",
59621
+ "credentialCheckEdge",
59622
+ "claimRetryCount",
59623
+ "projectDirectory",
59624
+ "targetUrl",
59625
+ "lastRetryEdge"
58966
59626
  ]);
58967
59627
  var STATUS_ACTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
58968
59628
  "activity_credential_failed",
@@ -59081,9 +59741,16 @@ function parseStatus(raw, rejectUnknownKeys = false) {
59081
59741
  const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
59082
59742
  const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
59083
59743
  const wake = row.wake === void 0 ? void 0 : parseListenerWake(row.wake, rejectUnknownKeys);
59084
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE19.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE19.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE19.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path15.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path15.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE19.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE19.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || typeof row.routeMode === "string" && isStoredListenerRouteMode(row.routeMode)) || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || wake === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
59744
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE19.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE19.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE19.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !LISTENER_STATUS_STATES.includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path15.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path15.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE19.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE19.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || typeof row.routeMode === "string" && isStoredListenerRouteMode(row.routeMode)) || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || wake === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
59085
59745
  row.activityLastErrorCode
59086
- )) || !(row.idlePollMs === void 0 || row.idlePollMs === null || typeof row.idlePollMs === "number" && Number.isSafeInteger(row.idlePollMs) && row.idlePollMs >= 0)) {
59746
+ )) || !(row.idlePollMs === void 0 || row.idlePollMs === null || typeof row.idlePollMs === "number" && Number.isSafeInteger(row.idlePollMs) && row.idlePollMs >= 0) || !(row.pushReconcileWaitMs === void 0 || row.pushReconcileWaitMs === null || typeof row.pushReconcileWaitMs === "number" && Number.isSafeInteger(row.pushReconcileWaitMs) && row.pushReconcileWaitMs >= 0) || !(row.nextAttemptAt === void 0 || nullableTimestamp3(row.nextAttemptAt)) || !(row.credentialStopAt === void 0 || nullableTimestamp3(row.credentialStopAt)) || !(row.renewalExpiresAt === void 0 || nullableTimestamp3(row.renewalExpiresAt)) || !(row.credentialCheckEdge === void 0 || row.credentialCheckEdge === null || row.credentialCheckEdge === "read" || row.credentialCheckEdge === "command") || !(row.claimRetryCount === void 0 || typeof row.claimRetryCount === "number" && Number.isSafeInteger(row.claimRetryCount) && row.claimRetryCount >= 0) || !(row.projectDirectory === void 0 || typeof row.projectDirectory === "string" && (0, import_node_path15.isAbsolute)(row.projectDirectory)) || !(row.targetUrl === void 0 || typeof row.targetUrl === "string" && (() => {
59747
+ try {
59748
+ const url = new URL(row.targetUrl);
59749
+ return (url.protocol === "https:" || url.protocol === "http:") && url.origin === row.targetUrl && !url.username && !url.password;
59750
+ } catch {
59751
+ return false;
59752
+ }
59753
+ })()) || !(row.lastRetryEdge === void 0 || row.lastRetryEdge === "read" || row.lastRetryEdge === "command" || row.lastRetryEdge === "local")) {
59087
59754
  throw new Error("stored listener status is malformed");
59088
59755
  }
59089
59756
  const routeMode = row.routeMode ?? "worker";
@@ -59122,12 +59789,14 @@ function parseStatus(raw, rejectUnknownKeys = false) {
59122
59789
  providerVersion: row.providerVersion ?? null,
59123
59790
  providerLastMeasuredVersion: row.providerLastMeasuredVersion ?? null,
59124
59791
  ...row.cswarmVersion === void 0 ? {} : { cswarmVersion: row.cswarmVersion },
59792
+ ...row.renewalExpiresAt === void 0 ? {} : { renewalExpiresAt: row.renewalExpiresAt ?? null },
59125
59793
  routeMode,
59126
59794
  deferOverChars,
59127
59795
  pendingForMainCount: row.pendingForMainCount ?? 0,
59128
59796
  droppedForMainCount: row.droppedForMainCount ?? 0,
59129
59797
  ...readHealth === void 0 ? {} : { readHealth },
59130
59798
  ...row.idlePollMs === void 0 ? {} : { idlePollMs: row.idlePollMs ?? null },
59799
+ ...row.pushReconcileWaitMs === void 0 ? {} : { pushReconcileWaitMs: row.pushReconcileWaitMs ?? null },
59131
59800
  ...wake === void 0 ? {} : { wake }
59132
59801
  };
59133
59802
  }
@@ -59540,7 +60209,9 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
59540
60209
 
59541
60210
  // src/listener/supervisor.ts
59542
60211
  var import_node_crypto20 = require("node:crypto");
60212
+ init_signals();
59543
60213
  init_delivery();
60214
+ init_command_client();
59544
60215
  init_credential_redaction();
59545
60216
  init_session_proof();
59546
60217
  init_types2();
@@ -59549,6 +60220,8 @@ var UUID_RE20 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-
59549
60220
  var LISTENER_RESTART_MAX_ATTEMPTS = 5;
59550
60221
  var LISTENER_RESTART_INITIAL_MS = 1e3;
59551
60222
  var LISTENER_RESTART_MAX_MS = 6e4;
60223
+ var LISTENER_RESTART_SUSTAINED_MAX_MS = 5 * 6e4;
60224
+ var LISTENER_RESTART_CLEAN_RUN_MS = 6e4;
59552
60225
  function nextListenerRestartMs(attempt, policy = {}, random = Math.random) {
59553
60226
  const initial = policy.initialMs ?? LISTENER_RESTART_INITIAL_MS;
59554
60227
  const max = policy.maxMs ?? LISTENER_RESTART_MAX_MS;
@@ -59556,6 +60229,17 @@ function nextListenerRestartMs(attempt, policy = {}, random = Math.random) {
59556
60229
  const exp = Math.min(max, initial * 2 ** (safeAttempt - 1));
59557
60230
  return Math.floor(exp * (0.5 + random() * 0.5));
59558
60231
  }
60232
+ function sustainedListenerRestartMs(policy = {}, random = Math.random) {
60233
+ const requested = policy.sustainedMaxMs ?? LISTENER_RESTART_SUSTAINED_MAX_MS;
60234
+ const cap = Math.min(Math.max(0, requested), 5 * 6e4);
60235
+ return Math.floor(cap * (0.5 + random() * 0.5));
60236
+ }
60237
+ function listenerRestartDelayMs(attempt, policy = {}, random = Math.random) {
60238
+ if (policy.maxAttempts === void 0 && attempt > LISTENER_RESTART_MAX_ATTEMPTS) {
60239
+ return sustainedListenerRestartMs(policy, random);
60240
+ }
60241
+ return nextListenerRestartMs(attempt, policy, random);
60242
+ }
59559
60243
  async function defaultRestartSleep(ms, signal) {
59560
60244
  if (ms <= 0 || signal.aborted) return;
59561
60245
  await new Promise((resolve7) => {
@@ -59585,9 +60269,18 @@ function safeErrorCode(error2) {
59585
60269
  const normalized = explicit.toLowerCase().replace(/[^a-z0-9_-]+/g, "_");
59586
60270
  if (normalized.length > 0) return normalized.slice(0, 96);
59587
60271
  }
60272
+ const read = classifySignalReadFailure(error2);
60273
+ if (read.code === "http_status") return `http_${read.httpStatus}`;
60274
+ if (read.code === "malformed_response") return read.code;
60275
+ if (error2 instanceof DeliveryResponseError) return "malformed_response";
59588
60276
  const name = error2.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "_");
59589
60277
  return name.slice(0, 96) || "listener_error";
59590
60278
  }
60279
+ function retryEdgeOf(error2) {
60280
+ if (error2 instanceof DeliveryHttpError || error2 instanceof DeliveryResponseError || error2 instanceof DeliveryTransportError || error2 instanceof CommandHttpError || error2 instanceof CommandTransportError) return "command";
60281
+ if (classifySignalReadFailure(error2).code !== "unclassified") return "read";
60282
+ return "local";
60283
+ }
59591
60284
  function localDiagnostic(message, maxChars) {
59592
60285
  const redacted = message.replace(
59593
60286
  new RegExp(SECRET_SHAPE_RE.source, "gi"),
@@ -59658,6 +60351,8 @@ async function runListenerSupervisor(options) {
59658
60351
  profileId: options.profileId,
59659
60352
  workspaceId: options.workspaceId.toLowerCase(),
59660
60353
  principalId: options.principalId.toLowerCase(),
60354
+ ...options.projectDirectory ? { projectDirectory: options.projectDirectory } : {},
60355
+ ...options.targetUrl ? { targetUrl: options.targetUrl } : {},
59661
60356
  pid: process.pid,
59662
60357
  state: "starting",
59663
60358
  startedAt,
@@ -59704,9 +60399,16 @@ async function runListenerSupervisor(options) {
59704
60399
  activityPublishFailures: 0,
59705
60400
  activityLastErrorCode: null,
59706
60401
  idlePollMs: null,
60402
+ pushReconcileWaitMs: null,
59707
60403
  wake: emptyListenerWakeStatus(),
60404
+ nextAttemptAt: null,
60405
+ credentialStopAt: null,
60406
+ credentialCheckEdge: null,
60407
+ claimRetryCount: 0,
59708
60408
  logPath: options.paths.logPath
59709
60409
  };
60410
+ let lastClaimRetryCode = null;
60411
+ let lastAckRetryCode = null;
59710
60412
  let writes = Promise.resolve();
59711
60413
  const chain = (work) => {
59712
60414
  writes = writes.then(work).catch(() => void 0);
@@ -59778,6 +60480,7 @@ async function runListenerSupervisor(options) {
59778
60480
  return fitted.length > 0 ? fitted : null;
59779
60481
  };
59780
60482
  let lastWakePersistMs = 0;
60483
+ let attemptReadyAtMs = null;
59781
60484
  const onEvent = (event) => {
59782
60485
  if (event.type === "wake") {
59783
60486
  const previousWake = status.wake;
@@ -59825,9 +60528,10 @@ async function runListenerSupervisor(options) {
59825
60528
  status = {
59826
60529
  ...status,
59827
60530
  idlePollMs: event.intervalMs,
60531
+ pushReconcileWaitMs: event.pushReconcileWait ? event.intervalMs : status.pushReconcileWaitMs,
59828
60532
  readHealth: recordListenerClaimCadence(
59829
60533
  status.readHealth ?? emptyListenerReadHealth(),
59830
- event.intervalMs > 0 ? event.intervalMs : 1,
60534
+ status.wake?.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : event.intervalMs > 0 ? event.intervalMs : 1,
59831
60535
  event.ts
59832
60536
  ),
59833
60537
  updatedAt: event.ts
@@ -59841,9 +60545,13 @@ async function runListenerSupervisor(options) {
59841
60545
  return;
59842
60546
  }
59843
60547
  if (event.type === "ready") {
60548
+ attemptReadyAtMs = now();
59844
60549
  const versionNotice = options.getProviderVersionNotice?.() ?? null;
59845
60550
  transition("ready", {
59846
60551
  readyAt: event.ts,
60552
+ nextAttemptAt: null,
60553
+ credentialStopAt: null,
60554
+ renewalExpiresAt: null,
59847
60555
  // Deliberately does NOT clear consecutiveAckFailureCount. Reaching
59848
60556
  // `ready` is not provider proof: the permission canary is its own
59849
60557
  // prompt, and a provider can answer it and fail every real one --
@@ -59908,9 +60616,97 @@ async function runListenerSupervisor(options) {
59908
60616
  });
59909
60617
  return;
59910
60618
  }
60619
+ if (event.type === "credential_check") {
60620
+ transition("credential_check", {
60621
+ credentialStopAt: event.stopAt,
60622
+ credentialCheckEdge: event.edge,
60623
+ renewalExpiresAt: event.renewalExpiresAt ?? null,
60624
+ lastErrorCode: event.code,
60625
+ lastErrorDetail: null,
60626
+ lastErrorReasonCode: null,
60627
+ nextAttemptAt: event.nextAttemptAt ?? null
60628
+ });
60629
+ log({
60630
+ ts: event.ts,
60631
+ event: "listener_credential_check",
60632
+ failure_code: event.code,
60633
+ attempt: event.checks,
60634
+ reason: event.stopAt
60635
+ });
60636
+ return;
60637
+ }
60638
+ if (event.type === "credential_check_cleared") {
60639
+ transition(status.claimRetryCount && status.claimRetryCount > 0 ? "claim_retry" : status.readyAt === null ? "starting" : "ready", {
60640
+ credentialStopAt: null,
60641
+ credentialCheckEdge: null,
60642
+ renewalExpiresAt: null,
60643
+ lastErrorCode: status.claimRetryCount && status.claimRetryCount > 0 ? lastClaimRetryCode : null,
60644
+ lastRetryEdge: status.claimRetryCount && status.claimRetryCount > 0 ? "command" : void 0,
60645
+ lastErrorDetail: null,
60646
+ lastErrorReasonCode: null,
60647
+ nextAttemptAt: null
60648
+ });
60649
+ log({
60650
+ ts: event.ts,
60651
+ event: "listener_credential_check_cleared"
60652
+ });
60653
+ return;
60654
+ }
60655
+ if (event.type === "claim_retry") {
60656
+ lastClaimRetryCode = event.code;
60657
+ transition(status.credentialStopAt ? "credential_check" : "claim_retry", {
60658
+ lastRetryEdge: "command",
60659
+ claimRetryCount: event.attempts,
60660
+ lastErrorCode: status.credentialStopAt ? status.lastErrorCode : event.code,
60661
+ renewalExpiresAt: event.renewalExpiresAt ?? null,
60662
+ lastErrorDetail: null,
60663
+ nextAttemptAt: new Date(Date.parse(event.ts) + event.delayMs).toISOString()
60664
+ });
60665
+ log({ ts: event.ts, event: "listener_claim_retry", failure_code: event.code, attempt: event.attempts });
60666
+ return;
60667
+ }
60668
+ if (event.type === "claim_retry_cleared") {
60669
+ lastClaimRetryCode = null;
60670
+ transition(status.credentialStopAt ? "credential_check" : status.readyAt === null ? "starting" : "ready", {
60671
+ claimRetryCount: 0,
60672
+ lastErrorCode: status.credentialStopAt ? status.lastErrorCode : null,
60673
+ lastErrorDetail: null,
60674
+ nextAttemptAt: null,
60675
+ renewalExpiresAt: null
60676
+ });
60677
+ return;
60678
+ }
60679
+ if (event.type === "ack_retry") {
60680
+ lastAckRetryCode = event.code;
60681
+ transition(status.credentialStopAt ? "credential_check" : "ack_retry", {
60682
+ lastRetryEdge: "command",
60683
+ lastErrorCode: status.credentialStopAt ? status.lastErrorCode : event.code,
60684
+ renewalExpiresAt: event.renewalExpiresAt ?? null,
60685
+ lastErrorDetail: null,
60686
+ nextAttemptAt: new Date(Date.parse(event.ts) + event.delayMs).toISOString()
60687
+ });
60688
+ log({ ts: event.ts, event: "listener_ack_retry", failure_code: event.code, attempt: event.attempt });
60689
+ return;
60690
+ }
60691
+ if (event.type === "ack_retry_cleared") {
60692
+ lastAckRetryCode = null;
60693
+ if (status.state === "ack_retry") {
60694
+ transition(status.readyAt === null ? "starting" : "ready", {
60695
+ lastErrorCode: null,
60696
+ lastErrorDetail: null,
60697
+ nextAttemptAt: null,
60698
+ renewalExpiresAt: null
60699
+ });
60700
+ }
60701
+ return;
60702
+ }
59911
60703
  if (event.type === "read_retry") {
59912
60704
  status = {
59913
60705
  ...status,
60706
+ lastErrorCode: event.code,
60707
+ renewalExpiresAt: event.renewalExpiresAt ?? null,
60708
+ lastRetryEdge: "read",
60709
+ nextAttemptAt: new Date(Date.parse(event.ts) + event.delayMs).toISOString(),
59914
60710
  readHealth: recordListenerReadRetry(
59915
60711
  status.readHealth ?? emptyListenerReadHealth(),
59916
60712
  {
@@ -59929,6 +60725,7 @@ async function runListenerSupervisor(options) {
59929
60725
  attempt: event.attempt,
59930
60726
  episode_attempt: event.episodeAttempt,
59931
60727
  reason_code: event.failure.code,
60728
+ failure_code: event.code,
59932
60729
  ...event.failure.httpStatus === null ? {} : { http_status: event.failure.httpStatus },
59933
60730
  ...event.failure.errorConstructor === null ? {} : { error_constructor: event.failure.errorConstructor },
59934
60731
  delay_ms: event.delayMs
@@ -59938,6 +60735,12 @@ async function runListenerSupervisor(options) {
59938
60735
  if (event.type === "read_recovered") {
59939
60736
  status = {
59940
60737
  ...status,
60738
+ ...status.lastRetryEdge === "read" ? {
60739
+ nextAttemptAt: null,
60740
+ renewalExpiresAt: null,
60741
+ lastErrorCode: status.claimRetryCount && status.claimRetryCount > 0 ? lastClaimRetryCode : status.state === "ack_retry" ? lastAckRetryCode : null,
60742
+ lastRetryEdge: status.claimRetryCount && status.claimRetryCount > 0 || status.state === "ack_retry" ? "command" : void 0
60743
+ } : {},
59941
60744
  readHealth: recordListenerReadRecovery(
59942
60745
  status.readHealth ?? emptyListenerReadHealth(),
59943
60746
  {
@@ -60141,16 +60944,18 @@ async function runListenerSupervisor(options) {
60141
60944
  });
60142
60945
  };
60143
60946
  const policy = options.restart ?? {};
60144
- const maxAttempts = policy.maxAttempts ?? LISTENER_RESTART_MAX_ATTEMPTS;
60947
+ const explicitCeiling = policy.maxAttempts;
60145
60948
  const isRestartable = policy.isRestartable ?? isRestartableListenerStop;
60146
60949
  const restartSleep = policy.sleep ?? defaultRestartSleep;
60147
60950
  const restartRandom = policy.random ?? Math.random;
60951
+ const cleanRunMs = policy.cleanRunMs ?? LISTENER_RESTART_CLEAN_RUN_MS;
60148
60952
  try {
60149
60953
  let restarts = 0;
60150
60954
  let exhausted = false;
60151
60955
  let eligible = false;
60152
60956
  let stop;
60153
60957
  for (; ; ) {
60958
+ attemptReadyAtMs = null;
60154
60959
  stop = await options.run(
60155
60960
  controller.signal,
60156
60961
  onEvent,
@@ -60159,14 +60964,23 @@ async function runListenerSupervisor(options) {
60159
60964
  if (stop.reason === "cancelled" || controller.signal.aborted) break;
60160
60965
  eligible = isRestartable(stop);
60161
60966
  if (!eligible) break;
60162
- if (restarts >= maxAttempts) {
60967
+ const cleanForMs = attemptReadyAtMs === null ? 0 : Math.max(0, now() - attemptReadyAtMs);
60968
+ if (cleanForMs >= cleanRunMs) restarts = 0;
60969
+ if (explicitCeiling !== void 0 && restarts >= explicitCeiling) {
60163
60970
  exhausted = true;
60164
60971
  break;
60165
60972
  }
60166
60973
  restarts += 1;
60167
- const delayMs = nextListenerRestartMs(restarts, policy, restartRandom);
60974
+ const expiry = options.getCredentialExpiryMs?.() ?? null;
60975
+ const deadline = expiry === null ? null : expiry - RENEWAL_WINDOW_EXPIRY_MARGIN_MS;
60976
+ const renewalAt = options.getCredentialRenewalAt?.();
60977
+ const boundary = renewalAt !== null && expiry !== null && now() < expiry ? renewalAt !== void 0 && now() < renewalAt ? renewalAt : deadline : null;
60978
+ const proposedDelayMs = listenerRestartDelayMs(restarts, policy, restartRandom);
60979
+ const cappedDelayMs = boundary !== null ? Math.min(proposedDelayMs, Math.max(0, boundary - now())) : proposedDelayMs;
60980
+ const delayMs = Math.max(LISTENER_REQUEST_WAIT_FLOOR_MS, cappedDelayMs);
60168
60981
  const restartCode = safeErrorCode(stop.error);
60169
60982
  const restartStderrTail = takeTail();
60983
+ const nextAttemptAt = new Date(now() + delayMs).toISOString();
60170
60984
  log({
60171
60985
  ts: iso2(now),
60172
60986
  event: "listener_restarting",
@@ -60178,16 +60992,22 @@ async function runListenerSupervisor(options) {
60178
60992
  transition("starting", {
60179
60993
  readyAt: null,
60180
60994
  lastErrorCode: restartCode,
60995
+ lastRetryEdge: retryEdgeOf(stop.error),
60181
60996
  lastErrorDetail: safeErrorDetail(stop.error),
60182
60997
  ...providerFailureFields(stop.error),
60183
60998
  lastWorkerStderrTail: restartStderrTail,
60184
- ...providerStatusFields(options.getProviderVersionNotice?.() ?? null)
60999
+ ...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
61000
+ nextAttemptAt,
61001
+ credentialStopAt: null,
61002
+ credentialCheckEdge: null,
61003
+ claimRetryCount: 0
60185
61004
  });
60186
- await restartSleep(delayMs, controller.signal);
61005
+ if (delayMs > 0) await restartSleep(delayMs, controller.signal);
60187
61006
  if (controller.signal.aborted) {
60188
61007
  stop = { reason: "cancelled" };
60189
61008
  break;
60190
61009
  }
61010
+ transition("starting", { nextAttemptAt: null });
60191
61011
  }
60192
61012
  const stoppedAt = iso2(now);
60193
61013
  if (stop.reason === "cancelled") {
@@ -60197,11 +61017,16 @@ async function runListenerSupervisor(options) {
60197
61017
  lastErrorDetail: null,
60198
61018
  lastErrorReasonCode: null,
60199
61019
  lastWorkerStderrTail: null,
60200
- providerMinimumRequiredVersion: null
61020
+ providerMinimumRequiredVersion: null,
61021
+ nextAttemptAt: null,
61022
+ credentialStopAt: null,
61023
+ renewalExpiresAt: null,
61024
+ credentialCheckEdge: null,
61025
+ claimRetryCount: 0
60201
61026
  });
60202
61027
  log({ ts: stoppedAt, event: "listener_stopped" });
60203
61028
  } else {
60204
- const code = stop.reason === "credential" ? "credential_stopped" : safeErrorCode(stop.error);
61029
+ const code = stop.reason === "credential" ? stop.error instanceof ListenerCredentialStateMismatchError ? stop.error.code : "credential_stopped" : safeErrorCode(stop.error);
60205
61030
  const failedStderrTail = takeTail();
60206
61031
  transition("failed", {
60207
61032
  stoppedAt,
@@ -60209,7 +61034,12 @@ async function runListenerSupervisor(options) {
60209
61034
  lastErrorDetail: safeErrorDetail(stop.error),
60210
61035
  ...providerFailureFields(stop.error),
60211
61036
  ...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
60212
- lastWorkerStderrTail: failedStderrTail
61037
+ lastWorkerStderrTail: failedStderrTail,
61038
+ nextAttemptAt: null,
61039
+ credentialStopAt: null,
61040
+ renewalExpiresAt: null,
61041
+ credentialCheckEdge: stop.reason === "credential" && !(stop.error instanceof ListenerCredentialStateMismatchError) ? status.credentialCheckEdge ?? null : null,
61042
+ claimRetryCount: 0
60213
61043
  });
60214
61044
  log({
60215
61045
  ts: stoppedAt,
@@ -60237,7 +61067,11 @@ async function runListenerSupervisor(options) {
60237
61067
  error2 instanceof Error ? error2 : new Error(String(error2))
60238
61068
  ),
60239
61069
  ...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
60240
- lastWorkerStderrTail: failedStderrTail
61070
+ lastWorkerStderrTail: failedStderrTail,
61071
+ nextAttemptAt: null,
61072
+ credentialStopAt: null,
61073
+ credentialCheckEdge: null,
61074
+ claimRetryCount: 0
60241
61075
  });
60242
61076
  log({
60243
61077
  ts: stoppedAt,
@@ -60256,7 +61090,7 @@ async function effectiveListenerStatus(paths) {
60256
61090
  return await queryListenerControl(paths, "status");
60257
61091
  } catch {
60258
61092
  const stored = await readListenerStatus(paths);
60259
- if (stored && (stored.state === "starting" || stored.state === "ready" || stored.state === "stopping")) {
61093
+ if (stored && LISTENER_RUNNING_STATES.includes(stored.state)) {
60260
61094
  const failed = {
60261
61095
  ...stored,
60262
61096
  state: "failed",
@@ -60269,7 +61103,9 @@ async function effectiveListenerStatus(paths) {
60269
61103
  says it is what the service reported. */
60270
61104
  currentDeliverySignalId: null,
60271
61105
  currentDeliverySince: null,
60272
- heldBackDeliveries: []
61106
+ heldBackDeliveries: [],
61107
+ credentialStopAt: null,
61108
+ nextAttemptAt: null
60273
61109
  };
60274
61110
  await writeListenerStatus(paths, failed);
60275
61111
  return failed;
@@ -60298,7 +61134,7 @@ async function waitForListenerReady(paths, options = {}) {
60298
61134
  if (options.expectedPid !== void 0 && last.pid !== options.expectedPid) {
60299
61135
  throw new ListenerAlreadyRunningError();
60300
61136
  }
60301
- if (last.state === "ready") return last;
61137
+ if (LISTENER_RUNNING_STATES.includes(last.state) && last.state !== "starting" && last.state !== "stopping") return last;
60302
61138
  if (last.state === "failed" || last.state === "stopped") {
60303
61139
  throw new ListenerStartupError(last.lastErrorCode ?? last.state);
60304
61140
  }
@@ -60324,6 +61160,7 @@ async function waitForListenerReady(paths, options = {}) {
60324
61160
  }
60325
61161
  await sleep2(pollMs);
60326
61162
  }
61163
+ if (last !== null && (options.expectedPid === void 0 || last.pid === options.expectedPid) && (!options.isProcessAlive || options.isProcessAlive()) && last.state === "starting") return last;
60327
61164
  throw new ListenerStartupError(last?.lastErrorCode ?? "ready_timeout");
60328
61165
  }
60329
61166
 
@@ -63873,8 +64710,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
63873
64710
  ]);
63874
64711
  var UUID_RE24 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
63875
64712
  function packageVersion() {
63876
- if ("0.1.72".length > 0) {
63877
- return "0.1.72";
64713
+ if ("0.1.73".length > 0) {
64714
+ return "0.1.73";
63878
64715
  }
63879
64716
  try {
63880
64717
  const value = JSON.parse(
@@ -65827,7 +66664,7 @@ function accepted(label, result) {
65827
66664
  );
65828
66665
  }
65829
66666
  }
65830
- async function agentSession(cloud, workspaceId2, agent, fetcher) {
66667
+ async function agentSession(cloud, workspaceId2, agent, fetcher, listenerMode = false) {
65831
66668
  let store2 = null;
65832
66669
  try {
65833
66670
  const candidate = await agentCredentialStore({
@@ -65854,6 +66691,7 @@ async function agentSession(cloud, workspaceId2, agent, fetcher) {
65854
66691
  expiresAt: agent.expiresAt
65855
66692
  },
65856
66693
  store: store2,
66694
+ listenerMode,
65857
66695
  ...fetcher ? { fetcher } : {}
65858
66696
  });
65859
66697
  }
@@ -65975,7 +66813,7 @@ function listenerRouteConfiguration(routeValue, deferOverValue) {
65975
66813
  }
65976
66814
  return { routeMode, deferOverChars: null };
65977
66815
  }
65978
- var TURN_BUDGET_CREDENTIAL_MARGIN_MS = 6e4;
66816
+ var TURN_BUDGET_CREDENTIAL_MARGIN_MS = RENEWAL_WINDOW_EXPIRY_MARGIN_MS;
65979
66817
  function clampTurnBudgetToCredential(budgetMs, credentialExpiresAt, nowMs) {
65980
66818
  if (credentialExpiresAt === null) return budgetMs;
65981
66819
  const horizonMs = credentialExpiresAt - nowMs - TURN_BUDGET_CREDENTIAL_MARGIN_MS;
@@ -67165,7 +68003,7 @@ async function runInboxFollowCommand(args) {
67165
68003
  signal: controller.signal,
67166
68004
  refusalToleranceMs,
67167
68005
  ...pageLimit === void 0 ? {} : { pageLimit },
67168
- isCredentialFailure: (error2) => isFollowCredentialFailure(error2) || error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalRevoked || error2 instanceof RenewalSuspended,
68006
+ isCredentialFailure: isFollowRenewalCredentialFailure,
67169
68007
  arm: async ({ after, limit }) => {
67170
68008
  const credential = selected.session ? { kind: "agent", token: await selected.session.bearer() } : signalCredentialOf(selected);
67171
68009
  if (credential.kind === "agent") renderedBearer = credential.token;
@@ -67428,9 +68266,9 @@ function emptyAttendanceEvidence() {
67428
68266
  }
67429
68267
  function listenerAttendanceState(status, evidence) {
67430
68268
  const pending = status.pendingForMainCount ?? 0;
67431
- const connected = status.state === "ready";
68269
+ const connected = LISTENER_RUNNING_STATES.includes(status.state) && status.state !== "starting" && status.state !== "stopping" && status.state !== "credential_check" && status.state !== "claim_retry" && status.state !== "ack_retry" && status.readHealth?.currentEpisodeStartedAt == null;
67432
68270
  const attendingSurfaces = evidence.attendingSurfaces ?? [];
67433
- const hasSurface = attendingSurfaces.length > 0;
68271
+ const hasSurface = evidence.hookSurfaceExists || attendingSurfaces.length > 0;
67434
68272
  const attendanceState = pending > 0 ? "unattended" : hasSurface && evidence.hookSurfaceAdvanced ? "attended" : hasSurface ? "unproven" : "unattended";
67435
68273
  const attended = attendanceState === "attended" ? true : attendanceState === "unattended" ? false : null;
67436
68274
  const lastAckOutcome = status.lastAckOutcome ?? null;
@@ -67455,26 +68293,25 @@ function listenerReadHealthSummary(status, nowMs) {
67455
68293
  );
67456
68294
  }
67457
68295
  function listenerLapseNotices(status, summary) {
68296
+ const down = status.state === "stopped" || status.state === "failed";
67458
68297
  const health = status.readHealth ?? emptyListenerReadHealth();
67459
68298
  const notices = [];
67460
- if (health.currentReasonCode === "host_ports_exhausted") {
68299
+ if (!down && health.currentReasonCode === "host_ports_exhausted") {
67461
68300
  notices.push({
67462
68301
  code: "listener_host_ports_exhausted",
67463
68302
  message: "This host has run out of outbound ports. The listener is probing only once per minute so it does not amplify the outage.",
67464
68303
  nextStep: "Find the consumer: lsof -nP -iTCP | awk '{print $1}' | sort | uniq -c | sort -rn"
67465
68304
  });
67466
- } else if (
67467
- // Reuse arrival-watch.ts's 60s loud-lapse transition. The listener keeps
67468
- // the episode in durable status instead of the monitor's process-local machine.
67469
- summary.currentEpisodeDurationMs !== null && summary.currentEpisodeDurationMs >= ARRIVAL_RETRY_NOTICE_THRESHOLD_MS
67470
- ) {
68305
+ } else if (!down && // Reuse arrival-watch.ts's 60s loud-lapse transition. The listener keeps
68306
+ // the episode in durable status instead of the monitor's process-local machine.
68307
+ summary.currentEpisodeDurationMs !== null && summary.currentEpisodeDurationMs >= ARRIVAL_RETRY_NOTICE_THRESHOLD_MS) {
67471
68308
  notices.push({
67472
68309
  code: "listener_read_retry_persisting",
67473
68310
  message: `Listener reads have failed continuously for ${Math.floor(summary.currentEpisodeDurationMs / 1e3)}s. This is still in progress.`,
67474
- nextStep: "Check cswarm status and the CommonSwarm service. If both are healthy, restart the listener."
68311
+ nextStep: "Leave the listener running while it waits for the read service; check the target URL and CommonSwarm service."
67475
68312
  });
67476
68313
  }
67477
- if (summary.throughputLapseHours.length > 0) {
68314
+ if (!down && summary.throughputLapseHours.length > 0) {
67478
68315
  const latest = summary.throughputLapseHours.at(-1);
67479
68316
  const pending = status.pendingDeliveryCount;
67480
68317
  const pendingClause = pending === null ? " No pending count was recorded." : ` Pending deliveries now: ${pending}.`;
@@ -67635,6 +68472,75 @@ function listenerStatusJson(status, permissionMode, evidence = emptyAttendanceEv
67635
68472
  host_limits: isLiveListenerRouteMode(status.routeMode ?? "main") ? listenerMainHostLimits() : listenerHostLimits(status.provider)
67636
68473
  };
67637
68474
  }
68475
+ var CSWARM_UPDATE_INSTALLER = "curl -fsSL https://commonswarm.com/install.sh | sh";
68476
+ var CSWARM_UPDATE_NPM = "npm install -g commonswarm";
68477
+ var CSWARM_UPGRADE_STOP = `The command edge requires a newer cswarm (upgrade_required). Update with ${CSWARM_UPDATE_INSTALLER} or ${CSWARM_UPDATE_NPM}. ${RENEWAL_UPGRADE_LISTENER_ACTION}`;
68478
+ function credentialStoppedSentence(edge = null) {
68479
+ const codes = (edge === "command" ? COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES : CONFIRMED_CREDENTIAL_LOSS_CODES).join(" or ");
68480
+ return `the server refused this credential (${codes} means revoked, expired, or unknown), a local renewal stop fired, or local credential state is missing. The listener has stopped and will not retry. Run cswarm whoami with this credential to see the grant state, then follow its next step`;
68481
+ }
68482
+ function credentialCheckSentence(status) {
68483
+ if (status.state !== "credential_check") return null;
68484
+ if (typeof status.credentialStopAt !== "string") return null;
68485
+ const codes = (status.credentialCheckEdge === "command" ? COMMAND_CONFIRMED_CREDENTIAL_LOSS_CODES : CONFIRMED_CREDENTIAL_LOSS_CODES).join(" or ");
68486
+ if (status.renewalExpiresAt && Date.parse(status.renewalExpiresAt) < Date.parse(status.credentialStopAt)) {
68487
+ return `The server refused this credential (${codes}). The listener is still running and retrying the credential check${status.nextAttemptAt ? ` at ${status.nextAttemptAt}` : ""}. The current token expires at ${status.renewalExpiresAt}; unless renewal succeeds first, the listener stops on the next renewal answer after expiry. Run cswarm whoami with this credential to see the grant state.`;
68488
+ }
68489
+ return `The server refused this credential (${codes}). The listener is still running. It will stop at ${status.credentialStopAt} if every check until then confirms the loss; a transient answer extends the check window. Run cswarm whoami with this credential to see the grant state.`;
68490
+ }
68491
+ function listenerRetrySentence(status) {
68492
+ if (status.lastErrorCode === "renewal_retry" && status.nextAttemptAt) {
68493
+ return `Credential renewal is retrying. The current token expires at ${status.renewalExpiresAt ?? "an unknown time"}. The listener will retry at ${status.nextAttemptAt} with backoff of at least one second. Reads and claims pause while renewal is unresolved because the successor may already have been issued. If renewal does not succeed before expiry, the listener stops and needs a new credential.`;
68494
+ }
68495
+ if (!LISTENER_RUNNING_STATES.includes(status.state) || typeof status.nextAttemptAt !== "string" || status.state !== "starting" && status.lastRetryEdge !== "read") {
68496
+ return null;
68497
+ }
68498
+ const code = status.lastErrorCode ?? "no code recorded";
68499
+ if (status.lastRetryEdge === "read" && (status.readHealth?.currentEpisodeAttempts ?? 0) > 0) {
68500
+ const failure = status.readHealth;
68501
+ const detail = failure.currentHttpStatus === null ? "" : ` (HTTP ${failure.currentHttpStatus})`;
68502
+ const next = ListenerCapabilityError.READ_EDGE_CODES.includes(code) ? `Check ${status.targetUrl ?? "the target URL"} and update the read edge before starting a model.` : `Check ${status.targetUrl ?? "the target URL"} and read edge version. Leave the listener running while the read service recovers.`;
68503
+ return `The read edge failed (${code}${detail}). The listener is still running and will try again at ${status.nextAttemptAt}. ${next}`;
68504
+ }
68505
+ return `The ${status.lastRetryEdge === "command" ? "command edge" : "last attempt"} failed (${code}). The listener is still running and will try again at ${status.nextAttemptAt}. Leave it running. To stop it now: cswarm listen stop --workspace-id ${status.workspaceId} --principal-id ${status.principalId}`;
68506
+ }
68507
+ function listenerDownSentence(status) {
68508
+ if (status.state === "stopped") {
68509
+ return `This listener is stopped and is not reading signals. Start it again by piping the same agent credential into: ${listenerRestartCommand(status)}`;
68510
+ }
68511
+ if (status.state !== "failed") return null;
68512
+ if (status.lastErrorCode === "credential_stopped") {
68513
+ return `This listener stopped because ${credentialStoppedSentence(status.credentialCheckEdge ?? null)}.`;
68514
+ }
68515
+ if (status.lastErrorCode === "local_credential_state_mismatch") {
68516
+ return "This listener stopped because its local credential state did not preserve the live credential. Check that its local state directory is writable and intact, then restart the listener with the credential.";
68517
+ }
68518
+ if (status.lastErrorCode === H0_SEAT_CLAIM_REFUSED_CODE) {
68519
+ return `${H0_SEAT_LISTENER_STOP_SENTENCE}.`;
68520
+ }
68521
+ if (status.lastErrorCode === "upgrade_required") return CSWARM_UPGRADE_STOP;
68522
+ const code = status.lastErrorCode ?? "no code recorded";
68523
+ return `This listener failed (${code}) and is not reading signals. Read ${status.logPath}, then restart it by piping the same agent credential into: ${listenerRestartCommand(status)}`;
68524
+ }
68525
+ function listenerDeliveryRetrySentence(status) {
68526
+ if (status.lastErrorCode === "renewal_retry") return null;
68527
+ if (status.lastRetryEdge === "read") return null;
68528
+ const when = status.nextAttemptAt ? ` at ${status.nextAttemptAt}` : " with backoff";
68529
+ const code = status.lastErrorCode ?? "no code recorded";
68530
+ if (status.state === "claim_retry") {
68531
+ if (DELIVERY_SESSION_PROOF_CODES.includes(code)) {
68532
+ return `The claim is refused (${code}); this managed seat needs a live session. CONNECTED is no while claims fail. Start or renew the seat's session, or stop the listener. The listener will try again${when}.`;
68533
+ }
68534
+ return `The claim failed (${code}) ${status.claimRetryCount ?? 0} times. ${code === "delivery_unreachable" ? "The server could not be reached." : "The command edge did not accept the claim."} The listener is running and will try again${when}, reading signals after repeated failures.`;
68535
+ }
68536
+ if (status.state === "ack_retry") {
68537
+ if (DELIVERY_SESSION_PROOF_CODES.includes(code)) {
68538
+ return `The delivery acknowledgement is refused (${code}); this managed seat needs a live session. CONNECTED is no while acknowledgements fail. Start or renew the seat's session, or stop the listener. The listener will try again${when}.`;
68539
+ }
68540
+ return `The delivery acknowledgement failed (${code}). The inbox is waiting on this acknowledgement. The listener will try again${when} and read signals after repeated failures.`;
68541
+ }
68542
+ return null;
68543
+ }
67638
68544
  function renderListenerStatus(status, evidence = emptyAttendanceEvidence(), nowMs = Date.now(), installed = null) {
67639
68545
  const routeMode = status.routeMode ?? "main";
67640
68546
  const deliveryFailureRun = status.consecutiveAckFailureCount ?? 0;
@@ -67645,13 +68551,24 @@ function renderListenerStatus(status, evidence = emptyAttendanceEvidence(), nowM
67645
68551
  const readHealth = status.readHealth ?? emptyListenerReadHealth();
67646
68552
  const readSummary = listenerReadHealthSummary(status, nowMs);
67647
68553
  const lapseNotices = listenerLapseNotices(status, readSummary);
68554
+ const down = status.state === "stopped" || status.state === "failed";
68555
+ const retrying = LISTENER_RUNNING_STATES.includes(status.state) && (status.state === "starting" || status.lastRetryEdge === "read") && typeof status.nextAttemptAt === "string";
68556
+ const credentialCheck = credentialCheckSentence(status);
68557
+ const retrySentence = listenerRetrySentence(status);
68558
+ const deliveryRetrySentence = listenerDeliveryRetrySentence(status);
68559
+ const downSentence = listenerDownSentence(status);
67648
68560
  const lines = [
67649
- lapseNotices.length > 0 ? `Listener LAPSE for agent ${status.principalId}: ${lapseNotices.map((notice) => notice.code).join(", ")}.` : pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
68561
+ down ? `Listener ${status.state} for agent ${status.principalId}.` : credentialCheck !== null ? `Listener credential check for agent ${status.principalId}.` : retrying ? `Listener retrying for agent ${status.principalId}.` : lapseNotices.length > 0 ? `Listener LAPSE for agent ${status.principalId}: ${lapseNotices.map((notice) => notice.code).join(", ")}.` : pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
68562
+ ...credentialCheck === null ? [] : [credentialCheck],
68563
+ ...deliveryRetrySentence === null ? [] : [deliveryRetrySentence],
68564
+ ...retrySentence === null ? [] : [retrySentence],
68565
+ ...downSentence === null ? [] : [downSentence],
67650
68566
  `CONNECTED: ${attendance.connected ? "yes" : "no"}. Transport state is ${status.state}.`,
67651
68567
  listenerAttendingSentence(evidence.attendingSurfaces ?? []),
67652
68568
  `ATTENDED: ${attendance.attendanceState === "attended" ? "yes. The session hook has surfaced messages on this host" : attendance.attendanceState === "unattended" ? (evidence.attendingSurfaces ?? []).length === 0 ? `no. ${LISTENER_NONE_ATTENDING_SENTENCE.replace(/\.$/, "")}` : "no. The main-session queue is not draining" : "not yet proven on this host"}.`,
67653
68569
  `HANDLED: ${attendance.handledState === "handled" ? `yes. The newest delivery acknowledgement was ${status.lastAckOutcome}` : attendance.handledState === "not_handled" ? routeMode === "worker" ? deliveryFailureRun >= LISTENER_DELIVERY_FAILING_THRESHOLD ? `no. ${deliveryFailureRun} ${deliveryFailureRun === 1 ? "delivery has" : "deliveries have"} failed since the last reply; the newest delivery acknowledgement was ${status.lastAckOutcome ?? "not recorded"}${status.lastErrorCode ? ` (${status.lastErrorCode})` : ""}` : `no. The newest delivery acknowledgement was ${status.lastAckOutcome ?? "not recorded"}${status.lastErrorCode ? ` (${status.lastErrorCode})` : ""}` : "no. Queued messages have not reached the session hook" : "not yet measured"}.`,
67654
68570
  `Provider: ${status.provider}; process: ${status.pid}; started: ${status.startedAt}.`,
68571
+ `Target URL: ${status.targetUrl ?? "not recorded"}.`,
67655
68572
  `Provider executable: ${status.providerExecutable ?? "not measured"}.`,
67656
68573
  `Connections opened: ${status.connectionsOpened ?? "not measured"}.`,
67657
68574
  `Connection reuse ratio: ${status.connectionReuseRatio ?? "not measured"}.`,
@@ -67668,7 +68585,7 @@ function renderListenerStatus(status, evidence = emptyAttendanceEvidence(), nowM
67668
68585
  status.idlePollMs === void 0 || status.idlePollMs === null ? "Current idle poll interval has not been reported yet." : idlePollStatusSentence(status.idlePollMs),
67669
68586
  listenerWakeStatusSentence(
67670
68587
  status.wake ?? emptyListenerWakeStatus(),
67671
- status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : IDLE_POLL_DEFAULT_MS,
68588
+ status.wake?.mode === LISTENER_WAKE_MODE_PUSH ? status.pushReconcileWaitMs && status.pushReconcileWaitMs > 0 ? status.pushReconcileWaitMs : LISTENER_RECONCILE_POLL_MS : status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : IDLE_POLL_DEFAULT_MS,
67672
68589
  status.wake?.lastWakeAt ? relativeAge(status.wake.lastWakeAt, nowMs) : null
67673
68590
  )
67674
68591
  ];
@@ -67704,7 +68621,7 @@ function renderListenerStatus(status, evidence = emptyAttendanceEvidence(), nowM
67704
68621
  }
67705
68622
  if (status.providerVersion && status.providerLastMeasuredVersion) {
67706
68623
  lines.push(
67707
- status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : status.state === "ready" ? `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It was measured before startup failed; compatibility was not established. Next: resolve the startup failure before verifying this provider release.`
68624
+ status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : LISTENER_RUNNING_STATES.includes(status.state) && status.readyAt !== null ? `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.` : status.state === "starting" ? `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is still starting; compatibility was not established. Next: check the listener status after it is ready.` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It was measured before startup failed; compatibility was not established. Next: resolve the startup failure before verifying this provider release.`
67708
68625
  );
67709
68626
  } else {
67710
68627
  lines.push("Provider version: not measured.");
@@ -67794,6 +68711,18 @@ function renderListenerStatus(status, evidence = emptyAttendanceEvidence(), nowM
67794
68711
  }
67795
68712
  return lines.join("\n");
67796
68713
  }
68714
+ function listenerStartPendingMessage(status) {
68715
+ if (status.state === "starting" && status.lastErrorCode) {
68716
+ const code = status.lastErrorCode;
68717
+ const capability = ListenerCapabilityError.READ_EDGE_CODES.includes(code);
68718
+ const target2 = status.targetUrl ?? "the target URL";
68719
+ if (status.lastRetryEdge !== "read") {
68720
+ return `Listener ${status.lastRetryEdge === "command" ? "command edge" : "startup"} failed (${code}); check ${target2}. Use cswarm listen status to follow retries.`;
68721
+ }
68722
+ return capability ? `Listener read edge failed (${code}); check ${target2}. ${listenerFailureMessage(code)}. Use cswarm listen status to follow retries.` : `Listener read edge failed (${code}); check ${target2} and read edge version. Use cswarm listen status to follow retries.`;
68723
+ }
68724
+ return "Listener is still starting or checking; use cswarm listen status to follow it.";
68725
+ }
67797
68726
  async function unsurfacedPendingMainStats(instanceDirectory, fallback) {
67798
68727
  try {
67799
68728
  const queue = new FilePendingMainQueue(instanceDirectory);
@@ -67841,7 +68770,8 @@ function listenerProviderIdentitySummary(status) {
67841
68770
  }
67842
68771
  return parts.join("; ");
67843
68772
  }
67844
- function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequiredVersion) {
68773
+ function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequiredVersion, credentialEdge = null) {
68774
+ if (code === "upgrade_required") return CSWARM_UPGRADE_STOP;
67845
68775
  if (code === "version_below_floor") {
67846
68776
  if (provider === "codex") {
67847
68777
  return "the Codex listener requires codex-acp 1.1.9 or newer; update the bridge, then retry";
@@ -67893,11 +68823,17 @@ function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequi
67893
68823
  if (code.startsWith("opencode_auth_") || code === "opencode_project_config_active" || code === "opencode_config_probe_failed") {
67894
68824
  return `OpenCode host safety check failed (${code}); re-authenticate and ensure OPENCODE_DISABLE_PROJECT_CONFIG keeps project allow from merging`;
67895
68825
  }
67896
- if (code === "sender_relation_capability_missing" || code === "cursor_capability_missing") {
68826
+ if (ListenerCapabilityError.READ_EDGE_CODES.includes(code)) {
67897
68827
  return `the deployed read service lacks the safe listener capability (${code}); update/deploy the read edge before starting a model`;
67898
68828
  }
67899
68829
  if (code === "credential_stopped") {
67900
- return "the agent credential expired, was revoked, reached its renewal horizon, or its grant was suspended; run cswarm whoami with this credential to see the grant state, then follow its next step";
68830
+ return credentialStoppedSentence(credentialEdge);
68831
+ }
68832
+ if (code === "local_credential_state_mismatch") {
68833
+ return "the listener's local credential state did not preserve the live credential; check the local state directory, then restart with the credential";
68834
+ }
68835
+ if (code === H0_SEAT_CLAIM_REFUSED_CODE) {
68836
+ return H0_SEAT_LISTENER_STOP_SENTENCE;
67901
68837
  }
67902
68838
  if (code === "permission_canary_failed") {
67903
68839
  if (provider === "claude") {
@@ -68053,7 +68989,8 @@ async function runConfiguredListener(options) {
68053
68989
  options.cloud,
68054
68990
  options.workspaceId,
68055
68991
  options.agent,
68056
- boundFetch
68992
+ boundFetch,
68993
+ true
68057
68994
  );
68058
68995
  } catch (error2) {
68059
68996
  if (managedContextPath !== null) {
@@ -68064,6 +69001,15 @@ async function runConfiguredListener(options) {
68064
69001
  }
68065
69002
  let storedCredential = null;
68066
69003
  const credentialSession = {
69004
+ get expiry() {
69005
+ return liveCredentialSession.expiry;
69006
+ },
69007
+ get renewalDue() {
69008
+ return liveCredentialSession.renewalDue;
69009
+ },
69010
+ get renewalAt() {
69011
+ return liveCredentialSession.renewalAt;
69012
+ },
68067
69013
  bearer: async () => {
68068
69014
  const credential = await liveCredentialSession.bearer();
68069
69015
  if (credential !== storedCredential) {
@@ -68078,7 +69024,7 @@ async function runConfiguredListener(options) {
68078
69024
  }
68079
69025
  const stored = await readListenerCredentialState(paths.instanceDirectory);
68080
69026
  if (stored === null || stored.credential !== credential) {
68081
- throw new Error("listener credential state did not preserve the live credential");
69027
+ throw new ListenerCredentialStateMismatchError();
68082
69028
  }
68083
69029
  return stored.credential;
68084
69030
  }
@@ -68215,11 +69161,15 @@ async function runConfiguredListener(options) {
68215
69161
  profileId: options.cloud.profileId,
68216
69162
  workspaceId: options.workspaceId,
68217
69163
  principalId: options.principalId,
69164
+ projectDirectory: options.cwd,
69165
+ targetUrl: options.cloud.url,
68218
69166
  provider: options.provider,
68219
69167
  cswarmVersion: CLI_BUILD_VERSION,
68220
69168
  permissionMode: options.permissionMode,
68221
69169
  routeMode,
68222
69170
  deferOverChars,
69171
+ getCredentialExpiryMs: () => credentialSession.expiry,
69172
+ getCredentialRenewalAt: () => credentialSession.renewalAt,
68223
69173
  // The bound a timeout event reports: the last turn's clamped budget when
68224
69174
  // one has run, else the configured cap.
68225
69175
  getTurnBudgetMs: () => lastAppliedTurnBudgetMs ?? turnBudgetMs,
@@ -68389,7 +69339,7 @@ async function runListenStart(args) {
68389
69339
  ...stateDirectory2 ? { stateDirectory: stateDirectory2 } : {}
68390
69340
  });
68391
69341
  const existing = await effectiveListenerStatus(paths);
68392
- if (existing && (existing.state === "starting" || existing.state === "ready" || existing.state === "stopping")) {
69342
+ if (existing && LISTENER_RUNNING_STATES.includes(existing.state)) {
68393
69343
  throw new Error(
68394
69344
  `a listener is already ${existing.state} for agent ${principalId}`
68395
69345
  );
@@ -68500,7 +69450,8 @@ async function runListenStart(args) {
68500
69450
  provider,
68501
69451
  detail,
68502
69452
  reasonCode,
68503
- failedStatus?.providerMinimumRequiredVersion
69453
+ failedStatus?.providerMinimumRequiredVersion,
69454
+ failedStatus?.credentialCheckEdge ?? null
68504
69455
  );
68505
69456
  throw new Error(
68506
69457
  failedStatus === null ? message : `${message}. ${listenerProviderIdentitySummary(failedStatus)}`
@@ -68516,7 +69467,8 @@ async function runListenStart(args) {
68516
69467
  provider,
68517
69468
  status.lastErrorDetail,
68518
69469
  status.lastErrorReasonCode,
68519
- status.providerMinimumRequiredVersion
69470
+ status.providerMinimumRequiredVersion,
69471
+ status.credentialCheckEdge ?? null
68520
69472
  )}. ${listenerProviderIdentitySummary(status)}`
68521
69473
  );
68522
69474
  }
@@ -68550,7 +69502,7 @@ async function runListenStart(args) {
68550
69502
  const hostNote = `--provider ${provider} names the attendance surface kind for this seat. ${workerAudience}
68551
69503
  `;
68552
69504
  process.stdout.write(
68553
- `${args.has("foreground") ? "Listener stopped." : (status.pendingForMainCount ?? 0) > 0 ? "Listener transport is connected, but queued messages are unattended." : "Listener is ready and will keep receiving after this command exits."}
69505
+ `${args.has("foreground") ? "Listener stopped." : LISTENER_RUNNING_STATES.includes(status.state) && status.state !== "ready" ? listenerStartPendingMessage(status) : (status.pendingForMainCount ?? 0) > 0 ? "Listener transport is connected, but queued messages are unattended." : "Listener is ready and will keep receiving after this command exits."}
68554
69506
  ${renderListenerStatus(status, attendanceEvidence)}
68555
69507
  The short credential rotates while this process remains alive and secure local state is available. Run cswarm whoami with this credential to see whether its grant is timeboxed or standing.
68556
69508
  ` + routingNote + hostNote + `Use listen status/stop with the same agent credential, --workspace-id ${workspaceId2}, and the same Cloud target. --principal-id ${principalId} remains available when no credential is supplied.
@@ -68693,7 +69645,7 @@ async function runListenStatusOrStop(args, command2) {
68693
69645
  };
68694
69646
  const attendanceEvidence = await collectListenerAttendanceEvidence({
68695
69647
  instanceDirectory: paths.instanceDirectory,
68696
- cwd: process.cwd(),
69648
+ cwd: listenerAttendanceProjectDirectory(status, process.cwd()),
68697
69649
  principalId,
68698
69650
  cloud,
68699
69651
  workspaceId: workspaceId2,
@@ -68995,15 +69947,13 @@ function settingsHaveScopedClaudeHook(settings, principalId) {
68995
69947
  );
68996
69948
  });
68997
69949
  }
68998
- async function listenerHookSurfacePresent(instanceDirectory, cwd, principalId) {
68999
- const surface = await new FileHookSurfaceStore(instanceDirectory).evidence();
69000
- if (surface.exists) return true;
69950
+ async function listenerSettingsHookInstalled(cwd, principalId) {
69001
69951
  const repositoryRoot = gitRepositoryRoot(cwd) ?? cwd;
69002
- const settingsPaths = /* @__PURE__ */ new Set([
69952
+ const settingsPaths = [
69003
69953
  (0, import_node_path24.join)(repositoryRoot, CLAUDE_PROJECT_SETTINGS_IGNORE_LINE),
69004
69954
  (0, import_node_path24.join)(repositoryRoot, CLAUDE_REPO_SETTINGS_IGNORE_LINE),
69005
69955
  userClaudeSettingsTarget().path
69006
- ]);
69956
+ ];
69007
69957
  for (const path of settingsPaths) {
69008
69958
  if (settingsHaveScopedClaudeHook(readClaudeSettings(path), principalId)) {
69009
69959
  return true;
@@ -69011,6 +69961,11 @@ async function listenerHookSurfacePresent(instanceDirectory, cwd, principalId) {
69011
69961
  }
69012
69962
  return false;
69013
69963
  }
69964
+ async function listenerHookSurfacePresent(instanceDirectory, cwd, principalId) {
69965
+ const surface = await new FileHookSurfaceStore(instanceDirectory).evidence();
69966
+ if (surface.exists) return true;
69967
+ return await listenerSettingsHookInstalled(cwd, principalId);
69968
+ }
69014
69969
  async function listenerWatcherSurfacePresent(cloud, workspaceId2, principalId) {
69015
69970
  return await arrivalWatchLockHeld(
69016
69971
  arrivalWatchLockPath(cloud, workspaceId2, principalId)
@@ -69029,9 +69984,11 @@ async function listenerHasAttendanceSurface(options) {
69029
69984
  options.principalId
69030
69985
  );
69031
69986
  }
69987
+ function listenerAttendanceProjectDirectory(status, callerDirectory) {
69988
+ return status.projectDirectory ?? callerDirectory;
69989
+ }
69032
69990
  async function collectListenerAttendanceEvidence(options) {
69033
- const hook = options.hookSurfaceExists || await listenerHookSurfacePresent(
69034
- options.instanceDirectory,
69991
+ const settingsHook = await listenerSettingsHookInstalled(
69035
69992
  options.cwd,
69036
69993
  options.principalId
69037
69994
  );
@@ -69042,10 +69999,10 @@ async function collectListenerAttendanceEvidence(options) {
69042
69999
  );
69043
70000
  return {
69044
70001
  pendingForMainOldestAt: options.pendingForMainOldestAt,
69045
- hookSurfaceExists: hook,
70002
+ hookSurfaceExists: options.hookSurfaceExists,
69046
70003
  hookSurfaceAdvanced: options.hookSurfaceAdvanced,
69047
70004
  watcherLockHeld: watcher,
69048
- attendingSurfaces: listenerAttendingSurfaces(hook, watcher)
70005
+ attendingSurfaces: listenerAttendingSurfaces(settingsHook, watcher)
69049
70006
  };
69050
70007
  }
69051
70008
  function claudeUserPromptHookSnippet(principalId) {
@@ -70678,6 +71635,9 @@ ${usage()}
70678
71635
  process.exitCode = exitCodeFor(error2);
70679
71636
  });
70680
71637
  }
71638
+ function isFollowRenewalCredentialFailure(error2) {
71639
+ return isFollowCredentialFailure(error2) || error2 instanceof RenewalReauthorisationRequired || error2 instanceof RenewalCredentialCheckError || error2 instanceof RenewalRevoked || error2 instanceof RenewalSuspended;
71640
+ }
70681
71641
  // Annotate the CommonJS export names for ESM import in node:
70682
71642
  0 && (module.exports = {
70683
71643
  AGENT_COMMANDS,
@@ -70711,12 +71671,15 @@ ${usage()}
70711
71671
  agentToolsForTransport,
70712
71672
  clampTurnBudgetToCredential,
70713
71673
  claudeUserPromptHookSnippet,
71674
+ collectListenerAttendanceEvidence,
70714
71675
  describeAudience,
70715
71676
  formatBodySourceConflict,
70716
71677
  formatBodySourceMissing,
70717
71678
  formatBodyUsage,
70718
71679
  formatOrList,
70719
71680
  isCliMain,
71681
+ isFollowRenewalCredentialFailure,
71682
+ listenerAttendanceProjectDirectory,
70720
71683
  listenerFailureMessage,
70721
71684
  listenerHostLimits,
70722
71685
  listenerMainHostLimits,
@@ -70724,6 +71687,8 @@ ${usage()}
70724
71687
  listenerPollIntervalMs,
70725
71688
  listenerProviderInstallEvidence,
70726
71689
  listenerRouteConfiguration,
71690
+ listenerSettingsHookInstalled,
71691
+ listenerStartPendingMessage,
70727
71692
  listenerStatusJson,
70728
71693
  messageFormatAdvisory,
70729
71694
  postSignalAllowedFlags,