commonswarm 0.1.52 → 0.1.54

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 +492 -224
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -13519,7 +13519,8 @@ __export(cli_exports, {
13519
13519
  replyRefusalHint: () => replyRefusalHint,
13520
13520
  resolveDetachedClaudeExecutable: () => resolveDetachedClaudeExecutable,
13521
13521
  resolveDetachedCodexExecutable: () => resolveDetachedCodexExecutable,
13522
- resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
13522
+ resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer,
13523
+ usage: () => usage
13523
13524
  });
13524
13525
  module.exports = __toCommonJS(cli_exports);
13525
13526
  var import_node_crypto22 = require("node:crypto");
@@ -13530,6 +13531,188 @@ var import_node_os10 = require("node:os");
13530
13531
  var import_node_path21 = require("node:path");
13531
13532
  var import_promises13 = require("node:readline/promises");
13532
13533
 
13534
+ // src/protocol/events.ts
13535
+ var SCHEMA_VERSION = 1;
13536
+ var EVENT_TYPES = [
13537
+ "TaskCreated",
13538
+ "LeaseAcquired",
13539
+ "LeaseRenewed",
13540
+ "LeaseHandedOff",
13541
+ "LeaseTakenOver",
13542
+ "TaskSubmitted",
13543
+ "TaskClosed",
13544
+ "TaskReopened",
13545
+ "CommandRejected"
13546
+ ];
13547
+
13548
+ // src/protocol/reducer.ts
13549
+ function req(payload, keys, type, seq) {
13550
+ if (!payload || typeof payload !== "object") throw new StreamIntegrityError(`event "${type}" at seq ${seq} has a non-object payload`);
13551
+ for (const k of keys) {
13552
+ if (payload[k] === void 0) {
13553
+ throw new StreamIntegrityError(`event "${type}" at seq ${seq} is missing payload field "${String(k)}"`);
13554
+ }
13555
+ }
13556
+ return payload;
13557
+ }
13558
+ var UnknownEventTypeError = class extends Error {
13559
+ constructor(type, seq) {
13560
+ super(`unknown authoritative event type "${type}" at seq ${seq}; halting`);
13561
+ this.type = type;
13562
+ this.seq = seq;
13563
+ this.name = "UnknownEventTypeError";
13564
+ }
13565
+ type;
13566
+ seq;
13567
+ };
13568
+ var StreamIntegrityError = class extends Error {
13569
+ constructor(message) {
13570
+ super(message);
13571
+ this.name = "StreamIntegrityError";
13572
+ }
13573
+ };
13574
+ function reduceTask(prev, env) {
13575
+ if (!EVENT_TYPES.includes(env.type)) {
13576
+ throw new UnknownEventTypeError(env.type, env.seq);
13577
+ }
13578
+ if (env.schema_version !== SCHEMA_VERSION) {
13579
+ throw new StreamIntegrityError(`event "${env.type}" at seq ${env.seq} is schema v${env.schema_version}, expected v${SCHEMA_VERSION} (upcast before reduce)`);
13580
+ }
13581
+ if (env.type === "CommandRejected") {
13582
+ if (!prev) throw new StreamIntegrityError(`CommandRejected before task exists (seq ${env.seq})`);
13583
+ req(env.payload, ["task_id", "command", "reason", "detail"], env.type, env.seq);
13584
+ return prev;
13585
+ }
13586
+ if (env.type === "TaskCreated") {
13587
+ if (prev) throw new StreamIntegrityError(`TaskCreated for an already-existing task (seq ${env.seq})`);
13588
+ const p = req(env.payload, ["task_id", "slug"], env.type, env.seq);
13589
+ return {
13590
+ task_id: p.task_id,
13591
+ slug: p.slug,
13592
+ lifecycle: "open",
13593
+ version: 1,
13594
+ epoch: 0,
13595
+ owner: null,
13596
+ lease_expiry: null,
13597
+ submission: null,
13598
+ closed_disposition: null
13599
+ };
13600
+ }
13601
+ if (!prev) throw new StreamIntegrityError(`event "${env.type}" before TaskCreated (seq ${env.seq})`);
13602
+ const s = prev;
13603
+ function assertEpochIncrease(ep) {
13604
+ if (typeof ep !== "number" || ep <= s.epoch) {
13605
+ throw new StreamIntegrityError(`${env.type} at seq ${env.seq} has non-increasing epoch ${ep} (current ${s.epoch})`);
13606
+ }
13607
+ }
13608
+ const leaseLifecycle = s.submission ? "awaiting_review" : "active";
13609
+ switch (env.type) {
13610
+ case "LeaseAcquired": {
13611
+ const p = req(env.payload, ["task_id", "epoch", "owner", "lease_expiry"], env.type, env.seq);
13612
+ assertEpochIncrease(p.epoch);
13613
+ return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.owner, lease_expiry: p.lease_expiry };
13614
+ }
13615
+ case "LeaseRenewed": {
13616
+ const p = req(env.payload, ["task_id", "epoch", "lease_expiry"], env.type, env.seq);
13617
+ if (p.epoch !== s.epoch) throw new StreamIntegrityError(`LeaseRenewed at seq ${env.seq} epoch ${p.epoch} != current ${s.epoch}`);
13618
+ return { ...s, lease_expiry: p.lease_expiry };
13619
+ }
13620
+ case "LeaseHandedOff": {
13621
+ const p = req(env.payload, ["task_id", "epoch", "from_owner", "to_owner", "lease_expiry"], env.type, env.seq);
13622
+ assertEpochIncrease(p.epoch);
13623
+ return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.to_owner, lease_expiry: p.lease_expiry };
13624
+ }
13625
+ case "LeaseTakenOver": {
13626
+ const p = req(env.payload, ["task_id", "epoch", "owner", "lease_expiry", "grant_id"], env.type, env.seq);
13627
+ assertEpochIncrease(p.epoch);
13628
+ return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.owner, lease_expiry: p.lease_expiry };
13629
+ }
13630
+ case "TaskSubmitted": {
13631
+ const p = req(env.payload, ["task_id", "epoch", "branch", "head_sha", "evidence_set"], env.type, env.seq);
13632
+ return {
13633
+ ...s,
13634
+ lifecycle: "awaiting_review",
13635
+ submission: { epoch: p.epoch, branch: p.branch, head_sha: p.head_sha, evidence_set: [...p.evidence_set] }
13636
+ };
13637
+ }
13638
+ case "TaskClosed": {
13639
+ const p = req(env.payload, ["task_id", "epoch", "disposition", "grant_id"], env.type, env.seq);
13640
+ return { ...s, lifecycle: "done", closed_disposition: p.disposition };
13641
+ }
13642
+ case "TaskReopened": {
13643
+ const p = req(env.payload, ["task_id", "version"], env.type, env.seq);
13644
+ if (p.version <= s.version) throw new StreamIntegrityError(`TaskReopened at seq ${env.seq} version ${p.version} not > current ${s.version}`);
13645
+ return { ...s, lifecycle: "reopened", version: p.version, submission: null, owner: null, lease_expiry: null };
13646
+ }
13647
+ default: {
13648
+ throw new UnknownEventTypeError(env.type, env.seq);
13649
+ }
13650
+ }
13651
+ }
13652
+
13653
+ // src/protocol/idempotency.ts
13654
+ function canonicalJson(value) {
13655
+ return JSON.stringify(sortValue(value));
13656
+ }
13657
+ function sortValue(v) {
13658
+ if (Array.isArray(v)) return v.map(sortValue);
13659
+ if (v && typeof v === "object") {
13660
+ const out = {};
13661
+ for (const k of Object.keys(v).sort()) {
13662
+ out[k] = sortValue(v[k]);
13663
+ }
13664
+ return out;
13665
+ }
13666
+ return v;
13667
+ }
13668
+
13669
+ // src/protocol/upcasters.ts
13670
+ var registry = /* @__PURE__ */ new Map();
13671
+ function key(type, fromVersion) {
13672
+ return `${type}:${fromVersion}`;
13673
+ }
13674
+ function registerUpcaster(type, fromVersion, fn) {
13675
+ registry.set(key(type, fromVersion), fn);
13676
+ }
13677
+ var UpcastError = class extends Error {
13678
+ constructor(message) {
13679
+ super(message);
13680
+ this.name = "UpcastError";
13681
+ }
13682
+ };
13683
+ function upcastPayload(type, fromVersion, payload) {
13684
+ let v = fromVersion;
13685
+ let p = payload;
13686
+ if (v > SCHEMA_VERSION) {
13687
+ throw new UpcastError(`event "${type}" is schema v${v}, newer than supported v${SCHEMA_VERSION}; halting`);
13688
+ }
13689
+ while (v < SCHEMA_VERSION) {
13690
+ const fn = registry.get(key(type, v));
13691
+ if (!fn) throw new UpcastError(`no upcaster for "${type}" v${v}\u2192v${v + 1}`);
13692
+ p = fn(p);
13693
+ v += 1;
13694
+ }
13695
+ return { payload: p, schema_version: SCHEMA_VERSION };
13696
+ }
13697
+ function upcastEnvelope(raw) {
13698
+ const { payload, schema_version } = upcastPayload(raw.type, raw.schema_version, raw.payload);
13699
+ return { ...raw, payload, schema_version };
13700
+ }
13701
+ registerUpcaster("TaskCreated", 0, (p) => ({ task_id: p.id, slug: p.name }));
13702
+
13703
+ // src/protocol/workspace-commands.ts
13704
+ var INVITATION_MAX_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
13705
+ var AGENT_TOKEN_DEFAULT_TTL_MS = 60 * 60 * 1e3;
13706
+ var AGENT_TOKEN_MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
13707
+ var RENEWAL_HORIZON_DEFAULT_MS = 30 * 24 * 60 * 60 * 1e3;
13708
+ var RENEWAL_HORIZON_MAX_MS = 90 * 24 * 60 * 60 * 1e3;
13709
+
13710
+ // src/protocol/brain-version-window.ts
13711
+ var BRAIN_FILE_PREFIX = "brain--";
13712
+ var BRAIN_FILE_SUFFIX = ".md";
13713
+ var BRAIN_TOPIC_MAX_LENGTH = 255 - BRAIN_FILE_PREFIX.length - BRAIN_FILE_SUFFIX.length;
13714
+ var FILE_VERSION_PRECONDITION_FAILED = "file_version_precondition_failed";
13715
+
13533
13716
  // src/cloud/auth.ts
