commonswarm 0.1.38 → 0.1.39

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 +136 -24
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -13520,7 +13520,7 @@ __export(cli_exports, {
13520
13520
  resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
13521
13521
  });
13522
13522
  module.exports = __toCommonJS(cli_exports);
13523
- var import_node_crypto19 = require("node:crypto");
13523
+ var import_node_crypto20 = require("node:crypto");
13524
13524
  var import_node_fs7 = require("node:fs");
13525
13525
  var import_promises11 = require("node:fs/promises");
13526
13526
  var import_node_path20 = require("node:path");
@@ -33451,6 +33451,7 @@ var FileListenerEffectStore = class {
33451
33451
  };
33452
33452
 
33453
33453
  // src/listener/grok-model.ts
33454
+ var import_node_crypto14 = require("node:crypto");
33454
33455
  var import_promises5 = require("node:fs/promises");
33455
33456
  var import_node_os7 = require("node:os");
33456
33457
  var import_node_path11 = require("node:path");
@@ -33695,7 +33696,7 @@ var GrokListenerModel = class {
33695
33696
  clientName: "cswarm-listener"
33696
33697
  });
33697
33698
  try {
33698
- await handle.session.enablePromptsAfterCanary();
33699
+ await this.enablePromptsAfterGrokCanary(handle);
33699
33700
  this.workerCanary = false;
33700
33701
  this.worker = handle;
33701
33702
  return handle;
@@ -33704,6 +33705,37 @@ var GrokListenerModel = class {
33704
33705
  throw error;
33705
33706
  }
33706
33707
  }
