commonswarm 0.1.45 → 0.1.46

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 +1668 -216
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -13511,6 +13511,7 @@ __export(cli_exports, {
13511
13511
  listenerFailureMessage: () => listenerFailureMessage,
13512
13512
  listenerHostLimits: () => listenerHostLimits,
13513
13513
  listenerPermissionMode: () => listenerPermissionMode,
13514
+ listenerProviderInstallEvidence: () => listenerProviderInstallEvidence,
13514
13515
  listenerRouteConfiguration: () => listenerRouteConfiguration,
13515
13516
  listenerStatusJson: () => listenerStatusJson,
13516
13517
  renderListenerStatus: () => renderListenerStatus,
@@ -13521,7 +13522,7 @@ __export(cli_exports, {
13521
13522
  resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
13522
13523
  });
13523
13524
  module.exports = __toCommonJS(cli_exports);
13524
- var import_node_crypto20 = require("node:crypto");
13525
+ var import_node_crypto21 = require("node:crypto");
13525
13526
  var import_node_child_process9 = require("node:child_process");
13526
13527
  var import_node_fs7 = require("node:fs");
13527
13528
  var import_promises12 = require("node:fs/promises");
@@ -23848,8 +23849,7 @@ function parseStoredCurrentTarget(raw) {
23848
23849
  throw new Error("stored current target is malformed");
23849
23850
  }
23850
23851
  const record = value;
23851
- const keys = Object.keys(record).sort();
23852
- if (keys.length !== 3 || keys[0] !== "anonKey" || keys[1] !== "url" || keys[2] !== "version" || record.version !== 1 || typeof record.url !== "string" || typeof record.anonKey !== "string") {
23852
+ if (record.version !== 1 || typeof record.url !== "string" || typeof record.anonKey !== "string") {
23853
23853
  throw new Error("stored current target is malformed");
23854
23854
  }
23855
23855
  try {
@@ -28750,10 +28750,19 @@ var SIGNAL_BODY_DISPLAY_MAX = 8e3;
28750
28750
  var SIGNAL_ABOUT_DISPLAY_MAX = 500;
28751
28751
  var SIGNAL_READ_TIMEOUT_MS = 3e4;
28752
28752
  var SignalReadTimeoutError = class extends Error {
28753
- constructor(message = "signal read timed out") {
28753
+ constructor(message = "signal read timed out", phase = "response") {
28754
28754
  super(message);
28755
+ this.phase = phase;
28755
28756
  this.name = "SignalReadTimeoutError";
28756
28757
  }
28758
+ phase;
28759
+ };
28760
+ var SignalHostPortsExhaustedError = class extends Error {
28761
+ code = "EADDRNOTAVAIL";
28762
+ constructor() {
28763
+ super("the host could not allocate an outbound source port");
28764
+ this.name = "SignalHostPortsExhaustedError";
28765
+ }
28757
28766
  };
28758
28767
  var SIGNAL_WAIT_MIN_SECONDS = 1;
28759
28768
  var SIGNAL_WAIT_MAX_SECONDS = 300;
@@ -28792,9 +28801,17 @@ var plainHttpRetryAfterMs = /* @__PURE__ */ new WeakMap();
28792
28801
  var plainHttpStatus = /* @__PURE__ */ new WeakMap();
28793
28802
  var plainHttpEnvelope = /* @__PURE__ */ new WeakMap();
28794
28803
  var plainTransportErrors = /* @__PURE__ */ new WeakSet();
28795
- function plainTransportError() {
28804
+ var plainTransportFailureCodes = /* @__PURE__ */ new WeakMap();
28805
+ var plainMalformedErrors = /* @__PURE__ */ new WeakSet();
28806
+ function plainTransportError(failureCode2 = "no_response") {
28796
28807
  const error = new Error("signal read could not reach the cloud service");
28797
28808
  plainTransportErrors.add(error);
28809
+ plainTransportFailureCodes.set(error, failureCode2);
28810
+ return error;
28811
+ }
28812
+ function plainMalformedError(message) {
28813
+ const error = new Error(message);
28814
+ plainMalformedErrors.add(error);
28798
28815
  return error;
28799
28816
  }
28800
28817
  function checkedUuid2(value, field) {
@@ -28999,7 +29016,62 @@ function followErrorEnvelope(error) {
28999
29016
  return EMPTY_SERVER_ERROR_ENVELOPE;
29000
29017
  }
29001
29018
  function isTransportFollowMessage(error) {
29002
- return error instanceof SignalTransportError || error instanceof Error && plainTransportErrors.has(error);
29019
+ return error instanceof SignalTransportError || error instanceof SignalHostPortsExhaustedError || error instanceof Error && plainTransportErrors.has(error);
29020
+ }
29021
+ function safeConstructorName(error) {
29022
+ if (error === null || typeof error !== "object") return typeof error;
29023
+ const name = error.constructor?.name;
29024
+ if (typeof name !== "string" || name.length === 0) return "Unknown";
29025
+ return name.replace(/[^A-Za-z0-9_$-]+/g, "_").slice(0, 96) || "Unknown";
29026
+ }
29027
+ function classifySignalReadFailure(error) {
29028
+ if (error instanceof SignalHostPortsExhaustedError || error !== null && typeof error === "object" && error.code === "EADDRNOTAVAIL") {
29029
+ return {
29030
+ code: "host_ports_exhausted",
29031
+ httpStatus: null,
29032
+ errorConstructor: null
29033
+ };
29034
+ }
29035
+ const http = followHttpDetails(error);
29036
+ if (http !== null) {
29037
+ return {
29038
+ code: "http_status",
29039
+ httpStatus: http.status,
29040
+ errorConstructor: null
29041
+ };
29042
+ }
29043
+ if (error instanceof SignalReadTimeoutError) {
29044
+ return {
29045
+ code: error.phase === "body" ? "body_timeout" : "no_response",
29046
+ httpStatus: null,
29047
+ errorConstructor: null
29048
+ };
29049
+ }
29050
+ if (error instanceof Error && plainTransportErrors.has(error)) {
29051
+ return {
29052
+ code: plainTransportFailureCodes.get(error) ?? "no_response",
29053
+ httpStatus: null,
29054
+ errorConstructor: null
29055
+ };
29056
+ }
29057
+ if (error instanceof SignalTransportError) {
29058
+ return { code: "no_response", httpStatus: null, errorConstructor: null };
29059
+ }
29060
+ if (error instanceof SignalMalformedError || error instanceof Error && plainMalformedErrors.has(error)) {
29061
+ return {
29062
+ code: "malformed_response",
29063
+ httpStatus: null,
29064
+ errorConstructor: null
29065
+ };
29066
+ }
29067
+ if (error instanceof Error && error.name === "AbortError") {
29068
+ return { code: "aborted", httpStatus: null, errorConstructor: null };
29069
+ }
29070
+ return {
29071
+ code: "unclassified",
29072
+ httpStatus: null,
29073
+ errorConstructor: safeConstructorName(error)
29074
+ };
29003
29075
  }
29004
29076
  function isRestartableReadError(error) {
29005
29077
  if (error instanceof SignalReadTimeoutError) return true;
@@ -29051,6 +29123,7 @@ async function fetchSignalRead(fetcher, input, init, timeoutMs = SIGNAL_READ_TIM
29051
29123
  }
29052
29124
  const deadlineController = new AbortController();
29053
29125
  let timedOut = false;
29126
+ let responseReceived = false;
29054
29127
  const signal = init.signal ? AbortSignal.any([init.signal, deadlineController.signal]) : deadlineController.signal;
29055
29128
  let onAbort = () => {
29056
29129
  };
@@ -29078,22 +29151,31 @@ async function fetchSignalRead(fetcher, input, init, timeoutMs = SIGNAL_READ_TIM
29078
29151
  signal
29079
29152
  });
29080
29153
  } catch (error) {
29081
- if (signal.aborted || timedOut || error?.name === "AbortError") {
29154
+ if (signal.aborted || timedOut) {
29082
29155
  return "timeout";
29083
29156
  }
29157
+ if (error instanceof Error && error.name === "AbortError") throw error;
29158
+ if (error !== null && typeof error === "object" && error.code === "EADDRNOTAVAIL") {
29159
+ throw new SignalHostPortsExhaustedError();
29160
+ }
29084
29161
  return null;
29085
29162
  }
29163
+ responseReceived = true;
29086
29164
  if (signal.aborted || timedOut) return "timeout";
29087
29165
  try {
29088
29166
  return { response, body: await response.json() };
29089
- } catch {
29167
+ } catch (error) {
29090
29168
  if (signal.aborted || timedOut) return "timeout";
29169
+ if (error instanceof Error && error.name === "AbortError") throw error;
29091
29170
  return { response, body: null };
29092
29171
  }
29093
29172
  })();
29094
29173
  const raced = await Promise.race([read, aborted]);
29095
29174
  if (raced === "timeout") {
29096
- throw new SignalReadTimeoutError();
29175
+ throw new SignalReadTimeoutError(
29176
+ "signal read timed out",
29177
+ responseReceived ? "body" : "response"
29178
+ );
29097
29179
  }
29098
29180
  return raced;
29099
29181
  } finally {
@@ -29122,7 +29204,9 @@ async function fetchSignalReadRetrying(fetcher, input, init, timeoutMs = SIGNAL_
29122
29204
  function mapReadFailure(error, waitBound) {
29123
29205
  if (error instanceof SignalReadTimeoutError) {
29124
29206
  if (waitBound) throw error;
29125
- throw plainTransportError();
29207
+ throw plainTransportError(
29208
+ error.phase === "body" ? "body_timeout" : "no_response"
29209
+ );
29126
29210
  }
29127
29211
  throw error;
29128
29212
  }
@@ -29178,7 +29262,7 @@ async function humanSignals(target2, credential, query, options) {
29178
29262
  throwSignalHttp(response, body);
29179
29263
  }
29180
29264
  if (!Array.isArray(body)) {
29181
- throw new Error("signal read returned malformed JSON");
29265
+ throw plainMalformedError("signal read returned malformed JSON");
29182
29266
  }
29183
29267
  const parsed = body.map((value) => parseSignalRecord(value));
29184
29268
  return sortSignals(rowsAfterCursor(parsed, query.after), ascending);
@@ -29246,7 +29330,7 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
29246
29330
  const { response, body } = result;
29247
29331
  if (!response.ok) throwSignalHttp(response, body);
29248
29332
  if (!body || typeof body !== "object" || Array.isArray(body) || !Array.isArray(body.signals)) {
29249
- throw new Error("signal read returned malformed JSON");
29333
+ throw plainMalformedError("signal read returned malformed JSON");
29250
29334
  }
29251
29335
  const capabilities = signalReadCapabilities(
29252
29336
  body.capabilities
@@ -29736,6 +29820,7 @@ function resolveRefusalToleranceMs(raw, warn = () => {
29736
29820
  }
29737
29821
  function isRetryableFollowError(error) {
29738
29822
  if (serverRefusedRetry(followErrorEnvelope(error))) return false;
29823
+ if (error instanceof SignalHostPortsExhaustedError) return true;
29739
29824
  if (error instanceof SignalReadTimeoutError) return true;
29740
29825
  if (isTransportFollowMessage(error)) return true;
29741
29826
  const http = followHttpDetails(error);
@@ -30026,9 +30111,8 @@ function parseCursor(raw, workspaceId2, principalId) {
30026
30111
  throw new Error("stored arrival cursor is malformed");
30027
30112
  }
30028
30113
  const row = value;
30029
- const keys = Object.keys(row).sort();
30030
30114
  const cursor = row.cursor;
30031
- if (keys.join(",") !== "cursor,principal_id,version,workspace_id" || row.version !== 1 || row.workspace_id !== workspaceId2.toLowerCase() || row.principal_id !== principalId.toLowerCase() || !(cursor === null || typeof cursor === "object" && !Array.isArray(cursor) && Object.keys(cursor).sort().join(",") === "created_at,id" && typeof cursor.created_at === "string" && Number.isFinite(Date.parse(cursor.created_at)) && typeof cursor.id === "string" && UUID_RE10.test(cursor.id))) {
30115
+ if (row.version !== 1 || row.workspace_id !== workspaceId2.toLowerCase() || row.principal_id !== principalId.toLowerCase() || !(cursor === null || typeof cursor === "object" && !Array.isArray(cursor) && typeof cursor.created_at === "string" && Number.isFinite(Date.parse(cursor.created_at)) && typeof cursor.id === "string" && UUID_RE10.test(cursor.id))) {
30032
30116
  throw new Error("stored arrival cursor is malformed");
30033
30117
  }
30034
30118
  if (cursor === null) return null;
@@ -30755,11 +30839,7 @@ function signalReceiptJsonPayload(report, nowMs = Date.now()) {
30755
30839
  var import_node_child_process3 = require("node:child_process");
30756
30840
  var import_node_crypto12 = require("node:crypto");
30757
30841
 
30758
- // src/host/stderr-tail.ts
30759
- var RING_CAPACITY_BYTES = 4096;
30760
- var TAIL_MAX_CHARS = 2048;
30761
- var STDERR_EXIT_GRACE_MS = 100;
30762
- var STDERR_READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
30842
+ // src/host/credential-redaction.ts
30763
30843
  var EXOTIC_SEPARATORS = "\\u00a0\\u1680\\u2000-\\u200d\\u2028\\u2029\\u202a-\\u202e\\u2060\\u2066-\\u2069\\u202f\\u205f\\u3000\\ufeff";
30764
30844
  var SEPARATOR_CLASS_SOURCE = "\\t\\n\\x0b\\f\\r " + EXOTIC_SEPARATORS;
30765
30845
  var ANSI_ESCAPE_GLOBAL_RE2 = new RegExp("\\u001b\\[[0-?]*[ -\\/]*[@-~]", "g");
@@ -30771,8 +30851,17 @@ var CREDENTIAL_PREFIX_RE = new RegExp(
30771
30851
  `swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*`,
30772
30852
  "gi"
30773
30853
  );
30854
+ function redactCredentialText(value) {
30855
+ return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(CREDENTIAL_PREFIX_RE, "[redacted-credential]");
30856
+ }
30857
+
30858
+ // src/host/stderr-tail.ts
30859
+ var RING_CAPACITY_BYTES = 4096;
30860
+ var TAIL_MAX_CHARS = 2048;
30861
+ var STDERR_EXIT_GRACE_MS = 100;
30862
+ var STDERR_READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
30774
30863
  function sanitizeStderrTail(raw) {
30775
- return raw.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(CREDENTIAL_PREFIX_RE, "[redacted-credential]").slice(-TAIL_MAX_CHARS).trim();
30864
+ return redactCredentialText(raw).slice(-TAIL_MAX_CHARS).trim();
30776
30865
  }
30777
30866
  function attachStderrTailRing(stderr) {
30778
30867
  const chunks = [];
@@ -30984,7 +31073,7 @@ function permissionDecisionToResult(decision) {
30984
31073
  var SECRET_VALUE_RE = /(?:(?:api[_-]?key|token|secret|password|authorization|bearer)\s*[:=]\s*)(["']?)([^\s"'\\]{8,})\1/gi;
30985
31074
  var JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
30986
31075
  function redactString(value) {
30987
- return value.replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
31076
+ return redactCredentialText(value).replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
30988
31077
  }
30989
31078
  function redactUnknown(value, depth = 0) {
30990
31079
  if (depth > 6) return "[truncated]";
@@ -31100,12 +31189,14 @@ var AcpVersionBelowFloorError = class extends AcpVersionError {
31100
31189
  actual;
31101
31190
  };
31102
31191
  var AcpPermissionCanaryError = class extends AcpHostError {
31103
- constructor(message, reasonCode = null) {
31192
+ constructor(message, reasonCode = null, minimumRequiredVersion = null) {
31104
31193
  super("permission_canary_failed", message);
31105
31194
  this.reasonCode = reasonCode;
31195
+ this.minimumRequiredVersion = minimumRequiredVersion;
31106
31196
  this.name = "AcpPermissionCanaryError";
31107
31197
  }
31108
31198
  reasonCode;
31199
+ minimumRequiredVersion;
31109
31200
  };
31110
31201
  var AcpPromptsBlockedError = class extends AcpHostError {
31111
31202
  constructor() {
@@ -32698,6 +32789,7 @@ async function openOpenCodeAcpSession(options) {
32698
32789
 
32699
32790
  // src/host/claude.ts
32700
32791
  var import_node_child_process4 = require("node:child_process");
32792
+ var import_node_module = require("node:module");
32701
32793
  var import_node_fs4 = require("node:fs");
32702
32794
  var import_node_path7 = require("node:path");
32703
32795
  var CHILD_EXIT_WAIT_MS2 = 3e3;
@@ -32808,6 +32900,51 @@ function parseClaudeVersionOutput(stdout) {
32808
32900
  function parseClaudeCodeVersionOutput(stdout) {
32809
32901
  return parseProviderVersionOutput(stdout, /\bClaude Code\b/i, false);
32810
32902
  }
32903
+ function semanticVersion(value) {
32904
+ if (typeof value !== "string") return null;
32905
+ return parseProviderVersionOutput(`${value}
32906
+ `, /\bnever-a-product-name\b/i);
32907
+ }
32908
+ function readPackageAtOrAbove(entrypoint, expectedName) {
32909
+ let directory = (0, import_node_path7.dirname)(entrypoint);
32910
+ for (let depth = 0; depth < 5; depth += 1) {
32911
+ const path = (0, import_node_path7.join)(directory, "package.json");
32912
+ try {
32913
+ const row = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
32914
+ if (row && typeof row === "object" && !Array.isArray(row) && row.name === expectedName) {
32915
+ return { path, row };
32916
+ }
32917
+ } catch {
32918
+ }
32919
+ const parent = (0, import_node_path7.dirname)(directory);
32920
+ if (parent === directory) break;
32921
+ directory = parent;
32922
+ }
32923
+ return null;
32924
+ }
32925
+ function measureClaudeBundleVersions(executable) {
32926
+ const adapter = readPackageAtOrAbove(
32927
+ executable,
32928
+ "@agentclientprotocol/claude-agent-acp"
32929
+ );
32930
+ if (!adapter) return { agentSdkVersion: null, claudeCodeVersion: null };
32931
+ try {
32932
+ const sdkEntrypoint = (0, import_node_module.createRequire)(adapter.path).resolve(
32933
+ "@anthropic-ai/claude-agent-sdk"
32934
+ );
32935
+ const sdk = readPackageAtOrAbove(
32936
+ sdkEntrypoint,
32937
+ "@anthropic-ai/claude-agent-sdk"
32938
+ );
32939
+ if (!sdk) return { agentSdkVersion: null, claudeCodeVersion: null };
32940
+ return {
32941
+ agentSdkVersion: semanticVersion(sdk.row.version),
32942
+ claudeCodeVersion: semanticVersion(sdk.row.claudeCodeVersion)
32943
+ };
32944
+ } catch {
32945
+ return { agentSdkVersion: null, claudeCodeVersion: null };
32946
+ }
32947
+ }
32811
32948
  async function readClaudeVersionOutput(executable, options) {
32812
32949
  const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
32813
32950
  const env = options?.env ?? sanitizeChildEnv(process.env);
@@ -32831,24 +32968,21 @@ async function readClaudeVersionOutput(executable, options) {
32831
32968
  );
32832
32969
  });
32833
32970
  }
32834
- async function assertClaudeVersionFloor(executable, options) {
32835
- const minimumVersion = options?.minimumVersion ?? CLAUDE_ACP_MIN_VERSION;
32836
- const lastMeasuredVersion = options?.lastMeasuredVersion ?? CLAUDE_ACP_LAST_MEASURED_VERSION;
32837
- const stdout = await readClaudeVersionOutput(executable, options);
32838
- const version3 = parseClaudeVersionOutput(stdout);
32839
- if (!version3) {
32840
- throw new AcpVersionParseError(
32841
- `could not parse claude-agent-acp version from: ${stdout.trim().slice(0, 200)}`
32842
- );
32843
- }
32844
- assertProviderVersionFloor({
32845
- provider: "claude-agent-acp",
32846
- version: version3,
32847
- minimumVersion,
32848
- lastMeasuredVersion,
32849
- ...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
32850
- });
32851
- return version3;
32971
+ async function inspectClaudeBridgeExecutable(executable = "claude-agent-acp", options) {
32972
+ const resolved = resolveClaudeExecutable(
32973
+ executable,
32974
+ options?.pathEnv,
32975
+ options?.platform
32976
+ );
32977
+ const output = await readClaudeVersionOutput(resolved, options);
32978
+ const bundle = measureClaudeBundleVersions(resolved);
32979
+ return {
32980
+ executable: resolved,
32981
+ providerVersion: parseClaudeVersionOutput(output),
32982
+ lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
32983
+ bundledAgentSdkVersion: bundle.agentSdkVersion,
32984
+ bundledClaudeCodeVersion: bundle.claudeCodeVersion
32985
+ };
32852
32986
  }
32853
32987
  function buildClaudeAcpArgs() {
32854
32988
  return [];
@@ -32913,29 +33047,57 @@ async function openClaudeAcpSession(options) {
32913
33047
  let executable;
32914
33048
  let env = baseEnv;
32915
33049
  let claudeCodeExecutable;
33050
+ let providerVersion;
33051
+ let bundleVersions = {
33052
+ agentSdkVersion: null,
33053
+ claudeCodeVersion: null
33054
+ };
33055
+ const reportRuntime = (resolved, version3) => {
33056
+ bundleVersions = measureClaudeBundleVersions(resolved);
33057
+ options.onRuntimeNotice?.({
33058
+ executable: resolved,
33059
+ providerVersion: version3,
33060
+ lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
33061
+ bundledAgentSdkVersion: bundleVersions.agentSdkVersion,
33062
+ bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion
33063
+ });
33064
+ };
33065
+ const admitBridgeVersion = (resolved, version3) => {
33066
+ providerVersion = version3;
33067
+ reportRuntime(resolved, version3);
33068
+ assertProviderVersionFloor({
33069
+ provider: "claude-agent-acp",
33070
+ version: version3,
33071
+ minimumVersion: CLAUDE_ACP_MIN_VERSION,
33072
+ lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
33073
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
33074
+ });
33075
+ };
32916
33076
  if (requestedExecutable) {
32917
33077
  const output = await readClaudeVersionOutput(requestedExecutable, {
32918
33078
  env: baseEnv
32919
33079
  });
32920
33080
  const bridgeVersion = parseClaudeVersionOutput(output);
32921
33081
  if (bridgeVersion) {
32922
- assertProviderVersionFloor({
32923
- provider: "claude-agent-acp",
32924
- version: bridgeVersion,
32925
- minimumVersion: CLAUDE_ACP_MIN_VERSION,
32926
- lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
32927
- ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
32928
- });
32929
33082
  executable = requestedExecutable;
33083
+ admitBridgeVersion(executable, bridgeVersion);
32930
33084
  } else if (parseClaudeCodeVersionOutput(output)) {
32931
33085
  claudeCodeExecutable = requestedExecutable;
32932
33086
  executable = resolvePackagedClaudeBridge(resolvedPathEnv);
32933
33087
  env = buildClaudeChildEnv(parentEnv, claudeCodeExecutable);
32934
- await assertClaudeVersionFloor(executable, {
32935
- env,
32936
- ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
33088
+ const bridgeOutput = await readClaudeVersionOutput(executable, {
33089
+ env
32937
33090
  });
33091
+ const packagedVersion = parseClaudeVersionOutput(bridgeOutput);
33092
+ if (!packagedVersion) {
33093
+ reportRuntime(executable, null);
33094
+ throw new AcpVersionParseError(
33095
+ `could not parse claude-agent-acp version from: ${bridgeOutput.trim().slice(0, 200)}`
33096
+ );
33097
+ }
33098
+ admitBridgeVersion(executable, packagedVersion);
32938
33099
  } else {
33100
+ reportRuntime(requestedExecutable, null);
32939
33101
  throw new AcpVersionError(
32940
33102
  `could not identify Claude executable from: ${output.trim().slice(0, 200)}`
32941
33103
  );
@@ -32946,10 +33108,19 @@ async function openClaudeAcpSession(options) {
32946
33108
  resolvedPathEnv
32947
33109
  );
32948
33110
  if (!options.skipVersionCheck) {
32949
- await assertClaudeVersionFloor(executable, {
32950
- env: baseEnv,
32951
- ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
33111
+ const output = await readClaudeVersionOutput(executable, {
33112
+ env: baseEnv
32952
33113
  });
33114
+ const version3 = parseClaudeVersionOutput(output);
33115
+ if (!version3) {
33116
+ reportRuntime(executable, null);
33117
+ throw new AcpVersionParseError(
33118
+ `could not parse claude-agent-acp version from: ${output.trim().slice(0, 200)}`
33119
+ );
33120
+ }
33121
+ admitBridgeVersion(executable, version3);
33122
+ } else {
33123
+ reportRuntime(executable, null);
32953
33124
  }
32954
33125
  }
32955
33126
  if (options.signal?.aborted) {
@@ -33041,7 +33212,17 @@ async function openClaudeAcpSession(options) {
33041
33212
  })();
33042
33213
  return closePromise;
33043
33214
  };
33044
- return { session, child, executable, args, env, close };
33215
+ return {
33216
+ session,
33217
+ child,
33218
+ executable,
33219
+ args,
33220
+ env,
33221
+ ...providerVersion ? { providerVersion } : {},
33222
+ ...bundleVersions.agentSdkVersion ? { bundledAgentSdkVersion: bundleVersions.agentSdkVersion } : {},
33223
+ ...bundleVersions.claudeCodeVersion ? { bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion } : {},
33224
+ close
33225
+ };
33045
33226
  } catch (error) {
33046
33227
  removeAbortListener();
33047
33228
  transport.close();
@@ -33893,6 +34074,18 @@ var V1_EFFECT_KEYS = /* @__PURE__ */ new Set([
33893
34074
  "updatedAt"
33894
34075
  ]);
33895
34076
  var V2_EFFECT_KEYS = /* @__PURE__ */ new Set([...V1_EFFECT_KEYS, "signalKind"]);
34077
+ var EFFECT_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
34078
+ "lease_id",
34079
+ "leaseId",
34080
+ "listenerBearer",
34081
+ "bearer",
34082
+ "token",
34083
+ "prompt",
34084
+ "ackCommandId",
34085
+ "ack_command_id",
34086
+ "claimCommandId",
34087
+ "claim_command_id"
34088
+ ]);
33896
34089
  function defaultListenerStateDirectory() {
33897
34090
  return process.env.XDG_STATE_HOME ? (0, import_node_path9.join)(process.env.XDG_STATE_HOME, "cswarm", "listeners") : (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".cswarm", "listeners");
33898
34091
  }
@@ -33911,9 +34104,9 @@ function integer(value) {
33911
34104
  function nullableString2(value, max) {
33912
34105
  return value === null || typeof value === "string" && value.length <= max;
33913
34106
  }
33914
- function rejectUnknownKeys(row, allowed) {
34107
+ function rejectSensitiveKeys(row) {
33915
34108
  for (const key2 of Object.keys(row)) {
33916
- if (!allowed.has(key2)) {
34109
+ if (EFFECT_SENSITIVE_KEYS.has(key2)) {
33917
34110
  throw new Error("stored listener effect is malformed");
33918
34111
  }
33919
34112
  }
@@ -33929,17 +34122,16 @@ function parseListenerEffectRecord(raw, expectedId) {
33929
34122
  throw new Error("stored listener effect is malformed");
33930
34123
  }
33931
34124
  const row = value;
34125
+ rejectSensitiveKeys(row);
33932
34126
  if (typeof row.version !== "number" || row.version !== 1 && row.version !== 2 || typeof row.signalId !== "string" || row.signalId.toLowerCase() !== expectedId || !UUID_RE13.test(row.signalId)) {
33933
34127
  throw new Error("stored listener effect is malformed");
33934
34128
  }
33935
34129
  if (row.version === 1) {
33936
- rejectUnknownKeys(row, V1_EFFECT_KEYS);
33937
34130
  if ("signalKind" in row || row.state === "observed" || row.state === "routed_main") {
33938
34131
  throw new Error("stored listener effect is malformed");
33939
34132
  }
33940
34133
  return upcastV1Ask(row);
33941
34134
  }
33942
- rejectUnknownKeys(row, V2_EFFECT_KEYS);
33943
34135
  return parseV2Record(row);
33944
34136
  }
33945
34137
  function upcastV1Ask(row) {
@@ -34392,6 +34584,7 @@ var GrokListenerModel = class {
34392
34584
  ...this.options.effort ? { effort: this.options.effort } : {},
34393
34585
  ...this.options.env ? { env: this.options.env } : {},
34394
34586
  ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
34587
+ ...this.options.events ? { events: this.options.events } : {},
34395
34588
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
34396
34589
  clientName: "cswarm-listener"
34397
34590
  });
@@ -34832,6 +35025,7 @@ var OpenCodeListenerModel = class {
34832
35025
  ...this.options.model ? { model: this.options.model } : {},
34833
35026
  ...this.options.env ? { env: this.options.env } : {},
34834
35027
  ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
35028
+ ...this.options.events ? { events: this.options.events } : {},
34835
35029
  ...this.options.allowMissingAuth === true ? { allowMissingAuth: true } : {},
34836
35030
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
34837
35031
  clientName: "cswarm-listener"
@@ -34935,6 +35129,35 @@ var import_node_crypto16 = require("node:crypto");
34935
35129
  var import_promises7 = require("node:fs/promises");
34936
35130
  var import_node_os8 = require("node:os");
34937
35131
  var import_node_path13 = require("node:path");
35132
+ 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/;
35133
+ var CLAUDE_AUTH_FAILURE_RE = /\b(?:authentication failed|authentication required|not authenticated|OAuth (?:sign-in|login|token)|keychain\/OAuth|please (?:log|sign) in)\b/i;
35134
+ var CLAUDE_CANARY_TIMEOUT_RE = /^ACP request timed out: session\/prompt(?: \(failed \d+ attempts\))?$/;
35135
+ function classifyClaudeCanaryFailure(detail, typedReasonCode) {
35136
+ const recorded = detail?.trim() ?? "";
35137
+ const demanded = CLAUDE_CODE_VERSION_REQUIRED_RE.exec(recorded);
35138
+ if (demanded?.[2]) {
35139
+ return {
35140
+ code: "claude_bridge_version_required",
35141
+ minimumRequiredVersion: demanded[2]
35142
+ };
35143
+ }
35144
+ if (typedReasonCode === "claude_canary_timeout" || typedReasonCode === "timeout" || (typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_CANARY_TIMEOUT_RE.test(recorded)) {
35145
+ return { code: "claude_canary_timeout", minimumRequiredVersion: null };
35146
+ }
35147
+ if (typedReasonCode === "claude_canary_auth_failed") {
35148
+ return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
35149
+ }
35150
+ if (typedReasonCode === "claude_bridge_version_required") {
35151
+ return {
35152
+ code: "claude_bridge_version_required",
35153
+ minimumRequiredVersion: demanded?.[2] ?? null
35154
+ };
35155
+ }
35156
+ if ((typedReasonCode === "rpc_error" || typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_AUTH_FAILURE_RE.test(recorded)) {
35157
+ return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
35158
+ }
35159
+ return { code: "claude_canary_unknown", minimumRequiredVersion: null };
35160
+ }
34938
35161
  var ClaudeListenerClosedDuringOpen = class extends Error {
34939
35162
  constructor() {
34940
35163
  super("listener model closed while the Claude worker was opening");
@@ -35048,6 +35271,8 @@ var ClaudeListenerModel = class {
35048
35271
  ...this.options.executable ? { executable: this.options.executable } : {},
35049
35272
  ...this.options.env ? { env: this.options.env } : {},
35050
35273
  ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
35274
+ ...this.options.onRuntimeNotice ? { onRuntimeNotice: this.options.onRuntimeNotice } : {},
35275
+ ...this.options.events ? { events: this.options.events } : {},
35051
35276
  signal: controller.signal,
35052
35277
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
35053
35278
  clientName: "cswarm-listener"
@@ -35076,6 +35301,7 @@ var ClaudeListenerModel = class {
35076
35301
  (0, import_node_os8.tmpdir)(),
35077
35302
  `cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
35078
35303
  );
35304
+ let canaryError;
35079
35305
  let sentinelCreated = false;
35080
35306
  try {
35081
35307
  await handle.session.enablePromptsAfterCanary({
@@ -35083,6 +35309,8 @@ var ClaudeListenerModel = class {
35083
35309
  probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else.`,
35084
35310
  ...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
35085
35311
  });
35312
+ } catch (error) {
35313
+ canaryError = error;
35086
35314
  } finally {
35087
35315
  try {
35088
35316
  await (0, import_promises7.lstat)(sentinelPath);
@@ -35094,9 +35322,22 @@ var ClaudeListenerModel = class {
35094
35322
  }
35095
35323
  if (sentinelCreated) {
35096
35324
  throw new AcpPermissionCanaryError(
35097
- "Claude bridge wrote the permission canary sentinel before denial"
35325
+ "Claude bridge wrote the permission canary sentinel before denial",
35326
+ "claude_canary_write_not_blocked"
35098
35327
  );
35099
35328
  }
35329
+ if (canaryError instanceof AcpPermissionCanaryError) {
35330
+ const shape = classifyClaudeCanaryFailure(
35331
+ canaryError.message,
35332
+ canaryError.reasonCode
35333
+ );
35334
+ throw new AcpPermissionCanaryError(
35335
+ canaryError.message,
35336
+ shape.code,
35337
+ shape.minimumRequiredVersion
35338
+ );
35339
+ }
35340
+ if (canaryError !== void 0) throw canaryError;
35100
35341
  }
35101
35342
  };
35102
35343
 
@@ -35222,6 +35463,7 @@ var CodexListenerModel = class {
35222
35463
  ...this.options.executable ? { executable: this.options.executable } : {},
35223
35464
  ...this.options.env ? { env: this.options.env } : {},
35224
35465
  ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
35466
+ ...this.options.events ? { events: this.options.events } : {},
35225
35467
  signal: controller.signal,
35226
35468
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
35227
35469
  clientName: "cswarm-listener"
@@ -35909,6 +36151,21 @@ var UUID_RE15 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-
35909
36151
  var MAX_QUEUE_BYTES = 1024 * 1024;
35910
36152
  var QUEUE_FILE = "pending-for-main.json";
35911
36153
  var QUEUE_LOCK = "pending-for-main";
36154
+ var QUEUE_KEYS = /* @__PURE__ */ new Set(["version", "entries", "droppedCount"]);
36155
+ var ENTRY_KEYS = /* @__PURE__ */ new Set([
36156
+ "signalId",
36157
+ "workspaceId",
36158
+ "principalId",
36159
+ "fromId",
36160
+ "fromKind",
36161
+ "kind",
36162
+ "senderName",
36163
+ "body",
36164
+ "attachmentCount",
36165
+ "createdAt",
36166
+ "queuedAt",
36167
+ "observationPending"
36168
+ ]);
35912
36169
  var LISTENER_MAIN_QUEUE_MAX = 200;
35913
36170
  var LISTENER_DEFER_OVER_MIN = 1;
35914
36171
  var LISTENER_DEFER_OVER_MAX = 1e4;
@@ -35939,26 +36196,12 @@ function decideListenerRoute(route, threshold, bodyLength) {
35939
36196
  function checkedTimestamp2(value) {
35940
36197
  return typeof value === "string" && Number.isFinite(Date.parse(value));
35941
36198
  }
35942
- function parseEntry(value) {
36199
+ function parseEntry(value, rejectUnknownKeys) {
35943
36200
  if (!value || typeof value !== "object" || Array.isArray(value)) {
35944
36201
  throw new Error("stored pending-for-main entry is malformed");
35945
36202
  }
35946
36203
  const row = value;
35947
- const allowed = /* @__PURE__ */ new Set([
35948
- "signalId",
35949
- "workspaceId",
35950
- "principalId",
35951
- "fromId",
35952
- "fromKind",
35953
- "kind",
35954
- "senderName",
35955
- "body",
35956
- "attachmentCount",
35957
- "createdAt",
35958
- "queuedAt",
35959
- "observationPending"
35960
- ]);
35961
- if (Object.keys(row).some((key2) => !allowed.has(key2))) {
36204
+ if (rejectUnknownKeys && Object.keys(row).some((key2) => !ENTRY_KEYS.has(key2))) {
35962
36205
  throw new Error("stored pending-for-main entry is malformed");
35963
36206
  }
35964
36207
  if (typeof row.signalId !== "string" || !UUID_RE15.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE15.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE15.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE15.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !(row.attachmentCount === void 0 || typeof row.attachmentCount === "number" && Number.isSafeInteger(row.attachmentCount) && row.attachmentCount >= 1 && row.attachmentCount <= 8) || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt) || !(row.observationPending === void 0 || row.observationPending === true)) {
@@ -35979,7 +36222,7 @@ function parseEntry(value) {
35979
36222
  ...row.observationPending === true ? { observationPending: true } : {}
35980
36223
  };
35981
36224
  }
35982
- function parseFile(raw) {
36225
+ function parseFile(raw, rejectUnknownKeys = false) {
35983
36226
  let value;
35984
36227
  try {
35985
36228
  value = JSON.parse(raw);
@@ -35990,12 +36233,10 @@ function parseFile(raw) {
35990
36233
  throw new Error("stored pending-for-main queue is malformed");
35991
36234
  }
35992
36235
  const row = value;
35993
- if (Object.keys(row).some(
35994
- (key2) => key2 !== "version" && key2 !== "entries" && key2 !== "droppedCount"
35995
- ) || row.version !== 1 || !Array.isArray(row.entries) || row.entries.length > LISTENER_MAIN_QUEUE_MAX || !(row.droppedCount === void 0 || typeof row.droppedCount === "number" && Number.isSafeInteger(row.droppedCount) && row.droppedCount >= 0)) {
36236
+ if (rejectUnknownKeys && Object.keys(row).some((key2) => !QUEUE_KEYS.has(key2)) || row.version !== 1 || !Array.isArray(row.entries) || row.entries.length > LISTENER_MAIN_QUEUE_MAX || !(row.droppedCount === void 0 || typeof row.droppedCount === "number" && Number.isSafeInteger(row.droppedCount) && row.droppedCount >= 0)) {
35996
36237
  throw new Error("stored pending-for-main queue is malformed");
35997
36238
  }
35998
- const entries = row.entries.map(parseEntry);
36239
+ const entries = row.entries.map((entry) => parseEntry(entry, rejectUnknownKeys));
35999
36240
  if (new Set(entries.map((entry) => entry.signalId)).size !== entries.length) {
36000
36241
  throw new Error("stored pending-for-main queue repeats a signal");
36001
36242
  }
@@ -36016,7 +36257,7 @@ var FilePendingMainQueue = class {
36016
36257
  return raw === null ? { version: 1, entries: [], droppedCount: 0 } : parseFile(raw);
36017
36258
  }
36018
36259
  async writeUnlocked(file) {
36019
- const canonical = parseFile(JSON.stringify(file));
36260
+ const canonical = parseFile(JSON.stringify(file), true);
36020
36261
  await writeSecureJsonFile(this.path, JSON.stringify(canonical));
36021
36262
  }
36022
36263
  async read() {
@@ -36030,7 +36271,7 @@ var FilePendingMainQueue = class {
36030
36271
  return { count: file.entries.length, droppedCount: file.droppedCount };
36031
36272
  }
36032
36273
  async enqueue(entry) {
36033
- const checked = parseEntry(entry);
36274
+ const checked = parseEntry(entry, true);
36034
36275
  return await withFileLock(this.directory, QUEUE_LOCK, async () => {
36035
36276
  const file = await this.readUnlocked();
36036
36277
  if (file.entries.some((item) => item.signalId === checked.signalId)) {
@@ -36089,7 +36330,7 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
36089
36330
  createdAt: signal.created_at,
36090
36331
  queuedAt: new Date(now).toISOString(),
36091
36332
  ...options.observationPending ? { observationPending: true } : {}
36092
- });
36333
+ }, true);
36093
36334
  }
36094
36335
 
36095
36336
  // src/listener/runtime.ts
@@ -36102,6 +36343,7 @@ var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ON
36102
36343
  var LISTENER_PROMPT_START_MINIMUM_MS = SIGNAL_READ_TIMEOUT_MS + ACP_DEFAULT_REQUEST_TIMEOUT_MS + LISTENER_REPLY_ONLY_MINIMUM_MS;
36103
36344
  var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
36104
36345
  var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
36346
+ var LISTENER_HOST_PORTS_PROBE_MS = 6e4;
36105
36347
  var UUID_RE16 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
36106
36348
  var ListenerCapabilityError = class extends Error {
36107
36349
  code;
@@ -36536,6 +36778,8 @@ async function runListenerRuntime(options) {
36536
36778
  let ready = false;
36537
36779
  let deliveryMode = null;
36538
36780
  let readAttempt = 0;
36781
+ let readEpisodeStartedAtMs = null;
36782
+ let readEpisodeAttempts = 0;
36539
36783
  const onAbort = () => {
36540
36784
  options.model.cancel();
36541
36785
  };
@@ -36633,6 +36877,18 @@ async function runListenerRuntime(options) {
36633
36877
  }
36634
36878
  });
36635
36879
  requireCapabilities(page);
36880
+ if (ready && readEpisodeStartedAtMs !== null) {
36881
+ const recoveredAtMs = now();
36882
+ options.onEvent?.({
36883
+ type: "read_recovered",
36884
+ attempts: readEpisodeAttempts,
36885
+ durationMs: Math.max(0, recoveredAtMs - readEpisodeStartedAtMs),
36886
+ startedAt: new Date(readEpisodeStartedAtMs).toISOString(),
36887
+ ts: new Date(recoveredAtMs).toISOString()
36888
+ });
36889
+ readEpisodeStartedAtMs = null;
36890
+ readEpisodeAttempts = 0;
36891
+ }
36636
36892
  const nextMode = classifyDeliveryMode(page, durableConfigured);
36637
36893
  if (nextMode !== deliveryMode) {
36638
36894
  deliveryMode = nextMode;
@@ -36653,19 +36909,25 @@ async function runListenerRuntime(options) {
36653
36909
  stop = { reason: "credential", error: asError2(error) };
36654
36910
  break;
36655
36911
  }
36656
- if (isAbort2(error)) {
36657
- stop = { reason: "cancelled" };
36658
- break;
36659
- }
36660
- if (isRetryableFollowError(error)) {
36912
+ const failure = classifySignalReadFailure(error);
36913
+ if (isRetryableFollowError(error) || failure.code === "aborted" || failure.code === "host_ports_exhausted") {
36661
36914
  readAttempt += 1;
36662
- const delayMs = nextFollowBackoffMs(readAttempt, null, random);
36915
+ const delayMs = failure.code === "host_ports_exhausted" ? LISTENER_HOST_PORTS_PROBE_MS : nextFollowBackoffMs(readAttempt, null, random);
36663
36916
  if (ready) {
36917
+ const failedAtMs = now();
36918
+ if (readEpisodeStartedAtMs === null) {
36919
+ readEpisodeStartedAtMs = failedAtMs;
36920
+ readEpisodeAttempts = 0;
36921
+ }
36922
+ readEpisodeAttempts += 1;
36664
36923
  options.onEvent?.({
36665
36924
  type: "read_retry",
36666
36925
  attempt: readAttempt,
36926
+ episodeAttempt: readEpisodeAttempts,
36927
+ episodeStartedAt: new Date(readEpisodeStartedAtMs).toISOString(),
36928
+ failure,
36667
36929
  delayMs,
36668
- ts: eventTime(now)
36930
+ ts: new Date(failedAtMs).toISOString()
36669
36931
  });
36670
36932
  }
36671
36933
  await sleep2(delayMs, abort);
@@ -36688,6 +36950,7 @@ async function runListenerRuntime(options) {
36688
36950
  type: "ready",
36689
36951
  workspaceId: options.workspaceId,
36690
36952
  principalId: options.principalId,
36953
+ cadenceMs: pollMs,
36691
36954
  ts: eventTime(now)
36692
36955
  });
36693
36956
  if (options.declareModel !== void 0) {
@@ -37217,13 +37480,261 @@ async function runListenerRuntime(options) {
37217
37480
  return stop ?? { reason: "cancelled" };
37218
37481
  }
37219
37482
 
37483
+ // src/listener/read-health.ts
37484
+ var HOUR_MS = 60 * 6e4;
37485
+ var MINUTE_MS = 6e4;
37486
+ var HEALTH_WINDOW_MS = 24 * HOUR_MS;
37487
+ var LISTENER_READ_RETRY_HOUR_CAP = 25;
37488
+ var LISTENER_READ_RETRY_MINUTE_CAP = 61;
37489
+ var LISTENER_CLAIM_HOUR_CAP = 25;
37490
+ var LISTENER_THROUGHPUT_LAPSE_RATIO = 0.5;
37491
+ var FAILURE_CODES = /* @__PURE__ */ new Set([
37492
+ "http_status",
37493
+ "no_response",
37494
+ "body_timeout",
37495
+ "malformed_response",
37496
+ "aborted",
37497
+ "host_ports_exhausted",
37498
+ "unclassified"
37499
+ ]);
37500
+ function emptyListenerReadHealth() {
37501
+ return {
37502
+ currentEpisodeStartedAt: null,
37503
+ currentEpisodeAttempts: 0,
37504
+ currentReasonCode: null,
37505
+ currentHttpStatus: null,
37506
+ currentErrorConstructor: null,
37507
+ retryHours: [],
37508
+ retryMinutes: [],
37509
+ claimCadenceMs: null,
37510
+ claimHours: []
37511
+ };
37512
+ }
37513
+ function bucketStart(ts, sizeMs) {
37514
+ const time = Date.parse(ts);
37515
+ return new Date(Math.floor(time / sizeMs) * sizeMs).toISOString();
37516
+ }
37517
+ function trimNewest(rows3, cap) {
37518
+ return rows3.sort((left, right) => Date.parse(left.hourStart) - Date.parse(right.hourStart)).slice(-cap);
37519
+ }
37520
+ function recordRetryHour(rows3, ts, episodeStarted) {
37521
+ const hourStart = bucketStart(ts, HOUR_MS);
37522
+ const next = rows3.map((row) => ({ ...row }));
37523
+ const existing = next.find((row) => row.hourStart === hourStart);
37524
+ if (existing) {
37525
+ existing.retries += 1;
37526
+ if (episodeStarted) existing.episodes += 1;
37527
+ } else {
37528
+ next.push({
37529
+ hourStart,
37530
+ retries: 1,
37531
+ episodes: episodeStarted ? 1 : 0,
37532
+ longestEpisodeAttempts: 0,
37533
+ longestEpisodeDurationMs: 0
37534
+ });
37535
+ }
37536
+ return trimNewest(next, LISTENER_READ_RETRY_HOUR_CAP);
37537
+ }
37538
+ function recordRetryMinute(rows3, ts) {
37539
+ const minuteStart = bucketStart(ts, MINUTE_MS);
37540
+ const next = rows3.map((row) => ({ ...row }));
37541
+ const existing = next.find((row) => row.minuteStart === minuteStart);
37542
+ if (existing) {
37543
+ existing.retries += 1;
37544
+ } else {
37545
+ next.push({ minuteStart, retries: 1 });
37546
+ }
37547
+ return next.sort(
37548
+ (left, right) => Date.parse(left.minuteStart) - Date.parse(right.minuteStart)
37549
+ ).slice(-LISTENER_READ_RETRY_MINUTE_CAP);
37550
+ }
37551
+ function recordListenerReadRetry(health, input) {
37552
+ return {
37553
+ ...health,
37554
+ currentEpisodeStartedAt: input.episodeStartedAt,
37555
+ currentEpisodeAttempts: input.episodeAttempt,
37556
+ currentReasonCode: input.failure.code,
37557
+ currentHttpStatus: input.failure.httpStatus,
37558
+ currentErrorConstructor: input.failure.errorConstructor,
37559
+ retryHours: recordRetryHour(
37560
+ health.retryHours,
37561
+ input.ts,
37562
+ input.episodeAttempt === 1
37563
+ ),
37564
+ retryMinutes: recordRetryMinute(health.retryMinutes, input.ts)
37565
+ };
37566
+ }
37567
+ function recordListenerReadRecovery(health, input) {
37568
+ const hourStart = bucketStart(input.startedAt, HOUR_MS);
37569
+ const retryHours = health.retryHours.map((row) => ({ ...row }));
37570
+ const hour = retryHours.find((row) => row.hourStart === hourStart);
37571
+ if (hour) {
37572
+ if (input.durationMs > hour.longestEpisodeDurationMs || input.durationMs === hour.longestEpisodeDurationMs && input.attempts > hour.longestEpisodeAttempts) {
37573
+ hour.longestEpisodeAttempts = input.attempts;
37574
+ hour.longestEpisodeDurationMs = input.durationMs;
37575
+ }
37576
+ }
37577
+ return {
37578
+ ...health,
37579
+ currentEpisodeStartedAt: null,
37580
+ currentEpisodeAttempts: 0,
37581
+ currentReasonCode: null,
37582
+ currentHttpStatus: null,
37583
+ currentErrorConstructor: null,
37584
+ retryHours: trimNewest(retryHours, LISTENER_READ_RETRY_HOUR_CAP)
37585
+ };
37586
+ }
37587
+ function recordListenerClaimCadence(health, cadenceMs) {
37588
+ return { ...health, claimCadenceMs: cadenceMs };
37589
+ }
37590
+ function recordListenerClaim(health, ts) {
37591
+ const hourStart = bucketStart(ts, HOUR_MS);
37592
+ const claimHours = health.claimHours.map((row) => ({ ...row }));
37593
+ const hour = claimHours.find((row) => row.hourStart === hourStart);
37594
+ if (hour) hour.claims += 1;
37595
+ else claimHours.push({ hourStart, claims: 1 });
37596
+ return {
37597
+ ...health,
37598
+ claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
37599
+ };
37600
+ }
37601
+ function hasExpectedKeys(value, expected, rejectUnknownKeys) {
37602
+ const actual = Object.keys(value);
37603
+ const allowed = new Set(expected);
37604
+ return expected.every((key2) => actual.includes(key2)) && (!rejectUnknownKeys || actual.every((key2) => allowed.has(key2)));
37605
+ }
37606
+ function validTimestamp(value) {
37607
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
37608
+ }
37609
+ function validCount(value) {
37610
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
37611
+ }
37612
+ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
37613
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
37614
+ const row = value;
37615
+ if (!hasExpectedKeys(row, [
37616
+ "currentEpisodeStartedAt",
37617
+ "currentEpisodeAttempts",
37618
+ "currentReasonCode",
37619
+ "currentHttpStatus",
37620
+ "currentErrorConstructor",
37621
+ "retryHours",
37622
+ "retryMinutes",
37623
+ "claimCadenceMs",
37624
+ "claimHours"
37625
+ ], rejectUnknownKeys)) return null;
37626
+ if (!(row.currentEpisodeStartedAt === null || validTimestamp(row.currentEpisodeStartedAt)) || !validCount(row.currentEpisodeAttempts) || !(row.currentReasonCode === null || typeof row.currentReasonCode === "string" && FAILURE_CODES.has(row.currentReasonCode)) || !(row.currentHttpStatus === null || typeof row.currentHttpStatus === "number" && Number.isSafeInteger(row.currentHttpStatus) && row.currentHttpStatus >= 100 && row.currentHttpStatus <= 599) || !(row.currentErrorConstructor === null || typeof row.currentErrorConstructor === "string" && /^[A-Za-z0-9_$-]{1,96}$/.test(row.currentErrorConstructor)) || !(row.claimCadenceMs === null || typeof row.claimCadenceMs === "number" && Number.isSafeInteger(row.claimCadenceMs) && row.claimCadenceMs >= 1) || !Array.isArray(row.retryHours) || row.retryHours.length > LISTENER_READ_RETRY_HOUR_CAP || !Array.isArray(row.retryMinutes) || row.retryMinutes.length > LISTENER_READ_RETRY_MINUTE_CAP || !Array.isArray(row.claimHours) || row.claimHours.length > LISTENER_CLAIM_HOUR_CAP) return null;
37627
+ if (row.currentEpisodeStartedAt === null !== (row.currentEpisodeAttempts === 0) || row.currentEpisodeStartedAt === null !== (row.currentReasonCode === null) || row.currentReasonCode === "http_status" !== (row.currentHttpStatus !== null) || row.currentReasonCode === "unclassified" !== (row.currentErrorConstructor !== null)) return null;
37628
+ for (const value2 of row.retryHours) {
37629
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
37630
+ const hour = value2;
37631
+ if (!hasExpectedKeys(hour, [
37632
+ "hourStart",
37633
+ "retries",
37634
+ "episodes",
37635
+ "longestEpisodeAttempts",
37636
+ "longestEpisodeDurationMs"
37637
+ ], rejectUnknownKeys) || !validTimestamp(hour.hourStart) || !validCount(hour.retries) || !validCount(hour.episodes) || !validCount(hour.longestEpisodeAttempts) || !validCount(hour.longestEpisodeDurationMs)) return null;
37638
+ }
37639
+ for (const value2 of row.retryMinutes) {
37640
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
37641
+ const minute = value2;
37642
+ if (!hasExpectedKeys(minute, ["minuteStart", "retries"], rejectUnknownKeys) || !validTimestamp(minute.minuteStart) || !validCount(minute.retries)) return null;
37643
+ }
37644
+ for (const value2 of row.claimHours) {
37645
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
37646
+ const hour = value2;
37647
+ if (!hasExpectedKeys(hour, ["hourStart", "claims"], rejectUnknownKeys) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
37648
+ }
37649
+ return {
37650
+ currentEpisodeStartedAt: row.currentEpisodeStartedAt,
37651
+ currentEpisodeAttempts: row.currentEpisodeAttempts,
37652
+ currentReasonCode: row.currentReasonCode,
37653
+ currentHttpStatus: row.currentHttpStatus,
37654
+ currentErrorConstructor: row.currentErrorConstructor,
37655
+ retryHours: row.retryHours.map((hour) => ({
37656
+ hourStart: hour.hourStart,
37657
+ retries: hour.retries,
37658
+ episodes: hour.episodes,
37659
+ longestEpisodeAttempts: hour.longestEpisodeAttempts,
37660
+ longestEpisodeDurationMs: hour.longestEpisodeDurationMs
37661
+ })),
37662
+ retryMinutes: row.retryMinutes.map((minute) => ({
37663
+ minuteStart: minute.minuteStart,
37664
+ retries: minute.retries
37665
+ })),
37666
+ claimCadenceMs: row.claimCadenceMs,
37667
+ claimHours: row.claimHours.map((hour) => ({
37668
+ hourStart: hour.hourStart,
37669
+ claims: hour.claims
37670
+ }))
37671
+ };
37672
+ }
37673
+ function summarizeListenerReadHealth(health, readyAt, nowMs) {
37674
+ const windowStart = nowMs - HEALTH_WINDOW_MS;
37675
+ const retryHours = health.retryHours.filter(
37676
+ (row) => Date.parse(row.hourStart) + HOUR_MS > windowStart && Date.parse(row.hourStart) <= nowMs
37677
+ );
37678
+ let episodesLast24h = retryHours.reduce((sum, row) => sum + row.episodes, 0);
37679
+ let longestEpisodeAttemptsLast24h = 0;
37680
+ let longestEpisodeDurationMsLast24h = 0;
37681
+ for (const row of retryHours) {
37682
+ if (row.longestEpisodeDurationMs > longestEpisodeDurationMsLast24h || row.longestEpisodeDurationMs === longestEpisodeDurationMsLast24h && row.longestEpisodeAttempts > longestEpisodeAttemptsLast24h) {
37683
+ longestEpisodeAttemptsLast24h = row.longestEpisodeAttempts;
37684
+ longestEpisodeDurationMsLast24h = row.longestEpisodeDurationMs;
37685
+ }
37686
+ }
37687
+ const currentStartedMs = health.currentEpisodeStartedAt === null ? null : Date.parse(health.currentEpisodeStartedAt);
37688
+ const currentEpisodeDurationMs = currentStartedMs === null ? null : Math.max(0, nowMs - currentStartedMs);
37689
+ if (currentStartedMs !== null && currentStartedMs >= windowStart && currentEpisodeDurationMs !== null && (currentEpisodeDurationMs > longestEpisodeDurationMsLast24h || currentEpisodeDurationMs === longestEpisodeDurationMsLast24h && health.currentEpisodeAttempts > longestEpisodeAttemptsLast24h)) {
37690
+ longestEpisodeAttemptsLast24h = health.currentEpisodeAttempts;
37691
+ longestEpisodeDurationMsLast24h = currentEpisodeDurationMs;
37692
+ }
37693
+ const rollingMinuteStart = Math.floor((nowMs - HOUR_MS) / MINUTE_MS) * MINUTE_MS;
37694
+ const retriesLastHour = health.retryMinutes.reduce((sum, row) => Date.parse(row.minuteStart) >= rollingMinuteStart && Date.parse(row.minuteStart) <= nowMs ? sum + row.retries : sum, 0);
37695
+ const claimThroughputHours = [];
37696
+ if (health.claimCadenceMs !== null && readyAt !== null) {
37697
+ const readyMs = Date.parse(readyAt);
37698
+ const firstFullHour = Math.ceil(readyMs / HOUR_MS) * HOUR_MS;
37699
+ const currentHour = Math.floor(nowMs / HOUR_MS) * HOUR_MS;
37700
+ const first = Math.max(firstFullHour, currentHour - HEALTH_WINDOW_MS);
37701
+ const claimsByHour = new Map(
37702
+ health.claimHours.map((row) => [row.hourStart, row.claims])
37703
+ );
37704
+ const expectedClaims = HOUR_MS / health.claimCadenceMs;
37705
+ for (let hour = first; hour < currentHour; hour += HOUR_MS) {
37706
+ const hourStart = new Date(hour).toISOString();
37707
+ const claims = claimsByHour.get(hourStart) ?? 0;
37708
+ claimThroughputHours.push({
37709
+ hourStart,
37710
+ claims,
37711
+ expectedClaims,
37712
+ ratio: claims / expectedClaims
37713
+ });
37714
+ }
37715
+ }
37716
+ const throughputLapseHours = claimThroughputHours.filter(
37717
+ (hour) => hour.ratio < LISTENER_THROUGHPUT_LAPSE_RATIO
37718
+ );
37719
+ return {
37720
+ currentEpisodeDurationMs,
37721
+ episodesLast24h,
37722
+ longestEpisodeAttemptsLast24h,
37723
+ longestEpisodeDurationMsLast24h,
37724
+ retriesLastHour,
37725
+ retryHours,
37726
+ claimThroughputHours,
37727
+ throughputLapseHours
37728
+ };
37729
+ }
37730
+
37220
37731
  // src/listener/control.ts
37221
37732
  var import_node_net = require("node:net");
37222
37733
  var import_promises9 = require("node:fs/promises");
37223
37734
  var import_node_path16 = require("node:path");
37224
37735
  var UUID_RE17 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
37225
37736
  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-]+)*)?$/;
37226
- var MAX_STATUS_BYTES = 16 * 1024;
37737
+ var MAX_STATUS_BYTES = 32 * 1024;
37227
37738
  var MAX_CONTROL_BYTES = 8 * 1024;
37228
37739
  var CONTROL_TIMEOUT_MS = 2e3;
37229
37740
  var START_LOCK_WAIT_MS = 2e3;
@@ -37270,8 +37781,13 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
37270
37781
  "lastSignalId",
37271
37782
  "lastErrorCode",
37272
37783
  "lastErrorDetail",
37784
+ "lastErrorReasonCode",
37785
+ "providerExecutable",
37273
37786
  "providerVersion",
37274
37787
  "providerLastMeasuredVersion",
37788
+ "providerBundledAgentSdkVersion",
37789
+ "providerBundledClaudeCodeVersion",
37790
+ "providerMinimumRequiredVersion",
37275
37791
  "cswarmVersion",
37276
37792
  "lastWorkerStderrTail",
37277
37793
  "logPath",
@@ -37284,7 +37800,10 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
37284
37800
  "routeMode",
37285
37801
  "deferOverChars",
37286
37802
  "pendingForMainCount",
37287
- "droppedForMainCount"
37803
+ "droppedForMainCount",
37804
+ "readHealth",
37805
+ "connectionsOpened",
37806
+ "connectionReuseRatio"
37288
37807
  ]);
37289
37808
  var STATUS_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
37290
37809
  "leaseId",
@@ -37310,7 +37829,7 @@ var STATUS_DELIVERY_KEYS = [
37310
37829
  "lastClaimAt",
37311
37830
  "lastAckAt"
37312
37831
  ];
37313
- function parseStatus(raw) {
37832
+ function parseStatus(raw, rejectUnknownKeys = false) {
37314
37833
  let value;
37315
37834
  try {
37316
37835
  value = JSON.parse(raw);
@@ -37325,14 +37844,15 @@ function parseStatus(raw) {
37325
37844
  if (STATUS_SENSITIVE_KEYS.has(key2)) {
37326
37845
  throw new Error("stored listener status contains a forbidden field");
37327
37846
  }
37328
- if (!STATUS_ALLOWED_KEYS.has(key2)) {
37847
+ if (rejectUnknownKeys && !STATUS_ALLOWED_KEYS.has(key2)) {
37329
37848
  throw new Error("stored listener status is malformed");
37330
37849
  }
37331
37850
  }
37332
37851
  const nullableUuid3 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE17.test(candidate);
37333
37852
  const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
37334
37853
  const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
37335
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE17.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE17.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE17.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.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.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.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)) {
37854
+ const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
37855
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE17.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE17.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE17.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.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)) {
37336
37856
  throw new Error("stored listener status is malformed");
37337
37857
  }
37338
37858
  const routeMode = row.routeMode ?? "worker";
@@ -37340,8 +37860,11 @@ function parseStatus(raw) {
37340
37860
  if (routeMode === "split" && deferOverChars === null || routeMode !== "split" && deferOverChars !== null) {
37341
37861
  throw new Error("stored listener status routing fields are malformed");
37342
37862
  }
37863
+ const knownRow = Object.fromEntries(
37864
+ Object.entries(row).filter(([key2]) => STATUS_ALLOWED_KEYS.has(key2))
37865
+ );
37343
37866
  return {
37344
- ...row,
37867
+ ...knownRow,
37345
37868
  deliveryMode: row.deliveryMode ?? null,
37346
37869
  pendingDeliveryCount: row.pendingDeliveryCount ?? null,
37347
37870
  lastTerminalDeliveryFailureCount: row.lastTerminalDeliveryFailureCount ?? null,
@@ -37356,11 +37879,15 @@ function parseStatus(raw) {
37356
37879
  routeMode,
37357
37880
  deferOverChars,
37358
37881
  pendingForMainCount: row.pendingForMainCount ?? 0,
37359
- droppedForMainCount: row.droppedForMainCount ?? 0
37882
+ droppedForMainCount: row.droppedForMainCount ?? 0,
37883
+ ...readHealth === void 0 ? {} : { readHealth }
37360
37884
  };
37361
37885
  }
37362
37886
  async function writeListenerStatus(paths, status) {
37363
37887
  const serialized = JSON.stringify(status);
37888
+ if (Buffer.byteLength(serialized, "utf8") > MAX_STATUS_BYTES) {
37889
+ throw new Error("listener status is too large");
37890
+ }
37364
37891
  const parsed = JSON.parse(serialized);
37365
37892
  for (const key2 of STATUS_DELIVERY_KEYS) {
37366
37893
  if (!(key2 in parsed)) {
@@ -37370,7 +37897,7 @@ async function writeListenerStatus(paths, status) {
37370
37897
  if (!("lastErrorDetail" in parsed)) {
37371
37898
  throw new Error("listener status is missing local error detail metadata");
37372
37899
  }
37373
- parseStatus(serialized);
37900
+ parseStatus(serialized, true);
37374
37901
  await writeSecureJsonFile(paths.statusPath, serialized);
37375
37902
  }
37376
37903
  async function readListenerStatus(paths) {
@@ -37395,6 +37922,12 @@ async function appendListenerEvent(paths, event) {
37395
37922
  "passed",
37396
37923
  "reason",
37397
37924
  "delay_ms",
37925
+ "reason_code",
37926
+ "http_status",
37927
+ "error_constructor",
37928
+ "episode_attempt",
37929
+ "attempts",
37930
+ "duration_ms",
37398
37931
  "index",
37399
37932
  "delivery_mode",
37400
37933
  "pending_delivery_count",
@@ -37433,6 +37966,29 @@ async function appendListenerEvent(paths, event) {
37433
37966
  if ((key2 === "attempt" || key2 === "total") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
37434
37967
  throw new Error("listener event attempt count is not allowed");
37435
37968
  }
37969
+ if ((key2 === "episode_attempt" || key2 === "attempts") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
37970
+ throw new Error("listener event episode count is not allowed");
37971
+ }
37972
+ if (key2 === "duration_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
37973
+ throw new Error("listener event episode duration is not allowed");
37974
+ }
37975
+ if (key2 === "http_status" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 100 && value <= 599)) {
37976
+ throw new Error("listener event HTTP status is not allowed");
37977
+ }
37978
+ if (key2 === "reason_code" && !(typeof value === "string" && [
37979
+ "http_status",
37980
+ "no_response",
37981
+ "body_timeout",
37982
+ "malformed_response",
37983
+ "aborted",
37984
+ "host_ports_exhausted",
37985
+ "unclassified"
37986
+ ].includes(value))) {
37987
+ throw new Error("listener event read reason code is not allowed");
37988
+ }
37989
+ if (key2 === "error_constructor" && !(typeof value === "string" && /^[A-Za-z0-9_$-]{1,96}$/.test(value))) {
37990
+ throw new Error("listener event error constructor is not allowed");
37991
+ }
37436
37992
  if (key2 === "passed" && typeof value !== "boolean") {
37437
37993
  throw new Error("listener event canary result is not allowed");
37438
37994
  }
@@ -37476,6 +38032,12 @@ async function appendListenerEvent(paths, event) {
37476
38032
  if (event.event === "listener_canary_attempt" && (typeof event.attempt !== "number" || typeof event.total !== "number" || event.attempt > event.total || typeof event.passed !== "boolean" || !(event.reason === null || typeof event.reason === "string"))) {
37477
38033
  throw new Error("listener canary attempt event is incomplete");
37478
38034
  }
38035
+ if (event.event === "listener_read_retry" && (typeof event.reason_code !== "string" || typeof event.episode_attempt !== "number" || event.reason_code === "http_status" !== (typeof event.http_status === "number") || event.reason_code === "unclassified" !== (typeof event.error_constructor === "string"))) {
38036
+ throw new Error("listener read retry event is incomplete");
38037
+ }
38038
+ if (event.event === "listener_read_recovered" && (typeof event.attempts !== "number" || typeof event.duration_ms !== "number")) {
38039
+ throw new Error("listener read recovery event is incomplete");
38040
+ }
37479
38041
  await ensureSecureStateDirectory(paths.instanceDirectory);
37480
38042
  const serialized = `${JSON.stringify(event)}
37481
38043
  `;
@@ -37760,6 +38322,28 @@ function localDiagnostic(message, maxChars) {
37760
38322
  function safeErrorDetail(error) {
37761
38323
  return localDiagnostic(error.message, 2048);
37762
38324
  }
38325
+ function providerStatusFields(notice) {
38326
+ if (!notice) return {};
38327
+ return {
38328
+ providerExecutable: notice.executable ?? null,
38329
+ providerVersion: notice.runningVersion,
38330
+ providerLastMeasuredVersion: notice.runningVersion === null ? null : notice.lastMeasuredVersion,
38331
+ providerBundledAgentSdkVersion: notice.bundledAgentSdkVersion ?? null,
38332
+ providerBundledClaudeCodeVersion: notice.bundledClaudeCodeVersion ?? null
38333
+ };
38334
+ }
38335
+ function providerFailureFields(error) {
38336
+ if (!(error instanceof AcpPermissionCanaryError)) {
38337
+ return {
38338
+ lastErrorReasonCode: null,
38339
+ providerMinimumRequiredVersion: null
38340
+ };
38341
+ }
38342
+ return {
38343
+ lastErrorReasonCode: error.reasonCode,
38344
+ providerMinimumRequiredVersion: error.minimumRequiredVersion
38345
+ };
38346
+ }
37763
38347
  var TAIL_SERIALIZED_BUDGET_BYTES = 3e3;
37764
38348
  function fitWorkerStderrTailForLog(tail) {
37765
38349
  let fitted = tail.trim();
@@ -37800,8 +38384,13 @@ async function runListenerSupervisor(options) {
37800
38384
  lastSignalId: null,
37801
38385
  lastErrorCode: null,
37802
38386
  lastErrorDetail: null,
38387
+ lastErrorReasonCode: null,
38388
+ providerExecutable: null,
37803
38389
  providerVersion: null,
37804
38390
  providerLastMeasuredVersion: null,
38391
+ providerBundledAgentSdkVersion: null,
38392
+ providerBundledClaudeCodeVersion: null,
38393
+ providerMinimumRequiredVersion: null,
37805
38394
  lastWorkerStderrTail: null,
37806
38395
  deliveryMode: null,
37807
38396
  pendingDeliveryCount: null,
@@ -37813,14 +38402,27 @@ async function runListenerSupervisor(options) {
37813
38402
  deferOverChars: options.deferOverChars ?? null,
37814
38403
  pendingForMainCount: 0,
37815
38404
  droppedForMainCount: 0,
38405
+ readHealth: emptyListenerReadHealth(),
38406
+ connectionsOpened: 0,
38407
+ connectionReuseRatio: 0,
37816
38408
  logPath: options.paths.logPath
37817
38409
  };
37818
38410
  let writes = Promise.resolve();
37819
38411
  const chain = (work) => {
37820
38412
  writes = writes.then(work).catch(() => void 0);
37821
38413
  };
38414
+ const statusSnapshot = () => {
38415
+ const metrics = options.getConnectionMetrics?.();
38416
+ return {
38417
+ ...structuredClone(status),
38418
+ ...metrics ? {
38419
+ connectionsOpened: metrics.connectionsOpened,
38420
+ connectionReuseRatio: metrics.connectionReuseRatio
38421
+ } : {}
38422
+ };
38423
+ };
37822
38424
  const persist = () => {
37823
- const snapshot = structuredClone(status);
38425
+ const snapshot = statusSnapshot();
37824
38426
  chain(() => writeListenerStatus(options.paths, snapshot));
37825
38427
  };
37826
38428
  const log = (event) => {
@@ -37838,7 +38440,7 @@ async function runListenerSupervisor(options) {
37838
38440
  const prepare = options.prepare;
37839
38441
  const control = await startListenerControlServer({
37840
38442
  paths: options.paths,
37841
- status: () => structuredClone(status),
38443
+ status: statusSnapshot,
37842
38444
  stop: () => {
37843
38445
  if (status.state === "stopped" || status.state === "failed") return;
37844
38446
  transition("stopping");
@@ -37876,9 +38478,16 @@ async function runListenerSupervisor(options) {
37876
38478
  readyAt: event.ts,
37877
38479
  lastErrorCode: null,
37878
38480
  lastErrorDetail: null,
38481
+ lastErrorReasonCode: null,
37879
38482
  lastWorkerStderrTail: null,
37880
- providerVersion: versionNotice?.runningVersion ?? null,
37881
- providerLastMeasuredVersion: versionNotice?.lastMeasuredVersion ?? null
38483
+ providerMinimumRequiredVersion: null,
38484
+ ...providerStatusFields(versionNotice),
38485
+ ...event.cadenceMs === void 0 ? {} : {
38486
+ readHealth: recordListenerClaimCadence(
38487
+ status.readHealth ?? emptyListenerReadHealth(),
38488
+ event.cadenceMs
38489
+ )
38490
+ }
37882
38491
  });
37883
38492
  log({ ts: event.ts, event: "listener_ready" });
37884
38493
  return;
@@ -37920,14 +38529,54 @@ async function runListenerSupervisor(options) {
37920
38529
  return;
37921
38530
  }
37922
38531
  if (event.type === "read_retry") {
38532
+ status = {
38533
+ ...status,
38534
+ readHealth: recordListenerReadRetry(
38535
+ status.readHealth ?? emptyListenerReadHealth(),
38536
+ {
38537
+ ts: event.ts,
38538
+ episodeStartedAt: event.episodeStartedAt,
38539
+ episodeAttempt: event.episodeAttempt,
38540
+ failure: event.failure
38541
+ }
38542
+ ),
38543
+ updatedAt: event.ts
38544
+ };
38545
+ persist();
37923
38546
  log({
37924
38547
  ts: event.ts,
37925
38548
  event: "listener_read_retry",
37926
38549
  attempt: event.attempt,
38550
+ episode_attempt: event.episodeAttempt,
38551
+ reason_code: event.failure.code,
38552
+ ...event.failure.httpStatus === null ? {} : { http_status: event.failure.httpStatus },
38553
+ ...event.failure.errorConstructor === null ? {} : { error_constructor: event.failure.errorConstructor },
37927
38554
  delay_ms: event.delayMs
37928
38555
  });
37929
38556
  return;
37930
38557
  }
38558
+ if (event.type === "read_recovered") {
38559
+ status = {
38560
+ ...status,
38561
+ readHealth: recordListenerReadRecovery(
38562
+ status.readHealth ?? emptyListenerReadHealth(),
38563
+ {
38564
+ startedAt: event.startedAt,
38565
+ attempts: event.attempts,
38566
+ durationMs: event.durationMs
38567
+ }
38568
+ ),
38569
+ updatedAt: event.ts
38570
+ };
38571
+ persist();
38572
+ log({
38573
+ ts: event.ts,
38574
+ event: "listener_read_recovered",
38575
+ attempts: event.attempts,
38576
+ duration_ms: event.durationMs
38577
+ });
38578
+ return;
38579
+ }
37931
38580
  if (event.type === "malformed_row") {
37932
38581
  log({
37933
38582
  ts: event.ts,
@@ -37964,6 +38613,10 @@ async function runListenerSupervisor(options) {
37964
38613
  if (event.type === "delivery_claim") {
37965
38614
  status = {
37966
38615
  ...status,
38616
+ readHealth: recordListenerClaim(
38617
+ status.readHealth ?? emptyListenerReadHealth(),
38618
+ event.ts
38619
+ ),
37967
38620
  pendingDeliveryCount: event.pendingDeliveryCount,
37968
38621
  lastClaimAt: event.ts,
37969
38622
  updatedAt: event.ts
@@ -38088,9 +38741,9 @@ async function runListenerSupervisor(options) {
38088
38741
  readyAt: null,
38089
38742
  lastErrorCode: restartCode,
38090
38743
  lastErrorDetail: safeErrorDetail(stop.error),
38744
+ ...providerFailureFields(stop.error),
38091
38745
  lastWorkerStderrTail: restartStderrTail,
38092
- providerVersion: null,
38093
- providerLastMeasuredVersion: null
38746
+ ...providerStatusFields(options.getProviderVersionNotice?.() ?? null)
38094
38747
  });
38095
38748
  await restartSleep(delayMs, controller.signal);
38096
38749
  if (controller.signal.aborted) {
@@ -38104,7 +38757,9 @@ async function runListenerSupervisor(options) {
38104
38757
  stoppedAt,
38105
38758
  lastErrorCode: null,
38106
38759
  lastErrorDetail: null,
38107
- lastWorkerStderrTail: null
38760
+ lastErrorReasonCode: null,
38761
+ lastWorkerStderrTail: null,
38762
+ providerMinimumRequiredVersion: null
38108
38763
  });
38109
38764
  log({ ts: stoppedAt, event: "listener_stopped" });
38110
38765
  } else {
@@ -38114,6 +38769,8 @@ async function runListenerSupervisor(options) {
38114
38769
  stoppedAt,
38115
38770
  lastErrorCode: code,
38116
38771
  lastErrorDetail: safeErrorDetail(stop.error),
38772
+ ...providerFailureFields(stop.error),
38773
+ ...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
38117
38774
  lastWorkerStderrTail: failedStderrTail
38118
38775
  });
38119
38776
  log({
@@ -38138,6 +38795,10 @@ async function runListenerSupervisor(options) {
38138
38795
  lastErrorDetail: safeErrorDetail(
38139
38796
  error instanceof Error ? error : new Error(String(error))
38140
38797
  ),
38798
+ ...providerFailureFields(
38799
+ error instanceof Error ? error : new Error(String(error))
38800
+ ),
38801
+ ...providerStatusFields(options.getProviderVersionNotice?.() ?? null),
38141
38802
  lastWorkerStderrTail: failedStderrTail
38142
38803
  });
38143
38804
  log({
@@ -38150,7 +38811,7 @@ async function runListenerSupervisor(options) {
38150
38811
  await writes.catch(() => void 0);
38151
38812
  await control.close().catch(() => void 0);
38152
38813
  }
38153
- return status;
38814
+ return statusSnapshot();
38154
38815
  }
38155
38816
  async function effectiveListenerStatus(paths) {
38156
38817
  try {
@@ -38383,7 +39044,7 @@ function assertPlainObject(input, allowedKeys, requiredKeys, errMessage = "deliv
38383
39044
  throw new Error(errMessage);
38384
39045
  }
38385
39046
  }
38386
- function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
39047
+ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId, rejectUnknownKeys = false) {
38387
39048
  if (Buffer.byteLength(raw, "utf8") > MAX_JOURNAL_BYTES) {
38388
39049
  throw new Error("stored delivery journal is malformed");
38389
39050
  }
@@ -38404,7 +39065,7 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
38404
39065
  throw new Error("stored delivery journal is malformed");
38405
39066
  }
38406
39067
  const ownProps = Object.getOwnPropertyNames(value);
38407
- if (ownProps.length !== ALLOWED_TOP_KEYS.size || !ownProps.every((p) => ALLOWED_TOP_KEYS.has(p))) {
39068
+ if ([...ALLOWED_TOP_KEYS].some((key2) => !ownProps.includes(key2)) || rejectUnknownKeys && !ownProps.every((key2) => ALLOWED_TOP_KEYS.has(key2))) {
38408
39069
  throw new Error("stored delivery journal is malformed");
38409
39070
  }
38410
39071
  for (const prop of ownProps) {
@@ -38438,8 +39099,16 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
38438
39099
  if (!isValidIsoTimestamp(row.updatedAt)) {
38439
39100
  throw new Error("stored delivery journal is malformed");
38440
39101
  }
39102
+ const base = {
39103
+ version: 1,
39104
+ workspaceId: row.workspaceId,
39105
+ principalId: row.principalId,
39106
+ listenerInstanceId: row.listenerInstanceId,
39107
+ nextClaimOrdinal: row.nextClaimOrdinal,
39108
+ updatedAt: row.updatedAt
39109
+ };
38441
39110
  if (row.active === null) {
38442
- return row;
39111
+ return { ...base, active: null };
38443
39112
  }
38444
39113
  if (typeof row.active !== "object" || row.active === null || Array.isArray(row.active)) {
38445
39114
  throw new Error("stored delivery journal is malformed");
@@ -38452,8 +39121,10 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
38452
39121
  throw new Error("stored delivery journal is malformed");
38453
39122
  }
38454
39123
  const activeProps = Object.getOwnPropertyNames(row.active);
38455
- const legacyWithoutFingerprint = activeProps.length === ALLOWED_ACTIVE_KEYS.size - 1 && !activeProps.includes("signalFingerprint");
38456
- if (!legacyWithoutFingerprint && activeProps.length !== ALLOWED_ACTIVE_KEYS.size || !activeProps.every((p) => ALLOWED_ACTIVE_KEYS.has(p))) {
39124
+ const requiredActiveKeys = [...ALLOWED_ACTIVE_KEYS].filter(
39125
+ (key2) => key2 !== "signalFingerprint"
39126
+ );
39127
+ if (requiredActiveKeys.some((key2) => !activeProps.includes(key2)) || rejectUnknownKeys && !activeProps.every((key2) => ALLOWED_ACTIVE_KEYS.has(key2))) {
38457
39128
  throw new Error("stored delivery journal is malformed");
38458
39129
  }
38459
39130
  for (const prop of activeProps) {
@@ -38521,7 +39192,7 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
38521
39192
  throw new Error("stored delivery journal is malformed");
38522
39193
  }
38523
39194
  const ackProps = Object.getOwnPropertyNames(active.ack);
38524
- if (ackProps.length !== ALLOWED_ACK_KEYS.size || !ackProps.every((p) => ALLOWED_ACK_KEYS.has(p))) {
39195
+ if ([...ALLOWED_ACK_KEYS].some((key2) => !ackProps.includes(key2)) || rejectUnknownKeys && !ackProps.every((key2) => ALLOWED_ACK_KEYS.has(key2))) {
38525
39196
  throw new Error("stored delivery journal is malformed");
38526
39197
  }
38527
39198
  for (const prop of ackProps) {
@@ -38530,32 +39201,52 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
38530
39201
  throw new Error("stored delivery journal is malformed");
38531
39202
  }
38532
39203
  }
38533
- const ack = active.ack;
38534
- if (typeof ack.commandId !== "string" || !COMMAND_ID_RE3.test(ack.commandId)) {
39204
+ const ack2 = active.ack;
39205
+ if (typeof ack2.commandId !== "string" || !COMMAND_ID_RE3.test(ack2.commandId)) {
38535
39206
  throw new Error("stored delivery journal is malformed");
38536
39207
  }
38537
39208
  const expectedAckCmdId = ackCommandId(active.leaseId);
38538
- if (ack.commandId !== expectedAckCmdId) {
39209
+ if (ack2.commandId !== expectedAckCmdId) {
38539
39210
  throw new Error("stored delivery journal is malformed");
38540
39211
  }
38541
- if (typeof ack.outcome !== "string" || !ALLOWED_OUTCOMES.has(ack.outcome)) {
39212
+ if (typeof ack2.outcome !== "string" || !ALLOWED_OUTCOMES.has(ack2.outcome)) {
38542
39213
  throw new Error("stored delivery journal is malformed");
38543
39214
  }
38544
- if (ack.outcome === "failed_terminal") {
38545
- if (typeof ack.lastErrorCode !== "string" || !ALLOWED_ERROR_CODES.has(ack.lastErrorCode)) {
39215
+ if (ack2.outcome === "failed_terminal") {
39216
+ if (typeof ack2.lastErrorCode !== "string" || !ALLOWED_ERROR_CODES.has(ack2.lastErrorCode)) {
38546
39217
  throw new Error("stored delivery journal is malformed");
38547
39218
  }
38548
39219
  } else {
38549
- if (ack.lastErrorCode !== null) {
39220
+ if (ack2.lastErrorCode !== null) {
38550
39221
  throw new Error("stored delivery journal is malformed");
38551
39222
  }
38552
39223
  }
38553
- if (!isValidIsoTimestamp(ack.preparedAt)) {
39224
+ if (!isValidIsoTimestamp(ack2.preparedAt)) {
38554
39225
  throw new Error("stored delivery journal is malformed");
38555
39226
  }
38556
39227
  }
38557
39228
  }
38558
- return row;
39229
+ const ack = active.ack;
39230
+ return {
39231
+ ...base,
39232
+ active: {
39233
+ phase: active.phase,
39234
+ claimOrdinal: active.claimOrdinal,
39235
+ claimCommandId: active.claimCommandId,
39236
+ claimCreatedAt: active.claimCreatedAt,
39237
+ claimLastAttemptAt: active.claimLastAttemptAt,
39238
+ signalId: active.signalId,
39239
+ leaseId: active.leaseId,
39240
+ leasedUntil: active.leasedUntil,
39241
+ ...active.signalFingerprint === void 0 ? {} : { signalFingerprint: active.signalFingerprint },
39242
+ ack: ack === null ? null : {
39243
+ commandId: ack.commandId,
39244
+ outcome: ack.outcome,
39245
+ lastErrorCode: ack.lastErrorCode,
39246
+ preparedAt: ack.preparedAt
39247
+ }
39248
+ }
39249
+ };
38559
39250
  }
38560
39251
  var FileListenerDeliveryJournal = class {
38561
39252
  instanceDirectory;
@@ -38619,7 +39310,8 @@ var FileListenerDeliveryJournal = class {
38619
39310
  parseJournalRecord(
38620
39311
  serialized,
38621
39312
  this.options.workspaceId,
38622
- this.options.principalId
39313
+ this.options.principalId,
39314
+ true
38623
39315
  );
38624
39316
  await writeSecureJsonFile(this.journalPath, serialized);
38625
39317
  }
@@ -38869,7 +39561,8 @@ async function openListenerDeliveryJournal(options) {
38869
39561
  parseJournalRecord(
38870
39562
  serialized2,
38871
39563
  workspaceIdSnapshot,
38872
- principalIdSnapshot
39564
+ principalIdSnapshot,
39565
+ true
38873
39566
  );
38874
39567
  await writeSecureJsonFile(journal.journalPath, serialized2);
38875
39568
  return {
@@ -38903,7 +39596,8 @@ async function openListenerDeliveryJournal(options) {
38903
39596
  parseJournalRecord(
38904
39597
  serialized,
38905
39598
  workspaceIdSnapshot,
38906
- principalIdSnapshot
39599
+ principalIdSnapshot,
39600
+ true
38907
39601
  );
38908
39602
  await writeSecureJsonFile(journal.journalPath, serialized);
38909
39603
  return {
@@ -39059,7 +39753,7 @@ function parseState(raw) {
39059
39753
  }
39060
39754
  const row = value;
39061
39755
  const topicVersions = row.topicVersions;
39062
- if (Object.keys(row).sort().join(",") !== "principalId,topicVersions,version" || row.version !== 1 || typeof row.principalId !== "string" || !UUID_RE20.test(row.principalId) || !topicVersions || typeof topicVersions !== "object" || Array.isArray(topicVersions) || Object.keys(topicVersions).length > MAX_BRAIN_DIGEST_TOPICS) {
39756
+ if (row.version !== 1 || typeof row.principalId !== "string" || !UUID_RE20.test(row.principalId) || !topicVersions || typeof topicVersions !== "object" || Array.isArray(topicVersions) || Object.keys(topicVersions).length > MAX_BRAIN_DIGEST_TOPICS) {
39063
39757
  throw new Error("stored brain digest state is malformed");
39064
39758
  }
39065
39759
  for (const [topic, version3] of Object.entries(topicVersions)) {
@@ -39171,7 +39865,26 @@ function exactKeys2(row, keys) {
39171
39865
  const expected = new Set(keys);
39172
39866
  return Object.keys(row).length === expected.size && Object.keys(row).every((key2) => expected.has(key2));
39173
39867
  }
39174
- function parseListenerCredential(raw) {
39868
+ function hasRequiredKeys(row, keys) {
39869
+ return keys.every((key2) => Object.hasOwn(row, key2));
39870
+ }
39871
+ var LISTENER_CREDENTIAL_KEYS = [
39872
+ "version",
39873
+ "profileId",
39874
+ "targetUrl",
39875
+ "anonKey",
39876
+ "workspaceId",
39877
+ "principalId",
39878
+ "credential",
39879
+ "updatedAt"
39880
+ ];
39881
+ var HOOK_SURFACE_KEYS = /* @__PURE__ */ new Set([
39882
+ "version",
39883
+ "surfacedSignalIds",
39884
+ "reportedDroppedCount",
39885
+ "credentialFailureReported"
39886
+ ]);
39887
+ function parseListenerCredential(raw, rejectUnknownKeys = false) {
39175
39888
  let value;
39176
39889
  try {
39177
39890
  value = JSON.parse(raw);
@@ -39182,16 +39895,7 @@ function parseListenerCredential(raw) {
39182
39895
  throw new Error("stored listener hook credential is malformed");
39183
39896
  }
39184
39897
  const row = value;
39185
- if (!exactKeys2(row, [
39186
- "version",
39187
- "profileId",
39188
- "targetUrl",
39189
- "anonKey",
39190
- "workspaceId",
39191
- "principalId",
39192
- "credential",
39193
- "updatedAt"
39194
- ]) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !UUID_RE21.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE21.test(row.principalId) || typeof row.credential !== "string" || !TOKEN_RE.test(row.credential) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
39898
+ if (!(rejectUnknownKeys ? exactKeys2(row, LISTENER_CREDENTIAL_KEYS) : hasRequiredKeys(row, LISTENER_CREDENTIAL_KEYS)) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !UUID_RE21.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE21.test(row.principalId) || typeof row.credential !== "string" || !TOKEN_RE.test(row.credential) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
39195
39899
  throw new Error("stored listener hook credential is malformed");
39196
39900
  }
39197
39901
  const target2 = cloudTarget(row.targetUrl, row.anonKey);
@@ -39222,7 +39926,7 @@ async function writeListenerCredentialState(instanceDirectory, input) {
39222
39926
  principalId: input.principalId,
39223
39927
  credential: input.credential,
39224
39928
  updatedAt: new Date(input.now ?? Date.now()).toISOString()
39225
- }));
39929
+ }), true);
39226
39930
  await writeSecureJsonFile(
39227
39931
  (0, import_node_path20.join)(instanceDirectory, LISTENER_CREDENTIAL_FILE),
39228
39932
  JSON.stringify(record)
@@ -39238,7 +39942,7 @@ async function readListenerCredentialState(instanceDirectory) {
39238
39942
  );
39239
39943
  return raw === null ? null : parseListenerCredential(raw);
39240
39944
  }
39241
- function parseSurface(raw) {
39945
+ function parseSurface(raw, rejectUnknownKeys = false) {
39242
39946
  let value;
39243
39947
  try {
39244
39948
  value = JSON.parse(raw);
@@ -39249,9 +39953,7 @@ function parseSurface(raw) {
39249
39953
  throw new Error("stored listener hook surface state is malformed");
39250
39954
  }
39251
39955
  const row = value;
39252
- if (Object.keys(row).some(
39253
- (key2) => key2 !== "version" && key2 !== "surfacedSignalIds" && key2 !== "reportedDroppedCount" && key2 !== "credentialFailureReported"
39254
- ) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !UUID_RE21.test(id)) || !(row.reportedDroppedCount === void 0 || typeof row.reportedDroppedCount === "number" && Number.isSafeInteger(row.reportedDroppedCount) && row.reportedDroppedCount >= 0) || !(row.credentialFailureReported === void 0 || typeof row.credentialFailureReported === "boolean")) {
39956
+ if (rejectUnknownKeys && Object.keys(row).some((key2) => !HOOK_SURFACE_KEYS.has(key2)) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !UUID_RE21.test(id)) || !(row.reportedDroppedCount === void 0 || typeof row.reportedDroppedCount === "number" && Number.isSafeInteger(row.reportedDroppedCount) && row.reportedDroppedCount >= 0) || !(row.credentialFailureReported === void 0 || typeof row.credentialFailureReported === "boolean")) {
39255
39957
  throw new Error("stored listener hook surface state is malformed");
39256
39958
  }
39257
39959
  const ids = row.surfacedSignalIds.map((id) => String(id).toLowerCase());
@@ -39345,7 +40047,7 @@ var FileHookSurfaceStore = class {
39345
40047
  surfacedSignalIds: [...seen].slice(-HOOK_SURFACED_IDS_MAX),
39346
40048
  reportedDroppedCount: options.droppedCount ?? state.reportedDroppedCount,
39347
40049
  credentialFailureReported: options.credentialFailureReported ?? state.credentialFailureReported
39348
- })))
40050
+ }), true))
39349
40051
  );
39350
40052
  }, { timeoutMs: HOOK_LOCK_TIMEOUT_MS });
39351
40053
  }
@@ -39368,7 +40070,7 @@ function parseGlobalState(raw) {
39368
40070
  throw new Error("stored hook cooldown state is malformed");
39369
40071
  }
39370
40072
  const row = value;
39371
- if (!exactKeys2(row, ["version", "lastCheckAt"]) || row.version !== 1 || typeof row.lastCheckAt !== "number" || !Number.isSafeInteger(row.lastCheckAt) || row.lastCheckAt < 0) {
40073
+ if (!hasRequiredKeys(row, ["version", "lastCheckAt"]) || row.version !== 1 || typeof row.lastCheckAt !== "number" || !Number.isSafeInteger(row.lastCheckAt) || row.lastCheckAt < 0) {
39372
40074
  throw new Error("stored hook cooldown state is malformed");
39373
40075
  }
39374
40076
  return { version: 1, lastCheckAt: row.lastCheckAt };
@@ -39995,6 +40697,470 @@ function renderListenerAttendanceCanary(result, workspaceId2, principalId) {
39995
40697
  return lines.join("\n");
39996
40698
  }
39997
40699
 
40700
+ // src/listener/activity.ts
40701
+ var import_node_crypto20 = require("node:crypto");
40702
+ var ACTIVITY_FRAME_INTERVAL_MS = 750;
40703
+ var ACTIVITY_HEARTBEAT_MS = 15e3;
40704
+ var ACTIVITY_TOOL_TITLE_MAX = 160;
40705
+ var ACTIVITY_REQUEST_TIMEOUT_MS = 5e3;
40706
+ var SYSTEM_CLOCK = {
40707
+ now: Date.now,
40708
+ setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
40709
+ clearTimer: (timer2) => clearTimeout(timer2)
40710
+ };
40711
+ var TERMINAL_TOOL_STATUSES = /* @__PURE__ */ new Set([
40712
+ "cancelled",
40713
+ "completed",
40714
+ "done",
40715
+ "failed",
40716
+ "rejected"
40717
+ ]);
40718
+ var AgentActivityEndpointTransport = class {
40719
+ constructor(target2, credentialSession, fetcher) {
40720
+ this.target = target2;
40721
+ this.credentialSession = credentialSession;
40722
+ this.fetcher = fetcher ?? fetch;
40723
+ }
40724
+ target;
40725
+ credentialSession;
40726
+ fetcher;
40727
+ async publish(frame) {
40728
+ const credential = await this.credentialSession.bearer();
40729
+ const response = await this.fetcher(
40730
+ `${this.target.url}/functions/v1/activity`,
40731
+ {
40732
+ method: "POST",
40733
+ headers: {
40734
+ authorization: `Bearer ${credential}`,
40735
+ apikey: this.target.anonKey,
40736
+ "content-type": "application/json"
40737
+ },
40738
+ body: JSON.stringify({
40739
+ version: frame.version,
40740
+ workspace_id: frame.workspaceId,
40741
+ stream_id: frame.streamId,
40742
+ sequence: frame.sequence,
40743
+ phase: frame.phase,
40744
+ signal_id: frame.signalId,
40745
+ tool_title: frame.toolTitle,
40746
+ elapsed_ms: frame.elapsedMs
40747
+ }),
40748
+ signal: AbortSignal.timeout(ACTIVITY_REQUEST_TIMEOUT_MS)
40749
+ }
40750
+ );
40751
+ await response.body?.cancel();
40752
+ if (!response.ok) {
40753
+ throw new Error(`activity publish failed (${response.status})`);
40754
+ }
40755
+ }
40756
+ };
40757
+ var ListenerActivityController = class {
40758
+ constructor(options) {
40759
+ this.options = options;
40760
+ this.clock = options.clock ?? SYSTEM_CLOCK;
40761
+ this.streamId = options.streamId ?? (0, import_node_crypto20.randomUUID)();
40762
+ this.events = { update: (update) => this.onSessionUpdate(update) };
40763
+ }
40764
+ options;
40765
+ events;
40766
+ clock;
40767
+ streamId;
40768
+ sequence = 0;
40769
+ phase = "idle";
40770
+ signalId = null;
40771
+ signalStartedAt = null;
40772
+ runningTools = /* @__PURE__ */ new Map();
40773
+ latestToolId = null;
40774
+ dirty = false;
40775
+ sending = false;
40776
+ closed = false;
40777
+ lastSentAt = Number.NEGATIVE_INFINITY;
40778
+ timer = null;
40779
+ heartbeatTimer = null;
40780
+ /** Observe the listener state machine without changing its durable effect path. */
40781
+ onRuntimeEvent(event) {
40782
+ if (event.type === "ready") {
40783
+ this.setIdle();
40784
+ } else if (event.type === "delivery_claim" && event.signalId !== null) {
40785
+ this.beginSignal(event.signalId);
40786
+ } else if (event.type === "routing_decision") {
40787
+ this.beginSignal(event.signalId);
40788
+ } else if (event.type === "effect") {
40789
+ this.setIdle();
40790
+ }
40791
+ }
40792
+ /** Wrap one provider model so prompt and reply phases are visible. */
40793
+ instrumentModel(model) {
40794
+ return {
40795
+ start: async () => await model.start(),
40796
+ prompt: async (signal, mode3, prompt, attempt) => {
40797
+ this.beginSignal(signal.id);
40798
+ this.setPhase("prompting");
40799
+ const result = await model.prompt(signal, mode3, prompt, attempt);
40800
+ this.runningTools.clear();
40801
+ this.latestToolId = null;
40802
+ this.setPhase("replying");
40803
+ return result;
40804
+ },
40805
+ cancel: () => model.cancel(),
40806
+ close: async () => await model.close()
40807
+ };
40808
+ }
40809
+ /** Stop pending local timers; a missing next frame becomes stale in the panel. */
40810
+ close() {
40811
+ this.closed = true;
40812
+ if (this.timer !== null) this.clock.clearTimer(this.timer);
40813
+ if (this.heartbeatTimer !== null) this.clock.clearTimer(this.heartbeatTimer);
40814
+ this.timer = null;
40815
+ this.heartbeatTimer = null;
40816
+ }
40817
+ onSessionUpdate(update) {
40818
+ if (this.signalId === null) return;
40819
+ if (update.kind !== "tool_call" && update.kind !== "tool_call_update") return;
40820
+ const id = update.toolCallId;
40821
+ if (!id) return;
40822
+ const status = update.status?.toLowerCase();
40823
+ if (status && TERMINAL_TOOL_STATUSES.has(status)) {
40824
+ this.runningTools.delete(id);
40825
+ if (this.latestToolId === id) {
40826
+ this.latestToolId = [...this.runningTools.keys()].at(-1) ?? null;
40827
+ }
40828
+ this.setPhase(this.runningTools.size > 0 ? "tool-running" : "prompting");
40829
+ return;
40830
+ }
40831
+ const sanitizedTitle = update.title === void 0 ? this.runningTools.get(id) : sanitizeText(update.title).slice(0, ACTIVITY_TOOL_TITLE_MAX);
40832
+ const title = sanitizedTitle && sanitizedTitle.length > 0 ? sanitizedTitle : "Tool";
40833
+ this.runningTools.set(id, title);
40834
+ this.latestToolId = id;
40835
+ this.setPhase("tool-running");
40836
+ }
40837
+ beginSignal(signalId) {
40838
+ if (this.signalId === signalId) return;
40839
+ this.signalId = signalId;
40840
+ this.signalStartedAt = this.clock.now();
40841
+ this.runningTools.clear();
40842
+ this.latestToolId = null;
40843
+ this.setPhase("claimed");
40844
+ }
40845
+ setIdle() {
40846
+ this.signalId = null;
40847
+ this.signalStartedAt = null;
40848
+ this.runningTools.clear();
40849
+ this.latestToolId = null;
40850
+ this.setPhase("idle");
40851
+ }
40852
+ setPhase(phase) {
40853
+ this.phase = phase;
40854
+ if (this.heartbeatTimer !== null) {
40855
+ this.clock.clearTimer(this.heartbeatTimer);
40856
+ this.heartbeatTimer = null;
40857
+ }
40858
+ this.dirty = true;
40859
+ this.schedule();
40860
+ }
40861
+ armHeartbeat() {
40862
+ if (this.closed || this.heartbeatTimer !== null) return;
40863
+ this.heartbeatTimer = this.clock.setTimer(() => {
40864
+ this.heartbeatTimer = null;
40865
+ this.dirty = true;
40866
+ this.schedule();
40867
+ }, ACTIVITY_HEARTBEAT_MS);
40868
+ }
40869
+ schedule() {
40870
+ if (this.closed || this.timer !== null || this.sending) return;
40871
+ const delay2 = Math.max(
40872
+ 0,
40873
+ this.lastSentAt + ACTIVITY_FRAME_INTERVAL_MS - this.clock.now()
40874
+ );
40875
+ if (delay2 === 0) {
40876
+ void this.flush();
40877
+ return;
40878
+ }
40879
+ this.timer = this.clock.setTimer(() => {
40880
+ this.timer = null;
40881
+ void this.flush();
40882
+ }, delay2);
40883
+ }
40884
+ async flush() {
40885
+ if (this.closed || this.sending || !this.dirty) return;
40886
+ this.dirty = false;
40887
+ this.sending = true;
40888
+ this.lastSentAt = this.clock.now();
40889
+ const toolTitle = this.latestToolId === null ? null : this.runningTools.get(this.latestToolId) ?? null;
40890
+ const frame = {
40891
+ version: 1,
40892
+ workspaceId: this.options.workspaceId,
40893
+ streamId: this.streamId,
40894
+ sequence: ++this.sequence,
40895
+ phase: this.phase,
40896
+ signalId: this.signalId,
40897
+ toolTitle,
40898
+ elapsedMs: this.signalStartedAt === null ? 0 : Math.max(0, Math.round(this.clock.now() - this.signalStartedAt))
40899
+ };
40900
+ try {
40901
+ await this.options.transport.publish(frame);
40902
+ } catch {
40903
+ } finally {
40904
+ this.sending = false;
40905
+ if (this.dirty) this.schedule();
40906
+ else this.armHeartbeat();
40907
+ }
40908
+ }
40909
+ };
40910
+
40911
+ // src/listener/http-client.ts
40912
+ var import_node_http2 = require("node:http");
40913
+ var import_node_https = require("node:https");
40914
+ var import_node_zlib = require("node:zlib");
40915
+ var LISTENER_HTTP_IDLE_TIMEOUT_MS = 6e4;
40916
+ function responseHeaders(message) {
40917
+ const headers = new Headers();
40918
+ for (let index = 0; index < message.rawHeaders.length; index += 2) {
40919
+ const name = message.rawHeaders[index];
40920
+ const value = message.rawHeaders[index + 1];
40921
+ if (name !== void 0 && value !== void 0) headers.append(name, value);
40922
+ }
40923
+ return headers;
40924
+ }
40925
+ function decodeResponseBody(bytes, headers) {
40926
+ const codings = (headers.get("content-encoding") ?? "").split(",").map((coding) => coding.trim().toLowerCase()).filter((coding) => coding !== "" && coding !== "identity");
40927
+ if (!codings.every((coding) => ["br", "deflate", "gzip", "x-gzip"].includes(coding))) {
40928
+ return Uint8Array.from(bytes).buffer;
40929
+ }
40930
+ let decoded = bytes;
40931
+ for (const coding of codings.reverse()) {
40932
+ if (coding === "br") decoded = (0, import_node_zlib.brotliDecompressSync)(decoded);
40933
+ else if (coding === "deflate") decoded = (0, import_node_zlib.inflateSync)(decoded);
40934
+ else decoded = (0, import_node_zlib.gunzipSync)(decoded);
40935
+ }
40936
+ return Uint8Array.from(decoded).buffer;
40937
+ }
40938
+ var ListenerHttpClient = class {
40939
+ fetch;
40940
+ httpAgent = new import_node_http2.Agent({
40941
+ keepAlive: true,
40942
+ maxSockets: 1,
40943
+ maxFreeSockets: 1,
40944
+ scheduling: "fifo"
40945
+ });
40946
+ httpsAgent = new import_node_https.Agent({
40947
+ keepAlive: true,
40948
+ maxSockets: 1,
40949
+ maxFreeSockets: 1,
40950
+ scheduling: "fifo"
40951
+ });
40952
+ idleTimeoutMs;
40953
+ sockets = /* @__PURE__ */ new WeakSet();
40954
+ requestCount = 0;
40955
+ openedCount = 0;
40956
+ activeRequests = 0;
40957
+ idleTimer = null;
40958
+ closed = false;
40959
+ constructor(options = {}) {
40960
+ const idleTimeoutMs = options.idleTimeoutMs ?? LISTENER_HTTP_IDLE_TIMEOUT_MS;
40961
+ if (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs <= 0) {
40962
+ throw new Error("listener HTTP idle timeout must be a positive integer");
40963
+ }
40964
+ this.idleTimeoutMs = idleTimeoutMs;
40965
+ this.fetch = this.request.bind(this);
40966
+ }
40967
+ /** Snapshot process-local counts without exposing the agents themselves. */
40968
+ metrics() {
40969
+ return {
40970
+ requests: this.requestCount,
40971
+ connectionsOpened: this.openedCount,
40972
+ connectionReuseRatio: this.openedCount === 0 ? 0 : this.requestCount / this.openedCount
40973
+ };
40974
+ }
40975
+ /** Close every idle or active socket when the listener process stops. */
40976
+ close() {
40977
+ if (this.closed) return;
40978
+ this.closed = true;
40979
+ this.clearIdleTimer();
40980
+ this.destroyAgents();
40981
+ }
40982
+ clearIdleTimer() {
40983
+ if (this.idleTimer === null) return;
40984
+ clearTimeout(this.idleTimer);
40985
+ this.idleTimer = null;
40986
+ }
40987
+ destroyAgents() {
40988
+ this.httpAgent.destroy();
40989
+ this.httpsAgent.destroy();
40990
+ }
40991
+ beginRequest() {
40992
+ if (this.closed) throw new Error("listener HTTP client is closed");
40993
+ this.clearIdleTimer();
40994
+ this.requestCount += 1;
40995
+ this.activeRequests += 1;
40996
+ }
40997
+ finishRequest() {
40998
+ this.activeRequests -= 1;
40999
+ if (this.activeRequests !== 0 || this.closed) return;
41000
+ this.idleTimer = setTimeout(() => {
41001
+ this.idleTimer = null;
41002
+ if (this.activeRequests === 0 && !this.closed) this.destroyAgents();
41003
+ }, this.idleTimeoutMs);
41004
+ this.idleTimer.unref?.();
41005
+ }
41006
+ trackSocket(request) {
41007
+ request.once("socket", (socket) => {
41008
+ if (this.sockets.has(socket)) return;
41009
+ this.sockets.add(socket);
41010
+ const opened = () => {
41011
+ this.openedCount += 1;
41012
+ };
41013
+ if (socket.connecting) socket.once("connect", opened);
41014
+ else opened();
41015
+ });
41016
+ }
41017
+ async request(input, init) {
41018
+ const webRequest = new Request(input, init);
41019
+ const body = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : Buffer.from(await webRequest.arrayBuffer());
41020
+ let url = new URL(webRequest.url);
41021
+ let method = webRequest.method;
41022
+ let headers = new Headers(webRequest.headers);
41023
+ let redirected = false;
41024
+ for (let redirects = 0; ; redirects += 1) {
41025
+ const response = await this.sendOnce({
41026
+ url,
41027
+ method,
41028
+ headers,
41029
+ body: method === "GET" || method === "HEAD" ? null : body,
41030
+ signal: webRequest.signal,
41031
+ redirected
41032
+ });
41033
+ const location2 = response.headers.get("location");
41034
+ if (location2 === null || ![301, 302, 303, 307, 308].includes(response.status) || webRequest.redirect === "manual") {
41035
+ return response;
41036
+ }
41037
+ if (webRequest.redirect === "error" || redirects >= 20) {
41038
+ throw new TypeError("fetch failed while following a redirect");
41039
+ }
41040
+ const nextUrl = new URL(location2, url);
41041
+ if (nextUrl.protocol !== "http:" && nextUrl.protocol !== "https:") {
41042
+ throw new TypeError("listener HTTP client accepts only http and https URLs");
41043
+ }
41044
+ const rewriteToGet = response.status === 303 && method !== "HEAD" || (response.status === 301 || response.status === 302) && method === "POST";
41045
+ if (rewriteToGet) {
41046
+ method = "GET";
41047
+ for (const name of [
41048
+ "content-encoding",
41049
+ "content-language",
41050
+ "content-length",
41051
+ "content-location",
41052
+ "content-type",
41053
+ "transfer-encoding"
41054
+ ]) {
41055
+ headers.delete(name);
41056
+ }
41057
+ }
41058
+ if (nextUrl.origin !== url.origin) {
41059
+ for (const name of [
41060
+ "apikey",
41061
+ "authorization",
41062
+ "cookie",
41063
+ "host",
41064
+ "proxy-authorization"
41065
+ ]) {
41066
+ headers.delete(name);
41067
+ }
41068
+ }
41069
+ url = nextUrl;
41070
+ redirected = true;
41071
+ }
41072
+ }
41073
+ async sendOnce(options) {
41074
+ if (options.url.protocol !== "http:" && options.url.protocol !== "https:") {
41075
+ throw new TypeError("listener HTTP client accepts only http and https URLs");
41076
+ }
41077
+ const headers = Object.fromEntries(options.headers.entries());
41078
+ if (!("accept-encoding" in headers)) headers["accept-encoding"] = "gzip, deflate";
41079
+ if (options.body !== null && !("content-length" in headers) && !("transfer-encoding" in headers)) {
41080
+ headers["content-length"] = String(options.body.byteLength);
41081
+ }
41082
+ this.beginRequest();
41083
+ let finished = false;
41084
+ const finish = () => {
41085
+ if (finished) return;
41086
+ finished = true;
41087
+ this.finishRequest();
41088
+ };
41089
+ return await new Promise((resolve2, reject) => {
41090
+ const send = options.url.protocol === "https:" ? import_node_https.request : import_node_http2.request;
41091
+ const agent = options.url.protocol === "https:" ? this.httpsAgent : this.httpAgent;
41092
+ let request;
41093
+ try {
41094
+ request = send(options.url, {
41095
+ agent,
41096
+ method: options.method,
41097
+ headers,
41098
+ signal: options.signal
41099
+ });
41100
+ } catch (error) {
41101
+ finish();
41102
+ reject(error);
41103
+ return;
41104
+ }
41105
+ this.trackSocket(request);
41106
+ request.once("error", (error) => {
41107
+ finish();
41108
+ reject(error);
41109
+ });
41110
+ request.once("response", (message) => {
41111
+ const chunks = [];
41112
+ let settled = false;
41113
+ const rejectResponse = (error) => {
41114
+ if (settled) return;
41115
+ settled = true;
41116
+ finish();
41117
+ reject(error);
41118
+ };
41119
+ message.on("data", (chunk) => {
41120
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
41121
+ });
41122
+ message.once("aborted", () => {
41123
+ rejectResponse(new TypeError("response body was aborted"));
41124
+ });
41125
+ message.once("error", rejectResponse);
41126
+ message.once("end", () => {
41127
+ if (settled) return;
41128
+ settled = true;
41129
+ finish();
41130
+ const status = message.statusCode ?? 0;
41131
+ const bytes = Buffer.concat(chunks);
41132
+ const webHeaders = responseHeaders(message);
41133
+ const responseBody2 = status === 204 || status === 205 || status === 304 ? null : decodeResponseBody(bytes, webHeaders);
41134
+ try {
41135
+ const response = new Response(responseBody2, {
41136
+ status,
41137
+ statusText: message.statusMessage,
41138
+ headers: webHeaders
41139
+ });
41140
+ Object.defineProperties(response, {
41141
+ redirected: { value: options.redirected },
41142
+ url: { value: options.url.href }
41143
+ });
41144
+ resolve2(response);
41145
+ } catch (error) {
41146
+ reject(error);
41147
+ }
41148
+ });
41149
+ });
41150
+ try {
41151
+ if (options.body !== null && options.body.byteLength > 0) {
41152
+ request.write(options.body);
41153
+ }
41154
+ request.end();
41155
+ } catch (error) {
41156
+ request.destroy();
41157
+ finish();
41158
+ reject(error);
41159
+ }
41160
+ });
41161
+ }
41162
+ };
41163
+
39998
41164
  // src/resume.ts
39999
41165
  var import_node_child_process8 = require("node:child_process");
40000
41166
  function execFileText(file, args) {
@@ -40410,8 +41576,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
40410
41576
  AGENT_CREDENTIAL_MESSAGE_D088
40411
41577
  ];
40412
41578
  function packageVersion() {
40413
- if ("0.1.45".length > 0) {
40414
- return "0.1.45";
41579
+ if ("0.1.46".length > 0) {
41580
+ return "0.1.46";
40415
41581
  }
40416
41582
  try {
40417
41583
  const value = JSON.parse(
@@ -41035,7 +42201,7 @@ async function runNew(args) {
41035
42201
  assertWorkspaceName(name);
41036
42202
  const cloud = await target(args);
41037
42203
  const human = await humanCredential(args, cloud);
41038
- const proposedId = (0, import_node_crypto20.randomUUID)();
42204
+ const proposedId = (0, import_node_crypto21.randomUUID)();
41039
42205
  let result;
41040
42206
  try {
41041
42207
  result = await new ThinCommandClient(cloud).sendConnect({
@@ -42058,7 +43224,7 @@ function accepted(label, result) {
42058
43224
  );
42059
43225
  }
42060
43226
  }
42061
- async function agentSession(cloud, workspaceId2, agent) {
43227
+ async function agentSession(cloud, workspaceId2, agent, fetcher) {
42062
43228
  let store2 = null;
42063
43229
  try {
42064
43230
  const candidate = await agentCredentialStore({
@@ -42084,7 +43250,8 @@ async function agentSession(cloud, workspaceId2, agent) {
42084
43250
  runId: agent.runId,
42085
43251
  expiresAt: agent.expiresAt
42086
43252
  },
42087
- store: store2
43253
+ store: store2,
43254
+ ...fetcher ? { fetcher } : {}
42088
43255
  });
42089
43256
  }
42090
43257
  async function commandWorkspaceAndCredential(args, cloud, options = {}) {
@@ -42366,8 +43533,8 @@ function prepareSignalAttachments(localPaths) {
42366
43533
  name,
42367
43534
  bytes,
42368
43535
  contentType,
42369
- fileId: (0, import_node_crypto20.randomUUID)(),
42370
- versionId: (0, import_node_crypto20.randomUUID)(),
43536
+ fileId: (0, import_node_crypto21.randomUUID)(),
43537
+ versionId: (0, import_node_crypto21.randomUUID)(),
42371
43538
  createCommandId: newCommandId(),
42372
43539
  commitCommandId: newCommandId()
42373
43540
  };
@@ -43052,6 +44219,7 @@ async function runInboxNotifyCommand(args) {
43052
44219
  );
43053
44220
  }
43054
44221
  const controller = new AbortController();
44222
+ const httpClient = new ListenerHttpClient();
43055
44223
  const stop = () => controller.abort();
43056
44224
  process.on("SIGINT", stop);
43057
44225
  process.on("SIGTERM", stop);
@@ -43082,7 +44250,7 @@ async function runInboxNotifyCommand(args) {
43082
44250
  ...after === null ? {} : { after }
43083
44251
  }
43084
44252
  },
43085
- { signal: controller.signal }
44253
+ { signal: controller.signal, fetcher: httpClient.fetch }
43086
44254
  );
43087
44255
  },
43088
44256
  emit: async (signal) => {
@@ -43116,6 +44284,7 @@ async function runInboxNotifyCommand(args) {
43116
44284
  } finally {
43117
44285
  process.off("SIGINT", stop);
43118
44286
  process.off("SIGTERM", stop);
44287
+ httpClient.close();
43119
44288
  }
43120
44289
  }
43121
44290
  async function runReceipt(args) {
@@ -43188,6 +44357,7 @@ async function runInboxFollowCommand(args) {
43188
44357
  includeStale: args.has("include-stale")
43189
44358
  };
43190
44359
  const controller = new AbortController();
44360
+ const httpClient = new ListenerHttpClient();
43191
44361
  let legacyCursorWarned = false;
43192
44362
  let malformedRowWarnings = 0;
43193
44363
  const onAbortSignal = () => controller.abort();
@@ -43219,7 +44389,7 @@ async function runInboxFollowCommand(args) {
43219
44389
  cloud,
43220
44390
  credential,
43221
44391
  query,
43222
- { signal: controller.signal },
44392
+ { signal: controller.signal, fetcher: httpClient.fetch },
43223
44393
  {
43224
44394
  allowLegacyCursorFallback: true,
43225
44395
  tolerateMalformedRows: true,
@@ -43251,7 +44421,7 @@ async function runInboxFollowCommand(args) {
43251
44421
  cloud,
43252
44422
  credential,
43253
44423
  query,
43254
- { signal: controller.signal }
44424
+ { signal: controller.signal, fetcher: httpClient.fetch }
43255
44425
  );
43256
44426
  },
43257
44427
  emit: (frame) => {
@@ -43269,6 +44439,7 @@ async function runInboxFollowCommand(args) {
43269
44439
  } finally {
43270
44440
  process.off("SIGINT", onAbortSignal);
43271
44441
  process.off("SIGTERM", onAbortSignal);
44442
+ httpClient.close();
43272
44443
  }
43273
44444
  }
43274
44445
  function listenerUuid(value, flag) {
@@ -43453,16 +44624,107 @@ function listenerAttendanceState(status, evidence) {
43453
44624
  function listenerAttendanceRemedy(principalId) {
43454
44625
  return `cswarm hook install claude --principal-id ${principalId} --write, then start a fresh session. Or restart the listener with --route worker.`;
43455
44626
  }
44627
+ function listenerReadHealthSummary(status, nowMs) {
44628
+ return summarizeListenerReadHealth(
44629
+ status.readHealth ?? emptyListenerReadHealth(),
44630
+ status.readyAt,
44631
+ nowMs
44632
+ );
44633
+ }
44634
+ function listenerLapseNotices(status, summary) {
44635
+ const health = status.readHealth ?? emptyListenerReadHealth();
44636
+ const notices = [];
44637
+ if (health.currentReasonCode === "host_ports_exhausted") {
44638
+ notices.push({
44639
+ code: "listener_host_ports_exhausted",
44640
+ message: "This host has run out of outbound ports. The listener is probing only once per minute so it does not amplify the outage.",
44641
+ nextStep: "Find the consumer: lsof -nP -iTCP | awk '{print $1}' | sort | uniq -c | sort -rn"
44642
+ });
44643
+ } else if (
44644
+ // Reuse arrival-watch.ts's 60s loud-lapse transition. The listener keeps
44645
+ // the episode in durable status instead of the monitor's process-local machine.
44646
+ summary.currentEpisodeDurationMs !== null && summary.currentEpisodeDurationMs >= ARRIVAL_RETRY_NOTICE_THRESHOLD_MS
44647
+ ) {
44648
+ notices.push({
44649
+ code: "listener_read_retry_persisting",
44650
+ message: `Listener reads have failed continuously for ${Math.floor(summary.currentEpisodeDurationMs / 1e3)}s. This is still in progress.`,
44651
+ nextStep: "Check cswarm status and the CommonSwarm service. If both are healthy, restart the listener."
44652
+ });
44653
+ }
44654
+ if (summary.throughputLapseHours.length > 0) {
44655
+ const latest = summary.throughputLapseHours.at(-1);
44656
+ notices.push({
44657
+ code: "listener_claim_throughput_lapse",
44658
+ message: `Claim throughput fell below 0.50 for the full hour at ${latest.hourStart}: ${latest.claims}/${Math.round(latest.expectedClaims)} expected (${latest.ratio.toFixed(3)}).`,
44659
+ nextStep: "This host is starving the listener \u2014 check load/memory pressure (sysctl kern.memorystatus_vm_pressure_level), or move the listener."
44660
+ });
44661
+ }
44662
+ return notices;
44663
+ }
44664
+ async function listenerProviderInstallEvidence(status) {
44665
+ if (status.provider !== "claude") return null;
44666
+ try {
44667
+ const notice = await inspectClaudeBridgeExecutable(
44668
+ status.providerExecutable ?? "claude-agent-acp",
44669
+ { pathEnv: process.env.PATH, env: process.env }
44670
+ );
44671
+ return {
44672
+ executable: notice.executable,
44673
+ providerVersion: notice.providerVersion,
44674
+ bundledAgentSdkVersion: notice.bundledAgentSdkVersion,
44675
+ bundledClaudeCodeVersion: notice.bundledClaudeCodeVersion
44676
+ };
44677
+ } catch {
44678
+ return {
44679
+ executable: null,
44680
+ providerVersion: null,
44681
+ bundledAgentSdkVersion: null,
44682
+ bundledClaudeCodeVersion: null
44683
+ };
44684
+ }
44685
+ }
44686
+ function versionIsBelow(left, right) {
44687
+ if (!left || !right) return false;
44688
+ try {
44689
+ return compareSemVer(left, right) < 0;
44690
+ } catch {
44691
+ return false;
44692
+ }
44693
+ }
44694
+ function providerRestartRequired(status, installed) {
44695
+ if (!installed) return false;
44696
+ return status.providerVersion !== null && status.providerVersion !== void 0 && installed.providerVersion !== null && status.providerVersion !== installed.providerVersion || status.providerBundledClaudeCodeVersion !== null && status.providerBundledClaudeCodeVersion !== void 0 && installed.bundledClaudeCodeVersion !== null && status.providerBundledClaudeCodeVersion !== installed.bundledClaudeCodeVersion;
44697
+ }
43456
44698
  function listenerStatusJson(status, permissionMode, evidence = {
43457
44699
  pendingForMainOldestAt: null,
43458
44700
  hookSurfaceExists: false,
43459
44701
  hookSurfaceAdvanced: false
43460
- }, nowMs = Date.now()) {
44702
+ }, nowMs = Date.now(), installed = null) {
43461
44703
  const mode3 = permissionMode ?? status.permissionMode;
43462
44704
  const attendance = listenerAttendanceState(status, evidence);
43463
44705
  const pending = status.pendingForMainCount ?? 0;
44706
+ const readHealth = status.readHealth ?? emptyListenerReadHealth();
44707
+ const readSummary = listenerReadHealthSummary(status, nowMs);
44708
+ const lapseNotices = listenerLapseNotices(status, readSummary);
43464
44709
  return {
43465
44710
  ...status,
44711
+ providerExecutable: status.providerExecutable ?? null,
44712
+ providerExecutableMeasured: typeof status.providerExecutable === "string",
44713
+ providerVersion: status.providerVersion ?? null,
44714
+ providerVersionMeasured: typeof status.providerVersion === "string",
44715
+ providerBundledAgentSdkVersion: status.providerBundledAgentSdkVersion ?? null,
44716
+ providerBundledClaudeCodeVersion: status.providerBundledClaudeCodeVersion ?? null,
44717
+ providerMinimumRequiredVersion: status.providerMinimumRequiredVersion ?? null,
44718
+ lastErrorReasonCode: status.lastErrorReasonCode ?? null,
44719
+ providerBelowDemandedMinimum: versionIsBelow(
44720
+ status.providerBundledClaudeCodeVersion,
44721
+ status.providerMinimumRequiredVersion
44722
+ ),
44723
+ providerOnDiskExecutable: installed?.executable ?? null,
44724
+ providerOnDiskVersion: installed?.providerVersion ?? null,
44725
+ providerOnDiskBundledAgentSdkVersion: installed?.bundledAgentSdkVersion ?? null,
44726
+ providerOnDiskBundledClaudeCodeVersion: installed?.bundledClaudeCodeVersion ?? null,
44727
+ providerRestartRequired: providerRestartRequired(status, installed),
43466
44728
  ...attendance,
43467
44729
  hookSurfaceExists: evidence.hookSurfaceExists,
43468
44730
  hookSurfaceAdvanced: evidence.hookSurfaceAdvanced,
@@ -43470,6 +44732,22 @@ function listenerStatusJson(status, permissionMode, evidence = {
43470
44732
  pendingForMainOldestAgeMs: evidence.pendingForMainOldestAt === null ? null : Math.max(0, nowMs - Date.parse(evidence.pendingForMainOldestAt)),
43471
44733
  attendanceWarningCode: pending > 0 ? "listener_unattended_main_queue" : null,
43472
44734
  attendanceNextStep: pending > 0 ? listenerAttendanceRemedy(status.principalId) : null,
44735
+ readRetryCurrentEpisodeStartedAt: readHealth.currentEpisodeStartedAt,
44736
+ readRetryCurrentEpisodeAttempts: readHealth.currentEpisodeAttempts,
44737
+ readRetryCurrentReasonCode: readHealth.currentReasonCode,
44738
+ readRetryCurrentHttpStatus: readHealth.currentHttpStatus,
44739
+ readRetryCurrentErrorConstructor: readHealth.currentErrorConstructor,
44740
+ readRetryCurrentEpisodeDurationMs: readSummary.currentEpisodeDurationMs,
44741
+ readRetryEpisodesLast24h: readSummary.episodesLast24h,
44742
+ readRetryLongestEpisodeAttemptsLast24h: readSummary.longestEpisodeAttemptsLast24h,
44743
+ readRetryLongestEpisodeDurationMsLast24h: readSummary.longestEpisodeDurationMsLast24h,
44744
+ readRetriesLastHour: readSummary.retriesLastHour,
44745
+ readRetryHours: readSummary.retryHours,
44746
+ claimCadenceMs: readHealth.claimCadenceMs,
44747
+ claimThroughputHours: readSummary.claimThroughputHours,
44748
+ listenerLapse: lapseNotices.length > 0,
44749
+ listenerLapseCodes: lapseNotices.map((notice) => notice.code),
44750
+ listenerLapseNextSteps: lapseNotices.map((notice) => notice.nextStep),
43473
44751
  deliveryMode: status.deliveryMode ?? null,
43474
44752
  pendingDeliveryCount: status.pendingDeliveryCount ?? null,
43475
44753
  lastTerminalDeliveryFailureCount: status.lastTerminalDeliveryFailureCount ?? null,
@@ -43480,6 +44758,8 @@ function listenerStatusJson(status, permissionMode, evidence = {
43480
44758
  deferOverChars: status.deferOverChars ?? null,
43481
44759
  pendingForMainCount: status.pendingForMainCount ?? 0,
43482
44760
  droppedForMainCount: status.droppedForMainCount ?? 0,
44761
+ connectionsOpened: status.connectionsOpened ?? null,
44762
+ connectionReuseRatio: status.connectionReuseRatio ?? null,
43483
44763
  ...mode3 ? {
43484
44764
  permission_mode: mode3,
43485
44765
  /* "allowed once" alone overstates it: allowOnceOrDeny selects allow_once only when the
@@ -43495,27 +44775,56 @@ function renderListenerStatus(status, evidence = {
43495
44775
  pendingForMainOldestAt: null,
43496
44776
  hookSurfaceExists: false,
43497
44777
  hookSurfaceAdvanced: false
43498
- }, nowMs = Date.now()) {
44778
+ }, nowMs = Date.now(), installed = null) {
43499
44779
  const routeMode = status.routeMode ?? "worker";
43500
44780
  const pendingForMainCount = status.pendingForMainCount ?? 0;
43501
44781
  const droppedForMainCount = status.droppedForMainCount ?? 0;
43502
44782
  const unattendedCount = `${pendingForMainCount} ${pendingForMainCount === 1 ? "message is" : "messages are"} unattended`;
43503
44783
  const attendance = listenerAttendanceState(status, evidence);
44784
+ const readHealth = status.readHealth ?? emptyListenerReadHealth();
44785
+ const readSummary = listenerReadHealthSummary(status, nowMs);
44786
+ const lapseNotices = listenerLapseNotices(status, readSummary);
43504
44787
  const lines = [
43505
- pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
44788
+ lapseNotices.length > 0 ? `Listener LAPSE for agent ${status.principalId}: ${lapseNotices.map((notice) => notice.code).join(", ")}.` : pendingForMainCount > 0 ? `Listener WARNING for agent ${status.principalId}: ${unattendedCount}.` : `Listener ${status.state} for agent ${status.principalId}.`,
43506
44789
  `CONNECTED: ${attendance.connected ? "yes" : "no"}. Transport state is ${status.state}.`,
43507
44790
  `ATTENDED: ${attendance.attendanceState === "attended" ? "yes. The session hook has surfaced messages on this host" : attendance.attendanceState === "unattended" ? "no. The main-session queue is not draining" : attendance.attendanceState === "not_required" ? "not required for the worker route" : "not yet proven on this host"}.`,
43508
44791
  `HANDLED: ${attendance.handledState === "handled" ? "yes. A delivery acknowledgement is recorded" : attendance.handledState === "not_handled" ? "no. Queued messages have not reached the session hook" : "not yet measured"}.`,
43509
44792
  `Provider: ${status.provider}; process: ${status.pid}; started: ${status.startedAt}.`,
44793
+ `Provider executable: ${status.providerExecutable ?? "not measured"}.`,
44794
+ `Connections opened: ${status.connectionsOpened ?? "not measured"}.`,
44795
+ `Connection reuse ratio: ${status.connectionReuseRatio ?? "not measured"}.`,
43510
44796
  status.readyAt ? `Ready since: ${status.readyAt}.` : "Not ready yet.",
43511
44797
  status.lastSignalId ? pendingForMainCount > 0 ? `Last claimed and queued signal: ${status.lastSignalId}. It is not handled yet.` : routeMode === "worker" ? `Last handled signal: ${status.lastSignalId}.` : `Last listener signal: ${status.lastSignalId}. Local status does not prove its final observed receipt.` : "No signal has been handled yet.",
43512
- status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener process error is recorded."
44798
+ status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener process error is recorded.",
44799
+ readHealth.currentEpisodeStartedAt === null ? "Current read retry episode: none." : `Current read retry episode: ${readHealth.currentEpisodeAttempts} attempt${readHealth.currentEpisodeAttempts === 1 ? "" : "s"} since ${readHealth.currentEpisodeStartedAt}; reason ${readHealth.currentReasonCode}${readHealth.currentHttpStatus === null ? "" : ` (HTTP ${readHealth.currentHttpStatus})`}${readHealth.currentErrorConstructor === null ? "" : ` (${readHealth.currentErrorConstructor})`}.`,
44800
+ `Read retry episodes in the last 24h: ${readSummary.episodesLast24h}; retries in the rolling hour: ${readSummary.retriesLastHour}.`,
44801
+ 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.`,
44802
+ 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("; ")}.`,
44803
+ 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("; ")}.`
43513
44804
  ];
44805
+ for (const notice of lapseNotices) {
44806
+ lines.push(`WARNING [${notice.code}]: ${notice.message}`);
44807
+ lines.push(`Next: ${notice.nextStep}`);
44808
+ }
43514
44809
  if (status.lastErrorDetail) {
43515
44810
  const [first, ...rest] = status.lastErrorDetail.split("\n");
43516
44811
  lines.push(`Last error detail (local only): ${first}`);
43517
44812
  for (const line of rest) lines.push(` ${line}`);
43518
44813
  }
44814
+ if (status.lastErrorReasonCode) {
44815
+ lines.push(`Last provider reason code: ${status.lastErrorReasonCode}.`);
44816
+ }
44817
+ if (status.provider === "claude" && status.lastErrorCode === "permission_canary_failed") {
44818
+ lines.push(
44819
+ `Canary diagnosis: ${listenerFailureMessage(
44820
+ status.lastErrorCode,
44821
+ status.provider,
44822
+ status.lastErrorDetail,
44823
+ status.lastErrorReasonCode,
44824
+ status.providerMinimumRequiredVersion
44825
+ )}.`
44826
+ );
44827
+ }
43519
44828
  if (status.lastWorkerStderrTail) {
43520
44829
  const tailLines = status.lastWorkerStderrTail.split("\n").filter((line) => line.trim().length > 0);
43521
44830
  lines.push("Worker stderr (local log only):");
@@ -43525,8 +44834,34 @@ function renderListenerStatus(status, evidence = {
43525
44834
  }
43526
44835
  if (status.providerVersion && status.providerLastMeasuredVersion) {
43527
44836
  lines.push(
43528
- status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.`
44837
+ status.providerVersion === status.providerLastMeasuredVersion ? `Provider version: ${status.providerVersion} (last measured: ${status.providerLastMeasuredVersion}).` : status.state === "ready" ? `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It is unverified but allowed because the startup permission canary passed. Next: verify this provider release with CommonSwarm and update the last-measured version.` : `Provider version ${status.providerVersion} is newer than the last measured version ${status.providerLastMeasuredVersion}. It was measured before startup failed; compatibility was not established. Next: resolve the startup failure before verifying this provider release.`
43529
44838
  );
44839
+ } else {
44840
+ lines.push("Provider version: not measured.");
44841
+ }
44842
+ if (status.provider === "claude") {
44843
+ lines.push(
44844
+ `Bundled Claude Code version: ${status.providerBundledClaudeCodeVersion ?? "not measured"}.`,
44845
+ `Bundled Claude agent SDK version: ${status.providerBundledAgentSdkVersion ?? "not measured"}.`
44846
+ );
44847
+ if (status.providerMinimumRequiredVersion) {
44848
+ lines.push(
44849
+ `Last API minimum demanded: Claude Code ${status.providerMinimumRequiredVersion}.`
44850
+ );
44851
+ }
44852
+ if (versionIsBelow(
44853
+ status.providerBundledClaudeCodeVersion,
44854
+ status.providerMinimumRequiredVersion
44855
+ )) {
44856
+ lines.push(
44857
+ `WARNING [claude_bridge_below_api_minimum]: this listener has bundled Claude Code ${status.providerBundledClaudeCodeVersion}, below the API minimum ${status.providerMinimumRequiredVersion}. Install the current bridge (npm i -g @agentclientprotocol/claude-agent-acp@latest), restart the listener, then run cswarm listen status and confirm that the bundled Claude Code version meets the API minimum ${status.providerMinimumRequiredVersion}.`
44858
+ );
44859
+ }
44860
+ if (providerRestartRequired(status, installed)) {
44861
+ lines.push(
44862
+ `A different Claude bridge is on disk: ${installed?.providerVersion ?? "version not measured"}${installed?.bundledClaudeCodeVersion ? ` (bundled Claude Code ${installed.bundledClaudeCodeVersion})` : ""}. Restart to pick up ${installed?.providerVersion ?? "the on-disk bridge"}.`
44863
+ );
44864
+ }
43530
44865
  }
43531
44866
  if (status.deliveryMode === "durable_claim") {
43532
44867
  lines.push("Delivery mode: durable claim and acknowledgement.");
@@ -43603,7 +44938,26 @@ async function unsurfacedPendingMainStats(instanceDirectory, fallback) {
43603
44938
  };
43604
44939
  }
43605
44940
  }
43606
- function listenerFailureMessage(code, provider, detail) {
44941
+ function quotedListenerFailureDetail(detail) {
44942
+ const recorded = detail?.trim();
44943
+ if (!recorded) return "not recorded";
44944
+ const bounded = recorded.length > 600 ? `${recorded.slice(0, 599)}\u2026` : recorded;
44945
+ return JSON.stringify(bounded);
44946
+ }
44947
+ function listenerProviderIdentitySummary(status) {
44948
+ const parts = [
44949
+ `Provider executable: ${status.providerExecutable ?? "not measured"}`,
44950
+ `provider version: ${status.providerVersion ?? "not measured"}`
44951
+ ];
44952
+ if (status.provider === "claude") {
44953
+ parts.push(
44954
+ `bundled Claude Code: ${status.providerBundledClaudeCodeVersion ?? "not measured"}`,
44955
+ `bundled Claude agent SDK: ${status.providerBundledAgentSdkVersion ?? "not measured"}`
44956
+ );
44957
+ }
44958
+ return parts.join("; ");
44959
+ }
44960
+ function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequiredVersion) {
43607
44961
  if (code === "version_below_floor") {
43608
44962
  if (provider === "codex") {
43609
44963
  return "the Codex listener requires codex-acp 1.1.9 or newer; update the bridge, then retry";
@@ -43663,11 +45017,25 @@ function listenerFailureMessage(code, provider, detail) {
43663
45017
  }
43664
45018
  if (code === "permission_canary_failed") {
43665
45019
  if (provider === "claude") {
43666
- return "the Claude bridge did not complete the ACP permission canary; the startup canary ran, but no workspace signal prompt was delivered. Confirm Claude Code keychain/OAuth sign-in, then retry";
45020
+ const shape = classifyClaudeCanaryFailure(detail, reasonCode);
45021
+ const ran = "the Claude ACP permission canary ran, but no workspace signal prompt was delivered";
45022
+ const response = `bridge response [${shape.code}]: ${quotedListenerFailureDetail(detail)}`;
45023
+ if (shape.code === "claude_bridge_version_required") {
45024
+ const minimum = minimumRequiredVersion ?? shape.minimumRequiredVersion;
45025
+ return `${ran}. ${response}. Next: install the current bridge (npm i -g @agentclientprotocol/claude-agent-acp@latest), restart the listener, then run cswarm listen status and confirm that the bundled Claude Code version meets the API minimum ${minimum ?? "reported there"}`;
45026
+ }
45027
+ if (shape.code === "claude_canary_timeout") {
45028
+ return `${ran}. ${response}. Next: run claude -p and check for session-limit text, check host load, then retry`;
45029
+ }
45030
+ if (shape.code === "claude_canary_auth_failed") {
45031
+ return `${ran}. ${response}. Next: confirm Claude Code keychain/OAuth sign-in, then retry`;
45032
+ }
45033
+ return `${ran}. ${response}. The cause was not determined. Next: inspect the quoted bridge response and local worker stderr, then retry only after the cause is known or the failure appears transient`;
43667
45034
  }
43668
45035
  if (provider === "codex") {
43669
45036
  const recorded = detail?.trim();
43670
- return recorded ? `the Codex bridge did not complete the read-only ACP permission canary; no workspace signal prompt was delivered. Recorded reason: ${JSON.stringify(recorded)}` : "the Codex bridge did not complete the read-only ACP permission canary; no workspace signal prompt was delivered. The recorded reason was unavailable. Next: run cswarm listen status, then retry";
45037
+ const gate = "the Codex listener did not pass the read-only ACP permission safety gate; no workspace signal prompt was delivered";
45038
+ return recorded ? `${gate}. Recorded reason: ${JSON.stringify(recorded)}` : `${gate}. The recorded reason was unavailable. Next: run cswarm listen status, then retry`;
43671
45039
  }
43672
45040
  if (provider === "grok") {
43673
45041
  return "the Grok bridge did not complete the ACP permission canary; no workspace signal prompt was delivered. The local cswarm listen status output includes the final error detail; read it, then retry";
@@ -43750,11 +45118,19 @@ async function runConfiguredListener(options) {
43750
45118
  principalId: options.principalId,
43751
45119
  ...options.stateDirectory ? { stateDirectory: options.stateDirectory } : {}
43752
45120
  });
43753
- const liveCredentialSession = await agentSession(
43754
- options.cloud,
43755
- options.workspaceId,
43756
- options.agent
43757
- );
45121
+ const httpClient = new ListenerHttpClient();
45122
+ let liveCredentialSession;
45123
+ try {
45124
+ liveCredentialSession = await agentSession(
45125
+ options.cloud,
45126
+ options.workspaceId,
45127
+ options.agent,
45128
+ httpClient.fetch
45129
+ );
45130
+ } catch (error) {
45131
+ httpClient.close();
45132
+ throw error;
45133
+ }
43758
45134
  let storedCredential = null;
43759
45135
  const credentialSession = {
43760
45136
  bearer: async () => {
@@ -43782,7 +45158,7 @@ async function runConfiguredListener(options) {
43782
45158
  options.cloud,
43783
45159
  credential,
43784
45160
  options.workspaceId,
43785
- context
45161
+ { ...context, fetcher: httpClient.fetch }
43786
45162
  );
43787
45163
  const provenance = listenerSenderProvenance(signal, senderDirectory);
43788
45164
  if (context.includeBrainDigest !== true) return provenance;
@@ -43793,7 +45169,8 @@ async function runConfiguredListener(options) {
43793
45169
  options.workspaceId,
43794
45170
  {
43795
45171
  ...context.signal ? { signal: context.signal } : {},
43796
- deadlineMs: context.deadlineMs
45172
+ deadlineMs: context.deadlineMs,
45173
+ fetcher: httpClient.fetch
43797
45174
  }
43798
45175
  );
43799
45176
  const brainDigest = await new FileBrainDigestStore(
@@ -43833,7 +45210,19 @@ async function runConfiguredListener(options) {
43833
45210
  let workerStderrGeneration = 0;
43834
45211
  let providerVersionNotice = null;
43835
45212
  const onVersionNotice = (notice) => {
43836
- providerVersionNotice = notice;
45213
+ providerVersionNotice = {
45214
+ runningVersion: notice.runningVersion,
45215
+ lastMeasuredVersion: notice.lastMeasuredVersion
45216
+ };
45217
+ };
45218
+ const onClaudeRuntimeNotice = (notice) => {
45219
+ providerVersionNotice = {
45220
+ runningVersion: notice.providerVersion,
45221
+ lastMeasuredVersion: notice.lastMeasuredVersion,
45222
+ executable: notice.executable,
45223
+ bundledAgentSdkVersion: notice.bundledAgentSdkVersion,
45224
+ bundledClaudeCodeVersion: notice.bundledClaudeCodeVersion
45225
+ };
43837
45226
  };
43838
45227
  const newWorkerStderrTailSink = () => {
43839
45228
  const generation = ++workerStderrGeneration;
@@ -43843,7 +45232,7 @@ async function runConfiguredListener(options) {
43843
45232
  lastWorkerStderrTail = tail.length > 0 ? tail : null;
43844
45233
  };
43845
45234
  };
43846
- const newModel = (onCanaryAttempt) => {
45235
+ const newModel = (onCanaryAttempt, events) => {
43847
45236
  providerVersionNotice = null;
43848
45237
  return options.provider === "opencode" ? new OpenCodeListenerModel({
43849
45238
  cwd: options.cwd,
@@ -43852,6 +45241,7 @@ async function runConfiguredListener(options) {
43852
45241
  onWorkerStderrTail: newWorkerStderrTailSink(),
43853
45242
  onCanaryAttempt,
43854
45243
  onVersionNotice,
45244
+ events,
43855
45245
  ...options.model ? { model: options.model } : {},
43856
45246
  ...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
43857
45247
  }) : options.provider === "claude" ? new ClaudeListenerModel({
@@ -43860,7 +45250,9 @@ async function runConfiguredListener(options) {
43860
45250
  promptTimeoutMs: resolveTurnBudgetMs,
43861
45251
  onWorkerStderrTail: newWorkerStderrTailSink(),
43862
45252
  onCanaryAttempt,
45253
+ onRuntimeNotice: onClaudeRuntimeNotice,
43863
45254
  onVersionNotice,
45255
+ events,
43864
45256
  ...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
43865
45257
  }) : options.provider === "codex" ? new CodexListenerModel({
43866
45258
  cwd: options.cwd,
@@ -43869,6 +45261,7 @@ async function runConfiguredListener(options) {
43869
45261
  onWorkerStderrTail: newWorkerStderrTailSink(),
43870
45262
  onCanaryAttempt,
43871
45263
  onVersionNotice,
45264
+ events,
43872
45265
  ...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
43873
45266
  }) : new GrokListenerModel({
43874
45267
  cwd: options.cwd,
@@ -43877,6 +45270,7 @@ async function runConfiguredListener(options) {
43877
45270
  onWorkerStderrTail: newWorkerStderrTailSink(),
43878
45271
  onCanaryAttempt,
43879
45272
  onVersionNotice,
45273
+ events,
43880
45274
  ...options.model ? { model: options.model } : {},
43881
45275
  ...options.effort ? { effort: options.effort } : {},
43882
45276
  ...options.executable ? { executable: options.executable } : {}
@@ -43907,6 +45301,7 @@ async function runConfiguredListener(options) {
43907
45301
  // one has run, else the configured cap.
43908
45302
  getTurnBudgetMs: () => lastAppliedTurnBudgetMs ?? turnBudgetMs,
43909
45303
  getProviderVersionNotice: () => providerVersionNotice,
45304
+ getConnectionMetrics: () => httpClient.metrics(),
43910
45305
  takeWorkerStderrTail: () => {
43911
45306
  const tail = lastWorkerStderrTail;
43912
45307
  lastWorkerStderrTail = null;
@@ -43940,28 +45335,48 @@ async function runConfiguredListener(options) {
43940
45335
  ts: (/* @__PURE__ */ new Date()).toISOString()
43941
45336
  });
43942
45337
  };
43943
- return await runListenerRuntime({
43944
- target: options.cloud,
45338
+ const activity = new ListenerActivityController({
43945
45339
  workspaceId: options.workspaceId,
43946
- principalId: options.principalId,
43947
- credentialSession,
43948
- store: effectStore,
43949
- model: newModel(onCanaryAttempt),
43950
- signal,
43951
- onEvent,
43952
- declareModel: listenerModelLabel(options.provider),
43953
- listenerInstanceId,
43954
- deliveryJournal: selectedJournal,
43955
- resolveSenderProvenance,
43956
- routeMode,
43957
- deferOverChars,
43958
- pendingMainQueue
45340
+ transport: new AgentActivityEndpointTransport(
45341
+ options.cloud,
45342
+ credentialSession,
45343
+ httpClient.fetch
45344
+ )
43959
45345
  });
45346
+ const instrumentedModel = activity.instrumentModel(
45347
+ newModel(onCanaryAttempt, activity.events)
45348
+ );
45349
+ try {
45350
+ return await runListenerRuntime({
45351
+ target: options.cloud,
45352
+ workspaceId: options.workspaceId,
45353
+ principalId: options.principalId,
45354
+ credentialSession,
45355
+ store: effectStore,
45356
+ model: instrumentedModel,
45357
+ signal,
45358
+ onEvent: (event) => {
45359
+ activity.onRuntimeEvent(event);
45360
+ onEvent(event);
45361
+ },
45362
+ declareModel: listenerModelLabel(options.provider),
45363
+ listenerInstanceId,
45364
+ deliveryJournal: selectedJournal,
45365
+ resolveSenderProvenance,
45366
+ routeMode,
45367
+ deferOverChars,
45368
+ pendingMainQueue,
45369
+ fetcher: httpClient.fetch
45370
+ });
45371
+ } finally {
45372
+ activity.close();
45373
+ }
43960
45374
  }
43961
45375
  });
43962
45376
  } finally {
43963
45377
  process.off("SIGINT", onProcessSignal);
43964
45378
  process.off("SIGTERM", onProcessSignal);
45379
+ httpClient.close();
43965
45380
  }
43966
45381
  }
43967
45382
  async function runListenStart(args) {
@@ -44111,18 +45526,30 @@ async function runListenStart(args) {
44111
45526
  if (error instanceof ListenerStartupError) {
44112
45527
  const failedStatus = await effectiveListenerStatus(paths).catch(() => null);
44113
45528
  const detail = failedStatus?.lastErrorCode === error.code ? failedStatus.lastErrorDetail : null;
44114
- throw new Error(listenerFailureMessage(error.code, provider, detail));
45529
+ const reasonCode = failedStatus?.lastErrorCode === error.code ? failedStatus.lastErrorReasonCode : null;
45530
+ const message = listenerFailureMessage(
45531
+ error.code,
45532
+ provider,
45533
+ detail,
45534
+ reasonCode,
45535
+ failedStatus?.providerMinimumRequiredVersion
45536
+ );
45537
+ throw new Error(
45538
+ failedStatus === null ? message : `${message}. ${listenerProviderIdentitySummary(failedStatus)}`
45539
+ );
44115
45540
  }
44116
45541
  throw error;
44117
45542
  }
44118
45543
  }
44119
45544
  if (status.state === "failed") {
44120
45545
  throw new Error(
44121
- listenerFailureMessage(
45546
+ `${listenerFailureMessage(
44122
45547
  status.lastErrorCode ?? "unknown_error",
44123
45548
  provider,
44124
- status.lastErrorDetail
44125
- )
45549
+ status.lastErrorDetail,
45550
+ status.lastErrorReasonCode,
45551
+ status.providerMinimumRequiredVersion
45552
+ )}. ${listenerProviderIdentitySummary(status)}`
44126
45553
  );
44127
45554
  }
44128
45555
  let attendanceEvidence = {
@@ -44304,11 +45731,22 @@ async function runListenStatusOrStop(args, command2) {
44304
45731
  hookSurfaceAdvanced: queueStats.hookSurfaceAdvanced
44305
45732
  };
44306
45733
  }
45734
+ const installed = command2 === "status" ? await listenerProviderInstallEvidence(status) : null;
44307
45735
  if (args.has("json")) {
44308
- printJson(listenerStatusJson(status, void 0, attendanceEvidence));
45736
+ printJson(
45737
+ listenerStatusJson(
45738
+ status,
45739
+ void 0,
45740
+ attendanceEvidence,
45741
+ Date.now(),
45742
+ installed
45743
+ )
45744
+ );
44309
45745
  } else {
44310
- process.stdout.write(`${renderListenerStatus(status, attendanceEvidence)}
44311
- `);
45746
+ process.stdout.write(
45747
+ `${renderListenerStatus(status, attendanceEvidence, Date.now(), installed)}
45748
+ `
45749
+ );
44312
45750
  }
44313
45751
  }
44314
45752
  async function runListenCanary(args) {
@@ -44345,16 +45783,23 @@ async function runListenCanary(args) {
44345
45783
  ...stateDirectory2 ? { stateDirectory: stateDirectory2 } : {}
44346
45784
  });
44347
45785
  const waitMs = parseWaitSeconds(args.optional("wait") ?? "10") * 1e3;
44348
- const result = await runListenerAttendanceCanary({
44349
- target: cloud,
44350
- workspaceId: workspaceId2,
44351
- principalId,
44352
- paths,
44353
- waitMs,
44354
- // Canary must remain read-only apart from its one self-note. It therefore
44355
- // uses the presented token and never enters the renewal/mint path.
44356
- credential: async () => agent.token
44357
- });
45786
+ const httpClient = new ListenerHttpClient();
45787
+ let result;
45788
+ try {
45789
+ result = await runListenerAttendanceCanary({
45790
+ target: cloud,
45791
+ workspaceId: workspaceId2,
45792
+ principalId,
45793
+ paths,
45794
+ waitMs,
45795
+ fetcher: httpClient.fetch,
45796
+ // Canary must remain read-only apart from its one self-note. It therefore
45797
+ // uses the presented token and never enters the renewal/mint path.
45798
+ credential: async () => agent.token
45799
+ });
45800
+ } finally {
45801
+ httpClient.close();
45802
+ }
44358
45803
  if (args.has("json")) {
44359
45804
  printJson({
44360
45805
  workspaceId: workspaceId2,
@@ -44620,19 +46065,25 @@ async function runHook(args) {
44620
46065
  process.exit(0);
44621
46066
  }, 3e3);
44622
46067
  hardExit.unref();
44623
- await runListenerHookCheck({
44624
- ...cooldownSeconds === void 0 ? {} : { cooldownSeconds },
44625
- ...principalIds.length === 0 ? {} : { principalIds },
44626
- write: async (output) => {
44627
- await new Promise((resolve2, reject) => {
44628
- process.stdout.write(`${output}
46068
+ const httpClient = new ListenerHttpClient();
46069
+ try {
46070
+ await runListenerHookCheck({
46071
+ ...cooldownSeconds === void 0 ? {} : { cooldownSeconds },
46072
+ ...principalIds.length === 0 ? {} : { principalIds },
46073
+ fetcher: httpClient.fetch,
46074
+ write: async (output) => {
46075
+ await new Promise((resolve2, reject) => {
46076
+ process.stdout.write(`${output}
44629
46077
  `, (error) => {
44630
- if (error) reject(error);
44631
- else resolve2();
46078
+ if (error) reject(error);
46079
+ else resolve2();
46080
+ });
44632
46081
  });
44633
- });
44634
- }
44635
- });
46082
+ }
46083
+ });
46084
+ } finally {
46085
+ httpClient.close();
46086
+ }
44636
46087
  return;
44637
46088
  }
44638
46089
  if (command2 !== "install" && command2 !== "uninstall") {
@@ -44758,8 +46209,8 @@ async function uploadNamedFile(context, name, bytes) {
44758
46209
  workspaceId: context.selected.selectedWorkspace,
44759
46210
  credential: context.selected.bearer
44760
46211
  };
44761
- const fileId = (0, import_node_crypto20.randomUUID)();
44762
- const versionId = (0, import_node_crypto20.randomUUID)();
46212
+ const fileId = (0, import_node_crypto21.randomUUID)();
46213
+ const versionId = (0, import_node_crypto21.randomUUID)();
44763
46214
  const createCommandId = newCommandId();
44764
46215
  const commitCommandId = newCommandId();
44765
46216
  const created = await onceRetried(
@@ -45170,7 +46621,7 @@ async function runDogfood(args) {
45170
46621
  const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
45171
46622
  const client = new ThinCommandClient(cloud);
45172
46623
  const route = stream(args);
45173
- const taskId = args.optional("task-id") ?? (0, import_node_crypto20.randomUUID)();
46624
+ const taskId = args.optional("task-id") ?? (0, import_node_crypto21.randomUUID)();
45174
46625
  const ttl = Number(args.optional("ttl-ms") ?? "3600000");
45175
46626
  if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
45176
46627
  throw new Error("--ttl-ms must be an integer in 1..14400000");
@@ -45543,6 +46994,7 @@ ${usage()}
45543
46994
  listenerFailureMessage,
45544
46995
  listenerHostLimits,
45545
46996
  listenerPermissionMode,
46997
+ listenerProviderInstallEvidence,
45546
46998
  listenerRouteConfiguration,
45547
46999
  listenerStatusJson,
45548
47000
  renderListenerStatus,