13534
13717
  var import_node_crypto2 = require("node:crypto");
13535
13718
  var import_node_http = require("node:http");
@@ -21959,189 +22142,6 @@ async function logout(target2, store2, scope = "local", options = {}) {
21959
22142
 
21960
22143
  // src/cloud/command-client.ts
21961
22144
  var import_node_crypto3 = require("node:crypto");
21962
-
21963
- // src/protocol/events.ts
21964
- var SCHEMA_VERSION = 1;
21965
- var EVENT_TYPES = [
21966
- "TaskCreated",
21967
- "LeaseAcquired",
21968
- "LeaseRenewed",
21969
- "LeaseHandedOff",
21970
- "LeaseTakenOver",
21971
- "TaskSubmitted",
21972
- "TaskClosed",
21973
- "TaskReopened",
21974
- "CommandRejected"
21975
- ];
21976
-
21977
- // src/protocol/reducer.ts
21978
- function req(payload, keys, type, seq) {
21979
- if (!payload || typeof payload !== "object") throw new StreamIntegrityError(`event "${type}" at seq ${seq} has a non-object payload`);
21980
- for (const k of keys) {
21981
- if (payload[k] === void 0) {
21982
- throw new StreamIntegrityError(`event "${type}" at seq ${seq} is missing payload field "${String(k)}"`);
21983
- }
21984
- }
21985
- return payload;
21986
- }
21987
- var UnknownEventTypeError = class extends Error {
21988
- constructor(type, seq) {
21989
- super(`unknown authoritative event type "${type}" at seq ${seq}; halting`);
21990
- this.type = type;
21991
- this.seq = seq;
21992
- this.name = "UnknownEventTypeError";
21993
- }
21994
- type;
21995
- seq;
21996
- };
21997
- var StreamIntegrityError = class extends Error {
21998
- constructor(message) {
21999
- super(message);
22000
- this.name = "StreamIntegrityError";
22001
- }
22002
- };
22003
- function reduceTask(prev, env) {
22004
- if (!EVENT_TYPES.includes(env.type)) {
22005
- throw new UnknownEventTypeError(env.type, env.seq);
22006
- }
22007
- if (env.schema_version !== SCHEMA_VERSION) {
22008
- throw new StreamIntegrityError(`event "${env.type}" at seq ${env.seq} is schema v${env.schema_version}, expected v${SCHEMA_VERSION} (upcast before reduce)`);
22009
- }
22010
- if (env.type === "CommandRejected") {
22011
- if (!prev) throw new StreamIntegrityError(`CommandRejected before task exists (seq ${env.seq})`);
22012
- req(env.payload, ["task_id", "command", "reason", "detail"], env.type, env.seq);
22013
- return prev;
22014
- }
22015
- if (env.type === "TaskCreated") {
22016
- if (prev) throw new StreamIntegrityError(`TaskCreated for an already-existing task (seq ${env.seq})`);
22017
- const p = req(env.payload, ["task_id", "slug"], env.type, env.seq);
22018
- return {
22019
- task_id: p.task_id,
22020
- slug: p.slug,
22021
- lifecycle: "open",
22022
- version: 1,
22023
- epoch: 0,
22024
- owner: null,
22025
- lease_expiry: null,
22026
- submission: null,
22027
- closed_disposition: null
22028
- };
22029
- }
22030
- if (!prev) throw new StreamIntegrityError(`event "${env.type}" before TaskCreated (seq ${env.seq})`);
22031
- const s = prev;
22032
- function assertEpochIncrease(ep) {
22033
- if (typeof ep !== "number" || ep <= s.epoch) {
22034
- throw new StreamIntegrityError(`${env.type} at seq ${env.seq} has non-increasing epoch ${ep} (current ${s.epoch})`);
22035
- }
22036
- }
22037
- const leaseLifecycle = s.submission ? "awaiting_review" : "active";
22038
- switch (env.type) {
22039
- case "LeaseAcquired": {
22040
- const p = req(env.payload, ["task_id", "epoch", "owner", "lease_expiry"], env.type, env.seq);
22041
- assertEpochIncrease(p.epoch);
22042
- return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.owner, lease_expiry: p.lease_expiry };
22043
- }
22044
- case "LeaseRenewed": {
22045
- const p = req(env.payload, ["task_id", "epoch", "lease_expiry"], env.type, env.seq);
22046
- if (p.epoch !== s.epoch) throw new StreamIntegrityError(`LeaseRenewed at seq ${env.seq} epoch ${p.epoch} != current ${s.epoch}`);
22047
- return { ...s, lease_expiry: p.lease_expiry };
22048
- }
22049
- case "LeaseHandedOff": {
22050
- const p = req(env.payload, ["task_id", "epoch", "from_owner", "to_owner", "lease_expiry"], env.type, env.seq);
22051
- assertEpochIncrease(p.epoch);
22052
- return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.to_owner, lease_expiry: p.lease_expiry };
22053
- }
22054
- case "LeaseTakenOver": {
22055
- const p = req(env.payload, ["task_id", "epoch", "owner", "lease_expiry", "grant_id"], env.type, env.seq);
22056
- assertEpochIncrease(p.epoch);
22057
- return { ...s, lifecycle: leaseLifecycle, epoch: p.epoch, owner: p.owner, lease_expiry: p.lease_expiry };
22058
- }
22059
- case "TaskSubmitted": {
22060
- const p = req(env.payload, ["task_id", "epoch", "branch", "head_sha", "evidence_set"], env.type, env.seq);
22061
- return {
22062
- ...s,
22063
- lifecycle: "awaiting_review",
22064
- submission: { epoch: p.epoch, branch: p.branch, head_sha: p.head_sha, evidence_set: [...p.evidence_set] }
22065
- };
22066
- }
22067
- case "TaskClosed": {
22068
- const p = req(env.payload, ["task_id", "epoch", "disposition", "grant_id"], env.type, env.seq);
22069
- return { ...s, lifecycle: "done", closed_disposition: p.disposition };
22070
- }
22071
- case "TaskReopened": {
22072
- const p = req(env.payload, ["task_id", "version"], env.type, env.seq);
22073
- if (p.version <= s.version) throw new StreamIntegrityError(`TaskReopened at seq ${env.seq} version ${p.version} not > current ${s.version}`);
22074
- return { ...s, lifecycle: "reopened", version: p.version, submission: null, owner: null, lease_expiry: null };
22075
- }
22076
- default: {
22077
- throw new UnknownEventTypeError(env.type, env.seq);
22078
- }
22079
- }
22080
- }
22081
-
22082
- // src/protocol/idempotency.ts
22083
- function canonicalJson(value) {
22084
- return JSON.stringify(sortValue(value));
22085
- }
22086
- function sortValue(v) {
22087
- if (Array.isArray(v)) return v.map(sortValue);
22088
- if (v && typeof v === "object") {
22089
- const out = {};
22090
- for (const k of Object.keys(v).sort()) {
22091
- out[k] = sortValue(v[k]);
22092
- }
22093
- return out;
22094
- }
22095
- return v;
22096
- }
22097
-
22098
- // src/protocol/upcasters.ts
22099
- var registry = /* @__PURE__ */ new Map();
22100
- function key(type, fromVersion) {
22101
- return `${type}:${fromVersion}`;
22102
- }
22103
- function registerUpcaster(type, fromVersion, fn) {
22104
- registry.set(key(type, fromVersion), fn);
22105
- }
22106
- var UpcastError = class extends Error {
22107
- constructor(message) {
22108
- super(message);
22109
- this.name = "UpcastError";
22110
- }
22111
- };
22112
- function upcastPayload(type, fromVersion, payload) {
22113
- let v = fromVersion;
22114
- let p = payload;
22115
- if (v > SCHEMA_VERSION) {
22116
- throw new UpcastError(`event "${type}" is schema v${v}, newer than supported v${SCHEMA_VERSION}; halting`);
22117
- }
22118
- while (v < SCHEMA_VERSION) {
22119
- const fn = registry.get(key(type, v));
22120
- if (!fn) throw new UpcastError(`no upcaster for "${type}" v${v}\u2192v${v + 1}`);
22121
- p = fn(p);
22122
- v += 1;
22123
- }
22124
- return { payload: p, schema_version: SCHEMA_VERSION };
22125
- }
22126
- function upcastEnvelope(raw) {
22127
- const { payload, schema_version } = upcastPayload(raw.type, raw.schema_version, raw.payload);
22128
- return { ...raw, payload, schema_version };
22129
- }
22130
- registerUpcaster("TaskCreated", 0, (p) => ({ task_id: p.id, slug: p.name }));
22131
-
22132
- // src/protocol/workspace-commands.ts
22133
- var INVITATION_MAX_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
22134
- var AGENT_TOKEN_DEFAULT_TTL_MS = 60 * 60 * 1e3;
22135
- var AGENT_TOKEN_MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
22136
- var RENEWAL_HORIZON_DEFAULT_MS = 30 * 24 * 60 * 60 * 1e3;
22137
- var RENEWAL_HORIZON_MAX_MS = 90 * 24 * 60 * 60 * 1e3;
22138
-
22139
- // src/protocol/brain-version-window.ts
22140
- var BRAIN_FILE_PREFIX = "brain--";
22141
- var BRAIN_FILE_SUFFIX = ".md";
22142
- var BRAIN_TOPIC_MAX_LENGTH = 255 - BRAIN_FILE_PREFIX.length - BRAIN_FILE_SUFFIX.length;
22143
-
22144
- // src/cloud/command-client.ts
22145
22145
  var AGENT_TOKEN_RE = /^swm_agt_[A-Za-z0-9_-]{43}$/;
