@cello-protocol/daemon 0.0.166 → 0.0.168

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 (40) hide show
  1. package/dist/daemon.d.ts.map +1 -1
  2. package/dist/daemon.js +52 -5
  3. package/dist/daemon.js.map +1 -1
  4. package/dist/delivery-session-suspects.d.ts +56 -0
  5. package/dist/delivery-session-suspects.d.ts.map +1 -0
  6. package/dist/delivery-session-suspects.js +89 -0
  7. package/dist/delivery-session-suspects.js.map +1 -0
  8. package/dist/delivery-sweep-bound.d.ts +79 -0
  9. package/dist/delivery-sweep-bound.d.ts.map +1 -0
  10. package/dist/delivery-sweep-bound.js +109 -0
  11. package/dist/delivery-sweep-bound.js.map +1 -0
  12. package/dist/document-control-notifier.d.ts +17 -0
  13. package/dist/document-control-notifier.d.ts.map +1 -1
  14. package/dist/document-control-notifier.js +40 -1
  15. package/dist/document-control-notifier.js.map +1 -1
  16. package/dist/document-delivery-transport.d.ts +2 -0
  17. package/dist/document-delivery-transport.d.ts.map +1 -1
  18. package/dist/document-delivery-transport.js +78 -3
  19. package/dist/document-delivery-transport.js.map +1 -1
  20. package/dist/document-delivery.d.ts +30 -4
  21. package/dist/document-delivery.d.ts.map +1 -1
  22. package/dist/document-delivery.js +294 -1
  23. package/dist/document-delivery.js.map +1 -1
  24. package/dist/document-handlers.d.ts.map +1 -1
  25. package/dist/document-handlers.js +209 -60
  26. package/dist/document-handlers.js.map +1 -1
  27. package/dist/document-layer.d.ts.map +1 -1
  28. package/dist/document-layer.js +16 -1
  29. package/dist/document-layer.js.map +1 -1
  30. package/dist/document-lifecycle.d.ts.map +1 -1
  31. package/dist/document-lifecycle.js +10 -3
  32. package/dist/document-lifecycle.js.map +1 -1
  33. package/dist/document-store.d.ts +90 -0
  34. package/dist/document-store.d.ts.map +1 -1
  35. package/dist/document-store.js +311 -0
  36. package/dist/document-store.js.map +1 -1
  37. package/dist/session-node-manager.d.ts.map +1 -1
  38. package/dist/session-node-manager.js +14 -2
  39. package/dist/session-node-manager.js.map +1 -1
  40. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAMrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAmC,KAAK,UAAU,EAAsB,MAAM,iBAAiB,CAAC;AAEvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAO/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAiB7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAsB,MAAM,2BAA2B,CAAC;AAS5G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AA6BnF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;IACrC;;OAEG;IACH,eAAe,IAAI,YAAY,CAAC;IAChC;;;;;;;;OAQG;IACH,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACxC;AAYD,qBAAa,4BAA6B,YAAW,mBAAmB;IAChE,UAAU,CAAC,MAAM,EAAE,iBAAiB;CA8B3C;AAID,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7E"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAMrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAmC,KAAK,UAAU,EAAsB,MAAM,iBAAiB,CAAC;AAEvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAO/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAiB7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAsB,MAAM,2BAA2B,CAAC;AAS5G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AA8BnF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;IACrC;;OAEG;IACH,eAAe,IAAI,YAAY,CAAC;IAChC;;;;;;;;OAQG;IACH,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACxC;AAYD,qBAAa,4BAA6B,YAAW,mBAAmB;IAChE,UAAU,CAAC,MAAM,EAAE,iBAAiB;CA8B3C;AAID,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7E"}
package/dist/daemon.js CHANGED
@@ -72,6 +72,7 @@ import { wireContentHash } from "./wire-content-hash.js";
72
72
  import { DocumentPublish } from "./document-publish.js";
73
73
  import { createDocumentDeliveryTransport } from "./document-delivery-transport.js";
74
74
  import { DocumentDelivery } from "./document-delivery.js";
75
+ import { sweepAgentEvicting } from "./delivery-sweep-bound.js";
75
76
  import { encodeDocumentUpdateEnvelope, DOCUMENT_UPDATE_ENCODING_V1 } from "@cello-protocol/protocol-types";
76
77
  import { createSealFlows } from "./seal-flows.js";
77
78
  import { registerCloseSessionHandler } from "./close-session-handler.js";
@@ -3610,7 +3611,18 @@ async function startDaemonHoldingLock(config, singletonLock) {
3610
3611
  const existing = documentDeliveryWorkers.get(agentName);
3611
3612
  if (existing)
3612
3613
  return existing;
3613
- const worker = new DocumentDelivery(documentLayer.store, documentTransportFor(agentName), logger);
3614
+ const worker = new DocumentDelivery(documentLayer.store, documentTransportFor(agentName), logger,
3615
+ // DOD-MP-CONTROL-DURABLE-1 — control frames derive their recipients the way the NOTIFIER
3616
+ // does, not the way envelopes do. `holdersFor` returns null for a document with no stored
3617
+ // genesis proposal; `controlHolders` has an explicit legacy branch for it. Deriving the two
3618
+ // halves differently turned a seeded row into a permanent no-op.
3619
+ (documentId) => {
3620
+ const ownerAgentId = documentOwnerKeyFor(agentName);
3621
+ if (ownerAgentId === null) {
3622
+ return { ok: false, reason: "document_owner_key_unavailable" };
3623
+ }
3624
+ return documentLayer.controlHolders(ownerAgentId, documentId);
3625
+ });
3614
3626
  documentDeliveryWorkers.set(agentName, worker);
3615
3627
  return worker;
3616
3628
  };
