@cello-protocol/daemon 0.0.165 → 0.0.167

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 (44) hide show
  1. package/dist/daemon.d.ts.map +1 -1
  2. package/dist/daemon.js +40 -4
  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 +7 -0
  13. package/dist/document-control-notifier.d.ts.map +1 -1
  14. package/dist/document-control-notifier.js +4 -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 +5 -0
  21. package/dist/document-delivery.d.ts.map +1 -1
  22. package/dist/document-delivery.js +134 -0
  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 +170 -60
  26. package/dist/document-handlers.js.map +1 -1
  27. package/dist/document-layer.d.ts +1 -0
  28. package/dist/document-layer.d.ts.map +1 -1
  29. package/dist/document-lifecycle.d.ts +3 -0
  30. package/dist/document-lifecycle.d.ts.map +1 -1
  31. package/dist/document-lifecycle.js +4 -0
  32. package/dist/document-lifecycle.js.map +1 -1
  33. package/dist/document-store.d.ts +63 -0
  34. package/dist/document-store.d.ts.map +1 -1
  35. package/dist/document-store.js +201 -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 +70 -27
  39. package/dist/session-node-manager.js.map +1 -1
  40. package/dist/session-terminal-refusal.d.ts +62 -0
  41. package/dist/session-terminal-refusal.d.ts.map +1 -0
  42. package/dist/session-terminal-refusal.js +66 -0
  43. package/dist/session-terminal-refusal.js.map +1 -0
  44. package/package.json +3 -3
