@cello-protocol/daemon 0.0.211 → 0.0.212

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 (180) hide show
  1. package/dist/agent-admin-handlers.d.ts.map +1 -1
  2. package/dist/agent-admin-handlers.js +3 -0
  3. package/dist/agent-admin-handlers.js.map +1 -1
  4. package/dist/agent-handlers.d.ts.map +1 -1
  5. package/dist/agent-handlers.js +44 -0
  6. package/dist/agent-handlers.js.map +1 -1
  7. package/dist/agent-id-migration.d.ts.map +1 -1
  8. package/dist/agent-id-migration.js +11 -2
  9. package/dist/agent-id-migration.js.map +1 -1
  10. package/dist/assignment-verify.d.ts.map +1 -1
  11. package/dist/assignment-verify.js +2 -2
  12. package/dist/assignment-verify.js.map +1 -1
  13. package/dist/attendance-wiring.d.ts +2 -21
  14. package/dist/attendance-wiring.d.ts.map +1 -1
  15. package/dist/attendance-wiring.js +57 -251
  16. package/dist/attendance-wiring.js.map +1 -1
  17. package/dist/away-detection.d.ts +16 -21
  18. package/dist/away-detection.d.ts.map +1 -1
  19. package/dist/away-detection.js +34 -23
  20. package/dist/away-detection.js.map +1 -1
  21. package/dist/boot-agents.d.ts.map +1 -1
  22. package/dist/close-session-handler.d.ts.map +1 -1
  23. package/dist/close-session-handler.js +47 -1
  24. package/dist/close-session-handler.js.map +1 -1
  25. package/dist/co-attendance.d.ts +1 -1
  26. package/dist/co-attendance.js +1 -1
  27. package/dist/consortium-fingerprint.d.ts +115 -0
  28. package/dist/consortium-fingerprint.d.ts.map +1 -0
  29. package/dist/consortium-fingerprint.js +175 -0
  30. package/dist/consortium-fingerprint.js.map +1 -0
  31. package/dist/content-park.d.ts.map +1 -1
  32. package/dist/content-park.js +80 -0
  33. package/dist/content-park.js.map +1 -1
  34. package/dist/daemon-handle.d.ts +1 -1
  35. package/dist/daemon-handle.d.ts.map +1 -1
  36. package/dist/daemon-status-report.d.ts +10 -2
  37. package/dist/daemon-status-report.d.ts.map +1 -1
  38. package/dist/daemon-status-report.js +6 -3
  39. package/dist/daemon-status-report.js.map +1 -1
  40. package/dist/daemon.d.ts.map +1 -1
  41. package/dist/daemon.js +41 -13
  42. package/dist/daemon.js.map +1 -1
  43. package/dist/directory-bootstrap.d.ts +3 -1
  44. package/dist/directory-bootstrap.d.ts.map +1 -1
  45. package/dist/directory-bootstrap.js +3 -1
  46. package/dist/directory-bootstrap.js.map +1 -1
  47. package/dist/directory-connect.d.ts.map +1 -1
  48. package/dist/directory-connect.js +10 -2
  49. package/dist/directory-connect.js.map +1 -1
  50. package/dist/disconnect-cleanup.d.ts +7 -0
  51. package/dist/disconnect-cleanup.d.ts.map +1 -1
  52. package/dist/disconnect-cleanup.js +16 -1
  53. package/dist/disconnect-cleanup.js.map +1 -1
  54. package/dist/document-notify.d.ts +0 -1
  55. package/dist/document-notify.d.ts.map +1 -1
  56. package/dist/document-notify.js +0 -1
  57. package/dist/document-notify.js.map +1 -1
  58. package/dist/document-store.d.ts +0 -1
  59. package/dist/document-store.d.ts.map +1 -1
  60. package/dist/document-store.js +0 -1
  61. package/dist/document-store.js.map +1 -1
  62. package/dist/document-write-path.d.ts.map +1 -1
  63. package/dist/document-write-path.js +2 -9
  64. package/dist/document-write-path.js.map +1 -1
  65. package/dist/inbound-sessions.d.ts +3 -1
  66. package/dist/inbound-sessions.d.ts.map +1 -1
  67. package/dist/inbound-sessions.js +12 -4
  68. package/dist/inbound-sessions.js.map +1 -1
  69. package/dist/index.d.ts +3 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +7 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/initiate-session-handler.d.ts.map +1 -1
  74. package/dist/initiate-session-handler.js +5 -1
  75. package/dist/initiate-session-handler.js.map +1 -1
  76. package/dist/park-envelope.d.ts +68 -0
  77. package/dist/park-envelope.d.ts.map +1 -1
  78. package/dist/park-envelope.js +86 -1
  79. package/dist/park-envelope.js.map +1 -1
  80. package/dist/park-recovery.d.ts +46 -0
  81. package/dist/park-recovery.d.ts.map +1 -1
  82. package/dist/park-recovery.js +100 -1
  83. package/dist/park-recovery.js.map +1 -1
  84. package/dist/relay-only.d.ts.map +1 -1
  85. package/dist/relay-only.js +7 -4
  86. package/dist/relay-only.js.map +1 -1
  87. package/dist/relay-receipt-store.d.ts +48 -17
  88. package/dist/relay-receipt-store.d.ts.map +1 -1
  89. package/dist/relay-receipt-store.js +77 -27
  90. package/dist/relay-receipt-store.js.map +1 -1
  91. package/dist/seal-escalation.d.ts.map +1 -1
  92. package/dist/seal-escalation.js +25 -0
  93. package/dist/seal-escalation.js.map +1 -1
  94. package/dist/seal-settle.d.ts +38 -0
  95. package/dist/seal-settle.d.ts.map +1 -0
  96. package/dist/seal-settle.js +33 -0
  97. package/dist/seal-settle.js.map +1 -0
  98. package/dist/session-assignment-parser.d.ts.map +1 -1
  99. package/dist/session-assignment-parser.js +5 -0
  100. package/dist/session-assignment-parser.js.map +1 -1
  101. package/dist/session-ceremony.d.ts +21 -14
  102. package/dist/session-ceremony.d.ts.map +1 -1
  103. package/dist/session-ceremony.js +25 -15
  104. package/dist/session-ceremony.js.map +1 -1
  105. package/dist/session-content-context.d.ts +11 -2
  106. package/dist/session-content-context.d.ts.map +1 -1
  107. package/dist/session-content-ingest.d.ts +0 -3
  108. package/dist/session-content-ingest.d.ts.map +1 -1
  109. package/dist/session-content-ingest.js +18 -111
  110. package/dist/session-content-ingest.js.map +1 -1
  111. package/dist/session-content-send.d.ts +5 -0
  112. package/dist/session-content-send.d.ts.map +1 -1
  113. package/dist/session-content-send.js +58 -0
  114. package/dist/session-content-send.js.map +1 -1
  115. package/dist/session-delivery-acks.d.ts +195 -0
  116. package/dist/session-delivery-acks.d.ts.map +1 -0
  117. package/dist/session-delivery-acks.js +674 -0
  118. package/dist/session-delivery-acks.js.map +1 -0
  119. package/dist/session-leaf-records.d.ts +19 -1
  120. package/dist/session-leaf-records.d.ts.map +1 -1
  121. package/dist/session-leaf-records.js +67 -1
  122. package/dist/session-leaf-records.js.map +1 -1
  123. package/dist/session-lifecycle.js +3 -3
  124. package/dist/session-lifecycle.js.map +1 -1
  125. package/dist/session-node-manager.d.ts +36 -21
  126. package/dist/session-node-manager.d.ts.map +1 -1
  127. package/dist/session-node-manager.js +99 -29
  128. package/dist/session-node-manager.js.map +1 -1
  129. package/dist/session-node-types.d.ts +18 -12
  130. package/dist/session-node-types.d.ts.map +1 -1
  131. package/dist/session-node-types.js +1 -9
  132. package/dist/session-node-types.js.map +1 -1
  133. package/dist/session-read-handlers.d.ts.map +1 -1
  134. package/dist/session-read-handlers.js +125 -4
  135. package/dist/session-read-handlers.js.map +1 -1
  136. package/dist/session-records.d.ts +17 -0
  137. package/dist/session-records.d.ts.map +1 -1
  138. package/dist/session-records.js +32 -0
  139. package/dist/session-records.js.map +1 -1
  140. package/dist/session-relay-client.d.ts +103 -6
  141. package/dist/session-relay-client.d.ts.map +1 -1
  142. package/dist/session-relay-client.js +369 -27
  143. package/dist/session-relay-client.js.map +1 -1
  144. package/dist/session-relay.d.ts +24 -2
  145. package/dist/session-relay.d.ts.map +1 -1
  146. package/dist/session-relay.js +21 -6
  147. package/dist/session-relay.js.map +1 -1
  148. package/dist/session-schema.d.ts.map +1 -1
  149. package/dist/session-schema.js +43 -0
  150. package/dist/session-schema.js.map +1 -1
  151. package/dist/session-seal-leaf-store.d.ts +6 -0
  152. package/dist/session-seal-leaf-store.d.ts.map +1 -1
  153. package/dist/session-seal-leaf-store.js +21 -4
  154. package/dist/session-seal-leaf-store.js.map +1 -1
  155. package/dist/session-seal.d.ts +12 -0
  156. package/dist/session-seal.d.ts.map +1 -1
  157. package/dist/session-seal.js +92 -3
  158. package/dist/session-seal.js.map +1 -1
  159. package/dist/session-views.d.ts +1 -0
  160. package/dist/session-views.d.ts.map +1 -1
  161. package/dist/session-views.js +81 -1
  162. package/dist/session-views.js.map +1 -1
  163. package/dist/signaling-connect.d.ts +1 -0
  164. package/dist/signaling-connect.d.ts.map +1 -1
  165. package/dist/signaling-connect.js +0 -1
  166. package/dist/signaling-connect.js.map +1 -1
  167. package/dist/status-handler.d.ts +9 -1
  168. package/dist/status-handler.d.ts.map +1 -1
  169. package/dist/status-handler.js +5 -2
  170. package/dist/status-handler.js.map +1 -1
  171. package/dist/test-handlers.js +4 -3
  172. package/dist/test-handlers.js.map +1 -1
  173. package/dist/trust-signal-store.d.ts +4 -2
  174. package/dist/trust-signal-store.d.ts.map +1 -1
  175. package/dist/trust-signal-store.js +4 -2
  176. package/dist/trust-signal-store.js.map +1 -1
  177. package/dist/types.d.ts +64 -0
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/types.js.map +1 -1
  180. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"session-relay-client.d.ts","sourceRoot":"","sources":["../src/session-relay-client.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGzE,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAC1C,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AACvD,iFAAiF;AACjF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAgBtD,CAAC;AAEH,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;IACzB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+CD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1E,gBAAgB,CA8ElB;AAED,eAAO,MAAM,aAAa,IAAO,CAAC;AAClC,iGAAiG;AACjG,eAAO,MAAM,cAAc,IAAO,CAAC;AACnC,uFAAuF;AACvF,eAAO,MAAM,aAAa,IAAO,CAAC;AAClC,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,IAAO,CAAC;AAKrC;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAIvF;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,UAAU,CAAC;IAC5B,eAAe,EAAE,UAAU,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,gDAAgD;AAChD,MAAM,MAAM,YAAY,GACpB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,eAAe,EAAE,MAAM,CAAC;IAKxB,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,UAAU,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAGN,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,sEAAsE;IACtE,WAAW,EAAE,WAAW,CAAC;IACzB,iFAAiF;IACjF,YAAY,EAAE,UAAU,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,MAAM,UAAU,GAAG,SAAS,CAAC;IAC3C;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,MAAM,EAAE,oCAAoC,GAAG,gCAAgC,CAAC;IAChF,oGAAoG;IACpG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAqBD;;;;;;GAMG;AACH;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,cAAc,EAAE,UAAU,CAAC;IAC3B,oEAAoE;IACpE,eAAe,EAAE,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,UAAU,CAAC;IACzB,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,mBAAmB,EAAE,UAAU,CAAC;CACjC;AAsDD;;;;GAIG;AACH,qBAAa,gBAAgB;;IAO3B,4EAA4E;IAC5E,kBAAkB,IAAI,MAAM,GAAG,IAAI;IA2DnC,+FAA+F;IAC/F,kBAAkB,IAAI,gBAAgB,GAAG,IAAI;gBA+FjC,IAAI,EAAE,oBAAoB;IA8BtC,2FAA2F;IAC3F,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;;;OAIG;IACH,eAAe,CACb,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,SAAS,EACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,EACjD,UAAU,CAAC,EAAE,oBAAoB;IACjC;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,UAAU,GAC3B,IAAI;IAoCP;;;;;;;;;;;;;;;;;OAiBG;IACG,uBAAuB,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgFtF,0EAA0E;IAC1E,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAM7C,WAAW,IAAI,OAAO;IAItB;;;;;;;;OAQG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAkiBzC;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAIhD;;;;;;;;;;;;;;;;;OAiBG;IACH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IA0JzD;;;;;;;;;;;;;;;;;;OAkBG;IACG,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IA6T3D;;;;;;;;;OASG;IACG,iBAAiB,CACrB,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,UAAU;IACvB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC;IAQxB;;;;;;;;;;;;;;;;;;OAkBG;IACG,mBAAmB,CACvB,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,YAAY,CAAC;IAIxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACG,UAAU,CACd,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,UAAU,GAAG,IAAI;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC;IAmjBxB,yFAAyF;IACzF;;;;;;;;;;;;;;;;;;;OAmBG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IAIvF;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS;IAIhF;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAMzD;;;;;;OAMG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IAKhE,KAAK,IAAI,IAAI;CAWd"}