33708
+ /** Force Grok's measured shell permission path without changing worker cwd. */
33709
+ async enablePromptsAfterGrokCanary(handle) {
33710
+ const sentinelPath = (0, import_node_path11.join)(
33711
+ (0, import_node_os7.tmpdir)(),
33712
+ `cswarm-grok-permission-canary-${process.pid}-${(0, import_node_crypto14.randomUUID)()}`
33713
+ );
33714
+ let canaryError;
33715
+ try {
33716
+ await handle.session.enablePromptsAfterCanary({
33717
+ timeoutMs: ACP_CANARY_TIMEOUT_MS,
33718
+ probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else.`,
33719
+ ...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
33720
+ });
33721
+ } catch (error) {
33722
+ canaryError = error;
33723
+ }
33724
+ let sentinelCreated = false;
33725
+ try {
33726
+ await (0, import_promises5.lstat)(sentinelPath);
33727
+ sentinelCreated = true;
33728
+ await (0, import_promises5.unlink)(sentinelPath);
33729
+ } catch (error) {
33730
+ if (error.code !== "ENOENT") throw error;
33731
+ }
33732
+ if (sentinelCreated) {
33733
+ throw new AcpPermissionCanaryError(
33734
+ "Grok bridge wrote the permission canary sentinel; permission denial did not block the write"
33735
+ );
33736
+ }
33737
+ if (canaryError !== void 0) throw canaryError;
33738
+ }
33707
33739
  /** Validate the operator's login artifact without copying or replacing its home. */
33708
33740
  async validateLocalAuth() {
33709
33741
  if (this.options.open) return;
@@ -33760,7 +33792,7 @@ var GrokListenerModel = class {
33760
33792
  };
33761
33793
 
33762
33794
  // src/listener/opencode-model.ts
33763
- var import_node_crypto14 = require("node:crypto");
33795
+ var import_node_crypto15 = require("node:crypto");
33764
33796
  var import_promises6 = require("node:fs/promises");
33765
33797
  var import_node_path12 = require("node:path");
33766
33798
  function asError(error) {
@@ -33786,7 +33818,7 @@ var OpenCodeListenerModel = class {
33786
33818
  prepareWorkerCwd;
33787
33819
  permissionMode;
33788
33820
  pendingOpenWaitMs;
33789
- instanceId = (0, import_node_crypto14.randomUUID)();
33821
+ instanceId = (0, import_node_crypto15.randomUUID)();
33790
33822
  worker = null;
33791
33823
  workerHome = null;
33792
33824
  /** Worker homes retained after failed close or unsettled open. */
@@ -34148,7 +34180,13 @@ var OpenCodeListenerModel = class {
34148
34180
  if (this.closed || this.cancelled || generation !== this.openGeneration) {
34149
34181
  await this.performSingleOwnerCleanup(handle, home, this.instanceId, pending);
34150
34182
  }
34151
- await handle.session.enablePromptsAfterCanary();
34183
+ if (this.options.onCanaryAttempt) {
34184
+ await handle.session.enablePromptsAfterCanary({
34185
+ onAttempt: this.options.onCanaryAttempt
34186
+ });
34187
+ } else {
34188
+ await handle.session.enablePromptsAfterCanary();
34189
+ }
34152
34190
  if (this.closed || this.cancelled || generation !== this.openGeneration) {
34153
34191
  await this.performSingleOwnerCleanup(handle, home, this.instanceId, pending);
34154
34192
  }
@@ -34193,7 +34231,7 @@ var OpenCodeListenerModel = class {
34193
34231
  };
34194
34232
 
34195
34233
  // src/listener/claude-model.ts
34196
- var import_node_crypto15 = require("node:crypto");
34234
+ var import_node_crypto16 = require("node:crypto");
34197
34235
  var import_promises7 = require("node:fs/promises");
34198
34236
  var import_node_os8 = require("node:os");
34199
34237
  var import_node_path13 = require("node:path");
@@ -34336,13 +34374,14 @@ var ClaudeListenerModel = class {
34336
34374
  async enablePromptsAfterClaudeCanary(handle) {
34337
34375
  const sentinelPath = (0, import_node_path13.join)(
34338
34376
  (0, import_node_os8.tmpdir)(),
34339
- `cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto15.randomUUID)()}`
34377
+ `cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
34340
34378
  );
34341
34379
  let sentinelCreated = false;
34342
34380
  try {
34343
34381
  await handle.session.enablePromptsAfterCanary({
34344
34382
  timeoutMs: ACP_CANARY_TIMEOUT_MS,
34345
- probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else.`
34383
+ probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else.`,
34384
+ ...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
34346
34385
  });
34347
34386
  } finally {
34348
34387
  try {
@@ -34362,7 +34401,7 @@ var ClaudeListenerModel = class {
34362
34401
  };
34363
34402
 
34364
34403
  // src/listener/codex-model.ts
34365
- var import_node_crypto16 = require("node:crypto");
34404
+ var import_node_crypto17 = require("node:crypto");
34366
34405
  var import_promises8 = require("node:fs/promises");
34367
34406
  var import_node_os9 = require("node:os");
34368
34407
  var import_node_path14 = require("node:path");
@@ -34505,13 +34544,14 @@ var CodexListenerModel = class {
34505
34544
  async enablePromptsAfterCodexCanary(handle) {
34506
34545
  const sentinelPath = (0, import_node_path14.join)(
34507
34546
  (0, import_node_os9.tmpdir)(),
34508
- `cswarm-codex-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
34547
+ `cswarm-codex-permission-canary-${process.pid}-${(0, import_node_crypto17.randomUUID)()}`
34509
34548
  );
34510
34549
  let sentinelCreated = false;
34511
34550
  try {
34512
34551
  await handle.session.enablePromptsAfterCanary({
34513
34552
  timeoutMs: ACP_CANARY_TIMEOUT_MS,
34514
- probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else.`
34553
+ probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else.`,
34554
+ ...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
34515
34555
  });
34516
34556
  } finally {
34517
34557
  try {
@@ -34531,7 +34571,7 @@ var CodexListenerModel = class {
34531
34571
  };
34532
34572
 
34533
34573
  // src/listener/runtime.ts
34534
- var import_node_crypto17 = require("node:crypto");
34574
+ var import_node_crypto18 = require("node:crypto");
34535
34575
 
34536
34576
  // src/cloud/delivery.ts
34537
34577
  var UUID_RE12 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -35487,7 +35527,7 @@ function sameEffectSignal(record, signal) {
35487
35527
  return record.signalId === signal.id.toLowerCase() && record.signalKind === signal.kind && record.askBody === signal.body && record.askUntil === signal.until && record.senderOwnerRelation === (signal.sender_owner_relation ?? "unknown");
35488
35528
  }
35489
35529
  function immutableSignalFingerprint(signalId, signalKind2, body, until, senderOwnerRelation) {
35490
- return (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify([
35530
+ return (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify([
35491
35531
  signalId,
35492
35532
  signalKind2,
35493
35533
  body,
@@ -36477,6 +36517,7 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
36477
36517
  "stoppedAt",
36478
36518
  "lastSignalId",
36479
36519
  "lastErrorCode",
36520
+ "lastErrorDetail",
36480
36521
  "providerVersion",
36481
36522
  "providerLastMeasuredVersion",
36482
36523
  "lastWorkerStderrTail",
@@ -36538,7 +36579,7 @@ function parseStatus(raw) {
36538
36579
  const nullableUuid2 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE15.test(candidate);
36539
36580
  const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
36540
36581
  const nullableTimestamp2 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
36541
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE15.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE15.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE15.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_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 || nullableTimestamp2(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp2(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp2(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)) {
36582
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE15.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE15.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE15.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.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.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 || nullableTimestamp2(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp2(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp2(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)) {
36542
36583
  throw new Error("stored listener status is malformed");
36543
36584
  }
36544
36585
  const routeMode = row.routeMode ?? "worker";
@@ -36554,6 +36595,7 @@ function parseStatus(raw) {
36554
36595
  lastTerminalDeliveryFailureAt: row.lastTerminalDeliveryFailureAt ?? null,
36555
36596
  lastClaimAt: row.lastClaimAt ?? null,
36556
36597
  lastAckAt: row.lastAckAt ?? null,
36598
+ lastErrorDetail: row.lastErrorDetail ?? null,
36557
36599
  lastWorkerStderrTail: row.lastWorkerStderrTail ?? null,
36558
36600
  providerVersion: row.providerVersion ?? null,
36559
36601
  providerLastMeasuredVersion: row.providerLastMeasuredVersion ?? null,
@@ -36571,6 +36613,9 @@ async function writeListenerStatus(paths, status) {
36571
36613
  throw new Error("listener status is missing delivery metadata fields");
36572
36614
  }
36573
36615
  }
36616
+ if (!("lastErrorDetail" in parsed)) {
36617
+ throw new Error("listener status is missing local error detail metadata");
36618
+ }
36574
36619
  parseStatus(serialized);
36575
36620
  await writeSecureJsonFile(paths.statusPath, serialized);
36576
36621
  }
@@ -36588,6 +36633,9 @@ async function appendListenerEvent(paths, event) {
36588
36633
  "status",
36589
36634
  "failure_code",
36590
36635
  "attempt",
36636
+ "total",
36637
+ "passed",
36638
+ "reason",
36591
36639
  "delay_ms",
36592
36640
  "index",
36593
36641
  "delivery_mode",
@@ -36624,6 +36672,15 @@ async function appendListenerEvent(paths, event) {
36624
36672
  if (!allowed.has(key2)) {
36625
36673
  throw new Error(`listener event field is not allowed: ${key2}`);
36626
36674
  }
36675
+ if ((key2 === "attempt" || key2 === "total") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
36676
+ throw new Error("listener event attempt count is not allowed");
36677
+ }
36678
+ if (key2 === "passed" && typeof value !== "boolean") {
36679
+ throw new Error("listener event canary result is not allowed");
36680
+ }
36681
+ if (key2 === "reason" && !(value === null || typeof value === "string" && value.length > 0)) {
36682
+ throw new Error("listener event canary reason is not allowed");
36683
+ }
36627
36684
  if (key2 === "delivery_mode" && !(value === null || typeof value === "string" && deliveryModes.has(value))) {
36628
36685
  throw new Error("listener event delivery mode is not allowed");
36629
36686
  }
@@ -36658,6 +36715,9 @@ async function appendListenerEvent(paths, event) {
36658
36715
  throw new Error("listener event contains unsafe text");
36659
36716
  }
36660
36717
  }
36718
+ 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"))) {
36719
+ throw new Error("listener canary attempt event is incomplete");
36720
+ }
36661
36721
  await ensureSecureStateDirectory(paths.instanceDirectory);
36662
36722
  const serialized = `${JSON.stringify(event)}
36663
36723
  `;
@@ -36887,7 +36947,7 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
36887
36947
  }
36888
36948
 
36889
36949
  // src/listener/supervisor.ts
36890
- var import_node_crypto18 = require("node:crypto");
36950
+ var import_node_crypto19 = require("node:crypto");
36891
36951
  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;
36892
36952
  var LISTENER_RESTART_MAX_ATTEMPTS = 5;
36893
36953
  var LISTENER_RESTART_INITIAL_MS = 1e3;
@@ -36931,6 +36991,17 @@ function safeErrorCode(error) {
36931
36991
  const name = error.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "_");
36932
36992
  return name.slice(0, 96) || "listener_error";
36933
36993
  }
36994
+ function localDiagnostic(message, maxChars) {
36995
+ const redacted = message.replace(
36996
+ /swm_(?:agt|inv|cap)_[^\s"'\\]*/gi,
36997
+ "[redacted]"
36998
+ ).trim();
36999
+ if (redacted.length === 0) return null;
37000
+ return redacted.slice(0, maxChars);
37001
+ }
37002
+ function safeErrorDetail(error) {
37003
+ return localDiagnostic(error.message, 2048);
37004
+ }
36934
37005
  var TAIL_SERIALIZED_BUDGET_BYTES = 3e3;
36935
37006
  function fitWorkerStderrTailForLog(tail) {
36936
37007
  let fitted = tail.trim();
@@ -36952,7 +37023,7 @@ async function runListenerSupervisor(options) {
36952
37023
  const now = options.now ?? Date.now;
36953
37024
  const startedAt = iso2(now);
36954
37025
  const controller = new AbortController();
36955
- const proposedInstanceId = (0, import_node_crypto18.randomUUID)();
37026
+ const proposedInstanceId = (0, import_node_crypto19.randomUUID)();
36956
37027
  let status = {
36957
37028
  version: 1,
36958
37029
  instanceId: proposedInstanceId,
@@ -36969,6 +37040,7 @@ async function runListenerSupervisor(options) {
36969
37040
  stoppedAt: null,
36970
37041
  lastSignalId: null,
36971
37042
  lastErrorCode: null,
37043
+ lastErrorDetail: null,
36972
37044
  providerVersion: null,
36973
37045
  providerLastMeasuredVersion: null,
36974
37046
  lastWorkerStderrTail: null,
@@ -37044,6 +37116,7 @@ async function runListenerSupervisor(options) {
37044
37116
  transition("ready", {
37045
37117
  readyAt: event.ts,
37046
37118
  lastErrorCode: null,
37119
+ lastErrorDetail: null,
37047
37120
  lastWorkerStderrTail: null,
37048
37121
  providerVersion: versionNotice?.runningVersion ?? null,
37049
37122
  providerLastMeasuredVersion: versionNotice?.lastMeasuredVersion ?? null
@@ -37051,6 +37124,17 @@ async function runListenerSupervisor(options) {
37051
37124
  log({ ts: event.ts, event: "listener_ready" });
37052
37125
  return;
37053
37126
  }
37127
+ if (event.type === "canary_attempt") {
37128
+ log({
37129
+ ts: event.ts,
37130
+ event: "listener_canary_attempt",
37131
+ attempt: event.attempt,
37132
+ total: event.total,
37133
+ passed: event.passed,
37134
+ reason: event.reason === null ? null : localDiagnostic(event.reason, 128)
37135
+ });
37136
+ return;
37137
+ }
37054
37138
  if (event.type === "effect") {
37055
37139
  status = {
37056
37140
  ...status,
@@ -37244,6 +37328,7 @@ async function runListenerSupervisor(options) {
37244
37328
  transition("starting", {
37245
37329
  readyAt: null,
37246
37330
  lastErrorCode: restartCode,
37331
+ lastErrorDetail: safeErrorDetail(stop.error),
37247
37332
  lastWorkerStderrTail: restartStderrTail,
37248
37333
  providerVersion: null,
37249
37334
  providerLastMeasuredVersion: null
@@ -37259,6 +37344,7 @@ async function runListenerSupervisor(options) {
37259
37344
  transition("stopped", {
37260
37345
  stoppedAt,
37261
37346
  lastErrorCode: null,
37347
+ lastErrorDetail: null,
37262
37348
  lastWorkerStderrTail: null
37263
37349
  });
37264
37350
  log({ ts: stoppedAt, event: "listener_stopped" });
@@ -37268,6 +37354,7 @@ async function runListenerSupervisor(options) {
37268
37354
  transition("failed", {
37269
37355
  stoppedAt,
37270
37356
  lastErrorCode: code,
37357
+ lastErrorDetail: safeErrorDetail(stop.error),
37271
37358
  lastWorkerStderrTail: failedStderrTail
37272
37359
  });
37273
37360
  log({
@@ -37289,6 +37376,9 @@ async function runListenerSupervisor(options) {
37289
37376
  transition("failed", {
37290
37377
  stoppedAt,
37291
37378
  lastErrorCode: code,
37379
+ lastErrorDetail: safeErrorDetail(
37380
+ error instanceof Error ? error : new Error(String(error))
37381
+ ),
37292
37382
  lastWorkerStderrTail: failedStderrTail
37293
37383
  });
37294
37384
  log({
@@ -38849,8 +38939,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
38849
38939
  AGENT_CREDENTIAL_MESSAGE_D088
38850
38940
  ];
38851
38941
  function packageVersion() {
38852
- if ("0.1.38".length > 0) {
38853
- return "0.1.38";
38942
+ if ("0.1.39".length > 0) {
38943
+ return "0.1.39";
38854
38944
  }
38855
38945
  try {
38856
38946
  const value = JSON.parse(
@@ -39449,7 +39539,7 @@ async function runNew(args) {
39449
39539
  assertWorkspaceName(name);
39450
39540
  const cloud = await target(args);
39451
39541
  const human = await humanCredential(args, cloud);
39452
- const proposedId = (0, import_node_crypto19.randomUUID)();
39542
+ const proposedId = (0, import_node_crypto20.randomUUID)();
39453
39543
  let result;
39454
39544
  try {
39455
39545
  result = await new ThinCommandClient(cloud).sendConnect({
@@ -41637,6 +41727,11 @@ function renderListenerStatus(status) {
41637
41727
  status.lastSignalId ? `Last handled signal: ${status.lastSignalId}.` : "No signal has been handled yet.",
41638
41728
  status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener error is recorded."
41639
41729
  ];
41730
+ if (status.lastErrorDetail) {
41731
+ const [first, ...rest] = status.lastErrorDetail.split("\n");
41732
+ lines.push(`Last error detail (local only): ${first}`);
41733
+ for (const line of rest) lines.push(` ${line}`);
41734
+ }
41640
41735
  if (status.lastWorkerStderrTail) {
41641
41736
  const tailLines = status.lastWorkerStderrTail.split("\n").filter((line) => line.trim().length > 0);
41642
41737
  lines.push("Worker stderr (local log only):");
@@ -41766,6 +41861,9 @@ function listenerFailureMessage(code, provider) {
41766
41861
  if (provider === "codex") {
41767
41862
  return "the Codex bridge did not complete the read-only ACP permission canary; no workspace signal prompt was delivered. Confirm ChatGPT/Codex sign-in, then retry";
41768
41863
  }
41864
+ if (provider === "grok") {
41865
+ 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";
41866
+ }
41769
41867
  return "the host did not prove that CommonSwarm controls ACP tool permissions; no model prompt was delivered";
41770
41868
  }
41771
41869
  if (code === "process_exit") {
@@ -41918,13 +42016,14 @@ async function runConfiguredListener(options) {
41918
42016
  lastWorkerStderrTail = tail.length > 0 ? tail : null;
41919
42017
  };
41920
42018
  };
41921
- const newModel = () => {
42019
+ const newModel = (onCanaryAttempt) => {
41922
42020
  providerVersionNotice = null;
41923
42021
  return options.provider === "opencode" ? new OpenCodeListenerModel({
41924
42022
  cwd: options.cwd,
41925
42023
  permissionMode: options.permissionMode,
41926
42024
  promptTimeoutMs: resolveTurnBudgetMs,
41927
42025
  onWorkerStderrTail: newWorkerStderrTailSink(),
42026
+ onCanaryAttempt,
41928
42027
  onVersionNotice,
41929
42028
  ...options.model ? { model: options.model } : {},
41930
42029
  ...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
@@ -41933,6 +42032,7 @@ async function runConfiguredListener(options) {
41933
42032
  permissionMode: options.permissionMode,
41934
42033
  promptTimeoutMs: resolveTurnBudgetMs,
41935
42034
  onWorkerStderrTail: newWorkerStderrTailSink(),
42035
+ onCanaryAttempt,
41936
42036
  onVersionNotice,
41937
42037
  ...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
41938
42038
  }) : options.provider === "codex" ? new CodexListenerModel({
@@ -41940,6 +42040,7 @@ async function runConfiguredListener(options) {
41940
42040
  permissionMode: options.permissionMode,
41941
42041
  promptTimeoutMs: resolveTurnBudgetMs,
41942
42042
  onWorkerStderrTail: newWorkerStderrTailSink(),
42043
+ onCanaryAttempt,
41943
42044
  onVersionNotice,
41944
42045
  ...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
41945
42046
  }) : new GrokListenerModel({
@@ -41947,6 +42048,7 @@ async function runConfiguredListener(options) {
41947
42048
  permissionMode: options.permissionMode,
41948
42049
  promptTimeoutMs: resolveTurnBudgetMs,
41949
42050
  onWorkerStderrTail: newWorkerStderrTailSink(),
42051
+ onCanaryAttempt,
41950
42052
  onVersionNotice,
41951
42053
  ...options.model ? { model: options.model } : {},
41952
42054
  ...options.effort ? { effort: options.effort } : {},
@@ -42000,13 +42102,23 @@ async function runConfiguredListener(options) {
42000
42102
  "listener delivery journal was not selected for this instance"
42001
42103
  );
42002
42104
  }
42105
+ const onCanaryAttempt = (attempt, total, result) => {
42106
+ onEvent({
42107
+ type: "canary_attempt",
42108
+ attempt,
42109
+ total,
42110
+ passed: result.passed,
42111
+ reason: result.reason ?? null,
42112
+ ts: (/* @__PURE__ */ new Date()).toISOString()
42113
+ });
42114
+ };
42003
42115
  return await runListenerRuntime({
42004
42116
  target: options.cloud,
42005
42117
  workspaceId: options.workspaceId,
42006
42118
  principalId: options.principalId,
42007
42119
  credentialSession,
42008
42120
  store: effectStore,
42009
- model: newModel(),
42121
+ model: newModel(onCanaryAttempt),
42010
42122
  signal,
42011
42123
  onEvent,
42012
42124
  declareModel: listenerModelLabel(options.provider),
@@ -42541,8 +42653,8 @@ async function runFilePut(args) {
42541
42653
  workspaceId: context.selected.selectedWorkspace,
42542
42654
  credential: context.selected.bearer
42543
42655
  };
42544
- const fileId = (0, import_node_crypto19.randomUUID)();
42545
- const versionId = (0, import_node_crypto19.randomUUID)();
42656
+ const fileId = (0, import_node_crypto20.randomUUID)();
42657
+ const versionId = (0, import_node_crypto20.randomUUID)();
42546
42658
  const createCommandId = newCommandId();
42547
42659
  const commitCommandId = newCommandId();
42548
42660
  const created = await onceRetried(
@@ -42779,7 +42891,7 @@ async function runDogfood(args) {
42779
42891
  const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
42780
42892
  const client = new ThinCommandClient(cloud);
42781
42893
  const route = stream(args);
42782
- const taskId = args.optional("task-id") ?? (0, import_node_crypto19.randomUUID)();
42894
+ const taskId = args.optional("task-id") ?? (0, import_node_crypto20.randomUUID)();
42783
42895
  const ttl = Number(args.optional("ttl-ms") ?? "3600000");
42784
42896
  if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
42785
42897
  throw new Error("--ttl-ms must be an integer in 1..14400000");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
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"