22146
22146
  var INVITATION_TOKEN_RE = /^swm_inv_[A-Za-z0-9_-]{43}$/;
22147
22147
  var CAPABILITY_TOKEN_RE = /^swm_cap_[A-Za-z0-9_-]{43}$/;
@@ -22978,13 +22978,18 @@ async function sendFileCommand(options, command2) {
22978
22978
  return body;
22979
22979
  }
22980
22980
  function fileVersionCreate(options, input) {
22981
+ const ifVersion = input.ifVersion ?? null;
22981
22982
  return sendFileCommand(options, {
22982
22983
  kind: "file_version_create",
22983
22984
  file_id: input.fileId,
22984
22985
  version_id: input.versionId,
22985
22986
  name: input.name,
22986
22987
  declared_size_bytes: input.declaredSizeBytes,
22987
- content_type: input.contentType
22988
+ content_type: input.contentType,
22989
+ /* The server validates an exact key set, so the key is sent only when a
22990
+ * precondition was asked for. An unconditional write is byte-identical to
22991
+ * what every earlier client sent. */
22992
+ ...ifVersion === null ? {} : { if_version: ifVersion }
22988
22993
  });
22989
22994
  }
22990
22995
  function fileVersionCommit(options, input) {
@@ -34514,6 +34519,25 @@ async function resolveBudgetAndPrompt(session, prompt, budget) {
34514
34519
  const timeoutMs = typeof budget === "number" ? budget : await budget();
34515
34520
  return await session.prompt(prompt, { timeoutMs });
34516
34521
  }
34522
+ var LISTENER_DELIVERY_MAX_LEASE_MS = 9e5;
34523
+ var LISTENER_DELIVERY_HOLD_RELEASE_REASONS = [
34524
+ "hold_budget",
34525
+ "lease_budget"
34526
+ ];
34527
+ var LISTENER_DELIVERY_HOLD_RELEASE_CLAUSES = {
34528
+ hold_budget: "it used the turn budget for one delivery",
34529
+ lease_budget: "what was left of its lease could not cover the next step"
34530
+ };
34531
+ var LISTENER_DELIVERY_HOLD_RELEASE_REMEDIES = {
34532
+ hold_budget: `a larger --turn-budget gives one delivery more of the seat. Past the ${LISTENER_DELIVERY_MAX_LEASE_MS / 6e4} minutes the service leases a delivery for it stops helping, because the turn then outlives its lease and the reply can no longer be acknowledged. The bound is read when the listener starts, so stop this listener and start it again to change it`,
34533
+ /* NOT a cap: nothing clamps the turn budget to the lease, and leaseSpent
34534
+ refuses to START a phase rather than interrupting one, so a 60m budget
34535
+ really does hold the worker for 60m. The sentence says raising past the
34536
+ lease stops helping, and why, which is what the code supports. An earlier
34537
+ version read "up to the 15 minutes the service leases it for", which a
34538
+ review arm read as a cap the code does not enforce. */
34539
+ lease_budget: "nothing needs raising: the row comes back under a new lease of full length, so the next attempt starts with the room this one ran out of. If it keeps being handed back, the service stops retrying it in the end, so look at the delivery rather than at the bound"
34540
+ };
34517
34541
 
34518
34542
  // src/listener/engine.ts
34519
34543
  var UUID_RE14 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -36735,11 +36759,11 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
36735
36759
  // src/listener/runtime.ts
36736
36760
  var LISTENER_PAGE_LIMIT = 100;
36737
36761
  var LISTENER_IDLE_POLL_MS = 2e3;
36738
- var LISTENER_DELIVERY_MAX_LEASE_MS = 9e5;
36739
36762
  var LISTENER_DELIVERY_SAFETY_MARGIN_MS = 3e4;
36740
36763
  var LISTENER_ACK_ONLY_MINIMUM_MS = DELIVERY_REQUEST_TIMEOUT_MS + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
36741
36764
  var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ONLY_MINIMUM_MS;
36742
36765
  var LISTENER_PROMPT_START_MINIMUM_MS = SIGNAL_READ_TIMEOUT_MS + ACP_DEFAULT_REQUEST_TIMEOUT_MS + LISTENER_REPLY_ONLY_MINIMUM_MS;
36766
+ var LISTENER_DELIVERY_HOLD_BUDGET_MS = LISTENER_PROMPT_TIMEOUT_MS;
36743
36767
  var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
36744
36768
  var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
36745
36769
  var LISTENER_HOST_PORTS_PROBE_MS = 6e4;
@@ -37012,6 +37036,7 @@ async function runListenerRuntime(options) {
37012
37036
  const pollMs = options.pollMs ?? LISTENER_IDLE_POLL_MS;
37013
37037
  const routeMode = options.routeMode ?? "worker";
37014
37038
  const deferOverChars = options.deferOverChars ?? null;
37039
+ const deliveryHoldBudgetMs = options.deliveryHoldBudgetMs ?? LISTENER_DELIVERY_HOLD_BUDGET_MS;
37015
37040
  const abort = options.signal;
37016
37041
  const hasInstanceId = options.listenerInstanceId !== void 0;
37017
37042
  const hasJournal = options.deliveryJournal !== void 0;
@@ -37033,6 +37058,12 @@ async function runListenerRuntime(options) {
37033
37058
  new Error("an injected delivery client requires durable delivery configuration")
37034
37059
  );
37035
37060
  }
37061
+ if (!Number.isSafeInteger(deliveryHoldBudgetMs) || deliveryHoldBudgetMs <= 0) {
37062
+ return await closeBeforeStart(
37063
+ options.model,
37064
+ new Error("listener delivery hold budget must be a positive number of milliseconds")
37065
+ );
37066
+ }
37036
37067
  try {
37037
37068
  decideListenerRoute(routeMode, deferOverChars, 0);
37038
37069
  if (routeMode !== "worker" && options.pendingMainQueue === void 0) {
@@ -37626,6 +37657,8 @@ async function runListenerRuntime(options) {
37626
37657
  stop = { reason: "cancelled" };
37627
37658
  break;
37628
37659
  }
37660
+ const claimedAtMs = Date.parse(active.claimCreatedAt);
37661
+ const holdStartedAtMs = Number.isFinite(claimedAtMs) ? Math.min(claimedAtMs, now()) : now();
37629
37662
  const signal = authoritativeSignal(claimed);
37630
37663
  let terminal = null;
37631
37664
  try {
@@ -37692,22 +37725,18 @@ async function runListenerRuntime(options) {
37692
37725
  throw new Error("stored listener effect does not match the authoritative delivery");
37693
37726
  }
37694
37727
  const requiredBudget = effectPhaseBudget(before);
37695
- if (leasedUntilMs <= now() + requiredBudget) {
37696
- await sleep2(
37697
- Math.max(
37698
- 0,
37699
- leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
37700
- ),
37701
- abort
37702
- );
37703
- if (abort?.aborted) {
37704
- stop = { reason: "cancelled" };
37705
- break;
37706
- }
37707
- if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
37708
- await journal.clearActive(eventTime(now));
37709
- after = null;
37710
- }
37728
+ const holdSpent = processAttempt > 0 && now() - holdStartedAtMs >= deliveryHoldBudgetMs;
37729
+ const leaseSpent = leasedUntilMs <= now() + requiredBudget;
37730
+ if (holdSpent || leaseSpent) {
37731
+ await journal.clearActive(eventTime(now));
37732
+ after = null;
37733
+ options.onEvent?.({
37734
+ type: "delivery_hold_released",
37735
+ signalId: signal.id,
37736
+ reason: holdSpent ? "hold_budget" : "lease_budget",
37737
+ heldMs: Math.max(0, now() - holdStartedAtMs),
37738
+ ts: eventTime(now)
37739
+ });
37711
37740
  break;
37712
37741
  }
37713
37742
  const processed = await engine.process(signal);
@@ -38206,6 +38235,10 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
38206
38235
  "lastAckOutcome",
38207
38236
  "consecutiveAckFailureCount",
38208
38237
  "lastAckSignalId",
38238
+ "currentDeliverySignalId",
38239
+ "currentDeliverySince",
38240
+ "heldBackDeliveries",
38241
+ "pendingDeliveryCountAt",
38209
38242
  "routeMode",
38210
38243
  "deferOverChars",
38211
38244
  "pendingForMainCount",
@@ -38250,6 +38283,30 @@ var STATUS_DELIVERY_KEYS = [
38250
38283
  "consecutiveAckFailureCount"
38251
38284
  ];
38252
38285
  var deliveryOutcomes = DELIVERY_ACK_OUTCOMES;
38286
+ var LISTENER_HELD_BACK_MAX = 16;
38287
+ function parseHeldBackDeliveries(value) {
38288
+ if (!Array.isArray(value) || value.length > LISTENER_HELD_BACK_MAX) return null;
38289
+ const parsed = [];
38290
+ for (const item of value) {
38291
+ if (!item || typeof item !== "object" || Array.isArray(item)) return null;
38292
+ const entry = item;
38293
+ for (const key2 of Object.keys(entry)) {
38294
+ if (key2 !== "signalId" && key2 !== "at" && key2 !== "reason") return null;
38295
+ }
38296
+ if (typeof entry.signalId !== "string" || !UUID_RE18.test(entry.signalId) || typeof entry.at !== "string" || !Number.isFinite(Date.parse(entry.at)) || typeof entry.reason !== "string" || !LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
38297
+ entry.reason
38298
+ )) {
38299
+ return null;
38300
+ }
38301
+ if (parsed.some((seen) => seen.signalId === entry.signalId)) return null;
38302
+ parsed.push({
38303
+ signalId: entry.signalId,
38304
+ at: entry.at,
38305
+ reason: entry.reason
38306
+ });
38307
+ }
38308
+ return parsed;
38309
+ }
38253
38310
  function parseStatus(raw, rejectUnknownKeys = false) {
38254
38311
  let value;
38255
38312
  try {
@@ -38273,7 +38330,8 @@ function parseStatus(raw, rejectUnknownKeys = false) {
38273
38330
  const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
38274
38331
  const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
38275
38332
  const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
38276
- if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE18.test(row.lastAckSignalId)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
38333
+ const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
38334
+ if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
38277
38335
  row.activityLastErrorCode
38278
38336
  ))) {
38279
38337
  throw new Error("stored listener status is malformed");
@@ -38299,6 +38357,16 @@ function parseStatus(raw, rejectUnknownKeys = false) {
38299
38357
  // Optional key: present only when the file carried it, so a status written
38300
38358
  // without it round-trips byte-for-byte (the routeMode pattern).
38301
38359
  ...row.lastAckSignalId === void 0 ? {} : { lastAckSignalId: row.lastAckSignalId ?? null },
38360
+ ...row.currentDeliverySignalId === void 0 ? {} : {
38361
+ currentDeliverySignalId: row.currentDeliverySignalId ?? null
38362
+ },
38363
+ ...row.currentDeliverySince === void 0 ? {} : {
38364
+ currentDeliverySince: row.currentDeliverySince ?? null
38365
+ },
38366
+ ...heldBackDeliveries === void 0 ? {} : { heldBackDeliveries },
38367
+ ...row.pendingDeliveryCountAt === void 0 ? {} : {
38368
+ pendingDeliveryCountAt: row.pendingDeliveryCountAt ?? null
38369
+ },
38302
38370
  lastErrorDetail: row.lastErrorDetail ?? null,
38303
38371
  lastWorkerStderrTail: row.lastWorkerStderrTail ?? null,
38304
38372
  providerVersion: row.providerVersion ?? null,
@@ -38375,7 +38443,10 @@ async function appendListenerEvent(paths, event) {
38375
38443
  "defer_over_chars",
38376
38444
  "body_length",
38377
38445
  "pending_main_count",
38378
- "dropped_count"
38446
+ "dropped_count",
38447
+ // How long one delivery held the worker seat, and why it gave it back.
38448
+ "held_ms",
38449
+ "release_reason"
38379
38450
  ]);
38380
38451
  const deliveryModes = /* @__PURE__ */ new Set(["durable_claim", "cursor_fallback"]);
38381
38452
  const routeModes = /* @__PURE__ */ new Set(["worker", "main", "split"]);
@@ -38437,6 +38508,14 @@ async function appendListenerEvent(paths, event) {
38437
38508
  if ((key2 === "body_length" || key2 === "pending_main_count" || key2 === "dropped_count") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
38438
38509
  throw new Error("listener event main-route count is not allowed");
38439
38510
  }
38511
+ if (key2 === "held_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
38512
+ throw new Error("listener event hold duration is not allowed");
38513
+ }
38514
+ if (key2 === "release_reason" && !(typeof value === "string" && LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
38515
+ value
38516
+ ))) {
38517
+ throw new Error("listener event hold release reason is not allowed");
38518
+ }
38440
38519
  if (key2 === "worker_stderr_tail" && !(typeof value === "string" && value.length > 0 && value.length <= 2048)) {
38441
38520
  throw new Error("listener event stderr tail is not allowed");
38442
38521
  }
@@ -38816,6 +38895,7 @@ async function runListenerSupervisor(options) {
38816
38895
  lastWorkerStderrTail: null,
38817
38896
  deliveryMode: null,
38818
38897
  pendingDeliveryCount: null,
38898
+ pendingDeliveryCountAt: null,
38819
38899
  lastTerminalDeliveryFailureCount: null,
38820
38900
  lastTerminalDeliveryFailureAt: null,
38821
38901
  lastClaimAt: null,
@@ -38827,6 +38907,11 @@ async function runListenerSupervisor(options) {
38827
38907
  lastAckOutcome: carried?.lastAckOutcome ?? null,
38828
38908
  consecutiveAckFailureCount: carried?.consecutiveAckFailureCount ?? null,
38829
38909
  lastAckSignalId: carried?.lastAckSignalId ?? null,
38910
+ /* Never carried across a restart: a seat this process does not hold cannot
38911
+ be reported as held, and the queue age restarts with the observations. */
38912
+ currentDeliverySignalId: null,
38913
+ currentDeliverySince: null,
38914
+ heldBackDeliveries: [],
38830
38915
  routeMode: options.routeMode ?? "worker",
38831
38916
  deferOverChars: options.deferOverChars ?? null,
38832
38917
  pendingForMainCount: 0,
@@ -38860,9 +38945,15 @@ async function runListenerSupervisor(options) {
38860
38945
  chain(() => appendListenerEvent(options.paths, event));
38861
38946
  };
38862
38947
  const transition = (state, changes = {}) => {
38948
+ const notWatching = state === "starting" || state === "stopped" || state === "failed";
38863
38949
  status = {
38864
38950
  ...status,
38865
38951
  ...changes,
38952
+ ...notWatching ? {
38953
+ currentDeliverySignalId: null,
38954
+ currentDeliverySince: null,
38955
+ heldBackDeliveries: []
38956
+ } : {},
38866
38957
  state,
38867
38958
  updatedAt: iso2(now)
38868
38959
  };
@@ -39051,6 +39142,7 @@ async function runListenerSupervisor(options) {
39051
39142
  ...status,
39052
39143
  deliveryMode: event.mode,
39053
39144
  pendingDeliveryCount: event.pendingDeliveryCount,
39145
+ pendingDeliveryCountAt: event.pendingDeliveryCount === null ? null : event.ts,
39054
39146
  updatedAt: event.ts
39055
39147
  };
39056
39148
  persist();
@@ -39063,6 +39155,7 @@ async function runListenerSupervisor(options) {
39063
39155
  return;
39064
39156
  }
39065
39157
  if (event.type === "delivery_claim") {
39158
+ const heldBack = (status.heldBackDeliveries ?? []).filter((entry) => entry.signalId !== event.signalId);
39066
39159
  status = {
39067
39160
  ...status,
39068
39161
  readHealth: recordListenerClaim(
@@ -39070,6 +39163,10 @@ async function runListenerSupervisor(options) {
39070
39163
  event.ts
39071
39164
  ),
39072
39165
  pendingDeliveryCount: event.pendingDeliveryCount,
39166
+ pendingDeliveryCountAt: event.ts,
39167
+ currentDeliverySignalId: event.signalId,
39168
+ currentDeliverySince: event.signalId === null ? null : event.ts,
39169
+ heldBackDeliveries: heldBack,
39073
39170
  lastClaimAt: event.ts,
39074
39171
  updatedAt: event.ts
39075
39172
  };
@@ -39101,6 +39198,36 @@ async function runListenerSupervisor(options) {
39101
39198
  });
39102
39199
  return;
39103
39200
  }
39201
+ if (event.type === "delivery_hold_released") {
39202
+ status = {
39203
+ ...status,
39204
+ currentDeliverySignalId: null,
39205
+ currentDeliverySince: null,
39206
+ /* Held back, NOT waiting to be claimed: the row keeps its live lease,
39207
+ so the service cannot hand it to anyone until that lease expires.
39208
+ Both review arms on 33cd24b measured the earlier wording counting it
39209
+ among deliveries "waiting to be claimed". Newest first, deduplicated
39210
+ on the id (a row can be released, redelivered and released again),
39211
+ and bounded. */
39212
+ heldBackDeliveries: [
39213
+ { signalId: event.signalId, at: event.ts, reason: event.reason },
39214
+ ...(status.heldBackDeliveries ?? []).filter(
39215
+ (entry) => entry.signalId !== event.signalId
39216
+ )
39217
+ ].slice(0, LISTENER_HELD_BACK_MAX),
39218
+ lastSignalId: event.signalId,
39219
+ updatedAt: event.ts
39220
+ };
39221
+ persist();
39222
+ log({
39223
+ ts: event.ts,
39224
+ event: "listener_delivery_hold_released",
39225
+ signal_id: event.signalId,
39226
+ release_reason: event.reason,
39227
+ held_ms: Math.max(0, Math.trunc(event.heldMs))
39228
+ });
39229
+ return;
39230
+ }
39104
39231
  if (event.type === "delivery_ack") {
39105
39232
  const failed = event.outcome === "failed_terminal";
39106
39233
  const providerProven = DELIVERY_PROVIDER_PROVEN_OUTCOMES.has(event.outcome);
@@ -39111,6 +39238,13 @@ async function runListenerSupervisor(options) {
39111
39238
  lastAckSignalId: event.signalId,
39112
39239
  consecutiveAckFailureCount: failed ? (status.consecutiveAckFailureCount ?? 0) + 1 : providerProven ? 0 : status.consecutiveAckFailureCount,
39113
39240
  pendingDeliveryCount: null,
39241
+ pendingDeliveryCountAt: null,
39242
+ currentDeliverySignalId: null,
39243
+ currentDeliverySince: null,
39244
+ // An acknowledged row is answered and gone; drop just that one.
39245
+ heldBackDeliveries: (status.heldBackDeliveries ?? []).filter(
39246
+ (entry) => entry.signalId !== event.signalId
39247
+ ),
39114
39248
  lastSignalId: event.signalId,
39115
39249
  updatedAt: event.ts
39116
39250
  };
@@ -39281,7 +39415,14 @@ async function effectiveListenerStatus(paths) {
39281
39415
  state: "failed",
39282
39416
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
39283
39417
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
39284
- lastErrorCode: "unclean_exit"
39418
+ lastErrorCode: "unclean_exit",
39419
+ /* The process is gone: it holds nothing and observes nothing, so every
39420
+ field whose sentence is rendered in the present tense against read
39421
+ time is cleared. pendingDeliveryCount stays, because its line already
39422
+ says it is what the service reported. */
39423
+ currentDeliverySignalId: null,
39424
+ currentDeliverySince: null,
39425
+ heldBackDeliveries: []
39285
39426
  };
39286
39427
  await writeListenerStatus(paths, failed);
39287
39428
  return failed;
@@ -41648,6 +41789,23 @@ var ListenerHttpClient = class {
41648
41789
 
41649
41790
  // src/resume.ts
41650
41791
  var import_node_child_process8 = require("node:child_process");
41792
+ var DEFAULT_PROCESS_TABLE_COMMAND = {
41793
+ file: "ps",
41794
+ args: ["-axo", "pid=,command="]
41795
+ };
41796
+ var ProcessTableError = class extends Error {
41797
+ constructor(command2, detail) {
41798
+ super(
41799
+ `could not read the host process table with ${command2.file}: ${detail}`
41800
+ );
41801
+ this.command = command2;
41802
+ this.detail = detail;
41803
+ this.name = "ProcessTableError";
41804
+ }
41805
+ command;
41806
+ detail;
41807
+ code = "process_table_unavailable";
41808
+ };
41651
41809
  function execFileText(file, args) {
41652
41810
  return new Promise((resolve3, reject) => {
41653
41811
  (0, import_node_child_process8.execFile)(file, [...args], {
@@ -41659,15 +41817,76 @@ function execFileText(file, args) {
41659
41817
  });
41660
41818
  });
41661
41819
  }
41662
- function systemProcessTable() {
41820
+ function parseProcessRow(line) {
41821
+ const match = /^\s*(\d+)\s+(.*)$/.exec(line);
41822
+ if (!match) return null;
41823
+ const pid = Number(match[1]);
41824
+ if (!Number.isSafeInteger(pid) || pid <= 0) return null;
41825
+ return { pid, command: match[2] };
41826
+ }
41827
+ var PROCESS_TABLE_STDERR_MAX_CHARS = 2e3;
41828
+ function systemProcessTable(options = {}) {
41829
+ const command2 = options.command ?? DEFAULT_PROCESS_TABLE_COMMAND;
41830
+ const retain = options.retain ?? (() => true);
41663
41831
  return {
41664
- async list() {
41665
- const output = await execFileText("ps", ["-axo", "pid=,command="]);
41666
- return output.split("\n").flatMap((line) => {
41667
- const match = /^\s*(\d+)\s+(.*)$/.exec(line);
41668
- if (!match) return [];
41669
- const pid = Number(match[1]);
41670
- return Number.isSafeInteger(pid) && pid > 0 ? [{ pid, command: match[2] }] : [];
41832
+ list() {
41833
+ return new Promise((resolve3, reject) => {
41834
+ const child = (0, import_node_child_process8.spawn)(command2.file, [...command2.args], {
41835
+ stdio: ["ignore", "pipe", "pipe"]
41836
+ });
41837
+ const rows3 = [];
41838
+ let pending = "";
41839
+ let stderr = "";
41840
+ let settled = false;
41841
+ const fail = (detail) => {
41842
+ if (settled) return;
41843
+ settled = true;
41844
+ child.stdout.destroy();
41845
+ reject(new ProcessTableError(command2, detail));
41846
+ };
41847
+ const take = (line) => {
41848
+ const row = parseProcessRow(line);
41849
+ if (row === null) return true;
41850
+ try {
41851
+ if (retain(row.command)) rows3.push(row);
41852
+ } catch {
41853
+ fail("its row filter threw");
41854
+ return false;
41855
+ }
41856
+ return true;
41857
+ };
41858
+ child.stdout.setEncoding("utf8");
41859
+ child.stdout.on("data", (chunk) => {
41860
+ if (settled) return;
41861
+ const lines = (pending + chunk).split("\n");
41862
+ pending = lines.pop() ?? "";
41863
+ for (const line of lines) {
41864
+ if (!take(line)) return;
41865
+ }
41866
+ });
41867
+ child.stderr.setEncoding("utf8");
41868
+ child.stderr.on("data", (chunk) => {
41869
+ const room = PROCESS_TABLE_STDERR_MAX_CHARS - stderr.length;
41870
+ if (room > 0) stderr += chunk.slice(0, room);
41871
+ });
41872
+ child.stdout.on("error", () => fail("its output stream failed"));
41873
+ child.stderr.on("error", () => fail("its error stream failed"));
41874
+ child.on("error", (error) => fail(error.name));
41875
+ child.on("close", (code, signal) => {
41876
+ if (settled) return;
41877
+ if (pending.length > 0 && !take(pending)) return;
41878
+ if (signal !== null) {
41879
+ fail(`it was stopped by ${signal}`);
41880
+ return;
41881
+ }
41882
+ if (code !== 0) {
41883
+ const trailer = stderr.trim().length > 0 ? `: ${stderr.trim().slice(0, PROCESS_TABLE_STDERR_MAX_CHARS)}` : "";
41884
+ fail(`it exited ${code}${trailer}`);
41885
+ return;
41886
+ }
41887
+ settled = true;
41888
+ resolve3(rows3);
41889
+ });
41671
41890
  });
41672
41891
  }
41673
41892
  };
@@ -41717,7 +41936,10 @@ function isNotifyCommand(command2) {
41717
41936
  return /(?:^|\s)inbox(?:\s|$)/.test(command2) && /(?:^|\s)--notify(?:\s|$)/.test(command2);
41718
41937
  }
41719
41938
  async function findNotifyWatchers(options) {
41720
- const processTable = options.processTable ?? systemProcessTable();
41939
+ const processTable = options.processTable ?? systemProcessTable({
41940
+ retain: isNotifyCommand,
41941
+ ...options.processTableCommand ? { command: options.processTableCommand } : {}
41942
+ });
41721
41943
  const stdoutConsumer = options.stdoutConsumer ?? lsofStdoutConsumer();
41722
41944
  const rows3 = await processTable.list();
41723
41945
  const matches = rows3.flatMap((row) => {
@@ -41985,6 +42207,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
41985
42207
  "grok-executable",
41986
42208
  "head-sha",
41987
42209
  "help",
42210
+ "if-version",
41988
42211
  "include-stale",
41989
42212
  "include-tombstoned",
41990
42213
  "invitation-id",
@@ -42056,8 +42279,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
42056
42279
  ]);
42057
42280
  var UUID_RE23 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
42058
42281
  function packageVersion() {
42059
- if ("0.1.52".length > 0) {
42060
- return "0.1.52";
42282
+ if ("0.1.54".length > 0) {
42283
+ return "0.1.54";
42061
42284
  }
42062
42285
  try {
42063
42286
  const value = JSON.parse(
@@ -42192,7 +42415,7 @@ Usage:
42192
42415
  cswarm file restore <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42193
42416
  cswarm brain ls [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42194
42417
  cswarm brain get <topic>[@<version>] [--version <n>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42195
- cswarm brain put <topic> [<markdown-path>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json] # without a path, reads Markdown from stdin
42418
+ cswarm brain put <topic> [<markdown-path>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--if-version <n>] [--json] # without a path, reads Markdown from stdin; --if-version refuses the write unless the live version is still <n>
42196
42419
  cswarm feedback "<text>" --kind bug|idea|friction [--about <ref>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
42197
42420
  cswarm listen start ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--turn-budget <duration>] [--route worker|main|split] [--defer-over <chars>] [--allow-unattended] [--foreground] [--json]
42198
42421
  cswarm listen canary ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--state-dir <path>] [--wait <seconds>] [--json]
@@ -42282,7 +42505,10 @@ due \u2014 a turn never outlives its credential. Right after a rotation the full
42282
42505
  budget is available up to the token TTL minus 60s (about 59m on the default 1h
42283
42506
  TTL); a turn that lands just before a rotation can be clamped to the ~5m
42284
42507
  renewal lead, and if it times out there, durable delivery retries it on the
42285
- fresh credential.
42508
+ fresh credential. The same budget also bounds how long ONE delivery may hold the
42509
+ worker seat across its retries: when it is spent the listener hands the seat
42510
+ back and claims the next delivery. After the lease ends the service either
42511
+ delivers the released one again or terminates it.
42286
42512
 
42287
42513
  listen start --route worker|main|split chooses where directed messages go. worker
42288
42514
  is the unchanged default. main queues every ask or note for the interactive session.
@@ -45305,6 +45531,12 @@ function listenerStatusJson(status, permissionMode, evidence = {
45305
45531
  lastAckOutcome: status.lastAckOutcome ?? null,
45306
45532
  consecutiveAckFailureCount: status.consecutiveAckFailureCount ?? null,
45307
45533
  lastAckSignalId: status.lastAckSignalId ?? null,
45534
+ currentDeliverySignalId: status.currentDeliverySignalId ?? null,
45535
+ currentDeliverySince: status.currentDeliverySince ?? null,
45536
+ currentDeliveryElapsedMs: status.currentDeliverySince ? Math.max(0, nowMs - Date.parse(status.currentDeliverySince)) : null,
45537
+ pendingDeliveryCountAt: status.pendingDeliveryCountAt ?? null,
45538
+ heldBackDeliveries: status.heldBackDeliveries ?? [],
45539
+ heldBackDeliveryCount: (status.heldBackDeliveries ?? []).length,
45308
45540
  routeMode: status.routeMode ?? "worker",
45309
45541
  deferOverChars: status.deferOverChars ?? null,
45310
45542
  pendingForMainCount: status.pendingForMainCount ?? 0,
@@ -45427,8 +45659,26 @@ function renderListenerStatus(status, evidence = {
45427
45659
  lines.push("Delivery mode has not been reported yet.");
45428
45660
  }
45429
45661
  if (status.pendingDeliveryCount !== null) {
45662
+ const observedAt = status.pendingDeliveryCountAt ?? null;
45663
+ lines.push(
45664
+ `Pending deliveries reported by the service: ${status.pendingDeliveryCount}.` + (observedAt === null ? " When the service reported it was not recorded." : ` The service reported that ${relativeAge(observedAt, nowMs)}.`)
45665
+ );
45666
+ }
45667
+ const currentDeliveryId = status.currentDeliverySignalId ?? null;
45668
+ const currentDeliverySince = status.currentDeliverySince ?? null;
45669
+ if (currentDeliveryId !== null && currentDeliverySince !== null) {
45670
+ lines.push(
45671
+ `Working on delivery ${currentDeliveryId}, claimed ${relativeAge(currentDeliverySince, nowMs)}.`
45672
+ );
45673
+ } else {
45674
+ lines.push("No delivery is being worked on right now.");
45675
+ }
45676
+ const heldBack = status.heldBackDeliveries ?? [];
45677
+ const newestHeldBack = heldBack[0];
45678
+ if (newestHeldBack !== void 0) {
45679
+ const others = heldBack.length - 1;
45430
45680
  lines.push(
45431
- `Pending deliveries reported by the service: ${status.pendingDeliveryCount}.`
45681
+ `Delivery ${newestHeldBack.signalId} was handed back ${relativeAge(newestHeldBack.at, nowMs)} because ${LISTENER_DELIVERY_HOLD_RELEASE_CLAUSES[newestHeldBack.reason]}.` + (others > 0 ? ` This listener is still tracking ${others} other handed-back ${others === 1 ? "delivery" : "deliveries"}.` : "") + ` This listener has not answered it. After the lease ends the service either delivers it again or terminates it. If this repeats, ${LISTENER_DELIVERY_HOLD_RELEASE_REMEDIES[newestHeldBack.reason]}.`
45432
45682
  );
45433
45683
  }
45434
45684
  lines.push(
@@ -45969,6 +46219,9 @@ async function runConfiguredListener(options) {
45969
46219
  },
45970
46220
  routeMode,
45971
46221
  deferOverChars,
46222
+ /* One delivery may hold the seat for one turn budget, not for the
46223
+ whole 15-minute lease. Same lever, so the two cannot drift. */
46224
+ deliveryHoldBudgetMs: turnBudgetMs,
45972
46225
  pendingMainQueue,
45973
46226
  fetcher: httpClient.fetch
45974
46227
  });
@@ -46796,7 +47049,7 @@ async function resolveFileSelector(context, selector) {
46796
47049
  }
46797
47050
  return match.file_id;
46798
47051
  }
46799
- async function uploadNamedFile(context, name, bytes) {
47052
+ async function uploadNamedFile(context, name, bytes, options = {}) {
46800
47053
  if (bytes.byteLength > FILE_MAX_VERSION_BYTES) {
46801
47054
  throw new Error(
46802
47055
  `this file is ${formatFileSize(bytes.byteLength)}; the per-file limit is ${formatFileSize(FILE_MAX_VERSION_BYTES)}, so the upload was not started`
@@ -46823,7 +47076,8 @@ async function uploadNamedFile(context, name, bytes) {
46823
47076
  versionId,
46824
47077
  name,
46825
47078
  declaredSizeBytes: bytes.byteLength,
46826
- contentType
47079
+ contentType,
47080
+ ...options.ifVersion === void 0 ? {} : { ifVersion: options.ifVersion }
46827
47081
  })
46828
47082
  );
46829
47083
  await onceRetried(
@@ -47117,7 +47371,8 @@ async function runBrainPut(args) {
47117
47371
  "cswarm brain put cannot read both the credential and Markdown from stdin; use --agent-token-file or pass a Markdown path"
47118
47372
  );
47119
47373
  }
47120
- const context = await fileContext(args, [], args.positionals.length);
47374
+ const context = await fileContext(args, ["if-version"], args.positionals.length);
47375
+ const ifVersion = args.optional("if-version") === void 0 ? void 0 : integer2(args, "if-version", { minimum: 0 });
47121
47376
  let bytes;
47122
47377
  if (localPath) {
47123
47378
  try {
@@ -47132,7 +47387,19 @@ async function runBrainPut(args) {
47132
47387
  bytes = await readBrainMarkdownFromStdin();
47133
47388
  }
47134
47389
  decodeBrainMarkdown(bytes);
47135
- const committed = await uploadNamedFile(context, brainFileName(topic), bytes);
47390
+ const committed = await uploadNamedFile(
47391
+ context,
47392
+ brainFileName(topic),
47393
+ bytes,
47394
+ ifVersion === void 0 ? {} : { ifVersion }
47395
+ ).catch((error) => {
47396
+ if (error instanceof FileCommandRefused && error.code === FILE_VERSION_PRECONDITION_FAILED) {
47397
+ throw new Error(
47398
+ `${error.message}. Someone saved a new version after you read this topic. Re-read it, apply your change to that copy, then put it again: cswarm brain get ${topic}`
47399
+ );
47400
+ }
47401
+ throw error;
47402
+ });
47136
47403
  if (args.has("json")) {
47137
47404
  process.stdout.write(`${JSON.stringify({ topic, ...committed }, null, 2)}
47138
47405
  `);
@@ -47634,5 +47901,6 @@ ${usage()}
47634
47901
  replyRefusalHint,
47635
47902
  resolveDetachedClaudeExecutable,
47636
47903
  resolveDetachedCodexExecutable,
47637
- resolveTurnBudgetOrDefer
47904
+ resolveTurnBudgetOrDefer,
47905
+ usage
47638
47906
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
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"