1
+ {"version":3,"file":"session-relay-client.d.ts","sourceRoot":"","sources":["../src/session-relay-client.ts"],"names":[],"mappings":"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"}
@@ -32,9 +32,28 @@ import { createHash } from "node:crypto";
32
32
  import * as lp from "it-length-prefixed";
33
33
  import { decode } from "cbor-x";
34
34
  import { encodeCbor, decodeSealPayload, encodeStructure1, decodeStructure1, computeGenesisPrevRoot } from "@cello-protocol/protocol-types";
35
+ import { encodeSessionLivenessQuery, decodeSessionLivenessResponse, encodeSessionAttendanceNotice, } from "@cello-protocol/protocol-types";
35
36
  import { verify } from "@cello-protocol/crypto";
36
37
  import { extractErrorMessage } from "./error-message.js";
37
38
  import { evaluateRelayAck } from "./relay-receipt-store.js";
39
+ /**
40
+ * DOD-M15-AWAYSCOPE-1 — what a liveness query answers with.
41
+ *
42
+ * `refused` is kept separate from the values rather than folded into `liveness`, because it means
43
+ * something different: the relay declined to say, most likely because this caller is not a
44
+ * participant of the session it named. The liveness is 'unknown' either way — the operator surface
45
+ * shows the same thing — but a caller that wants to tell "the relay would not tell me" from "the
46
+ * relay does not know" can, and a log line that conflated them would send the next investigation
47
+ * looking at the counterparty instead of at the session id.
48
+ */
49
+ /**
50
+ * The key an answer is matched to its question by: the two fields the RESPONSE carries back. One
51
+ * relay client serves every session an agent holds there, so two concurrent queries on one stream
52
+ * is ordinary — and an answer routed to whatever was asked last shows the wrong counterparty's row.
53
+ */
54
+ function livenessKey(sessionId, counterpartyPubkey) {
55
+ return `${Buffer.from(sessionId).toString("hex")}:${Buffer.from(counterpartyPubkey).toString("hex")}`;
56
+ }
38
57
  export const RELAY_PROTOCOL_ID = "/cello/relay/1.0.0";
