commonswarm 0.1.32 → 0.1.34

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 +228 -126
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -29586,6 +29586,7 @@ var DeliveryReceiptReadError = class extends Error {
29586
29586
  var ACK_OUTCOMES = /* @__PURE__ */ new Set([
29587
29587
  "replied",
29588
29588
  "observed",
29589
+ "queued",
29589
29590
  "expired",
29590
29591
  "failed_terminal"
29591
29592
  ]);
@@ -29795,6 +29796,7 @@ function signalReceiptCliState(receipt, nowMs) {
29795
29796
  if (state === "enqueued") return "not_delivered";
29796
29797
  if (state === "leased") return "working";
29797
29798
  if (state === "delivered") return "delivered";
29799
+ if (state === "queued") return "queued";
29798
29800
  return "finished";
29799
29801
  }
29800
29802
  function receiptCheckCommand(report) {
@@ -29834,6 +29836,13 @@ function renderSignalReceiptReport(report, nowMs = Date.now()) {
29834
29836
  `Check for the outcome with: ${receiptCheckCommand(report)}`
29835
29837
  ].join("\n");
29836
29838
  }
29839
+ if (state === "queued") {
29840
+ return [
29841
+ `Queued for agent ${receipt.recipient_agent_principal_id}'s interactive session ${relativeAge(receipt.acked_at, nowMs)}. The agent has not seen it yet.`,
29842
+ "It will appear at the agent's next prompt.",
29843
+ `Check again with: ${receiptCheckCommand(report)}`
29844
+ ].join("\n");
29845
+ }
29837
29846
  const finished = `Agent ${receipt.recipient_agent_principal_id} finished with outcome ${state} ${relativeAge(receipt.acked_at, nowMs)}.`;
29838
29847
  if (state === "replied") {
29839
29848
  return [
@@ -29844,7 +29853,7 @@ function renderSignalReceiptReport(report, nowMs = Date.now()) {
29844
29853
  if (state === "observed") {
29845
29854
  return [
29846
29855
  finished,
29847
- "The agent acknowledged the signal without sending a reply.",
29856
+ "The agent saw the signal without sending a reply.",
29848
29857
  `If you need an answer, send a new ask with: ${newAskCommand(report, receipt)}`
29849
29858
  ].join("\n");
29850
29859
  }
@@ -33059,7 +33068,7 @@ function parseV2Record(row) {
33059
33068
  throw new Error("stored listener effect is malformed");
33060
33069
  }
33061
33070
  if (signalKind2 === "note") {
33062
- if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
33071
+ if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" && row.state !== "routed_main" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
33063
33072
  throw new Error("stored listener effect is malformed");
33064
33073
  }
33065
33074
  } else if (row.state === "routed_main") {
@@ -33128,11 +33137,11 @@ function newObservedNoteRecord(input) {
33128
33137
  updatedAt: input.updatedAt
33129
33138
  };
33130
33139
  }
33131
- function newRoutedMainAskRecord(input) {
33140
+ function newRoutedMainRecord(input) {
33132
33141
  const base = newObservedNoteRecord(input);
33133
33142
  return {
33134
33143
  ...base,
33135
- signalKind: "ask",
33144
+ signalKind: input.signalKind,
33136
33145
  state: "routed_main"
33137
33146
  };
33138
33147
  }
@@ -34330,6 +34339,7 @@ var SENDER_OWNER_RELATIONS2 = /* @__PURE__ */ new Set([
34330
34339
  var DELIVERY_ACK_OUTCOMES = /* @__PURE__ */ new Set([
34331
34340
  "replied",
34332
34341
  "observed",
34342
+ "queued",
34333
34343
  "expired",
34334
34344
  "failed_terminal"
34335
34345
  ]);
@@ -34376,6 +34386,10 @@ var DELIVERY_SERVER_ERROR_CODES = Object.freeze([
34376
34386
  "internal_error"
34377
34387
  ]);
34378
34388
  var DELIVERY_UNKNOWN_ERROR_CODE = "unknown_error";
34389
+ function observationCommandId(signalId) {
34390
+ checkedUuidRequest(signalId, "signalId");
34391
+ return `observe_${signalId.toLowerCase().replaceAll("-", "")}`;
34392
+ }
34379
34393
  var DeliveryTransportError = class extends Error {
34380
34394
  constructor(message) {
34381
34395
  super(message);
@@ -34672,7 +34686,7 @@ function assertAckRequest(request) {
34672
34686
  checkedUuidRequest(request.listenerInstanceId, "listenerInstanceId");
34673
34687
  if (!DELIVERY_ACK_OUTCOMES.has(request.outcome)) {
34674
34688
  throw new Error(
34675
- "a delivery outcome must be replied, observed, expired, or failed_terminal"
34689
+ "a delivery outcome must be replied, observed, queued, expired, or failed_terminal"
34676
34690
  );
34677
34691
  }
34678
34692
  if (request.outcome === "failed_terminal") {
@@ -34864,6 +34878,34 @@ var DeliveryCommandClient = class {
34864
34878
  outcome: request.outcome
34865
34879
  };
34866
34880
  }
34881
+ /** Mark a queued delivery observed only after the interactive hook surfaced it. */
34882
+ async observeQueuedAgentDelivery(request) {
34883
+ checkedCommandId(request.commandId);
34884
+ assertAgentToken(request.credential);
34885
+ checkedUuidRequest(request.workspaceId, "workspaceId");
34886
+ checkedUuidRequest(request.signalId, "signalId");
34887
+ const { response, text } = await this.post(request, {
34888
+ kind: "ack_agent_delivery",
34889
+ signal_id: request.signalId.toLowerCase(),
34890
+ lease_id: null,
34891
+ listener_instance_id: null,
34892
+ outcome: "observed",
34893
+ last_error_code: null
34894
+ }, "delivery observation");
34895
+ if (!response.ok) throw refusal(response, text);
34896
+ parseAckSuccess(
34897
+ successBody(response, text, "delivery observation"),
34898
+ {
34899
+ signalId: request.signalId.toLowerCase(),
34900
+ outcome: "observed"
34901
+ }
34902
+ );
34903
+ return {
34904
+ httpStatus: response.status,
34905
+ signalId: request.signalId.toLowerCase(),
34906
+ outcome: "observed"
34907
+ };
34908
+ }
34867
34909
  };
34868
34910
 
34869
34911
  // src/listener/main-routing.ts
@@ -34917,12 +34959,13 @@ function parseEntry(value) {
34917
34959
  "senderName",
34918
34960
  "body",
34919
34961
  "createdAt",
34920
- "queuedAt"
34962
+ "queuedAt",
34963
+ "observationPending"
34921
34964
  ]);
34922
34965
  if (Object.keys(row).some((key2) => !allowed.has(key2))) {
34923
34966
  throw new Error("stored pending-for-main entry is malformed");
34924
34967
  }
34925
- if (typeof row.signalId !== "string" || !UUID_RE12.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE12.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE12.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE12.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt)) {
34968
+ if (typeof row.signalId !== "string" || !UUID_RE12.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE12.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE12.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE12.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt) || !(row.observationPending === void 0 || row.observationPending === true)) {
34926
34969
  throw new Error("stored pending-for-main entry is malformed");
34927
34970
  }
34928
34971
  return {
@@ -34935,7 +34978,8 @@ function parseEntry(value) {
34935
34978
  senderName: row.senderName,
34936
34979
  body: row.body,
34937
34980
  createdAt: row.createdAt,
34938
- queuedAt: row.queuedAt
34981
+ queuedAt: row.queuedAt,
34982
+ ...row.observationPending === true ? { observationPending: true } : {}
34939
34983
  };
34940
34984
  }
34941
34985
  function parseFile(raw) {
@@ -35031,9 +35075,9 @@ var FilePendingMainQueue = class {
35031
35075
  }, lockTimeoutMs === void 0 ? {} : { timeoutMs: lockTimeoutMs });
35032
35076
  }
35033
35077
  };
35034
- function pendingMainEntry(signal, principalId, provenance, now) {
35035
- if (signal.kind !== "ask") {
35036
- throw new Error("only directed asks can enter the pending-for-main queue");
35078
+ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
35079
+ if (signal.kind !== "ask" && signal.kind !== "note") {
35080
+ throw new Error("only directed asks and notes can enter the pending-for-main queue");
35037
35081
  }
35038
35082
  return parseEntry({
35039
35083
  signalId: signal.id,
@@ -35041,10 +35085,12 @@ function pendingMainEntry(signal, principalId, provenance, now) {
35041
35085
  principalId,
35042
35086
  fromId: signal.from,
35043
35087
  fromKind: signal.from_kind,
35088
+ kind: signal.kind,
35044
35089
  senderName: provenance.senderName,
35045
35090
  body: signal.body,
35046
35091
  createdAt: signal.created_at,
35047
- queuedAt: new Date(now).toISOString()
35092
+ queuedAt: new Date(now).toISOString(),
35093
+ ...options.observationPending ? { observationPending: true } : {}
35048
35094
  });
35049
35095
  }
35050
35096
 
@@ -35137,8 +35183,8 @@ function ackForTerminalEffect(record, now) {
35137
35183
  if (record.state === "observed" && record.signalKind === "note") {
35138
35184
  return { outcome: "observed", lastErrorCode: null };
35139
35185
  }
35140
- if (record.state === "routed_main" && record.signalKind === "ask") {
35141
- return { outcome: "observed", lastErrorCode: null };
35186
+ if (record.state === "routed_main") {
35187
+ return { outcome: "queued", lastErrorCode: null };
35142
35188
  }
35143
35189
  if (record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now()) {
35144
35190
  return { outcome: "expired", lastErrorCode: null };
@@ -35159,7 +35205,7 @@ function ackForTerminalEffect(record, now) {
35159
35205
  return { outcome: "failed_terminal", lastErrorCode: "local_effect_failed" };
35160
35206
  }
35161
35207
  function isAckableTerminalEffect(record, now) {
35162
- return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main" && record.signalKind === "ask" || record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now() || record.state === "failed" && record.signalKind === "ask";
35208
+ return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main" || record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now() || record.state === "failed" && record.signalKind === "ask";
35163
35209
  }
35164
35210
  function effectPhaseBudget(record) {
35165
35211
  if (record === null || record.state === "received" || record.state === "prompting") {
@@ -35170,6 +35216,9 @@ function effectPhaseBudget(record) {
35170
35216
  }
35171
35217
  return LISTENER_ACK_ONLY_MINIMUM_MS;
35172
35218
  }
35219
+ function observedNoteNeedsMainRoute(record, routeMode, deferOverChars) {
35220
+ return record?.state === "observed" && record.signalKind === "note" && decideListenerRoute(routeMode, deferOverChars, record.askBody.length) === "main";
35221
+ }
35173
35222
  function verifyPreparedAckEffect(record, active, now) {
35174
35223
  if (record === null || record.signalId !== active.signalId || active.ack === null) {
35175
35224
  throw new Error("prepared delivery ACK has no matching terminal effect");
@@ -35405,7 +35454,11 @@ async function runListenerRuntime(options) {
35405
35454
  ...options.resolveSenderProvenance === void 0 ? {} : { resolveSenderProvenance: options.resolveSenderProvenance },
35406
35455
  isCredentialFailure: isCredentialLoss
35407
35456
  });
35408
- const routeAskToMain = async (signal) => {
35457
+ const routeSignalToMain = async (signal) => {
35458
+ if (signal.kind !== "ask" && signal.kind !== "note") {
35459
+ throw new Error("only directed asks and notes can route to the main session");
35460
+ }
35461
+ const signalKind2 = signal.kind;
35409
35462
  let provenance = {
35410
35463
  senderName: null,
35411
35464
  operatorId: null,
@@ -35421,7 +35474,9 @@ async function runListenerRuntime(options) {
35421
35474
  }
35422
35475
  }
35423
35476
  const queued = await options.pendingMainQueue.enqueue(
35424
- pendingMainEntry(signal, options.principalId, provenance, now())
35477
+ pendingMainEntry(signal, options.principalId, provenance, now(), {
35478
+ observationPending: deliveryMode === "durable_claim"
35479
+ })
35425
35480
  );
35426
35481
  options.onEvent?.({
35427
35482
  type: "main_queue",
@@ -35433,13 +35488,19 @@ async function runListenerRuntime(options) {
35433
35488
  });
35434
35489
  const existing = await options.store.read(signal.id);
35435
35490
  if (existing !== null) {
35436
- if (!sameEffectSignal(existing, signal) || existing.state !== "routed_main") {
35437
- throw new Error("stored listener effect does not match the main-routed ask");
35491
+ if (!sameEffectSignal(existing, signal)) {
35492
+ throw new Error("stored listener effect does not match the main-routed message");
35493
+ }
35494
+ if (existing.state === "routed_main") {
35495
+ return existing;
35496
+ }
35497
+ if (!(existing.signalKind === "note" && existing.state === "observed")) {
35498
+ throw new Error("stored listener effect does not match the main-routed message");
35438
35499
  }
35439
- return existing;
35440
35500
  }
35441
- await options.store.write(newRoutedMainAskRecord({
35501
+ await options.store.write(newRoutedMainRecord({
35442
35502
  signalId: signal.id,
35503
+ signalKind: signalKind2,
35443
35504
  body: signal.body,
35444
35505
  until: signal.until,
35445
35506
  senderOwnerRelation: signal.sender_owner_relation ?? "unknown",
@@ -35447,7 +35508,7 @@ async function runListenerRuntime(options) {
35447
35508
  }));
35448
35509
  const persisted = await options.store.read(signal.id);
35449
35510
  if (persisted === null || !sameEffectSignal(persisted, signal) || persisted.state !== "routed_main") {
35450
- throw new Error("main-routed listener effect could not be verified");
35511
+ throw new Error("main-routed message effect could not be verified");
35451
35512
  }
35452
35513
  return persisted;
35453
35514
  };
@@ -35669,7 +35730,20 @@ async function runListenerRuntime(options) {
35669
35730
  const recovery = currentJournalRecord?.active ?? null;
35670
35731
  if (recovery?.phase === "ack_pending") {
35671
35732
  const horizon = Date.parse(recovery.leasedUntil) + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
35672
- if (page.capabilities.deliveryAck && now() < horizon) {
35733
+ let preparedNeedsMainRoute = false;
35734
+ if (recovery.signalId !== null) {
35735
+ try {
35736
+ preparedNeedsMainRoute = observedNoteNeedsMainRoute(
35737
+ await options.store.read(recovery.signalId),
35738
+ routeMode,
35739
+ deferOverChars
35740
+ );
35741
+ } catch (error) {
35742
+ stop = { reason: "fatal", error: asError2(error) };
35743
+ break;
35744
+ }
35745
+ }
35746
+ if (page.capabilities.deliveryAck && now() < horizon && !preparedNeedsMainRoute) {
35673
35747
  const ackStop = await sendPreparedAck(recovery);
35674
35748
  if (ackStop !== null) {
35675
35749
  stop = ackStop;
@@ -35708,7 +35782,7 @@ async function runListenerRuntime(options) {
35708
35782
  terminal = null;
35709
35783
  }
35710
35784
  }
35711
- if (terminal !== null && sameRecoveredEffect(recovery, terminal) && isAckableTerminalEffect(terminal, now)) {
35785
+ if (terminal !== null && sameRecoveredEffect(recovery, terminal) && !observedNoteNeedsMainRoute(terminal, routeMode, deferOverChars) && isAckableTerminalEffect(terminal, now)) {
35712
35786
  try {
35713
35787
  const mapped = ackForTerminalEffect(terminal, now);
35714
35788
  await options.deliveryJournal.prepareAck({
@@ -35902,7 +35976,33 @@ async function runListenerRuntime(options) {
35902
35976
  if (existing !== null && !sameEffectSignal(existing, signal)) {
35903
35977
  throw new Error("stored listener effect does not match the authoritative delivery");
35904
35978
  }
35905
- if (signal.kind === "note") {
35979
+ if (signal.kind !== "ask" && signal.kind !== "note") {
35980
+ throw new Error("claimed delivery has an unsupported signal kind");
35981
+ }
35982
+ const decision = decideListenerRoute(
35983
+ routeMode,
35984
+ deferOverChars,
35985
+ signal.body.length
35986
+ );
35987
+ options.onEvent?.({
35988
+ type: "routing_decision",
35989
+ signalId: signal.id,
35990
+ routeMode,
35991
+ decision,
35992
+ threshold: deferOverChars,
35993
+ bodyLength: signal.body.length,
35994
+ ts: eventTime(now)
35995
+ });
35996
+ if (decision === "main") {
35997
+ terminal = await routeSignalToMain(signal);
35998
+ options.onEvent?.({
35999
+ type: "effect",
36000
+ signalId: signal.id,
36001
+ status: "routed_main",
36002
+ failureCode: null,
36003
+ ts: eventTime(now)
36004
+ });
36005
+ } else if (signal.kind === "note") {
35906
36006
  if (existing === null) {
35907
36007
  await options.store.write(newObservedNoteRecord({
35908
36008
  signalId: signal.id,
@@ -35923,85 +36023,58 @@ async function runListenerRuntime(options) {
35923
36023
  failureCode: null,
35924
36024
  ts: eventTime(now)
35925
36025
  });
35926
- } else if (signal.kind === "ask") {
35927
- const decision = decideListenerRoute(
35928
- routeMode,
35929
- deferOverChars,
35930
- signal.body.length
35931
- );
35932
- options.onEvent?.({
35933
- type: "routing_decision",
35934
- signalId: signal.id,
35935
- routeMode,
35936
- decision,
35937
- threshold: deferOverChars,
35938
- bodyLength: signal.body.length,
35939
- ts: eventTime(now)
35940
- });
35941
- if (decision === "main") {
35942
- terminal = await routeAskToMain(signal);
36026
+ } else {
36027
+ let processAttempt = 0;
36028
+ while (terminal === null) {
36029
+ const before = await options.store.read(signal.id);
36030
+ if (before !== null && !sameEffectSignal(before, signal)) {
36031
+ throw new Error("stored listener effect does not match the authoritative delivery");
36032
+ }
36033
+ const requiredBudget = effectPhaseBudget(before);
36034
+ if (leasedUntilMs <= now() + requiredBudget) {
36035
+ await sleep2(
36036
+ Math.max(
36037
+ 0,
36038
+ leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
36039
+ ),
36040
+ abort
36041
+ );
36042
+ if (abort?.aborted) {
36043
+ stop = { reason: "cancelled" };
36044
+ break;
36045
+ }
36046
+ if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
36047
+ await journal.clearActive(eventTime(now));
36048
+ after = null;
36049
+ }
36050
+ break;
36051
+ }
36052
+ const processed = await engine.process(signal);
36053
+ const effect = "record" in processed ? processed.record : null;
35943
36054
  options.onEvent?.({
35944
36055
  type: "effect",
35945
36056
  signalId: signal.id,
35946
- status: "routed_main",
35947
- failureCode: null,
36057
+ status: processed.status,
36058
+ failureCode: effect?.failureCode ?? null,
35948
36059
  ts: eventTime(now)
35949
36060
  });
35950
- } else {
35951
- let processAttempt = 0;
35952
- while (terminal === null) {
35953
- const before = await options.store.read(signal.id);
35954
- if (before !== null && !sameEffectSignal(before, signal)) {
35955
- throw new Error("stored listener effect does not match the authoritative delivery");
35956
- }
35957
- const requiredBudget = effectPhaseBudget(before);
35958
- if (leasedUntilMs <= now() + requiredBudget) {
35959
- await sleep2(
35960
- Math.max(
35961
- 0,
35962
- leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
35963
- ),
35964
- abort
35965
- );
35966
- if (abort?.aborted) {
35967
- stop = { reason: "cancelled" };
35968
- break;
35969
- }
35970
- if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
35971
- await journal.clearActive(eventTime(now));
35972
- after = null;
35973
- }
36061
+ if (processed.status === "ignored") {
36062
+ throw new Error("claimed delivery was ignored by the listener engine");
36063
+ }
36064
+ if (processed.status === "retry_pending") {
36065
+ processAttempt += 1;
36066
+ await sleep2(
36067
+ deliveryRetryDelay(processAttempt, null, random),
36068
+ abort
36069
+ );
36070
+ if (abort?.aborted) {
36071
+ stop = { reason: "cancelled" };
35974
36072
  break;
35975
36073
  }
35976
- const processed = await engine.process(signal);
35977
- const effect = "record" in processed ? processed.record : null;
35978
- options.onEvent?.({
35979
- type: "effect",
35980
- signalId: signal.id,
35981
- status: processed.status,
35982
- failureCode: effect?.failureCode ?? null,
35983
- ts: eventTime(now)
35984
- });
35985
- if (processed.status === "ignored") {
35986
- throw new Error("claimed delivery was ignored by the listener engine");
35987
- }
35988
- if (processed.status === "retry_pending") {
35989
- processAttempt += 1;
35990
- await sleep2(
35991
- deliveryRetryDelay(processAttempt, null, random),
35992
- abort
35993
- );
35994
- if (abort?.aborted) {
35995
- stop = { reason: "cancelled" };
35996
- break;
35997
- }
35998
- continue;
35999
- }
36000
- terminal = processed.record;
36074
+ continue;
36001
36075
  }
36076
+ terminal = processed.record;
36002
36077
  }
36003
- } else {
36004
- throw new Error("claimed delivery has an unsupported signal kind");
36005
36078
  }
36006
36079
  } catch (error) {
36007
36080
  if (abort?.aborted) {
@@ -36050,24 +36123,7 @@ async function runListenerRuntime(options) {
36050
36123
  stop = { reason: "cancelled" };
36051
36124
  break;
36052
36125
  }
36053
- if (signal.kind === "note") {
36054
- try {
36055
- await readOrReplaceUnreadableEffect(options.store, signal, now);
36056
- const record2 = await observeFallbackNote(options.store, signal, now);
36057
- options.onEvent?.({
36058
- type: "effect",
36059
- signalId: signal.id,
36060
- status: "observed",
36061
- failureCode: record2.failureCode,
36062
- ts: eventTime(now)
36063
- });
36064
- } catch (error) {
36065
- stop = { reason: "fatal", error: asError2(error) };
36066
- break;
36067
- }
36068
- continue;
36069
- }
36070
- if (signal.kind !== "ask") continue;
36126
+ if (signal.kind !== "ask" && signal.kind !== "note") continue;
36071
36127
  let result;
36072
36128
  try {
36073
36129
  await readOrReplaceUnreadableEffect(options.store, signal, now);
@@ -36086,7 +36142,7 @@ async function runListenerRuntime(options) {
36086
36142
  ts: eventTime(now)
36087
36143
  });
36088
36144
  if (decision === "main") {
36089
- const record2 = await routeAskToMain(signal);
36145
+ await routeSignalToMain(signal);
36090
36146
  options.onEvent?.({
36091
36147
  type: "effect",
36092
36148
  signalId: signal.id,
@@ -36096,6 +36152,17 @@ async function runListenerRuntime(options) {
36096
36152
  });
36097
36153
  continue;
36098
36154
  }
36155
+ if (signal.kind === "note") {
36156
+ const record2 = await observeFallbackNote(options.store, signal, now);
36157
+ options.onEvent?.({
36158
+ type: "effect",
36159
+ signalId: signal.id,
36160
+ status: "observed",
36161
+ failureCode: record2.failureCode,
36162
+ ts: eventTime(now)
36163
+ });
36164
+ continue;
36165
+ }
36099
36166
  result = await engine.process(signal);
36100
36167
  } catch (error) {
36101
36168
  if (abort?.aborted) {
@@ -36341,6 +36408,7 @@ async function appendListenerEvent(paths, event) {
36341
36408
  const deliveryOutcomes = /* @__PURE__ */ new Set([
36342
36409
  "replied",
36343
36410
  "observed",
36411
+ "queued",
36344
36412
  "expired",
36345
36413
  "failed_terminal"
36346
36414
  ]);
@@ -37185,6 +37253,7 @@ var ALLOWED_PHASES = /* @__PURE__ */ new Set(["claim_pending", "leased", "ack_pe
37185
37253
  var ALLOWED_OUTCOMES = /* @__PURE__ */ new Set([
37186
37254
  "replied",
37187
37255
  "observed",
37256
+ "queued",
37188
37257
  "expired",
37189
37258
  "failed_terminal"
37190
37259
  ]);
@@ -38310,6 +38379,32 @@ function renderDroppedAsks(count2) {
38310
38379
  }
38311
38380
  var CREDENTIAL_READ_WARNING = "CommonSwarm could not read the configured listener credential safely. The hook is not checking routed asks. Restart the listener with a fresh credential; any credential state file must be mode 0600. Then run cswarm listen status.";
38312
38381
  var CREDENTIAL_401_WARNING = "CommonSwarm could not authenticate a listener credential (HTTP 401). The hook is not checking routed asks. Restart the listener with a fresh credential, then run cswarm listen status.";
38382
+ async function recordQueuedObservations(check, signalIds, options, now) {
38383
+ const stored = check.context.credential;
38384
+ const remainingMs = options.deadlineMs - now();
38385
+ if (signalIds.length === 0 || stored === null || options.signal.aborted || remainingMs <= 0) {
38386
+ return /* @__PURE__ */ new Set();
38387
+ }
38388
+ const client = new DeliveryCommandClient(
38389
+ cloudTarget(stored.targetUrl, stored.anonKey),
38390
+ options.fetcher ?? fetch,
38391
+ { deadlineMs: remainingMs, now }
38392
+ );
38393
+ const results = await Promise.all(signalIds.map(async (signalId) => {
38394
+ try {
38395
+ await client.observeQueuedAgentDelivery({
38396
+ workspaceId: stored.workspaceId,
38397
+ credential: stored.credential,
38398
+ commandId: observationCommandId(signalId),
38399
+ signalId
38400
+ });
38401
+ return signalId;
38402
+ } catch {
38403
+ return null;
38404
+ }
38405
+ }));
38406
+ return new Set(results.filter((signalId) => signalId !== null));
38407
+ }
38313
38408
  async function checkListenerHooks(options) {
38314
38409
  try {
38315
38410
  const stateDirectory2 = options.stateDirectory ?? defaultListenerStateDirectory();
@@ -38388,10 +38483,8 @@ async function checkListenerHooks(options) {
38388
38483
  check,
38389
38484
  store: store2,
38390
38485
  signalIds: staged.unseen.map((item) => item.signalId),
38391
- // Every staged queue entry is either written by this run or was committed
38392
- // after an earlier successful write. Removing both keeps the queue bounded
38393
- // without re-printing entries below the exactly-once high-water.
38394
- settledPendingSignalIds: check.pending.map((item) => item.signalId),
38486
+ plainPendingSignalIds: check.pending.filter((item) => item.observationPending !== true).map((item) => item.signalId),
38487
+ observationPendingSignalIds: check.pending.filter((item) => item.observationPending === true).map((item) => item.signalId),
38395
38488
  reportDrops,
38396
38489
  reportCredentialFailure
38397
38490
  });
@@ -38404,8 +38497,17 @@ async function checkListenerHooks(options) {
38404
38497
  ...commit.reportDrops ? { droppedCount: commit.check.droppedCount } : {},
38405
38498
  ...commit.reportCredentialFailure ? { credentialFailureReported: true } : commit.check.credentialHealthy ? { credentialFailureReported: false } : {}
38406
38499
  });
38500
+ const observedSignalIds = await recordQueuedObservations(
38501
+ commit.check,
38502
+ commit.observationPendingSignalIds,
38503
+ options,
38504
+ now
38505
+ );
38407
38506
  const remainingCount = await commit.check.queue.remove(
38408
- new Set(commit.settledPendingSignalIds),
38507
+ /* @__PURE__ */ new Set([
38508
+ ...commit.plainPendingSignalIds,
38509
+ ...observedSignalIds
38510
+ ]),
38409
38511
  HOOK_LOCK_TIMEOUT_MS
38410
38512
  );
38411
38513
  if (commit.check.context.paths !== null && commit.check.context.status !== null) {
@@ -38538,8 +38640,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
38538
38640
  AGENT_CREDENTIAL_MESSAGE_D088
38539
38641
  ];
38540
38642
  function packageVersion() {
38541
- if ("0.1.32".length > 0) {
38542
- return "0.1.32";
38643
+ if ("0.1.34".length > 0) {
38644
+ return "0.1.34";
38543
38645
  }
38544
38646
  try {
38545
38647
  const value = JSON.parse(
@@ -38738,11 +38840,11 @@ TTL); a turn that lands just before a rotation can be clamped to the ~5m
38738
38840
  renewal lead, and if it times out there, durable delivery retries it on the
38739
38841
  fresh credential.
38740
38842
 
38741
- listen start --route worker|main|split chooses where directed asks go. worker
38742
- is the unchanged default. main queues every ask for the interactive session.
38743
- split queues asks whose body is longer than --defer-over <chars>; the bound is
38744
- 1..10000 and an equal-length ask stays on the worker path. Run cswarm hook check
38745
- to surface queued asks. hook check has its own 3s ceiling, exits 0 on every
38843
+ listen start --route worker|main|split chooses where directed messages go. worker
38844
+ is the unchanged default. main queues every ask or note for the interactive session.
38845
+ split queues messages whose body is longer than --defer-over <chars>; the bound is
38846
+ 1..10000 and an equal-length message stays on the worker path. Run cswarm hook check
38847
+ to surface queued messages. hook check has its own 3s ceiling, exits 0 on every
38746
38848
  outcome, and skips network checks made within --cooldown seconds (default 30).
38747
38849
  hook install claude prints the UserPromptSubmit JSON by default and changes the
38748
38850
  project's .claude/settings.json only with --write; uninstall also requires --write.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
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"