@@ -0,0 +1,62 @@
1
+ /**
2
+ * DOD-MP-SESSION-RETIRE-1 — what this daemon DOES when the relay says a session is over.
3
+ *
4
+ * ── WHY THIS IS A MODULE AND NOT A BRANCH IN `sendContent` ────────────────────────────────────
5
+ *
6
+ * It was a branch, and that is precisely why it knew without acting. Reaching it requires a live
7
+ * `#activeNodes` entry holding a real relay client and a real session node — state no unit test can
8
+ * construct — so nothing could assert the response, and the response was: log it, refuse the send,
9
+ * and leave the local row saying `active`.
10
+ *
11
+ * The seam that has to be substituted to test this is the RELAY'S ANSWER. The thing under test is
12
+ * what we do about it. Keeping them in one function meant you could not have the second without
13
+ * faking the first, which is the shape that makes a test agree with whatever the code does.
14
+ *
15
+ * ── WHY RETIRING THE ROW IS THE WHOLE POINT ───────────────────────────────────────────────────
16
+ *
17
+ * The relay pushes `session_sealed` exactly once. A daemon that is down or restarting at that
18
+ * instant never records it, so this side keeps a row saying `active` for a session the relay has
19
+ * finished. Everything that picks a session by local status then picks THAT one, forever:
20
+ * `activeSessionsWith` filters on `status === "active"` and only opens a fresh session when nothing
21
+ * matches. So a stale row is not merely stale — it is a permanent block on ever opening the
22
+ * replacement.
23
+ *
24
+ * Observed live 2026-08-13: the document delivery worker resubmitted the same sealed session every
25
+ * 60 seconds, was told terminally each time, and the row survived `cello logout && cello login`
26
+ * because it is persisted. Nothing recovered on its own, and the only symptom was a pending count
27
+ * that never fell.
28
+ *
29
+ * The CONVERSATION path already handles this by telling a human to start a new session, and the
30
+ * guidance below still says so because that advice is right for a human. **Document delivery has no
31
+ * human in the loop**, so the daemon must act for them — availability and fallback are first-class
32
+ * protocol concerns, and a route whose only session is dead has no fallback at all.
33
+ *
34
+ * Marking it sealed is also simply TRUE. The relay is the authority on whether it will witness
35
+ * again, and this message is the relay saying no.
36
+ */
37
+ export interface TerminalRefusalDeps {
38
+ logger: {
39
+ error(event: string, ctx: Record<string, unknown>): void;
40
+ };
41
+ /**
42
+ * Retire the local session row. REQUIRED — a no-op default would restore the exact defect this
43
+ * module exists to remove, and it would do it silently.
44
+ */
45
+ retireSession(sessionId: string): void;
46
+ }
47
+ export interface TerminalRefusalInput {
48
+ sessionId: string;
49
+ /** The relay's own reason — `session_sealed` or `session_not_found`. */
50
+ reason: string;
51
+ correlationId: string | undefined;
52
+ }
53
+ export interface TerminalRefusalResult {
54
+ ok: false;
55
+ reason: string;
56
+ error: string;
57
+ /** Never durable: there is no later attempt that can succeed on this session. */
58
+ durable: false;
59
+ guidance: string;
60
+ }
61
+ export declare function terminalRelayRefusal(deps: TerminalRefusalDeps, input: TerminalRefusalInput): TerminalRefusalResult;
62
+ //# sourceMappingURL=session-terminal-refusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-terminal-refusal.d.ts","sourceRoot":"","sources":["../src/session-terminal-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;IACrE;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,OAAO,EAAE,KAAK,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,oBAAoB,GAC1B,qBAAqB,CA8BvB"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * DOD-MP-SESSION-RETIRE-1 — what this daemon DOES when the relay says a session is over.
3
+ *
4
+ * ── WHY THIS IS A MODULE AND NOT A BRANCH IN `sendContent` ────────────────────────────────────
5
+ *
6
+ * It was a branch, and that is precisely why it knew without acting. Reaching it requires a live
7
+ * `#activeNodes` entry holding a real relay client and a real session node — state no unit test can
8
+ * construct — so nothing could assert the response, and the response was: log it, refuse the send,
9
+ * and leave the local row saying `active`.
10
+ *
11
+ * The seam that has to be substituted to test this is the RELAY'S ANSWER. The thing under test is
12
+ * what we do about it. Keeping them in one function meant you could not have the second without
13
+ * faking the first, which is the shape that makes a test agree with whatever the code does.
14
+ *
15
+ * ── WHY RETIRING THE ROW IS THE WHOLE POINT ───────────────────────────────────────────────────
16
+ *
17
+ * The relay pushes `session_sealed` exactly once. A daemon that is down or restarting at that
18
+ * instant never records it, so this side keeps a row saying `active` for a session the relay has
19
+ * finished. Everything that picks a session by local status then picks THAT one, forever:
20
+ * `activeSessionsWith` filters on `status === "active"` and only opens a fresh session when nothing
21
+ * matches. So a stale row is not merely stale — it is a permanent block on ever opening the
22
+ * replacement.
23
+ *
24
+ * Observed live 2026-08-13: the document delivery worker resubmitted the same sealed session every
25
+ * 60 seconds, was told terminally each time, and the row survived `cello logout && cello login`
26
+ * because it is persisted. Nothing recovered on its own, and the only symptom was a pending count
27
+ * that never fell.
28
+ *
29
+ * The CONVERSATION path already handles this by telling a human to start a new session, and the
30
+ * guidance below still says so because that advice is right for a human. **Document delivery has no
31
+ * human in the loop**, so the daemon must act for them — availability and fallback are first-class
32
+ * protocol concerns, and a route whose only session is dead has no fallback at all.
33
+ *
34
+ * Marking it sealed is also simply TRUE. The relay is the authority on whether it will witness
35
+ * again, and this message is the relay saying no.
36
+ */
37
+ export function terminalRelayRefusal(deps, input) {
38
+ // RETIRE FIRST, THEN REPORT — and the code now matches the comment, which it did not. The logger
39
+ // is an injected dependency and can throw; logging first meant a throw there left the row live,
40
+ // which is the pre-fix state exactly. The retirement is the load-bearing act, so it goes first.
41
+ deps.retireSession(input.sessionId);
42
+ deps.logger.error("session.relay.hash.submit.terminal", {
43
+ sessionId: input.sessionId,
44
+ reason: input.reason,
45
+ correlationId: input.correlationId,
46
+ impact: "the relay has ended this session — nothing sent now can ever be part of its record",
47
+ });
48
+ return {
49
+ ok: false,
50
+ reason: input.reason,
51
+ error: input.reason === "session_sealed"
52
+ ? "the relay has already sealed this session, so nothing further can enter its record"
53
+ : "the relay no longer holds this session, so nothing further can enter its record",
54
+ durable: false,
55
+ // WORDED FOR BOTH CALLERS. `sendContent` is shared with `cello_send`, so a human reads this
56
+ // too — and "the next send will start a fresh one" is true only for the document worker, which
57
+ // opens sessions on its own. A person has to start one. Saying otherwise would leave them
58
+ // waiting for an automatic recovery that never comes, which is the same wrong-audience mistake
59
+ // as telling someone to wait out a fault on their own machine.
60
+ guidance: `This session is over as far as the relay is concerned, and anything sent now would be ` +
61
+ `invisible to the receipt. Nothing was sent. This daemon has now retired the session on its ` +
62
+ `side, so nothing is stuck on it: document delivery will open a fresh session by itself, and ` +
63
+ `a conversation needs you to start a new one. The conversation so far is not lost.`,
64
+ };
65
+ }
66
+ //# sourceMappingURL=session-terminal-refusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-terminal-refusal.js","sourceRoot":"","sources":["../src/session-terminal-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AA2BH,MAAM,UAAU,oBAAoB,CAClC,IAAyB,EACzB,KAA2B;IAE3B,iGAAiG;IACjG,gGAAgG;IAChG,gGAAgG;IAChG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE;QACtD,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,MAAM,EAAE,oFAAoF;KAC7F,CAAC,CAAC;IACH,OAAO;QACL,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EACH,KAAK,CAAC,MAAM,KAAK,gBAAgB;YAC/B,CAAC,CAAC,oFAAoF;YACtF,CAAC,CAAC,iFAAiF;QACvF,OAAO,EAAE,KAAK;QACd,4FAA4F;QAC5F,+FAA+F;QAC/F,0FAA0F;QAC1F,+FAA+F;QAC/F,+DAA+D;QAC/D,QAAQ,EACN,wFAAwF;YACxF,6FAA6F;YAC7F,8FAA8F;YAC9F,mFAAmF;KACtF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cello-protocol/daemon",
3
- "version": "0.0.165",
3
+ "version": "0.0.167",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,9 +36,9 @@
36
36
  "cbor-x": "^1.6.0",
37
37
  "it-length-prefixed": "^10.0.1",
38
38
  "@cello-protocol/crypto": "0.0.50",
39
+ "@cello-protocol/gateway": "0.0.34",
39
40
  "@cello-protocol/protocol-types": "0.0.54",
40
- "@cello-protocol/transport": "0.0.56",
41
- "@cello-protocol/gateway": "0.0.34"
41
+ "@cello-protocol/transport": "0.0.56"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^25.6.2",