@cello-protocol/daemon 0.0.227 → 0.0.229

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 (895) hide show
  1. package/dist/agent-admin-handlers.d.ts +56 -0
  2. package/dist/agent-admin-handlers.d.ts.map +1 -0
  3. package/dist/agent-admin-handlers.js +73 -0
  4. package/dist/agent-admin-handlers.js.map +1 -0
  5. package/dist/agent-handlers.d.ts +72 -0
  6. package/dist/agent-handlers.d.ts.map +1 -0
  7. package/dist/agent-handlers.js +604 -0
  8. package/dist/agent-handlers.js.map +1 -0
  9. package/dist/agent-id-migration.d.ts +70 -0
  10. package/dist/agent-id-migration.d.ts.map +1 -0
  11. package/dist/agent-id-migration.js +468 -0
  12. package/dist/agent-id-migration.js.map +1 -0
  13. package/dist/agent-loader.d.ts +37 -0
  14. package/dist/agent-loader.d.ts.map +1 -0
  15. package/dist/agent-loader.js +37 -0
  16. package/dist/agent-loader.js.map +1 -0
  17. package/dist/agent-selection-root.d.ts +29 -0
  18. package/dist/agent-selection-root.d.ts.map +1 -0
  19. package/dist/agent-selection-root.js +126 -0
  20. package/dist/agent-selection-root.js.map +1 -0
  21. package/dist/agent-selection.d.ts +73 -0
  22. package/dist/agent-selection.d.ts.map +1 -0
  23. package/dist/agent-selection.js +80 -0
  24. package/dist/agent-selection.js.map +1 -0
  25. package/dist/agent-settings-keys.d.ts +52 -0
  26. package/dist/agent-settings-keys.d.ts.map +1 -0
  27. package/dist/agent-settings-keys.js +114 -0
  28. package/dist/agent-settings-keys.js.map +1 -0
  29. package/dist/agent-state.d.ts +67 -0
  30. package/dist/agent-state.d.ts.map +1 -0
  31. package/dist/agent-state.js +66 -0
  32. package/dist/agent-state.js.map +1 -0
  33. package/dist/assignment-verify.d.ts +90 -0
  34. package/dist/assignment-verify.d.ts.map +1 -0
  35. package/dist/assignment-verify.js +301 -0
  36. package/dist/assignment-verify.js.map +1 -0
  37. package/dist/attendance-wiring.d.ts +23 -0
  38. package/dist/attendance-wiring.d.ts.map +1 -0
  39. package/dist/attendance-wiring.js +248 -0
  40. package/dist/attendance-wiring.js.map +1 -0
  41. package/dist/attestation-body.d.ts +47 -0
  42. package/dist/attestation-body.d.ts.map +1 -0
  43. package/dist/attestation-body.js +71 -0
  44. package/dist/attestation-body.js.map +1 -0
  45. package/dist/authorship-verification.d.ts +91 -0
  46. package/dist/authorship-verification.d.ts.map +1 -0
  47. package/dist/authorship-verification.js +532 -0
  48. package/dist/authorship-verification.js.map +1 -0
  49. package/dist/away-detection.d.ts +139 -0
  50. package/dist/away-detection.d.ts.map +1 -0
  51. package/dist/away-detection.js +186 -0
  52. package/dist/away-detection.js.map +1 -0
  53. package/dist/away-inbox-oneshot.d.ts +40 -0
  54. package/dist/away-inbox-oneshot.d.ts.map +1 -0
  55. package/dist/away-inbox-oneshot.js +311 -0
  56. package/dist/away-inbox-oneshot.js.map +1 -0
  57. package/dist/backup-restore-handlers.d.ts +10 -0
  58. package/dist/backup-restore-handlers.d.ts.map +1 -0
  59. package/dist/backup-restore-handlers.js +82 -0
  60. package/dist/backup-restore-handlers.js.map +1 -0
  61. package/dist/backup-restore.d.ts +102 -0
  62. package/dist/backup-restore.d.ts.map +1 -0
  63. package/dist/backup-restore.js +341 -0
  64. package/dist/backup-restore.js.map +1 -0
  65. package/dist/bin/cello-daemon.d.ts +13 -0
  66. package/dist/bin/cello-daemon.d.ts.map +1 -0
  67. package/dist/bin/cello-daemon.js.map +1 -0
  68. package/dist/boot-agents.d.ts +64 -0
  69. package/dist/boot-agents.d.ts.map +1 -0
  70. package/dist/boot-agents.js +241 -0
  71. package/dist/boot-agents.js.map +1 -0
  72. package/dist/boot-connection-state.d.ts +38 -0
  73. package/dist/boot-connection-state.d.ts.map +1 -0
  74. package/dist/boot-connection-state.js +95 -0
  75. package/dist/boot-connection-state.js.map +1 -0
  76. package/dist/boot-core.d.ts +36 -0
  77. package/dist/boot-core.d.ts.map +1 -0
  78. package/dist/boot-core.js +293 -0
  79. package/dist/boot-core.js.map +1 -0
  80. package/dist/boot-parked-content.d.ts +34 -0
  81. package/dist/boot-parked-content.d.ts.map +1 -0
  82. package/dist/boot-parked-content.js +528 -0
  83. package/dist/boot-parked-content.js.map +1 -0
  84. package/dist/boot-sweeps.d.ts +22 -0
  85. package/dist/boot-sweeps.d.ts.map +1 -0
  86. package/dist/boot-sweeps.js +51 -0
  87. package/dist/boot-sweeps.js.map +1 -0
  88. package/dist/bundled-consortium-manifest.d.ts +47 -0
  89. package/dist/bundled-consortium-manifest.d.ts.map +1 -0
  90. package/dist/bundled-consortium-manifest.js +116 -0
  91. package/dist/bundled-consortium-manifest.js.map +1 -0
  92. package/dist/cbor-blob-migration.d.ts +8 -0
  93. package/dist/cbor-blob-migration.d.ts.map +1 -0
  94. package/dist/cbor-blob-migration.js +116 -0
  95. package/dist/cbor-blob-migration.js.map +1 -0
  96. package/dist/challenge-verifier.d.ts +12 -0
  97. package/dist/challenge-verifier.d.ts.map +1 -0
  98. package/dist/challenge-verifier.js +11 -0
  99. package/dist/challenge-verifier.js.map +1 -0
  100. package/dist/channel-log-store.d.ts +80 -0
  101. package/dist/channel-log-store.d.ts.map +1 -0
  102. package/dist/channel-log-store.js +290 -0
  103. package/dist/channel-log-store.js.map +1 -0
  104. package/dist/close-commitment.d.ts +67 -0
  105. package/dist/close-commitment.d.ts.map +1 -0
  106. package/dist/close-commitment.js +78 -0
  107. package/dist/close-commitment.js.map +1 -0
  108. package/dist/close-session-handler.d.ts +94 -0
  109. package/dist/close-session-handler.d.ts.map +1 -0
  110. package/dist/close-session-handler.js +1418 -0
  111. package/dist/close-session-handler.js.map +1 -0
  112. package/dist/co-attendance.d.ts +29 -0
  113. package/dist/co-attendance.d.ts.map +1 -0
  114. package/dist/co-attendance.js +32 -0
  115. package/dist/co-attendance.js.map +1 -0
  116. package/dist/column-birth.d.ts +40 -0
  117. package/dist/column-birth.d.ts.map +1 -0
  118. package/dist/column-birth.js +61 -0
  119. package/dist/column-birth.js.map +1 -0
  120. package/dist/connect-or-start.d.ts +25 -0
  121. package/dist/connect-or-start.d.ts.map +1 -0
  122. package/dist/connect-or-start.js +175 -0
  123. package/dist/connect-or-start.js.map +1 -0
  124. package/dist/connection-agents.d.ts +16 -0
  125. package/dist/connection-agents.d.ts.map +1 -0
  126. package/dist/connection-agents.js +68 -0
  127. package/dist/connection-agents.js.map +1 -0
  128. package/dist/consent-migration.d.ts +49 -0
  129. package/dist/consent-migration.d.ts.map +1 -0
  130. package/dist/consent-migration.js +128 -0
  131. package/dist/consent-migration.js.map +1 -0
  132. package/dist/consortium-bootstrap.d.ts +138 -0
  133. package/dist/consortium-bootstrap.d.ts.map +1 -0
  134. package/dist/consortium-bootstrap.js +339 -0
  135. package/dist/consortium-bootstrap.js.map +1 -0
  136. package/dist/consortium-fingerprint.d.ts +115 -0
  137. package/dist/consortium-fingerprint.d.ts.map +1 -0
  138. package/dist/consortium-fingerprint.js +175 -0
  139. package/dist/consortium-fingerprint.js.map +1 -0
  140. package/dist/contact-handlers.d.ts +59 -0
  141. package/dist/contact-handlers.d.ts.map +1 -0
  142. package/dist/contact-handlers.js +343 -0
  143. package/dist/contact-handlers.js.map +1 -0
  144. package/dist/contact-pubkey-case.d.ts +65 -0
  145. package/dist/contact-pubkey-case.d.ts.map +1 -0
  146. package/dist/contact-pubkey-case.js +136 -0
  147. package/dist/contact-pubkey-case.js.map +1 -0
  148. package/dist/contacts-tier-migration.d.ts +90 -0
  149. package/dist/contacts-tier-migration.d.ts.map +1 -0
  150. package/dist/contacts-tier-migration.js +150 -0
  151. package/dist/contacts-tier-migration.js.map +1 -0
  152. package/dist/content-encryption-status.d.ts +111 -0
  153. package/dist/content-encryption-status.d.ts.map +1 -0
  154. package/dist/content-encryption-status.js +158 -0
  155. package/dist/content-encryption-status.js.map +1 -0
  156. package/dist/content-park-client.d.ts +90 -0
  157. package/dist/content-park-client.d.ts.map +1 -0
  158. package/dist/content-park-client.js +362 -0
  159. package/dist/content-park-client.js.map +1 -0
  160. package/dist/content-park.d.ts +52 -0
  161. package/dist/content-park.d.ts.map +1 -0
  162. package/dist/content-park.js +1309 -0
  163. package/dist/content-park.js.map +1 -0
  164. package/dist/cross-node-negotiation.d.ts +44 -0
  165. package/dist/cross-node-negotiation.d.ts.map +1 -0
  166. package/dist/cross-node-negotiation.js +34 -0
  167. package/dist/cross-node-negotiation.js.map +1 -0
  168. package/dist/daemon-handle.d.ts +57 -0
  169. package/dist/daemon-handle.d.ts.map +1 -0
  170. package/dist/daemon-handle.js +2 -0
  171. package/dist/daemon-handle.js.map +1 -0
  172. package/dist/daemon-status-report.d.ts +50 -0
  173. package/dist/daemon-status-report.d.ts.map +1 -0
  174. package/dist/daemon-status-report.js +81 -0
  175. package/dist/daemon-status-report.js.map +1 -0
  176. package/dist/daemon.d.ts +45 -0
  177. package/dist/daemon.d.ts.map +1 -0
  178. package/dist/daemon.js +1177 -0
  179. package/dist/daemon.js.map +1 -0
  180. package/dist/db-identity-store.d.ts +159 -0
  181. package/dist/db-identity-store.d.ts.map +1 -0
  182. package/dist/db-identity-store.js +502 -0
  183. package/dist/db-identity-store.js.map +1 -0
  184. package/dist/delivery-open-registry.d.ts +92 -0
  185. package/dist/delivery-open-registry.d.ts.map +1 -0
  186. package/dist/delivery-open-registry.js +121 -0
  187. package/dist/delivery-open-registry.js.map +1 -0
  188. package/dist/delivery-session-suspects.d.ts +56 -0
  189. package/dist/delivery-session-suspects.d.ts.map +1 -0
  190. package/dist/delivery-session-suspects.js +94 -0
  191. package/dist/delivery-session-suspects.js.map +1 -0
  192. package/dist/directory-auth-posture.d.ts +87 -0
  193. package/dist/directory-auth-posture.d.ts.map +1 -0
  194. package/dist/directory-auth-posture.js +134 -0
  195. package/dist/directory-auth-posture.js.map +1 -0
  196. package/dist/directory-bootstrap.d.ts +310 -0
  197. package/dist/directory-bootstrap.d.ts.map +1 -0
  198. package/dist/directory-bootstrap.js +557 -0
  199. package/dist/directory-bootstrap.js.map +1 -0
  200. package/dist/directory-connect.d.ts +27 -0
  201. package/dist/directory-connect.d.ts.map +1 -0
  202. package/dist/directory-connect.js +106 -0
  203. package/dist/directory-connect.js.map +1 -0
  204. package/dist/disconnect-cleanup.d.ts +51 -0
  205. package/dist/disconnect-cleanup.d.ts.map +1 -0
  206. package/dist/disconnect-cleanup.js +72 -0
  207. package/dist/disconnect-cleanup.js.map +1 -0
  208. package/dist/document-amendment-store.d.ts +120 -0
  209. package/dist/document-amendment-store.d.ts.map +1 -0
  210. package/dist/document-amendment-store.js +266 -0
  211. package/dist/document-amendment-store.js.map +1 -0
  212. package/dist/document-delivery-transport.d.ts +168 -0
  213. package/dist/document-delivery-transport.d.ts.map +1 -0
  214. package/dist/document-delivery-transport.js +206 -0
  215. package/dist/document-delivery-transport.js.map +1 -0
  216. package/dist/document-engine.d.ts +134 -0
  217. package/dist/document-engine.d.ts.map +1 -0
  218. package/dist/document-engine.js +282 -0
  219. package/dist/document-engine.js.map +1 -0
  220. package/dist/document-flag.d.ts +58 -0
  221. package/dist/document-flag.d.ts.map +1 -0
  222. package/dist/document-flag.js +70 -0
  223. package/dist/document-flag.js.map +1 -0
  224. package/dist/document-frame-router.d.ts +245 -0
  225. package/dist/document-frame-router.d.ts.map +1 -0
  226. package/dist/document-frame-router.js +397 -0
  227. package/dist/document-frame-router.js.map +1 -0
  228. package/dist/document-gate-wiring.d.ts +61 -0
  229. package/dist/document-gate-wiring.d.ts.map +1 -0
  230. package/dist/document-gate-wiring.js +125 -0
  231. package/dist/document-gate-wiring.js.map +1 -0
  232. package/dist/document-gate.d.ts +149 -0
  233. package/dist/document-gate.d.ts.map +1 -0
  234. package/dist/document-gate.js +509 -0
  235. package/dist/document-gate.js.map +1 -0
  236. package/dist/document-handlers.d.ts +47 -0
  237. package/dist/document-handlers.d.ts.map +1 -0
  238. package/dist/document-handlers.js +2203 -0
  239. package/dist/document-handlers.js.map +1 -0
  240. package/dist/document-handshake.d.ts +176 -0
  241. package/dist/document-handshake.d.ts.map +1 -0
  242. package/dist/document-handshake.js +452 -0
  243. package/dist/document-handshake.js.map +1 -0
  244. package/dist/document-inbound.d.ts +162 -0
  245. package/dist/document-inbound.d.ts.map +1 -0
  246. package/dist/document-inbound.js +530 -0
  247. package/dist/document-inbound.js.map +1 -0
  248. package/dist/document-json.d.ts +120 -0
  249. package/dist/document-json.d.ts.map +1 -0
  250. package/dist/document-json.js +191 -0
  251. package/dist/document-json.js.map +1 -0
  252. package/dist/document-layer.d.ts +215 -0
  253. package/dist/document-layer.d.ts.map +1 -0
  254. package/dist/document-layer.js +1025 -0
  255. package/dist/document-layer.js.map +1 -0
  256. package/dist/document-lifecycle.d.ts +52 -0
  257. package/dist/document-lifecycle.d.ts.map +1 -0
  258. package/dist/document-lifecycle.js +134 -0
  259. package/dist/document-lifecycle.js.map +1 -0
  260. package/dist/document-live-docs.d.ts +58 -0
  261. package/dist/document-live-docs.d.ts.map +1 -0
  262. package/dist/document-live-docs.js +126 -0
  263. package/dist/document-live-docs.js.map +1 -0
  264. package/dist/document-notify.d.ts +228 -0
  265. package/dist/document-notify.d.ts.map +1 -0
  266. package/dist/document-notify.js +580 -0
  267. package/dist/document-notify.js.map +1 -0
  268. package/dist/document-profile.d.ts +61 -0
  269. package/dist/document-profile.d.ts.map +1 -0
  270. package/dist/document-profile.js +112 -0
  271. package/dist/document-profile.js.map +1 -0
  272. package/dist/document-publish.d.ts +87 -0
  273. package/dist/document-publish.d.ts.map +1 -0
  274. package/dist/document-publish.js +173 -0
  275. package/dist/document-publish.js.map +1 -0
  276. package/dist/document-reachability.d.ts +42 -0
  277. package/dist/document-reachability.d.ts.map +1 -0
  278. package/dist/document-reachability.js +80 -0
  279. package/dist/document-reachability.js.map +1 -0
  280. package/dist/document-reconcile-engine.d.ts +66 -0
  281. package/dist/document-reconcile-engine.d.ts.map +1 -0
  282. package/dist/document-reconcile-engine.js +225 -0
  283. package/dist/document-reconcile-engine.js.map +1 -0
  284. package/dist/document-reconcile-scheduler.d.ts +163 -0
  285. package/dist/document-reconcile-scheduler.d.ts.map +1 -0
  286. package/dist/document-reconcile-scheduler.js +303 -0
  287. package/dist/document-reconcile-scheduler.js.map +1 -0
  288. package/dist/document-rejection.d.ts +251 -0
  289. package/dist/document-rejection.d.ts.map +1 -0
  290. package/dist/document-rejection.js +435 -0
  291. package/dist/document-rejection.js.map +1 -0
  292. package/dist/document-screen.d.ts +114 -0
  293. package/dist/document-screen.d.ts.map +1 -0
  294. package/dist/document-screen.js +223 -0
  295. package/dist/document-screen.js.map +1 -0
  296. package/dist/document-store.d.ts +372 -0
  297. package/dist/document-store.d.ts.map +1 -0
  298. package/dist/document-store.js +931 -0
  299. package/dist/document-store.js.map +1 -0
  300. package/dist/document-surface.d.ts +32 -0
  301. package/dist/document-surface.d.ts.map +1 -0
  302. package/dist/document-surface.js +151 -0
  303. package/dist/document-surface.js.map +1 -0
  304. package/dist/document-types.d.ts +94 -0
  305. package/dist/document-types.d.ts.map +1 -0
  306. package/dist/document-types.js +90 -0
  307. package/dist/document-types.js.map +1 -0
  308. package/dist/document-watch.d.ts +69 -0
  309. package/dist/document-watch.d.ts.map +1 -0
  310. package/dist/document-watch.js +108 -0
  311. package/dist/document-watch.js.map +1 -0
  312. package/dist/document-wiring.d.ts +53 -0
  313. package/dist/document-wiring.d.ts.map +1 -0
  314. package/dist/document-wiring.js +323 -0
  315. package/dist/document-wiring.js.map +1 -0
  316. package/dist/document-write-guard.d.ts +66 -0
  317. package/dist/document-write-guard.d.ts.map +1 -0
  318. package/dist/document-write-guard.js +98 -0
  319. package/dist/document-write-guard.js.map +1 -0
  320. package/dist/document-write-path.d.ts +94 -0
  321. package/dist/document-write-path.d.ts.map +1 -0
  322. package/dist/document-write-path.js +530 -0
  323. package/dist/document-write-path.js.map +1 -0
  324. package/dist/error-message.d.ts +7 -0
  325. package/dist/error-message.d.ts.map +1 -0
  326. package/dist/error-message.js +19 -0
  327. package/dist/error-message.js.map +1 -0
  328. package/dist/file-manifest-provider.d.ts +37 -0
  329. package/dist/file-manifest-provider.d.ts.map +1 -0
  330. package/dist/file-manifest-provider.js +105 -0
  331. package/dist/file-manifest-provider.js.map +1 -0
  332. package/dist/frame-values.d.ts +3 -0
  333. package/dist/frame-values.d.ts.map +1 -0
  334. package/dist/frame-values.js +84 -0
  335. package/dist/frame-values.js.map +1 -0
  336. package/dist/frontier-mismatch.d.ts +73 -0
  337. package/dist/frontier-mismatch.d.ts.map +1 -0
  338. package/dist/frontier-mismatch.js +89 -0
  339. package/dist/frontier-mismatch.js.map +1 -0
  340. package/dist/gateway-config-handlers.d.ts +26 -0
  341. package/dist/gateway-config-handlers.d.ts.map +1 -0
  342. package/dist/gateway-config-handlers.js +454 -0
  343. package/dist/gateway-config-handlers.js.map +1 -0
  344. package/dist/held-content.d.ts +145 -0
  345. package/dist/held-content.d.ts.map +1 -0
  346. package/dist/held-content.js +389 -0
  347. package/dist/held-content.js.map +1 -0
  348. package/dist/http-manifest-poll.d.ts +67 -0
  349. package/dist/http-manifest-poll.d.ts.map +1 -0
  350. package/dist/http-manifest-poll.js +150 -0
  351. package/dist/http-manifest-poll.js.map +1 -0
  352. package/dist/identity-migration.d.ts +40 -0
  353. package/dist/identity-migration.d.ts.map +1 -0
  354. package/dist/identity-migration.js +461 -0
  355. package/dist/identity-migration.js.map +1 -0
  356. package/dist/inbound-refusals.d.ts +283 -0
  357. package/dist/inbound-refusals.d.ts.map +1 -0
  358. package/dist/inbound-refusals.js +919 -0
  359. package/dist/inbound-refusals.js.map +1 -0
  360. package/dist/inbound-seal-request.d.ts +32 -0
  361. package/dist/inbound-seal-request.d.ts.map +1 -0
  362. package/dist/inbound-seal-request.js +228 -0
  363. package/dist/inbound-seal-request.js.map +1 -0
  364. package/dist/inbound-sessions.d.ts +291 -0
  365. package/dist/inbound-sessions.d.ts.map +1 -0
  366. package/dist/inbound-sessions.js +1609 -0
  367. package/dist/inbound-sessions.js.map +1 -0
  368. package/dist/inclusion-proof-handlers.d.ts +43 -0
  369. package/dist/inclusion-proof-handlers.d.ts.map +1 -0
  370. package/dist/inclusion-proof-handlers.js +565 -0
  371. package/dist/inclusion-proof-handlers.js.map +1 -0
  372. package/dist/inclusion-proof.d.ts +151 -0
  373. package/dist/inclusion-proof.d.ts.map +1 -0
  374. package/dist/inclusion-proof.js +228 -0
  375. package/dist/inclusion-proof.js.map +1 -0
  376. package/dist/index.d.ts +32 -0
  377. package/dist/index.d.ts.map +1 -0
  378. package/dist/index.js.map +1 -0
  379. package/dist/initiate-session-handler.d.ts +55 -0
  380. package/dist/initiate-session-handler.d.ts.map +1 -0
  381. package/dist/initiate-session-handler.js +310 -0
  382. package/dist/initiate-session-handler.js.map +1 -0
  383. package/dist/ipc-client.d.ts +31 -0
  384. package/dist/ipc-client.d.ts.map +1 -0
  385. package/dist/ipc-client.js +113 -0
  386. package/dist/ipc-client.js.map +1 -0
  387. package/dist/ipc-server.d.ts +63 -0
  388. package/dist/ipc-server.d.ts.map +1 -0
  389. package/dist/ipc-server.js +429 -0
  390. package/dist/ipc-server.js.map +1 -0
  391. package/dist/ipc-surface.d.ts +44 -0
  392. package/dist/ipc-surface.d.ts.map +1 -0
  393. package/dist/ipc-surface.js +107 -0
  394. package/dist/ipc-surface.js.map +1 -0
  395. package/dist/line-lcs.d.ts +51 -0
  396. package/dist/line-lcs.d.ts.map +1 -0
  397. package/dist/line-lcs.js +71 -0
  398. package/dist/line-lcs.js.map +1 -0
  399. package/dist/lock-file.d.ts +39 -0
  400. package/dist/lock-file.d.ts.map +1 -0
  401. package/dist/lock-file.js +120 -0
  402. package/dist/lock-file.js.map +1 -0
  403. package/dist/log-collapse.d.ts +66 -0
  404. package/dist/log-collapse.d.ts.map +1 -0
  405. package/dist/log-collapse.js +244 -0
  406. package/dist/log-collapse.js.map +1 -0
  407. package/dist/log-rotate.d.ts +67 -0
  408. package/dist/log-rotate.d.ts.map +1 -0
  409. package/dist/log-rotate.js +134 -0
  410. package/dist/log-rotate.js.map +1 -0
  411. package/dist/manifest-deps.d.ts +25 -0
  412. package/dist/manifest-deps.d.ts.map +1 -0
  413. package/dist/manifest-deps.js +151 -0
  414. package/dist/manifest-deps.js.map +1 -0
  415. package/dist/manifest-poll-scheduler.d.ts +31 -0
  416. package/dist/manifest-poll-scheduler.d.ts.map +1 -0
  417. package/dist/manifest-poll-scheduler.js +59 -0
  418. package/dist/manifest-poll-scheduler.js.map +1 -0
  419. package/dist/manifest-validity.d.ts +153 -0
  420. package/dist/manifest-validity.d.ts.map +1 -0
  421. package/dist/manifest-validity.js +268 -0
  422. package/dist/manifest-validity.js.map +1 -0
  423. package/dist/manifest-version-store-db.d.ts +24 -0
  424. package/dist/manifest-version-store-db.d.ts.map +1 -0
  425. package/dist/manifest-version-store-db.js +58 -0
  426. package/dist/manifest-version-store-db.js.map +1 -0
  427. package/dist/manifest-version-store.d.ts +16 -0
  428. package/dist/manifest-version-store.d.ts.map +1 -0
  429. package/dist/manifest-version-store.js +15 -0
  430. package/dist/manifest-version-store.js.map +1 -0
  431. package/dist/network-directory-node.d.ts +136 -0
  432. package/dist/network-directory-node.d.ts.map +1 -0
  433. package/dist/network-directory-node.js +810 -0
  434. package/dist/network-directory-node.js.map +1 -0
  435. package/dist/nonce-dedup.d.ts +68 -0
  436. package/dist/nonce-dedup.d.ts.map +1 -0
  437. package/dist/nonce-dedup.js +205 -0
  438. package/dist/nonce-dedup.js.map +1 -0
  439. package/dist/notification-dispatcher.d.ts +92 -0
  440. package/dist/notification-dispatcher.d.ts.map +1 -0
  441. package/dist/notification-dispatcher.js +210 -0
  442. package/dist/notification-dispatcher.js.map +1 -0
  443. package/dist/notification-handlers.d.ts +53 -0
  444. package/dist/notification-handlers.d.ts.map +1 -0
  445. package/dist/notification-handlers.js +461 -0
  446. package/dist/notification-handlers.js.map +1 -0
  447. package/dist/onboarding-guidance.d.ts +79 -0
  448. package/dist/onboarding-guidance.d.ts.map +1 -0
  449. package/dist/onboarding-guidance.js +95 -0
  450. package/dist/onboarding-guidance.js.map +1 -0
  451. package/dist/operator-guidance.d.ts +25 -0
  452. package/dist/operator-guidance.d.ts.map +1 -0
  453. package/dist/operator-guidance.js +50 -0
  454. package/dist/operator-guidance.js.map +1 -0
  455. package/dist/orphan-triage.d.ts +130 -0
  456. package/dist/orphan-triage.d.ts.map +1 -0
  457. package/dist/orphan-triage.js +207 -0
  458. package/dist/orphan-triage.js.map +1 -0
  459. package/dist/outbound-sessions.d.ts +126 -0
  460. package/dist/outbound-sessions.d.ts.map +1 -0
  461. package/dist/outbound-sessions.js +1052 -0
  462. package/dist/outbound-sessions.js.map +1 -0
  463. package/dist/park-envelope.d.ts +329 -0
  464. package/dist/park-envelope.d.ts.map +1 -0
  465. package/dist/park-envelope.js +509 -0
  466. package/dist/park-envelope.js.map +1 -0
  467. package/dist/park-recovery.d.ts +273 -0
  468. package/dist/park-recovery.d.ts.map +1 -0
  469. package/dist/park-recovery.js +717 -0
  470. package/dist/park-recovery.js.map +1 -0
  471. package/dist/park-refusals.d.ts +147 -0
  472. package/dist/park-refusals.d.ts.map +1 -0
  473. package/dist/park-refusals.js +331 -0
  474. package/dist/park-refusals.js.map +1 -0
  475. package/dist/quarantine-framing.d.ts +92 -0
  476. package/dist/quarantine-framing.d.ts.map +1 -0
  477. package/dist/quarantine-framing.js +111 -0
  478. package/dist/quarantine-framing.js.map +1 -0
  479. package/dist/reconnect-drain.d.ts +22 -0
  480. package/dist/reconnect-drain.d.ts.map +1 -0
  481. package/dist/reconnect-drain.js +65 -0
  482. package/dist/reconnect-drain.js.map +1 -0
  483. package/dist/recovered-position.d.ts +14 -0
  484. package/dist/recovered-position.d.ts.map +1 -0
  485. package/dist/recovered-position.js +59 -0
  486. package/dist/recovered-position.js.map +1 -0
  487. package/dist/refusal-notices.d.ts +196 -0
  488. package/dist/refusal-notices.d.ts.map +1 -0
  489. package/dist/refusal-notices.js +516 -0
  490. package/dist/refusal-notices.js.map +1 -0
  491. package/dist/refusal-reasons.d.ts +245 -0
  492. package/dist/refusal-reasons.d.ts.map +1 -0
  493. package/dist/refusal-reasons.js +363 -0
  494. package/dist/refusal-reasons.js.map +1 -0
  495. package/dist/register-handler.d.ts +36 -0
  496. package/dist/register-handler.d.ts.map +1 -0
  497. package/dist/register-handler.js +285 -0
  498. package/dist/register-handler.js.map +1 -0
  499. package/dist/registration-context.d.ts +72 -0
  500. package/dist/registration-context.d.ts.map +1 -0
  501. package/dist/registration-context.js +126 -0
  502. package/dist/registration-context.js.map +1 -0
  503. package/dist/registration-manager.d.ts +94 -0
  504. package/dist/registration-manager.d.ts.map +1 -0
  505. package/dist/registration-manager.js +585 -0
  506. package/dist/registration-manager.js.map +1 -0
  507. package/dist/registration-persistence.d.ts +183 -0
  508. package/dist/registration-persistence.d.ts.map +1 -0
  509. package/dist/registration-persistence.js +263 -0
  510. package/dist/registration-persistence.js.map +1 -0
  511. package/dist/registry-poll.d.ts +52 -0
  512. package/dist/registry-poll.d.ts.map +1 -0
  513. package/dist/registry-poll.js +140 -0
  514. package/dist/registry-poll.js.map +1 -0
  515. package/dist/registry-version-store-db.d.ts +22 -0
  516. package/dist/registry-version-store-db.d.ts.map +1 -0
  517. package/dist/registry-version-store-db.js +50 -0
  518. package/dist/registry-version-store-db.js.map +1 -0
  519. package/dist/relay-endpoints.d.ts +18 -0
  520. package/dist/relay-endpoints.d.ts.map +1 -0
  521. package/dist/relay-endpoints.js +9 -0
  522. package/dist/relay-endpoints.js.map +1 -0
  523. package/dist/relay-only.d.ts +140 -0
  524. package/dist/relay-only.d.ts.map +1 -0
  525. package/dist/relay-only.js +193 -0
  526. package/dist/relay-only.js.map +1 -0
  527. package/dist/relay-receipt-store.d.ts +155 -0
  528. package/dist/relay-receipt-store.d.ts.map +1 -0
  529. package/dist/relay-receipt-store.js +284 -0
  530. package/dist/relay-receipt-store.js.map +1 -0
  531. package/dist/relay-reconnect.d.ts +32 -0
  532. package/dist/relay-reconnect.d.ts.map +1 -0
  533. package/dist/relay-reconnect.js +29 -0
  534. package/dist/relay-reconnect.js.map +1 -0
  535. package/dist/reply-lag.d.ts +11 -0
  536. package/dist/reply-lag.d.ts.map +1 -0
  537. package/dist/reply-lag.js +46 -0
  538. package/dist/reply-lag.js.map +1 -0
  539. package/dist/resolve-named-agent.d.ts +49 -0
  540. package/dist/resolve-named-agent.d.ts.map +1 -0
  541. package/dist/resolve-named-agent.js +77 -0
  542. package/dist/resolve-named-agent.js.map +1 -0
  543. package/dist/restart-seal-resolver.d.ts +110 -0
  544. package/dist/restart-seal-resolver.d.ts.map +1 -0
  545. package/dist/restart-seal-resolver.js +353 -0
  546. package/dist/restart-seal-resolver.js.map +1 -0
  547. package/dist/resume-last-seen.d.ts +15 -0
  548. package/dist/resume-last-seen.d.ts.map +1 -0
  549. package/dist/resume-last-seen.js +44 -0
  550. package/dist/resume-last-seen.js.map +1 -0
  551. package/dist/retry-queue.d.ts +203 -0
  552. package/dist/retry-queue.d.ts.map +1 -0
  553. package/dist/retry-queue.js +701 -0
  554. package/dist/retry-queue.js.map +1 -0
  555. package/dist/roster-freshness.d.ts +160 -0
  556. package/dist/roster-freshness.d.ts.map +1 -0
  557. package/dist/roster-freshness.js +250 -0
  558. package/dist/roster-freshness.js.map +1 -0
  559. package/dist/screening-status.d.ts +15 -0
  560. package/dist/screening-status.d.ts.map +1 -0
  561. package/dist/screening-status.js +65 -0
  562. package/dist/screening-status.js.map +1 -0
  563. package/dist/seal-carried-close.d.ts +37 -0
  564. package/dist/seal-carried-close.d.ts.map +1 -0
  565. package/dist/seal-carried-close.js +179 -0
  566. package/dist/seal-carried-close.js.map +1 -0
  567. package/dist/seal-certificate-pull.d.ts +79 -0
  568. package/dist/seal-certificate-pull.d.ts.map +1 -0
  569. package/dist/seal-certificate-pull.js +186 -0
  570. package/dist/seal-certificate-pull.js.map +1 -0
  571. package/dist/seal-certified-root-check.d.ts +37 -0
  572. package/dist/seal-certified-root-check.d.ts.map +1 -0
  573. package/dist/seal-certified-root-check.js +127 -0
  574. package/dist/seal-certified-root-check.js.map +1 -0
  575. package/dist/seal-coordinator.d.ts +118 -0
  576. package/dist/seal-coordinator.d.ts.map +1 -0
  577. package/dist/seal-coordinator.js +927 -0
  578. package/dist/seal-coordinator.js.map +1 -0
  579. package/dist/seal-escalation.d.ts +80 -0
  580. package/dist/seal-escalation.d.ts.map +1 -0
  581. package/dist/seal-escalation.js +283 -0
  582. package/dist/seal-escalation.js.map +1 -0
  583. package/dist/seal-evidence-root-check.d.ts +70 -0
  584. package/dist/seal-evidence-root-check.d.ts.map +1 -0
  585. package/dist/seal-evidence-root-check.js +199 -0
  586. package/dist/seal-evidence-root-check.js.map +1 -0
  587. package/dist/seal-failure-store.d.ts +139 -0
  588. package/dist/seal-failure-store.d.ts.map +1 -0
  589. package/dist/seal-failure-store.js +207 -0
  590. package/dist/seal-failure-store.js.map +1 -0
  591. package/dist/seal-flows.d.ts +107 -0
  592. package/dist/seal-flows.d.ts.map +1 -0
  593. package/dist/seal-flows.js +638 -0
  594. package/dist/seal-flows.js.map +1 -0
  595. package/dist/seal-frontier-verify.d.ts +103 -0
  596. package/dist/seal-frontier-verify.d.ts.map +1 -0
  597. package/dist/seal-frontier-verify.js +143 -0
  598. package/dist/seal-frontier-verify.js.map +1 -0
  599. package/dist/seal-leaf.d.ts +58 -0
  600. package/dist/seal-leaf.d.ts.map +1 -0
  601. package/dist/seal-leaf.js +112 -0
  602. package/dist/seal-leaf.js.map +1 -0
  603. package/dist/seal-legibility-tbs.d.ts +25 -0
  604. package/dist/seal-legibility-tbs.d.ts.map +1 -0
  605. package/dist/seal-legibility-tbs.js +77 -0
  606. package/dist/seal-legibility-tbs.js.map +1 -0
  607. package/dist/seal-local-terminus.d.ts +41 -0
  608. package/dist/seal-local-terminus.d.ts.map +1 -0
  609. package/dist/seal-local-terminus.js +170 -0
  610. package/dist/seal-local-terminus.js.map +1 -0
  611. package/dist/seal-receipt-upgrade.d.ts +30 -0
  612. package/dist/seal-receipt-upgrade.d.ts.map +1 -0
  613. package/dist/seal-receipt-upgrade.js +49 -0
  614. package/dist/seal-receipt-upgrade.js.map +1 -0
  615. package/dist/seal-relay-silence.d.ts +51 -0
  616. package/dist/seal-relay-silence.d.ts.map +1 -0
  617. package/dist/seal-relay-silence.js +76 -0
  618. package/dist/seal-relay-silence.js.map +1 -0
  619. package/dist/seal-settle.d.ts +38 -0
  620. package/dist/seal-settle.d.ts.map +1 -0
  621. package/dist/seal-settle.js +33 -0
  622. package/dist/seal-settle.js.map +1 -0
  623. package/dist/seal-upgrade.d.ts +107 -0
  624. package/dist/seal-upgrade.d.ts.map +1 -0
  625. package/dist/seal-upgrade.js +200 -0
  626. package/dist/seal-upgrade.js.map +1 -0
  627. package/dist/sealed-conversation.d.ts +36 -0
  628. package/dist/sealed-conversation.d.ts.map +1 -0
  629. package/dist/sealed-conversation.js +171 -0
  630. package/dist/sealed-conversation.js.map +1 -0
  631. package/dist/sealed-leaf-set.d.ts +92 -0
  632. package/dist/sealed-leaf-set.d.ts.map +1 -0
  633. package/dist/sealed-leaf-set.js +122 -0
  634. package/dist/sealed-leaf-set.js.map +1 -0
  635. package/dist/send-claims.d.ts +56 -0
  636. package/dist/send-claims.d.ts.map +1 -0
  637. package/dist/send-claims.js +50 -0
  638. package/dist/send-claims.js.map +1 -0
  639. package/dist/session-assignment-parser.d.ts +104 -0
  640. package/dist/session-assignment-parser.d.ts.map +1 -0
  641. package/dist/session-assignment-parser.js +345 -0
  642. package/dist/session-assignment-parser.js.map +1 -0
  643. package/dist/session-category.d.ts +19 -0
  644. package/dist/session-category.d.ts.map +1 -0
  645. package/dist/session-category.js +14 -0
  646. package/dist/session-category.js.map +1 -0
  647. package/dist/session-ceremony.d.ts +297 -0
  648. package/dist/session-ceremony.d.ts.map +1 -0
  649. package/dist/session-ceremony.js +964 -0
  650. package/dist/session-ceremony.js.map +1 -0
  651. package/dist/session-closed.d.ts +79 -0
  652. package/dist/session-closed.d.ts.map +1 -0
  653. package/dist/session-closed.js +189 -0
  654. package/dist/session-closed.js.map +1 -0
  655. package/dist/session-connection-gater.d.ts +148 -0
  656. package/dist/session-connection-gater.d.ts.map +1 -0
  657. package/dist/session-connection-gater.js +332 -0
  658. package/dist/session-connection-gater.js.map +1 -0
  659. package/dist/session-content-context.d.ts +162 -0
  660. package/dist/session-content-context.d.ts.map +1 -0
  661. package/dist/session-content-context.js +2 -0
  662. package/dist/session-content-context.js.map +1 -0
  663. package/dist/session-content-handlers.d.ts +82 -0
  664. package/dist/session-content-handlers.d.ts.map +1 -0
  665. package/dist/session-content-handlers.js +1382 -0
  666. package/dist/session-content-handlers.js.map +1 -0
  667. package/dist/session-content-ingest.d.ts +206 -0
  668. package/dist/session-content-ingest.d.ts.map +1 -0
  669. package/dist/session-content-ingest.js +2167 -0
  670. package/dist/session-content-ingest.js.map +1 -0
  671. package/dist/session-content-send.d.ts +191 -0
  672. package/dist/session-content-send.d.ts.map +1 -0
  673. package/dist/session-content-send.js +1360 -0
  674. package/dist/session-content-send.js.map +1 -0
  675. package/dist/session-delivery-acks.d.ts +169 -0
  676. package/dist/session-delivery-acks.d.ts.map +1 -0
  677. package/dist/session-delivery-acks.js +569 -0
  678. package/dist/session-delivery-acks.js.map +1 -0
  679. package/dist/session-ephemerals.d.ts +279 -0
  680. package/dist/session-ephemerals.d.ts.map +1 -0
  681. package/dist/session-ephemerals.js +591 -0
  682. package/dist/session-ephemerals.js.map +1 -0
  683. package/dist/session-leaf-records.d.ts +159 -0
  684. package/dist/session-leaf-records.d.ts.map +1 -0
  685. package/dist/session-leaf-records.js +408 -0
  686. package/dist/session-leaf-records.js.map +1 -0
  687. package/dist/session-lifecycle.d.ts +303 -0
  688. package/dist/session-lifecycle.d.ts.map +1 -0
  689. package/dist/session-lifecycle.js +1679 -0
  690. package/dist/session-lifecycle.js.map +1 -0
  691. package/dist/session-liveness.d.ts +135 -0
  692. package/dist/session-liveness.d.ts.map +1 -0
  693. package/dist/session-liveness.js +347 -0
  694. package/dist/session-liveness.js.map +1 -0
  695. package/dist/session-name.d.ts +35 -0
  696. package/dist/session-name.d.ts.map +1 -0
  697. package/dist/session-name.js +60 -0
  698. package/dist/session-name.js.map +1 -0
  699. package/dist/session-node-factory.d.ts +18 -0
  700. package/dist/session-node-factory.d.ts.map +1 -0
  701. package/dist/session-node-factory.js +182 -0
  702. package/dist/session-node-factory.js.map +1 -0
  703. package/dist/session-node-manager.d.ts +877 -0
  704. package/dist/session-node-manager.d.ts.map +1 -0
  705. package/dist/session-node-manager.js +2998 -0
  706. package/dist/session-node-manager.js.map +1 -0
  707. package/dist/session-node-types.d.ts +1057 -0
  708. package/dist/session-node-types.d.ts.map +1 -0
  709. package/dist/session-node-types.js +657 -0
  710. package/dist/session-node-types.js.map +1 -0
  711. package/dist/session-notify.d.ts +46 -0
  712. package/dist/session-notify.d.ts.map +1 -0
  713. package/dist/session-notify.js +116 -0
  714. package/dist/session-notify.js.map +1 -0
  715. package/dist/session-own-chain-store.d.ts +65 -0
  716. package/dist/session-own-chain-store.d.ts.map +1 -0
  717. package/dist/session-own-chain-store.js +75 -0
  718. package/dist/session-own-chain-store.js.map +1 -0
  719. package/dist/session-queries.d.ts +476 -0
  720. package/dist/session-queries.d.ts.map +1 -0
  721. package/dist/session-queries.js +1007 -0
  722. package/dist/session-queries.js.map +1 -0
  723. package/dist/session-read-handlers.d.ts +87 -0
  724. package/dist/session-read-handlers.d.ts.map +1 -0
  725. package/dist/session-read-handlers.js +689 -0
  726. package/dist/session-read-handlers.js.map +1 -0
  727. package/dist/session-records.d.ts +341 -0
  728. package/dist/session-records.d.ts.map +1 -0
  729. package/dist/session-records.js +858 -0
  730. package/dist/session-records.js.map +1 -0
  731. package/dist/session-relay-client.d.ts +659 -0
  732. package/dist/session-relay-client.d.ts.map +1 -0
  733. package/dist/session-relay-client.js +2876 -0
  734. package/dist/session-relay-client.js.map +1 -0
  735. package/dist/session-relay.d.ts +397 -0
  736. package/dist/session-relay.d.ts.map +1 -0
  737. package/dist/session-relay.js +1636 -0
  738. package/dist/session-relay.js.map +1 -0
  739. package/dist/session-salt-agreement.d.ts +331 -0
  740. package/dist/session-salt-agreement.d.ts.map +1 -0
  741. package/dist/session-salt-agreement.js +472 -0
  742. package/dist/session-salt-agreement.js.map +1 -0
  743. package/dist/session-salts.d.ts +432 -0
  744. package/dist/session-salts.d.ts.map +1 -0
  745. package/dist/session-salts.js +1540 -0
  746. package/dist/session-salts.js.map +1 -0
  747. package/dist/session-schema.d.ts +30 -0
  748. package/dist/session-schema.d.ts.map +1 -0
  749. package/dist/session-schema.js +877 -0
  750. package/dist/session-schema.js.map +1 -0
  751. package/dist/session-seal-leaf-store.d.ts +70 -0
  752. package/dist/session-seal-leaf-store.d.ts.map +1 -0
  753. package/dist/session-seal-leaf-store.js +105 -0
  754. package/dist/session-seal-leaf-store.js.map +1 -0
  755. package/dist/session-seal.d.ts +334 -0
  756. package/dist/session-seal.d.ts.map +1 -0
  757. package/dist/session-seal.js +1017 -0
  758. package/dist/session-seal.js.map +1 -0
  759. package/dist/session-terminal-refusal.d.ts +65 -0
  760. package/dist/session-terminal-refusal.d.ts.map +1 -0
  761. package/dist/session-terminal-refusal.js +87 -0
  762. package/dist/session-terminal-refusal.js.map +1 -0
  763. package/dist/session-tree.d.ts +110 -0
  764. package/dist/session-tree.d.ts.map +1 -0
  765. package/dist/session-tree.js +144 -0
  766. package/dist/session-tree.js.map +1 -0
  767. package/dist/session-views.d.ts +47 -0
  768. package/dist/session-views.d.ts.map +1 -0
  769. package/dist/session-views.js +278 -0
  770. package/dist/session-views.js.map +1 -0
  771. package/dist/signal-handlers.d.ts +63 -0
  772. package/dist/signal-handlers.d.ts.map +1 -0
  773. package/dist/signal-handlers.js +980 -0
  774. package/dist/signal-handlers.js.map +1 -0
  775. package/dist/signal-requirement-policy.d.ts +51 -0
  776. package/dist/signal-requirement-policy.d.ts.map +1 -0
  777. package/dist/signal-requirement-policy.js +89 -0
  778. package/dist/signal-requirement-policy.js.map +1 -0
  779. package/dist/signal-revocability.d.ts +49 -0
  780. package/dist/signal-revocability.d.ts.map +1 -0
  781. package/dist/signal-revocability.js +93 -0
  782. package/dist/signal-revocability.js.map +1 -0
  783. package/dist/signal-submission.d.ts +181 -0
  784. package/dist/signal-submission.d.ts.map +1 -0
  785. package/dist/signal-submission.js +368 -0
  786. package/dist/signal-submission.js.map +1 -0
  787. package/dist/signaling-connect.d.ts +118 -0
  788. package/dist/signaling-connect.d.ts.map +1 -0
  789. package/dist/signaling-connect.js +538 -0
  790. package/dist/signaling-connect.js.map +1 -0
  791. package/dist/signaling-wiring.d.ts +105 -0
  792. package/dist/signaling-wiring.d.ts.map +1 -0
  793. package/dist/signaling-wiring.js +393 -0
  794. package/dist/signaling-wiring.js.map +1 -0
  795. package/dist/singleton-lock.d.ts +85 -0
  796. package/dist/singleton-lock.d.ts.map +1 -0
  797. package/dist/singleton-lock.js +219 -0
  798. package/dist/singleton-lock.js.map +1 -0
  799. package/dist/sqlcipher-db.d.ts +139 -0
  800. package/dist/sqlcipher-db.d.ts.map +1 -0
  801. package/dist/sqlcipher-db.js +357 -0
  802. package/dist/sqlcipher-db.js.map +1 -0
  803. package/dist/standing-receivers.d.ts +319 -0
  804. package/dist/standing-receivers.d.ts.map +1 -0
  805. package/dist/standing-receivers.js +1295 -0
  806. package/dist/standing-receivers.js.map +1 -0
  807. package/dist/start-agent.d.ts +64 -0
  808. package/dist/start-agent.d.ts.map +1 -0
  809. package/dist/start-agent.js +136 -0
  810. package/dist/start-agent.js.map +1 -0
  811. package/dist/status-handler.d.ts +54 -0
  812. package/dist/status-handler.d.ts.map +1 -0
  813. package/dist/status-handler.js +61 -0
  814. package/dist/status-handler.js.map +1 -0
  815. package/dist/submission-retry.d.ts +208 -0
  816. package/dist/submission-retry.d.ts.map +1 -0
  817. package/dist/submission-retry.js +506 -0
  818. package/dist/submission-retry.js.map +1 -0
  819. package/dist/telegram-bot-client.d.ts +34 -0
  820. package/dist/telegram-bot-client.d.ts.map +1 -0
  821. package/dist/telegram-bot-client.js +36 -0
  822. package/dist/telegram-bot-client.js.map +1 -0
  823. package/dist/telegram-doorbell.d.ts +38 -0
  824. package/dist/telegram-doorbell.d.ts.map +1 -0
  825. package/dist/telegram-doorbell.js +140 -0
  826. package/dist/telegram-doorbell.js.map +1 -0
  827. package/dist/test-handlers.d.ts +62 -0
  828. package/dist/test-handlers.d.ts.map +1 -0
  829. package/dist/test-handlers.js +240 -0
  830. package/dist/test-handlers.js.map +1 -0
  831. package/dist/testing.d.ts +10 -0
  832. package/dist/testing.d.ts.map +1 -0
  833. package/dist/testing.js +10 -0
  834. package/dist/testing.js.map +1 -0
  835. package/dist/transport-composition.d.ts +31 -0
  836. package/dist/transport-composition.d.ts.map +1 -0
  837. package/dist/transport-composition.js +55 -0
  838. package/dist/transport-composition.js.map +1 -0
  839. package/dist/transport-selector.d.ts +202 -0
  840. package/dist/transport-selector.d.ts.map +1 -0
  841. package/dist/transport-selector.js +196 -0
  842. package/dist/transport-selector.js.map +1 -0
  843. package/dist/trust-signal-pickup-listener.d.ts +43 -0
  844. package/dist/trust-signal-pickup-listener.d.ts.map +1 -0
  845. package/dist/trust-signal-pickup-listener.js +48 -0
  846. package/dist/trust-signal-pickup-listener.js.map +1 -0
  847. package/dist/trust-signal-store.d.ts +406 -0
  848. package/dist/trust-signal-store.d.ts.map +1 -0
  849. package/dist/trust-signal-store.js +939 -0
  850. package/dist/trust-signal-store.js.map +1 -0
  851. package/dist/trust-signal-sweep-tick.d.ts +60 -0
  852. package/dist/trust-signal-sweep-tick.d.ts.map +1 -0
  853. package/dist/trust-signal-sweep-tick.js +101 -0
  854. package/dist/trust-signal-sweep-tick.js.map +1 -0
  855. package/dist/trust-signal-sweep.d.ts +94 -0
  856. package/dist/trust-signal-sweep.d.ts.map +1 -0
  857. package/dist/trust-signal-sweep.js +147 -0
  858. package/dist/trust-signal-sweep.js.map +1 -0
  859. package/dist/type-registry.d.ts +42 -0
  860. package/dist/type-registry.d.ts.map +1 -0
  861. package/dist/type-registry.js +37 -0
  862. package/dist/type-registry.js.map +1 -0
  863. package/dist/types.d.ts +746 -0
  864. package/dist/types.d.ts.map +1 -0
  865. package/dist/types.js +20 -0
  866. package/dist/types.js.map +1 -0
  867. package/dist/unresolved-nodes-report.d.ts +14 -0
  868. package/dist/unresolved-nodes-report.d.ts.map +1 -0
  869. package/dist/unresolved-nodes-report.js +96 -0
  870. package/dist/unresolved-nodes-report.js.map +1 -0
  871. package/dist/vocabulary.d.ts +150 -0
  872. package/dist/vocabulary.d.ts.map +1 -0
  873. package/dist/vocabulary.js +386 -0
  874. package/dist/vocabulary.js.map +1 -0
  875. package/dist/who-label.d.ts +28 -0
  876. package/dist/who-label.d.ts.map +1 -0
  877. package/dist/who-label.js +31 -0
  878. package/dist/who-label.js.map +1 -0
  879. package/dist/who-resolver.d.ts +15 -0
  880. package/dist/who-resolver.d.ts.map +1 -0
  881. package/dist/who-resolver.js +47 -0
  882. package/dist/who-resolver.js.map +1 -0
  883. package/dist/wire-content-hash.d.ts +93 -0
  884. package/dist/wire-content-hash.d.ts.map +1 -0
  885. package/dist/wire-content-hash.js +116 -0
  886. package/dist/wire-content-hash.js.map +1 -0
  887. package/dist/withheld-content.d.ts +13 -0
  888. package/dist/withheld-content.d.ts.map +1 -0
  889. package/dist/withheld-content.js +46 -0
  890. package/dist/withheld-content.js.map +1 -0
  891. package/dist/witness-alerts.d.ts +40 -0
  892. package/dist/witness-alerts.d.ts.map +1 -0
  893. package/dist/witness-alerts.js +102 -0
  894. package/dist/witness-alerts.js.map +1 -0
  895. package/package.json +5 -5
