commonswarm 0.1.26 → 0.1.27

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 +224 -48
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -28550,12 +28550,12 @@ function parseRetryAfterMs(header, nowMs = Date.now()) {
28550
28550
  if (!Number.isFinite(when)) return null;
28551
28551
  return Math.max(0, Math.min(when - nowMs, SIGNAL_FOLLOW_BACKOFF_MAX_MS));
28552
28552
  }
28553
- function throwSignalHttp(response, body) {
28553
+ function throwSignalHttp(response, body, failure = "signal read failed") {
28554
28554
  const status = response.status;
28555
28555
  const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
28556
28556
  const envelope = parseServerErrorEnvelope(body);
28557
28557
  const error = new Error(
28558
- describeServerError(`signal read failed (HTTP ${status})`, envelope)
28558
+ describeServerError(`${failure} (HTTP ${status})`, envelope)
28559
28559
  );
28560
28560
  plainHttpStatus.set(error, status);
28561
28561
  plainHttpRetryAfterMs.set(error, retryAfterMs);
@@ -28912,7 +28912,7 @@ async function readAgentSignalDirectory(target2, token, workspaceId2, fetcherOrO
28912
28912
  }
28913
28913
  const { response, body } = result;
28914
28914
  if (!response.ok) {
28915
- throw new Error(`member read failed (HTTP ${response.status})`);
28915
+ throwSignalHttp(response, body, "member read failed");
28916
28916
  }
28917
28917
  if (!body || typeof body !== "object" || Array.isArray(body)) {
28918
28918
  throw new Error("member read returned malformed JSON");
@@ -29130,6 +29130,37 @@ function postSignalTargets(recipient) {
29130
29130
  };
29131
29131
  }
29132
29132
  var ASK_WAIT_TIMEOUT_MESSAGE = "Ask shared. No reply arrived before the wait ended; the ask remains live. Check for a reply with: cswarm inbox";
29133
+ function askFailureDetail(error) {
29134
+ const readHttp = followHttpDetails(error);
29135
+ if (readHttp !== null) {
29136
+ return {
29137
+ detail: describeServerError(
29138
+ `HTTP ${readHttp.status}`,
29139
+ followErrorEnvelope(error)
29140
+ ),
29141
+ serverError: true
29142
+ };
29143
+ }
29144
+ if (error instanceof CommandHttpError) {
29145
+ return {
29146
+ detail: `HTTP ${error.status}; server detail: ${error.message.replace(/^signal read failed \(HTTP \d+\)(?:: )?/, "").slice(0, 240)}`,
29147
+ serverError: true
29148
+ };
29149
+ }
29150
+ return {
29151
+ detail: error instanceof Error ? error.message.slice(0, 300) : "unknown error",
29152
+ serverError: false
29153
+ };
29154
+ }
29155
+ function askCreateFailureMessage(workspaceId2, error) {
29156
+ const failure = askFailureDetail(error);
29157
+ const cause = failure.serverError ? `server error before it was confirmed: ${failure.detail}` : `request failure before it was confirmed: ${failure.detail}`;
29158
+ return `Your message may not have been posted (${cause}). Check with: cswarm feed --workspace-id ${workspaceId2} \u2014 and resend if it is not there.`;
29159
+ }
29160
+ function askReplyReadFailureMessage(workspaceId2, error) {
29161
+ const failure = askFailureDetail(error);
29162
+ return `Your message was posted, but its reply could not be fetched (${failure.detail}). Do not resend this ask. Check with: cswarm inbox --workspace-id ${workspaceId2}`;
29163
+ }
29133
29164
  function renderSignals(signals, options) {
29134
29165
  if (signals.length === 0) {
29135
29166
  return [
@@ -29763,6 +29794,18 @@ var AcpVersionError = class extends AcpHostError {
29763
29794
  this.name = "AcpVersionError";
29764
29795
  }
29765
29796
  };
29797
+ var AcpVersionMismatchError = class extends AcpVersionError {
29798
+ constructor(expected, actual) {
29799
+ super(
29800
+ `refusing claude-agent-acp ${actual}; host core is measured for ${expected} only`
29801
+ );
29802
+ this.expected = expected;
29803
+ this.actual = actual;
29804
+ this.name = "AcpVersionMismatchError";
29805
+ }
29806
+ expected;
29807
+ actual;
29808
+ };
29766
29809
  var AcpPermissionCanaryError = class extends AcpHostError {
29767
29810
  constructor(message) {
29768
29811
  super("permission_canary_failed", message);
@@ -29800,11 +29843,26 @@ var AcpTransport = class extends import_node_events.EventEmitter {
29800
29843
  this.writable = options.writable;
29801
29844
  this.handlers = options.handlers ?? {};
29802
29845
  this.requestTimeoutMs = options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS;
29846
+ const readableEndGraceMs = Math.max(0, options.readableEndGraceMs ?? 0);
29847
+ let readableEndTimer = null;
29803
29848
  options.readable.on("data", (chunk) => {
29804
29849
  this.onData(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
29805
29850
  });
29806
29851
  options.readable.on("end", () => {
29807
- this.failAll(new AcpChildExitError(this.childExit?.code ?? null, this.childExit?.signal ?? null));
29852
+ const fail = () => {
29853
+ readableEndTimer = null;
29854
+ this.failAll(
29855
+ new AcpChildExitError(
29856
+ this.childExit?.code ?? null,
29857
+ this.childExit?.signal ?? null
29858
+ )
29859
+ );
29860
+ };
29861
+ if (readableEndGraceMs > 0 && options.onChildExit) {
29862
+ readableEndTimer = setTimeout(fail, readableEndGraceMs);
29863
+ } else {
29864
+ fail();
29865
+ }
29808
29866
  });
29809
29867
  options.readable.on("error", (err) => {
29810
29868
  this.failAll(asAcpHostError(err));
@@ -29813,6 +29871,10 @@ var AcpTransport = class extends import_node_events.EventEmitter {
29813
29871
  this.failAll(asAcpHostError(err));
29814
29872
  });
29815
29873
  options.onChildExit?.((code, signal) => {
29874
+ if (readableEndTimer) {
29875
+ clearTimeout(readableEndTimer);
29876
+ readableEndTimer = null;
29877
+ }
29816
29878
  this.childExit = { code, signal };
29817
29879
  this.failAll(new AcpChildExitError(code, signal));
29818
29880
  });
@@ -30633,6 +30695,7 @@ function createBoundTransport(options) {
30633
30695
  writable: options.writable,
30634
30696
  requestTimeoutMs: options.requestTimeoutMs,
30635
30697
  onChildExit: options.onChildExit,
30698
+ readableEndGraceMs: options.readableEndGraceMs,
30636
30699
  handlers: {
30637
30700
  onNotification: (method, params) => {
30638
30701
  options.getSession()?.handleAgentNotification(method, params);
@@ -31256,6 +31319,8 @@ var import_node_fs4 = require("node:fs");
31256
31319
  var import_node_path6 = require("node:path");
31257
31320
  var CHILD_EXIT_WAIT_MS2 = 3e3;
31258
31321
  var CHILD_KILL_WAIT_MS2 = 1e3;
31322
+ var STDERR_EXIT_GRACE_MS = 100;
31323
+ var READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
31259
31324
  var WINDOWS_NPM_SHIM_MAX_BYTES = 64 * 1024;
31260
31325
  var WINDOWS_NPM_ENTRYPOINT = [
31261
31326
  "node_modules",
@@ -31264,6 +31329,30 @@ var WINDOWS_NPM_ENTRYPOINT = [
31264
31329
  "dist",
31265
31330
  "index.js"
31266
31331
  ];
31332
+ function isPackagedClaudeBridge(executable) {
31333
+ const normalized = executable.replaceAll("\\", "/");
31334
+ return normalized.endsWith(
31335
+ "/node_modules/@agentclientprotocol/claude-agent-acp/dist/index.js"
31336
+ );
31337
+ }
31338
+ function resolvePackagedClaudeBridge(pathEnv, platform = process.platform) {
31339
+ const pathValue = pathEnv ?? process.env.PATH ?? "";
31340
+ const names = platform === "win32" ? ["claude-agent-acp.cmd"] : ["claude-agent-acp"];
31341
+ for (const dir of pathValue.split(import_node_path6.delimiter)) {
31342
+ if (!dir) continue;
31343
+ for (const name of names) {
31344
+ try {
31345
+ const candidate = resolvedClaudeCandidate((0, import_node_path6.join)(dir, name), platform);
31346
+ if (isPackagedClaudeBridge(candidate)) return candidate;
31347
+ } catch {
31348
+ }
31349
+ }
31350
+ }
31351
+ throw new AcpHostError(
31352
+ "executable_missing",
31353
+ "packaged claude-agent-acp executable not found; install @agentclientprotocol/claude-agent-acp@0.64.2"
31354
+ );
31355
+ }
31267
31356
  function resolveWindowsNpmShim(shim) {
31268
31357
  let source;
31269
31358
  try {
@@ -31336,12 +31425,17 @@ function parseClaudeVersionOutput(stdout) {
31336
31425
  const match = stdout.trim().match(/^(\d+\.\d+\.\d+)$/);
31337
31426
  return match?.[1] ?? null;
31338
31427
  }
31339
- async function assertClaudeMeasuredVersion(executable, options) {
31340
- const expected = options?.expected ?? CLAUDE_ACP_MEASURED_VERSION;
31428
+ 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;
31433
+ }
31434
+ async function readClaudeVersionOutput(executable, options) {
31341
31435
  const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
31342
31436
  const env = options?.env ?? sanitizeChildEnv(process.env);
31343
31437
  const launch = buildClaudeLaunch(executable, ["--version"], options?.platform);
31344
- const stdout = await new Promise((resolve, reject) => {
31438
+ return await new Promise((resolve, reject) => {
31345
31439
  (0, import_node_child_process4.execFile)(
31346
31440
  launch.command,
31347
31441
  launch.args,
@@ -31359,6 +31453,10 @@ async function assertClaudeMeasuredVersion(executable, options) {
31359
31453
  }
31360
31454
  );
31361
31455
  });
31456
+ }
31457
+ async function assertClaudeMeasuredVersion(executable, options) {
31458
+ const expected = options?.expected ?? CLAUDE_ACP_MEASURED_VERSION;
31459
+ const stdout = await readClaudeVersionOutput(executable, options);
31362
31460
  const version3 = parseClaudeVersionOutput(stdout);
31363
31461
  if (!version3) {
31364
31462
  throw new AcpVersionError(
@@ -31366,17 +31464,19 @@ async function assertClaudeMeasuredVersion(executable, options) {
31366
31464
  );
31367
31465
  }
31368
31466
  if (version3 !== expected) {
31369
- throw new AcpVersionError(
31370
- `refusing claude-agent-acp ${version3}; host core is measured for ${expected} only`
31371
- );
31467
+ throw new AcpVersionMismatchError(expected, version3);
31372
31468
  }
31373
31469
  return version3;
31374
31470
  }
31375
31471
  function buildClaudeAcpArgs() {
31376
31472
  return [];
31377
31473
  }
31378
- function buildClaudeChildEnv(parent) {
31379
- return sanitizeChildEnv(parent);
31474
+ function buildClaudeChildEnv(parent, claudeCodeExecutable) {
31475
+ const env = sanitizeChildEnv(parent);
31476
+ if (claudeCodeExecutable) {
31477
+ env.CLAUDE_CODE_EXECUTABLE = claudeCodeExecutable;
31478
+ }
31479
+ return env;
31380
31480
  }
31381
31481
  function waitForChildExit2(child, timeoutMs) {
31382
31482
  if (child.exitCode !== null || child.signalCode !== null) {
@@ -31419,13 +31519,49 @@ async function openClaudeAcpSession(options) {
31419
31519
  );
31420
31520
  }
31421
31521
  const pathEnv = parentEnv.PATH;
31422
- const executable = resolveClaudeExecutable(
31423
- options.executable ?? "claude-agent-acp",
31424
- typeof pathEnv === "string" ? pathEnv : void 0
31425
- );
31426
- const env = buildClaudeChildEnv(parentEnv);
31427
- if (!options.skipVersionCheck) {
31428
- await assertClaudeMeasuredVersion(executable, { env });
31522
+ const resolvedPathEnv = typeof pathEnv === "string" ? pathEnv : void 0;
31523
+ if (options.skipVersionCheck && options.executable) {
31524
+ throw new AcpHostError(
31525
+ "version_check_required",
31526
+ "skipVersionCheck cannot classify an explicit Claude executable"
31527
+ );
31528
+ }
31529
+ const requestedExecutable = options.executable ? resolveClaudeExecutable(options.executable, resolvedPathEnv) : null;
31530
+ const baseEnv = buildClaudeChildEnv(parentEnv);
31531
+ let executable;
31532
+ let env = baseEnv;
31533
+ let claudeCodeExecutable;
31534
+ if (requestedExecutable) {
31535
+ const output = await readClaudeVersionOutput(requestedExecutable, {
31536
+ env: baseEnv
31537
+ });
31538
+ const bridgeVersion = parseClaudeVersionOutput(output);
31539
+ if (bridgeVersion) {
31540
+ if (bridgeVersion !== CLAUDE_ACP_MEASURED_VERSION) {
31541
+ throw new AcpVersionMismatchError(
31542
+ CLAUDE_ACP_MEASURED_VERSION,
31543
+ bridgeVersion
31544
+ );
31545
+ }
31546
+ executable = requestedExecutable;
31547
+ } else if (parseClaudeCodeVersionOutput(output)) {
31548
+ claudeCodeExecutable = requestedExecutable;
31549
+ executable = resolvePackagedClaudeBridge(resolvedPathEnv);
31550
+ env = buildClaudeChildEnv(parentEnv, claudeCodeExecutable);
31551
+ await assertClaudeMeasuredVersion(executable, { env });
31552
+ } else {
31553
+ throw new AcpVersionError(
31554
+ `could not identify Claude executable from: ${output.trim().slice(0, 200)}`
31555
+ );
31556
+ }
31557
+ } else {
31558
+ executable = requestedExecutable ?? resolveClaudeExecutable(
31559
+ "claude-agent-acp",
31560
+ resolvedPathEnv
31561
+ );
31562
+ if (!options.skipVersionCheck) {
31563
+ await assertClaudeMeasuredVersion(executable, { env: baseEnv });
31564
+ }
31429
31565
  }
31430
31566
  if (options.signal?.aborted) {
31431
31567
  throw new AcpHostError(
@@ -31474,25 +31610,33 @@ async function openClaudeAcpSession(options) {
31474
31610
  throw new AcpHostError("spawn_failed", "child missing stdio pipes");
31475
31611
  }
31476
31612
  const stderrTail = attachStderrTailRing(child.stderr);
31477
- if (options.onStderrTail) {
31478
- const deliverTail = options.onStderrTail;
31479
- let tailDelivered = false;
31480
- const publishTail = () => {
31481
- if (tailDelivered) return;
31482
- tailDelivered = true;
31483
- deliverTail(stderrTail.read());
31484
- };
31485
- child.once("exit", publishTail);
31486
- child.once("close", publishTail);
31487
- }
31488
31613
  let sessionRef = null;
31489
31614
  const transport = createBoundTransport({
31490
31615
  readable: child.stdout,
31491
31616
  writable: child.stdin,
31492
31617
  requestTimeoutMs: options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS,
31618
+ readableEndGraceMs: READABLE_END_GRACE_MS,
31493
31619
  getSession: () => sessionRef,
31494
31620
  onChildExit: (handler) => {
31495
- child.on("exit", (code, signal) => handler(code, signal));
31621
+ const observeExit = (code, signal) => {
31622
+ let completed = false;
31623
+ let timer2 = null;
31624
+ const complete = () => {
31625
+ if (completed) return;
31626
+ completed = true;
31627
+ if (timer2) clearTimeout(timer2);
31628
+ child.removeListener("close", complete);
31629
+ options.onStderrTail?.(stderrTail.read());
31630
+ handler(code, signal);
31631
+ };
31632
+ child.once("close", complete);
31633
+ timer2 = setTimeout(complete, STDERR_EXIT_GRACE_MS);
31634
+ };
31635
+ if (child.exitCode !== null || child.signalCode !== null) {
31636
+ observeExit(child.exitCode, child.signalCode);
31637
+ } else {
31638
+ child.once("exit", observeExit);
31639
+ }
31496
31640
  }
31497
31641
  });
31498
31642
  try {
@@ -37850,8 +37994,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
37850
37994
  AGENT_CREDENTIAL_MESSAGE_D088
37851
37995
  ];
37852
37996
  function packageVersion() {
37853
- if ("0.1.26".length > 0) {
37854
- return "0.1.26";
37997
+ if ("0.1.27".length > 0) {
37998
+ return "0.1.27";
37855
37999
  }
37856
38000
  try {
37857
38001
  const value = JSON.parse(
@@ -39666,10 +39810,25 @@ async function runPostSignal(args, kind) {
39666
39810
  validateHumanWorkspace: true
39667
39811
  });
39668
39812
  const toSelector = allowTo ? args.optional("to") : void 0;
39669
- const recipient = toSelector === void 0 ? null : resolveSignalRecipient(
39670
- toSelector,
39671
- await signalDirectory(cloud, credential.selectedWorkspace, credential)
39672
- );
39813
+ let recipient = null;
39814
+ if (toSelector !== void 0) {
39815
+ let directory;
39816
+ try {
39817
+ directory = await signalDirectory(
39818
+ cloud,
39819
+ credential.selectedWorkspace,
39820
+ credential
39821
+ );
39822
+ } catch (error) {
39823
+ if (kind === "ask") {
39824
+ throw new Error(
39825
+ askCreateFailureMessage(credential.selectedWorkspace, error)
39826
+ );
39827
+ }
39828
+ throw error;
39829
+ }
39830
+ recipient = resolveSignalRecipient(toSelector, directory);
39831
+ }
39673
39832
  if (waitSeconds !== void 0 && recipient === null) {
39674
39833
  throw new Error(
39675
39834
  "ask --wait requires --to with a direct member or agent recipient"
@@ -39684,21 +39843,38 @@ async function runPostSignal(args, kind) {
39684
39843
  about: args.optional("about") === void 0 ? null : signalText(args.required("about"), "about"),
39685
39844
  ...untilMs2 === void 0 ? {} : { until_ms: untilMs2 }
39686
39845
  };
39687
- const result = await postSignalCommand(cloud, credential, command2);
39846
+ let result;
39847
+ try {
39848
+ result = await postSignalCommand(cloud, credential, command2);
39849
+ } catch (error) {
39850
+ if (kind === "ask") {
39851
+ throw new Error(
39852
+ askCreateFailureMessage(credential.selectedWorkspace, error)
39853
+ );
39854
+ }
39855
+ throw error;
39856
+ }
39688
39857
  const signal = result.response.signal;
39689
39858
  if (waitSeconds !== void 0) {
39690
39859
  const credentialForRead = signalCredentialOf(credential);
39691
39860
  const deadlineMs = waitDeadlineMs(waitSeconds);
39692
- const waitResult = await pollForSignals({
39693
- deadlineMs,
39694
- read: () => readSignals(cloud, credentialForRead, {
39695
- workspaceId: credential.selectedWorkspace,
39696
- inbox: true,
39697
- in_reply_to: signal.id,
39698
- includeStale: false,
39699
- limit: 1
39700
- }, { deadlineMs })
39701
- });
39861
+ let waitResult;
39862
+ try {
39863
+ waitResult = await pollForSignals({
39864
+ deadlineMs,
39865
+ read: () => readSignals(cloud, credentialForRead, {
39866
+ workspaceId: credential.selectedWorkspace,
39867
+ inbox: true,
39868
+ in_reply_to: signal.id,
39869
+ includeStale: false,
39870
+ limit: 1
39871
+ }, { deadlineMs })
39872
+ });
39873
+ } catch (error) {
39874
+ throw new Error(
39875
+ askReplyReadFailureMessage(credential.selectedWorkspace, error)
39876
+ );
39877
+ }
39702
39878
  const reply = waitResult.signals[0] ?? null;
39703
39879
  if (args.has("json")) {
39704
39880
  printJson(askWaitJsonPayload(signal, reply, waitResult.timedOut));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
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"