commonswarm 0.1.27 → 0.1.28

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 +292 -123
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -28443,7 +28443,7 @@ function parseSignalRecord(value) {
28443
28443
  throw new Error("signal read returned a malformed row");
28444
28444
  }
28445
28445
  const row = value;
28446
- if (typeof row.from_kind !== "string" || !["user", "agent"].includes(row.from_kind) || typeof row.kind !== "string" || !SIGNAL_KINDS.has(row.kind) || typeof row.body !== "string" || row.body.length < 1 || row.body.length > 2e3 || !(row.about === null || typeof row.about === "string" && row.about.length <= 500)) {
28446
+ if (typeof row.from_kind !== "string" || !["user", "agent"].includes(row.from_kind) || typeof row.kind !== "string" || !SIGNAL_KINDS.has(row.kind) || typeof row.body !== "string" || row.body.length < 1 || row.body.length > 8e3 || !(row.about === null || typeof row.about === "string" && row.about.length <= 500)) {
28447
28447
  throw new Error("signal read returned malformed signal data");
28448
28448
  }
28449
28449
  let senderOwnerRelation = "unknown";
@@ -29570,11 +29570,15 @@ var ACP_MAX_ACCUMULATED_TEXT_CHARS = 4194304;
29570
29570
  var ACP_DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
29571
29571
  var ACP_VERSION_CHECK_TIMEOUT_MS = 5e3;
29572
29572
  var ACP_CANARY_TIMEOUT_MS = 3e4;
29573
- var GROK_MEASURED_VERSION = "0.2.117";
29574
- var OPENCODE_MEASURED_VERSION = "1.18.10";
29575
- var CLAUDE_ACP_MEASURED_VERSION = "0.64.2";
29573
+ var GROK_MIN_VERSION = "0.2.117";
29574
+ var GROK_LAST_MEASURED_VERSION = "0.2.117";
29575
+ var OPENCODE_MIN_VERSION = "1.18.10";
29576
+ var OPENCODE_LAST_MEASURED_VERSION = "1.18.10";
29577
+ var CLAUDE_ACP_MIN_VERSION = "0.64.2";
29578
+ var CLAUDE_ACP_LAST_MEASURED_VERSION = "0.64.2";
29576
29579
  var CLAUDE_PERMISSION_MODE_ID = "default";
29577
- var CODEX_ACP_MEASURED_VERSION = "1.1.9";
29580
+ var CODEX_ACP_MIN_VERSION = "1.1.9";
29581
+ var CODEX_ACP_LAST_MEASURED_VERSION = "1.1.9";
29578
29582
  var CODEX_PERMISSION_MODE_ID = "read-only";
29579
29583
  var ACP_PROTOCOL_VERSION = 1;
29580
29584
  var OPENCODE_FORCED_PERMISSION_TOOLS = [
@@ -29789,21 +29793,30 @@ var AcpTransportError = class extends AcpHostError {
29789
29793
  cause;
29790
29794
  };
29791
29795
  var AcpVersionError = class extends AcpHostError {
29792
- constructor(message) {
29793
- super("version_refused", message);
29796
+ constructor(message, code = "version_refused") {
29797
+ super(code, message);
29794
29798
  this.name = "AcpVersionError";
29795
29799
  }
29796
29800
  };
29797
- var AcpVersionMismatchError = class extends AcpVersionError {
29798
- constructor(expected, actual) {
29801
+ var AcpVersionParseError = class extends AcpVersionError {
29802
+ constructor(message) {
29803
+ super(message, "version_unparseable");
29804
+ this.name = "AcpVersionParseError";
29805
+ }
29806
+ };
29807
+ var AcpVersionBelowFloorError = class extends AcpVersionError {
29808
+ constructor(provider, minimum, actual) {
29799
29809
  super(
29800
- `refusing claude-agent-acp ${actual}; host core is measured for ${expected} only`
29810
+ `refusing ${provider} ${actual}; CommonSwarm requires ${minimum} or newer`,
29811
+ "version_below_floor"
29801
29812
  );
29802
- this.expected = expected;
29813
+ this.provider = provider;
29814
+ this.minimum = minimum;
29803
29815
  this.actual = actual;
29804
- this.name = "AcpVersionMismatchError";
29816
+ this.name = "AcpVersionBelowFloorError";
29805
29817
  }
29806
- expected;
29818
+ provider;
29819
+ minimum;
29807
29820
  actual;
29808
29821
  };
29809
29822
  var AcpPermissionCanaryError = class extends AcpHostError {
@@ -30711,6 +30724,98 @@ function createBoundTransport(options) {
30711
30724
  });
30712
30725
  }
30713
30726
 
30727
+ // src/host/version.ts
30728
+ var CORE_IDENTIFIER = "(?:0|[1-9]\\d*)";
30729
+ var PRERELEASE_IDENTIFIER = "(?:0|[1-9]\\d*|[A-Za-z-][0-9A-Za-z-]*)";
30730
+ var BUILD_IDENTIFIER = "[0-9A-Za-z-]+";
30731
+ var SEMVER_SOURCE = `${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}(?:-${PRERELEASE_IDENTIFIER}(?:\\.${PRERELEASE_IDENTIFIER})*)?(?:\\+${BUILD_IDENTIFIER}(?:\\.${BUILD_IDENTIFIER})*)?`;
30732
+ var SEMVER_RE = new RegExp(`^${SEMVER_SOURCE}$`);
30733
+ function parseSemVer(value) {
30734
+ if (!SEMVER_RE.test(value)) return null;
30735
+ const withoutBuild = value.split("+", 1)[0];
30736
+ const dash = withoutBuild.indexOf("-");
30737
+ const coreText = dash === -1 ? withoutBuild : withoutBuild.slice(0, dash);
30738
+ const prereleaseText = dash === -1 ? null : withoutBuild.slice(dash + 1);
30739
+ const coreParts = coreText.split(".");
30740
+ if (coreParts.length !== 3) return null;
30741
+ return {
30742
+ core: [BigInt(coreParts[0]), BigInt(coreParts[1]), BigInt(coreParts[2])],
30743
+ prerelease: prereleaseText === null ? null : prereleaseText.split(".")
30744
+ };
30745
+ }
30746
+ function compareSemVer(left, right) {
30747
+ const a = parseSemVer(left);
30748
+ const b2 = parseSemVer(right);
30749
+ if (!a || !b2) {
30750
+ throw new AcpVersionParseError(
30751
+ `cannot compare invalid semantic versions: ${JSON.stringify(left)} and ${JSON.stringify(right)}`
30752
+ );
30753
+ }
30754
+ for (let index = 0; index < 3; index += 1) {
30755
+ if (a.core[index] < b2.core[index]) return -1;
30756
+ if (a.core[index] > b2.core[index]) return 1;
30757
+ }
30758
+ if (a.prerelease === null && b2.prerelease === null) return 0;
30759
+ if (a.prerelease === null) return 1;
30760
+ if (b2.prerelease === null) return -1;
30761
+ const length = Math.max(a.prerelease.length, b2.prerelease.length);
30762
+ for (let index = 0; index < length; index += 1) {
30763
+ const leftPart = a.prerelease[index];
30764
+ const rightPart = b2.prerelease[index];
30765
+ if (leftPart === void 0) return -1;
30766
+ if (rightPart === void 0) return 1;
30767
+ if (leftPart === rightPart) continue;
30768
+ const leftNumeric = /^\d+$/.test(leftPart);
30769
+ const rightNumeric = /^\d+$/.test(rightPart);
30770
+ if (leftNumeric && rightNumeric) {
30771
+ return BigInt(leftPart) < BigInt(rightPart) ? -1 : 1;
30772
+ }
30773
+ if (leftNumeric) return -1;
30774
+ if (rightNumeric) return 1;
30775
+ return leftPart < rightPart ? -1 : 1;
30776
+ }
30777
+ return 0;
30778
+ }
30779
+ function parseProviderVersionOutput(stdout, productPattern, allowBare = true) {
30780
+ const lines = stdout.split(/\r?\n/);
30781
+ for (const line of lines) {
30782
+ const pattern = new RegExp(productPattern.source, productPattern.flags.replace("g", ""));
30783
+ const product = pattern.exec(line);
30784
+ if (!product) continue;
30785
+ const after = line.slice(product.index + product[0].length);
30786
+ const afterMatch = new RegExp(
30787
+ `^\\s+(${SEMVER_SOURCE})(?=$|\\s|\\()`
30788
+ ).exec(after);
30789
+ if (afterMatch?.[1]) return afterMatch[1];
30790
+ const before = line.slice(0, product.index);
30791
+ const beforeMatch = new RegExp(`(${SEMVER_SOURCE})\\s*\\($`).exec(before);
30792
+ if (beforeMatch?.[1]) return beforeMatch[1];
30793
+ }
30794
+ if (!allowBare) return null;
30795
+ for (const line of lines) {
30796
+ const trimmed = line.trim();
30797
+ const match = new RegExp(`^(${SEMVER_SOURCE})$`).exec(trimmed);
30798
+ if (match?.[1]) return match[1];
30799
+ }
30800
+ return null;
30801
+ }
30802
+ function assertProviderVersionFloor(options) {
30803
+ if (compareSemVer(options.version, options.minimumVersion) < 0) {
30804
+ throw new AcpVersionBelowFloorError(
30805
+ options.provider,
30806
+ options.minimumVersion,
30807
+ options.version
30808
+ );
30809
+ }
30810
+ if (compareSemVer(options.version, options.lastMeasuredVersion) > 0) {
30811
+ options.onNewerVersion?.({
30812
+ provider: options.provider,
30813
+ runningVersion: options.version,
30814
+ lastMeasuredVersion: options.lastMeasuredVersion
30815
+ });
30816
+ }
30817
+ }
30818
+
30714
30819
  // src/host/opencode.ts
30715
30820
  var OPENCODE_HOME_OWNER_FILE = ".cswarm-opencode-owner.json";
30716
30821
  var MAX_OPENCODE_AUTH_BYTES = 256 * 1024;
@@ -30819,11 +30924,11 @@ async function releaseOpenCodeHome(home, instanceId) {
30819
30924
  }
30820
30925
  }
30821
30926
  function parseOpenCodeVersionOutput(stdout) {
30822
- const m = stdout.match(/\b(\d+\.\d+\.\d+)\b/);
30823
- return m?.[1] ?? null;
30927
+ return parseProviderVersionOutput(stdout, /\bopencode\b/i);
30824
30928
  }
30825
- async function assertOpenCodeMeasuredVersion(executable, options) {
30826
- const expected = options?.expected ?? OPENCODE_MEASURED_VERSION;
30929
+ async function assertOpenCodeVersionFloor(executable, options) {
30930
+ const minimumVersion = options?.minimumVersion ?? OPENCODE_MIN_VERSION;
30931
+ const lastMeasuredVersion = options?.lastMeasuredVersion ?? OPENCODE_LAST_MEASURED_VERSION;
30827
30932
  const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
30828
30933
  const env = sanitizeChildEnv(options?.env ?? process.env);
30829
30934
  const stdout = await new Promise((resolve, reject) => {
@@ -30846,15 +30951,17 @@ async function assertOpenCodeMeasuredVersion(executable, options) {
30846
30951
  });
30847
30952
  const version3 = parseOpenCodeVersionOutput(stdout);
30848
30953
  if (!version3) {
30849
- throw new AcpVersionError(
30954
+ throw new AcpVersionParseError(
30850
30955
  `could not parse opencode version from: ${stdout.trim().slice(0, 200)}`
30851
30956
  );
30852
30957
  }
30853
- if (version3 !== expected) {
30854
- throw new AcpVersionError(
30855
- `refusing opencode ${version3}; host core is measured for ${expected} only`
30856
- );
30857
- }
30958
+ assertProviderVersionFloor({
30959
+ provider: "opencode",
30960
+ version: version3,
30961
+ minimumVersion,
30962
+ lastMeasuredVersion,
30963
+ ...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
30964
+ });
30858
30965
  return version3;
30859
30966
  }
30860
30967
  function buildOpenCodeAcpArgs() {
@@ -31221,8 +31328,9 @@ async function openOpenCodeAcpSession(options) {
31221
31328
  };
31222
31329
  try {
31223
31330
  if (!options.skipVersionCheck) {
31224
- await assertOpenCodeMeasuredVersion(executable, {
31225
- env
31331
+ await assertOpenCodeVersionFloor(executable, {
31332
+ env,
31333
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
31226
31334
  });
31227
31335
  }
31228
31336
  if (!options.skipConfigProbe) {
@@ -31350,7 +31458,7 @@ function resolvePackagedClaudeBridge(pathEnv, platform = process.platform) {
31350
31458
  }
31351
31459
  throw new AcpHostError(
31352
31460
  "executable_missing",
31353
- "packaged claude-agent-acp executable not found; install @agentclientprotocol/claude-agent-acp@0.64.2"
31461
+ "packaged claude-agent-acp executable not found; install @agentclientprotocol/claude-agent-acp@latest (minimum 0.64.2)"
31354
31462
  );
31355
31463
  }
31356
31464
  function resolveWindowsNpmShim(shim) {
@@ -31422,14 +31530,10 @@ function buildClaudeLaunch(executable, args, platform = process.platform) {
31422
31530
  return platform === "win32" && (0, import_node_path6.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
31423
31531
  }
31424
31532
  function parseClaudeVersionOutput(stdout) {
31425
- const match = stdout.trim().match(/^(\d+\.\d+\.\d+)$/);
31426
- return match?.[1] ?? null;
31533
+ return parseProviderVersionOutput(stdout, /\bclaude-agent-acp\b/i);
31427
31534
  }
31428
31535
  function parseClaudeCodeVersionOutput(stdout) {
31429
- const match = stdout.match(
31430
- /^(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?) \(Claude Code\)\s*$/m
31431
- );
31432
- return match?.[1] ?? null;
31536
+ return parseProviderVersionOutput(stdout, /\bClaude Code\b/i, false);
31433
31537
  }
31434
31538
  async function readClaudeVersionOutput(executable, options) {
31435
31539
  const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
@@ -31454,18 +31558,23 @@ async function readClaudeVersionOutput(executable, options) {
31454
31558
  );
31455
31559
  });
31456
31560
  }
31457
- async function assertClaudeMeasuredVersion(executable, options) {
31458
- const expected = options?.expected ?? CLAUDE_ACP_MEASURED_VERSION;
31561
+ async function assertClaudeVersionFloor(executable, options) {
31562
+ const minimumVersion = options?.minimumVersion ?? CLAUDE_ACP_MIN_VERSION;
31563
+ const lastMeasuredVersion = options?.lastMeasuredVersion ?? CLAUDE_ACP_LAST_MEASURED_VERSION;
31459
31564
  const stdout = await readClaudeVersionOutput(executable, options);
31460
31565
  const version3 = parseClaudeVersionOutput(stdout);
31461
31566
  if (!version3) {
31462
- throw new AcpVersionError(
31567
+ throw new AcpVersionParseError(
31463
31568
  `could not parse claude-agent-acp version from: ${stdout.trim().slice(0, 200)}`
31464
31569
  );
31465
31570
  }
31466
- if (version3 !== expected) {
31467
- throw new AcpVersionMismatchError(expected, version3);
31468
- }
31571
+ assertProviderVersionFloor({
31572
+ provider: "claude-agent-acp",
31573
+ version: version3,
31574
+ minimumVersion,
31575
+ lastMeasuredVersion,
31576
+ ...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
31577
+ });
31469
31578
  return version3;
31470
31579
  }
31471
31580
  function buildClaudeAcpArgs() {
@@ -31537,18 +31646,22 @@ async function openClaudeAcpSession(options) {
31537
31646
  });
31538
31647
  const bridgeVersion = parseClaudeVersionOutput(output);
31539
31648
  if (bridgeVersion) {
31540
- if (bridgeVersion !== CLAUDE_ACP_MEASURED_VERSION) {
31541
- throw new AcpVersionMismatchError(
31542
- CLAUDE_ACP_MEASURED_VERSION,
31543
- bridgeVersion
31544
- );
31545
- }
31649
+ assertProviderVersionFloor({
31650
+ provider: "claude-agent-acp",
31651
+ version: bridgeVersion,
31652
+ minimumVersion: CLAUDE_ACP_MIN_VERSION,
31653
+ lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
31654
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
31655
+ });
31546
31656
  executable = requestedExecutable;
31547
31657
  } else if (parseClaudeCodeVersionOutput(output)) {
31548
31658
  claudeCodeExecutable = requestedExecutable;
31549
31659
  executable = resolvePackagedClaudeBridge(resolvedPathEnv);
31550
31660
  env = buildClaudeChildEnv(parentEnv, claudeCodeExecutable);
31551
- await assertClaudeMeasuredVersion(executable, { env });
31661
+ await assertClaudeVersionFloor(executable, {
31662
+ env,
31663
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
31664
+ });
31552
31665
  } else {
31553
31666
  throw new AcpVersionError(
31554
31667
  `could not identify Claude executable from: ${output.trim().slice(0, 200)}`
@@ -31560,7 +31673,10 @@ async function openClaudeAcpSession(options) {
31560
31673
  resolvedPathEnv
31561
31674
  );
31562
31675
  if (!options.skipVersionCheck) {
31563
- await assertClaudeMeasuredVersion(executable, { env: baseEnv });
31676
+ await assertClaudeVersionFloor(executable, {
31677
+ env: baseEnv,
31678
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
31679
+ });
31564
31680
  }
31565
31681
  }
31566
31682
  if (options.signal?.aborted) {
@@ -31761,13 +31877,11 @@ function buildCodexLaunch(executable, args, platform = process.platform) {
31761
31877
  return platform === "win32" && (0, import_node_path7.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
31762
31878
  }
31763
31879
  function parseCodexVersionOutput(stdout) {
31764
- const match = stdout.trim().match(
31765
- /^@agentclientprotocol\/codex-acp (\d+\.\d+\.\d+)$/
31766
- );
31767
- return match?.[1] ?? null;
31880
+ return parseProviderVersionOutput(stdout, /@agentclientprotocol\/codex-acp\b/i);
31768
31881
  }
31769
- async function assertCodexMeasuredVersion(executable, options) {
31770
- const expected = options?.expected ?? CODEX_ACP_MEASURED_VERSION;
31882
+ async function assertCodexVersionFloor(executable, options) {
31883
+ const minimumVersion = options?.minimumVersion ?? CODEX_ACP_MIN_VERSION;
31884
+ const lastMeasuredVersion = options?.lastMeasuredVersion ?? CODEX_ACP_LAST_MEASURED_VERSION;
31771
31885
  const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
31772
31886
  const env = options?.env ?? sanitizeChildEnv(process.env);
31773
31887
  const launch = buildCodexLaunch(executable, ["--version"], options?.platform);
@@ -31791,15 +31905,17 @@ async function assertCodexMeasuredVersion(executable, options) {
31791
31905
  });
31792
31906
  const version3 = parseCodexVersionOutput(stdout);
31793
31907
  if (!version3) {
31794
- throw new AcpVersionError(
31908
+ throw new AcpVersionParseError(
31795
31909
  `could not parse codex-acp version from: ${stdout.trim().slice(0, 200)}`
31796
31910
  );
31797
31911
  }
31798
- if (version3 !== expected) {
31799
- throw new AcpVersionError(
31800
- `refusing codex-acp ${version3}; host core is measured for ${expected} only`
31801
- );
31802
- }
31912
+ assertProviderVersionFloor({
31913
+ provider: "codex-acp",
31914
+ version: version3,
31915
+ minimumVersion,
31916
+ lastMeasuredVersion,
31917
+ ...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
31918
+ });
31803
31919
  return version3;
31804
31920
  }
31805
31921
  function buildCodexAcpArgs() {
@@ -31855,7 +31971,10 @@ async function openCodexAcpSession(options) {
31855
31971
  );
31856
31972
  const env = buildCodexChildEnv(parentEnv);
31857
31973
  if (!options.skipVersionCheck) {
31858
- await assertCodexMeasuredVersion(executable, { env });
31974
+ await assertCodexVersionFloor(executable, {
31975
+ env,
31976
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
31977
+ });
31859
31978
  }
31860
31979
  if (options.signal?.aborted) {
31861
31980
  throw new AcpHostError(
@@ -32765,10 +32884,12 @@ function resolveGrokExecutable(executable = "grok") {
32765
32884
  return executable;
32766
32885
  }
32767
32886
  function parseGrokVersionOutput(stdout) {
32768
- const m = stdout.match(/\bgrok\s+(\d+\.\d+\.\d+)\b/i);
32769
- return m?.[1] ?? null;
32887
+ return parseProviderVersionOutput(stdout, /\bgrok\b/i);
32770
32888
  }
32771
- async function assertGrokMeasuredVersion(executable, expected = GROK_MEASURED_VERSION, timeoutMs = ACP_VERSION_CHECK_TIMEOUT_MS) {
32889
+ async function assertGrokVersionFloor(executable, options) {
32890
+ const minimumVersion = options?.minimumVersion ?? GROK_MIN_VERSION;
32891
+ const lastMeasuredVersion = options?.lastMeasuredVersion ?? GROK_LAST_MEASURED_VERSION;
32892
+ const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
32772
32893
  const stdout = await new Promise((resolve, reject) => {
32773
32894
  (0, import_node_child_process6.execFile)(
32774
32895
  executable,
@@ -32789,15 +32910,17 @@ async function assertGrokMeasuredVersion(executable, expected = GROK_MEASURED_VE
32789
32910
  });
32790
32911
  const version3 = parseGrokVersionOutput(stdout);
32791
32912
  if (!version3) {
32792
- throw new AcpVersionError(
32913
+ throw new AcpVersionParseError(
32793
32914
  `could not parse grok version from: ${stdout.trim().slice(0, 200)}`
32794
32915
  );
32795
32916
  }
32796
- if (version3 !== expected) {
32797
- throw new AcpVersionError(
32798
- `refusing grok ${version3}; host core is measured for ${expected} only`
32799
- );
32800
- }
32917
+ assertProviderVersionFloor({
32918
+ provider: "grok",
32919
+ version: version3,
32920
+ minimumVersion,
32921
+ lastMeasuredVersion,
32922
+ ...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
32923
+ });
32801
32924
  return version3;
32802
32925
  }
32803
32926
  function buildGrokAcpArgs(options) {
@@ -32855,7 +32978,9 @@ async function terminateGrokChild(child) {
32855
32978
  async function openGrokAcpSession(options) {
32856
32979
  const executable = resolveGrokExecutable(options.executable ?? "grok");
32857
32980
  if (!options.skipVersionCheck) {
32858
- await assertGrokMeasuredVersion(executable);
32981
+ await assertGrokVersionFloor(executable, {
32982
+ ...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
32983
+ });
32859
32984
  }
32860
32985
  const args = buildGrokAcpArgs({
32861
32986
  model: options.model,
@@ -32984,6 +33109,7 @@ var GrokListenerModel = class {
32984
33109
  ...this.options.model ? { model: this.options.model } : {},
32985
33110
  ...this.options.effort ? { effort: this.options.effort } : {},
32986
33111
  ...this.options.env ? { env: this.options.env } : {},
33112
+ ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
32987
33113
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
32988
33114
  clientName: "cswarm-listener"
32989
33115
  });
@@ -33392,6 +33518,7 @@ var OpenCodeListenerModel = class {
33392
33518
  ...this.options.executable ? { executable: this.options.executable } : {},
33393
33519
  ...this.options.model ? { model: this.options.model } : {},
33394
33520
  ...this.options.env ? { env: this.options.env } : {},
33521
+ ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
33395
33522
  ...this.options.allowMissingAuth === true ? { allowMissingAuth: true } : {},
33396
33523
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
33397
33524
  clientName: "cswarm-listener"
@@ -33601,6 +33728,7 @@ var ClaudeListenerModel = class {
33601
33728
  permissionCallback,
33602
33729
  ...this.options.executable ? { executable: this.options.executable } : {},
33603
33730
  ...this.options.env ? { env: this.options.env } : {},
33731
+ ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
33604
33732
  signal: controller.signal,
33605
33733
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
33606
33734
  clientName: "cswarm-listener"
@@ -33769,6 +33897,7 @@ var CodexListenerModel = class {
33769
33897
  permissionCallback,
33770
33898
  ...this.options.executable ? { executable: this.options.executable } : {},
33771
33899
  ...this.options.env ? { env: this.options.env } : {},
33900
+ ...this.options.onVersionNotice ? { onVersionNotice: this.options.onVersionNotice } : {},
33772
33901
  signal: controller.signal,
33773
33902
  ...this.options.onWorkerStderrTail ? { onStderrTail: this.options.onWorkerStderrTail } : {},
33774
33903
  clientName: "cswarm-listener"
@@ -35662,6 +35791,7 @@ var import_node_net = require("node:net");
35662
35791
  var import_promises9 = require("node:fs/promises");
35663
35792
  var import_node_path15 = require("node:path");
35664
35793
  var UUID_RE13 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
35794
+ 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-]+)*)?$/;
35665
35795
  var MAX_STATUS_BYTES = 16 * 1024;
35666
35796
  var MAX_CONTROL_BYTES = 8 * 1024;
35667
35797
  var CONTROL_TIMEOUT_MS = 2e3;
@@ -35708,6 +35838,8 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
35708
35838
  "stoppedAt",
35709
35839
  "lastSignalId",
35710
35840
  "lastErrorCode",
35841
+ "providerVersion",
35842
+ "providerLastMeasuredVersion",
35711
35843
  "lastWorkerStderrTail",
35712
35844
  "logPath",
35713
35845
  "deliveryMode",
@@ -35767,7 +35899,7 @@ function parseStatus(raw) {
35767
35899
  const nullableUuid2 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE13.test(candidate);
35768
35900
  const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
35769
35901
  const nullableTimestamp = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
35770
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE13.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE13.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE13.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))) || !nullableUuid2(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(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_path15.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp(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)) {
35902
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE13.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE13.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE13.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))) || !nullableUuid2(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(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.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_path15.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp(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)) {
35771
35903
  throw new Error("stored listener status is malformed");
35772
35904
  }
35773
35905
  const routeMode = row.routeMode ?? "worker";
@@ -35784,6 +35916,8 @@ function parseStatus(raw) {
35784
35916
  lastClaimAt: row.lastClaimAt ?? null,
35785
35917
  lastAckAt: row.lastAckAt ?? null,
35786
35918
  lastWorkerStderrTail: row.lastWorkerStderrTail ?? null,
35919
+ providerVersion: row.providerVersion ?? null,
35920
+ providerLastMeasuredVersion: row.providerLastMeasuredVersion ?? null,
35787
35921
  routeMode,
35788
35922
  deferOverChars,
35789
35923
  pendingForMainCount: row.pendingForMainCount ?? 0,
@@ -36195,6 +36329,8 @@ async function runListenerSupervisor(options) {
36195
36329
  stoppedAt: null,
36196
36330
  lastSignalId: null,
36197
36331
  lastErrorCode: null,
36332
+ providerVersion: null,
36333
+ providerLastMeasuredVersion: null,
36198
36334
  lastWorkerStderrTail: null,
36199
36335
  deliveryMode: null,
36200
36336
  pendingDeliveryCount: null,
@@ -36264,10 +36400,13 @@ async function runListenerSupervisor(options) {
36264
36400
  };
36265
36401
  const onEvent = (event) => {
36266
36402
  if (event.type === "ready") {
36403
+ const versionNotice = options.getProviderVersionNotice?.() ?? null;
36267
36404
  transition("ready", {
36268
36405
  readyAt: event.ts,
36269
36406
  lastErrorCode: null,
36270
- lastWorkerStderrTail: null
36407
+ lastWorkerStderrTail: null,
36408
+ providerVersion: versionNotice?.runningVersion ?? null,
36409
+ providerLastMeasuredVersion: versionNotice?.lastMeasuredVersion ?? null
36271
36410
  });
36272
36411
  log({ ts: event.ts, event: "listener_ready" });
36273
36412
  return;
@@ -36465,7 +36604,9 @@ async function runListenerSupervisor(options) {
36465
36604
  transition("starting", {
36466
36605
  readyAt: null,
36467
36606
  lastErrorCode: restartCode,
36468
- lastWorkerStderrTail: restartStderrTail
36607
+ lastWorkerStderrTail: restartStderrTail,
36608
+ providerVersion: null,
36609
+ providerLastMeasuredVersion: null
36469
36610
  });
36470
36611
  await restartSleep(delayMs, controller.signal);
36471
36612
  if (controller.signal.aborted) {
@@ -37994,8 +38135,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
37994
38135
  AGENT_CREDENTIAL_MESSAGE_D088
37995
38136
  ];
37996
38137
  function packageVersion() {
37997
- if ("0.1.27".length > 0) {
37998
- return "0.1.27";
38138
+ if ("0.1.28".length > 0) {
38139
+ return "0.1.28";
37999
38140
  }
38000
38141
  try {
38001
38142
  const value = JSON.parse(
@@ -38111,8 +38252,8 @@ Usage:
38111
38252
  cswarm status [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--json]
38112
38253
  cswarm members [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
38113
38254
  cswarm working-on "<what>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--about <ref>] [--until <dur>] [--json]
38114
- cswarm note "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--to <member|agent>] [--about <ref>] [--until <dur>] [--json]
38115
- cswarm ask "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--to <member|agent>] [--about <ref>] [--until <dur>] [--wait <seconds>] [--json]
38255
+ cswarm note "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--to <member|agent>] [--about <ref>] [--until <dur>] [--json] # text: 1..8000 characters
38256
+ cswarm ask "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--to <member|agent>] [--about <ref>] [--until <dur>] [--wait <seconds>] [--json] # text: 1..8000 characters
38116
38257
  cswarm reply <signal-id> "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--until <dur>] [--json]
38117
38258
  cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--about <ref>] [--kind <kind>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
38118
38259
  cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
@@ -38178,7 +38319,7 @@ Signals (intention sharing) accept the same credential selection. Agent mode
38178
38319
  never opens a browser or infers a human's saved workspace. Durations use a whole
38179
38320
  number plus m, h, or d (for example 90m, 24h, or 7d) and are capped at 30d.
38180
38321
  Place -- before signal text that itself begins with -- to stop option parsing.
38181
- Signal text is at most 2000 characters and --about at most 500; a longer body is
38322
+ Signal text is at most 8000 characters and --about at most 500; a longer body is
38182
38323
  refused locally before any network call, so compose within the limit.
38183
38324
 
38184
38325
  listen start --turn-budget bounds ONE worker prompt turn (default 10m): how long
@@ -39662,13 +39803,18 @@ function resolveTurnBudgetOrDefer(configuredBudgetMs, credentialExpiresAt, nowMs
39662
39803
  }
39663
39804
  return clampTurnBudgetToCredential(configuredBudgetMs, credentialExpiresAt, nowMs);
39664
39805
  }
39665
- var SIGNAL_BODY_MAX = 2e3;
39806
+ var SIGNAL_BODY_MAX = 8e3;
39666
39807
  var SIGNAL_ABOUT_MAX = 500;
39667
39808
  function signalText(value, label) {
39668
39809
  const maximum = label === "body" ? SIGNAL_BODY_MAX : SIGNAL_ABOUT_MAX;
39669
39810
  if (value.length < (label === "body" ? 1 : 0) || value.length > maximum) {
39811
+ if (label === "body") {
39812
+ throw new Error(
39813
+ `signal text is ${value.length} characters; the maximum is ${maximum}`
39814
+ );
39815
+ }
39670
39816
  throw new Error(
39671
- `${label === "body" ? "signal text" : "--about"} must be ${label === "body" ? "1.." : "at most "}${maximum} characters`
39817
+ `--about must be at most ${maximum} characters`
39672
39818
  );
39673
39819
  }
39674
39820
  return value;
@@ -40329,7 +40475,7 @@ function listenerModelLabel(provider) {
40329
40475
  }
40330
40476
  function listenerProvider(args) {
40331
40477
  const provider = args.optional("provider");
40332
- const hints = "supported providers: grok \u2014 install Grok CLI 0.2.117 and run grok login; opencode \u2014 install OpenCode 1.18.10 and authenticate it; claude \u2014 npm install -g @agentclientprotocol/claude-agent-acp@0.64.2; codex \u2014 npm install -g @agentclientprotocol/codex-acp@1.1.9. You can use working-on, note, ask, and feed now; detached live receipt needs one of these adapters";
40478
+ const hints = "supported providers: grok \u2014 install Grok CLI 0.2.117 or newer and run grok login; opencode \u2014 install OpenCode 1.18.10 or newer and authenticate it; claude \u2014 npm install -g @agentclientprotocol/claude-agent-acp@latest (minimum 0.64.2); codex \u2014 npm install -g @agentclientprotocol/codex-acp@latest (minimum 1.1.9). You can use working-on, note, ask, and feed now; detached live receipt needs one of these adapters";
40333
40479
  if (provider === void 0) {
40334
40480
  throw new Error(`--provider is required; ${hints}`);
40335
40481
  }
@@ -40390,7 +40536,7 @@ function listenerHostLimits(provider) {
40390
40536
  };
40391
40537
  }
40392
40538
  if (provider === "claude") {
40393
- const host_configuration2 = "The Claude worker uses the operator-selected cwd and the normal Claude Code home through claude-agent-acp 0.64.2. Keychain/OAuth auth was measured; ANTHROPIC_API_KEY is stripped by the listener environment sanitizer.";
40539
+ const host_configuration2 = "The Claude worker uses the operator-selected cwd and the normal Claude Code home through claude-agent-acp 0.64.2 or newer. Keychain/OAuth auth was measured; ANTHROPIC_API_KEY is stripped by the listener environment sanitizer.";
40394
40540
  const deny_canary_scope2 = "The deny canary proves host reject + correlated terminal deny only.";
40395
40541
  const steady_allow_unproven2 = "The deny canary does not prove steady-state --permissions allow behavior.";
40396
40542
  const cross_owner_context2 = "All sender relations reach that same worker and local context; each prompt carries sender and operator provenance.";
@@ -40414,7 +40560,7 @@ function listenerHostLimits(provider) {
40414
40560
  };
40415
40561
  }
40416
40562
  if (provider === "codex") {
40417
- const host_configuration2 = "The Codex worker uses the operator-selected cwd and normal ChatGPT/Codex auth through codex-acp 1.1.9. CommonSwarm explicitly selects read-only mode after every session/new; API-key variables are stripped by the listener environment sanitizer.";
40563
+ const host_configuration2 = "The Codex worker uses the operator-selected cwd and normal ChatGPT/Codex auth through codex-acp 1.1.9 or newer. CommonSwarm explicitly selects read-only mode after every session/new; API-key variables are stripped by the listener environment sanitizer.";
40418
40564
  const deny_canary_scope2 = "The deny canary proves host reject + correlated terminal deny only.";
40419
40565
  const steady_allow_unproven2 = "The deny canary does not prove steady-state --permissions allow behavior.";
40420
40566
  const cross_owner_context2 = "All sender relations reach that same worker and local context; each prompt carries sender and operator provenance.";
@@ -40503,6 +40649,11 @@ function renderListenerStatus(status) {
40503
40649
  lines.push(` ${line}`);
40504
40650
  }
40505
40651
  }
40652
+ if (status.providerVersion && status.providerLastMeasuredVersion) {
40653
+ lines.push(
40654
+ `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.`
40655
+ );
40656
+ }
40506
40657
  if (status.deliveryMode === "durable_claim") {
40507
40658
  lines.push("Delivery mode: durable claim and acknowledgement.");
40508
40659
  } else if (status.deliveryMode === "cursor_fallback") {
@@ -40545,29 +40696,35 @@ function renderListenerStatus(status) {
40545
40696
  return lines.join("\n");
40546
40697
  }
40547
40698
  function listenerFailureMessage(code, provider) {
40548
- if (code === "version_refused") {
40699
+ if (code === "version_below_floor") {
40549
40700
  if (provider === "codex") {
40550
- return "the Codex listener requires codex-acp 1.1.9; run npm install -g @agentclientprotocol/codex-acp@1.1.9, then retry";
40701
+ return "the Codex listener requires codex-acp 1.1.9 or newer; update the bridge, then retry";
40551
40702
  }
40552
40703
  if (provider === "claude") {
40553
- return "the Claude listener requires claude-agent-acp 0.64.2; run npm install -g @agentclientprotocol/claude-agent-acp@0.64.2, then retry";
40704
+ return "the Claude listener requires claude-agent-acp 0.64.2 or newer; update the bridge, then retry";
40554
40705
  }
40555
40706
  if (provider === "opencode") {
40556
- return "the OpenCode listener requires OpenCode 1.18.10; install that pinned build, then retry";
40707
+ return "the OpenCode listener requires OpenCode 1.18.10 or newer; update OpenCode, then retry";
40557
40708
  }
40558
- return "the Grok listener requires Grok 0.2.117; install that pinned build and run grok login, then retry";
40709
+ return "the Grok listener requires Grok 0.2.117 or newer; update Grok, confirm grok login, then retry";
40710
+ }
40711
+ if (code === "version_unparseable") {
40712
+ return `the ${provider ?? "provider"} version output was not valid semantic version data, so startup stopped. Next: run the provider's --version command, then update or reinstall it`;
40713
+ }
40714
+ if (code === "version_refused") {
40715
+ return `the ${provider ?? "provider"} version check could not run, so startup stopped. Next: run the provider's --version command and fix that error, then retry`;
40559
40716
  }
40560
40717
  if (code === "executable_missing" && provider === "claude") {
40561
- return "claude-agent-acp is not installed; run npm install -g @agentclientprotocol/claude-agent-acp@0.64.2, then retry";
40718
+ return "claude-agent-acp is not installed; run npm install -g @agentclientprotocol/claude-agent-acp@latest (minimum 0.64.2), then retry";
40562
40719
  }
40563
40720
  if (code === "executable_missing" && provider === "codex") {
40564
- return "codex-acp is not installed; run npm install -g @agentclientprotocol/codex-acp@1.1.9, then retry";
40721
+ return "codex-acp is not installed; run npm install -g @agentclientprotocol/codex-acp@latest (minimum 1.1.9), then retry";
40565
40722
  }
40566
40723
  if (code === "permission_mode_unavailable" && provider === "claude") {
40567
- return "the Claude bridge does not expose the required manual permission mode; reinstall claude-agent-acp 0.64.2, then retry";
40724
+ return "the Claude bridge does not expose the required manual permission mode; update or reinstall claude-agent-acp, then retry";
40568
40725
  }
40569
40726
  if (code === "permission_mode_unavailable" && provider === "codex") {
40570
- return "the Codex bridge does not expose the required read-only permission mode; reinstall codex-acp 1.1.9, then retry";
40727
+ return "the Codex bridge does not expose the required read-only permission mode; update or reinstall codex-acp, then retry";
40571
40728
  }
40572
40729
  if (provider === "claude" && (code === "rpc_error" || code === "child_exit" || code === "timeout")) {
40573
40730
  return `the Claude bridge could not start (${code}); confirm Claude Code keychain/OAuth sign-in and network access, then retry. ANTHROPIC_API_KEY is not forwarded to detached listeners`;
@@ -40619,7 +40776,7 @@ function resolveDetachedClaudeExecutable(executable = "claude-agent-acp", pathEn
40619
40776
  if ((0, import_node_path19.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
40620
40777
  const detail = error instanceof Error ? error.message : code;
40621
40778
  throw new Error(
40622
- `could not use --claude-executable: ${detail}; reinstall the measured bridge with npm install -g @agentclientprotocol/claude-agent-acp@0.64.2 if this path should be replaced`
40779
+ `could not use --claude-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/claude-agent-acp@latest if this path should be replaced`
40623
40780
  );
40624
40781
  }
40625
40782
  throw new Error(listenerFailureMessage(code, "claude"));
@@ -40636,7 +40793,7 @@ function resolveDetachedCodexExecutable(executable = "codex-acp", pathEnv = proc
40636
40793
  if ((0, import_node_path19.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
40637
40794
  const detail = error instanceof Error ? error.message : code;
40638
40795
  throw new Error(
40639
- `could not use --codex-executable: ${detail}; reinstall the measured bridge with npm install -g @agentclientprotocol/codex-acp@1.1.9 if this path should be replaced`
40796
+ `could not use --codex-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/codex-acp@latest if this path should be replaced`
40640
40797
  );
40641
40798
  }
40642
40799
  throw new Error(listenerFailureMessage(code, "codex"));
@@ -40740,6 +40897,10 @@ async function runConfiguredListener(options) {
40740
40897
  };
40741
40898
  let lastWorkerStderrTail = null;
40742
40899
  let workerStderrGeneration = 0;
40900
+ let providerVersionNotice = null;
40901
+ const onVersionNotice = (notice) => {
40902
+ providerVersionNotice = notice;
40903
+ };
40743
40904
  const newWorkerStderrTailSink = () => {
40744
40905
  const generation = ++workerStderrGeneration;
40745
40906
  lastWorkerStderrTail = null;
@@ -40748,34 +40909,41 @@ async function runConfiguredListener(options) {
40748
40909
  lastWorkerStderrTail = tail.length > 0 ? tail : null;
40749
40910
  };
40750
40911
  };
40751
- const newModel = () => options.provider === "opencode" ? new OpenCodeListenerModel({
40752
- cwd: options.cwd,
40753
- permissionMode: options.permissionMode,
40754
- promptTimeoutMs: resolveTurnBudgetMs,
40755
- onWorkerStderrTail: newWorkerStderrTailSink(),
40756
- ...options.model ? { model: options.model } : {},
40757
- ...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
40758
- }) : options.provider === "claude" ? new ClaudeListenerModel({
40759
- cwd: options.cwd,
40760
- permissionMode: options.permissionMode,
40761
- promptTimeoutMs: resolveTurnBudgetMs,
40762
- onWorkerStderrTail: newWorkerStderrTailSink(),
40763
- ...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
40764
- }) : options.provider === "codex" ? new CodexListenerModel({
40765
- cwd: options.cwd,
40766
- permissionMode: options.permissionMode,
40767
- promptTimeoutMs: resolveTurnBudgetMs,
40768
- onWorkerStderrTail: newWorkerStderrTailSink(),
40769
- ...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
40770
- }) : new GrokListenerModel({
40771
- cwd: options.cwd,
40772
- permissionMode: options.permissionMode,
40773
- promptTimeoutMs: resolveTurnBudgetMs,
40774
- onWorkerStderrTail: newWorkerStderrTailSink(),
40775
- ...options.model ? { model: options.model } : {},
40776
- ...options.effort ? { effort: options.effort } : {},
40777
- ...options.executable ? { executable: options.executable } : {}
40778
- });
40912
+ const newModel = () => {
40913
+ providerVersionNotice = null;
40914
+ return options.provider === "opencode" ? new OpenCodeListenerModel({
40915
+ cwd: options.cwd,
40916
+ permissionMode: options.permissionMode,
40917
+ promptTimeoutMs: resolveTurnBudgetMs,
40918
+ onWorkerStderrTail: newWorkerStderrTailSink(),
40919
+ onVersionNotice,
40920
+ ...options.model ? { model: options.model } : {},
40921
+ ...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
40922
+ }) : options.provider === "claude" ? new ClaudeListenerModel({
40923
+ cwd: options.cwd,
40924
+ permissionMode: options.permissionMode,
40925
+ promptTimeoutMs: resolveTurnBudgetMs,
40926
+ onWorkerStderrTail: newWorkerStderrTailSink(),
40927
+ onVersionNotice,
40928
+ ...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
40929
+ }) : options.provider === "codex" ? new CodexListenerModel({
40930
+ cwd: options.cwd,
40931
+ permissionMode: options.permissionMode,
40932
+ promptTimeoutMs: resolveTurnBudgetMs,
40933
+ onWorkerStderrTail: newWorkerStderrTailSink(),
40934
+ onVersionNotice,
40935
+ ...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
40936
+ }) : new GrokListenerModel({
40937
+ cwd: options.cwd,
40938
+ permissionMode: options.permissionMode,
40939
+ promptTimeoutMs: resolveTurnBudgetMs,
40940
+ onWorkerStderrTail: newWorkerStderrTailSink(),
40941
+ onVersionNotice,
40942
+ ...options.model ? { model: options.model } : {},
40943
+ ...options.effort ? { effort: options.effort } : {},
40944
+ ...options.executable ? { executable: options.executable } : {}
40945
+ });
40946
+ };
40779
40947
  const onProcessSignal = () => {
40780
40948
  void stopListener(paths);
40781
40949
  };
@@ -40799,6 +40967,7 @@ async function runConfiguredListener(options) {
40799
40967
  // The bound a timeout event reports: the last turn's clamped budget when
40800
40968
  // one has run, else the configured cap.
40801
40969
  getTurnBudgetMs: () => lastAppliedTurnBudgetMs ?? turnBudgetMs,
40970
+ getProviderVersionNotice: () => providerVersionNotice,
40802
40971
  takeWorkerStderrTail: () => {
40803
40972
  const tail = lastWorkerStderrTail;
40804
40973
  lastWorkerStderrTail = null;
@@ -41017,8 +41186,8 @@ async function runListenStart(args) {
41017
41186
  ` : "";
41018
41187
  const workerAudience = routing.routeMode === "main" ? "Directed asks do not reach that worker." : routing.routeMode === "split" ? "Only asks at or below the split threshold reach that worker, with sender and operator provenance in the prompt." : "Every sender reaches that worker with sender and operator provenance in the prompt.";
41019
41188
  const hostNote = provider === "opencode" ? `The OpenCode worker uses one private auth/config home and your selected project cwd. ${workerAudience} Tool requests are approved one at a time by default, when the worker asks and the host offers a one-time approval; --permissions deny refuses them. The deny canary does not cover steady-state allow.
41020
- ` : provider === "claude" ? `The Claude worker uses your selected cwd and normal Claude Code keychain/OAuth state through claude-agent-acp 0.64.2. ${workerAudience}
41021
- ` : provider === "codex" ? `The Codex worker uses your selected cwd and normal ChatGPT/Codex auth through codex-acp 1.1.9. CommonSwarm selects read-only mode before its deny canary. ${workerAudience}
41189
+ ` : provider === "claude" ? `The Claude worker uses your selected cwd and normal Claude Code keychain/OAuth state through claude-agent-acp 0.64.2 or newer. ${workerAudience}
41190
+ ` : provider === "codex" ? `The Codex worker uses your selected cwd and normal ChatGPT/Codex auth through codex-acp 1.1.9 or newer. CommonSwarm selects read-only mode before its deny canary. ${workerAudience}
41022
41191
  ` : `The Grok worker uses your selected cwd and local Grok configuration, including user and cmux hooks. ${workerAudience}
41023
41192
  `;
41024
41193
  process.stdout.write(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "CommonSwarm CLI — coordination for teams where people and AI agents work side by side.",
5
5
  "bin": {
6
6
  "cswarm": "cswarm.cjs"