39
58
  /**
40
59
  * How long to wait before re-proving after a transport-class failure.
@@ -47,7 +66,6 @@ export const RELAY_PROTOCOL_ID = "/cello/relay/1.0.0";
47
66
  */
48
67
  export const PROOF_RETRY_DELAY_MS = 1_000;
49
68
  export const RELAY_AUTH_DOMAIN = "CELLO-RELAY-AUTH-v1";
50
- /** Structure 1 leaf kind: 0x00 = message, 0x02 = control (matches the relay). */
51
69
  /**
52
70
  * DOD-WITNESS-STALL-1 — relay refusals that can NEVER resolve.
53
71
  *
@@ -218,6 +236,7 @@ export function classifyRelayAuthRefusal(reason, extra = {}) {
218
236
  ...(extra.retryAfterMs !== undefined ? { retryAfterMs: extra.retryAfterMs } : {}),
219
237
  };
220
238
  }
239
+ /** Structure 1 leaf kind — these values match the relay's. Message leaf (the `cello_send` default). */
221
240
  export const LEAF_KIND_MSG = 0x00;
222
241
  /** Control leaf (SEAL etc.) — two distinct-sender ctrl leaves trigger directory notarization. */
223
242
  export const LEAF_KIND_CTRL = 0x02;
@@ -384,9 +403,9 @@ export class AgentRelayClient {
384
403
  /**
385
404
  * PER-SESSION acknowledgement state (session_id hex → the position AND the content at it).
386
405
  *
387
- * `seq` is the highest relay-assigned sequence. The relay's `seq_counter` is per session, and it
388
- * rejects `last_seen_seq > seq_counter`, so each session's submit MUST carry that session's own
389
- * high-water mark — NOT an agent-global one (which would make a newer session's first submit look
406
+ * `seq` is the highest relay-assigned sequence, advanced by an ack or a deliver. The relay's
407
+ * `seq_counter` is per session and it rejects `last_seen_seq > seq_counter`, so each session's
408
+ * submit MUST carry that session's own high-water mark — NOT an agent-global one (which would make a newer session's first submit look
390
409
  * ahead and get rejected).
391
410
  *
392
411
  * ⚠️ `hash` IS THE SAME FACT AS `seq`, WHICH IS WHY THEY LIVE IN ONE ENTRY — 033-ACKEMIT.
@@ -431,6 +450,18 @@ export class AgentRelayClient {
431
450
  * serialized on the shared stream, exactly like `#pendingRecord`, whose ack also carries no id.
432
451
  */
433
452
  #pendingRelease = null;
453
+ /**
454
+ * DOD-M15-AWAYSCOPE-1 — in-flight liveness queries, keyed `sessionIdHex:counterpartyHex`.
455
+ *
456
+ * ⚠️ A MAP, NOT A SLOT, and the first version was a slot. One `AgentRelayClient` serves EVERY
457
+ * session an agent holds on a relay and both callers fan out with `Promise.all`, so `cello status`
458
+ * with two open conversations fired two queries on one stream: the second overwrote the first's
459
+ * resolver, the first answer resolved the WRONG session's promise, and the other waited the full
460
+ * submit timeout — blowing the caller's budget so every row came back unenriched. With more than
461
+ * one session the feature did nothing, and when it did something it could show the wrong
462
+ * counterparty. The key is what the RESPONSE carries, so an answer is matched to its question.
463
+ */
464
+ #pendingLiveness = new Map();
434
465
  /** Serializes submits so only one is in flight at a time across all sessions. */
435
466
  #submitChain = Promise.resolve();
436
467
  /** session_id hex → { the live node to (re)dial from, inbound leaf handler, Option-B assignment to present }. */
@@ -488,14 +519,28 @@ export class AgentRelayClient {
488
519
  * point has nothing for them to anchor to and every submit on it is refused by name. Registering
489
520
  * without a seed is therefore a real fault, not a degraded mode.
490
521
  */
491
- genesisPrevRoot) {
522
+ genesisPrevRoot,
523
+ /**
524
+ * 069-ORDERPROOF — the relay key this session's ordering attestations verify under, from
525
+ * `relay_id` on the directory-signed assignment. Supplied by the caller because
526
+ * `session-node-manager` is where the durable copy lives, exactly as with the genesis above.
527
+ *
528
+ * ⚠️ ABSENT IS NOT A DEGRADED MODE. A session with no anchor refuses every attestation that
529
+ * arrives on it, by name, because the only alternative is checking a signature against a key
530
+ * the signer handed us.
531
+ */
532
+ relayAnchorHex) {
492
533
  const existing = this.#sessions.get(sessionIdHex);
493
534
  const carriedAssignment = assignment ?? existing?.assignment;
535
+ // The anchor is carried forward for the same reason the assignment is: a re-registration with
536
+ // nothing in hand must not silently strip a session of the key it verifies against.
537
+ const carriedAnchor = relayAnchorHex ?? carriedAssignment?.relayPubkeyHex ?? existing?.relayAnchorHex;
494
538
  this.#sessions.set(sessionIdHex, {
495
539
  node,
496
540
  onLeafDeliver: onLeafDeliver ?? (() => { }),
497
541
  // Carry the assignment forward across re-registration; never lose a recorded flag on re-register.
498
542
  assignment: carriedAssignment,
543
+ ...(carriedAnchor ? { relayAnchorHex: carriedAnchor } : {}),
499
544
  recorded: existing?.recorded ?? false,
500
545
  recordRejected: existing?.recordRejected ?? false,
501
546
  recordTimedOut: existing?.recordTimedOut ?? false,
@@ -560,6 +605,10 @@ export class AgentRelayClient {
560
605
  const sess = this.#sessions.get(sessionIdHex);
561
606
  if (!sess || !sess.assignment)
562
607
  return true;
608
+ // Nothing to PRESENT. The carry may exist only to name the relay this session's attestations
609
+ // verify against; there is no frame to send and nothing to wait for, so the submit proceeds.
610
+ if (!sess.assignment.assignmentSignature)
611
+ return true;
563
612
  if (sess.recorded)
564
613
  return true;
565
614
  // Terminal rejection: a relay that cleanly rejected this assignment will reject it
@@ -724,11 +773,25 @@ export class AgentRelayClient {
724
773
  return Buffer.from(s1.fields.senderPubkey).equals(Buffer.from(this.#senderPubkey));
725
774
  }
726
775
  /**
727
- * Verify a relay `hash_submit_ack`'s signed ordering record and durably store the receipt.
728
- * The relay signs TBS = SHA-256(content_hash || seq_BE4 || ts_BE8) with its ack-signing key, whose hex is
729
- * `relay_id`. We verify SELF-CONSISTENCY (the signature binds the sequence ⇒ a FORGED sequence fails) and
730
- * record the immutable receipt. The authoritative registered-relay check is the directory's at seal
731
- * (OPTIONB-SEAL). No-op when there is no receipt store, no pending leaf, or the ACK is unsigned.
776
+ * The relay pubkey the DIRECTORY named for a session — 069-ORDERPROOF, and the only key a relay
777
+ * ordering attestation is ever checked against.
778
+ *
779
+ * It rides the assignment carry, which is built from an assignment whose FROST signature this
780
+ * daemon verified before the session began, so it is anchored to something the relay does not
781
+ * control. `undefined` means no such key was recorded, and every attestation on that session is
782
+ * then refused for want of an anchor — never accepted against whatever key the frame supplies.
783
+ */
784
+ #expectedRelayPubkeyHex(sessionIdHex) {
785
+ return this.#sessions.get(sessionIdHex)?.relayAnchorHex;
786
+ }
787
+ /**
788
+ * Verify a relay `hash_submit_ack`'s ordering attestation and durably store the receipt.
789
+ *
790
+ * The relay signs the session, the content hash, the position it assigned and the running root of
791
+ * the tree after this leaf. We verify it **against the relay key on the directory-signed
792
+ * assignment**, and refuse otherwise — missing, malformed and mismatched all take the one path.
793
+ *
794
+ * Returns true to REJECT the submit. A send must not settle ok on a position nothing witnessed.
732
795
  */
733
796
  #captureReceipt(frame, structure1Cbor, seq) {
734
797
  if (seq < 0 || !structure1Cbor)
@@ -746,30 +809,40 @@ export class AgentRelayClient {
746
809
  }
747
810
  const contentHash = s1.fields.contentHash;
748
811
  const sessionId = s1.fields.sessionId;
812
+ const sessionIdHex = Buffer.from(sessionId).toString("hex");
749
813
  const ev = evaluateRelayAck({
814
+ sessionId,
750
815
  contentHash,
751
- sessionIdHex: Buffer.from(sessionId).toString("hex"),
816
+ runningRoot: frame["running_root"] instanceof Uint8Array ? frame["running_root"] : undefined,
817
+ sessionIdHex,
752
818
  agentPubkeyHex: this.senderPubkeyHex,
819
+ expectedRelayPubkeyHex: this.#expectedRelayPubkeyHex(sessionIdHex),
753
820
  relayId: typeof frame["relay_id"] === "string" ? frame["relay_id"] : undefined,
754
821
  relaySignature: frame["relay_signature"] instanceof Uint8Array ? frame["relay_signature"] : undefined,
755
822
  timestamp: typeof frame["timestamp"] === "number" ? frame["timestamp"] : undefined,
756
823
  sequenceNumber: seq,
757
824
  });
758
825
  switch (ev.kind) {
759
- case "unsigned":
760
- // A relay that SHOULD sign but didn't → no durable witness for this message. Unsigned ACKs are
761
- // tolerated, but make it diagnosable instead of invisible.
762
- this.#logger.debug("relay.receipt.unsigned", { seq, hashShort: Buffer.from(contentHash).toString("hex").slice(0, 16) });
763
- return false;
764
- case "bad_relay_id":
765
- this.#logger.warn("relay.receipt.bad_relay_id", { seq });
766
- return false;
767
- case "invalid_signature":
768
- // FORGED / corrupt ACK — the signature does not bind (hash, seq, ts). REJECT the submit so the send
769
- // does NOT settle ok on an unverified sequence (a forged ordering record must not drive ordering),
770
- // and store nothing. The send still completes via the direct
771
- // content path — the relay witness simply degrades to absent for this leaf.
772
- this.#logger.warn("relay.receipt.signature_invalid", { seq });
826
+ case "refused":
827
+ /**
828
+ * ONE OUTCOME, and the cause is in the log rather than in the control flow.
829
+ *
830
+ * The submit is rejected for all five causes. A message whose position no assigned relay
831
+ * attested has no ordering evidence behind it, and letting the send settle ok would report
832
+ * a witnessed message that nothing witnessed. `attestation_absent` takes this path with the
833
+ * rest deliberately: the party who can produce the absence is the relay, which is the party
834
+ * the check exists to constrain, so a softer answer for absence is a mute button with our
835
+ * own name on it.
836
+ */
837
+ this.#logger.warn("relay.attestation.refused", {
838
+ seq,
839
+ session: sessionIdHex,
840
+ cause: ev.cause,
841
+ hashShort: Buffer.from(contentHash).toString("hex").slice(0, 16),
842
+ impact: "the relay's ordering attestation for this message was refused, so this send did not " +
843
+ "settle and no evidence was stored. This agent holds no proof of where this message " +
844
+ "sits in the conversation, and nothing was recorded that could later be presented as one.",
845
+ });
773
846
  return true;
774
847
  case "store": {
775
848
  if (!this.#receiptStore)
@@ -821,6 +894,7 @@ export class AgentRelayClient {
821
894
  relayId: ev.receipt.relayId,
822
895
  relayTimestamp: ev.receipt.timestamp,
823
896
  relaySignatureHex: ev.receipt.signatureHex,
897
+ ...(ev.receipt.runningRootHex ? { relayRunningRootHex: ev.receipt.runningRootHex } : {}),
824
898
  }
825
899
  : {}),
826
900
  }, Date.now());
@@ -857,11 +931,77 @@ export class AgentRelayClient {
857
931
  // signed-but-INVALID ACK rejects the submit so the send does not settle ok on an unverified sequence.
858
932
  const rejectSubmit = this.#captureReceipt(frame, structure1Cbor, seq);
859
933
  this.#settlePending(rejectSubmit
860
- ? { ok: false, reason: "relay_ack_signature_invalid" }
934
+ /**
935
+ * ONE REASON for every refusal cause — 069-ORDERPROOF Done When 3. A distinct reason per
936
+ * cause would be observable, and the clause requires that a missing attestation and a
937
+ * malformed one look the same from outside. The cause is named in the log line above.
938
+ */
939
+ ? { ok: false, reason: "relay_ack_unverified" }
861
940
  : seq >= 0
862
941
  ? { ok: true, sequence_number: seq, structure1_cbor: structure1Cbor, structure2_cbor: structure2Cbor, sender_signature: senderSignature }
863
942
  : { ok: false, reason: "relay_ack_malformed" });
864
943
  }
944
+ else if (type === "session_liveness_response" || type === "session_liveness_refused") {
945
+ /**
946
+ * DOD-M15-AWAYSCOPE-1 — the relay's answer to "is my counterparty reachable, and is anyone
947
+ * attending it". Decoded through the shared codec rather than read field by field here: that
948
+ * codec is what refuses an attendance paired with a non-alive liveness, and re-reading the
949
+ * fields inline would quietly reintroduce the tolerance branch it exists to prevent.
950
+ *
951
+ * A REFUSAL resolves the waiter rather than dropping it. The relay refuses a query naming a
952
+ * session the caller is not in, and it deliberately does not say which of the two reasons —
953
+ * so the honest local answer is "unknown", not a hang until the timeout.
954
+ */
955
+ /**
956
+ * ⚠️ A REFUSAL NAMES NOTHING, so it cannot be matched. The relay refuses without echoing the
957
+ * session or the subject — deliberately, because telling "no such session" from "not your
958
+ * session" is the enumeration signal `DOD-M15-RELAYAUTH-1` closed. So there is nothing to key
959
+ * on, and the only sound reading is that every outstanding query on this stream was refused:
960
+ * the refusal is a property of the CALLER, and the caller is the same for all of them.
961
+ */
962
+ if (type === "session_liveness_refused") {
963
+ for (const r of this.#pendingLiveness.values())
964
+ r({ liveness: "unknown", observedAt: 0, refused: true });
965
+ this.#pendingLiveness.clear();
966
+ }
967
+ else {
968
+ const decoded = decodeSessionLivenessResponse(encodeCbor(frame));
969
+ if (decoded === null) {
970
+ // Malformed fails exactly like missing. A frame this build cannot read is not evidence of
971
+ // anything, and reporting it as 'gone' would invent an observation nobody made. It also
972
+ // cannot be matched to a waiter, so every outstanding query is answered 'unknown' rather
973
+ // than left to time out on a stream that just proved it speaks a shape we cannot read.
974
+ this.#logger.warn("session.relay.liveness.response_malformed", {
975
+ relayPeerId: this.#relayPeerId,
976
+ impact: "the counterparty's reachability is reported as unknown, which is what it is",
977
+ });
978
+ for (const r of this.#pendingLiveness.values())
979
+ r({ liveness: "unknown", observedAt: 0 });
980
+ this.#pendingLiveness.clear();
981
+ }
982
+ else {
983
+ // MATCHED ON WHAT THE ANSWER ITSELF NAMES. Resolving the most recent waiter instead would
984
+ // be right exactly once — when only one query is outstanding — and silently wrong the
985
+ // moment an agent holds two conversations through one relay, which is ordinary.
986
+ const key = livenessKey(decoded.session_id, decoded.counterparty_pubkey);
987
+ const r = this.#pendingLiveness.get(key);
988
+ if (r) {
989
+ this.#pendingLiveness.delete(key);
990
+ r({
991
+ liveness: decoded.liveness,
992
+ observedAt: decoded.observed_at,
993
+ ...(decoded.attendance !== undefined ? { attendance: decoded.attendance } : {}),
994
+ ...(decoded.attendance_observed_at !== undefined ? { attendanceObservedAt: decoded.attendance_observed_at } : {}),
995
+ });
996
+ }
997
+ else {
998
+ // A late answer to a query that already timed out, or one nobody asked for. Dropped —
999
+ // handing it to a different session's waiter is exactly the defect this key exists for.
1000
+ this.#logger.debug("session.relay.liveness.response_unmatched", { relayPeerId: this.#relayPeerId });
1001
+ }
1002
+ }
1003
+ }
1004
+ }
865
1005
  else if (type === "hash_submit_error") {
866
1006
  const reason = typeof frame["reason"] === "string" ? frame["reason"] : "relay_rejected";
867
1007
  // Carry the relay's `detail` through — see `SubmitResult`. Reading the class and discarding
@@ -1046,6 +1186,69 @@ export class AgentRelayClient {
1046
1186
  this.#logger.warn("relay.seal_leaf.counterparty.capture_skipped", { seq, session: sidHex, hasS2: !!structure2Cbor, hasS1: s1.length > 0, hasSender: !!senderHex });
1047
1187
  }
1048
1188
  }
1189
+ /**
1190
+ * ─── THE RECIPIENT KEEPS THE ORDERING PROOF TOO — 069-ORDERPROOF unit 2 ──────────────────
1191
+ *
1192
+ * Before this, the relay's attestation went to the SENDER alone, on the submit
1193
+ * acknowledgement. The recipient held no proof of where a single message of the conversation
1194
+ * sat. That made one party's copy the only copy, which is the dependency this order exists to
1195
+ * remove wearing a different coat.
1196
+ *
1197
+ * It is verified here exactly as on the send path — against the relay key the DIRECTORY named
1198
+ * for this session, never `relay_id` on the frame — and an attestation that does not verify is
1199
+ * refused and NOT stored. Nothing else changes: the leaf is still delivered to the session, the
1200
+ * acknowledgement still advances, and no tree, root or leaf count is touched. This is evidence
1201
+ * recorded beside the chain, never in it.
1202
+ *
1203
+ * Our own echoed leaf comes through here too. Storing it is a no-op — the ack path already
1204
+ * wrote the same row at the same position, and the store is INSERT OR IGNORE — but it is not
1205
+ * skipped, because the echo is the only copy a leaf COUNTER-SUBMITTED by the counterparty
1206
+ * ever produces for us.
1207
+ */
1208
+ if (this.#receiptStore && seq >= 0 && deliveredS1.ok) {
1209
+ const ev = evaluateRelayAck({
1210
+ sessionId: deliveredS1.fields.sessionId,
1211
+ contentHash: deliveredS1.fields.contentHash,
1212
+ runningRoot: frame["running_root"] instanceof Uint8Array ? frame["running_root"] : undefined,
1213
+ sessionIdHex: sidHex,
1214
+ agentPubkeyHex: this.senderPubkeyHex,
1215
+ expectedRelayPubkeyHex: this.#expectedRelayPubkeyHex(sidHex),
1216
+ relayId: typeof frame["relay_id"] === "string" ? frame["relay_id"] : undefined,
1217
+ relaySignature: frame["relay_signature"] instanceof Uint8Array ? frame["relay_signature"] : undefined,
1218
+ timestamp: typeof frame["timestamp"] === "number" ? frame["timestamp"] : undefined,
1219
+ sequenceNumber: seq,
1220
+ });
1221
+ if (ev.kind === "refused") {
1222
+ /**
1223
+ * LOUD, AND THE DELIVERY STILL LANDS. This is the one place the two halves differ, and
1224
+ * deliberately: on the send path a refusal means OUR message has no witness, so the send
1225
+ * must not settle. Here the counterparty's message has already been written and read —
1226
+ * dropping it would let a relay silence a conversation by withholding its own signature,
1227
+ * which hands the party the check constrains a far better weapon than the one it takes
1228
+ * away. So the message arrives and the evidence is recorded as absent.
1229
+ */
1230
+ this.#logger.warn("relay.attestation.delivered.refused", {
1231
+ seq,
1232
+ session: sidHex,
1233
+ cause: ev.cause,
1234
+ impact: "this message arrived, but the relay's proof of WHERE it sits in the conversation did " +
1235
+ "not, so this agent cannot later show a third party that this message was ordered here. " +
1236
+ "The message itself is unaffected and is in the transcript.",
1237
+ });
1238
+ }
1239
+ else {
1240
+ try {
1241
+ this.#receiptStore.store(ev.receipt, Date.now());
1242
+ }
1243
+ catch (err) {
1244
+ // A durable-evidence write failure is LOUD: the relay never re-sends a delivery, so a
1245
+ // swallowed write permanently loses this side's only copy of the ordering proof.
1246
+ this.#logger.error("relay.attestation.delivered.store_failed", {
1247
+ seq, session: sidHex, error: extractErrorMessage(err),
1248
+ });
1249
+ }
1250
+ }
1251
+ }
1049
1252
  const session = this.#sessions.get(sidHex);
