commonswarm 0.1.56 → 0.1.58

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 +1040 -128
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -13512,6 +13512,7 @@ __export(cli_exports, {
13512
13512
  listenerFailureMessage: () => listenerFailureMessage,
13513
13513
  listenerHostLimits: () => listenerHostLimits,
13514
13514
  listenerPermissionMode: () => listenerPermissionMode,
13515
+ listenerPollIntervalMs: () => listenerPollIntervalMs,
13515
13516
  listenerProviderInstallEvidence: () => listenerProviderInstallEvidence,
13516
13517
  listenerRouteConfiguration: () => listenerRouteConfiguration,
13517
13518
  listenerStatusJson: () => listenerStatusJson,
@@ -13528,10 +13529,10 @@ module.exports = __toCommonJS(cli_exports);
13528
13529
  var import_node_crypto22 = require("node:crypto");
13529
13530
  var import_node_child_process9 = require("node:child_process");
13530
13531
  var import_node_fs7 = require("node:fs");
13531
- var import_promises12 = require("node:fs/promises");
13532
+ var import_promises13 = require("node:fs/promises");
13532
13533
  var import_node_os10 = require("node:os");
13533
13534
  var import_node_path21 = require("node:path");
13534
- var import_promises13 = require("node:readline/promises");
13535
+ var import_promises14 = require("node:readline/promises");
13535
13536
 
13536
13537
  // src/protocol/events.ts
13537
13538
  var SCHEMA_VERSION = 1;
@@ -26174,7 +26175,7 @@ var src_default = Postgres;
26174
26175
  function Postgres(a, b2) {
26175
26176
  const options = parseOptions(a, b2), subscribe = options.no_subscribe || Subscribe(Postgres, { ...options });
26176
26177
  let ending = false;
26177
- const queries = queue_default(), connecting = queue_default(), reserved = queue_default(), closed = queue_default(), ended = queue_default(), open6 = queue_default(), busy = queue_default(), full = queue_default(), queues = { connecting, reserved, closed, ended, open: open6, busy, full };
26178
+ const queries = queue_default(), connecting = queue_default(), reserved = queue_default(), closed = queue_default(), ended = queue_default(), open7 = queue_default(), busy = queue_default(), full = queue_default(), queues = { connecting, reserved, closed, ended, open: open7, busy, full };
26178
26179
  const connections = [...Array(options.max)].map(() => connection_default(options, queues, { onopen, onend, onclose }));
26179
26180
  const sql = Sql(handler);
26180
26181
  Object.assign(sql, {
@@ -26287,7 +26288,7 @@ function Postgres(a, b2) {
26287
26288
  }
26288
26289
  async function reserve() {
26289
26290
  const queue = queue_default();
26290
- const c = open6.length ? open6.shift() : await new Promise((resolve3, reject) => {
26291
+ const c = open7.length ? open7.shift() : await new Promise((resolve3, reject) => {
26291
26292
  const query = { reserve: resolve3, reject };
26292
26293
  queries.push(query);
26293
26294
  closed.length && connect(closed.shift(), query);
@@ -26360,7 +26361,7 @@ function Postgres(a, b2) {
26360
26361
  c.queue.remove(c);
26361
26362
  queue.push(c);
26362
26363
  c.queue = queue;
26363
- queue === open6 ? c.idleTimer.start() : c.idleTimer.cancel();
26364
+ queue === open7 ? c.idleTimer.start() : c.idleTimer.cancel();
26364
26365
  return c;
26365
26366
  }
26366
26367
  function json(x) {
@@ -26374,8 +26375,8 @@ function Postgres(a, b2) {
26374
26375
  function handler(query) {
26375
26376
  if (ending)
26376
26377
  return query.reject(Errors.connection("CONNECTION_ENDED", options, options));
26377
- if (open6.length)
26378
- return go(open6.shift(), query);
26378
+ if (open7.length)
26379
+ return go(open7.shift(), query);
26379
26380
  if (closed.length)
26380
26381
  return connect(closed.shift(), query);
26381
26382
  busy.length ? go(busy.shift(), query) : queries.push(query);
@@ -26420,7 +26421,7 @@ function Postgres(a, b2) {
26420
26421
  }
26421
26422
  function onopen(c) {
26422
26423
  if (queries.length === 0)
26423
- return move(c, open6);
26424
+ return move(c, open7);
26424
26425
  let max = Math.ceil(queries.length / (connecting.length + 1)), ready = true;
26425
26426
  while (ready && queries.length && max-- > 0) {
26426
26427
  const query = queries.shift();
@@ -27125,6 +27126,34 @@ function describeRenewalGrant(grant) {
27125
27126
  return lines;
27126
27127
  }
27127
27128
 
27129
+ // src/cloud/wake.ts
27130
+ var WAKE_EVENT = "wake";
27131
+ var WAKE_TOPIC_RE = /^cswarm-wake:[A-Za-z0-9_-]{43}$/;
27132
+ function isWakeTopic(value) {
27133
+ return WAKE_TOPIC_RE.test(value);
27134
+ }
27135
+ var WakeHintError = class extends Error {
27136
+ code = "malformed_wake";
27137
+ constructor(message) {
27138
+ super(message);
27139
+ this.name = "WakeHintError";
27140
+ }
27141
+ };
27142
+ function parseOptionalWakeHint(value) {
27143
+ if (value === void 0 || value === null) return void 0;
27144
+ if (typeof value !== "object" || Array.isArray(value)) {
27145
+ throw new WakeHintError("wake hint must be an object");
27146
+ }
27147
+ const row = value;
27148
+ if (typeof row.topic !== "string" || !isWakeTopic(row.topic)) {
27149
+ throw new WakeHintError("wake hint topic is malformed");
27150
+ }
27151
+ if (row.event !== WAKE_EVENT) {
27152
+ throw new WakeHintError("wake hint event is malformed");
27153
+ }
27154
+ return { topic: row.topic, event: WAKE_EVENT };
27155
+ }
27156
+
27128
27157
  // src/cloud/renewal.ts
27129
27158
  var AGENT_TOKEN_DEFAULT_TTL_MS2 = 60 * 60 * 1e3;
27130
27159
  var AGENT_TOKEN_MAX_TTL_MS2 = 8 * 60 * 60 * 1e3;
@@ -27426,6 +27455,16 @@ async function requestSuccessor(options) {
27426
27455
  "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."
27427
27456
  );
27428
27457
  }
27458
+ let wake;
27459
+ try {
27460
+ wake = parseOptionalWakeHint(body.wake);
27461
+ } catch {
27462
+ throw new RenewalRefused(
27463
+ response.status,
27464
+ "malformed_wake",
27465
+ "The deployment returned a successor credential with a malformed wake hint. It was not stored."
27466
+ );
27467
+ }
27429
27468
  return {
27430
27469
  token,
27431
27470
  tokenId: tokenId.toLowerCase(),
@@ -27434,7 +27473,8 @@ async function requestSuccessor(options) {
27434
27473
  issuedAt,
27435
27474
  expiresAt,
27436
27475
  horizonExpiresAt: timestamp(body.horizon_expires_at),
27437
- successorsRemaining: count(body.successors_remaining)
27476
+ successorsRemaining: count(body.successors_remaining),
27477
+ ...wake === void 0 ? {} : { wake }
27438
27478
  };
27439
27479
  }
27440
27480
  var AgentCredentialSession = class _AgentCredentialSession {
@@ -30009,6 +30049,12 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
30009
30049
  const rawRows = body.signals;
30010
30050
  const parsedRows = parseSignalRows(rawRows, parseOptions2);
30011
30051
  const ascending = query.ascending === true || query.after !== void 0;
30052
+ let wake;
30053
+ try {
30054
+ wake = parseOptionalWakeHint(body.wake);
30055
+ } catch {
30056
+ throw plainMalformedError("signal read returned a malformed wake hint");
30057
+ }
30012
30058
  return {
30013
30059
  signals: sortSignals(
30014
30060
  rowsAfterCursor(parsedRows.signals, query.after),
@@ -30019,7 +30065,8 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
30019
30065
  rawCount: rawRows.length,
30020
30066
  nextCursor: rawRows.length === 0 ? null : cursorFromUnknown(rawRows[rawRows.length - 1]),
30021
30067
  malformedRows: parsedRows.malformedRows,
30022
- pendingDeliveryCount
30068
+ pendingDeliveryCount,
30069
+ ...wake === void 0 ? {} : { wake }
30023
30070
  };
30024
30071
  }
30025
30072
  function parseAgentMemberRow(value) {
@@ -30720,10 +30767,86 @@ async function runInboxFollow(options) {
30720
30767
  // src/cloud/arrival-watch.ts
30721
30768
  var import_node_os4 = require("node:os");
30722
30769
  var import_node_path4 = require("node:path");
30770
+ var import_promises4 = require("node:fs/promises");
30771
+
30772
+ // src/cloud/idle-poll.ts
30773
+ var IDLE_POLL_DEFAULT_MS = 15e3;
30774
+ var IDLE_POLL_MAX_MS = 6e4;
30775
+ var IDLE_POLL_MIN_MS = 1e3;
30776
+ var ARRIVAL_WATCH_POLL_MS = IDLE_POLL_MAX_MS;
30777
+ var DURATION_RE = /^([1-9]\d*)(s|m)$/;
30778
+ function formatIdlePollDuration(ms) {
30779
+ if (!Number.isSafeInteger(ms) || ms <= 0) {
30780
+ throw new Error("idle poll duration must be a positive number of milliseconds");
30781
+ }
30782
+ if (ms % 6e4 === 0) return `${ms / 6e4}m`;
30783
+ if (ms % 1e3 === 0) return `${ms / 1e3}s`;
30784
+ throw new Error("idle poll duration must be a whole number of seconds");
30785
+ }
30786
+ var IDLE_POLL_MIN_LABEL = formatIdlePollDuration(IDLE_POLL_MIN_MS);
30787
+ var IDLE_POLL_DEFAULT_LABEL = formatIdlePollDuration(IDLE_POLL_DEFAULT_MS);
30788
+ var IDLE_POLL_MAX_LABEL = formatIdlePollDuration(IDLE_POLL_MAX_MS);
30789
+ function idlePollDurationExamples() {
30790
+ const midMs = Math.min(IDLE_POLL_MAX_MS, IDLE_POLL_DEFAULT_MS * 2);
30791
+ const labels = [];
30792
+ const seen = /* @__PURE__ */ new Set();
30793
+ for (const ms of [IDLE_POLL_DEFAULT_MS, midMs, IDLE_POLL_MAX_MS]) {
30794
+ const label = formatIdlePollDuration(ms);
30795
+ if (seen.has(label)) continue;
30796
+ seen.add(label);
30797
+ labels.push(label);
30798
+ }
30799
+ return labels;
30800
+ }
30801
+ var IDLE_POLL_DURATION_EXAMPLES = idlePollDurationExamples();
30802
+ function idlePollDurationHint() {
30803
+ return idlePollDurationExamples().join(", ");
30804
+ }
30805
+ function idlePollBoundSentence() {
30806
+ return `between ${IDLE_POLL_MIN_LABEL} and ${IDLE_POLL_MAX_LABEL}`;
30807
+ }
30808
+ function parseIdlePollIntervalMs(value, defaultMs = IDLE_POLL_DEFAULT_MS) {
30809
+ if (value === void 0) return defaultMs;
30810
+ const match = DURATION_RE.exec(value);
30811
+ if (!match) {
30812
+ throw new Error(
30813
+ `--poll-interval must be a duration such as ${idlePollDurationHint()}`
30814
+ );
30815
+ }
30816
+ const unit = match[2] === "s" ? 1e3 : 6e4;
30817
+ const milliseconds = Number(match[1]) * unit;
30818
+ if (!Number.isSafeInteger(milliseconds) || milliseconds < IDLE_POLL_MIN_MS || milliseconds > IDLE_POLL_MAX_MS) {
30819
+ throw new Error(`--poll-interval must be ${idlePollBoundSentence()}`);
30820
+ }
30821
+ return milliseconds;
30822
+ }
30823
+ function nextIdlePollMs(baseMs, emptyStreak, maxMs = IDLE_POLL_MAX_MS) {
30824
+ if (!Number.isSafeInteger(baseMs) || baseMs < 0) {
30825
+ throw new Error("idle poll base must be a non-negative number of milliseconds");
30826
+ }
30827
+ if (!Number.isSafeInteger(emptyStreak) || emptyStreak < 0) {
30828
+ throw new Error("idle poll empty streak must be a non-negative integer");
30829
+ }
30830
+ if (!Number.isSafeInteger(maxMs) || maxMs < 0) {
30831
+ throw new Error("idle poll max must be a non-negative number of milliseconds");
30832
+ }
30833
+ const shift = Math.min(emptyStreak, 16);
30834
+ const grown = baseMs * 2 ** shift;
30835
+ return Math.min(maxMs, grown);
30836
+ }
30837
+ function idlePollStatusSentence(currentMs) {
30838
+ return `Current idle poll interval: ${formatIdlePollDuration(currentMs)}.`;
30839
+ }
30840
+ function idlePollHelpSentence(defaultMs = IDLE_POLL_DEFAULT_MS) {
30841
+ 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.`;
30842
+ }
30843
+
30844
+ // src/cloud/arrival-watch.ts
30723
30845
  var UUID_RE11 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
30724
30846
  var CURSOR_MAX_BYTES = 4 * 1024;
30725
30847
  var ARRIVAL_SNIPPET_MAX = 180;
30726
- var ARRIVAL_WATCH_POLL_MS = 25e3;
30848
+ var WATCH_LOCK_MAX_BYTES = 512;
30849
+ var ARRIVAL_WATCH_POLL_MS2 = ARRIVAL_WATCH_POLL_MS;
30727
30850
  var ARRIVAL_RETRY_NOTICE_THRESHOLD_MS = 6e4;
30728
30851
  var EXIT_NOTIFY_ORPHANED = 74;
30729
30852
  var NotifyStdoutClosedError = class extends Error {
@@ -30776,6 +30899,94 @@ function arrivalCursorPath(target2, workspaceId2, principalId, root = stateRoot(
30776
30899
  `${target2.profileId}-${workspaceId2.toLowerCase()}-${principalId.toLowerCase()}.json`
30777
30900
  );
30778
30901
  }
30902
+ function arrivalWatchLockPath(target2, workspaceId2, principalId, root = stateRoot()) {
30903
+ return arrivalCursorPath(target2, workspaceId2, principalId, root).replace(
30904
+ /\.json$/u,
30905
+ ".lock"
30906
+ );
30907
+ }
30908
+ function arrivalWatchAlreadyRunningSentence(pid) {
30909
+ return `inbox --notify is already running for this agent as pid ${pid}.`;
30910
+ }
30911
+ var ArrivalWatchAlreadyRunningError = class extends Error {
30912
+ code = "notify_already_running";
30913
+ pid;
30914
+ constructor(pid) {
30915
+ super(arrivalWatchAlreadyRunningSentence(pid));
30916
+ this.name = "ArrivalWatchAlreadyRunningError";
30917
+ this.pid = pid;
30918
+ }
30919
+ };
30920
+ function pidIsAlive(pid) {
30921
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
30922
+ try {
30923
+ process.kill(pid, 0);
30924
+ return true;
30925
+ } catch (error) {
30926
+ return error.code !== "ESRCH";
30927
+ }
30928
+ }
30929
+ function parseWatchLock(raw) {
30930
+ let value;
30931
+ try {
30932
+ value = JSON.parse(raw);
30933
+ } catch {
30934
+ return null;
30935
+ }
30936
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
30937
+ const row = value;
30938
+ if (row.version !== 1 || !Number.isSafeInteger(row.pid) || row.pid <= 0) {
30939
+ return null;
30940
+ }
30941
+ return { pid: row.pid };
30942
+ }
30943
+ async function acquireArrivalWatchLock(path, pid = process.pid) {
30944
+ if (!Number.isSafeInteger(pid) || pid <= 0) {
30945
+ throw new Error("arrival watch lock pid must be a positive integer");
30946
+ }
30947
+ await ensureSecureStateDirectory((0, import_node_path4.dirname)(path));
30948
+ const payload = `${JSON.stringify({ version: 1, pid })}
30949
+ `;
30950
+ for (let attempt = 0; attempt < 2; attempt += 1) {
30951
+ try {
30952
+ const handle = await (0, import_promises4.open)(path, "wx", 384);
30953
+ try {
30954
+ await handle.writeFile(payload, "utf8");
30955
+ } finally {
30956
+ await handle.close();
30957
+ }
30958
+ return;
30959
+ } catch (error) {
30960
+ if (error.code !== "EEXIST") throw error;
30961
+ }
30962
+ let existing = null;
30963
+ try {
30964
+ const raw = await (0, import_promises4.readFile)(path, "utf8");
30965
+ if (Buffer.byteLength(raw, "utf8") <= WATCH_LOCK_MAX_BYTES) {
30966
+ existing = parseWatchLock(raw);
30967
+ }
30968
+ } catch (error) {
30969
+ if (error.code !== "ENOENT") throw error;
30970
+ continue;
30971
+ }
30972
+ if (existing !== null && pidIsAlive(existing.pid)) {
30973
+ throw new ArrivalWatchAlreadyRunningError(existing.pid);
30974
+ }
30975
+ await (0, import_promises4.unlink)(path).catch(() => void 0);
30976
+ }
30977
+ throw new Error("arrival watch lock could not be acquired");
30978
+ }
30979
+ async function releaseArrivalWatchLock(path, pid = process.pid) {
30980
+ try {
30981
+ const raw = await (0, import_promises4.readFile)(path, "utf8");
30982
+ const existing = parseWatchLock(raw);
30983
+ if (existing === null || existing.pid !== pid) return;
30984
+ await (0, import_promises4.unlink)(path);
30985
+ } catch (error) {
30986
+ if (error.code === "ENOENT") return;
30987
+ throw error;
30988
+ }
30989
+ }
30779
30990
  function parseCursor(raw, workspaceId2, principalId) {
30780
30991
  let value;
30781
30992
  try {
@@ -30889,8 +31100,9 @@ function assertCursorPage(page) {
30889
31100
  }
30890
31101
  }
30891
31102
  async function runArrivalWatch(options) {
30892
- const pollMs = options.pollMs ?? ARRIVAL_WATCH_POLL_MS;
31103
+ const pollMs = options.pollMs ?? ARRIVAL_WATCH_POLL_MS2;
30893
31104
  const random = options.random ?? Math.random;
31105
+ let emptyIdleStreak = 0;
30894
31106
  let cursor = await options.store.read();
30895
31107
  let baseline = cursor === void 0;
30896
31108
  let attempt = 0;
@@ -30915,6 +31127,12 @@ async function runArrivalWatch(options) {
30915
31127
  timer2 = setTimeout(finish, ms);
30916
31128
  });
30917
31129
  };
31130
+ const idleWait = async (hadDelivery) => {
31131
+ if (hadDelivery) emptyIdleStreak = 0;
31132
+ const intervalMs = nextIdlePollMs(pollMs, emptyIdleStreak, IDLE_POLL_MAX_MS);
31133
+ if (!hadDelivery) emptyIdleStreak += 1;
31134
+ await wait(intervalMs);
31135
+ };
30918
31136
  while (!cancelled()) {
30919
31137
  try {
30920
31138
  const page = await options.readPage({
@@ -30940,7 +31158,7 @@ async function runArrivalWatch(options) {
30940
31158
  await options.store.write(cursor);
30941
31159
  baseline = false;
30942
31160
  if (cancelled()) break;
30943
- await wait(pollMs);
31161
+ await idleWait(false);
30944
31162
  continue;
30945
31163
  }
30946
31164
  const emittedSignals = [];
@@ -30956,7 +31174,8 @@ async function runArrivalWatch(options) {
30956
31174
  }
30957
31175
  if (cancelled()) break;
30958
31176
  const fullPage = page.rawCount >= SIGNAL_FOLLOW_PAGE_LIMIT;
30959
- await wait(fullPage ? 0 : pollMs);
31177
+ if (fullPage) await wait(0);
31178
+ else await idleWait(emittedSignals.length > 0);
30960
31179
  } catch (error) {
30961
31180
  if (cancelled()) break;
30962
31181
  const http = followHttpDetails(error);
@@ -31903,11 +32122,18 @@ function parseClaimSuccess(body, expected, now) {
31903
32122
  "delivery claim response returned more deliveries than its pending count"
31904
32123
  );
31905
32124
  }
32125
+ let wake;
32126
+ try {
32127
+ wake = parseOptionalWakeHint(row.wake);
32128
+ } catch {
32129
+ throw new DeliveryProtocolError("delivery claim response wake field is malformed");
32130
+ }
31906
32131
  return {
31907
32132
  capabilities,
31908
32133
  deliveries,
31909
32134
  pendingDeliveryCount,
31910
- terminalDeliveryFailureCount
32135
+ terminalDeliveryFailureCount,
32136
+ ...wake === void 0 ? {} : { wake }
31911
32137
  };
31912
32138
  }
31913
32139
  function parseAckSuccess(body, expected) {
@@ -32121,7 +32347,8 @@ var DeliveryCommandClient = class {
32121
32347
  capabilities: parsed.capabilities,
32122
32348
  deliveries: parsed.deliveries,
32123
32349
  pendingDeliveryCount: parsed.pendingDeliveryCount,
32124
- terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount
32350
+ terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount,
32351
+ ...parsed.wake === void 0 ? {} : { wake: parsed.wake }
32125
32352
  };
32126
32353
  }
32127
32354
  /** Acknowledge one leased delivery with an exact terminal outcome. */
@@ -32288,12 +32515,13 @@ var CONTROL_AND_SEPARATOR_STRIP_RE = new RegExp(
32288
32515
  "[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f" + EXOTIC_SEPARATORS + "]",
32289
32516
  "g"
32290
32517
  );
32291
- var CREDENTIAL_PREFIX_RE = new RegExp(
32292
- `swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*`,
32293
- "gi"
32518
+ var SECRET_SHAPE_RE = new RegExp(
32519
+ `swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*|cswarm-wake:[A-Za-z0-9_-]{43}`,
32520
+ "i"
32294
32521
  );
32522
+ var SECRET_SHAPE_GLOBAL_RE = new RegExp(SECRET_SHAPE_RE.source, "gi");
32295
32523
  function redactCredentialText(value) {
32296
- return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(CREDENTIAL_PREFIX_RE, "[redacted-credential]");
32524
+ return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(SECRET_SHAPE_GLOBAL_RE, "[redacted-credential]");
32297
32525
  }
32298
32526
 
32299
32527
  // src/host/stderr-tail.ts
@@ -32368,7 +32596,7 @@ function attachStderrTailExitObserver(child, onStderrTail) {
32368
32596
 
32369
32597
  // src/host/opencode.ts
32370
32598
  var import_node_fs3 = require("node:fs");
32371
- var import_promises4 = require("node:fs/promises");
32599
+ var import_promises5 = require("node:fs/promises");
32372
32600
  var import_node_os5 = require("node:os");
32373
32601
  var import_node_path6 = require("node:path");
32374
32602
 
@@ -33712,18 +33940,18 @@ function buildOpenCodeHomeOwner(options) {
33712
33940
  }
33713
33941
  async function writeOpenCodeHomeOwner(home, owner) {
33714
33942
  const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
33715
- await (0, import_promises4.writeFile)(path, `${JSON.stringify(owner)}
33943
+ await (0, import_promises5.writeFile)(path, `${JSON.stringify(owner)}
33716
33944
  `, {
33717
33945
  flag: "wx",
33718
33946
  mode: 384
33719
33947
  });
33720
- await (0, import_promises4.chmod)(path, 384);
33948
+ await (0, import_promises5.chmod)(path, 384);
33721
33949
  }
33722
33950
  async function readOpenCodeHomeOwner(home) {
33723
33951
  const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
33724
33952
  let raw;
33725
33953
  try {
33726
- raw = await (0, import_promises4.readFile)(path, "utf8");
33954
+ raw = await (0, import_promises5.readFile)(path, "utf8");
33727
33955
  } catch {
33728
33956
  return null;
33729
33957
  }
@@ -33744,10 +33972,10 @@ async function releaseOpenCodeHome(home, instanceId) {
33744
33972
  return;
33745
33973
  }
33746
33974
  try {
33747
- await (0, import_promises4.rm)(home, { recursive: true, force: true });
33975
+ await (0, import_promises5.rm)(home, { recursive: true, force: true });
33748
33976
  } catch {
33749
- await (0, import_promises4.chmod)(home, 448);
33750
- await (0, import_promises4.rm)(home, { recursive: true, force: true });
33977
+ await (0, import_promises5.chmod)(home, 448);
33978
+ await (0, import_promises5.rm)(home, { recursive: true, force: true });
33751
33979
  }
33752
33980
  }
33753
33981
  function parseOpenCodeVersionOutput(stdout) {
@@ -33815,7 +34043,7 @@ function buildOpenCodeSafeConfigJson(options) {
33815
34043
  async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
33816
34044
  let info;
33817
34045
  try {
33818
- info = await (0, import_promises4.lstat)(sourceAuthPath);
34046
+ info = await (0, import_promises5.lstat)(sourceAuthPath);
33819
34047
  } catch (error) {
33820
34048
  if (error.code === "ENOENT") {
33821
34049
  if (options?.allowMissing) return null;
@@ -33844,7 +34072,7 @@ async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
33844
34072
  "OpenCode auth file exceeds the listener safety bound"
33845
34073
  );
33846
34074
  }
33847
- const raw = await (0, import_promises4.readFile)(sourceAuthPath);
34075
+ const raw = await (0, import_promises5.readFile)(sourceAuthPath);
33848
34076
  if (raw.byteLength > MAX_OPENCODE_AUTH_BYTES) {
33849
34077
  throw new AcpHostError(
33850
34078
  "opencode_auth_too_large",
@@ -33870,53 +34098,53 @@ function resolveOpenCodeAuthSourcePath(parent = process.env) {
33870
34098
  return (0, import_node_path6.join)(home, ".local", "share", "opencode", "auth.json");
33871
34099
  }
33872
34100
  async function prepareOpenCodeIsolatedHome(options) {
33873
- const home = options.home ?? await (0, import_promises4.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), OPENCODE_HOME_PREFIX));
34101
+ const home = options.home ?? await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), OPENCODE_HOME_PREFIX));
33874
34102
  if (!(0, import_node_path6.isAbsolute)(home)) {
33875
34103
  throw new AcpHostError(
33876
34104
  "isolated_home_invalid",
33877
34105
  "isolated OpenCode home must be absolute"
33878
34106
  );
33879
34107
  }
33880
- await (0, import_promises4.chmod)(home, 448);
34108
+ await (0, import_promises5.chmod)(home, 448);
33881
34109
  try {
33882
34110
  const xdgConfig = (0, import_node_path6.join)(home, "xdg-config");
33883
34111
  const xdgData = (0, import_node_path6.join)(home, "xdg-data");
33884
34112
  const xdgCache = (0, import_node_path6.join)(home, "xdg-cache");
33885
34113
  const xdgState = (0, import_node_path6.join)(home, "xdg-state");
33886
34114
  for (const dir of [xdgConfig, xdgData, xdgCache, xdgState]) {
33887
- await (0, import_promises4.mkdir)(dir, { recursive: true, mode: 448 });
33888
- await (0, import_promises4.chmod)(dir, 448);
34115
+ await (0, import_promises5.mkdir)(dir, { recursive: true, mode: 448 });
34116
+ await (0, import_promises5.chmod)(dir, 448);
33889
34117
  }
33890
34118
  const configDir = (0, import_node_path6.join)(xdgConfig, "opencode");
33891
34119
  const dataDir = (0, import_node_path6.join)(xdgData, "opencode");
33892
- await (0, import_promises4.mkdir)(configDir, { recursive: true, mode: 448 });
33893
- await (0, import_promises4.mkdir)(dataDir, { recursive: true, mode: 448 });
33894
- await (0, import_promises4.chmod)(configDir, 448);
33895
- await (0, import_promises4.chmod)(dataDir, 448);
34120
+ await (0, import_promises5.mkdir)(configDir, { recursive: true, mode: 448 });
34121
+ await (0, import_promises5.mkdir)(dataDir, { recursive: true, mode: 448 });
34122
+ await (0, import_promises5.chmod)(configDir, 448);
34123
+ await (0, import_promises5.chmod)(dataDir, 448);
33896
34124
  const configPath = (0, import_node_path6.join)(configDir, "opencode.json");
33897
- await (0, import_promises4.writeFile)(
34125
+ await (0, import_promises5.writeFile)(
33898
34126
  configPath,
33899
34127
  buildOpenCodeSafeConfigJson(
33900
34128
  options.model ? { model: options.model } : void 0
33901
34129
  ),
33902
34130
  { flag: "wx", mode: 384 }
33903
34131
  );
33904
- await (0, import_promises4.chmod)(configPath, 384);
34132
+ await (0, import_promises5.chmod)(configPath, 384);
33905
34133
  const sourceAuth = resolveOpenCodeAuthSourcePath(options.env ?? process.env);
33906
34134
  const authBytes = await readValidatedOpenCodeAuth(sourceAuth, {
33907
34135
  allowMissing: options.allowMissingAuth === true
33908
34136
  });
33909
34137
  if (authBytes) {
33910
34138
  const destAuth = (0, import_node_path6.join)(dataDir, "auth.json");
33911
- await (0, import_promises4.writeFile)(destAuth, authBytes, { flag: "wx", mode: 384 });
33912
- await (0, import_promises4.chmod)(destAuth, 384);
34139
+ await (0, import_promises5.writeFile)(destAuth, authBytes, { flag: "wx", mode: 384 });
34140
+ await (0, import_promises5.chmod)(destAuth, 384);
33913
34141
  }
33914
34142
  const owner = options.owner ?? buildOpenCodeHomeOwner({ role: "ephemeral" });
33915
34143
  await writeOpenCodeHomeOwner(home, owner);
33916
34144
  return home;
33917
34145
  } catch (error) {
33918
34146
  if (!options.home) {
33919
- await (0, import_promises4.rm)(home, { recursive: true, force: true }).catch(() => void 0);
34147
+ await (0, import_promises5.rm)(home, { recursive: true, force: true }).catch(() => void 0);
33920
34148
  }
33921
34149
  throw error;
33922
34150
  }
@@ -33941,10 +34169,10 @@ function buildOpenCodeChildEnv(parent, home) {
33941
34169
  };
33942
34170
  }
33943
34171
  async function assertOpenCodeEffectiveConfig(options) {
33944
- const hostile = await (0, import_promises4.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), "cswarm-opencode-hostile-"));
34172
+ const hostile = await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), "cswarm-opencode-hostile-"));
33945
34173
  try {
33946
- await (0, import_promises4.chmod)(hostile, 448);
33947
- await (0, import_promises4.writeFile)(
34174
+ await (0, import_promises5.chmod)(hostile, 448);
34175
+ await (0, import_promises5.writeFile)(
33948
34176
  (0, import_node_path6.join)(hostile, "opencode.json"),
33949
34177
  `${JSON.stringify({
33950
34178
  permission: {
@@ -34007,7 +34235,7 @@ async function assertOpenCodeEffectiveConfig(options) {
34007
34235
  assertForcedAskPermissionMap(map);
34008
34236
  return { permission: map };
34009
34237
  } finally {
34010
- await (0, import_promises4.rm)(hostile, { recursive: true, force: true }).catch(() => void 0);
34238
+ await (0, import_promises5.rm)(hostile, { recursive: true, force: true }).catch(() => void 0);
34011
34239
  }
34012
34240
  }
34013
34241
  function assertForcedAskPermissionMap(map) {
@@ -34058,7 +34286,7 @@ async function sweepStaleOpenCodeHomes(options) {
34058
34286
  let removed = 0;
34059
34287
  let entries;
34060
34288
  try {
34061
- entries = await (0, import_promises4.readdir)(root);
34289
+ entries = await (0, import_promises5.readdir)(root);
34062
34290
  } catch {
34063
34291
  return 0;
34064
34292
  }
@@ -34066,7 +34294,7 @@ async function sweepStaleOpenCodeHomes(options) {
34066
34294
  if (!name.startsWith(OPENCODE_HOME_PREFIX)) continue;
34067
34295
  const full = (0, import_node_path6.join)(root, name);
34068
34296
  try {
34069
- const st = await (0, import_promises4.lstat)(full);
34297
+ const st = await (0, import_promises5.lstat)(full);
34070
34298
  if (!st.isDirectory() || st.isSymbolicLink()) continue;
34071
34299
  if (selfUid !== null && typeof st.uid === "number" && st.uid !== selfUid) {
34072
34300
  continue;
@@ -34080,12 +34308,12 @@ async function sweepStaleOpenCodeHomes(options) {
34080
34308
  if (alive(owner.pid)) {
34081
34309
  continue;
34082
34310
  }
34083
- await (0, import_promises4.rm)(full, { recursive: true, force: true });
34311
+ await (0, import_promises5.rm)(full, { recursive: true, force: true });
34084
34312
  removed += 1;
34085
34313
  continue;
34086
34314
  }
34087
34315
  if (now - st.mtimeMs < maxAgeMs) continue;
34088
- await (0, import_promises4.rm)(full, { recursive: true, force: true });
34316
+ await (0, import_promises5.rm)(full, { recursive: true, force: true });
34089
34317
  removed += 1;
34090
34318
  } catch {
34091
34319
  }
@@ -34146,10 +34374,10 @@ async function openOpenCodeAcpSession(options) {
34146
34374
  const disposeHome = async () => {
34147
34375
  if (createdHome) {
34148
34376
  try {
34149
- await (0, import_promises4.rm)(home, { recursive: true, force: true });
34377
+ await (0, import_promises5.rm)(home, { recursive: true, force: true });
34150
34378
  } catch {
34151
- await (0, import_promises4.chmod)(home, 448);
34152
- await (0, import_promises4.rm)(home, { recursive: true, force: true });
34379
+ await (0, import_promises5.chmod)(home, 448);
34380
+ await (0, import_promises5.rm)(home, { recursive: true, force: true });
34153
34381
  }
34154
34382
  }
34155
34383
  };
@@ -35827,7 +36055,7 @@ var FileListenerEffectStore = class {
35827
36055
 
35828
36056
  // src/listener/grok-model.ts
35829
36057
  var import_node_crypto14 = require("node:crypto");
35830
- var import_promises5 = require("node:fs/promises");
36058
+ var import_promises6 = require("node:fs/promises");
35831
36059
  var import_node_os7 = require("node:os");
35832
36060
  var import_node_path11 = require("node:path");
35833
36061
 
@@ -36099,9 +36327,9 @@ var GrokListenerModel = class {
36099
36327
  }
36100
36328
  let sentinelCreated = false;
36101
36329
  try {
36102
- await (0, import_promises5.lstat)(sentinelPath);
36330
+ await (0, import_promises6.lstat)(sentinelPath);
36103
36331
  sentinelCreated = true;
36104
- await (0, import_promises5.unlink)(sentinelPath);
36332
+ await (0, import_promises6.unlink)(sentinelPath);
36105
36333
  } catch (error) {
36106
36334
  if (error.code !== "ENOENT") throw error;
36107
36335
  }
@@ -36123,7 +36351,7 @@ var GrokListenerModel = class {
36123
36351
  const sourceAuth = (0, import_node_path11.join)(sourceHome, "auth.json");
36124
36352
  let info;
36125
36353
  try {
36126
- info = await (0, import_promises5.lstat)(sourceAuth);
36354
+ info = await (0, import_promises6.lstat)(sourceAuth);
36127
36355
  } catch (error) {
36128
36356
  if (error.code !== "ENOENT") throw error;
36129
36357
  throw new AcpHostError(
@@ -36149,7 +36377,7 @@ var GrokListenerModel = class {
36149
36377
  "Grok auth file exceeds the listener safety bound"
36150
36378
  );
36151
36379
  }
36152
- const raw = await (0, import_promises5.readFile)(sourceAuth);
36380
+ const raw = await (0, import_promises6.readFile)(sourceAuth);
36153
36381
  if (raw.byteLength > MAX_GROK_AUTH_BYTES) {
36154
36382
  throw new AcpHostError(
36155
36383
  "grok_auth_too_large",
@@ -36169,7 +36397,7 @@ var GrokListenerModel = class {
36169
36397
 
36170
36398
  // src/listener/opencode-model.ts
36171
36399
  var import_node_crypto15 = require("node:crypto");
36172
- var import_promises6 = require("node:fs/promises");
36400
+ var import_promises7 = require("node:fs/promises");
36173
36401
  var import_node_path12 = require("node:path");
36174
36402
  function asError(error) {
36175
36403
  return error instanceof Error ? error : new Error(String(error));
@@ -36181,8 +36409,8 @@ var OpenCodeListenerModel = class {
36181
36409
  this.openSession = options.open ?? openOpenCodeAcpSession;
36182
36410
  this.prepareHome = options.prepareHome ?? prepareOpenCodeIsolatedHome;
36183
36411
  this.prepareWorkerCwd = options.prepareWorkerCwd ?? (async (home) => {
36184
- const cwd = await (0, import_promises6.mkdtemp)((0, import_node_path12.join)(home, "canary-cwd-"));
36185
- await (0, import_promises6.chmod)(cwd, 448);
36412
+ const cwd = await (0, import_promises7.mkdtemp)((0, import_node_path12.join)(home, "canary-cwd-"));
36413
+ await (0, import_promises7.chmod)(cwd, 448);
36186
36414
  return cwd;
36187
36415
  });
36188
36416
  this.permissionMode = options.permissionMode ?? "deny";
@@ -36526,11 +36754,11 @@ var OpenCodeListenerModel = class {
36526
36754
  await closeWorker();
36527
36755
  } catch (error) {
36528
36756
  if (error?.code !== "child_exit_timeout") {
36529
- await (0, import_promises6.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
36757
+ await (0, import_promises7.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
36530
36758
  }
36531
36759
  throw error;
36532
36760
  }
36533
- await (0, import_promises6.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
36761
+ await (0, import_promises7.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
36534
36762
  })();
36535
36763
  return await closePromise;
36536
36764
  };
@@ -36601,7 +36829,7 @@ var OpenCodeListenerModel = class {
36601
36829
  throw finalErr;
36602
36830
  } finally {
36603
36831
  if (canaryCwd !== null && !canaryCwdOwnedByHandle && !retainCanaryCwd) {
36604
- await (0, import_promises6.rm)(canaryCwd, { recursive: true, force: true }).catch(() => void 0);
36832
+ await (0, import_promises7.rm)(canaryCwd, { recursive: true, force: true }).catch(() => void 0);
36605
36833
  }
36606
36834
  }
36607
36835
  }
@@ -36609,7 +36837,7 @@ var OpenCodeListenerModel = class {
36609
36837
 
36610
36838
  // src/listener/claude-model.ts
36611
36839
  var import_node_crypto16 = require("node:crypto");
36612
- var import_promises7 = require("node:fs/promises");
36840
+ var import_promises8 = require("node:fs/promises");
36613
36841
  var import_node_os8 = require("node:os");
36614
36842
  var import_node_path13 = require("node:path");
36615
36843
  var CLAUDE_CODE_VERSION_REQUIRED_RE = /\bClaude Code (\d+\.\d+\.\d+) does not support this model; version (\d+\.\d+\.\d+) or newer is required\b/;
@@ -36798,9 +37026,9 @@ var ClaudeListenerModel = class {
36798
37026
  canaryError = error;
36799
37027
  } finally {
36800
37028
  try {
36801
- await (0, import_promises7.lstat)(sentinelPath);
37029
+ await (0, import_promises8.lstat)(sentinelPath);
36802
37030
  sentinelCreated = true;
36803
- await (0, import_promises7.unlink)(sentinelPath);
37031
+ await (0, import_promises8.unlink)(sentinelPath);
36804
37032
  } catch (error) {
36805
37033
  if (error.code !== "ENOENT") throw error;
36806
37034
  }
@@ -36829,7 +37057,7 @@ var ClaudeListenerModel = class {
36829
37057
 
36830
37058
  // src/listener/codex-model.ts
36831
37059
  var import_node_crypto17 = require("node:crypto");
36832
- var import_promises8 = require("node:fs/promises");
37060
+ var import_promises9 = require("node:fs/promises");
36833
37061
  var import_node_os9 = require("node:os");
36834
37062
  var import_node_path14 = require("node:path");
36835
37063
  var CodexListenerClosedDuringOpen = class extends Error {
@@ -36977,11 +37205,11 @@ var CodexListenerModel = class {
36977
37205
  const configuredHome = this.options.env?.HOME;
36978
37206
  const home = configuredHome && (0, import_node_path14.isAbsolute)(configuredHome) ? configuredHome : (0, import_node_os9.homedir)();
36979
37207
  const sentinelDirectory = (0, import_node_path14.join)(home, ".cswarm", "canary");
36980
- await (0, import_promises8.mkdir)(sentinelDirectory, { recursive: true, mode: 448 });
36981
- await (0, import_promises8.chmod)(sentinelDirectory, 448);
37208
+ await (0, import_promises9.mkdir)(sentinelDirectory, { recursive: true, mode: 448 });
37209
+ await (0, import_promises9.chmod)(sentinelDirectory, 448);
36982
37210
  const [workerCwd, canaryDirectory] = await Promise.all([
36983
- (0, import_promises8.realpath)(this.options.cwd),
36984
- (0, import_promises8.realpath)(sentinelDirectory)
37211
+ (0, import_promises9.realpath)(this.options.cwd),
37212
+ (0, import_promises9.realpath)(sentinelDirectory)
36985
37213
  ]);
36986
37214
  if (pathIsInsideOrEqual(workerCwd, canaryDirectory)) {
36987
37215
  throw new AcpPermissionCanaryError(
@@ -37004,9 +37232,9 @@ var CodexListenerModel = class {
37004
37232
  canaryError = error;
37005
37233
  } finally {
37006
37234
  try {
37007
- await (0, import_promises8.lstat)(sentinelPath);
37235
+ await (0, import_promises9.lstat)(sentinelPath);
37008
37236
  sentinelCreated = true;
37009
- await (0, import_promises8.unlink)(sentinelPath);
37237
+ await (0, import_promises9.unlink)(sentinelPath);
37010
37238
  } catch (error) {
37011
37239
  if (error.code !== "ENOENT") throw error;
37012
37240
  }
@@ -37238,9 +37466,350 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
37238
37466
  }, true);
37239
37467
  }
37240
37468
 
37469
+ // src/listener/wake.ts
37470
+ var LISTENER_RECONCILE_POLL_MS = 3e5;
37471
+ var WAKE_COALESCE_MS = 1e3;
37472
+ var WAKE_CLAIMS_PER_MINUTE_BUDGET = 50;
37473
+ var WAKE_RATE_LIMIT_POLL_MS = 6e4;
37474
+ var REALTIME_SUBSCRIBE_STATUS = {
37475
+ SUBSCRIBED: "SUBSCRIBED",
37476
+ CHANNEL_ERROR: "CHANNEL_ERROR",
37477
+ CLOSED: "CLOSED",
37478
+ TIMED_OUT: "TIMED_OUT"
37479
+ };
37480
+ var LISTENER_WAKE_MODES = ["push", "poll"];
37481
+ var LISTENER_WAKE_MODE_PUSH = LISTENER_WAKE_MODES[0];
37482
+ var LISTENER_WAKE_MODE_POLL = LISTENER_WAKE_MODES[1];
37483
+ var LISTENER_WAKE_MODE_SET = new Set(
37484
+ LISTENER_WAKE_MODES
37485
+ );
37486
+ var WAKE_ERROR_CODES = [
37487
+ "channel_error",
37488
+ "closed",
37489
+ "timed_out",
37490
+ "rate_limited",
37491
+ "wake_budget"
37492
+ ];
37493
+ var WAKE_ERROR_CODE_SET = new Set(WAKE_ERROR_CODES);
37494
+ var WAKE_ERROR_CODE_WAKE_BUDGET = WAKE_ERROR_CODES.find(
37495
+ (code) => code === "wake_budget"
37496
+ );
37497
+ var LISTENER_WAKE_STATUS_KEYS = [
37498
+ "mode",
37499
+ "subscribedAt",
37500
+ "reconnects",
37501
+ "lastWakeAt",
37502
+ "lastReconcileAt",
37503
+ "errorCode",
37504
+ "topicRotatedAt",
37505
+ "rateLimited"
37506
+ ];
37507
+ var LISTENER_WAKE_SENSITIVE_KEYS = [
37508
+ "topic",
37509
+ "wakeTopic",
37510
+ "wake_topic"
37511
+ ];
37512
+ function defaultRealtime(target2) {
37513
+ const client = createClient(target2.url, target2.anonKey, {
37514
+ auth: { persistSession: false, autoRefreshToken: false }
37515
+ });
37516
+ return client.realtime;
37517
+ }
37518
+ function isSubscribeStatus(value) {
37519
+ return value === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED || value === REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR || value === REALTIME_SUBSCRIBE_STATUS.CLOSED || value === REALTIME_SUBSCRIBE_STATUS.TIMED_OUT;
37520
+ }
37521
+ function wakeErrorCodeFromSubscribeStatus(status) {
37522
+ switch (status) {
37523
+ case REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR:
37524
+ return "channel_error";
37525
+ case REALTIME_SUBSCRIBE_STATUS.CLOSED:
37526
+ return "closed";
37527
+ case REALTIME_SUBSCRIBE_STATUS.TIMED_OUT:
37528
+ return "timed_out";
37529
+ default:
37530
+ return null;
37531
+ }
37532
+ }
37533
+ function emptyListenerWakeStatus() {
37534
+ return {
37535
+ mode: LISTENER_WAKE_MODE_POLL,
37536
+ subscribedAt: null,
37537
+ reconnects: 0,
37538
+ lastWakeAt: null,
37539
+ lastReconcileAt: null,
37540
+ errorCode: null,
37541
+ topicRotatedAt: null,
37542
+ rateLimited: false
37543
+ };
37544
+ }
37545
+ function listenerWakePersistWorthy(previous, next, lastPersistMs, nowMs) {
37546
+ if (previous === void 0) return true;
37547
+ if (previous.mode !== next.mode || previous.rateLimited !== next.rateLimited || previous.errorCode !== next.errorCode || previous.reconnects !== next.reconnects || previous.subscribedAt !== next.subscribedAt || previous.topicRotatedAt !== next.topicRotatedAt || previous.lastReconcileAt !== next.lastReconcileAt) {
37548
+ return true;
37549
+ }
37550
+ if (previous.lastWakeAt !== next.lastWakeAt) {
37551
+ return nowMs - lastPersistMs >= WAKE_COALESCE_MS;
37552
+ }
37553
+ return false;
37554
+ }
37555
+ function listenerWakeStatusSentence(wake, pollIntervalMs, lastWakeLabel) {
37556
+ if (wake.mode === LISTENER_WAKE_MODE_PUSH) {
37557
+ const last = lastWakeLabel === null ? "no wake yet" : `last wake ${lastWakeLabel}`;
37558
+ return `${LISTENER_WAKE_MODE_PUSH} (Realtime), ${last}, reconcile every ${formatIdlePollDuration(LISTENER_RECONCILE_POLL_MS)}.`;
37559
+ }
37560
+ if (wake.errorCode === WAKE_ERROR_CODE_WAKE_BUDGET) {
37561
+ return `Subscribed; claims paused until the minute clears (${WAKE_ERROR_CODE_WAKE_BUDGET}); polling every ${formatIdlePollDuration(pollIntervalMs)} meanwhile.`;
37562
+ }
37563
+ const code = wake.errorCode ?? "disconnected";
37564
+ return `${LISTENER_WAKE_MODE_POLL} every ${formatIdlePollDuration(pollIntervalMs)}. Realtime not connected (${code}).`;
37565
+ }
37566
+ var WakeSubscriber = class {
37567
+ now;
37568
+ target;
37569
+ createRealtime;
37570
+ realtime = null;
37571
+ channel = null;
37572
+ topic = null;
37573
+ waiter = null;
37574
+ pending = null;
37575
+ connectionState = "disconnected";
37576
+ subscribedAt = null;
37577
+ reconnects = 0;
37578
+ lastWakeAt = null;
37579
+ lastReconcileAt = null;
37580
+ lastErrorCode = null;
37581
+ topicRotatedAt = null;
37582
+ rateLimitedUntil = 0;
37583
+ lastClaimAt = 0;
37584
+ wakeClaimTimes = [];
37585
+ closed = false;
37586
+ everSubscribed = false;
37587
+ constructor(options) {
37588
+ this.target = options.target;
37589
+ this.now = options.now ?? Date.now;
37590
+ this.createRealtime = options.createRealtime ?? defaultRealtime;
37591
+ }
37592
+ get state() {
37593
+ return this.connectionState;
37594
+ }
37595
+ get hasTopic() {
37596
+ return this.topic !== null;
37597
+ }
37598
+ snapshot(nowMs = this.now()) {
37599
+ const serverLimited = nowMs < this.rateLimitedUntil;
37600
+ const overBudget = this.overWakeBudget(nowMs);
37601
+ const rateLimited = serverLimited || overBudget;
37602
+ const mode3 = this.connectionState === "subscribed" && !rateLimited ? LISTENER_WAKE_MODE_PUSH : LISTENER_WAKE_MODE_POLL;
37603
+ const errorCode = serverLimited ? "rate_limited" : overBudget ? WAKE_ERROR_CODE_WAKE_BUDGET : this.lastErrorCode;
37604
+ return {
37605
+ mode: mode3,
37606
+ subscribedAt: this.subscribedAt,
37607
+ reconnects: this.reconnects,
37608
+ lastWakeAt: this.lastWakeAt,
37609
+ lastReconcileAt: this.lastReconcileAt,
37610
+ errorCode,
37611
+ topicRotatedAt: this.topicRotatedAt,
37612
+ rateLimited
37613
+ };
37614
+ }
37615
+ noteReconcile(nowMs = this.now()) {
37616
+ this.lastReconcileAt = new Date(nowMs).toISOString();
37617
+ }
37618
+ noteClaim(nowMs = this.now()) {
37619
+ this.lastClaimAt = nowMs;
37620
+ }
37621
+ noteWakeClaim(nowMs = this.now()) {
37622
+ this.noteClaim(nowMs);
37623
+ this.wakeClaimTimes.push(nowMs);
37624
+ this.trimWakeClaims(nowMs);
37625
+ }
37626
+ coalescingRemainingMs(nowMs = this.now()) {
37627
+ if (this.lastClaimAt <= 0) return 0;
37628
+ return Math.max(0, this.lastClaimAt + WAKE_COALESCE_MS - nowMs);
37629
+ }
37630
+ overWakeBudget(nowMs = this.now()) {
37631
+ this.trimWakeClaims(nowMs);
37632
+ return this.wakeClaimTimes.length >= WAKE_CLAIMS_PER_MINUTE_BUDGET;
37633
+ }
37634
+ canClaimOnWake(nowMs = this.now()) {
37635
+ if (nowMs < this.rateLimitedUntil) return false;
37636
+ if (this.coalescingRemainingMs(nowMs) > 0) return false;
37637
+ return !this.overWakeBudget(nowMs);
37638
+ }
37639
+ markRateLimited(nowMs = this.now()) {
37640
+ this.rateLimitedUntil = nowMs + WAKE_RATE_LIMIT_POLL_MS;
37641
+ this.lastErrorCode = "rate_limited";
37642
+ this.emitPending("state");
37643
+ }
37644
+ setTopic(topic) {
37645
+ if (this.closed) return;
37646
+ if (!isWakeTopic(topic)) {
37647
+ throw new Error("wake topic is malformed");
37648
+ }
37649
+ if (this.topic === topic) return;
37650
+ const rotated = this.topic !== null;
37651
+ void this.detachChannel();
37652
+ this.topic = topic;
37653
+ if (rotated) {
37654
+ this.topicRotatedAt = new Date(this.now()).toISOString();
37655
+ }
37656
+ this.connect();
37657
+ }
37658
+ next(options) {
37659
+ if (this.waiter !== null) {
37660
+ throw new Error("wake next() already has a waiter");
37661
+ }
37662
+ if (options.signal?.aborted) {
37663
+ return Promise.resolve("deadline");
37664
+ }
37665
+ const nowMs = this.now();
37666
+ if (this.pending !== null) {
37667
+ if (!(this.pending === "wake" && this.wakeClaimPaused(nowMs))) {
37668
+ const reason = this.pending;
37669
+ this.pending = null;
37670
+ return Promise.resolve(reason);
37671
+ }
37672
+ }
37673
+ if (nowMs >= options.until) {
37674
+ return Promise.resolve("deadline");
37675
+ }
37676
+ return new Promise((resolve3) => {
37677
+ const finish = (reason) => {
37678
+ if (this.waiter === null) return;
37679
+ const current = this.waiter;
37680
+ this.waiter = null;
37681
+ if (current.timer !== null) clearTimeout(current.timer);
37682
+ if (current.signal && current.onAbort) {
37683
+ current.signal.removeEventListener("abort", current.onAbort);
37684
+ }
37685
+ resolve3(reason);
37686
+ };
37687
+ const delay2 = Math.max(0, options.until - this.now());
37688
+ const timer2 = setTimeout(() => finish("deadline"), delay2);
37689
+ const onAbort = () => finish("deadline");
37690
+ options.signal?.addEventListener("abort", onAbort, { once: true });
37691
+ this.waiter = {
37692
+ resolve: finish,
37693
+ timer: timer2,
37694
+ onAbort,
37695
+ signal: options.signal
37696
+ };
37697
+ });
37698
+ }
37699
+ async close() {
37700
+ this.closed = true;
37701
+ this.topic = null;
37702
+ this.finishWait("deadline");
37703
+ await this.detachChannel();
37704
+ try {
37705
+ this.realtime?.disconnect?.();
37706
+ } catch {
37707
+ }
37708
+ this.realtime = null;
37709
+ this.connectionState = "disconnected";
37710
+ }
37711
+ trimWakeClaims(nowMs) {
37712
+ const minuteStart = Math.floor(nowMs / 6e4) * 6e4;
37713
+ this.wakeClaimTimes = this.wakeClaimTimes.filter((ts) => ts >= minuteStart);
37714
+ }
37715
+ /** Client budget or a server 429: do not claim on wake; poll covers the window. */
37716
+ wakeClaimPaused(nowMs) {
37717
+ return nowMs < this.rateLimitedUntil || this.overWakeBudget(nowMs);
37718
+ }
37719
+ emitPending(reason) {
37720
+ if (this.waiter !== null) {
37721
+ if (reason === "wake" && this.wakeClaimPaused(this.now())) {
37722
+ this.pending = "wake";
37723
+ return;
37724
+ }
37725
+ this.finishWait(reason);
37726
+ return;
37727
+ }
37728
+ if (reason === "wake" || this.pending !== "wake") {
37729
+ this.pending = reason;
37730
+ }
37731
+ }
37732
+ finishWait(reason) {
37733
+ const waiter = this.waiter;
37734
+ if (waiter === null) return;
37735
+ this.waiter = null;
37736
+ if (waiter.timer !== null) clearTimeout(waiter.timer);
37737
+ if (waiter.signal && waiter.onAbort) {
37738
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
37739
+ }
37740
+ waiter.resolve(reason);
37741
+ }
37742
+ connect() {
37743
+ if (this.closed || this.topic === null) return;
37744
+ if (this.realtime === null) {
37745
+ this.realtime = this.createRealtime(this.target);
37746
+ void this.realtime.setAuth(this.target.anonKey);
37747
+ }
37748
+ const topic = this.topic;
37749
+ this.connectionState = "connecting";
37750
+ this.lastErrorCode = null;
37751
+ const channel = this.realtime.channel(topic, {
37752
+ config: { private: true }
37753
+ });
37754
+ this.channel = channel;
37755
+ channel.on("broadcast", { event: WAKE_EVENT }, () => {
37756
+ this.lastWakeAt = new Date(this.now()).toISOString();
37757
+ this.emitPending("wake");
37758
+ });
37759
+ channel.subscribe((status) => {
37760
+ this.onSubscribeStatus(status);
37761
+ });
37762
+ }
37763
+ onSubscribeStatus(status) {
37764
+ if (this.closed) return;
37765
+ if (!isSubscribeStatus(status)) return;
37766
+ const wasSubscribed = this.connectionState === "subscribed";
37767
+ if (status === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED) {
37768
+ this.connectionState = "subscribed";
37769
+ this.subscribedAt = new Date(this.now()).toISOString();
37770
+ this.lastErrorCode = null;
37771
+ if (this.everSubscribed && !wasSubscribed) this.reconnects += 1;
37772
+ this.everSubscribed = true;
37773
+ if (!wasSubscribed) this.emitPending("state");
37774
+ return;
37775
+ }
37776
+ const code = wakeErrorCodeFromSubscribeStatus(status);
37777
+ if (status === REALTIME_SUBSCRIBE_STATUS.CLOSED) {
37778
+ this.connectionState = "disconnected";
37779
+ } else {
37780
+ this.connectionState = "errored";
37781
+ }
37782
+ this.lastErrorCode = code;
37783
+ this.subscribedAt = null;
37784
+ if (wasSubscribed) this.emitPending("state");
37785
+ }
37786
+ async detachChannel() {
37787
+ const channel = this.channel;
37788
+ this.channel = null;
37789
+ if (channel === null) return;
37790
+ try {
37791
+ await channel.unsubscribe();
37792
+ } catch {
37793
+ }
37794
+ try {
37795
+ await this.realtime?.removeChannel?.(channel);
37796
+ } catch {
37797
+ }
37798
+ if (this.channel !== null) return;
37799
+ if (this.connectionState === "subscribed") {
37800
+ this.connectionState = "disconnected";
37801
+ this.subscribedAt = null;
37802
+ }
37803
+ }
37804
+ };
37805
+ function createWakeSubscriber(options) {
37806
+ return new WakeSubscriber(options);
37807
+ }
37808
+
37241
37809
  // src/listener/runtime.ts
37242
37810
  var LISTENER_PAGE_LIMIT = 100;
37243
- var LISTENER_IDLE_POLL_MS = 2e3;
37811
+ var LISTENER_IDLE_POLL_MS = IDLE_POLL_DEFAULT_MS;
37812
+ var LISTENER_IDLE_POLL_MAX_MS = IDLE_POLL_MAX_MS;
37244
37813
  var LISTENER_DELIVERY_SAFETY_MARGIN_MS = 3e4;
37245
37814
  var LISTENER_ACK_ONLY_MINIMUM_MS = DELIVERY_REQUEST_TIMEOUT_MS + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
37246
37815
  var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ONLY_MINIMUM_MS;
@@ -37525,10 +38094,22 @@ async function runListenerRuntime(options) {
37525
38094
  const random = options.random ?? Math.random;
37526
38095
  const pageLimit = options.pageLimit ?? LISTENER_PAGE_LIMIT;
37527
38096
  const pollMs = options.pollMs ?? LISTENER_IDLE_POLL_MS;
38097
+ let emptyIdleStreak = 0;
37528
38098
  const routeMode = options.routeMode ?? "worker";
37529
38099
  const deferOverChars = options.deferOverChars ?? null;
37530
38100
  const deliveryHoldBudgetMs = options.deliveryHoldBudgetMs ?? LISTENER_DELIVERY_HOLD_BUDGET_MS;
37531
38101
  const abort = options.signal;
38102
+ const idleSleep = async (hadDelivery) => {
38103
+ if (hadDelivery) emptyIdleStreak = 0;
38104
+ const intervalMs = nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
38105
+ if (!hadDelivery) emptyIdleStreak += 1;
38106
+ options.onEvent?.({
38107
+ type: "idle_poll",
38108
+ intervalMs,
38109
+ ts: eventTime(now)
38110
+ });
38111
+ await sleep2(intervalMs, abort);
38112
+ };
37532
38113
  const hasInstanceId = options.listenerInstanceId !== void 0;
37533
38114
  const hasJournal = options.deliveryJournal !== void 0;
37534
38115
  if (hasInstanceId !== hasJournal) {
@@ -37714,6 +38295,35 @@ async function runListenerRuntime(options) {
37714
38295
  abort.addEventListener("abort", onAbort);
37715
38296
  }
37716
38297
  let stop;
38298
+ let wakeSubscriber = options.wake ?? null;
38299
+ let reconcileDueAt = now();
38300
+ const ensureWake = () => {
38301
+ if (wakeSubscriber === null) {
38302
+ wakeSubscriber = options.createWake ? options.createWake(options.target) : createWakeSubscriber({ target: options.target, now });
38303
+ }
38304
+ return wakeSubscriber;
38305
+ };
38306
+ const applyWakeHint = (hint) => {
38307
+ if (hint === void 0) return;
38308
+ try {
38309
+ ensureWake().setTopic(hint.topic);
38310
+ } catch {
38311
+ }
38312
+ };
38313
+ const emitWake = () => {
38314
+ if (wakeSubscriber === null) return;
38315
+ options.onEvent?.({
38316
+ type: "wake",
38317
+ wake: wakeSubscriber.snapshot(now()),
38318
+ ts: eventTime(now)
38319
+ });
38320
+ };
38321
+ const waitCapMs = () => {
38322
+ if (wakeSubscriber !== null && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
38323
+ return LISTENER_RECONCILE_POLL_MS;
38324
+ }
38325
+ return nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
38326
+ };
37717
38327
  const sendPreparedAck = async (active) => {
37718
38328
  if (active.phase !== "ack_pending" || active.signalId === null || active.leaseId === null || active.leasedUntil === null || active.ack === null) {
37719
38329
  return {
@@ -37780,8 +38390,33 @@ async function runListenerRuntime(options) {
37780
38390
  stop = { reason: "cancelled" };
37781
38391
  break;
37782
38392
  }
37783
- let page;
37784
- try {
38393
+ let skipRead = false;
38394
+ if (ready && deliveryMode === "durable_claim" && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
38395
+ const until = Math.min(reconcileDueAt, now() + waitCapMs());
38396
+ const reason = await wakeSubscriber.next({
38397
+ until,
38398
+ ...abort ? { signal: abort } : {}
38399
+ });
38400
+ emitWake();
38401
+ if (abort?.aborted) {
38402
+ stop = { reason: "cancelled" };
38403
+ break;
38404
+ }
38405
+ if (reason === "wake" && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
38406
+ const coalesceMs = wakeSubscriber.coalescingRemainingMs(now());
38407
+ if (coalesceMs > 0) await sleep2(coalesceMs, abort);
38408
+ if (abort?.aborted) {
38409
+ stop = { reason: "cancelled" };
38410
+ break;
38411
+ }
38412
+ if (wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
38413
+ skipRead = true;
38414
+ }
38415
+ }
38416
+ }
38417
+ let page = null;
38418
+ if (skipRead) {
38419
+ } else try {
37785
38420
  const token = await options.credentialSession.bearer();
37786
38421
  page = await readPage({
37787
38422
  token,
@@ -37799,6 +38434,8 @@ async function runListenerRuntime(options) {
37799
38434
  }
37800
38435
  });
37801
38436
  requireCapabilities(page);
38437
+ applyWakeHint(page.wake);
38438
+ emitWake();
37802
38439
  if (ready && readEpisodeStartedAtMs !== null) {
37803
38440
  const recoveredAtMs = now();
37804
38441
  options.onEvent?.({
@@ -37926,7 +38563,7 @@ async function runListenerRuntime(options) {
37926
38563
  break;
37927
38564
  }
37928
38565
  }
37929
- if (page.capabilities.deliveryAck && now() < horizon && !preparedNeedsMainRoute) {
38566
+ if ((page?.capabilities.deliveryAck === true || skipRead) && now() < horizon && !preparedNeedsMainRoute) {
37930
38567
  const ackStop = await sendPreparedAck(recovery);
37931
38568
  if (ackStop !== null) {
37932
38569
  stop = ackStop;
@@ -37936,7 +38573,7 @@ async function runListenerRuntime(options) {
37936
38573
  stop = { reason: "cancelled" };
37937
38574
  break;
37938
38575
  }
37939
- await sleep2(pollMs, abort);
38576
+ await idleSleep(true);
37940
38577
  continue;
37941
38578
  }
37942
38579
  await sleep2(Math.max(0, horizon - now()), abort);
@@ -37956,7 +38593,7 @@ async function runListenerRuntime(options) {
37956
38593
  continue;
37957
38594
  }
37958
38595
  if (recovery?.phase === "leased") {
37959
- if (page.capabilities.deliveryAck) {
38596
+ if (page?.capabilities.deliveryAck === true || skipRead) {
37960
38597
  let terminal = null;
37961
38598
  if (recovery.signalId !== null) {
37962
38599
  try {
@@ -38066,6 +38703,10 @@ async function runListenerRuntime(options) {
38066
38703
  stop = { reason: "credential", error: asError2(error) };
38067
38704
  break;
38068
38705
  }
38706
+ if (error instanceof DeliveryHttpError && error.code === "rate_limited") {
38707
+ wakeSubscriber?.markRateLimited(now());
38708
+ emitWake();
38709
+ }
38069
38710
  if (!isRetryableDeliveryError(error)) {
38070
38711
  stop = { reason: "fatal", error: asError2(error) };
38071
38712
  break;
@@ -38084,6 +38725,14 @@ async function runListenerRuntime(options) {
38084
38725
  stop = { reason: "fatal", error: new Error("delivery claim did not settle") };
38085
38726
  break;
38086
38727
  }
38728
+ applyWakeHint(result.wake);
38729
+ if (!skipRead && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
38730
+ wakeSubscriber.noteReconcile(now());
38731
+ reconcileDueAt = now() + LISTENER_RECONCILE_POLL_MS;
38732
+ }
38733
+ if (skipRead) wakeSubscriber?.noteWakeClaim(now());
38734
+ else wakeSubscriber?.noteClaim(now());
38735
+ emitWake();
38087
38736
  const claimed = result.deliveries[0] ?? null;
38088
38737
  options.onEvent?.({
38089
38738
  type: "delivery_claim",
@@ -38114,9 +38763,28 @@ async function runListenerRuntime(options) {
38114
38763
  stop = { reason: "fatal", error: asError2(error) };
38115
38764
  break;
38116
38765
  }
38117
- await sleep2(pollMs, abort);
38766
+ if (wakeSubscriber !== null && wakeSubscriber.hasTopic) {
38767
+ const snap = wakeSubscriber.snapshot(now());
38768
+ const intervalMs = snap.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
38769
+ if (snap.mode === LISTENER_WAKE_MODE_PUSH) emptyIdleStreak = 0;
38770
+ else emptyIdleStreak += 1;
38771
+ options.onEvent?.({
38772
+ type: "idle_poll",
38773
+ intervalMs,
38774
+ ts: eventTime(now)
38775
+ });
38776
+ emitWake();
38777
+ continue;
38778
+ }
38779
+ await idleSleep(false);
38118
38780
  continue;
38119
38781
  }
38782
+ emptyIdleStreak = 0;
38783
+ options.onEvent?.({
38784
+ type: "idle_poll",
38785
+ intervalMs: pollMs,
38786
+ ts: eventTime(now)
38787
+ });
38120
38788
  const leasedUntilMs = Date.parse(claimed.leasedUntil);
38121
38789
  if (!Number.isFinite(leasedUntilMs) || leasedUntilMs > now() + LISTENER_DELIVERY_MAX_LEASE_MS) {
38122
38790
  stop = { reason: "fatal", error: new Error("delivery lease deadline is invalid") };
@@ -38302,6 +38970,7 @@ async function runListenerRuntime(options) {
38302
38970
  }
38303
38971
  continue;
38304
38972
  }
38973
+ if (page === null) continue;
38305
38974
  for (const signal of page.signals) {
38306
38975
  if (abort?.aborted) {
38307
38976
  stop = { reason: "cancelled" };
@@ -38389,11 +39058,15 @@ async function runListenerRuntime(options) {
38389
39058
  continue;
38390
39059
  }
38391
39060
  after = null;
38392
- await sleep2(pollMs, abort);
39061
+ await idleSleep(page.signals.length > 0);
38393
39062
  }
38394
39063
  } finally {
38395
39064
  abort?.removeEventListener("abort", onAbort);
38396
39065
  options.model.cancel();
39066
+ try {
39067
+ await wakeSubscriber?.close();
39068
+ } catch {
39069
+ }
38397
39070
  try {
38398
39071
  await options.model.close();
38399
39072
  } catch (error) {
@@ -38411,6 +39084,7 @@ var LISTENER_READ_RETRY_HOUR_CAP = 25;
38411
39084
  var LISTENER_READ_RETRY_MINUTE_CAP = 61;
38412
39085
  var LISTENER_CLAIM_HOUR_CAP = 25;
38413
39086
  var LISTENER_THROUGHPUT_LAPSE_RATIO = 0.5;
39087
+ var LISTENER_MODE_CHANGE_SKIP_MAX = 1;
38414
39088
  var FAILURE_CODES = /* @__PURE__ */ new Set([
38415
39089
  "http_status",
38416
39090
  "no_response",
@@ -38507,12 +39181,52 @@ function recordListenerReadRecovery(health, input) {
38507
39181
  retryHours: trimNewest(retryHours, LISTENER_READ_RETRY_HOUR_CAP)
38508
39182
  };
38509
39183
  }
38510
- function recordListenerClaimCadence(health, cadenceMs) {
38511
- return { ...health, claimCadenceMs: cadenceMs };
39184
+ function freezeClosedHourExpectedClaims(rows3, currentHourStart) {
39185
+ return rows3.map((row) => {
39186
+ if (row.hourStart === currentHourStart) return row;
39187
+ if (row.expectedClaims !== void 0) return row;
39188
+ if (row.cadenceMs === void 0 || row.cadenceMs < 1) return row;
39189
+ return { ...row, expectedClaims: HOUR_MS / row.cadenceMs };
39190
+ });
39191
+ }
39192
+ function recordListenerClaimCadence(health, cadenceMs, ts) {
39193
+ const hourStart = bucketStart(ts, HOUR_MS);
39194
+ const claimHours = freezeClosedHourExpectedClaims(
39195
+ health.claimHours.map((row) => ({ ...row })),
39196
+ hourStart
39197
+ );
39198
+ const hour = claimHours.find((row) => row.hourStart === hourStart);
39199
+ if (hour) {
39200
+ hour.cadenceMs = hour.cadenceMs === void 0 ? cadenceMs : Math.max(hour.cadenceMs, cadenceMs);
39201
+ } else {
39202
+ claimHours.push({ hourStart, claims: 0, cadenceMs });
39203
+ }
39204
+ return {
39205
+ ...health,
39206
+ claimCadenceMs: cadenceMs,
39207
+ claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
39208
+ };
39209
+ }
39210
+ function recordListenerWakeModeChange(health, ts) {
39211
+ const hourStart = bucketStart(ts, HOUR_MS);
39212
+ const claimHours = freezeClosedHourExpectedClaims(
39213
+ health.claimHours.map((row) => ({ ...row })),
39214
+ hourStart
39215
+ );
39216
+ const hour = claimHours.find((row) => row.hourStart === hourStart);
39217
+ if (hour) hour.modeChanged = true;
39218
+ else claimHours.push({ hourStart, claims: 0, modeChanged: true });
39219
+ return {
39220
+ ...health,
39221
+ claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
39222
+ };
38512
39223
  }
38513
39224
  function recordListenerClaim(health, ts) {
38514
39225
  const hourStart = bucketStart(ts, HOUR_MS);
38515
- const claimHours = health.claimHours.map((row) => ({ ...row }));
39226
+ const claimHours = freezeClosedHourExpectedClaims(
39227
+ health.claimHours.map((row) => ({ ...row })),
39228
+ hourStart
39229
+ );
38516
39230
  const hour = claimHours.find((row) => row.hourStart === hourStart);
38517
39231
  if (hour) hour.claims += 1;
38518
39232
  else claimHours.push({ hourStart, claims: 1 });
@@ -38567,7 +39281,15 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
38567
39281
  for (const value2 of row.claimHours) {
38568
39282
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
38569
39283
  const hour = value2;
38570
- if (!hasExpectedKeys(hour, ["hourStart", "claims"], rejectUnknownKeys) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
39284
+ if (!hasExpectedKeys(hour, ["hourStart", "claims"], false) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
39285
+ if (rejectUnknownKeys && Object.keys(hour).some(
39286
+ (key2) => key2 !== "hourStart" && key2 !== "claims" && key2 !== "cadenceMs" && key2 !== "expectedClaims" && key2 !== "modeChanged"
39287
+ )) return null;
39288
+ if (hour.cadenceMs !== void 0 && !(typeof hour.cadenceMs === "number" && Number.isSafeInteger(hour.cadenceMs) && hour.cadenceMs >= 1)) return null;
39289
+ if (hour.expectedClaims !== void 0 && !(typeof hour.expectedClaims === "number" && Number.isFinite(hour.expectedClaims) && hour.expectedClaims > 0)) return null;
39290
+ if (hour.modeChanged !== void 0 && typeof hour.modeChanged !== "boolean") {
39291
+ return null;
39292
+ }
38571
39293
  }
38572
39294
  return {
38573
39295
  currentEpisodeStartedAt: row.currentEpisodeStartedAt,
@@ -38587,10 +39309,18 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
38587
39309
  retries: minute.retries
38588
39310
  })),
38589
39311
  claimCadenceMs: row.claimCadenceMs,
38590
- claimHours: row.claimHours.map((hour) => ({
38591
- hourStart: hour.hourStart,
38592
- claims: hour.claims
38593
- }))
39312
+ claimHours: row.claimHours.map((hour) => {
39313
+ const cadenceMs = hour.cadenceMs;
39314
+ const expectedClaims = hour.expectedClaims;
39315
+ const modeChanged = hour.modeChanged;
39316
+ return {
39317
+ hourStart: hour.hourStart,
39318
+ claims: hour.claims,
39319
+ ...typeof cadenceMs === "number" ? { cadenceMs } : {},
39320
+ ...typeof expectedClaims === "number" ? { expectedClaims } : {},
39321
+ ...modeChanged === true ? { modeChanged: true } : {}
39322
+ };
39323
+ })
38594
39324
  };
38595
39325
  }
38596
39326
  function summarizeListenerReadHealth(health, readyAt, nowMs) {
@@ -38624,10 +39354,19 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
38624
39354
  const claimsByHour = new Map(
38625
39355
  health.claimHours.map((row) => [row.hourStart, row.claims])
38626
39356
  );
38627
- const expectedClaims = HOUR_MS / health.claimCadenceMs;
39357
+ const cadenceByHour = /* @__PURE__ */ new Map();
39358
+ const expectedByHour = /* @__PURE__ */ new Map();
39359
+ for (const row of health.claimHours) {
39360
+ if (row.cadenceMs !== void 0) cadenceByHour.set(row.hourStart, row.cadenceMs);
39361
+ if (row.expectedClaims !== void 0) {
39362
+ expectedByHour.set(row.hourStart, row.expectedClaims);
39363
+ }
39364
+ }
38628
39365
  for (let hour = first; hour < currentHour; hour += HOUR_MS) {
38629
39366
  const hourStart = new Date(hour).toISOString();
38630
39367
  const claims = claimsByHour.get(hourStart) ?? 0;
39368
+ const cadenceMs = cadenceByHour.get(hourStart) ?? health.claimCadenceMs;
39369
+ const expectedClaims = expectedByHour.get(hourStart) ?? HOUR_MS / cadenceMs;
38631
39370
  claimThroughputHours.push({
38632
39371
  hourStart,
38633
39372
  claims,
@@ -38636,9 +39375,25 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
38636
39375
  });
38637
39376
  }
38638
39377
  }
38639
- const throughputLapseHours = claimThroughputHours.filter(
38640
- (hour) => hour.ratio < LISTENER_THROUGHPUT_LAPSE_RATIO
38641
- );
39378
+ const consecutiveModeChangedHours = (hourStart) => {
39379
+ let count2 = 0;
39380
+ let t = Date.parse(hourStart);
39381
+ if (!Number.isFinite(t)) return 0;
39382
+ while (true) {
39383
+ const start = new Date(t).toISOString();
39384
+ const row = health.claimHours.find((hour) => hour.hourStart === start);
39385
+ if (row?.modeChanged !== true) break;
39386
+ count2 += 1;
39387
+ t -= HOUR_MS;
39388
+ }
39389
+ return count2;
39390
+ };
39391
+ const throughputLapseHours = claimThroughputHours.filter((hour) => {
39392
+ if (hour.ratio >= LISTENER_THROUGHPUT_LAPSE_RATIO) return false;
39393
+ const consecutive = consecutiveModeChangedHours(hour.hourStart);
39394
+ if (consecutive === 0) return true;
39395
+ return consecutive > LISTENER_MODE_CHANGE_SKIP_MAX;
39396
+ });
38642
39397
  return {
38643
39398
  currentEpisodeDurationMs,
38644
39399
  episodesLast24h,
@@ -38654,7 +39409,7 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
38654
39409
  // src/listener/control.ts
38655
39410
  var import_node_crypto19 = require("node:crypto");
38656
39411
  var import_node_net = require("node:net");
38657
- var import_promises9 = require("node:fs/promises");
39412
+ var import_promises10 = require("node:fs/promises");
38658
39413
  var import_node_path16 = require("node:path");
38659
39414
  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;
38660
39415
  var SEMVER_RE2 = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
@@ -38741,7 +39496,9 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
38741
39496
  "connectionsOpened",
38742
39497
  "connectionReuseRatio",
38743
39498
  "activityPublishFailures",
38744
- "activityLastErrorCode"
39499
+ "activityLastErrorCode",
39500
+ "idlePollMs",
39501
+ "wake"
38745
39502
  ]);
38746
39503
  var STATUS_ACTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
38747
39504
  "activity_credential_failed",
@@ -38764,7 +39521,10 @@ var STATUS_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
38764
39521
  "reply",
38765
39522
  "owner",
38766
39523
  "ownerId",
38767
- "owner_id"
39524
+ "owner_id",
39525
+ "topic",
39526
+ "wakeTopic",
39527
+ "wake_topic"
38768
39528
  ]);
38769
39529
  var STATUS_DELIVERY_KEYS = [
38770
39530
  "deliveryMode",
@@ -38801,6 +39561,37 @@ function parseHeldBackDeliveries(value) {
38801
39561
  }
38802
39562
  return parsed;
38803
39563
  }
39564
+ var WAKE_STATUS_KEY_SET = new Set(LISTENER_WAKE_STATUS_KEYS);
39565
+ var WAKE_SENSITIVE_KEY_SET = new Set(LISTENER_WAKE_SENSITIVE_KEYS);
39566
+ function nullableIso(value) {
39567
+ return value === null || typeof value === "string" && Number.isFinite(Date.parse(value));
39568
+ }
39569
+ function parseListenerWake(value, rejectUnknownKeys = false) {
39570
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
39571
+ const row = value;
39572
+ for (const key2 of Object.keys(row)) {
39573
+ if (WAKE_SENSITIVE_KEY_SET.has(key2)) return null;
39574
+ if (rejectUnknownKeys && !WAKE_STATUS_KEY_SET.has(key2)) return null;
39575
+ }
39576
+ for (const key2 of LISTENER_WAKE_STATUS_KEYS) {
39577
+ if (!(key2 in row)) return null;
39578
+ }
39579
+ const mode3 = LISTENER_WAKE_MODES.find((item) => item === row.mode);
39580
+ if (mode3 === void 0 || !nullableIso(row.subscribedAt) || !(typeof row.reconnects === "number" && Number.isSafeInteger(row.reconnects) && row.reconnects >= 0) || !nullableIso(row.lastWakeAt) || !nullableIso(row.lastReconcileAt) || !(row.errorCode === null || typeof row.errorCode === "string" && WAKE_ERROR_CODE_SET.has(row.errorCode)) || !nullableIso(row.topicRotatedAt) || typeof row.rateLimited !== "boolean") {
39581
+ return null;
39582
+ }
39583
+ if (mode3 === LISTENER_WAKE_MODE_PUSH && row.subscribedAt === null) return null;
39584
+ return {
39585
+ mode: mode3,
39586
+ subscribedAt: row.subscribedAt,
39587
+ reconnects: row.reconnects,
39588
+ lastWakeAt: row.lastWakeAt,
39589
+ lastReconcileAt: row.lastReconcileAt,
39590
+ errorCode: row.errorCode,
39591
+ topicRotatedAt: row.topicRotatedAt,
39592
+ rateLimited: row.rateLimited
39593
+ };
39594
+ }
38804
39595
  function parseStatus(raw, rejectUnknownKeys = false) {
38805
39596
  let value;
38806
39597
  try {
@@ -38825,9 +39616,10 @@ function parseStatus(raw, rejectUnknownKeys = false) {
38825
39616
  const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
38826
39617
  const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
38827
39618
  const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
38828
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.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 && !/swm_(?:agt|inv|cap)_/i.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_path16.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 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.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_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(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 || !(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(
39619
+ const wake = row.wake === void 0 ? void 0 : parseListenerWake(row.wake, rejectUnknownKeys);
39620
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.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_path16.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_path16.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_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(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(
38829
39621
  row.activityLastErrorCode
38830
- ))) {
39622
+ )) || !(row.idlePollMs === void 0 || row.idlePollMs === null || typeof row.idlePollMs === "number" && Number.isSafeInteger(row.idlePollMs) && row.idlePollMs >= 0)) {
38831
39623
  throw new Error("stored listener status is malformed");
38832
39624
  }
38833
39625
  const routeMode = row.routeMode ?? "worker";
@@ -38870,7 +39662,9 @@ function parseStatus(raw, rejectUnknownKeys = false) {
38870
39662
  deferOverChars,
38871
39663
  pendingForMainCount: row.pendingForMainCount ?? 0,
38872
39664
  droppedForMainCount: row.droppedForMainCount ?? 0,
38873
- ...readHealth === void 0 ? {} : { readHealth }
39665
+ ...readHealth === void 0 ? {} : { readHealth },
39666
+ ...row.idlePollMs === void 0 ? {} : { idlePollMs: row.idlePollMs ?? null },
39667
+ ...wake === void 0 ? {} : { wake }
38874
39668
  };
38875
39669
  }
38876
39670
  async function writeListenerStatus(paths, status) {
@@ -38940,7 +39734,12 @@ async function appendListenerEvent(paths, event) {
38940
39734
  "dropped_count",
38941
39735
  // How long one delivery held the worker seat, and why it gave it back.
38942
39736
  "held_ms",
38943
- "release_reason"
39737
+ "release_reason",
39738
+ "idle_poll_ms",
39739
+ "wake_mode",
39740
+ "wake_error_code",
39741
+ "wake_reconnects",
39742
+ "rate_limited"
38944
39743
  ]);
38945
39744
  const deliveryModes = /* @__PURE__ */ new Set(["durable_claim", "cursor_fallback"]);
38946
39745
  const routeModes = /* @__PURE__ */ new Set(["worker", "main", "split"]);
@@ -39005,6 +39804,21 @@ async function appendListenerEvent(paths, event) {
39005
39804
  if (key2 === "held_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
39006
39805
  throw new Error("listener event hold duration is not allowed");
39007
39806
  }
39807
+ if (key2 === "idle_poll_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
39808
+ throw new Error("listener event idle poll interval is not allowed");
39809
+ }
39810
+ if (key2 === "wake_mode" && !(typeof value === "string" && LISTENER_WAKE_MODE_SET.has(value))) {
39811
+ throw new Error("listener event wake mode is not allowed");
39812
+ }
39813
+ if (key2 === "wake_error_code" && !(value === null || typeof value === "string" && WAKE_ERROR_CODE_SET.has(value))) {
39814
+ throw new Error("listener event wake error code is not allowed");
39815
+ }
39816
+ if (key2 === "wake_reconnects" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
39817
+ throw new Error("listener event wake reconnect count is not allowed");
39818
+ }
39819
+ if (key2 === "rate_limited" && typeof value !== "boolean") {
39820
+ throw new Error("listener event rate-limited flag is not allowed");
39821
+ }
39008
39822
  if (key2 === "release_reason" && !(typeof value === "string" && LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
39009
39823
  value
39010
39824
  ))) {
@@ -39019,7 +39833,7 @@ async function appendListenerEvent(paths, event) {
39019
39833
  if (typeof value === "string" && // worker_stderr_tail is deliberately exempt from the generic 128-char
39020
39834
  // cap (its own bound is 2048, above); the secret scan still applies to
39021
39835
  // every string, the tail included.
39022
- (key2 !== "worker_stderr_tail" && value.length > 128 || /swm_(?:agt|inv|cap)_/i.test(value))) {
39836
+ (key2 !== "worker_stderr_tail" && value.length > 128 || SECRET_SHAPE_RE.test(value))) {
39023
39837
  throw new Error("listener event contains unsafe text");
39024
39838
  }
39025
39839
  }
@@ -39039,7 +39853,7 @@ async function appendListenerEvent(paths, event) {
39039
39853
  throw new Error("listener event is too large");
39040
39854
  }
39041
39855
  try {
39042
- const info = await (0, import_promises9.lstat)(paths.logPath);
39856
+ const info = await (0, import_promises10.lstat)(paths.logPath);
39043
39857
  if (!info.isFile() || info.isSymbolicLink() || (info.mode & 511) !== 384) {
39044
39858
  throw new Error("listener event log is not a secure regular file");
39045
39859
  }
@@ -39049,14 +39863,14 @@ async function appendListenerEvent(paths, event) {
39049
39863
  } catch (error) {
39050
39864
  if (error.code !== "ENOENT") throw error;
39051
39865
  }
39052
- const handle = await (0, import_promises9.open)(paths.logPath, "a", 384);
39866
+ const handle = await (0, import_promises10.open)(paths.logPath, "a", 384);
39053
39867
  try {
39054
39868
  await handle.writeFile(serialized, "utf8");
39055
39869
  await handle.sync();
39056
39870
  } finally {
39057
39871
  await handle.close();
39058
39872
  }
39059
- await (0, import_promises9.chmod)(paths.logPath, 384);
39873
+ await (0, import_promises10.chmod)(paths.logPath, 384);
39060
39874
  }
39061
39875
  function parseControlRequest(raw) {
39062
39876
  let value;
@@ -39091,7 +39905,7 @@ async function startupLock(paths) {
39091
39905
  while (Date.now() < deadline) {
39092
39906
  let handle;
39093
39907
  try {
39094
- handle = await (0, import_promises9.open)(lockPath, "wx", 384);
39908
+ handle = await (0, import_promises10.open)(lockPath, "wx", 384);
39095
39909
  } catch (error) {
39096
39910
  if (error.code !== "EEXIST") throw error;
39097
39911
  try {
@@ -39100,9 +39914,9 @@ async function startupLock(paths) {
39100
39914
  } catch (queryError) {
39101
39915
  if (queryError instanceof ListenerAlreadyRunningError) throw queryError;
39102
39916
  }
39103
- const info = await (0, import_promises9.lstat)(lockPath).catch(() => null);
39917
+ const info = await (0, import_promises10.lstat)(lockPath).catch(() => null);
39104
39918
  if (info && Date.now() - info.mtimeMs >= START_LOCK_STALE_MS) {
39105
- await (0, import_promises9.unlink)(lockPath).catch(() => void 0);
39919
+ await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
39106
39920
  continue;
39107
39921
  }
39108
39922
  await new Promise((resolve3) => setTimeout(resolve3, 25));
@@ -39114,12 +39928,12 @@ async function startupLock(paths) {
39114
39928
  await handle.sync();
39115
39929
  } catch (error) {
39116
39930
  await handle.close().catch(() => void 0);
39117
- await (0, import_promises9.unlink)(lockPath).catch(() => void 0);
39931
+ await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
39118
39932
  throw error;
39119
39933
  }
39120
39934
  return async () => {
39121
39935
  await handle.close().catch(() => void 0);
39122
- await (0, import_promises9.unlink)(lockPath).catch(() => void 0);
39936
+ await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
39123
39937
  };
39124
39938
  }
39125
39939
  throw new ListenerAlreadyRunningError();
@@ -39136,7 +39950,7 @@ async function prepareSocket(paths) {
39136
39950
  } catch (error) {
39137
39951
  if (error instanceof ListenerAlreadyRunningError) throw error;
39138
39952
  if (process.platform !== "win32") {
39139
- await (0, import_promises9.unlink)(paths.socketPath).catch((unlinkError) => {
39953
+ await (0, import_promises10.unlink)(paths.socketPath).catch((unlinkError) => {
39140
39954
  if (unlinkError.code !== "ENOENT") {
39141
39955
  throw unlinkError;
39142
39956
  }
@@ -39193,13 +40007,13 @@ async function startListenerControlServer(options) {
39193
40007
  server.listen(options.paths.socketPath);
39194
40008
  });
39195
40009
  if (process.platform !== "win32") {
39196
- await (0, import_promises9.chmod)(options.paths.socketPath, 384);
40010
+ await (0, import_promises10.chmod)(options.paths.socketPath, 384);
39197
40011
  }
39198
40012
  } catch (error) {
39199
40013
  if (server.listening) {
39200
40014
  await new Promise((resolve3) => server.close(() => resolve3()));
39201
40015
  if (process.platform !== "win32") {
39202
- await (0, import_promises9.unlink)(options.paths.socketPath).catch(() => void 0);
40016
+ await (0, import_promises10.unlink)(options.paths.socketPath).catch(() => void 0);
39203
40017
  }
39204
40018
  }
39205
40019
  throw error;
@@ -39210,7 +40024,7 @@ async function startListenerControlServer(options) {
39210
40024
  close: async () => {
39211
40025
  await new Promise((resolve3) => server.close(() => resolve3()));
39212
40026
  if (process.platform !== "win32") {
39213
- await (0, import_promises9.unlink)(options.paths.socketPath).catch(() => void 0);
40027
+ await (0, import_promises10.unlink)(options.paths.socketPath).catch(() => void 0);
39214
40028
  }
39215
40029
  }
39216
40030
  };
@@ -39307,7 +40121,7 @@ function safeErrorCode(error) {
39307
40121
  }
39308
40122
  function localDiagnostic(message, maxChars) {
39309
40123
  const redacted = message.replace(
39310
- /swm_(?:agt|inv|cap)_[^\s"'\\]*/gi,
40124
+ new RegExp(SECRET_SHAPE_RE.source, "gi"),
39311
40125
  "[redacted]"
39312
40126
  ).trim();
39313
40127
  if (redacted.length === 0) return null;
@@ -39415,6 +40229,8 @@ async function runListenerSupervisor(options) {
39415
40229
  connectionReuseRatio: 0,
39416
40230
  activityPublishFailures: 0,
39417
40231
  activityLastErrorCode: null,
40232
+ idlePollMs: null,
40233
+ wake: emptyListenerWakeStatus(),
39418
40234
  logPath: options.paths.logPath
39419
40235
  };
39420
40236
  let writes = Promise.resolve();
@@ -39487,7 +40303,69 @@ async function runListenerSupervisor(options) {
39487
40303
  const fitted = fitWorkerStderrTailForLog(tail);
39488
40304
  return fitted.length > 0 ? fitted : null;
39489
40305
  };
40306
+ let lastWakePersistMs = 0;
39490
40307
  const onEvent = (event) => {
40308
+ if (event.type === "wake") {
40309
+ const previousWake = status.wake;
40310
+ const previous = previousWake?.mode;
40311
+ let readHealth = status.readHealth ?? emptyListenerReadHealth();
40312
+ if (previous !== void 0 && previous !== event.wake.mode) {
40313
+ readHealth = recordListenerWakeModeChange(readHealth, event.ts);
40314
+ }
40315
+ const cadenceMs = event.wake.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : null;
40316
+ if (cadenceMs !== null) {
40317
+ readHealth = recordListenerClaimCadence(
40318
+ readHealth,
40319
+ cadenceMs,
40320
+ event.ts
40321
+ );
40322
+ }
40323
+ status = {
40324
+ ...status,
40325
+ wake: event.wake,
40326
+ readHealth,
40327
+ updatedAt: event.ts
40328
+ };
40329
+ const eventMs = Date.parse(event.ts);
40330
+ const nowMs = Number.isFinite(eventMs) ? eventMs : Date.now();
40331
+ if (listenerWakePersistWorthy(
40332
+ previousWake,
40333
+ event.wake,
40334
+ lastWakePersistMs,
40335
+ nowMs
40336
+ )) {
40337
+ lastWakePersistMs = nowMs;
40338
+ persist();
40339
+ log({
40340
+ ts: event.ts,
40341
+ event: "listener_wake",
40342
+ wake_mode: event.wake.mode,
40343
+ wake_error_code: event.wake.errorCode,
40344
+ wake_reconnects: event.wake.reconnects,
40345
+ rate_limited: event.wake.rateLimited
40346
+ });
40347
+ }
40348
+ return;
40349
+ }
40350
+ if (event.type === "idle_poll") {
40351
+ status = {
40352
+ ...status,
40353
+ idlePollMs: event.intervalMs,
40354
+ readHealth: recordListenerClaimCadence(
40355
+ status.readHealth ?? emptyListenerReadHealth(),
40356
+ event.intervalMs > 0 ? event.intervalMs : 1,
40357
+ event.ts
40358
+ ),
40359
+ updatedAt: event.ts
40360
+ };
40361
+ persist();
40362
+ log({
40363
+ ts: event.ts,
40364
+ event: "listener_idle_poll",
40365
+ idle_poll_ms: event.intervalMs
40366
+ });
40367
+ return;
40368
+ }
39491
40369
  if (event.type === "ready") {
39492
40370
  const versionNotice = options.getProviderVersionNotice?.() ?? null;
39493
40371
  transition("ready", {
@@ -39512,7 +40390,8 @@ async function runListenerSupervisor(options) {
39512
40390
  ...event.cadenceMs === void 0 ? {} : {
39513
40391
  readHealth: recordListenerClaimCadence(
39514
40392
  status.readHealth ?? emptyListenerReadHealth(),
39515
- event.cadenceMs
40393
+ event.cadenceMs,
40394
+ event.ts
39516
40395
  )
39517
40396
  }
39518
40397
  });
@@ -40785,6 +41664,7 @@ function buildListenerChildArgs(spec) {
40785
41664
  ...spec.model ? ["--model", spec.model] : [],
40786
41665
  ...provider === "grok" && spec.effort ? ["--effort", spec.effort] : [],
40787
41666
  ...spec.turnBudget ? ["--turn-budget", spec.turnBudget] : [],
41667
+ ...spec.pollInterval ? ["--poll-interval", spec.pollInterval] : [],
40788
41668
  ...spec.route && spec.route !== "worker" ? ["--route", spec.route] : [],
40789
41669
  ...spec.deferOver !== void 0 ? ["--defer-over", String(spec.deferOver)] : []
40790
41670
  ];
@@ -40820,7 +41700,7 @@ async function spawnDetachedListener(options) {
40820
41700
  }
40821
41701
 
40822
41702
  // src/listener/hook.ts
40823
- var import_promises10 = require("node:fs/promises");
41703
+ var import_promises11 = require("node:fs/promises");
40824
41704
  var import_node_path20 = require("node:path");
40825
41705
 
40826
41706
  // src/listener/brain-digest.ts
@@ -41218,7 +42098,7 @@ async function listenerIsLive(context) {
41218
42098
  async function discoverStoredStatusContexts(stateDirectory2) {
41219
42099
  let entries;
41220
42100
  try {
41221
- entries = await (0, import_promises10.readdir)(stateDirectory2, { withFileTypes: true });
42101
+ entries = await (0, import_promises11.readdir)(stateDirectory2, { withFileTypes: true });
41222
42102
  } catch (error) {
41223
42103
  if (error.code === "ENOENT") return [];
41224
42104
  throw error;
@@ -41618,7 +42498,7 @@ async function runListenerHookCheck(options = {}) {
41618
42498
  }
41619
42499
 
41620
42500
  // src/listener/attendance-canary.ts
41621
- var import_promises11 = require("node:fs/promises");
42501
+ var import_promises12 = require("node:fs/promises");
41622
42502
  var LOG_TAIL_BYTES = 256 * 1024;
41623
42503
  function agentReceipt(receipts, principalId) {
41624
42504
  for (const receipt of receipts) {
@@ -41631,7 +42511,7 @@ function agentReceipt(receipts, principalId) {
41631
42511
  async function readLogTail(path) {
41632
42512
  let handle;
41633
42513
  try {
41634
- handle = await (0, import_promises11.open)(path, "r");
42514
+ handle = await (0, import_promises12.open)(path, "r");
41635
42515
  } catch (error) {
41636
42516
  if (error.code === "ENOENT") return "";
41637
42517
  throw error;
@@ -42735,6 +43615,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
42735
43615
  "slug",
42736
43616
  "state-dir",
42737
43617
  "thread",
43618
+ "poll-interval",
42738
43619
  "renewal-horizon-days",
42739
43620
  "standing",
42740
43621
  "task-id",
@@ -42781,8 +43662,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
42781
43662
  ]);
42782
43663
  var UUID_RE23 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
42783
43664
  function packageVersion() {
42784
- if ("0.1.56".length > 0) {
42785
- return "0.1.56";
43665
+ if ("0.1.58".length > 0) {
43666
+ return "0.1.58";
42786
43667
  }
42787
43668
  try {
42788
43669
  const value = JSON.parse(
@@ -42923,7 +43804,7 @@ Usage:
42923
43804
  cswarm brain get <topic>[@<version>] [--version <n>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42924
43805
  cswarm brain put <topic> [<markdown-path>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--if-version <n>] [--json] # without a path, reads Markdown from stdin; --if-version refuses the write unless the live version is still <n>
42925
43806
  cswarm feedback "<text>" --kind bug|idea|friction [--about <ref>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42926
- cswarm listen start ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--turn-budget <duration>] [--route worker|main|split] [--defer-over <chars>] [--allow-unattended] [--foreground] [--json]
43807
+ cswarm listen start ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--turn-budget <duration>] [--poll-interval <duration>] [--route worker|main|split] [--defer-over <chars>] [--allow-unattended] [--foreground] [--json]
42927
43808
  cswarm listen canary ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--state-dir <path>] [--wait <seconds>] [--json]
42928
43809
  cswarm listen status ${agentCredential2} [--url <url> --anon-key <key>] --workspace-id <uuid> [--principal-id <uuid>] [--json]
42929
43810
  cswarm listen stop ${agentCredential2} [--url <url> --anon-key <key>] --workspace-id <uuid> [--principal-id <uuid>] [--json]
@@ -43013,6 +43894,8 @@ Place -- before signal text that itself begins with -- to stop option parsing.
43013
43894
  Signal text is at most 8000 characters and --about at most 500; a longer body is
43014
43895
  refused locally before any network call, so compose within the limit.
43015
43896
 
43897
+ ${idlePollHelpSentence()}
43898
+
43016
43899
  listen start --turn-budget bounds ONE worker prompt turn (default 10m): how long
43017
43900
  the worker may think and use tools on a single message before the turn times out
43018
43901
  and durable delivery retries it. A whole number plus s, m, or h (for example
@@ -43251,7 +44134,7 @@ async function stdinInviteLink() {
43251
44134
  return link;
43252
44135
  }
43253
44136
  async function confirmationLine(prompt) {
43254
- const reader = (0, import_promises13.createInterface)({
44137
+ const reader = (0, import_promises14.createInterface)({
43255
44138
  input: process.stdin,
43256
44139
  output: process.stderr,
43257
44140
  terminal: Boolean(process.stdin.isTTY)
@@ -44555,6 +45438,9 @@ function listenerTurnBudgetMs(value) {
44555
45438
  }
44556
45439
  return milliseconds;
44557
45440
  }
45441
+ function listenerPollIntervalMs(value) {
45442
+ return parseIdlePollIntervalMs(value);
45443
+ }
44558
45444
  function listenerRouteConfiguration(routeValue, deferOverValue) {
44559
45445
  const routeMode = routeValue ?? "worker";
44560
45446
  if (routeMode !== "worker" && routeMode !== "main" && routeMode !== "split") {
@@ -45535,6 +46421,12 @@ async function runInboxNotifyCommand(args) {
45535
46421
  const stop = () => controller.abort();
45536
46422
  process.on("SIGINT", stop);
45537
46423
  process.on("SIGTERM", stop);
46424
+ const lockPath = arrivalWatchLockPath(
46425
+ cloud,
46426
+ selected.selectedWorkspace,
46427
+ principalId
46428
+ );
46429
+ await acquireArrivalWatchLock(lockPath);
45538
46430
  try {
45539
46431
  const retryNotices = createArrivalRetryNoticePolicy();
45540
46432
  let renderedBearer = selected.bearer;
@@ -45608,6 +46500,7 @@ async function runInboxNotifyCommand(args) {
45608
46500
  process.off("SIGINT", stop);
45609
46501
  process.off("SIGTERM", stop);
45610
46502
  httpClient.close();
46503
+ await releaseArrivalWatchLock(lockPath);
45611
46504
  }
45612
46505
  }
45613
46506
  async function runReceipt(args) {
@@ -46107,6 +47000,10 @@ function listenerStatusJson(status, permissionMode, evidence = {
46107
47000
  readRetriesLastHour: readSummary.retriesLastHour,
46108
47001
  readRetryHours: readSummary.retryHours,
46109
47002
  claimCadenceMs: readHealth.claimCadenceMs,
47003
+ idlePollMs: status.idlePollMs ?? null,
47004
+ idlePollSentence: status.idlePollMs === void 0 || status.idlePollMs === null ? null : idlePollStatusSentence(status.idlePollMs),
47005
+ wake: status.wake ?? emptyListenerWakeStatus(),
47006
+ mode: (status.wake ?? emptyListenerWakeStatus()).mode,
46110
47007
  claimThroughputHours: readSummary.claimThroughputHours,
46111
47008
  listenerLapse: lapseNotices.length > 0,
46112
47009
  listenerLapseCodes: lapseNotices.map((notice) => notice.code),
@@ -46177,7 +47074,13 @@ function renderListenerStatus(status, evidence = {
46177
47074
  `Read retry episodes in the last 24h: ${readSummary.episodesLast24h}; retries in the rolling hour: ${readSummary.retriesLastHour}.`,
46178
47075
  readSummary.longestEpisodeAttemptsLast24h === 0 ? "Longest read retry episode in the last 24h: none recorded." : `Longest read retry episode in the last 24h: ${readSummary.longestEpisodeAttemptsLast24h} attempts over ${Math.floor(readSummary.longestEpisodeDurationMsLast24h / 1e3)}s.`,
46179
47076
  readSummary.retryHours.length === 0 ? "Read retries by hour in the last 24h: none." : `Read retries by hour in the last 24h: ${readSummary.retryHours.map((hour) => `${hour.hourStart}=${hour.retries}`).join("; ")}.`,
46180
- readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}.`
47077
+ readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}.`,
47078
+ status.idlePollMs === void 0 || status.idlePollMs === null ? "Current idle poll interval has not been reported yet." : idlePollStatusSentence(status.idlePollMs),
47079
+ listenerWakeStatusSentence(
47080
+ status.wake ?? emptyListenerWakeStatus(),
47081
+ status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : IDLE_POLL_DEFAULT_MS,
47082
+ status.wake?.lastWakeAt ? relativeAge(status.wake.lastWakeAt, nowMs) : null
47083
+ )
46181
47084
  ];
46182
47085
  for (const notice of lapseNotices) {
46183
47086
  lines.push(`WARNING [${notice.code}]: ${notice.message}`);
@@ -46811,6 +47714,7 @@ async function runConfiguredListener(options) {
46811
47714
  /* One delivery may hold the seat for one turn budget, not for the
46812
47715
  whole 15-minute lease. Same lever, so the two cannot drift. */
46813
47716
  deliveryHoldBudgetMs: turnBudgetMs,
47717
+ ...options.pollMs === void 0 ? {} : { pollMs: options.pollMs },
46814
47718
  pendingMainQueue,
46815
47719
  fetcher: httpClient.fetch
46816
47720
  });
@@ -46841,6 +47745,7 @@ async function runListenStart(args) {
46841
47745
  "codex-executable",
46842
47746
  "state-dir",
46843
47747
  "turn-budget",
47748
+ "poll-interval",
46844
47749
  "route",
46845
47750
  "defer-over",
46846
47751
  "allow-unattended",
@@ -46855,6 +47760,7 @@ async function runListenStart(args) {
46855
47760
  const provider = listenerProvider(args);
46856
47761
  validateListenerProviderFlags(args, provider);
46857
47762
  const turnBudgetMs = listenerTurnBudgetMs(args.optional("turn-budget"));
47763
+ const pollMs = listenerPollIntervalMs(args.optional("poll-interval"));
46858
47764
  const routing = listenerRouteConfiguration(
46859
47765
  args.optional("route"),
46860
47766
  args.optional("defer-over")
@@ -46897,6 +47803,7 @@ async function runListenStart(args) {
46897
47803
  permissionMode,
46898
47804
  provider,
46899
47805
  turnBudgetMs,
47806
+ pollMs,
46900
47807
  ...routing,
46901
47808
  ...args.optional("model") ? { model: args.required("model") } : {},
46902
47809
  ...args.optional("effort") ? { effort: args.required("effort") } : {},
@@ -46951,6 +47858,7 @@ async function runListenStart(args) {
46951
47858
  ...args.optional("model") ? { model: args.required("model") } : {},
46952
47859
  ...args.optional("effort") ? { effort: args.required("effort") } : {},
46953
47860
  ...args.optional("turn-budget") ? { turnBudget: args.required("turn-budget") } : {},
47861
+ ...args.optional("poll-interval") ? { pollInterval: args.required("poll-interval") } : {},
46954
47862
  ...args.optional("grok-executable") ? { executable: args.required("grok-executable") } : {},
46955
47863
  ...opencodeExecutable ? { opencodeExecutable } : {},
46956
47864
  ...claudeExecutable ? { claudeExecutable } : {},
@@ -47059,12 +47967,14 @@ async function runListenSupervisor(args) {
47059
47967
  "codex-executable",
47060
47968
  "state-dir",
47061
47969
  "turn-budget",
47970
+ "poll-interval",
47062
47971
  "route",
47063
47972
  "defer-over"
47064
47973
  ], 1);
47065
47974
  const provider = listenerProvider(args);
47066
47975
  validateListenerProviderFlags(args, provider);
47067
47976
  const turnBudgetMs = listenerTurnBudgetMs(args.optional("turn-budget"));
47977
+ const pollMs = listenerPollIntervalMs(args.optional("poll-interval"));
47068
47978
  const routing = listenerRouteConfiguration(
47069
47979
  args.optional("route"),
47070
47980
  args.optional("defer-over")
@@ -47085,6 +47995,7 @@ async function runListenSupervisor(args) {
47085
47995
  permissionMode: listenerPermissionMode(args.optional("permissions")),
47086
47996
  provider,
47087
47997
  turnBudgetMs,
47998
+ pollMs,
47088
47999
  ...routing,
47089
48000
  ...args.optional("model") ? { model: args.required("model") } : {},
47090
48001
  ...args.optional("effort") ? { effort: args.required("effort") } : {},
@@ -48337,7 +49248,7 @@ async function runSeed(args) {
48337
49248
  if (!tokenOut || !(0, import_node_path21.isAbsolute)(tokenOut)) {
48338
49249
  throw new Error("SEED_TOKEN_OUT must be an absolute path");
48339
49250
  }
48340
- const tokenFile = await (0, import_promises12.open)(tokenOut, "wx", 384).catch((error) => {
49251
+ const tokenFile = await (0, import_promises13.open)(tokenOut, "wx", 384).catch((error) => {
48341
49252
  if (error.code === "EEXIST") {
48342
49253
  throw new Error("SEED_TOKEN_OUT already exists; refusing to overwrite it");
48343
49254
  }
@@ -48376,7 +49287,7 @@ async function runSeed(args) {
48376
49287
  tokenWritten = true;
48377
49288
  }
48378
49289
  await tokenFile.close();
48379
- if (!tokenWritten) await (0, import_promises12.unlink)(tokenOut);
49290
+ if (!tokenWritten) await (0, import_promises13.unlink)(tokenOut);
48380
49291
  process.stdout.write(`${JSON.stringify({
48381
49292
  userId: result.userId,
48382
49293
  membershipRole: result.membershipRole,
@@ -48389,7 +49300,7 @@ async function runSeed(args) {
48389
49300
  `);
48390
49301
  } catch (error) {
48391
49302
  await tokenFile.close().catch(() => void 0);
48392
- if (!tokenWritten) await (0, import_promises12.unlink)(tokenOut).catch(() => void 0);
49303
+ if (!tokenWritten) await (0, import_promises13.unlink)(tokenOut).catch(() => void 0);
48393
49304
  throw error;
48394
49305
  }
48395
49306
  }
@@ -48653,6 +49564,7 @@ ${usage()}
48653
49564
  listenerFailureMessage,
48654
49565
  listenerHostLimits,
48655
49566
  listenerPermissionMode,
49567
+ listenerPollIntervalMs,
48656
49568
  listenerProviderInstallEvidence,
48657
49569
  listenerRouteConfiguration,
48658
49570
  listenerStatusJson,