commonswarm 0.1.32 → 0.1.33

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 +98 -16
  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
  }
@@ -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,7 +35075,7 @@ var FilePendingMainQueue = class {
35031
35075
  }, lockTimeoutMs === void 0 ? {} : { timeoutMs: lockTimeoutMs });
35032
35076
  }
35033
35077
  };
35034
- function pendingMainEntry(signal, principalId, provenance, now) {
35078
+ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
35035
35079
  if (signal.kind !== "ask") {
35036
35080
  throw new Error("only directed asks can enter the pending-for-main queue");
35037
35081
  }
@@ -35044,7 +35088,8 @@ function pendingMainEntry(signal, principalId, provenance, now) {
35044
35088
  senderName: provenance.senderName,
35045
35089
  body: signal.body,
35046
35090
  createdAt: signal.created_at,
35047
- queuedAt: new Date(now).toISOString()
35091
+ queuedAt: new Date(now).toISOString(),
35092
+ ...options.observationPending ? { observationPending: true } : {}
35048
35093
  });
35049
35094
  }
35050
35095
 
@@ -35138,7 +35183,7 @@ function ackForTerminalEffect(record, now) {
35138
35183
  return { outcome: "observed", lastErrorCode: null };
35139
35184
  }
35140
35185
  if (record.state === "routed_main" && record.signalKind === "ask") {
35141
- return { outcome: "observed", lastErrorCode: null };
35186
+ return { outcome: "queued", lastErrorCode: null };
35142
35187
  }
35143
35188
  if (record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now()) {
35144
35189
  return { outcome: "expired", lastErrorCode: null };
@@ -35421,7 +35466,9 @@ async function runListenerRuntime(options) {
35421
35466
  }
35422
35467
  }
35423
35468
  const queued = await options.pendingMainQueue.enqueue(
35424
- pendingMainEntry(signal, options.principalId, provenance, now())
35469
+ pendingMainEntry(signal, options.principalId, provenance, now(), {
35470
+ observationPending: deliveryMode === "durable_claim"
35471
+ })
35425
35472
  );
35426
35473
  options.onEvent?.({
35427
35474
  type: "main_queue",
@@ -36341,6 +36388,7 @@ async function appendListenerEvent(paths, event) {
36341
36388
  const deliveryOutcomes = /* @__PURE__ */ new Set([
36342
36389
  "replied",
36343
36390
  "observed",
36391
+ "queued",
36344
36392
  "expired",
36345
36393
  "failed_terminal"
36346
36394
  ]);
@@ -37185,6 +37233,7 @@ var ALLOWED_PHASES = /* @__PURE__ */ new Set(["claim_pending", "leased", "ack_pe
37185
37233
  var ALLOWED_OUTCOMES = /* @__PURE__ */ new Set([
37186
37234
  "replied",
37187
37235
  "observed",
37236
+ "queued",
37188
37237
  "expired",
37189
37238
  "failed_terminal"
37190
37239
  ]);
@@ -38310,6 +38359,32 @@ function renderDroppedAsks(count2) {
38310
38359
  }
38311
38360
  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
38361
  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.";
38362
+ async function recordQueuedObservations(check, signalIds, options, now) {
38363
+ const stored = check.context.credential;
38364
+ const remainingMs = options.deadlineMs - now();
38365
+ if (signalIds.length === 0 || stored === null || options.signal.aborted || remainingMs <= 0) {
38366
+ return /* @__PURE__ */ new Set();
38367
+ }
38368
+ const client = new DeliveryCommandClient(
38369
+ cloudTarget(stored.targetUrl, stored.anonKey),
38370
+ options.fetcher ?? fetch,
38371
+ { deadlineMs: remainingMs, now }
38372
+ );
38373
+ const results = await Promise.all(signalIds.map(async (signalId) => {
38374
+ try {
38375
+ await client.observeQueuedAgentDelivery({
38376
+ workspaceId: stored.workspaceId,
38377
+ credential: stored.credential,
38378
+ commandId: observationCommandId(signalId),
38379
+ signalId
38380
+ });
38381
+ return signalId;
38382
+ } catch {
38383
+ return null;
38384
+ }
38385
+ }));
38386
+ return new Set(results.filter((signalId) => signalId !== null));
38387
+ }
38313
38388
  async function checkListenerHooks(options) {
38314
38389
  try {
38315
38390
  const stateDirectory2 = options.stateDirectory ?? defaultListenerStateDirectory();
@@ -38388,10 +38463,8 @@ async function checkListenerHooks(options) {
38388
38463
  check,
38389
38464
  store: store2,
38390
38465
  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),
38466
+ plainPendingSignalIds: check.pending.filter((item) => item.observationPending !== true).map((item) => item.signalId),
38467
+ observationPendingSignalIds: check.pending.filter((item) => item.observationPending === true).map((item) => item.signalId),
38395
38468
  reportDrops,
38396
38469
  reportCredentialFailure
38397
38470
  });
@@ -38404,8 +38477,17 @@ async function checkListenerHooks(options) {
38404
38477
  ...commit.reportDrops ? { droppedCount: commit.check.droppedCount } : {},
38405
38478
  ...commit.reportCredentialFailure ? { credentialFailureReported: true } : commit.check.credentialHealthy ? { credentialFailureReported: false } : {}
38406
38479
  });
38480
+ const observedSignalIds = await recordQueuedObservations(
38481
+ commit.check,
38482
+ commit.observationPendingSignalIds,
38483
+ options,
38484
+ now
38485
+ );
38407
38486
  const remainingCount = await commit.check.queue.remove(
38408
- new Set(commit.settledPendingSignalIds),
38487
+ /* @__PURE__ */ new Set([
38488
+ ...commit.plainPendingSignalIds,
38489
+ ...observedSignalIds
38490
+ ]),
38409
38491
  HOOK_LOCK_TIMEOUT_MS
38410
38492
  );
38411
38493
  if (commit.check.context.paths !== null && commit.check.context.status !== null) {
@@ -38538,8 +38620,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
38538
38620
  AGENT_CREDENTIAL_MESSAGE_D088
38539
38621
  ];
38540
38622
  function packageVersion() {
38541
- if ("0.1.32".length > 0) {
38542
- return "0.1.32";
38623
+ if ("0.1.33".length > 0) {
38624
+ return "0.1.33";
38543
38625
  }
38544
38626
  try {
38545
38627
  const value = JSON.parse(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
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"