1050
1253
  if (session && seq >= 0) {
1051
1254
  session.onLeafDeliver({
@@ -1446,6 +1649,139 @@ export class AgentRelayClient {
1446
1649
  * ⚠️ **THE FRAME CARRIES NO PEER ID, deliberately.** The relay frees the peer this connection
1447
1650
  * authenticated as. A peer id on the wire would let any registered agent free another's.
1448
1651
  */
1652
+ /**
1653
+ * DOD-M15-AWAYSCOPE-1 — tell this relay whether anyone is attending this agent, for one session.
1654
+ *
1655
+ * ── WHY THIS EXISTS AT ALL ──────────────────────────────────────────────────────────────────────
1656
+ *
1657
+ * An unattended agent used to answer inbound messages with its away greeting. That greeting took a
1658
+ * hash-chain leaf inside a live conversation, and on session `e7dd3f43…` it cost two operators a
1659
+ * completed conversation's receipt, permanently. The fact itself is worth telling — a counterparty
1660
+ * mid-exchange genuinely needs to know whether to wait — but it is a fact ABOUT the session, not a
1661
+ * sentence IN it. So it rides here instead, and takes no leaf.
1662
+ *
1663
+ * ── FIRE AND FORGET, AND THAT IS THE DESIGN ─────────────────────────────────────────────────────
1664
+ *
1665
+ * Nothing is awaited and the relay sends no ack. The notice may be dropped, duplicated or arrive
1666
+ * out of order; `observed_at` settles it, last-write-wins. A failure changes nothing the operator
1667
+ * is doing — the counterparty reads "unknown" until the next notice, which is what is true.
1668
+ *
1669
+ * ⚠️ NO RETRY, NO QUEUE. Anything that made this reliable would want ordering, ordering wants a
1670
+ * witness, and a witnessed position is how machine chatter reached the leaf set. A status hint
1671
+ * that is allowed to be lost is the safe shape.
1672
+ */
1673
+ announceAttendance(node, sessionId, attendance) {
1674
+ if (this.#closed)
1675
+ return;
1676
+ void (async () => {
1677
+ try {
1678
+ if (!(await this.#ensureConnected(node)))
1679
+ return;
1680
+ const stream = this.#stream;
1681
+ if (!stream)
1682
+ return;
1683
+ stream.send(lp.encode.single(encodeSessionAttendanceNotice({
1684
+ type: "session_attendance_notice",
1685
+ session_id: sessionId,
1686
+ attendance,
1687
+ // OUR clock, stated plainly. The relay bounds it against its own and refuses a value too
1688
+ // far ahead rather than clamping — clamping would turn a hostile timestamp into a valid
1689
+ // recent one, and this client is the code an adversary gets to rewrite.
1690
+ observed_at: Date.now(),
1691
+ })));
1692
+ this.#logger.debug("session.relay.attendance.announced", {
1693
+ relayPeerId: this.#relayPeerId,
1694
+ sessionId: Buffer.from(sessionId).toString("hex").slice(0, 16),
1695
+ attendance,
1696
+ });
1697
+ }
1698
+ catch (err) {
1699
+ this.#logger.debug("session.relay.attendance.announce_failed", {
1700
+ relayPeerId: this.#relayPeerId,
1701
+ error: extractErrorMessage(err),
1702
+ impact: "the counterparty reads 'unknown' for this agent until the next notice lands",
1703
+ });
1704
+ }
1705
+ })();
1706
+ }
1707
+ /**
1708
+ * DOD-M15-AWAYSCOPE-1 — ask the relay about the counterparty: reachable, and attended?
1709
+ *
1710
+ * Two facts with two producers, which is why they are separate fields. `liveness` is the RELAY's
1711
+ * observation — it holds that agent's standing connection or it does not. `attendance` is the far
1712
+ * DAEMON's assertion about itself, which the relay was told and repeats; absent whenever
1713
+ * `liveness` is not 'alive', because a daemon nobody can reach asserts nothing.
1714
+ *
1715
+ * ⚠️ EVERY FAILURE ANSWERS 'unknown', never 'gone'. A timeout, a dead stream, a refusal and an
1716
+ * unparseable frame all mean "this side learned nothing"; reporting any as `gone` invents an
1717
+ * observation nobody made, and `gone` is the value that tells an operator their peer has left.
1718
+ */
1719
+ /**
1720
+ * Test-only seams for the liveness query path, which cannot be reached any other way.
1721
+ *
1722
+ * ⚠️ THEY EXIST BECAUSE A ONE-SESSION FAKE HID A REAL DEFECT. Every other test of this feature
1723
+ * stubs `queryLiveness` outright, so the code that matches an ANSWER to its QUESTION never ran —
1724
+ * and it was wrong: a single resolver slot handed one session's answer to another session's
1725
+ * promise the moment an agent held two conversations through one relay, which is ordinary.
1726
+ * Reaching that code needs a live stream and a dispatched frame, and both are private.
1727
+ *
1728
+ * Named `ForTest` like `__setLastActivityAtForTest` and `patchRelayClientForTest`, and doing
1729
+ * nothing production does not: one installs the stream a dial would have produced, the other
1730
+ * feeds the reader loop a frame the relay would have sent.
1731
+ */
1732
+ installStreamForTest(stream) { this.#stream = stream; }
1733
+ dispatchForTest(frame) { this.#dispatch(frame); }
1734
+ async queryLiveness(sessionId, counterpartyPubkey) {
1735
+ const unknown = { liveness: "unknown", observedAt: 0 };
1736
+ if (this.#closed)
1737
+ return unknown;
1738
+ /**
1739
+ * ⚠️ IT DOES NOT DIAL — the one place in this client that refuses to connect. Every other caller
1740
+ * here does the operator's work; this one serves a READ (`cello status`, `cello sessions`), and
1741
+ * a status command that opens a connection as a side effect changes what it is reporting on. It
1742
+ * would also make a read of a dozen sessions pay a dial each, against a budget measured in what
1743
+ * a person waits at a terminal. The cost is small and precise: right after a daemon restart a
1744
+ * status read says 'unknown' instead of dialling; the first real message establishes the stream.
1745
+ */
1746
+ const stream = this.#stream;
1747
+ if (!stream)
1748
+ return unknown;
1749
+ let resolveLiveness;
1750
+ const answer = new Promise((r) => { resolveLiveness = r; });
1751
+ const key = livenessKey(sessionId, counterpartyPubkey);
1752
+ this.#pendingLiveness.set(key, resolveLiveness);
1753
+ try {
1754
+ stream.send(lp.encode.single(encodeSessionLivenessQuery({
1755
+ type: "session_liveness_query",
1756
+ session_id: sessionId,
1757
+ counterparty_pubkey: counterpartyPubkey,
1758
+ })));
1759
+ }
1760
+ catch (err) {
1761
+ this.#pendingLiveness.delete(key);
1762
+ this.#logger.debug("session.relay.liveness.query_failed", {
1763
+ relayPeerId: this.#relayPeerId, error: extractErrorMessage(err),
1764
+ });
1765
+ return unknown;
1766
+ }
1767
+ let timer;
1768
+ const timeout = new Promise((r) => { timer = setTimeout(() => r("timeout"), HASH_SUBMIT_TIMEOUT_MS); });
1769
+ try {
1770
+ const result = await Promise.race([answer, timeout]);
1771
+ if (result === "timeout") {
1772
+ // NOT a stream reset, for the same reason a release timeout is not: a liveness query is not
1773
+ // ordered against anything, so tearing the shared stream down would cost every sibling
1774
+ // session its in-flight submits to tidy up one status read.
1775
+ this.#pendingLiveness.delete(key);
1776
+ this.#logger.debug("session.relay.liveness.query_timeout", { relayPeerId: this.#relayPeerId });
1777
+ return unknown;
1778
+ }
1779
+ return result;
1780
+ }
1781
+ finally {
1782
+ clearTimeout(timer);
1783
+ }
1784
+ }
1449
1785
  async releaseReservation(node) {
1450
1786
  if (this.#closed)
1451
1787
  return false;
@@ -1761,6 +2097,13 @@ export class AgentRelayClient {
1761
2097
  if (r)
1762
2098
  r(false);
1763
2099
  }
2100
+ // DOD-M15-AWAYSCOPE-1: and an in-flight liveness query, so a status read does not wait its
2101
+ // whole timeout on a stream that is already gone. "unknown" is the honest answer.
2102
+ {
2103
+ for (const r of this.#pendingLiveness.values())
2104
+ r({ liveness: "unknown", observedAt: 0 });
2105
+ this.#pendingLiveness.clear();
2106
+ }
1764
2107
  // A pure-receiver session issues no submit, so it would never trigger a re-dial
1765
2108
  // after the node that owned the stream is torn down. If sessions remain, proactively
1766
2109
  // re-establish from any still-live registered session node so queued leaf_delivers
@@ -2436,7 +2779,6 @@ export class AgentRelayClient {
2436
2779
  }
2437
2780
  }
2438
2781
  }
2439
- /** The highest relay-assigned sequence observed for a given session (ack or deliver). */
2440
2782
  /**
2441
2783
  * Advance this session's acknowledgement from a message that ARRIVED — 033-ACKEMIT review F1.
2442
2784
  *