@@ -0,0 +1,1360 @@
1
+ /**
2
+ * CELLO Daemon — SENDING A MESSAGE
3
+ *
4
+ * The outbound half of the content path, split out of `session-node-manager.ts` with its inbound
5
+ * counterpart in `session-content-ingest.ts`. What happens between an operator calling
6
+ * `cello_send` and the bytes leaving this machine: hashing and labelling the content, having the
7
+ * relay witness its position, signing our own authorship of it, encrypting it, placing our leaf,
8
+ * opening the stream, and arming the timer that parks the message when no acknowledgement returns.
9
+ *
10
+ * **Moved verbatim, comments included.**
11
+ *
12
+ * The two halves are separated because they share almost nothing: outbound reaches nothing on the
13
+ * inbound side at all, and inbound reaches exactly ONE thing here — `resolveAwaitingAck`, when the
14
+ * counterparty's delivery acknowledgement arrives on the stream the receiver is already reading.
15
+ * That single edge is why this class is constructed first and handed to the other one.
16
+ */
17
+ import * as lp from "it-length-prefixed";
18
+ import { encodeCbor, decodeStructure1, encodeStructure1 } from "@cello-protocol/protocol-types";
19
+ import { verify, sealSessionContent } from "@cello-protocol/crypto";
20
+ import { CELLO_CONTENT_PROTOCOL_ID } from "@cello-protocol/transport";
21
+ import { CONTENT_ENCRYPTION_GUIDANCE, SESSION_CONTENT_ENCRYPTION_V1 } from "./content-encryption-status.js";
22
+ import { REFUSAL_KINDS, noteLocalCredentialRefusal, relayAckHashRefusalNotice } from "./refusal-reasons.js";
23
+ import { parkRefusalGuidance } from "./park-envelope.js";
24
+ import { terminalRelayRefusal } from "./session-terminal-refusal.js";
25
+ import { classifyRelayAuthRefusal, isTerminalRelayRefusal, isLocalCredentialRefusal } from "./session-relay-client.js";
26
+ import { REDIAL_COOLDOWN_MS } from "./session-node-types.js";
27
+ import { extractErrorMessage } from "./error-message.js";
28
+ export class SessionContentSender {
29
+ #ctx;
30
+ constructor(ctx) {
31
+ this.#ctx = ctx;
32
+ }
33
+ /**
34
+ * Send content over the session node's direct P2P content stream.
35
+ * On a dead/missing stream this returns a NAMED, diagnosable failure — never a silent success
36
+ * (which desyncs the two sides). Do not swallow a send error here.
37
+ *
38
+ * SCOPE — what this send path does, and the one thing it still does not:
39
+ *
40
+ * - It delivers content over the direct /cello/content/1.0.0 P2P stream AND submits a
41
+ * K_local-signed content_hash leaf to the relay (`session.relay.hash.submitted` below).
42
+ * Both happen. `witnessed` on the response says whether the relay accepted the leaf.
43
+ *
44
+ * - The `sequence_number` returned is still the LOCAL leaf index, not a relay-assigned global
45
+ * one. Each daemon appends in its own observation order, so two roots agree only under
46
+ * ping-ponged traffic; concurrent bidirectional sends can still diverge.
47
+ *
48
+ * ⚠️ STATED SEPARATELY BECAUSE THEY WENT STALE AT DIFFERENT TIMES. This block once said the relay
49
+ * hash-submit did not exist and that the sequence was local BECAUSE of that. The first is false
50
+ * now; the second's CLAIM holds and its REASON did not — correcting both on one verdict would
51
+ * have deleted a live limitation along with a dead one.
52
+ */
53
+ async sendContent(agentName, sessionId, content, contentHash,
54
+ /**
55
+ * Required alongside the two below — every production caller already passes one, and an optional
56
+ * parameter in front of a required one is what TypeScript refuses. Making it explicit costs
57
+ * nothing and removes the last place a positional argument can silently shift.
58
+ */
59
+ correlationId,
60
+ /**
61
+ * The DOMAIN this content belongs to, as the relay and the directory will see it. Defaults to
62
+ * MESSAGE so `cello_send` is unchanged; the document path passes 0x04/0x05. Not cosmetic — the
63
+ * directory computes `final_message` and `answered` from the witnessed kind, and both of its
64
+ * document exclusions were dead while every document leaf arrived here as a message.
65
+ *
66
+ * ⚠️ ALSO REQUIRED NOW, and for the same reason as `contentHashAlg` below — this parameter is the
67
+ * precedent, not a bystander. It defaulted to MESSAGE, the document adapter in `daemon.ts`
68
+ * silently dropped it, and the wire was wrong for a whole release: *"0.0.145 shipped the fix
69
+ * everywhere except here."* A default that matches the common case makes the omission invisible
70
+ * at every call site and at typecheck. Every caller states its kind now.
71
+ */
72
+ leafKind,
73
+ /**
74
+ * `DOD-M15-SEALWIRE-1` part B2b — the algorithm `contentHash` was produced under, taken from
75
+ * `contentHashForSession` by the caller that computed the hash.
76
+ *
77
+ * Passed rather than re-derived HERE, deliberately: re-deriving would ask "how would this
78
+ * session hash something now?", and the answer can differ from how THIS message was actually
79
+ * hashed. A hash and its label must travel together or the peer refuses a message nobody touched.
80
+ *
81
+ * ⚠️ REQUIRED, NOT DEFAULTED — review B2b-1 F4, and the default is what made four mutants
82
+ * unfalsifiable. It was `= CONTENT_HASH_ALGS.SHA256`, which equals the only value in play today,
83
+ * so DROPPING THE ARGUMENT AT ANY OF THE FIVE HOPS produced byte-identical output and the whole
84
+ * 2,800-test daemon suite stayed green. Measured at all four send sites individually.
85
+ *
86
+ * A default that equals the current value makes every threading edit invisible until the value
87
+ * changes — and the day it changes, the dropped argument mislabels the message and every peer
88
+ * refuses it as a tamper. Required makes a dropped argument a TYPECHECK failure instead of a
89
+ * test question nobody can answer.
90
+ */
91
+ contentHashAlg) {
92
+ const entry = this.#ctx.activeNodes.get(this.#ctx.sessionKey(agentName, sessionId));
93
+ if (!entry) {
94
+ // M12-P13: no node, so nothing was witnessed and nothing was queued — the caller must NOT
95
+ // commit a leaf for this. `durable` is a required field precisely so a new failure branch
96
+ // cannot be added without answering the question every caller now asks.
97
+ return { ok: false, reason: "session_node_unavailable", error: "no active session node for this session", durable: false };
98
+ }
99
+ // R1 (MSG-001-3b): witness the message-leaf HASH to the relay FIRST, INDEPENDENT of
100
+ // direct delivery. The relay is the ordering authority (Structure 2): it assigns the
101
+ // canonical sequence from the hash whether or not the counterparty is reachable for direct
102
+ // content. So an OFFLINE recipient still gets a sequence, and the parked content is later
103
+ // recovered AT that sequence (DOD-MSG-4 recovery-not-desync). The relay only ever sees the
104
+ // hash (INV-3). Best-effort: a relay miss degrades to local-only sequencing.
105
+ //
106
+ // This MUST run BEFORE the direct send, not after it: an offline recipient never completes a
107
+ // direct send, and sequencing after it would leave their content with no sequence at all.
108
+ //
109
+ // DOD-MSG-4 (self-ordering content frame): the relay's committed ordering record for this leaf,
110
+ // captured from the hash submit so it can be stamped into the content frame (and the parked
111
+ // entry). Undefined if the relay is unreachable / an old relay — the receiver then falls back to
112
+ // the leaf_deliver witness stream / arrival order.
113
+ let orderingS1;
114
+ let orderingS2;
115
+ /**
116
+ * `DOD-M15-AUTHORSHIP-ABSENT-1`: the sender's signature over `orderingS1`, when the relay
117
+ * witnessed this leaf. Undefined when it did not — and the frame builder below then signs a
118
+ * Structure 1 of its own rather than shipping a frame with nothing to check.
119
+ */
120
+ let orderingSig;
121
+ /**
122
+ * DOD-M15-SEALWIRE-1 bullet 5, SENT half. Our own Ed25519 signature over `orderingS1`.
123
+ *
124
+ * The submit path already computes this — `keyProvider.sign(structure1)` — and puts it on the
125
+ * wire as `sender_signature`. It was simply never handed back, which is the whole of the defect:
126
+ * a RECEIVED row could prove its author to a third party and a SENT row could not, so half the
127
+ * transcript was provable and half was assertion.
128
+ */
129
+ let sentAuthorship;
130
+ // DOD-M12B-INDEX-1: the relay's answer to "where does this message go", carried to the caller.
131
+ let assignedSeq;
132
+ // DOD-MP-SESSION-RETIRE-1 — the relay's answer SURVIVES to the caller even when the direct send
133
+ // then succeeds. `relay_session_gone` is deliberately not terminal (it also fires for perfectly
134
+ // live sessions whenever the relay restarts, because the relay stores sessions in memory), so
135
+ // this path warns and carries on — and the send returns `ok: true, delivered: true` for a leaf
136
+ // that was never witnessed. The content arrives; the RECORD stops growing, silently.
137
+ //
138
+ // Reporting it does not change success or failure for any existing caller. It lets the document
139
+ // worker, which has no human in the loop, notice that a session's record is dead and route
140
+ // around it. Without this the suspicion counter could never see the one reason it exists for,
141
+ // and the successful direct send actively CLEARED it.
142
+ let relayRefusal;
143
+ if (entry.relayClient && entry.relaySessionIdBytes) {
144
+ try {
145
+ const witnessed = await entry.relayClient.submitMessageHash(entry.node, entry.relaySessionIdBytes, contentHash, leafKind);
146
+ if (witnessed.ok) {
147
+ orderingS1 = witnessed.structure1_cbor;
148
+ orderingS2 = witnessed.structure2_cbor;
149
+ // `DOD-M15-AUTHORSHIP-ABSENT-1`: the signature that goes ON THE FRAME beside `orderingS1`.
150
+ // Captured here, next to the bytes it signs, because a signature assigned anywhere else
151
+ // could end up beside a different Structure 1 — a proof next to the wrong signed bytes is
152
+ // worse than no proof, since it looks checkable and fails.
153
+ orderingSig = witnessed.sender_signature;
154
+ /**
155
+ * PAIRED WITH THE BYTES IT SIGNS, in one place, so the two can never be assigned apart.
156
+ *
157
+ * ⚠️ THE PUBKEY COMES FROM INSIDE `structure1_cbor`, NOT FROM AN AGENT LOOKUP — the same
158
+ * source the RECEIVED half uses. A verifier checks the signature against the key in the
159
+ * signed bytes; storing any other key would produce a row that looks checkable and fails,
160
+ * and a lookup could drift from what was actually signed (a rotated identity, the wrong
161
+ * agent resolved by name). Taking it from the signed bytes makes that class impossible.
162
+ */
163
+ if (witnessed.structure1_cbor && witnessed.sender_signature) {
164
+ /**
165
+ * ⚠️ THE DROP IS SOFT, BUT IT MUST NOT BE SILENT — review pass 1, F2.
166
+ *
167
+ * My comment here claimed the resulting row is "distinguishable from one that carries a
168
+ * signature". True, and it misses the comparison that matters: **it is byte-identical to
169
+ * an UNWITNESSED send** — `self_authored`, both proof columns NULL. So the record cannot
170
+ * tell "the relay never witnessed this" from "we witnessed it, held the proof, and
171
+ * dropped it decoding our own bytes."
172
+ *
173
+ * And the asymmetry with the received half is the argument. The received half is soft
174
+ * about a missing ORDERING record (`#recordFrameOrdering`) because the COUNTERPARTY
175
+ * supplied those bytes — an absence we cannot resolve. It is not soft about a missing
176
+ * authorship proof any more; `DOD-M15-AUTHORSHIP-ABSENT-1` refuses that outright.
177
+ * Here **we produced them**, in `session-relay-client.ts`, moments earlier. A failure
178
+ * means our own encoder and decoder disagree: an internal invariant break that would
179
+ * strip authorship from every sent row for the life of the process. Soft is still right
180
+ * — throwing would lose a delivered message over a missing attestation — but soft and
181
+ * unannounced is the silent-fallback pattern this milestone exists to find.
182
+ */
183
+ const dropAuthorship = (reason, error, extra) => {
184
+ this.#ctx.logger.warn("session.sent.authorship.unavailable", {
185
+ sessionId,
186
+ agentName,
187
+ reason,
188
+ // WHICH ROW lost its proof — review pass 2, M2. Without the sequence an operator knows
189
+ // a message is unproven and not which one, in a transcript of hundreds.
190
+ ...(witnessed.ok ? { relaySequence: witnessed.sequence_number } : {}),
191
+ ...(extra ?? {}),
192
+ ...(error === undefined ? {} : { error: extractErrorMessage(error) }),
193
+ impact: "this sent message is recorded with attribution 'self_authored' and NO signature, so the row " +
194
+ "asserts its author rather than proving one. It is indistinguishable in the database from a " +
195
+ "send the relay never witnessed — this log line is the only thing that tells them apart.",
196
+ guidance: "We produced these bytes ourselves, so a decode or shape failure here means this daemon's own " +
197
+ "encoder and decoder disagree. Treat it as an internal invariant break, not a peer problem.",
198
+ correlationId,
199
+ });
200
+ };
201
+ try {
202
+ // Structure 1 = [version, content_hash, sender_pubkey, session_id, last_seen_seq,
203
+ // timestamp] (+ last_seen_hash at 6 on a v2 claim — 020-ACKHASH). The sender pubkey is
204
+ // index 2 in both; the same read `#recordFrameOrdering` does for the received half.
205
+ const s1Decoded = decodeStructure1(witnessed.structure1_cbor);
206
+ // NAMED AT ITS CAUSE. A failed decode falling through to the shape check below would
207
+ // report `pubkey_shape` for bytes that never yielded a pubkey at all, sending the next
208
+ // reader to audit a key when the layout is what disagreed.
209
+ const pk = s1Decoded.ok ? s1Decoded.fields.senderPubkey : undefined;
210
+ // The SIGNATURE is length-checked too (review F2): the guard checked the pubkey's 32
211
+ // bytes and only truthiness on the signature, so a zero-length one would have stored an
212
+ // uncheckable BLOB. Not reachable today — `sign()` returns 64 — and the asymmetry is
213
+ // the kind that stops being unreachable quietly.
214
+ /**
215
+ * ⚠️ VERIFIED BEFORE IT IS STORED, NOT SHAPE-CHECKED — review pass 2, H2, and this is
216
+ * worth more than any test of it.
217
+ *
218
+ * This used to accept the pair on 32 bytes and 64 bytes. A shape check cannot tell a
219
+ * real proof from 96 bytes that resemble one, and **nothing downstream ever checks
220
+ * either**: not at write time, and not at read time, because no production reader of
221
+ * these columns exists yet. So a wrong Structure-1 index, a wrong key, or a pair from
222
+ * two different submits would all have been persisted as a row that **looks checkable
223
+ * to an auditor and fails** — strictly worse than the honest unproven row it replaced.
224
+ *
225
+ * The received half has always done this (`#verifyAuthorshipClaim` verifies before
226
+ * storing and treats a failure as fatal). The sent half did not, and every ingredient
227
+ * was already in scope on this line.
228
+ *
229
+ * What it buys over a test: a wrong index becomes **impossible to persist**. The row
230
+ * gets NULL, the warn below fires, and it happens in production on the machine that
231
+ * caused it — not in a suite someone has to remember to write.
232
+ *
233
+ * NOT fatal, unlike the received half, and the asymmetry is deliberate: there the
234
+ * failure means a COUNTERPARTY sent something that does not verify, which is an
235
+ * identity problem. Here it means our own encoder and decoder disagree — bad, but it
236
+ * must not cost the operator a delivered message.
237
+ */
238
+ if (!s1Decoded.ok) {
239
+ dropAuthorship("structure1_decode_failed", undefined, { structure1Reason: s1Decoded.reason });
240
+ }
241
+ else if (!(pk instanceof Uint8Array) || pk.length !== 32) {
242
+ dropAuthorship("pubkey_shape", undefined, { pubkeyLen: pk instanceof Uint8Array ? pk.length : -1 });
243
+ }
244
+ else if (witnessed.sender_signature.length !== 64) {
245
+ dropAuthorship("signature_shape", undefined, { sigLen: witnessed.sender_signature.length });
246
+ }
247
+ else if (!verify(pk, witnessed.structure1_cbor, witnessed.sender_signature)) {
248
+ dropAuthorship("pair_does_not_verify");
249
+ }
250
+ else {
251
+ sentAuthorship = { senderPubkey: pk, senderSig: witnessed.sender_signature };
252
+ }
253
+ }
254
+ catch (err) {
255
+ // NOT a decode failure — review F3. `decodeStructure1` never throws and its failure is
256
+ // handled as the first branch above, with its own reason. What can still throw in here
257
+ // is `verify()` and the Buffer work, so this names that instead of sending the reader
258
+ // to audit a CBOR layout that decoded fine.
259
+ dropAuthorship("authorship_verify_threw", err);
260
+ }
261
+ }
262
+ // 1-BASED → 0-BASED. The relay numbers the first leaf of a session 1
263
+ // (`relay-node.ts`: `const seq = state.seq_counter + 1`), and this tree is 0-indexed.
264
+ // Every RECEIVE path in this file normalises with -1 and says so; the send path took the
265
+ // raw number, which puts every comparison against `tree.size()` one position out — so a
266
+ // perfectly healthy first message reads as "ahead of the tail" and is held behind a gap
267
+ // that does not exist. Do not remove this without changing both receive sites too.
268
+ assignedSeq = witnessed.sequence_number - 1;
269
+ /**
270
+ * DOD-M15-SEALPRECOND-1 — FROM HERE UNTIL `placeOwnLeaf`, THIS TREE IS SHORT AND KNOWS IT.
271
+ *
272
+ * The relay has ordered this leaf, so every other party counts it, and this side cannot
273
+ * append it until the caller places it at the assigned position (DOD-M12B-INDEX-1, which
274
+ * is not being changed here). A seal signed inside that span covers a tree one leaf short
275
+ * of the relay's leaf set: `merkle_root_mismatch`, and a receipt lost permanently for
276
+ * BOTH sides — measured on session `e7dd3f43…`, 2026-09-11.
277
+ *
278
+ * Recorded rather than inferred, for the reason `missingLeaves` counts a witness map
279
+ * instead of subtracting `treeSize` from `highWaterSeq`: the relay's sequence space holds
280
+ * CTRL leaves and this tree does not, so arithmetic between the two reads a phantom gap
281
+ * forever after the first seal.
282
+ */
283
+ this.#noteOwnLeafOrdered(agentName, sessionId, Buffer.from(contentHash).toString("hex"), assignedSeq);
284
+ this.#ctx.logger.info("session.relay.hash.submitted", {
285
+ sessionId,
286
+ // BOTH SPACES, NAMED. The relay's number is 1-based and the leaf index is 0-based, and
287
+ // reading one as the other is the defect this milestone exists to stop — so a log that
288
+ // carries only "sequenceNumber" invites exactly that mistake on the next investigation.
289
+ relaySequence: witnessed.sequence_number,
290
+ leafIndex: assignedSeq,
291
+ correlationId,
292
+ });
293
+ }
294
+ else if (isTerminalRelayRefusal(witnessed.reason)) {
295
+ // TERMINAL — REFUSE THE SEND, AND RETIRE THE SESSION. The relay has ended this session, so
296
+ // this leaf can never enter the record and neither can any leaf after it. Continuing would
297
+ // deliver content the conversation cannot prove it exchanged, and report `delivered: true`
298
+ // while doing it.
299
+ //
300
+ // That is not hypothetical. Measured 2026-08-09: a session whose relay had sealed it after
301
+ // both away-responders fired ran for 68 more minutes and 8 more messages, every send
302
+ // reporting success, against a chain that had stopped growing at six leaves.
303
+ //
304
+ // Refused rather than parked: parking is for content the peer has not received YET. There
305
+ // is no yet.
306
+ //
307
+ // DOD-MP-SESSION-RETIRE-1 — extracted to a function so the behaviour is REACHABLE BY A
308
+ // TEST. Inline, it needed a live `#activeNodes` entry holding a real relay client, which
309
+ // no unit test can construct — so nothing could assert what this daemon DOES about a
310
+ // terminal refusal, and for a long time the answer was "logs it and carries on". The seam
311
+ // that must be substituted is the relay's ANSWER; the thing under test is the response to
312
+ // it. Splitting them makes the second testable without faking the first.
313
+ return terminalRelayRefusal({
314
+ logger: this.#ctx.logger,
315
+ // FULL TEARDOWN, not a status write. Every other terminal path goes through these,
316
+ // and the difference is not bookkeeping: `destroySessionNode` also records the
317
+ // terminal answer for a BLOCKED `cello_receive` (which otherwise hangs to timeout),
318
+ // detaches the relay stream, stops the libp2p node, evicts the plaintext caches, and
319
+ // re-arms the standing receiver — which on a fixed-port deployment is the moment the
320
+ // port is freed. A DB-only flip leaves the corpse in `#activeNodes` holding the very
321
+ // port the replacement session may need, which would defeat this fix's own purpose.
322
+ //
323
+ // THE TWO TERMINAL REASONS ARE NOT THE SAME FACT and must not share a status:
324
+ // session_sealed — the seal really completed; a FROST certificate exists at the
325
+ // directory and `cello_sealed_receipt` pulls it on a local miss.
326
+ // session_not_found — the relay never held it or lost it. There may be no
327
+ // certificate anywhere, so writing "sealed" would be a
328
+ // FABRICATED NOTARIZATION CLAIM: `cello_close_session` would
329
+ // answer "already sealed, view its notarization" while the
330
+ // receipt read answers "not sealed yet" — the two-answers-
331
+ // pointing-at-each-other deadlock `seal-certificate-pull.ts`
332
+ // exists to kill. `abandoned` is the state invented for
333
+ // locally-terminal-with-nothing-to-notarize.
334
+ // ONLY `session_sealed` RETIRES ANYTHING. The other terminal reason,
335
+ // `session_not_found`, is documented THREE FUNCTIONS AWAY as **transient**
336
+ // (DOD-FIRSTMSG-WITNESS-1): the relay does not hold the session YET, and in all 23
337
+ // logged first-message failures the assignment landed 5ms–2.1s after the rejected
338
+ // submit. Retiring on it would destroy a live session seconds old — trading a stuck
339
+ // document for a killed conversation, which is a strictly worse bug than the one this
340
+ // unit fixes. It still refuses the send; it just does not reach for the shovel.
341
+ retireSession: (id) => {
342
+ /**
343
+ * `session_sealed` retires as SEALED. `seal_refused` retires as ABANDONED —
344
+ * `DOD-M15-TERMINAL-REASON-1`, and the distinction is the whole reason that unit
345
+ * exists.
346
+ *
347
+ * A refused seal has NO certificate: a directory read it and rejected it. Writing
348
+ * `sealed` here would be a fabricated notarization claim — `cello_close_session`
349
+ * answering "already sealed, view its notarization" while the receipt read answers
350
+ * "not sealed yet", which is the two-answers-pointing-at-each-other deadlock
351
+ * described above. `abandoned` is the state invented for exactly this:
352
+ * locally terminal with nothing to notarize.
353
+ */
354
+ if (witnessed.reason === "seal_refused") {
355
+ // `abandonSession`, not a hand-rolled flip-then-teardown: it already does the
356
+ // status write synchronously BEFORE the async teardown yields, which is the
357
+ // ordering the comment above spends a paragraph on. Reimplementing it here would
358
+ // be a second copy of that reasoning, free to drift from the first.
359
+ void this.#ctx.abandonSession(agentName, id);
360
+ return;
361
+ }
362
+ if (witnessed.reason !== "session_sealed")
363
+ return;
364
+ // STATUS FIRST AND SYNCHRONOUS, teardown second — the order `abandonSession` uses,
365
+ // and for a sharper reason here: `destroySessionNode` returns early at its
366
+ // `if (!entry) return` when the node is not in `#activeNodes`, and the status write
367
+ // lives AFTER that guard. There is an entry on this path today (we are mid-send on
368
+ // its relay client), but resting the whole fix on that is resting it on a
369
+ // coincidence — a concurrent teardown would leave the row `active` and the loop
370
+ // would resume. The flip is the load-bearing half; the teardown is what makes the
371
+ // memory agree with it.
372
+ this.#ctx.updateSessionStatus(agentName, id, "sealed");
373
+ void this.#ctx.destroySessionNode(agentName, id, "sealed");
374
+ },
375
+ }, { sessionId, reason: witnessed.reason, ...(witnessed.detail === undefined ? {} : { detail: witnessed.detail }), correlationId });
376
+ }
377
+ else {
378
+ this.#ctx.logger.warn("session.relay.hash.submit.failed", {
379
+ sessionId,
380
+ reason: witnessed.reason,
381
+ // The relay's own words about what happened, when it sent any. `reason` is the class and
382
+ // this is the cause — without it a refusal reaches the operator as a bare code, which is
383
+ // the state this field existed to end and never actually did.
384
+ ...(witnessed.detail === undefined ? {} : { detail: witnessed.detail }),
385
+ correlationId,
386
+ });
387
+ /**
388
+ * ─── 033-ACKEMIT: THIS ONE REACHES THE OPERATOR, NOT JUST THE LOG ─────────────────────
389
+ *
390
+ * Every other refusal on this branch is an availability answer — the relay is busy, the
391
+ * session is not recorded, the stream died — and the send degrades to unwitnessed, which
392
+ * is the documented path. These two are not availability. The WITNESS is telling us that
393
+ * the acknowledgement this daemon signed disagrees with the record, and that is a
394
+ * statement about the integrity of the conversation.
395
+ *
396
+ * `logger.warn` followed by a bare assignment is the exact shape Invariant 2's recurring
397
+ * box names — a guard that fires correctly into a file nobody opens. The named surface is
398
+ * `noteContentRefusal`, the same one every inbound refusal in this file uses, so it lands
399
+ * where the operator already looks for "something was rejected and here is why".
400
+ *
401
+ * It does NOT stop the send. The relay refused to witness this leaf, so the message
402
+ * degrades to unwitnessed exactly as any other refusal does and the operator keeps their
403
+ * conversation; what changes is that they are told the record has stopped agreeing with
404
+ * itself, at the moment it happens, instead of discovering it at the seal.
405
+ */
406
+ if (witnessed.reason === "ack_hash_mismatch" || witnessed.reason === "ack_hash_unverifiable") {
407
+ /**
408
+ * THE SENTENCES LIVE IN `refusal-reasons.ts` — 033-ACKEMIT review F6. They were inline
409
+ * here, behind a real relay answering a real refusal, so nothing could test them; and
410
+ * the one that was wrong (a remedy naming a relay handover this system does not have)
411
+ * was wrong for as long as that lasted.
412
+ */
413
+ const { impact, guidance } = relayAckHashRefusalNotice(witnessed.reason === "ack_hash_unverifiable", this.#ctx.mailboxRouteAvailable(agentName));
414
+ this.#ctx.logger.error("session.relay.ack_hash.refused", {
415
+ agentName, sessionId, correlationId, reason: witnessed.reason,
416
+ ...(witnessed.detail === undefined ? {} : { detail: witnessed.detail }),
417
+ impact, guidance,
418
+ });
419
+ this.#ctx.notices.noteContentRefusal(agentName, sessionId, witnessed.reason, {
420
+ kind: REFUSAL_KINDS.REFUSED, impact, guidance,
421
+ });
422
+ }
423
+ // DOD-M15-TOKENSTALE-1 — about the AGENT, not this message: see `noteLocalCredentialRefusal`.
424
+ if (isLocalCredentialRefusal(witnessed.reason))
425
+ noteLocalCredentialRefusal(this.#ctx, agentName, sessionId, correlationId, witnessed.reason, classifyRelayAuthRefusal(witnessed.reason).advice);
426
+ relayRefusal = witnessed.reason;
427
+ }
428
+ }
429
+ catch (relayErr) {
430
+ this.#ctx.logger.warn("session.relay.hash.submit.failed", { sessionId,
431
+ reason: extractErrorMessage(relayErr),
432
+ correlationId,
433
+ });
434
+ }
435
+ }
436
+ // Attempt direct peer↔peer content delivery. On success the receiver's `persisted` ACK
437
+ // resolves the awaiting timer; on failure (counterparty offline) the hash is already
438
+ // witnessed above, so the caller / TTF path parks the SEALED content to the relay
439
+ // store-and-forward backstop and the recipient recovers it at the witnessed sequence (2b).
440
+ // Held outside the try so the catch can retire a stream that was opened and then failed to
441
+ // write. Without it every failure leaks the OUTBOUND half of the stream the receiver-side
442
+ // `finally` retires — same defect, other end, other cap (64 outbound per protocol per
443
+ // connection). See the note on #handleContentStream's finally.
444
+ let sendStream;
445
+ // 🔗 `DOD-M15-SELFCHAIN-1`: true when THIS side built the claim, so this side owns advancing its
446
+ // chain once the message has gone. Declared out here because the PARK path — which is also a
447
+ // delivery — runs in the catch below.
448
+ let ownClaimAwaitingSend = false;
449
+ /**
450
+ * 034-CARRYLEAF — HOISTED OUT OF THE TRY so the PARK path in the catch can carry them.
451
+ *
452
+ * The park copy is built when the direct send fails, which is inside the catch below; declaring
453
+ * these in the try left the parked envelope with only the relay-WITNESSED claim, so a message
454
+ * its author deliberately did not witness was parked with no ordering claim at all and could
455
+ * never be witnessed by its recipient.
456
+ */
457
+ let frameS1;
458
+ let frameSig;
459
+ try {
460
+ /**
461
+ * ─── EVERY FRAME CARRIES ITS OWN PROOF — `DOD-M15-AUTHORSHIP-ABSENT-1` ────────────────────
462
+ *
463
+ * Structure 1 used to be built and signed INSIDE the relay submit, so a send the relay never
464
+ * witnessed put a frame on the wire with nothing on it to check — and the receiver, having no
465
+ * proof to compare, ingested it and attributed it anyway. There was always something to sign;
466
+ * nobody signed it.
467
+ *
468
+ * ⚠️ `structure2_cbor` IS DROPPED WHEN WE BUILD OUR OWN, and that pairing is load-bearing. The
469
+ * relay's record commits its copy of the sender's signature to the EXACT Structure 1 that was
470
+ * submitted; put it beside a Structure 1 built here (different timestamp, different
471
+ * last_seen_seq) and the receiver's cross-check fails against bytes that were never altered —
472
+ * a freeze on an honest message. The two travel together or the relay's half does not travel.
473
+ *
474
+ * ⚠️ SIGNED HERE, BEFORE THE SESSION KEY IS READ, and the order is load-bearing rather than
475
+ * tidy. `sessionKey` below is read once and used to seal the body several `await`s later; a
476
+ * key agreed with the counterparty inside that window leaves this side sealing under the key
477
+ * it captured while the far side has already moved on, and every message is refused as
478
+ * `decrypt_failed`. Signing costs two awaits, and putting them between the read and the seal
479
+ * widened that window enough to break the live two-node round trip. Measured, not reasoned
480
+ * about: seam-3 went red and both daemons logged `session.key.agreed` before the refusal.
481
+ */
482
+ frameS1 = orderingS1;
483
+ frameSig = orderingSig;
484
+ let frameS2 = orderingS2;
485
+ // A relay-witnessed claim advanced the chain on its ack; this flag covers the other case.
486
+ if (frameS1 === undefined || frameSig === undefined) {
487
+ const own = await this.#signOwnContentClaim(agentName, sessionId, entry, contentHash);
488
+ ownClaimAwaitingSend = true;
489
+ frameS1 = own.structure1;
490
+ frameSig = own.signature;
491
+ frameS2 = undefined;
492
+ /**
493
+ * ⚠️ **AND OUR OWN TRANSCRIPT ROW GETS THE PROOF TOO** — review M3.
494
+ *
495
+ * `sentAuthorship` is set above only when the relay witnessed the leaf, because that was
496
+ * the only path that ever produced a signature. This path produces one — and dropping it
497
+ * here would leave the counterparty's transcript able to prove we wrote the message while
498
+ * ours recorded `self_authored` with a NULL signature. That is exactly the half-provable
499
+ * transcript `DOD-M15-SEALWIRE-1` bullet 5 exists to close, reappearing on the one path
500
+ * that had no proof to lose before this unit and has one now.
501
+ *
502
+ * VERIFIED BEFORE IT IS STORED, the same discipline as the witnessed path: the pubkey comes
503
+ * from INSIDE the bytes we signed, never from an agent lookup, and a pair that does not
504
+ * verify is dropped loudly rather than persisted as a row that looks checkable and fails.
505
+ * A failure here means this daemon's own encoder and decoder disagree.
506
+ */
507
+ const s1Decoded = decodeStructure1(own.structure1);
508
+ const pk = s1Decoded.ok ? s1Decoded.fields.senderPubkey : undefined;
509
+ if (!s1Decoded.ok) {
510
+ this.#ctx.logger.warn("session.sent.authorship.unavailable", {
511
+ agentName, sessionId, correlationId, reason: "own_structure1_decode_failed",
512
+ structure1Reason: s1Decoded.reason,
513
+ impact: "this sent message is recorded with attribution 'self_authored' and NO signature, so the row asserts its author rather than proving one — even though this side signed the claim it put on the wire.",
514
+ guidance: "We produced these bytes ourselves moments ago, so a decode failure here means this daemon's own encoder and decoder disagree. Treat it as an internal invariant break, not a peer problem.",
515
+ });
516
+ }
517
+ else if (!verify(pk, own.structure1, own.signature)) {
518
+ this.#ctx.logger.warn("session.sent.authorship.unavailable", {
519
+ agentName, sessionId, correlationId, reason: "own_pair_does_not_verify",
520
+ impact: "this sent message is recorded with attribution 'self_authored' and NO signature. The signature this side just produced does not verify against the key inside the bytes it signed.",
521
+ guidance: "An internal invariant break: the signer and the encoder disagree. The message still went out; only the local proof was dropped.",
522
+ });
523
+ }
524
+ else {
525
+ sentAuthorship = { senderPubkey: pk, senderSig: own.signature };
526
+ }
527
+ }
528
+ /**
529
+ * 🚨 NO KEY, NO DIRECT SEND — `DOD-M15-EPHEMERAL-AUTH-1`, and there is no plaintext fallback.
530
+ *
531
+ * Throwing here rather than sending in the open, because the catch below PARKS the content —
532
+ * and the mailbox copy is sealed to the counterparty's long-term identity key, so the message
533
+ * still travels encrypted and still arrives. The failure mode is a delay, never an exposure.
534
+ *
535
+ * A fallback to plaintext would be a thing an attacker steers a session into: strip the key
536
+ * frame and a system that "carries on, degraded" gives up the body while the operator reads a
537
+ * warning they have learned to scroll past. That is why this is a throw and not a warning.
538
+ */
539
+ // FAIL FAST, before a stream is opened for a message that cannot go out. The key this reads is
540
+ // NOT the one that seals the body — see the read beside `sealSessionContent` below.
541
+ const preflight = this.#ctx.ephemerals.contentEncryptionState(agentName, sessionId);
542
+ if (preflight.key === null) {
543
+ throw new Error(`content_not_encryptable: ${preflight.reason} — ${CONTENT_ENCRYPTION_GUIDANCE[preflight.reason]}`);
544
+ }
545
+ const stream = await this.openContentStream(agentName, sessionId, entry, correlationId);
546
+ sendStream = stream;
547
+ // AC-001/AC-003: arm the TTF tracking BEFORE the frame goes on the wire. The
548
+ // receiver's `persisted` ACK can come back fast (in-process / low-latency
549
+ // transports), so registering the awaiting entry after send would let the ACK
550
+ // race ahead of it and be dropped — the timer would then spuriously fire. The
551
+ // content is delivered to the wire but NOT yet confirmed persisted; the ACK
552
+ // resolves it (content.delivery.acked) and TTF expiry hands it to the park
553
+ // backstop. The correlationId rides in the frame so the receiver's
554
+ // session.content.received shares ONE flow id with the sender.
555
+ /**
556
+ * ⚠️ THE TRACKER KEEPS THE PLAINTEXT, AND THAT IS DELIBERATE — 007-CRYPTO.
557
+ *
558
+ * When live delivery times out, THIS is the copy the park backstop seals into the relay
559
+ * mailbox, under `sealToRecipient` to the counterparty's long-term identity key. Encrypting
560
+ * `content` in place would put a body locked under a session key — one about to be destroyed —
561
+ * inside the mailbox envelope, and the recipient would open the outer seal onto bytes nothing
562
+ * can read. It passes every live-delivery test and fails only for messages that park.
563
+ *
564
+ * So the session key encrypts the copy that goes ON THE WIRE, below, and nothing else.
565
+ */
566
+ // 034-CARRYLEAF: the SIGNED claim and its domain ride the awaiting entry, so a message that
567
+ // ends up re-parked after a restart still reaches its recipient in a shape they can witness.
568
+ this.#trackAwaitingAck(agentName, sessionId, content, contentHash, correlationId, frameS1 ?? orderingS1, orderingS2, contentHashAlg, frameSig, leafKind);
569
+ /**
570
+ * THE WIRE COPY. `content_hash` above was computed over the PLAINTEXT and stays that way: the
571
+ * transcript, the seal and the salted hash all depend on it meaning what it means today, and
572
+ * the receiver decrypts before it verifies.
573
+ *
574
+ * ⚠️ **THE KEY IS READ HERE, ADJACENT TO THE SEAL, AND IT USED TO BE READ FAR ABOVE.**
575
+ *
576
+ * The read sat before `#openContentStream`, so the captured key crossed an `await` — several,
577
+ * once this unit added signing — before it sealed anything. A session key agreed with the
578
+ * counterparty inside that window left this side sealing under the key it captured while the
579
+ * far side had already moved on, and **every message was then refused as `decrypt_failed`**:
580
+ * a false tamper report on honest content, on both sides, for the life of the session.
581
+ *
582
+ * Not theoretical. It is what reddened four live-libp2p fixtures when identity keys were first
583
+ * wired into them — both daemons logged `session.key.agreed`, and the refusal followed.
584
+ *
585
+ * A window cannot be closed by reasoning about who wins it, only by removing it: nothing may
586
+ * run between the read and the seal. The preflight above stays because failing before a stream
587
+ * is opened is worth one extra read.
588
+ *
589
+ * ⚠️ THIS CLOSES THE LOCAL WINDOW AND NOT THE CLASS (review F8). The sender still seals at T
590
+ * and the receiver still decrypts at T+flight, so a re-key landing in THAT interval produces
591
+ * the same false tamper report. What is removed is the part this side controls; the rest is a
592
+ * property of there being two machines.
593
+ */
594
+ const sealState = this.#ctx.ephemerals.contentEncryptionState(agentName, sessionId);
595
+ if (sealState.key === null) {
596
+ // Reachable only if the key vanished between the preflight and here — a re-key or a
597
+ // teardown mid-send. Named as its own cause rather than reusing the preflight's, so a log
598
+ // reader can tell "never had one" from "had one and lost it while sending".
599
+ throw new Error(`content_not_encryptable: ${sealState.reason} — the key was present when this send began ` +
600
+ `and gone by the time it sealed. ${CONTENT_ENCRYPTION_GUIDANCE[sealState.reason]}`);
601
+ }
602
+ const wireBody = sealSessionContent(sealState.key, content);
603
+ const frame = encodeCbor({
604
+ type: "content_frame",
605
+ session_id: sessionId,
606
+ content_hash: contentHash,
607
+ content_bytes: wireBody,
608
+ // WHICH scheme the body is under. Present because the receiver must not have to guess from
609
+ // a length, and absent is not a valid state — a frame without it is refused, not read raw.
610
+ content_encryption: SESSION_CONTENT_ENCRYPTION_V1,
611
+ correlation_id: correlationId,
612
+ // DOD-MSG-4 (self-ordering): the relay's signed ordering record, so the receiver verifies +
613
+ // orders from the frame ALONE (no dependence on the separate leaf_deliver witness timing).
614
+ // structure1_cbor = sender-signed bytes (verify); structure2_cbor = relay's committed seq +
615
+ // prev_root (order). Structure 2 is omitted if the relay was unreachable — the receiver
616
+ // falls back to the witness stream for POSITION. Structure 1 and its signature are never
617
+ // omitted: `DOD-M15-AUTHORSHIP-ABSENT-1`, and a frame without them is refused on arrival.
618
+ structure1_cbor: frameS1,
619
+ sender_signature: frameSig,
620
+ structure2_cbor: frameS2,
621
+ // DOD-M15-SEALWIRE-1 part B2b: HOW `content_hash` was produced. An older peer ignores an
622
+ // unknown CBOR key, so emitting it is safe for every build in existence; a newer one reads
623
+ // it and verifies under the named algorithm instead of assuming.
624
+ content_hash_alg: contentHashAlg,
625
+ /**
626
+ * 034-CARRYLEAF review F5 — WHICH LEAF DOMAIN this content belongs to.
627
+ *
628
+ * Documents and rejection envelopes ride this same frame, and the receiver had no way to
629
+ * recover their kind: it appended them locally as "doc" from its own inspection of the
630
+ * body, while anything it witnessed on the sender's behalf went to the relay as a MESSAGE
631
+ * leaf. The certified root is over content hashes, so no root diverges — but the relay's
632
+ * canonical log and the carried `leaf_kind` would describe the leaf as something it is not,
633
+ * and a leaf kind selects a HASH DOMAIN everywhere else in this protocol.
634
+ *
635
+ * Same argument as `content_hash_alg` beside it: an older peer ignores an unknown CBOR key,
636
+ * so emitting it is safe for every build in existence, and a receiver that does not see it
637
+ * declines to witness rather than guessing (see `#witnessReceivedLeaf`).
638
+ */
639
+ leaf_kind: leafKind,
640
+ });
641
+ // Injected dial failure — thrown from inside the try so it lands in exactly the catch the
642
+ // real connection_lost lands in, and the whole downstream path (untrack → park → durable
643
+ // enqueue) runs unmodified.
644
+ if (this.#ctx.sendFaultRemaining > 0) {
645
+ this.#ctx.sendFaultRemaining -= 1;
646
+ this.#ctx.logger.warn("content.send.fault.injected", { sessionId, contentHash: Buffer.from(contentHash).toString("hex") });
647
+ throw new Error("connection_lost: injected direct-send fault");
648
+ }
649
+ stream.send(lp.encode.single(frame));
650
+ // NOT SWALLOWED. This was `try { await stream.close(); } catch { }` followed by
651
+ // `delivered: true` — which reports a frame as delivered when the flush failed.
652
+ //
653
+ // `close()` waits for the write buffer to drain, so a reset mid-flush throws HERE, and that
654
+ // is precisely the case where the bytes never left. Discarding it made `delivered: true` mean
655
+ // "we called send and close did not visibly complain", while every caller reads it as "the
656
+ // peer has it" — and the sender then never retries, because nothing told it to.
657
+ //
658
+ // Letting it reach the catch below is the honest outcome: that path parks the content against
659
+ // the relay backstop and reports `delivered: false` if it can, or `ok: false` if it cannot.
660
+ // A close that failed for a benign reason costs a redundant park, which the receiver dedups
661
+ // on the content hash. A false delivered costs the message.
662
+ await stream.close();
663
+ /**
664
+ * 🔗 THE MESSAGE HAS GONE, SO THE CHAIN ADVANCES — `DOD-M15-SELFCHAIN-1`, review F7.
665
+ *
666
+ * Below `stream.close()` deliberately: close waits for the write buffer to drain, so a reset
667
+ * mid-flush throws above this line and the bytes never left. Advancing before it would point
668
+ * the chain at a message the counterparty never saw, and every later message would then be
669
+ * refused by them for a reason that names tampering.
670
+ */
671
+ if (ownClaimAwaitingSend)
672
+ this.#advanceOwnChain(agentName, sessionId, entry, contentHash);
673
+ this.#ctx.liveness.clearSessionImpairment(agentName, sessionId, "direct_send", correlationId);
674
+ return { ok: true, delivered: true, ...(assignedSeq === undefined ? {} : { sequenceNumber: assignedSeq }), ...(sentAuthorship === undefined ? {} : { authorship: sentAuthorship }), ...(relayRefusal === undefined ? {} : { relayRefusal }) };
675
+ }
676
+ catch (err) {
677
+ /**
678
+ * Review F7: a content-key fault is NOT a transport fault, and labelling it `direct_send`
679
+ * points the operator at the connection when the connection is fine. `content_not_encryptable`
680
+ * is thrown twice above — once before the stream is opened, once at the seal — and both are
681
+ * about this machine's key state.
682
+ */
683
+ const failure = extractErrorMessage(err);
684
+ this.#ctx.liveness.markSessionImpaired(agentName, sessionId, {
685
+ cause: failure.startsWith("content_not_encryptable") ? "content_key" : "direct_send",
686
+ error: failure, correlationId,
687
+ });
688
+ if (sendStream !== undefined) {
689
+ try {
690
+ sendStream.abort(err instanceof Error ? err : new Error(String(err)));
691
+ }
692
+ catch { /* already gone */ }
693
+ }
694
+ // The send failed after (possibly) arming the awaiting tracking — drop it so a
695
+ // never-delivered frame does not later fire a spurious TTF park.
696
+ this.#untrackAwaitingAck(agentName, sessionId, contentHash);
697
+ // 2b: direct delivery failed (counterparty offline). The hash is already witnessed (R1, the
698
+ // sequence is assigned), so deposit the content to the relay store-and-forward backstop now;
699
+ // the recipient pulls + recovers it on next online (DOD-MSG-3/4).
700
+ // DOD-LEAVEMSG-1: the deposit is now AWAITED (was fire-and-forget) so a genuine park success
701
+ // can be reported as "dispatched to relay" instead of a raw stream failure — the operator/
702
+ // agent sees the truth (the message IS in flight, just not direct), not a false negative.
703
+ const hashHex = Buffer.from(contentHash).toString("hex");
704
+ // NAME THE CAUSE. This catch used to discard `err` outright, so a park reported only its exit
705
+ // point — "dispatched to relay" — and never what went wrong. Measured 2026-08-17: 212 parks on
706
+ // one daemon, not one of them recording a reason, which is what made a one-way session look
707
+ // like a protocol mystery for a night.
708
+ //
709
+ // `counterpartySessionPeerId` is the load-bearing field. It is recorded ONCE at session
710
+ // establishment and never refreshed. (CORRECTED 2026-08-18: it does NOT rebuild "on every
711
+ // signaling reconnect". CORRECTED AGAIN, 056-SLOTDEAD: the rebuild triggers that correction
712
+ // named are gone too — NOTHING rebuilds a standing receiver now; a lost reservation is
713
+ // re-taken in place, and only promotion into a session changes a receiver's identity.)
714
+ // If the two ever cross, every send goes one-way forever and nothing says so. With this line that becomes a single grep instead of a
715
+ // night.
716
+ this.#ctx.logger.warn("session.content.direct.send.failed", {
717
+ agentName,
718
+ sessionId,
719
+ contentHash: hashHex,
720
+ counterpartySessionPeerId: entry.counterpartySessionPeerId,
721
+ error: extractErrorMessage(err),
722
+ // "Cannot write to a stream that is closed" names where the write died, never why. The
723
+ // why is almost always the per-protocol stream cap, and these two numbers are what turn
724
+ // that from a log-measurement session into a grep.
725
+ ...this.#ctx.streamCensus(entry.node, entry.counterpartySessionPeerId),
726
+ correlationId,
727
+ });
728
+ /**
729
+ * ⚠️ **THE PARKED COPY CARRIES THE SIGNED CLAIM, NOT ONLY THE WITNESSED ONE — 034-CARRYLEAF
730
+ * review F1, and this is what closes the withholding attack on the MAILBOX route.**
731
+ *
732
+ * It passed `orderingS1`, which exists only when the relay witnessed this leaf — so a message
733
+ * the sender deliberately did not witness was parked with no ordering claim at all, and the
734
+ * recipient recovered it holding nothing the relay would accept as proof of authorship. They
735
+ * could read it and could never put it in a receipt.
736
+ *
737
+ * `frameS1`/`frameSig` are the pair the DIRECT frame carries for this same message: the
738
+ * relay's witnessed claim when there is one, and otherwise a claim this agent signed itself.
739
+ * Either way it is the author's signature over the author's own ordering claim, which is the
740
+ * only form the relay accepts when the recipient witnesses on their behalf.
741
+ */
742
+ const attempt = await this.#ctx.park.parkContent(agentName, sessionId, hashHex, content, frameS1, orderingS2, contentHashAlg, frameSig, leafKind);
743
+ if (attempt.outcome === "parked") {
744
+ /**
745
+ * 🔗 A PARK IS A DELIVERY, so the chain advances here too — `DOD-M15-SELFCHAIN-1`.
746
+ *
747
+ * The mailbox copy is sealed to the counterparty's long-term key and they WILL open it, so
748
+ * the message is part of the conversation. Leaving the chain behind here would make this
749
+ * side's next message link to something the counterparty has already moved past, and they
750
+ * would refuse it as a broken chain — a false tamper report caused by our own relay being
751
+ * briefly unreachable.
752
+ */
753
+ if (ownClaimAwaitingSend)
754
+ this.#advanceOwnChain(agentName, sessionId, entry, contentHash);
755
+ this.#ctx.liveness.noteImpairmentRetention(agentName, sessionId, "parked");
756
+ return { ok: true, delivered: false, parked: true, ...(assignedSeq === undefined ? {} : { sequenceNumber: assignedSeq }), ...(sentAuthorship === undefined ? {} : { authorship: sentAuthorship }), ...(relayRefusal === undefined ? {} : { relayRefusal }) };
757
+ }
758
+ // M12-P12: the deposit was refused, and #untrackAwaitingAck above already dropped the
759
+ // in-memory entry — so without this, NOTHING holds the content and the TTF timer that would
760
+ // have enqueued it is cancelled. The recipient has already witnessed this sequence, so it
761
+ // holds every later message in the session behind the gap, forever, and tells no one.
762
+ // Enqueue durably instead; the drain hook re-parks it the moment the standing receiver is
763
+ // rebuilt. Only on a REFUSAL — a successful deposit must not be re-parked, and an
764
+ // unconfigured session has no park target to retry against (F6).
765
+ let durable = false;
766
+ if (attempt.outcome === "refused") {
767
+ try {
768
+ // M12-P13 (review HIGH-1): `durable` is now OBSERVED from the enqueue, not asserted around
769
+ // it. Two ways this used to lie, both of which now commit a chain leaf and so cannot be
770
+ // allowed to: the queue's content-derived dedupe key collides and it silently drops the
771
+ // copy, and the `?.` no-ops entirely when the composition root never wired the hook. An
772
+ // absent hook is not a queue.
773
+ if (this.#ctx.onParkFailed === null) {
774
+ this.#ctx.logger.error("content.park.durable_enqueue.unwired", {
775
+ sessionId, contentHash: hashHex, agentName,
776
+ impact: "no durable queue is wired — the content is NOT retained and will NOT be retried",
777
+ });
778
+ }
779
+ else {
780
+ // B2b-1 review F1: the DURABLE writer. Without the 7th argument the column this unit
781
+ // added has no producer at all — every queued row would carry NULL, and the crash
782
+ // backstop would re-park a salted message as sha256 and have it refused forever.
783
+ durable = this.#ctx.onParkFailed(agentName, sessionId, hashHex, content, orderingS1, orderingS2, contentHashAlg);
784
+ }
785
+ if (!durable) {
786
+ if (this.#ctx.onParkFailed !== null) {
787
+ this.#ctx.logger.error("content.park.durable_enqueue.dropped", {
788
+ sessionId, contentHash: hashHex, agentName,
789
+ impact: "the durable queue refused this copy (identical content already queued) — it is NOT separately retained",
790
+ });
791
+ }
792
+ }
793
+ else {
794
+ // F5: the successful enqueue must be visible. Without this the live run that has to
795
+ // PROVE this fix has nothing to point at, and this log is the sender-side counterpart to
796
+ // `session.content.held` on the receiver — the two together make the trace readable.
797
+ // M12-P13: `witnessed` rides along because the caller is about to commit a hash-chain
798
+ // leaf on the strength of this. Without a relay ordering record the recipient recovers
799
+ // in arrival order instead — the accepted degradation, but it must not be invisible.
800
+ this.#ctx.logger.info("content.park.deferred", {
801
+ sessionId, contentHash: hashHex, agentName,
802
+ selfOrdering: Boolean(orderingS1 && orderingS2),
803
+ witnessed: Boolean(orderingS1 && orderingS2),
804
+ });
805
+ }
806
+ }
807
+ catch (hookErr) {
808
+ // F3: enqueueAwaitingContent throws ON PURPOSE when the persist fails, because that is
809
+ // data loss. Swallowing it into the same response the durable case returns would tell the
810
+ // operator "it will retry" about a message that is simply gone. Named for its own cause,
811
+ // and the response says so below.
812
+ this.#ctx.logger.error("content.park.durable_enqueue.failed", {
813
+ sessionId, contentHash: hashHex, agentName,
814
+ impact: "content is NOT durable and will NOT be retried — the message is lost",
815
+ error: extractErrorMessage(hookErr),
816
+ });
817
+ }
818
+ }
819
+ // error.message extracted — never [object Object]. libp2p/cross-package errors are not
820
+ // always `instanceof Error` in this realm, so fall back to a message property / JSON.
821
+ // This was fourteen lines reimplementing `extractErrorMessage` exactly; the helper it was
822
+ // duplicating already existed two files away. DOD-M15-ERRFORMAT-1.
823
+ const errMsg = extractErrorMessage(err);
824
+ // F3: the two failures are NOT interchangeable to the caller. `reason` is a contract string
825
+ // and stays put; `guidance` carries the difference, because "we are retrying this" and "this
826
+ // message is gone, send it again" demand opposite actions from the operator.
827
+ //
828
+ // The same distinction is recorded on the session, because `cello_receive` will be asked
829
+ // about this later and would otherwise have to guess — and its guess ("it was parked, do not
830
+ // resend") is the exact opposite of what the lost case needs.
831
+ this.#ctx.liveness.noteImpairmentRetention(agentName, sessionId, durable ? "durable" : "lost");
832
+ /**
833
+ * DOD-M15-SEALPRECOND-1 — the send failed, so nothing here will place this leaf.
834
+ *
835
+ * UNCONDITIONAL, including the durable case whose caller DOES place: those callers place
836
+ * synchronously on the continuation of this return, with no await between, so nothing can
837
+ * read the marker in the interval. `document-delivery-transport.ts` places nothing on a
838
+ * failure at all — leaving its marker would make every later close of that session decline,
839
+ * and a session whose only exit is force-abandon has no receipt.
840
+ */
841
+ this.#clearOwnLeafOrdered(agentName, sessionId, Buffer.from(contentHash).toString("hex"));
842
+ return {
843
+ ok: false,
844
+ reason: "session_stream_unavailable",
845
+ error: errMsg,
846
+ // Carried on the failure path too: a DURABLY QUEUED message still owns the position the
847
+ // relay witnessed for it before delivery was attempted, and its leaf must go there.
848
+ ...(assignedSeq === undefined ? {} : { sequenceNumber: assignedSeq }),
849
+ // …and so does its PROOF, for the same reason. It was signed before the hand-off failed.
850
+ ...(sentAuthorship === undefined ? {} : { authorship: sentAuthorship }),
851
+ // M12-P13: the machine-readable half of the distinction below. M12-P12 shipped it in the
852
+ // guidance SENTENCE only, so the callers that have to ACT on it — commit the leaf for a
853
+ // queued message, never for a lost one — would have had to substring-match English. None
854
+ // did, and the sequence the relay had already witnessed was left as a permanent hole.
855
+ durable,
856
+ // M12-P13 (review MEDIUM-5): the specific standing-receiver state, carried rather than
857
+ // discarded. `reason` names where this surfaced; `cause` names what actually blocked it —
858
+ // the exact distinction M12-P12 added `standingReceiverAbsenceReason()` for, which then
859
+ // died inside #parkContent. An operator keying on `reason` alone is sent to the transport
860
+ // when the blocker is the receiver.
861
+ ...(attempt.cause !== undefined ? { cause: attempt.cause } : {}),
862
+ /**
863
+ * ⚠️ A FAULT THAT IS NOT THE RELAY MUST NOT SAY IT IS — B2b-2 constraint 6.
864
+ *
865
+ * The `durable` branch below is written for a relay that is down: queued, retried, nothing
866
+ * for you to do. That is right for the case it was written for and WRONG for a producer-side
867
+ * refusal, which reaches the same branch by the same route. There the relay was never asked,
868
+ * and the drain re-parks the same entry into the same throw — so the message is genuinely
869
+ * durable and will genuinely never leave. Telling that operator to wait costs them however
870
+ * long they are willing to wait before they stop believing the message.
871
+ *
872
+ * This is the reason `cause` had to become a code first: the distinction is unbranchable
873
+ * while the field holds an English paragraph.
874
+ */
875
+ guidance: parkRefusalGuidance(attempt.cause, durable, attempt.retryAfterMs),
876
+ };
877
+ }
878
+ }
879
+ /**
880
+ * DOD-M12B-REDIAL-1 — open a content stream, re-dialling once if the connection has gone.
881
+ *
882
+ * `newStream` never dials. It looks for an already-open connection filed under the recorded peer
883
+ * id and throws `connection_lost` when there is none — and NOTHING re-dialled: not on
884
+ * `session.liveness.changed → gone`, not on signaling reconnect, not on agent offline→online, not
885
+ * in the drain hook. So one blip and that session parked EVERY message for the rest of its life,
886
+ * on both sides, permanently. The relay backstop kept the messages moving, which is exactly why
887
+ * it hid: nothing was lost, the conversation just stopped being a conversation.
888
+ *
889
+ * DEMAND-DRIVEN, never a timer. A background re-dial loop is what produced the 2026-08-17
890
+ * notification storm — surviving halves of abandoned sessions dialling continuously while the
891
+ * operator saw connection requests from agents nobody was driving. This fires only when a send
892
+ * actually needs the connection, and a cooldown bounds a burst against a peer that is genuinely
893
+ * gone: five sends cost one dial, not five.
894
+ */
895
+ async openContentStream(agentName, sessionId, entry, correlationId) {
896
+ const attempt = async () => {
897
+ if (this.#ctx.connectionLossRemaining > 0) {
898
+ this.#ctx.connectionLossRemaining -= 1;
899
+ throw { reason: "no_connection", message: "injected connection loss" };
900
+ }
901
+ return entry.node.newStream(entry.counterpartySessionPeerId, CELLO_CONTENT_PROTOCOL_ID);
902
+ };
903
+ try {
904
+ return await attempt();
905
+ }
906
+ catch (err) {
907
+ const reason = err?.reason;
908
+ // ONLY for a missing connection, and `no_connection` is the reason that means exactly that.
909
+ // NOT `connection_lost`, which is the transport's catch-all default and therefore also covers
910
+ // a stream that failed on a healthy connection — the per-protocol stream cap of
911
+ // DOD-M12B-ACK-1. Dialling there fixes nothing and shows the counterparty a connection
912
+ // request caused by a defect on this side, which is the storm this unit exists to avoid.
913
+ if (reason !== "no_connection")
914
+ throw err;
915
+ const key = this.#ctx.sessionKey(agentName, sessionId);
916
+ const addrs = this.#ctx.counterpartyAddrs.get(key);
917
+ if (!addrs || addrs.length === 0) {
918
+ // ABSENT IS NOT FINE, and it is not silent. A session we never dialled — the responder's
919
+ // half — has no addresses to dial back with, and that is a real limitation the operator
920
+ // should be able to see rather than infer from a park.
921
+ this.#ctx.logger.warn("session.transport.redial.unavailable", {
922
+ sessionId, agentName, correlationId,
923
+ impact: "the direct path is down and this side holds no address for the counterparty, so every send parks until they re-establish",
924
+ });
925
+ throw err;
926
+ }
927
+ const now = Date.now();
928
+ const notBefore = this.#ctx.redialNotBefore.get(key) ?? 0;
929
+ if (now < notBefore) {
930
+ this.#ctx.logger.debug("session.transport.redial.cooldown", {
931
+ sessionId, agentName, retryInMs: notBefore - now, correlationId,
932
+ });
933
+ throw err;
934
+ }
935
+ this.#ctx.redialNotBefore.set(key, now + REDIAL_COOLDOWN_MS);
936
+ this.#ctx.logger.info("session.transport.redial.attempted", { sessionId, agentName, addrs: addrs.length, correlationId });
937
+ const reconnected = await this.#ctx.connectToCounterparty(agentName, sessionId, addrs);
938
+ if (!reconnected.ok) {
939
+ this.#ctx.logger.warn("session.transport.redial.failed", {
940
+ sessionId, agentName, reason: reconnected.reason, error: reconnected.error, correlationId,
941
+ });
942
+ throw err;
943
+ }
944
+ this.#ctx.logger.info("session.transport.redial.succeeded", { sessionId, agentName, correlationId });
945
+ // Cleared so the NEXT blip is repaired immediately: the cooldown exists to bound a dead peer,
946
+ // not to make a live one wait.
947
+ this.#ctx.redialNotBefore.delete(key);
948
+ return attempt();
949
+ }
950
+ }
951
+ /**
952
+ * DOD-M12B-INDEX-1 — commit THIS agent's own leaf at the position the relay assigned it.
953
+ *
954
+ * The receiver has always enforced "leaf index === canonical position": content witnessed ahead
955
+ * of the next expected leaf is held, not appended out of order. The sender never did. It had the
956
+ * position in hand — the relay answers about 4 ms before the append — and called a push-only
957
+ * append that puts the leaf at the tail whatever the tail happens to be. While its own tree has
958
+ * no gap the two agree and nothing shows; the first gap puts its leaf at someone else's index,
959
+ * parts its root from the counterparty's, and the next seal gets `leaf_count_mismatch`, which is
960
+ * terminal.
961
+ *
962
+ * DELIVERY IS NOT DEFERRED BY THIS. The caller has already put the bytes on the wire; only the
963
+ * leaf waits for its slot, exactly as a received message does. Holding our own send is only
964
+ * affordable because holds are durable (DOD-M12B-STRAND-1) — before that it would have risked
965
+ * losing the message outright.
966
+ *
967
+ * `assignedSeq` absent means no ordering authority answered. That is the documented degradation
968
+ * and it appends in arrival order as before: with no position there is no discipline to enforce,
969
+ * and refusing would take messaging down whenever the relay is unreachable.
970
+ */
971
+ /**
972
+ * DOD-M15-SEALPRECOND-1 — the relay has ordered a leaf of ours; the tree has not placed it yet.
973
+ * Public so a test can drive the exact state the 2026-09-11 close was in with no live relay.
974
+ */
975
+ noteOwnLeafOrdered(agentName, sessionId, contentHashHex, assignedSeq) {
976
+ this.#noteOwnLeafOrdered(agentName, sessionId, contentHashHex, assignedSeq);
977
+ }
978
+ #noteOwnLeafOrdered(agentName, sessionId, contentHashHex, assignedSeq) {
979
+ if (assignedSeq < 0)
980
+ return;
981
+ const key = this.#ctx.sessionKey(agentName, sessionId);
982
+ let map = this.#ctx.ownLeavesOrdered.get(key);
983
+ if (!map) {
984
+ map = new Map();
985
+ this.#ctx.ownLeavesOrdered.set(key, map);
986
+ }
987
+ map.set(contentHashHex, assignedSeq);
988
+ }
989
+ #clearOwnLeafOrdered(agentName, sessionId, contentHashHex) {
990
+ const map = this.#ctx.ownLeavesOrdered.get(this.#ctx.sessionKey(agentName, sessionId));
991
+ map?.delete(contentHashHex);
992
+ if (map?.size === 0)
993
+ this.#ctx.ownLeavesOrdered.delete(this.#ctx.sessionKey(agentName, sessionId));
994
+ }
995
+ placeOwnLeaf(agentName, sessionId, contentHashHex, sentBytes, assignedSeq, correlationId,
996
+ /**
997
+ * ⚠️ NO DEFAULT, for the same reason `authorship` has none.
998
+ *
999
+ * `kind = "msg"` meant a caller writing a `doc` or a `ctrl` leaf got a `msg` leaf by saying
1000
+ * nothing, and the tree recorded a leaf kind the author never chose. TypeScript also forbids a
1001
+ * required parameter after a defaulted one, so leaving the default here would have forced
1002
+ * `authorship` back to optional — which is the defect above. Every one of the seven call sites
1003
+ * already passed a kind or wanted "msg"; making it explicit cost nothing and removes a second
1004
+ * silent answer from the same signature.
1005
+ */
1006
+ kind,
1007
+ /**
1008
+ * DOD-M15-SEALWIRE-1 bullet 5 — the proof for THIS send, so a held row keeps it.
1009
+ *
1010
+ * ⚠️ REQUIRED, AND `undefined` IS A VALID ANSWER — the two are not the same thing.
1011
+ *
1012
+ * This was `authorship?:` for exactly one review cycle, and in that cycle THREE of the seven
1013
+ * call sites omitted it: `daemon.ts` 1440, 1671, 1685 — the away-reply path, which is the
1014
+ * highest-traffic sent-writer in the daemon and the one with no human watching it. All three
1015
+ * had the proof **already in a local variable one line below**, handed to
1016
+ * `recordTranscriptMessage` and not to this method. Nothing went red, because an optional
1017
+ * parameter's whole behaviour on omission is to look deliberate.
1018
+ *
1019
+ * An unwitnessed send genuinely has no proof, so absence must stay expressible. Requiring the
1020
+ * parameter keeps that while making the caller SAY it: omission is now a type error, and
1021
+ * `undefined` is a claim the author made rather than one the signature made for them.
1022
+ */
1023
+ authorship) {
1024
+ /**
1025
+ * DOD-M15-SEALPRECOND-1 — the marker is resolved HERE, on every outcome, and that is the point.
1026
+ *
1027
+ * Appended, appended-behind-the-frontier, or held: all three have dealt with the relay's
1028
+ * position for this content, and a held leaf is already refused by `heldOwn` — counting it in
1029
+ * both places would report one message as two conditions. Cleared at the TOP so no early
1030
+ * return can leave a marker behind, which would make the session unsealable.
1031
+ */
1032
+ this.#clearOwnLeafOrdered(agentName, sessionId, contentHashHex);
1033
+ // Hydrate before reading the frontier: a durable hold this process has not read back yet would
1034
+ // make the tree look further along than it is.
1035
+ this.#ctx.held.ensureHeldRestored(agentName, sessionId);
1036
+ const nextExpected = this.#ctx.getSessionTree(agentName, sessionId).size();
1037
+ if (assignedSeq === undefined) {
1038
+ const { leafIndex } = this.#ctx.appendSessionLeaf(agentName, sessionId, kind, contentHashHex, correlationId);
1039
+ /**
1040
+ * ─── DOD-M15-UNWITNESSED-1(b): SAY IT HERE, where the code says the damage happens ────────
1041
+ *
1042
+ * This branch was entirely silent — no log, no flag — while the `position_behind_frontier`
1043
+ * branch twenty lines below logs at ERROR and its own comment says the loss occurred
1044
+ * *"at the unwitnessed append, not here."* So the system announced the consequence one send
1045
+ * later than it announced nothing about the cause.
1046
+ *
1047
+ * **This does NOT gate the seal, deliberately.** The DoD bar is explicit: do not gate on
1048
+ * suspicion, because a relay that has not witnessed a leaf YET is indistinguishable here from
1049
+ * one that never will, and refusing on the first would make a healthy session unsealable —
1050
+ * which is worse than the thing it guards. What was missing is not a refusal, it is the
1051
+ * ERROR at the moment the code already believes something was lost.
1052
+ */
1053
+ this.#ctx.logger.error("session.tree.own_leaf_unwitnessed", {
1054
+ sessionId,
1055
+ leafIndex,
1056
+ kind,
1057
+ correlationId,
1058
+ impact: "this message was appended to the local record with NO relay witness, so the ordering " +
1059
+ "authority has no copy of it. The counterparty's tree cannot gain this leaf from the " +
1060
+ "relay, so the two records may no longer agree — and a bilateral seal needs them to.",
1061
+ guidance:
1062
+ // ⚠️ DO NOT REINSTATE "the next send re-establishes ordering". It is false and it is the
1063
+ // opposite of the remedy: an unwitnessed append leaves this tree one leaf ahead of the
1064
+ // relay's, so the next WITNESSED send takes the divergence branch and makes the session
1065
+ // permanently unsealable. The response guidance in session-content-handlers.ts was
1066
+ // corrected for exactly this and calls the old sentence the most damaging one in the unit
1067
+ // that added it; this copy was missed and kept telling the operator to keep talking.
1068
+ "Delivered and in your transcript, but the relay did not witness it — neither side has " +
1069
+ "independent proof you sent it. Do NOT resend, and do not keep sending: your record is " +
1070
+ "now one leaf ahead of the relay's, so the next witnessed message reports this session " +
1071
+ "as diverged and a diverged session can never be sealed. If the receipt matters, close now.",
1072
+ });
1073
+ /**
1074
+ * ─── `016-RELAYLOSS`: CARRIED TO THE CALLER, because the log is not a consumer ────────────
1075
+ *
1076
+ * Measured with a real relay black-holed mid-conversation: the send stalls for the submit
1077
+ * timeout, then returns `{ok: true, sequence_number: N, delivered: true}` — BYTE-IDENTICAL to
1078
+ * the witnessed send that preceded it. The error above is correct, complete, and read by
1079
+ * nobody the operator can ask.
1080
+ *
1081
+ * Its two siblings twenty lines below already do this: `diverged` and the held case both
1082
+ * refuse to return an ordinary success, on the stated grounds that doing so "reports a
1083
+ * healthy send on a conversation that has silently lost the one thing the protocol exists to
1084
+ * produce." This branch is where that loss OCCURS — `position_behind_frontier`'s own comment
1085
+ * says the seal "was already lost at the unwitnessed append, not here" — so it is the branch
1086
+ * that most needed to say so, and it was the one that said nothing.
1087
+ */
1088
+ return { placed: true, leafIndex, unwitnessed: true };
1089
+ }
1090
+ if (assignedSeq === nextExpected) {
1091
+ const { leafIndex } = this.#ctx.appendSessionLeaf(agentName, sessionId, kind, contentHashHex, correlationId);
1092
+ return { placed: true, leafIndex };
1093
+ }
1094
+ if (assignedSeq < nextExpected) {
1095
+ // THE TREE AND THE RELAY HAVE ALREADY DIVERGED, and refusing here does not undo that.
1096
+ //
1097
+ // This side is AHEAD of the relay's counter, which happens by design: a message whose relay
1098
+ // submit failed still appends unwitnessed (the documented degradation). From then on every
1099
+ // ack comes back behind our frontier and the two can never agree again — the seal was already
1100
+ // lost at the unwitnessed append, not here.
1101
+ //
1102
+ // So the choice is between a record that is short by every subsequent message and one that is
1103
+ // complete but skewed. Appending at the tail keeps the operator's own words in their own
1104
+ // transcript, which is worth more than a tidiness the roots cannot recover anyway; writing
1105
+ // over the assigned slot is the one thing never done, because that rewrites a leaf a root has
1106
+ // already been computed over. The divergence is reported at ERROR and carried to the caller
1107
+ // rather than dressed up as an ordinary success.
1108
+ this.#ctx.logger.error("session.tree.position_behind_frontier", {
1109
+ agentName, sessionId, assignedSeq, nextExpected, contentHash: contentHashHex, correlationId,
1110
+ impact: "this side's tree is ahead of the relay's counter, so the two can no longer agree on a root — the message is kept in the local record and this session can no longer be sealed bilaterally",
1111
+ });
1112
+ this.#ctx.records.markSessionDiverged(agentName, sessionId);
1113
+ const { leafIndex } = this.#ctx.appendSessionLeaf(agentName, sessionId, kind, contentHashHex, correlationId);
1114
+ return { placed: true, leafIndex, diverged: true };
1115
+ }
1116
+ // Ahead of the tail: hold it, exactly as the receiver holds theirs, and let #releaseHeld put it
1117
+ // in at its own index when the gap fills.
1118
+ const key = this.#ctx.sessionKey(agentName, sessionId);
1119
+ let held = this.#ctx.heldContent.get(key);
1120
+ if (!held) {
1121
+ held = new Map();
1122
+ this.#ctx.heldContent.set(key, held);
1123
+ }
1124
+ held.set(assignedSeq, { content: sentBytes, contentHashHex, correlationId, origin: "sent", kind, ...(authorship ? { authorship } : {}) });
1125
+ this.#ctx.queries.persistHeldContent(agentName, sessionId, assignedSeq, sentBytes, sentBytes, contentHashHex, false, correlationId, "sent", kind);
1126
+ this.#ctx.logger.info("session.content.held", {
1127
+ sessionId, canonicalSeq: assignedSeq, nextExpected, gap: assignedSeq - nextExpected,
1128
+ origin: "sent", correlationId,
1129
+ });
1130
+ return { placed: false, heldAt: assignedSeq };
1131
+ }
1132
+ /**
1133
+ * Arm awaiting-ACK tracking for a just-sent content frame (AC-001/AC-003). Records
1134
+ * the content + a TTF timer keyed by content hash; a `persisted` ACK on the inbound
1135
+ * content stream resolves it, TTF expiry hands it to the park backstop. The timer is
1136
+ * `unref`'d so an in-flight wait never keeps the daemon process (or a test runner)
1137
+ * alive on its own.
1138
+ */
1139
+ /**
1140
+ * `contentHashAlg` is `string | undefined`, NOT optional — B2b-1 pass-2 F1.
1141
+ *
1142
+ * The previous fix applied this shape to `#parkContent` and left its SIBLING on the same code path
1143
+ * optional, so dropping the argument here compiled clean and no test could see it — the TTF park
1144
+ * route reads this map, and a v2 envelope omits the field entirely whenever the value is `sha256`,
1145
+ * which is every value in play today. That re-opened the exact finding the fix closed.
1146
+ */
1147
+ #trackAwaitingAck(agentName, sessionId, content, contentHash, correlationId, structure1Cbor, structure2Cbor, contentHashAlg, structure1Signature, leafKind) {
1148
+ const hashHex = Buffer.from(contentHash).toString("hex");
1149
+ const ackKey = this.#ctx.sessionKey(agentName, sessionId);
1150
+ let bySession = this.#ctx.awaitingAck.get(ackKey);
1151
+ if (!bySession) {
1152
+ bySession = new Map();
1153
+ this.#ctx.awaitingAck.set(ackKey, bySession);
1154
+ }
1155
+ // Replace any prior timer for the same (session, hash) so we never leak a timer.
1156
+ const prior = bySession.get(hashHex);
1157
+ if (prior)
1158
+ clearTimeout(prior.timer);
1159
+ const timer = setTimeout(() => {
1160
+ this.#ctx.handleTtfExpiry(agentName, sessionId, hashHex);
1161
+ }, this.#ctx.contentTtfMs);
1162
+ if (typeof timer.unref === "function")
1163
+ timer.unref();
1164
+ // DOD-MSG-4 (2b, review #1): retain the relay's ordering record so a TTF-triggered park carries
1165
+ // it too (not only the direct-dial-fail park) — so a TTF-parked entry is self-ordering on recover.
1166
+ // B2b-1 review F2: the algorithm rides WITH the entry. The TTF-expiry park route reads this map
1167
+ // minutes later, in-process, and without it that copy names nothing (= sha256) while the direct
1168
+ // frame named something else — the same message, two claims about what it is, no restart needed.
1169
+ bySession.set(hashHex, { timer, content, correlationId, structure1Cbor, structure2Cbor, contentHashAlg, structure1Signature, leafKind });
1170
+ }
1171
+ /**
1172
+ * Resolve an awaiting-ACK entry on a `persisted` delivery ACK (AC-001/AC-002): cancel
1173
+ * the TTF timer, emit content.delivery.acked, and clear the durable backstop entry.
1174
+ * A `received`-level ACK is NOT handled here — the protocol acts on `persisted` only,
1175
+ * so a received ACK leaves the timer armed.
1176
+ */
1177
+ resolveAwaitingAck(agentName, sessionId, contentHash) {
1178
+ const hashHex = Buffer.from(contentHash).toString("hex");
1179
+ const ackKey = this.#ctx.sessionKey(agentName, sessionId);
1180
+ const bySession = this.#ctx.awaitingAck.get(ackKey);
1181
+ const entry = bySession?.get(hashHex);
1182
+ if (!entry || !bySession)
1183
+ return; // unknown / already resolved — idempotent
1184
+ clearTimeout(entry.timer);
1185
+ bySession.delete(hashHex);
1186
+ if (bySession.size === 0)
1187
+ this.#ctx.awaitingAck.delete(ackKey);
1188
+ this.#ctx.logger.info("content.delivery.acked", {
1189
+ sessionId,
1190
+ contentHash: hashHex,
1191
+ level: "persisted",
1192
+ correlationId: entry.correlationId,
1193
+ });
1194
+ // Clear the durable crash-backstop entry so the startup flush does not re-park
1195
+ // already-delivered content.
1196
+ try {
1197
+ this.#ctx.onAwaitingPersisted?.(agentName, sessionId, hashHex);
1198
+ }
1199
+ catch (err) {
1200
+ this.#ctx.logger.error("content.delivery.ack.backstop.failed", {
1201
+ sessionId, contentHash: hashHex, error: extractErrorMessage(err),
1202
+ });
1203
+ }
1204
+ }
1205
+ /** Cancel and drop a single awaiting-ACK entry (e.g. the send failed after arming). */
1206
+ #untrackAwaitingAck(agentName, sessionId, contentHash) {
1207
+ const hashHex = Buffer.from(contentHash).toString("hex");
1208
+ const ackKey = this.#ctx.sessionKey(agentName, sessionId);
1209
+ const bySession = this.#ctx.awaitingAck.get(ackKey);
1210
+ const entry = bySession?.get(hashHex);
1211
+ if (!entry || !bySession)
1212
+ return;
1213
+ clearTimeout(entry.timer);
1214
+ bySession.delete(hashHex);
1215
+ if (bySession.size === 0)
1216
+ this.#ctx.awaitingAck.delete(ackKey);
1217
+ }
1218
+ /** Cancel and drop all awaiting-ACK timers for a session (teardown). */
1219
+ clearAwaitingForSession(agentName, sessionId) {
1220
+ const ackKey = this.#ctx.sessionKey(agentName, sessionId);
1221
+ const bySession = this.#ctx.awaitingAck.get(ackKey);
1222
+ if (!bySession)
1223
+ return;
1224
+ for (const entry of bySession.values())
1225
+ clearTimeout(entry.timer);
1226
+ this.#ctx.awaitingAck.delete(ackKey);
1227
+ }
1228
+ /**
1229
+ * `DOD-M15-AUTHORSHIP-ABSENT-1` — SIGN OUR OWN CLAIM, with no relay involved.
1230
+ *
1231
+ * The relay submit has always built these bytes and signed them (`session-relay-client.ts`); this
1232
+ * is the same construction, for the path where no submit happens. It is not a fallback in the
1233
+ * silent sense — it produces exactly the artifact the witnessed path produces, minus the relay's
1234
+ * countersigned position, which was never part of the authorship claim.
1235
+ *
1236
+ * ⚠️ THROWS when this agent has no identity key, and the throw is the correct outcome. It lands in
1237
+ * the direct-send catch, which parks the message to the relay mailbox exactly as a failed dial
1238
+ * does — so the message is not lost, and the operator hears about a local fault instead of a
1239
+ * counterparty who mysteriously stopped receiving. Shipping the frame unsigned would guarantee a
1240
+ * refusal at the far end and blame the wrong machine for it.
1241
+ */
1242
+ async #signOwnContentClaim(agentName, sessionId, entry, contentHash) {
1243
+ const signer = this.#ctx.keyProvider(agentName);
1244
+ if (!signer) {
1245
+ throw new Error("content_not_signable: this machine has no identity key for this agent, so it cannot sign " +
1246
+ "the message it is about to send and the counterparty would refuse it as unattributable");
1247
+ }
1248
+ // The 16-byte relay session id when this session has one, so a frame built here is
1249
+ // byte-comparable with one built by the submit. Falling back to the local id is not a
1250
+ // second meaning: for every session created without an assignment the two are the same value
1251
+ // (`relaySessionIdBytes` is set from `sessionId` on exactly those paths).
1252
+ const sessionIdBytes = entry.relaySessionIdBytes ?? Uint8Array.from(Buffer.from(sessionId, "hex"));
1253
+ /**
1254
+ * ⚠️ **THIS COMMENT USED TO READ "v1 DELIBERATELY" AND IT WAS RIGHT UNTIL NOW — 033-ACKEMIT.**
1255
+ *
1256
+ * It said `last_seen_hash` was `WITHHOLD-SEAL-1`'s emitter and "not owed here", and that a v1
1257
+ * claim makes no content acknowledgement at all, "which is honest, where an invented one would
1258
+ * not be." Accurate for `020-ACKHASH`, which shipped the reader only. It is rewritten rather
1259
+ * than deleted because it is the sentence that would otherwise explain away the LAST production
1260
+ * path still emitting v1 — and this unit's own Definition of Done is that a grep finds none.
1261
+ *
1262
+ * The reasoning it rested on has been answered: nothing is invented here. The acknowledgement
1263
+ * is read from the same `#lastSeen` entry the submit reads, so a frame built on this path and
1264
+ * one built by a submit make the same claim about the same message.
1265
+ */
1266
+ const sessionIdHexForAck = Buffer.from(sessionIdBytes).toString("hex");
1267
+ /**
1268
+ * The pair, from ONE accessor. Falling back to the session's genesis when there is no relay
1269
+ * client at all is not an invention either: nothing has been witnessed on this session, so the
1270
+ * honest acknowledgement is position 0 and the agreed starting point of the chain.
1271
+ */
1272
+ const ack = entry.relayClient?.lastSeenAck(sessionIdHexForAck)
1273
+ ?? this.#ctx.lastAck.get(this.#ctx.sessionKey(agentName, sessionId))
1274
+ ?? (() => { const g = this.#ctx.leafRecords.sessionGenesisPrevRoot(agentName, sessionId); return g ? { seq: 0, hash: g } : undefined; })();
1275
+ if (!ack) {
1276
+ /**
1277
+ * ⚠️ NO STARTING POINT MEANS NO SEND — `DOD-M15-SELFCHAIN-1`.
1278
+ *
1279
+ * This used to emit a shorter claim: `last_seen_seq: 0` with no hashes, on the argument that
1280
+ * "I have seen nothing of yours" is honest and asserts nothing false. It IS honest, and it is
1281
+ * no longer a shape this protocol has. Both chain links are required, and a session with no
1282
+ * recorded starting point has nothing for them to anchor to.
1283
+ *
1284
+ * Refusing costs a message on a session that was brokered without an assignment. Sending one
1285
+ * costs the ability to prove the order of the whole conversation later, and the cost is
1286
+ * invisible until someone disputes it — which is exactly the failure this unit exists to end.
1287
+ * Refuse loudly, and say what to do about it.
1288
+ */
1289
+ throw new Error("session_unchainable: this session has no recorded starting point on this machine, so a " +
1290
+ "message sent on it could not link to anything and its place in the conversation could " +
1291
+ "never be proven. Restart the session so it is registered with its genesis.");
1292
+ }
1293
+ const structure1 = encodeStructure1({
1294
+ contentHash,
1295
+ senderPubkey: await signer.getPublicKey(),
1296
+ sessionId: sessionIdBytes,
1297
+ // The highest counterparty position this session has seen, from the same source the submit
1298
+ // reads — and now the content hash at it, taken from the same entry so the two cannot
1299
+ // describe different messages.
1300
+ lastSeenSeq: ack.seq,
1301
+ timestamp: Date.now(),
1302
+ lastSeenHash: ack.hash,
1303
+ /**
1304
+ * ─── THE SELF LINK ON THE UNWITNESSED PATH — `DOD-M15-SELFCHAIN-1` ────────────────────────
1305
+ *
1306
+ * This is the path that matters most for it. A conversation that ran while the relay was down
1307
+ * is precisely the one whose order gets disputed later, so the chain cannot depend on a relay
1308
+ * having been there to witness it.
1309
+ *
1310
+ * ⚠️ ONE CHAIN, READ THROUGH THE RELAY CLIENT FIRST. This used to read only the durable
1311
+ * store, while the witnessed path reads an in-memory map first — so a session that mixed the
1312
+ * two, which is every session where the relay comes and goes, walked two different chains and
1313
+ * the lagging one produced a link the counterparty refuses.
1314
+ *
1315
+ * ⚠️ AND THE LAST FALLBACK IS THE SESSION GENESIS, NOT `ack.hash`. `ack.hash` is what the
1316
+ * COUNTERPARTY last said; it is only the genesis until they have said anything. Falling back
1317
+ * to it meant this agent's own first message linked to the other party's message — refused by
1318
+ * every checker, and reported as tampering against a party that had done nothing.
1319
+ */
1320
+ prevOwnHash: this.#ctx.ownChainOf(agentName, sessionId, entry, await signer.getPublicKey())
1321
+ ?? this.#ctx.leafRecords.sessionGenesisPrevRoot(agentName, sessionId)
1322
+ ?? ack.hash,
1323
+ });
1324
+ /**
1325
+ * ⚠️ THE CHAIN IS NOT ADVANCED HERE, AND IT USED TO BE — review F7.
1326
+ *
1327
+ * `SessionOwnChainStore.record` says in capitals that it is called AFTER the send succeeds. This
1328
+ * ran at signing time, before a stream was even opened. Any failure between here and delivery
1329
+ * left the chain pointing at a message the counterparty never saw, and every later message was
1330
+ * then refused by them for a reason that names tampering. A retransmission must re-use the same
1331
+ * predecessor, because a retransmission is the same message.
1332
+ *
1333
+ * `#advanceOwnChain` is called by the send path at each point where the message has actually
1334
+ * gone — the direct send, and the relay park that is the fallback for it.
1335
+ */
1336
+ return { structure1, signature: await signer.sign(structure1) };
1337
+ }
1338
+ /**
1339
+ * Record what this agent just sent — called ONLY once the message has actually gone.
1340
+ *
1341
+ * Writes through the relay client when there is one, so the in-memory chain the witnessed path
1342
+ * reads and the durable row stay one chain rather than two. Falls back to the store directly for
1343
+ * a session with no relay client at all, which is exactly the unwitnessed case this unit exists
1344
+ * to cover.
1345
+ */
1346
+ #advanceOwnChain(agentName, sessionId, entry, contentHash) {
1347
+ const relayHex = entry?.relaySessionIdBytes
1348
+ ? Buffer.from(entry.relaySessionIdBytes).toString("hex")
1349
+ : sessionId;
1350
+ if (entry?.relayClient) {
1351
+ entry.relayClient.noteOwnLeaf(relayHex, contentHash);
1352
+ return;
1353
+ }
1354
+ const ownPubkeyHex = this.#ctx.queries.ownPubkeyHex(agentName);
1355
+ if (!ownPubkeyHex)
1356
+ return;
1357
+ this.#ctx.ownChainStore?.record(ownPubkeyHex, sessionId, contentHash, Date.now());
1358
+ }
1359
+ }
1360
+ //# sourceMappingURL=session-content-send.js.map