@@ -3678,6 +3690,15 @@ async function startDaemonHoldingLock(config, singletonLock) {
3678
3690
  // Floored, so a misread env cannot turn the sweep into a busy loop against the directory.
3679
3691
  const tickOverride = Number(process.env["CELLO_DOCUMENT_DELIVERY_TICK_MS"]);
3680
3692
  const DOCUMENT_DELIVERY_TICK_MS = Number.isFinite(tickOverride) && tickOverride >= 250 ? tickOverride : 60_000;
3693
+ // DOD-MP-SWEEP-ALIVE-1 — how long ONE agent's pass may run before the sweep stops waiting for
3694
+ // it. Generous relative to real work (the ack-grace budget for a whole pass is 30s) and finite,
3695
+ // which is the only property that matters: the defect is an unbounded wait, not a slow one.
3696
+ // Generous relative to what a HEALTHY pass can legitimately spend: the discovery lookup is
3697
+ // bounded at 10s per (document, holder), the whole-pass ack grace is 30s, and only the dial is
3698
+ // unbounded. Six pairs against a slow directory is already ~90s, so a 120s bound would warn on
3699
+ // the normal case — and a signal that fires when nothing is wrong teaches an operator to ignore
3700
+ // the one that matters. Scales with the tick so the fast-tick test path does not sit out minutes.
3701
+ const DOCUMENT_DELIVERY_PASS_TIMEOUT_MS = Math.max(180_000, DOCUMENT_DELIVERY_TICK_MS * 3);
3681
3702
  let documentDeliveryRunning = false;
3682
3703
  let documentDeliveryStopping = false;
3683
3704
  let documentDeliveryInFlight = null;
@@ -3691,6 +3712,7 @@ async function startDaemonHoldingLock(config, singletonLock) {
3691
3712
  documentDeliveryInFlight = (async () => {
3692
3713
  try {
3693
3714
  let agentsSwept = 0;
3715
+ let stuck = 0;
3694
3716
  let attempted = 0;
3695
3717
  let delivered = 0;
3696
3718
  let failed = 0;
@@ -3706,23 +3728,48 @@ async function startDaemonHoldingLock(config, singletonLock) {
3706
3728
  // and leaves a fully synced document invisible with no error on any path.
3707
3729
  if (ownerAgentId === null)
3708
3730
  continue;
3709
- agentsSwept++;
3731
+ // COUNTED AFTER THE PASS, not before — an agent whose pass never finished was not swept,
3732
+ // and saying it was is what made the stall indistinguishable from an idle daemon.
3710
3733
  // FANOUT-1: targets are the DERIVED holders minus ourselves — never per-sender
3711
3734
  // config, never the genesis peer column (§7-1's silent-divergence hazard).
3712
3735
  const holdersFor = (documentId) => {
3713
3736
  const holders = documentLayer.holdersFor(ownerAgentId, documentId);
3714
3737
  return holders === null ? null : holders.filter((h) => h !== ownerAgentId);
3715
3738
  };
3716
- const result = await documentDeliveryFor(agentName).tick(ownerAgentId, holdersFor, Date.now(), {
3717
- senderAgentId: ownerAgentId,
3739
+ // DOD-MP-SWEEP-ALIVE-1 BOUNDED. The `finally` below releases the overlap guard only
3740
+ // when this body settles, so an await that never settles ends document delivery for the
3741
+ // life of the process, silently: the interval keeps firing, returns at the guard, and
3742
+ // logs nothing. Observed live 2026-08-13. One wedged agent now degrades alone and loudly
3743
+ // instead of taking every other agent's documents down with it.
3744
+ const bounded = await sweepAgentEvicting({
3745
+ logger,
3746
+ timeoutMs: DOCUMENT_DELIVERY_PASS_TIMEOUT_MS,
3747
+ agentName,
3748
+ // Evicting the wedged worker is what makes the recovery real: `DocumentDelivery.tick`
3749
+ // caches its pass and would otherwise hand back the same hung promise forever, so the
3750
+ // agent would never deliver again AND every later pass would pay the full bound
3751
+ // waiting for it — a 2x sweep-interval regression for every healthy agent.
3752
+ workers: documentDeliveryWorkers,
3753
+ run: () => documentDeliveryFor(agentName).tick(ownerAgentId, holdersFor, Date.now(), {
3754
+ senderAgentId: ownerAgentId,
3755
+ }),
3718
3756
  });
3757
+ if (!bounded.completed) {
3758
+ stuck += 1;
3759
+ continue;
3760
+ }
3761
+ agentsSwept++;
3762
+ const result = bounded.value;
3719
3763
  attempted += result.attempted;
3720
3764
  delivered += result.delivered;
3721
3765
  failed += result.failed;
3722
3766
  }
3723
3767
  // H3: a sweep that delivers nothing was byte-for-byte identical to a healthy idle one, which
3724
3768
  // is what made every scoping bug above invisible. One line per tick, always.
3725
- logger.debug("document.delivery.sweep", { agents: agentsSwept, attempted, delivered, failed });
3769
+ // `stuck` IS PART OF THE LINE. Counting a wedged agent as swept reported `agents: 3,
3770
+ // attempted: 0` — byte-for-byte a healthy idle sweep, which is the exact shape the comment
3771
+ // below exists to prevent, on the one occasion it matters most.
3772
+ logger.debug("document.delivery.sweep", { agents: agentsSwept, stuck, attempted, delivered, failed });
3726
3773
  if (agentsSwept === 0 && documentLayer.store.anyDocumentExists()) {
3727
3774
  logger.warn("document.delivery.sweep_empty", {
3728
3775
  reason: "documents exist but no agent was swept — nothing will ever be delivered",