@cello-protocol/daemon 0.0.225 → 0.0.227

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 (891) hide show
  1. package/package.json +5 -5
  2. package/dist/agent-admin-handlers.d.ts +0 -56
  3. package/dist/agent-admin-handlers.d.ts.map +0 -1
  4. package/dist/agent-admin-handlers.js +0 -73
  5. package/dist/agent-admin-handlers.js.map +0 -1
  6. package/dist/agent-handlers.d.ts +0 -72
  7. package/dist/agent-handlers.d.ts.map +0 -1
  8. package/dist/agent-handlers.js +0 -604
  9. package/dist/agent-handlers.js.map +0 -1
  10. package/dist/agent-id-migration.d.ts +0 -70
  11. package/dist/agent-id-migration.d.ts.map +0 -1
  12. package/dist/agent-id-migration.js +0 -468
  13. package/dist/agent-id-migration.js.map +0 -1
  14. package/dist/agent-loader.d.ts +0 -37
  15. package/dist/agent-loader.d.ts.map +0 -1
  16. package/dist/agent-loader.js +0 -37
  17. package/dist/agent-loader.js.map +0 -1
  18. package/dist/agent-selection-root.d.ts +0 -29
  19. package/dist/agent-selection-root.d.ts.map +0 -1
  20. package/dist/agent-selection-root.js +0 -126
  21. package/dist/agent-selection-root.js.map +0 -1
  22. package/dist/agent-selection.d.ts +0 -73
  23. package/dist/agent-selection.d.ts.map +0 -1
  24. package/dist/agent-selection.js +0 -80
  25. package/dist/agent-selection.js.map +0 -1
  26. package/dist/agent-settings-keys.d.ts +0 -52
  27. package/dist/agent-settings-keys.d.ts.map +0 -1
  28. package/dist/agent-settings-keys.js +0 -114
  29. package/dist/agent-settings-keys.js.map +0 -1
  30. package/dist/agent-state.d.ts +0 -67
  31. package/dist/agent-state.d.ts.map +0 -1
  32. package/dist/agent-state.js +0 -66
  33. package/dist/agent-state.js.map +0 -1
  34. package/dist/assignment-verify.d.ts +0 -90
  35. package/dist/assignment-verify.d.ts.map +0 -1
  36. package/dist/assignment-verify.js +0 -301
  37. package/dist/assignment-verify.js.map +0 -1
  38. package/dist/attendance-wiring.d.ts +0 -23
  39. package/dist/attendance-wiring.d.ts.map +0 -1
  40. package/dist/attendance-wiring.js +0 -248
  41. package/dist/attendance-wiring.js.map +0 -1
  42. package/dist/attestation-body.d.ts +0 -47
  43. package/dist/attestation-body.d.ts.map +0 -1
  44. package/dist/attestation-body.js +0 -71
  45. package/dist/attestation-body.js.map +0 -1
  46. package/dist/authorship-verification.d.ts +0 -91
  47. package/dist/authorship-verification.d.ts.map +0 -1
  48. package/dist/authorship-verification.js +0 -532
  49. package/dist/authorship-verification.js.map +0 -1
  50. package/dist/away-detection.d.ts +0 -139
  51. package/dist/away-detection.d.ts.map +0 -1
  52. package/dist/away-detection.js +0 -186
  53. package/dist/away-detection.js.map +0 -1
  54. package/dist/away-inbox-oneshot.d.ts +0 -40
  55. package/dist/away-inbox-oneshot.d.ts.map +0 -1
  56. package/dist/away-inbox-oneshot.js +0 -311
  57. package/dist/away-inbox-oneshot.js.map +0 -1
  58. package/dist/backup-restore-handlers.d.ts +0 -10
  59. package/dist/backup-restore-handlers.d.ts.map +0 -1
  60. package/dist/backup-restore-handlers.js +0 -82
  61. package/dist/backup-restore-handlers.js.map +0 -1
  62. package/dist/backup-restore.d.ts +0 -102
  63. package/dist/backup-restore.d.ts.map +0 -1
  64. package/dist/backup-restore.js +0 -341
  65. package/dist/backup-restore.js.map +0 -1
  66. package/dist/bin/cello-daemon.d.ts +0 -13
  67. package/dist/bin/cello-daemon.d.ts.map +0 -1
  68. package/dist/bin/cello-daemon.js.map +0 -1
  69. package/dist/boot-agents.d.ts +0 -64
  70. package/dist/boot-agents.d.ts.map +0 -1
  71. package/dist/boot-agents.js +0 -241
  72. package/dist/boot-agents.js.map +0 -1
  73. package/dist/boot-connection-state.d.ts +0 -38
  74. package/dist/boot-connection-state.d.ts.map +0 -1
  75. package/dist/boot-connection-state.js +0 -95
  76. package/dist/boot-connection-state.js.map +0 -1
  77. package/dist/boot-core.d.ts +0 -36
  78. package/dist/boot-core.d.ts.map +0 -1
  79. package/dist/boot-core.js +0 -293
  80. package/dist/boot-core.js.map +0 -1
  81. package/dist/boot-parked-content.d.ts +0 -34
  82. package/dist/boot-parked-content.d.ts.map +0 -1
  83. package/dist/boot-parked-content.js +0 -528
  84. package/dist/boot-parked-content.js.map +0 -1
  85. package/dist/boot-sweeps.d.ts +0 -22
  86. package/dist/boot-sweeps.d.ts.map +0 -1
  87. package/dist/boot-sweeps.js +0 -51
  88. package/dist/boot-sweeps.js.map +0 -1
  89. package/dist/bundled-consortium-manifest.d.ts +0 -47
  90. package/dist/bundled-consortium-manifest.d.ts.map +0 -1
  91. package/dist/bundled-consortium-manifest.js +0 -116
  92. package/dist/bundled-consortium-manifest.js.map +0 -1
  93. package/dist/cbor-blob-migration.d.ts +0 -8
  94. package/dist/cbor-blob-migration.d.ts.map +0 -1
  95. package/dist/cbor-blob-migration.js +0 -116
  96. package/dist/cbor-blob-migration.js.map +0 -1
  97. package/dist/challenge-verifier.d.ts +0 -12
  98. package/dist/challenge-verifier.d.ts.map +0 -1
  99. package/dist/challenge-verifier.js +0 -11
  100. package/dist/challenge-verifier.js.map +0 -1
  101. package/dist/close-commitment.d.ts +0 -67
  102. package/dist/close-commitment.d.ts.map +0 -1
  103. package/dist/close-commitment.js +0 -78
  104. package/dist/close-commitment.js.map +0 -1
  105. package/dist/close-session-handler.d.ts +0 -94
  106. package/dist/close-session-handler.d.ts.map +0 -1
  107. package/dist/close-session-handler.js +0 -1418
  108. package/dist/close-session-handler.js.map +0 -1
  109. package/dist/co-attendance.d.ts +0 -29
  110. package/dist/co-attendance.d.ts.map +0 -1
  111. package/dist/co-attendance.js +0 -32
  112. package/dist/co-attendance.js.map +0 -1
  113. package/dist/column-birth.d.ts +0 -40
  114. package/dist/column-birth.d.ts.map +0 -1
  115. package/dist/column-birth.js +0 -61
  116. package/dist/column-birth.js.map +0 -1
  117. package/dist/connect-or-start.d.ts +0 -25
  118. package/dist/connect-or-start.d.ts.map +0 -1
  119. package/dist/connect-or-start.js +0 -175
  120. package/dist/connect-or-start.js.map +0 -1
  121. package/dist/connection-agents.d.ts +0 -16
  122. package/dist/connection-agents.d.ts.map +0 -1
  123. package/dist/connection-agents.js +0 -68
  124. package/dist/connection-agents.js.map +0 -1
  125. package/dist/consent-migration.d.ts +0 -49
  126. package/dist/consent-migration.d.ts.map +0 -1
  127. package/dist/consent-migration.js +0 -128
  128. package/dist/consent-migration.js.map +0 -1
  129. package/dist/consortium-bootstrap.d.ts +0 -138
  130. package/dist/consortium-bootstrap.d.ts.map +0 -1
  131. package/dist/consortium-bootstrap.js +0 -339
  132. package/dist/consortium-bootstrap.js.map +0 -1
  133. package/dist/consortium-fingerprint.d.ts +0 -115
  134. package/dist/consortium-fingerprint.d.ts.map +0 -1
  135. package/dist/consortium-fingerprint.js +0 -175
  136. package/dist/consortium-fingerprint.js.map +0 -1
  137. package/dist/contact-handlers.d.ts +0 -59
  138. package/dist/contact-handlers.d.ts.map +0 -1
  139. package/dist/contact-handlers.js +0 -343
  140. package/dist/contact-handlers.js.map +0 -1
  141. package/dist/contact-pubkey-case.d.ts +0 -65
  142. package/dist/contact-pubkey-case.d.ts.map +0 -1
  143. package/dist/contact-pubkey-case.js +0 -136
  144. package/dist/contact-pubkey-case.js.map +0 -1
  145. package/dist/contacts-tier-migration.d.ts +0 -90
  146. package/dist/contacts-tier-migration.d.ts.map +0 -1
  147. package/dist/contacts-tier-migration.js +0 -150
  148. package/dist/contacts-tier-migration.js.map +0 -1
  149. package/dist/content-encryption-status.d.ts +0 -111
  150. package/dist/content-encryption-status.d.ts.map +0 -1
  151. package/dist/content-encryption-status.js +0 -158
  152. package/dist/content-encryption-status.js.map +0 -1
  153. package/dist/content-park-client.d.ts +0 -90
  154. package/dist/content-park-client.d.ts.map +0 -1
  155. package/dist/content-park-client.js +0 -362
  156. package/dist/content-park-client.js.map +0 -1
  157. package/dist/content-park.d.ts +0 -52
  158. package/dist/content-park.d.ts.map +0 -1
  159. package/dist/content-park.js +0 -1309
  160. package/dist/content-park.js.map +0 -1
  161. package/dist/cross-node-negotiation.d.ts +0 -44
  162. package/dist/cross-node-negotiation.d.ts.map +0 -1
  163. package/dist/cross-node-negotiation.js +0 -34
  164. package/dist/cross-node-negotiation.js.map +0 -1
  165. package/dist/daemon-handle.d.ts +0 -57
  166. package/dist/daemon-handle.d.ts.map +0 -1
  167. package/dist/daemon-handle.js +0 -2
  168. package/dist/daemon-handle.js.map +0 -1
  169. package/dist/daemon-status-report.d.ts +0 -50
  170. package/dist/daemon-status-report.d.ts.map +0 -1
  171. package/dist/daemon-status-report.js +0 -81
  172. package/dist/daemon-status-report.js.map +0 -1
  173. package/dist/daemon.d.ts +0 -45
  174. package/dist/daemon.d.ts.map +0 -1
  175. package/dist/daemon.js +0 -1177
  176. package/dist/daemon.js.map +0 -1
  177. package/dist/db-identity-store.d.ts +0 -159
  178. package/dist/db-identity-store.d.ts.map +0 -1
  179. package/dist/db-identity-store.js +0 -502
  180. package/dist/db-identity-store.js.map +0 -1
  181. package/dist/delivery-open-registry.d.ts +0 -92
  182. package/dist/delivery-open-registry.d.ts.map +0 -1
  183. package/dist/delivery-open-registry.js +0 -121
  184. package/dist/delivery-open-registry.js.map +0 -1
  185. package/dist/delivery-session-suspects.d.ts +0 -56
  186. package/dist/delivery-session-suspects.d.ts.map +0 -1
  187. package/dist/delivery-session-suspects.js +0 -94
  188. package/dist/delivery-session-suspects.js.map +0 -1
  189. package/dist/directory-auth-posture.d.ts +0 -87
  190. package/dist/directory-auth-posture.d.ts.map +0 -1
  191. package/dist/directory-auth-posture.js +0 -134
  192. package/dist/directory-auth-posture.js.map +0 -1
  193. package/dist/directory-bootstrap.d.ts +0 -310
  194. package/dist/directory-bootstrap.d.ts.map +0 -1
  195. package/dist/directory-bootstrap.js +0 -557
  196. package/dist/directory-bootstrap.js.map +0 -1
  197. package/dist/directory-connect.d.ts +0 -27
  198. package/dist/directory-connect.d.ts.map +0 -1
  199. package/dist/directory-connect.js +0 -106
  200. package/dist/directory-connect.js.map +0 -1
  201. package/dist/disconnect-cleanup.d.ts +0 -51
  202. package/dist/disconnect-cleanup.d.ts.map +0 -1
  203. package/dist/disconnect-cleanup.js +0 -72
  204. package/dist/disconnect-cleanup.js.map +0 -1
  205. package/dist/document-amendment-store.d.ts +0 -120
  206. package/dist/document-amendment-store.d.ts.map +0 -1
  207. package/dist/document-amendment-store.js +0 -266
  208. package/dist/document-amendment-store.js.map +0 -1
  209. package/dist/document-delivery-transport.d.ts +0 -168
  210. package/dist/document-delivery-transport.d.ts.map +0 -1
  211. package/dist/document-delivery-transport.js +0 -206
  212. package/dist/document-delivery-transport.js.map +0 -1
  213. package/dist/document-engine.d.ts +0 -134
  214. package/dist/document-engine.d.ts.map +0 -1
  215. package/dist/document-engine.js +0 -282
  216. package/dist/document-engine.js.map +0 -1
  217. package/dist/document-flag.d.ts +0 -58
  218. package/dist/document-flag.d.ts.map +0 -1
  219. package/dist/document-flag.js +0 -70
  220. package/dist/document-flag.js.map +0 -1
  221. package/dist/document-frame-router.d.ts +0 -245
  222. package/dist/document-frame-router.d.ts.map +0 -1
  223. package/dist/document-frame-router.js +0 -397
  224. package/dist/document-frame-router.js.map +0 -1
  225. package/dist/document-gate-wiring.d.ts +0 -61
  226. package/dist/document-gate-wiring.d.ts.map +0 -1
  227. package/dist/document-gate-wiring.js +0 -125
  228. package/dist/document-gate-wiring.js.map +0 -1
  229. package/dist/document-gate.d.ts +0 -149
  230. package/dist/document-gate.d.ts.map +0 -1
  231. package/dist/document-gate.js +0 -509
  232. package/dist/document-gate.js.map +0 -1
  233. package/dist/document-handlers.d.ts +0 -47
  234. package/dist/document-handlers.d.ts.map +0 -1
  235. package/dist/document-handlers.js +0 -2203
  236. package/dist/document-handlers.js.map +0 -1
  237. package/dist/document-handshake.d.ts +0 -176
  238. package/dist/document-handshake.d.ts.map +0 -1
  239. package/dist/document-handshake.js +0 -452
  240. package/dist/document-handshake.js.map +0 -1
  241. package/dist/document-inbound.d.ts +0 -162
  242. package/dist/document-inbound.d.ts.map +0 -1
  243. package/dist/document-inbound.js +0 -530
  244. package/dist/document-inbound.js.map +0 -1
  245. package/dist/document-json.d.ts +0 -120
  246. package/dist/document-json.d.ts.map +0 -1
  247. package/dist/document-json.js +0 -191
  248. package/dist/document-json.js.map +0 -1
  249. package/dist/document-layer.d.ts +0 -215
  250. package/dist/document-layer.d.ts.map +0 -1
  251. package/dist/document-layer.js +0 -1025
  252. package/dist/document-layer.js.map +0 -1
  253. package/dist/document-lifecycle.d.ts +0 -52
  254. package/dist/document-lifecycle.d.ts.map +0 -1
  255. package/dist/document-lifecycle.js +0 -134
  256. package/dist/document-lifecycle.js.map +0 -1
  257. package/dist/document-live-docs.d.ts +0 -58
  258. package/dist/document-live-docs.d.ts.map +0 -1
  259. package/dist/document-live-docs.js +0 -126
  260. package/dist/document-live-docs.js.map +0 -1
  261. package/dist/document-notify.d.ts +0 -228
  262. package/dist/document-notify.d.ts.map +0 -1
  263. package/dist/document-notify.js +0 -580
  264. package/dist/document-notify.js.map +0 -1
  265. package/dist/document-profile.d.ts +0 -61
  266. package/dist/document-profile.d.ts.map +0 -1
  267. package/dist/document-profile.js +0 -112
  268. package/dist/document-profile.js.map +0 -1
  269. package/dist/document-publish.d.ts +0 -87
  270. package/dist/document-publish.d.ts.map +0 -1
  271. package/dist/document-publish.js +0 -173
  272. package/dist/document-publish.js.map +0 -1
  273. package/dist/document-reachability.d.ts +0 -42
  274. package/dist/document-reachability.d.ts.map +0 -1
  275. package/dist/document-reachability.js +0 -80
  276. package/dist/document-reachability.js.map +0 -1
  277. package/dist/document-reconcile-engine.d.ts +0 -66
  278. package/dist/document-reconcile-engine.d.ts.map +0 -1
  279. package/dist/document-reconcile-engine.js +0 -225
  280. package/dist/document-reconcile-engine.js.map +0 -1
  281. package/dist/document-reconcile-scheduler.d.ts +0 -163
  282. package/dist/document-reconcile-scheduler.d.ts.map +0 -1
  283. package/dist/document-reconcile-scheduler.js +0 -303
  284. package/dist/document-reconcile-scheduler.js.map +0 -1
  285. package/dist/document-rejection.d.ts +0 -251
  286. package/dist/document-rejection.d.ts.map +0 -1
  287. package/dist/document-rejection.js +0 -435
  288. package/dist/document-rejection.js.map +0 -1
  289. package/dist/document-screen.d.ts +0 -114
  290. package/dist/document-screen.d.ts.map +0 -1
  291. package/dist/document-screen.js +0 -223
  292. package/dist/document-screen.js.map +0 -1
  293. package/dist/document-store.d.ts +0 -372
  294. package/dist/document-store.d.ts.map +0 -1
  295. package/dist/document-store.js +0 -931
  296. package/dist/document-store.js.map +0 -1
  297. package/dist/document-surface.d.ts +0 -32
  298. package/dist/document-surface.d.ts.map +0 -1
  299. package/dist/document-surface.js +0 -151
  300. package/dist/document-surface.js.map +0 -1
  301. package/dist/document-types.d.ts +0 -94
  302. package/dist/document-types.d.ts.map +0 -1
  303. package/dist/document-types.js +0 -90
  304. package/dist/document-types.js.map +0 -1
  305. package/dist/document-watch.d.ts +0 -69
  306. package/dist/document-watch.d.ts.map +0 -1
  307. package/dist/document-watch.js +0 -108
  308. package/dist/document-watch.js.map +0 -1
  309. package/dist/document-wiring.d.ts +0 -53
  310. package/dist/document-wiring.d.ts.map +0 -1
  311. package/dist/document-wiring.js +0 -323
  312. package/dist/document-wiring.js.map +0 -1
  313. package/dist/document-write-guard.d.ts +0 -66
  314. package/dist/document-write-guard.d.ts.map +0 -1
  315. package/dist/document-write-guard.js +0 -98
  316. package/dist/document-write-guard.js.map +0 -1
  317. package/dist/document-write-path.d.ts +0 -94
  318. package/dist/document-write-path.d.ts.map +0 -1
  319. package/dist/document-write-path.js +0 -530
  320. package/dist/document-write-path.js.map +0 -1
  321. package/dist/error-message.d.ts +0 -7
  322. package/dist/error-message.d.ts.map +0 -1
  323. package/dist/error-message.js +0 -19
  324. package/dist/error-message.js.map +0 -1
  325. package/dist/file-manifest-provider.d.ts +0 -37
  326. package/dist/file-manifest-provider.d.ts.map +0 -1
  327. package/dist/file-manifest-provider.js +0 -105
  328. package/dist/file-manifest-provider.js.map +0 -1
  329. package/dist/frame-values.d.ts +0 -3
  330. package/dist/frame-values.d.ts.map +0 -1
  331. package/dist/frame-values.js +0 -84
  332. package/dist/frame-values.js.map +0 -1
  333. package/dist/frontier-mismatch.d.ts +0 -73
  334. package/dist/frontier-mismatch.d.ts.map +0 -1
  335. package/dist/frontier-mismatch.js +0 -89
  336. package/dist/frontier-mismatch.js.map +0 -1
  337. package/dist/gateway-config-handlers.d.ts +0 -26
  338. package/dist/gateway-config-handlers.d.ts.map +0 -1
  339. package/dist/gateway-config-handlers.js +0 -429
  340. package/dist/gateway-config-handlers.js.map +0 -1
  341. package/dist/held-content.d.ts +0 -145
  342. package/dist/held-content.d.ts.map +0 -1
  343. package/dist/held-content.js +0 -389
  344. package/dist/held-content.js.map +0 -1
  345. package/dist/http-manifest-poll.d.ts +0 -67
  346. package/dist/http-manifest-poll.d.ts.map +0 -1
  347. package/dist/http-manifest-poll.js +0 -150
  348. package/dist/http-manifest-poll.js.map +0 -1
  349. package/dist/identity-migration.d.ts +0 -40
  350. package/dist/identity-migration.d.ts.map +0 -1
  351. package/dist/identity-migration.js +0 -461
  352. package/dist/identity-migration.js.map +0 -1
  353. package/dist/inbound-refusals.d.ts +0 -283
  354. package/dist/inbound-refusals.d.ts.map +0 -1
  355. package/dist/inbound-refusals.js +0 -919
  356. package/dist/inbound-refusals.js.map +0 -1
  357. package/dist/inbound-seal-request.d.ts +0 -32
  358. package/dist/inbound-seal-request.d.ts.map +0 -1
  359. package/dist/inbound-seal-request.js +0 -228
  360. package/dist/inbound-seal-request.js.map +0 -1
  361. package/dist/inbound-sessions.d.ts +0 -291
  362. package/dist/inbound-sessions.d.ts.map +0 -1
  363. package/dist/inbound-sessions.js +0 -1609
  364. package/dist/inbound-sessions.js.map +0 -1
  365. package/dist/inclusion-proof-handlers.d.ts +0 -43
  366. package/dist/inclusion-proof-handlers.d.ts.map +0 -1
  367. package/dist/inclusion-proof-handlers.js +0 -565
  368. package/dist/inclusion-proof-handlers.js.map +0 -1
  369. package/dist/inclusion-proof.d.ts +0 -151
  370. package/dist/inclusion-proof.d.ts.map +0 -1
  371. package/dist/inclusion-proof.js +0 -228
  372. package/dist/inclusion-proof.js.map +0 -1
  373. package/dist/index.d.ts +0 -32
  374. package/dist/index.d.ts.map +0 -1
  375. package/dist/index.js.map +0 -1
  376. package/dist/initiate-session-handler.d.ts +0 -55
  377. package/dist/initiate-session-handler.d.ts.map +0 -1
  378. package/dist/initiate-session-handler.js +0 -310
  379. package/dist/initiate-session-handler.js.map +0 -1
  380. package/dist/ipc-client.d.ts +0 -31
  381. package/dist/ipc-client.d.ts.map +0 -1
  382. package/dist/ipc-client.js +0 -113
  383. package/dist/ipc-client.js.map +0 -1
  384. package/dist/ipc-server.d.ts +0 -63
  385. package/dist/ipc-server.d.ts.map +0 -1
  386. package/dist/ipc-server.js +0 -429
  387. package/dist/ipc-server.js.map +0 -1
  388. package/dist/ipc-surface.d.ts +0 -44
  389. package/dist/ipc-surface.d.ts.map +0 -1
  390. package/dist/ipc-surface.js +0 -107
  391. package/dist/ipc-surface.js.map +0 -1
  392. package/dist/line-lcs.d.ts +0 -51
  393. package/dist/line-lcs.d.ts.map +0 -1
  394. package/dist/line-lcs.js +0 -71
  395. package/dist/line-lcs.js.map +0 -1
  396. package/dist/lock-file.d.ts +0 -39
  397. package/dist/lock-file.d.ts.map +0 -1
  398. package/dist/lock-file.js +0 -120
  399. package/dist/lock-file.js.map +0 -1
  400. package/dist/log-collapse.d.ts +0 -66
  401. package/dist/log-collapse.d.ts.map +0 -1
  402. package/dist/log-collapse.js +0 -244
  403. package/dist/log-collapse.js.map +0 -1
  404. package/dist/log-rotate.d.ts +0 -67
  405. package/dist/log-rotate.d.ts.map +0 -1
  406. package/dist/log-rotate.js +0 -134
  407. package/dist/log-rotate.js.map +0 -1
  408. package/dist/manifest-deps.d.ts +0 -25
  409. package/dist/manifest-deps.d.ts.map +0 -1
  410. package/dist/manifest-deps.js +0 -151
  411. package/dist/manifest-deps.js.map +0 -1
  412. package/dist/manifest-poll-scheduler.d.ts +0 -31
  413. package/dist/manifest-poll-scheduler.d.ts.map +0 -1
  414. package/dist/manifest-poll-scheduler.js +0 -59
  415. package/dist/manifest-poll-scheduler.js.map +0 -1
  416. package/dist/manifest-validity.d.ts +0 -153
  417. package/dist/manifest-validity.d.ts.map +0 -1
  418. package/dist/manifest-validity.js +0 -268
  419. package/dist/manifest-validity.js.map +0 -1
  420. package/dist/manifest-version-store-db.d.ts +0 -24
  421. package/dist/manifest-version-store-db.d.ts.map +0 -1
  422. package/dist/manifest-version-store-db.js +0 -58
  423. package/dist/manifest-version-store-db.js.map +0 -1
  424. package/dist/manifest-version-store.d.ts +0 -16
  425. package/dist/manifest-version-store.d.ts.map +0 -1
  426. package/dist/manifest-version-store.js +0 -15
  427. package/dist/manifest-version-store.js.map +0 -1
  428. package/dist/network-directory-node.d.ts +0 -136
  429. package/dist/network-directory-node.d.ts.map +0 -1
  430. package/dist/network-directory-node.js +0 -810
  431. package/dist/network-directory-node.js.map +0 -1
  432. package/dist/nonce-dedup.d.ts +0 -68
  433. package/dist/nonce-dedup.d.ts.map +0 -1
  434. package/dist/nonce-dedup.js +0 -205
  435. package/dist/nonce-dedup.js.map +0 -1
  436. package/dist/notification-dispatcher.d.ts +0 -92
  437. package/dist/notification-dispatcher.d.ts.map +0 -1
  438. package/dist/notification-dispatcher.js +0 -210
  439. package/dist/notification-dispatcher.js.map +0 -1
  440. package/dist/notification-handlers.d.ts +0 -53
  441. package/dist/notification-handlers.d.ts.map +0 -1
  442. package/dist/notification-handlers.js +0 -461
  443. package/dist/notification-handlers.js.map +0 -1
  444. package/dist/onboarding-guidance.d.ts +0 -79
  445. package/dist/onboarding-guidance.d.ts.map +0 -1
  446. package/dist/onboarding-guidance.js +0 -95
  447. package/dist/onboarding-guidance.js.map +0 -1
  448. package/dist/operator-guidance.d.ts +0 -25
  449. package/dist/operator-guidance.d.ts.map +0 -1
  450. package/dist/operator-guidance.js +0 -50
  451. package/dist/operator-guidance.js.map +0 -1
  452. package/dist/orphan-triage.d.ts +0 -130
  453. package/dist/orphan-triage.d.ts.map +0 -1
  454. package/dist/orphan-triage.js +0 -207
  455. package/dist/orphan-triage.js.map +0 -1
  456. package/dist/outbound-sessions.d.ts +0 -126
  457. package/dist/outbound-sessions.d.ts.map +0 -1
  458. package/dist/outbound-sessions.js +0 -1052
  459. package/dist/outbound-sessions.js.map +0 -1
  460. package/dist/park-envelope.d.ts +0 -329
  461. package/dist/park-envelope.d.ts.map +0 -1
  462. package/dist/park-envelope.js +0 -509
  463. package/dist/park-envelope.js.map +0 -1
  464. package/dist/park-recovery.d.ts +0 -273
  465. package/dist/park-recovery.d.ts.map +0 -1
  466. package/dist/park-recovery.js +0 -717
  467. package/dist/park-recovery.js.map +0 -1
  468. package/dist/park-refusals.d.ts +0 -147
  469. package/dist/park-refusals.d.ts.map +0 -1
  470. package/dist/park-refusals.js +0 -331
  471. package/dist/park-refusals.js.map +0 -1
  472. package/dist/quarantine-framing.d.ts +0 -92
  473. package/dist/quarantine-framing.d.ts.map +0 -1
  474. package/dist/quarantine-framing.js +0 -111
  475. package/dist/quarantine-framing.js.map +0 -1
  476. package/dist/reconnect-drain.d.ts +0 -22
  477. package/dist/reconnect-drain.d.ts.map +0 -1
  478. package/dist/reconnect-drain.js +0 -65
  479. package/dist/reconnect-drain.js.map +0 -1
  480. package/dist/recovered-position.d.ts +0 -14
  481. package/dist/recovered-position.d.ts.map +0 -1
  482. package/dist/recovered-position.js +0 -59
  483. package/dist/recovered-position.js.map +0 -1
  484. package/dist/refusal-notices.d.ts +0 -196
  485. package/dist/refusal-notices.d.ts.map +0 -1
  486. package/dist/refusal-notices.js +0 -516
  487. package/dist/refusal-notices.js.map +0 -1
  488. package/dist/refusal-reasons.d.ts +0 -245
  489. package/dist/refusal-reasons.d.ts.map +0 -1
  490. package/dist/refusal-reasons.js +0 -363
  491. package/dist/refusal-reasons.js.map +0 -1
  492. package/dist/register-handler.d.ts +0 -36
  493. package/dist/register-handler.d.ts.map +0 -1
  494. package/dist/register-handler.js +0 -285
  495. package/dist/register-handler.js.map +0 -1
  496. package/dist/registration-context.d.ts +0 -72
  497. package/dist/registration-context.d.ts.map +0 -1
  498. package/dist/registration-context.js +0 -126
  499. package/dist/registration-context.js.map +0 -1
  500. package/dist/registration-manager.d.ts +0 -94
  501. package/dist/registration-manager.d.ts.map +0 -1
  502. package/dist/registration-manager.js +0 -585
  503. package/dist/registration-manager.js.map +0 -1
  504. package/dist/registration-persistence.d.ts +0 -183
  505. package/dist/registration-persistence.d.ts.map +0 -1
  506. package/dist/registration-persistence.js +0 -263
  507. package/dist/registration-persistence.js.map +0 -1
  508. package/dist/registry-poll.d.ts +0 -52
  509. package/dist/registry-poll.d.ts.map +0 -1
  510. package/dist/registry-poll.js +0 -140
  511. package/dist/registry-poll.js.map +0 -1
  512. package/dist/registry-version-store-db.d.ts +0 -22
  513. package/dist/registry-version-store-db.d.ts.map +0 -1
  514. package/dist/registry-version-store-db.js +0 -50
  515. package/dist/registry-version-store-db.js.map +0 -1
  516. package/dist/relay-endpoints.d.ts +0 -18
  517. package/dist/relay-endpoints.d.ts.map +0 -1
  518. package/dist/relay-endpoints.js +0 -9
  519. package/dist/relay-endpoints.js.map +0 -1
  520. package/dist/relay-only.d.ts +0 -140
  521. package/dist/relay-only.d.ts.map +0 -1
  522. package/dist/relay-only.js +0 -193
  523. package/dist/relay-only.js.map +0 -1
  524. package/dist/relay-receipt-store.d.ts +0 -155
  525. package/dist/relay-receipt-store.d.ts.map +0 -1
  526. package/dist/relay-receipt-store.js +0 -284
  527. package/dist/relay-receipt-store.js.map +0 -1
  528. package/dist/relay-reconnect.d.ts +0 -32
  529. package/dist/relay-reconnect.d.ts.map +0 -1
  530. package/dist/relay-reconnect.js +0 -29
  531. package/dist/relay-reconnect.js.map +0 -1
  532. package/dist/reply-lag.d.ts +0 -11
  533. package/dist/reply-lag.d.ts.map +0 -1
  534. package/dist/reply-lag.js +0 -46
  535. package/dist/reply-lag.js.map +0 -1
  536. package/dist/resolve-named-agent.d.ts +0 -49
  537. package/dist/resolve-named-agent.d.ts.map +0 -1
  538. package/dist/resolve-named-agent.js +0 -77
  539. package/dist/resolve-named-agent.js.map +0 -1
  540. package/dist/restart-seal-resolver.d.ts +0 -110
  541. package/dist/restart-seal-resolver.d.ts.map +0 -1
  542. package/dist/restart-seal-resolver.js +0 -353
  543. package/dist/restart-seal-resolver.js.map +0 -1
  544. package/dist/resume-last-seen.d.ts +0 -15
  545. package/dist/resume-last-seen.d.ts.map +0 -1
  546. package/dist/resume-last-seen.js +0 -44
  547. package/dist/resume-last-seen.js.map +0 -1
  548. package/dist/retry-queue.d.ts +0 -203
  549. package/dist/retry-queue.d.ts.map +0 -1
  550. package/dist/retry-queue.js +0 -701
  551. package/dist/retry-queue.js.map +0 -1
  552. package/dist/roster-freshness.d.ts +0 -160
  553. package/dist/roster-freshness.d.ts.map +0 -1
  554. package/dist/roster-freshness.js +0 -250
  555. package/dist/roster-freshness.js.map +0 -1
  556. package/dist/screening-status.d.ts +0 -15
  557. package/dist/screening-status.d.ts.map +0 -1
  558. package/dist/screening-status.js +0 -65
  559. package/dist/screening-status.js.map +0 -1
  560. package/dist/seal-carried-close.d.ts +0 -37
  561. package/dist/seal-carried-close.d.ts.map +0 -1
  562. package/dist/seal-carried-close.js +0 -179
  563. package/dist/seal-carried-close.js.map +0 -1
  564. package/dist/seal-certificate-pull.d.ts +0 -79
  565. package/dist/seal-certificate-pull.d.ts.map +0 -1
  566. package/dist/seal-certificate-pull.js +0 -186
  567. package/dist/seal-certificate-pull.js.map +0 -1
  568. package/dist/seal-certified-root-check.d.ts +0 -37
  569. package/dist/seal-certified-root-check.d.ts.map +0 -1
  570. package/dist/seal-certified-root-check.js +0 -127
  571. package/dist/seal-certified-root-check.js.map +0 -1
  572. package/dist/seal-coordinator.d.ts +0 -118
  573. package/dist/seal-coordinator.d.ts.map +0 -1
  574. package/dist/seal-coordinator.js +0 -927
  575. package/dist/seal-coordinator.js.map +0 -1
  576. package/dist/seal-escalation.d.ts +0 -80
  577. package/dist/seal-escalation.d.ts.map +0 -1
  578. package/dist/seal-escalation.js +0 -283
  579. package/dist/seal-escalation.js.map +0 -1
  580. package/dist/seal-evidence-root-check.d.ts +0 -70
  581. package/dist/seal-evidence-root-check.d.ts.map +0 -1
  582. package/dist/seal-evidence-root-check.js +0 -199
  583. package/dist/seal-evidence-root-check.js.map +0 -1
  584. package/dist/seal-failure-store.d.ts +0 -129
  585. package/dist/seal-failure-store.d.ts.map +0 -1
  586. package/dist/seal-failure-store.js +0 -173
  587. package/dist/seal-failure-store.js.map +0 -1
  588. package/dist/seal-flows.d.ts +0 -107
  589. package/dist/seal-flows.d.ts.map +0 -1
  590. package/dist/seal-flows.js +0 -638
  591. package/dist/seal-flows.js.map +0 -1
  592. package/dist/seal-frontier-verify.d.ts +0 -103
  593. package/dist/seal-frontier-verify.d.ts.map +0 -1
  594. package/dist/seal-frontier-verify.js +0 -143
  595. package/dist/seal-frontier-verify.js.map +0 -1
  596. package/dist/seal-leaf.d.ts +0 -58
  597. package/dist/seal-leaf.d.ts.map +0 -1
  598. package/dist/seal-leaf.js +0 -112
  599. package/dist/seal-leaf.js.map +0 -1
  600. package/dist/seal-legibility-tbs.d.ts +0 -25
  601. package/dist/seal-legibility-tbs.d.ts.map +0 -1
  602. package/dist/seal-legibility-tbs.js +0 -77
  603. package/dist/seal-legibility-tbs.js.map +0 -1
  604. package/dist/seal-local-terminus.d.ts +0 -41
  605. package/dist/seal-local-terminus.d.ts.map +0 -1
  606. package/dist/seal-local-terminus.js +0 -170
  607. package/dist/seal-local-terminus.js.map +0 -1
  608. package/dist/seal-receipt-upgrade.d.ts +0 -30
  609. package/dist/seal-receipt-upgrade.d.ts.map +0 -1
  610. package/dist/seal-receipt-upgrade.js +0 -49
  611. package/dist/seal-receipt-upgrade.js.map +0 -1
  612. package/dist/seal-relay-silence.d.ts +0 -51
  613. package/dist/seal-relay-silence.d.ts.map +0 -1
  614. package/dist/seal-relay-silence.js +0 -76
  615. package/dist/seal-relay-silence.js.map +0 -1
  616. package/dist/seal-settle.d.ts +0 -38
  617. package/dist/seal-settle.d.ts.map +0 -1
  618. package/dist/seal-settle.js +0 -33
  619. package/dist/seal-settle.js.map +0 -1
  620. package/dist/seal-upgrade.d.ts +0 -107
  621. package/dist/seal-upgrade.d.ts.map +0 -1
  622. package/dist/seal-upgrade.js +0 -200
  623. package/dist/seal-upgrade.js.map +0 -1
  624. package/dist/sealed-conversation.d.ts +0 -36
  625. package/dist/sealed-conversation.d.ts.map +0 -1
  626. package/dist/sealed-conversation.js +0 -171
  627. package/dist/sealed-conversation.js.map +0 -1
  628. package/dist/sealed-leaf-set.d.ts +0 -92
  629. package/dist/sealed-leaf-set.d.ts.map +0 -1
  630. package/dist/sealed-leaf-set.js +0 -122
  631. package/dist/sealed-leaf-set.js.map +0 -1
  632. package/dist/send-claims.d.ts +0 -56
  633. package/dist/send-claims.d.ts.map +0 -1
  634. package/dist/send-claims.js +0 -50
  635. package/dist/send-claims.js.map +0 -1
  636. package/dist/session-assignment-parser.d.ts +0 -98
  637. package/dist/session-assignment-parser.d.ts.map +0 -1
  638. package/dist/session-assignment-parser.js +0 -325
  639. package/dist/session-assignment-parser.js.map +0 -1
  640. package/dist/session-category.d.ts +0 -19
  641. package/dist/session-category.d.ts.map +0 -1
  642. package/dist/session-category.js +0 -14
  643. package/dist/session-category.js.map +0 -1
  644. package/dist/session-ceremony.d.ts +0 -297
  645. package/dist/session-ceremony.d.ts.map +0 -1
  646. package/dist/session-ceremony.js +0 -964
  647. package/dist/session-ceremony.js.map +0 -1
  648. package/dist/session-closed.d.ts +0 -79
  649. package/dist/session-closed.d.ts.map +0 -1
  650. package/dist/session-closed.js +0 -189
  651. package/dist/session-closed.js.map +0 -1
  652. package/dist/session-connection-gater.d.ts +0 -148
  653. package/dist/session-connection-gater.d.ts.map +0 -1
  654. package/dist/session-connection-gater.js +0 -332
  655. package/dist/session-connection-gater.js.map +0 -1
  656. package/dist/session-content-context.d.ts +0 -162
  657. package/dist/session-content-context.d.ts.map +0 -1
  658. package/dist/session-content-context.js +0 -2
  659. package/dist/session-content-context.js.map +0 -1
  660. package/dist/session-content-handlers.d.ts +0 -82
  661. package/dist/session-content-handlers.d.ts.map +0 -1
  662. package/dist/session-content-handlers.js +0 -1382
  663. package/dist/session-content-handlers.js.map +0 -1
  664. package/dist/session-content-ingest.d.ts +0 -206
  665. package/dist/session-content-ingest.d.ts.map +0 -1
  666. package/dist/session-content-ingest.js +0 -2167
  667. package/dist/session-content-ingest.js.map +0 -1
  668. package/dist/session-content-send.d.ts +0 -191
  669. package/dist/session-content-send.d.ts.map +0 -1
  670. package/dist/session-content-send.js +0 -1360
  671. package/dist/session-content-send.js.map +0 -1
  672. package/dist/session-delivery-acks.d.ts +0 -169
  673. package/dist/session-delivery-acks.d.ts.map +0 -1
  674. package/dist/session-delivery-acks.js +0 -569
  675. package/dist/session-delivery-acks.js.map +0 -1
  676. package/dist/session-ephemerals.d.ts +0 -279
  677. package/dist/session-ephemerals.d.ts.map +0 -1
  678. package/dist/session-ephemerals.js +0 -591
  679. package/dist/session-ephemerals.js.map +0 -1
  680. package/dist/session-leaf-records.d.ts +0 -159
  681. package/dist/session-leaf-records.d.ts.map +0 -1
  682. package/dist/session-leaf-records.js +0 -408
  683. package/dist/session-leaf-records.js.map +0 -1
  684. package/dist/session-lifecycle.d.ts +0 -303
  685. package/dist/session-lifecycle.d.ts.map +0 -1
  686. package/dist/session-lifecycle.js +0 -1679
  687. package/dist/session-lifecycle.js.map +0 -1
  688. package/dist/session-liveness.d.ts +0 -135
  689. package/dist/session-liveness.d.ts.map +0 -1
  690. package/dist/session-liveness.js +0 -347
  691. package/dist/session-liveness.js.map +0 -1
  692. package/dist/session-name.d.ts +0 -35
  693. package/dist/session-name.d.ts.map +0 -1
  694. package/dist/session-name.js +0 -60
  695. package/dist/session-name.js.map +0 -1
  696. package/dist/session-node-factory.d.ts +0 -18
  697. package/dist/session-node-factory.d.ts.map +0 -1
  698. package/dist/session-node-factory.js +0 -182
  699. package/dist/session-node-factory.js.map +0 -1
  700. package/dist/session-node-manager.d.ts +0 -875
  701. package/dist/session-node-manager.d.ts.map +0 -1
  702. package/dist/session-node-manager.js +0 -2995
  703. package/dist/session-node-manager.js.map +0 -1
  704. package/dist/session-node-types.d.ts +0 -1057
  705. package/dist/session-node-types.d.ts.map +0 -1
  706. package/dist/session-node-types.js +0 -657
  707. package/dist/session-node-types.js.map +0 -1
  708. package/dist/session-notify.d.ts +0 -46
  709. package/dist/session-notify.d.ts.map +0 -1
  710. package/dist/session-notify.js +0 -116
  711. package/dist/session-notify.js.map +0 -1
  712. package/dist/session-own-chain-store.d.ts +0 -65
  713. package/dist/session-own-chain-store.d.ts.map +0 -1
  714. package/dist/session-own-chain-store.js +0 -75
  715. package/dist/session-own-chain-store.js.map +0 -1
  716. package/dist/session-queries.d.ts +0 -476
  717. package/dist/session-queries.d.ts.map +0 -1
  718. package/dist/session-queries.js +0 -1007
  719. package/dist/session-queries.js.map +0 -1
  720. package/dist/session-read-handlers.d.ts +0 -87
  721. package/dist/session-read-handlers.d.ts.map +0 -1
  722. package/dist/session-read-handlers.js +0 -682
  723. package/dist/session-read-handlers.js.map +0 -1
  724. package/dist/session-records.d.ts +0 -341
  725. package/dist/session-records.d.ts.map +0 -1
  726. package/dist/session-records.js +0 -858
  727. package/dist/session-records.js.map +0 -1
  728. package/dist/session-relay-client.d.ts +0 -659
  729. package/dist/session-relay-client.d.ts.map +0 -1
  730. package/dist/session-relay-client.js +0 -2876
  731. package/dist/session-relay-client.js.map +0 -1
  732. package/dist/session-relay.d.ts +0 -397
  733. package/dist/session-relay.d.ts.map +0 -1
  734. package/dist/session-relay.js +0 -1636
  735. package/dist/session-relay.js.map +0 -1
  736. package/dist/session-salt-agreement.d.ts +0 -331
  737. package/dist/session-salt-agreement.d.ts.map +0 -1
  738. package/dist/session-salt-agreement.js +0 -472
  739. package/dist/session-salt-agreement.js.map +0 -1
  740. package/dist/session-salts.d.ts +0 -432
  741. package/dist/session-salts.d.ts.map +0 -1
  742. package/dist/session-salts.js +0 -1540
  743. package/dist/session-salts.js.map +0 -1
  744. package/dist/session-schema.d.ts +0 -30
  745. package/dist/session-schema.d.ts.map +0 -1
  746. package/dist/session-schema.js +0 -877
  747. package/dist/session-schema.js.map +0 -1
  748. package/dist/session-seal-leaf-store.d.ts +0 -70
  749. package/dist/session-seal-leaf-store.d.ts.map +0 -1
  750. package/dist/session-seal-leaf-store.js +0 -105
  751. package/dist/session-seal-leaf-store.js.map +0 -1
  752. package/dist/session-seal.d.ts +0 -334
  753. package/dist/session-seal.d.ts.map +0 -1
  754. package/dist/session-seal.js +0 -1017
  755. package/dist/session-seal.js.map +0 -1
  756. package/dist/session-terminal-refusal.d.ts +0 -65
  757. package/dist/session-terminal-refusal.d.ts.map +0 -1
  758. package/dist/session-terminal-refusal.js +0 -87
  759. package/dist/session-terminal-refusal.js.map +0 -1
  760. package/dist/session-tree.d.ts +0 -110
  761. package/dist/session-tree.d.ts.map +0 -1
  762. package/dist/session-tree.js +0 -144
  763. package/dist/session-tree.js.map +0 -1
  764. package/dist/session-views.d.ts +0 -47
  765. package/dist/session-views.d.ts.map +0 -1
  766. package/dist/session-views.js +0 -278
  767. package/dist/session-views.js.map +0 -1
  768. package/dist/signal-handlers.d.ts +0 -63
  769. package/dist/signal-handlers.d.ts.map +0 -1
  770. package/dist/signal-handlers.js +0 -980
  771. package/dist/signal-handlers.js.map +0 -1
  772. package/dist/signal-requirement-policy.d.ts +0 -51
  773. package/dist/signal-requirement-policy.d.ts.map +0 -1
  774. package/dist/signal-requirement-policy.js +0 -89
  775. package/dist/signal-requirement-policy.js.map +0 -1
  776. package/dist/signal-revocability.d.ts +0 -49
  777. package/dist/signal-revocability.d.ts.map +0 -1
  778. package/dist/signal-revocability.js +0 -93
  779. package/dist/signal-revocability.js.map +0 -1
  780. package/dist/signal-submission.d.ts +0 -181
  781. package/dist/signal-submission.d.ts.map +0 -1
  782. package/dist/signal-submission.js +0 -368
  783. package/dist/signal-submission.js.map +0 -1
  784. package/dist/signaling-connect.d.ts +0 -118
  785. package/dist/signaling-connect.d.ts.map +0 -1
  786. package/dist/signaling-connect.js +0 -538
  787. package/dist/signaling-connect.js.map +0 -1
  788. package/dist/signaling-wiring.d.ts +0 -105
  789. package/dist/signaling-wiring.d.ts.map +0 -1
  790. package/dist/signaling-wiring.js +0 -393
  791. package/dist/signaling-wiring.js.map +0 -1
  792. package/dist/singleton-lock.d.ts +0 -85
  793. package/dist/singleton-lock.d.ts.map +0 -1
  794. package/dist/singleton-lock.js +0 -219
  795. package/dist/singleton-lock.js.map +0 -1
  796. package/dist/sqlcipher-db.d.ts +0 -139
  797. package/dist/sqlcipher-db.d.ts.map +0 -1
  798. package/dist/sqlcipher-db.js +0 -357
  799. package/dist/sqlcipher-db.js.map +0 -1
  800. package/dist/standing-receivers.d.ts +0 -319
  801. package/dist/standing-receivers.d.ts.map +0 -1
  802. package/dist/standing-receivers.js +0 -1295
  803. package/dist/standing-receivers.js.map +0 -1
  804. package/dist/start-agent.d.ts +0 -64
  805. package/dist/start-agent.d.ts.map +0 -1
  806. package/dist/start-agent.js +0 -136
  807. package/dist/start-agent.js.map +0 -1
  808. package/dist/status-handler.d.ts +0 -54
  809. package/dist/status-handler.d.ts.map +0 -1
  810. package/dist/status-handler.js +0 -61
  811. package/dist/status-handler.js.map +0 -1
  812. package/dist/submission-retry.d.ts +0 -208
  813. package/dist/submission-retry.d.ts.map +0 -1
  814. package/dist/submission-retry.js +0 -506
  815. package/dist/submission-retry.js.map +0 -1
  816. package/dist/telegram-bot-client.d.ts +0 -34
  817. package/dist/telegram-bot-client.d.ts.map +0 -1
  818. package/dist/telegram-bot-client.js +0 -36
  819. package/dist/telegram-bot-client.js.map +0 -1
  820. package/dist/telegram-doorbell.d.ts +0 -38
  821. package/dist/telegram-doorbell.d.ts.map +0 -1
  822. package/dist/telegram-doorbell.js +0 -140
  823. package/dist/telegram-doorbell.js.map +0 -1
  824. package/dist/test-handlers.d.ts +0 -62
  825. package/dist/test-handlers.d.ts.map +0 -1
  826. package/dist/test-handlers.js +0 -240
  827. package/dist/test-handlers.js.map +0 -1
  828. package/dist/testing.d.ts +0 -10
  829. package/dist/testing.d.ts.map +0 -1
  830. package/dist/testing.js +0 -10
  831. package/dist/testing.js.map +0 -1
  832. package/dist/transport-composition.d.ts +0 -31
  833. package/dist/transport-composition.d.ts.map +0 -1
  834. package/dist/transport-composition.js +0 -55
  835. package/dist/transport-composition.js.map +0 -1
  836. package/dist/transport-selector.d.ts +0 -202
  837. package/dist/transport-selector.d.ts.map +0 -1
  838. package/dist/transport-selector.js +0 -196
  839. package/dist/transport-selector.js.map +0 -1
  840. package/dist/trust-signal-pickup-listener.d.ts +0 -43
  841. package/dist/trust-signal-pickup-listener.d.ts.map +0 -1
  842. package/dist/trust-signal-pickup-listener.js +0 -48
  843. package/dist/trust-signal-pickup-listener.js.map +0 -1
  844. package/dist/trust-signal-store.d.ts +0 -406
  845. package/dist/trust-signal-store.d.ts.map +0 -1
  846. package/dist/trust-signal-store.js +0 -939
  847. package/dist/trust-signal-store.js.map +0 -1
  848. package/dist/trust-signal-sweep-tick.d.ts +0 -60
  849. package/dist/trust-signal-sweep-tick.d.ts.map +0 -1
  850. package/dist/trust-signal-sweep-tick.js +0 -101
  851. package/dist/trust-signal-sweep-tick.js.map +0 -1
  852. package/dist/trust-signal-sweep.d.ts +0 -94
  853. package/dist/trust-signal-sweep.d.ts.map +0 -1
  854. package/dist/trust-signal-sweep.js +0 -147
  855. package/dist/trust-signal-sweep.js.map +0 -1
  856. package/dist/type-registry.d.ts +0 -42
  857. package/dist/type-registry.d.ts.map +0 -1
  858. package/dist/type-registry.js +0 -37
  859. package/dist/type-registry.js.map +0 -1
  860. package/dist/types.d.ts +0 -746
  861. package/dist/types.d.ts.map +0 -1
  862. package/dist/types.js +0 -20
  863. package/dist/types.js.map +0 -1
  864. package/dist/unresolved-nodes-report.d.ts +0 -14
  865. package/dist/unresolved-nodes-report.d.ts.map +0 -1
  866. package/dist/unresolved-nodes-report.js +0 -96
  867. package/dist/unresolved-nodes-report.js.map +0 -1
  868. package/dist/vocabulary.d.ts +0 -150
  869. package/dist/vocabulary.d.ts.map +0 -1
  870. package/dist/vocabulary.js +0 -386
  871. package/dist/vocabulary.js.map +0 -1
  872. package/dist/who-label.d.ts +0 -28
  873. package/dist/who-label.d.ts.map +0 -1
  874. package/dist/who-label.js +0 -31
  875. package/dist/who-label.js.map +0 -1
  876. package/dist/who-resolver.d.ts +0 -15
  877. package/dist/who-resolver.d.ts.map +0 -1
  878. package/dist/who-resolver.js +0 -47
  879. package/dist/who-resolver.js.map +0 -1
  880. package/dist/wire-content-hash.d.ts +0 -93
  881. package/dist/wire-content-hash.d.ts.map +0 -1
  882. package/dist/wire-content-hash.js +0 -116
  883. package/dist/wire-content-hash.js.map +0 -1
  884. package/dist/withheld-content.d.ts +0 -13
  885. package/dist/withheld-content.d.ts.map +0 -1
  886. package/dist/withheld-content.js +0 -46
  887. package/dist/withheld-content.js.map +0 -1
  888. package/dist/witness-alerts.d.ts +0 -40
  889. package/dist/witness-alerts.d.ts.map +0 -1
  890. package/dist/witness-alerts.js +0 -102
  891. package/dist/witness-alerts.js.map +0 -1
@@ -1,1418 +0,0 @@
1
- /**
2
- * cello_close_session — the bilateral close, and every way it can fail.
3
- *
4
- * M7 error discipline: each distinct failure cause produces a DISTINCT error code
5
- * (session_already_sealed, seal_in_progress, seal_interrupted_counterparty_unavailable,
6
- * seal_interrupted_rejected_by_counterparty, signaling_reconnecting). A close that fails must tell
7
- * the operator WHY, not merely that it failed — this handler exists as much for its error paths as
8
- * for its happy path.
9
- *
10
- * SI-001: there is NO auto-seal on a session_interrupted receipt. The operator must close
11
- * explicitly. A daemon that sealed on its own would notarize a conversation nobody chose to end.
12
- *
13
- * SI-001 IS NARROWER THAN IT READS, and the boundary matters (DOD-M12B-RESTART-SEAL-1, 2026-08-17;
14
- * RESTATED 2026-08-18 for DOD-M12B-PENDING-RESOLVE-1). SI-001 governs a LIVE interruption — the
15
- * relay says the counterparty vanished while the operator is at the keyboard and may still want to
16
- * wait. `restart-seal-resolver.ts` seals TWO different populations, and the test that licenses both
17
- * is **somebody chose to end this**, not "we caused it":
18
- *
19
- * 1. `interrupted` with `interrupted_by = 'local'` — OUR OWN stop destroyed it, and it cannot be
20
- * resumed because the transport keypairs died with the process. The only alternative is
21
- * force-abandon, which forfeits the receipt: the choice is seal-or-abandon, not seal-or-resume.
22
- * 2. `seal_interrupted_pending` — a seal commitment nobody asked the directory to notarize.
23
- *
24
- * **The old sentence here — "nothing the counterparty caused is auto-sealed" — is no longer true,
25
- * and saying so plainly matters because this paragraph is what a future widening will cite.** A
26
- * responder-side pending row is created by a request the COUNTERPARTY sent. What licenses it is not
27
- * that we caused it but that they explicitly asked to seal; an initiator-side row is licensed by
28
- * their signed leaf. Either way the conversation is one somebody chose to end, which is exactly what
29
- * SI-001 protects against — and SI-001 itself holds unchanged for a live interruption.
30
- *
31
- * This was on my DO-NOT-CUT list. It has fifteen dependencies — a long list, but a KNOWN one, and
32
- * that is the entire difference from a closure over 73 shared locals.
33
- */
34
- import { randomUUID } from "node:crypto";
35
- import { isLocalCredentialRefusal } from "./session-relay-client.js";
36
- /**
37
- * DOD-M12B-ABANDON-NOTIFY-1 — how long the courtesy notice may delay a force-abandon.
38
- *
39
- * Force-abandon is the operator's escape hatch out of a session that can never seal, and it must
40
- * always return. Telling the counterparty is worth a moment; it is not worth the escape hatch.
41
- */
42
- const ABANDON_NOTICE_DEADLINE_MS = 3_000;
43
- /**
44
- * What a failed SEAL-leaf submit means, by reason. `seal_stale` used to share the "local and
45
- * temporary" text, which sent an operator to agent startup and relay reachability while every retry
46
- * failed the same way: the relay was refusing because it holds a message this side never recorded.
47
- */
48
- function sealSubmitCause(reason) {
49
- if (reason === "counterparty_content_missing") {
50
- return "The other side filed a message with the relay that this side does not hold, so a close signed now would seal a conversation missing it. " +
51
- "It may still be arriving: wait a moment and close again. If it keeps failing, this side refused or lost that message (check cello_inbox for a refusal), " +
52
- "and this conversation cannot be sealed as it stands — closing with { force: true } ends it without a seal.";
53
- }
54
- if (reason === "seal_stale") {
55
- return "The relay holds a message from the other side that this side has not recorded, so a close signed now would seal a conversation missing it. " +
56
- "If they are still sending, wait for the message to arrive (cello_receive) and close again. " +
57
- "If nothing arrives, retrying will fail the same way: report the session id — do not force-abandon, which forfeits the seal.";
58
- }
59
- return "That is usually local and temporary — an agent that is not started yet (cello_start_agent), or a relay this daemon cannot currently reach (cello_status). " +
60
- "The conversation is intact either way; retry cello_close_session once the daemon reports healthy.";
61
- }
62
- import { classifyOnlineResult } from "./cross-node-negotiation.js";
63
- import { validateSessionName } from "./session-name.js";
64
- import { escalateToUnilateralSeal as runUnilateralEscalation, UNILATERAL_SEAL_TIMEOUT_MS } from "./seal-escalation.js";
65
- import { describeSealCommitted } from "./close-commitment.js";
66
- import { extractErrorMessage } from "./error-message.js";
67
- import { awaitOwnRecordSettled } from "./seal-settle.js";
68
- export function registerCloseSessionHandler(deps) {
69
- const { resolveConsortiumRoster, handlers, logger, sessionNodeManager, getConnState, resolveCurrentAgent, NO_CURRENT_AGENT_RESPONSE, getKeyProvider, signalingFor, sendOver, waitForSignalingConnected, openVisitingConnection, runDiscoveryLookup, crossNodeBrokerBySession, sealKey, sealInterruptedInProgress, registerBackgroundSeal, sealFailures, pendingSealWaiters, pendingUnilateralWaiters, handleSealInterruptedFlow, handleActiveSealFlow, recoverParkedContent, } = deps;
70
- const UNILATERAL_TIMEOUT_MS = deps.unilateralTimeoutMs ?? UNILATERAL_SEAL_TIMEOUT_MS;
71
- /**
72
- * How long the seal path waits to learn where the counterparty is homed.
73
- *
74
- * DELIBERATELY SHORT, and not the 10s the session negotiator uses. This lookup is a point-read of
75
- * replicated presence — milliseconds against a healthy directory — and it sits in front of EVERY
76
- * interrupted close, including same-node ones that need no dial at all. Borrowing the negotiator's
77
- * 10s added ten seconds to every such close and pushed the unilateral-escalation tests past their
78
- * budget: the seal did not break, it just arrived too late to matter.
79
- *
80
- * A directory that cannot answer within this window is one we would not want to wait on anyway —
81
- * the close proceeds on the home stream, exactly as it did before the lookup existed.
82
- */
83
- const SEAL_DISCOVERY_TIMEOUT_MS = 2_500;
84
- /**
85
- * Open a transient visiting connection to the node that BROKERED this session, when that is not
86
- * the node this agent is homed on. Returns null for a same-node session (the ordinary case) and
87
- * whenever the connection cannot be established — in both the caller proceeds on the home stream.
88
- *
89
- * WHY THE CLIENT HAS TO DIAL. Directories never forward signaling to each other; the M12 journal
90
- * struck that out as a channel that does not exist. A directory routes a frame by looking up a
91
- * stream IT holds, and a daemon holds its stream to its own home node — so seal frames pushed by
92
- * the broker reach a party only if that party holds a stream to the BROKER. Without this the
93
- * close times out reporting the counterparty unavailable while they are online and waiting.
94
- *
95
- * SHARED BY BOTH SEAL PATHS ON PURPOSE. This lived inline in the active branch, gated on
96
- * `status === "active"`, and the interrupted branch sent the same frames without it — which is
97
- * precisely the defect (two agents on two nodes, symmetric timeout, 2026-08-07). One
98
- * implementation is what stops that divergence coming back.
99
- */
100
- async function openSealBrokerConnection(agentName, sessionId, correlationId,
101
- /** Only set on the RETRY, after a home-stream attempt already failed — see the call site. */
102
- discoverVia) {
103
- let brokerNodeForSeal = crossNodeBrokerBySession.get(`${agentName}:${sessionId}`);
104
- // THE MAP DOES NOT SURVIVE THE RESTART THAT CREATES THE CONDITION. It is in-memory, populated
105
- // while a session is brokered, and emptied by a daemon restart — which is exactly what flips a
106
- // live session to `interrupted`. Gating on it alone meant the dial never fired for the case it
107
- // was added for (shipped as 0.0.140 and disproved against real stranded sessions the same hour).
108
- //
109
- // The answer is NOT to persist the broker. The node that brokered the session hours ago need
110
- // not be where the counterparty lives now — agents re-home. What matters at seal time is where
111
- // they are NOW, which any directory can answer from replicated presence. Classified by the same
112
- // `classifyOnlineResult` the outbound path uses, so "same node" and "offline" mean here exactly
113
- // what they mean there.
114
- if (!brokerNodeForSeal && discoverVia && runDiscoveryLookup) {
115
- const sig = signalingFor(agentName);
116
- if (sig) {
117
- const disc = await runDiscoveryLookup(sig, discoverVia.counterpartyPubkeyHex, SEAL_DISCOVERY_TIMEOUT_MS, correlationId);
118
- if (disc.kind === "result") {
119
- const action = classifyOnlineResult(disc.state, disc.owningNodeIds, sig.currentDirectoryNodeId ?? null);
120
- // Only `cross_node` warrants a dial. `same_node` already has the right stream, and
121
- // `offline` is a real answer — dialling their node cannot conjure a stream they do not have.
122
- if (action.kind === "cross_node")
123
- brokerNodeForSeal = action.owningNodeId;
124
- }
125
- else {
126
- // A lookup that did not answer is the pre-fix behaviour, not a new failure: proceed on the
127
- // home stream. Logged so it stays distinguishable from "the counterparty is on our node".
128
- logger.warn("session.seal.broker.discovery_failed", { agentName, sessionId, kind: disc.kind, correlationId });
129
- }
130
- }
131
- }
132
- // Same-node session, or nowhere to dial: the home stream is already the right one.
133
- if (!brokerNodeForSeal)
134
- return null;
135
- const sealKp = getKeyProvider(agentName);
136
- if (!sealKp) {
137
- // Never skip the cross-node reconnect silently — without a key provider the seal reverts to
138
- // the pre-fix timeout, and this log is what keeps that distinguishable from an ordinary
139
- // counterparty-didn't-close timeout.
140
- logger.warn("session.seal.broker.no_keyprovider", { agentName, brokerNode: brokerNodeForSeal, correlationId });
141
- return null;
142
- }
143
- const sealPubHex = Buffer.from(await sealKp.getPublicKey()).toString("hex");
144
- const roster = await resolveConsortiumRoster();
145
- const brokerTarget = roster?.find((e) => e.nodeId === brokerNodeForSeal) ?? null;
146
- if (!brokerTarget) {
147
- logger.warn("session.seal.broker.unresolved", { agentName, brokerNode: brokerNodeForSeal, correlationId });
148
- return null;
149
- }
150
- const conn = openVisitingConnection(agentName, sealKp, sealPubHex, { peerId: brokerTarget.peerId, multiaddr: brokerTarget.multiaddr }, correlationId, brokerNodeForSeal);
151
- if (await waitForSignalingConnected(conn.mgr, 10_000)) {
152
- logger.info("session.seal.broker.reconnected", { agentName, brokerNode: brokerNodeForSeal, correlationId });
153
- return conn;
154
- }
155
- // Tear the half-open connection down rather than leaking it, and proceed degraded.
156
- await conn.stop("seal-broker-unreachable");
157
- logger.warn("session.seal.broker.unreachable", { agentName, brokerNode: brokerNodeForSeal, correlationId });
158
- return null;
159
- }
160
- // ─── M7-SESSION-001: cello_close_session ────────────────────────────────────
161
- // M7 error discipline: each distinct failure cause produces a distinct error code.
162
- // AC-010: session_already_sealed
163
- // AC-011: seal_in_progress
164
- // AC-012: seal_interrupted_counterparty_unavailable
165
- // AC-013: seal_interrupted_rejected_by_counterparty
166
- // DB-001: signaling_reconnecting
167
- // SI-001: no auto-seal on session_interrupted receipt; operator must call explicitly
168
- /**
169
- * DOD-TERMINAL-STATE-DIVERGENCE-1 — close failures that might mean "it is ALREADY sealed, you were
170
- * just never told".
171
- *
172
- * `session_sealed` is pushed once. Miss it and this side holds a non-terminal row while the
173
- * counterparty holds a receipt, and every close from here fails in a way that reads exactly like
174
- * "the seal has not happened yet". These are the reasons where that is a live possibility:
175
- *
176
- * - the counterparty REJECTED our seal request. It does that when it considers the session
177
- * already finished, which is precisely the divergence.
178
- * - the unilateral escalation TIMED OUT with no answer.
179
- * - the relay says the session is sealed or gone — terminal, so nothing more can be added, which
180
- * also means whatever exists is final.
181
- *
182
- * NOT included: `session_not_found`, `invalid_session_id`, an abandoned session, or a refusal we
183
- * generated locally. Asking there would be noise on a question already answered.
184
- */
185
- const MAY_ALREADY_BE_SEALED = new Set([
186
- "seal_interrupted_rejected_by_counterparty",
187
- "seal_unilateral_timeout",
188
- "session_seal_already_pending",
189
- "session_sealed",
190
- /**
191
- * DOD-M15-TERMINAL-REASON-1. Without this, a close on a session the relay REFUSED skips
192
- * `pullSealCertificate` entirely — the one probe that distinguishes "never sealed" from "sealed
193
- * and you were never told". Losing it here is how a close ends with no receipt and no
194
- * explanation of why.
195
- */
196
- "seal_refused",
197
- "relay_session_gone",
198
- ]);
199
- /**
200
- * The close, wrapped so a failure gets ONE chance to discover the seal already happened.
201
- *
202
- * Wrapped rather than patched into each exit: this handler has six failure returns and a seventh
203
- * would be added without the recovery. The operator's experience is what matters here — they ran a
204
- * close, it failed, and the answer they get should be a receipt if one exists.
205
- */
206
- const closeSession = async (params, connectionId) => {
207
- const first = await runClose(params, connectionId);
208
- const failed = first;
209
- if (failed?.ok !== false || !deps.pullSealCertificate)
210
- return first;
211
- if (!MAY_ALREADY_BE_SEALED.has(String(failed.reason)))
212
- return first;
213
- const connState = getConnState(connectionId);
214
- const agentName = resolveCurrentAgent(connState, params?.agent);
215
- // `session_id` — the IPC parameter name. The MCP tool's `cello_session_id` is translated by the
216
- // shim, and reading that name here would have found nothing and silently skipped the recovery.
217
- const sessionId = typeof params?.session_id === "string" ? params.session_id : "";
218
- if (!agentName || sessionId.length === 0)
219
- return first;
220
- const pulled = await deps.pullSealCertificate(agentName, sessionId);
221
- if (!pulled.ok) {
222
- // Said out loud. "We asked and there is none" is a different fact from "we never asked", and
223
- // an operator deciding whether to force-abandon needs to know which one they have.
224
- logger.info("seal.certificate.pull.none_on_close", {
225
- agentName, sessionId, closeReason: failed.reason, pullReason: pulled.reason ?? "",
226
- });
227
- return {
228
- ...first,
229
- seal_lookup: pulled.reason === "not_found" ? "asked_none_exists" : `asked_${pulled.reason ?? "failed"}`,
230
- };
231
- }
232
- const recovered = sessionNodeManager.getSealCertificate(agentName, sessionId);
233
- if (!recovered)
234
- return first;
235
- logger.info("seal.certificate.recovered_on_close", {
236
- agentName, sessionId, sealedRoot: recovered.sealed_root,
237
- impact: "the seal existed and this side had never been told; the close now returns the receipt",
238
- });
239
- return {
240
- ok: true,
241
- sessionId,
242
- sealed_root: recovered.sealed_root,
243
- recovered: true,
244
- guidance: "This session was ALREADY sealed — the notarization existed and this daemon had never been " +
245
- "told, which is why the close appeared to fail. The certificate has been fetched and " +
246
- "verified against its signature, and is now held locally. Read it with cello_sealed_receipt.",
247
- };
248
- };
249
- handlers.set("cello_close_session", closeSession);
250
- /**
251
- * DOD-M12B-SEAL-ESCALATE-DUP-1: the body lives in `seal-escalation.ts` so the away/one-shot path
252
- * in `daemon.ts` shares it. It used to be a second copy, and it missed every refusal this
253
- * milestone added.
254
- */
255
- const escalateToUnilateralSeal = (record, sessionId, escalation, correlationId, opts = { refuseOnUnusableCarry: false }) => runUnilateralEscalation({
256
- logger, sessionNodeManager, sendOver, pendingUnilateralWaiters, sealKey, getKeyProvider,
257
- timeoutMs: UNILATERAL_TIMEOUT_MS,
258
- onSealed: (a, sid) => { crossNodeBrokerBySession.delete(`${a}:${sid}`); },
259
- }, record.agent_name, sessionId, escalation, correlationId, opts);
260
- /**
261
- * Post this side's SEAL ctrl leaf (or recover the one a previous run already posted) and ask the
262
- * directory to notarize. `null` means there was nothing to escalate with — the caller keeps its
263
- * own result rather than inventing a failure.
264
- *
265
- * SHARED so a third copy cannot appear. It has two callers: the interrupted close, and the
266
- * `seal_interrupted_pending` close that used to be refused outright.
267
- */
268
- const submitAndEscalate = async (record, sessionId, correlationId) => {
269
- const submitted = await sessionNodeManager.submitSealLeaf(record.agent_name, sessionId, correlationId);
270
- if (submitted.ok || submitted.reason === "responder_seal_already_submitted") {
271
- logger.info("session.interrupted.seal.leaf.submitted", {
272
- agentName: record.agent_name, sessionId, correlationId,
273
- });
274
- }
275
- else {
276
- logger.warn("session.interrupted.seal.leaf.submit_failed", {
277
- agentName: record.agent_name, sessionId, reason: submitted.reason, correlationId,
278
- impact: "both sides hold a signed commitment, but no notarization was requested — this session has no receipt and cannot get one once the relay drops it",
279
- });
280
- }
281
- // The root and sequence the directory verifies against. `responder_seal_already_submitted`
282
- // carries them from the FIRST submit — from THIS process's memory, or recovered from the
283
- // durable carry after a restart — so a repeat close can still escalate (M8B FINDING-1).
284
- const escalation = submitted.ok
285
- ? { reportedRootHex: submitted.reportedRootHex, sequenceNumber: submitted.sequenceNumber }
286
- : typeof submitted.reportedRootHex === "string" && typeof submitted.sequenceNumber === "number"
287
- ? { reportedRootHex: submitted.reportedRootHex, sequenceNumber: submitted.sequenceNumber }
288
- : null;
289
- // `submitted.ok` always yields an escalation above, so this is only ever the failure reason.
290
- // Kept as a total expression rather than a cast: if submitSealLeaf's contract is ever loosened,
291
- // this names the new case instead of building `{reportedRootHex: undefined}` and throwing at
292
- // `Buffer.from` several frames away.
293
- if (!escalation)
294
- return { escalated: false, reason: submitted.ok ? "submit_returned_no_root" : submitted.reason };
295
- return await escalateToUnilateralSeal(record, sessionId, escalation, correlationId, {
296
- refuseOnUnusableCarry: true,
297
- });
298
- };
299
- const runClose = async (params, connectionId) => {
300
- const connState = getConnState(connectionId);
301
- // CC-3 / M8C-AUTOSTART-1 F18: explicit { agent } > this connection's current > sole online agent.
302
- const agentName = resolveCurrentAgent(connState, params?.agent);
303
- if (!agentName) {
304
- return NO_CURRENT_AGENT_RESPONSE;
305
- }
306
- // round-2 BLOCKING: the public IPC contract field is snake_case `session_id`
307
- // (this is what cello-mcp.ts forwards verbatim through IpcProxy, matching the
308
- // rest of the public MCP tool surface — target_pubkey, content_hash, timeout_ms).
309
- // Reading camelCase `sessionId` here meant every real proxy invocation produced
310
- // undefined → missing_params. Consume the field the producer actually sends.
311
- const sessionId = params?.session_id;
312
- if (!sessionId) {
313
- return {
314
- ok: false,
315
- reason: "missing_params",
316
- guidance: "Provide 'session_id' parameter with the hex session ID to close.",
317
- };
318
- }
319
- // DOD-SESSION-NAME-1 (AC-A6): THE NAME MUST NEVER BREAK THE CLOSE. A close is a seal ceremony
320
- // and the seal is the valuable thing — a notarized receipt both parties keep. So a bad name is
321
- // refused HERE, before any of it starts: not half-closed-then-failed, and not silently dropped
322
- // and sealed anyway. Validate, then close. The name is a sticky note; it does not get a vote on
323
- // whether the notarization happens.
324
- const nameCheck = validateSessionName(params?.session_name);
325
- if (!nameCheck.ok) {
326
- logger.warn("session.name.rejected", {
327
- // agentId, not agentName — session.name.set/.cleared key on agentId (AC-A16), and an
328
- // operator correlating rejections against sets on two different keys gets nothing.
329
- agentId: sessionNodeManager.resolveAgentId(agentName), sessionId, reason: nameCheck.reason, source: "close",
330
- nameLength: typeof params?.session_name === "string" ? params.session_name.length : null,
331
- });
332
- return { ok: false, reason: nameCheck.reason, guidance: nameCheck.guidance };
333
- }
334
- // DOD-LOOP-1: scope the lookup to the current agent — the composite (agent, session_id) key IS
335
- // the ownership scope. A session_id owned only by a DIFFERENT agent does not exist in this
336
- // agent's namespace (returns null → session_not_found), which is correct for loopback (two
337
- // agents can hold the same session_id on one daemon).
338
- const record = sessionNodeManager.getSessionRecord(agentName, sessionId);
339
- if (!record) {
340
- return {
341
- ok: false,
342
- reason: "session_not_found",
343
- guidance: "No session found with this ID. Check cello_sessions for active and interrupted sessions.",
344
- };
345
- }
346
- // Ownership: redundant now that the lookup is agent-scoped (record.agent_name === currentAgent),
347
- // kept as a defensive invariant.
348
- if (record.agent_name !== agentName) {
349
- return {
350
- ok: false,
351
- reason: "session_not_owned",
352
- guidance: "This session belongs to a different agent. Call cello_use_agent to switch to the agent that owns it (see cello_sessions), then retry.",
353
- };
354
- }
355
- // DOD-SESSION-NAME-1 (AC-A7): the name is written HERE — once, on the way in — rather than at
356
- // each of the terminal exits (bilateral seal, unilateral seal, force-abandon). Threading it
357
- // through all of them is how one silently ends up without it, and a name dropped on the very
358
- // path where it is most useful (a force-abandoned ghost is the session you most want to identify
359
- // later) is a defect nothing would fail on.
360
- //
361
- // It sits ABOVE the status gates deliberately. A name is not close-scoped: renaming is legal in
362
- // ANY status (AC-A9), sealed included. Below the already-sealed gate, a RETRIED close carrying a
363
- // name — the seal completed but the agent's call was interrupted, so it calls again — would be
364
- // answered "already sealed, no further action is needed" and the name would be silently dropped,
365
- // on exactly the path where the agent believes it just named the session.
366
- //
367
- // Writing it before the close COMPLETES is safe for the same reason: if the seal below fails, the
368
- // operator has a named open session — a state they could produce with cello_name_session anyway.
369
- // Nothing about the name gates, delays, or alters the ceremony.
370
- if (nameCheck.value !== null) {
371
- sessionNodeManager.setSessionName(agentName, sessionId, nameCheck.value);
372
- logger.info("session.name.set", {
373
- agentId: record.agent_id, sessionId, nameLength: nameCheck.value.length, source: "close",
374
- });
375
- }
376
- // AC-010: already sealed
377
- if (record.status === "sealed") {
378
- return {
379
- ok: false,
380
- reason: "session_already_sealed",
381
- // A name given here WAS applied (above) — say so, rather than let "no further action is
382
- // needed" imply the name went nowhere.
383
- ...(nameCheck.value !== null ? { session_name: nameCheck.value } : {}),
384
- guidance: nameCheck.value !== null
385
- ? "Already sealed by both sides. Nothing more to do; the name you gave was applied. View the receipt with cello_sealed_receipt."
386
- : "Already sealed by both sides. Nothing more to do; view the receipt with cello_sealed_receipt.",
387
- };
388
- }
389
- // An ABANDONED session is TERMINAL, and a close without --force must say so rather than try to
390
- // seal it. The already-abandoned early-return below lives INSIDE the force branch, so a plain
391
- // `cello close-session <abandoned-id>` fell through to the seal flow and fired a bilateral seal
392
- // ceremony at a counterparty that, by definition, was never there — the exact unsealable hang
393
- // that force exists to escape, re-entered from the other side. Found by the unit reviewer on this
394
- // diff; pre-existing, fixed here rather than left for someone to hit in the dark.
395
- // Scoped to the NON-force path: `force` on an already-abandoned session stays idempotent
396
- // (ok:true / already_abandoned, below) — that contract is tested and must not change.
397
- if (record.status === "abandoned" && params?.force !== true) {
398
- return {
399
- ok: false,
400
- reason: "session_abandoned",
401
- guidance: "This session was force-abandoned — it is terminal and has no counterparty to seal with, so it cannot be closed. It is already off the open list; check cello_sessions.",
402
- };
403
- }
404
- // CC-5/F21 terminal-escape: force-abandon a session that can never be bilaterally sealed — a
405
- // half-open handshake the counterparty never joined, whose normal close fires a seal the absent/
406
- // rejecting counterparty can't complete (seal_interrupted_rejected_by_counterparty / timeout). Marks
407
- // it locally-terminal ("abandoned") with NO seal, so it leaves the open list. Additive: placed BEFORE
408
- // the seal branches, it never touches the seal flow. Owner-scoped (the lookup above is agent-scoped)
409
- // and idempotent. NOT for a healthy session — a normal close (no force) still seals so both parties
410
- // get a notarized receipt; force is the escape hatch for a provably unsealable ghost.
411
- if (params?.force === true) {
412
- if (record.status === "abandoned") {
413
- return { ok: true, status: "abandoned", reason: "already_abandoned", guidance: "This session was already force-abandoned." };
414
- }
415
- // A session that reached a seal attempt (interrupted / seal_interrupted_pending) MIGHT have
416
- // been notarized on the counterparty's side without this side ever learning — the
417
- // no-pull-twin divergence. A half-open ghost never got that far and forfeits nothing. Warning
418
- // on both would put the same paragraph on the ninety routine force-abandons that cost nothing
419
- // and the one that cost a receipt, which buries the only occurrence that matters.
420
- const mayHaveSealed = record.status === "interrupted" || record.status === "seal_interrupted_pending";
421
- // ONE id for the close and every line the notice emits, so they can be joined. Minting a
422
- // fresh one inside the notice call meant its sent/failed/skipped lines belonged to no flow.
423
- const correlationId = randomUUID();
424
- // DOD-M12B-ABANDON-NOTIFY-1: TELL THEM FIRST, while the session node still exists — the
425
- // abandon tears it down, and after that there is no stream to tell them on. Awaited so the
426
- // answer can be honest about whether they were reached, but it can only ever return: it never
427
- // throws and never blocks the abandon, which is the operator's escape hatch and must not
428
- // depend on the counterparty being reachable.
429
- //
430
- // Without this the other side keeps its half live, keeps retrying delivery into it, and keeps
431
- // trying to re-establish — which is what produced the 2026-08-17 storm, where the operator saw
432
- // connection requests from agents nobody was driving.
433
- // DELIBERATELY FAIL-OPEN, and this is the one place in the milestone where that is right:
434
- // force-abandon is the operator's escape hatch out of a session that can never seal, and it
435
- // must not become conditional on a courtesy. The notice already handles its own failures; this
436
- // catches the unexpected — an absent method, a throw from the transport layer — so that no
437
- // fault in telling the counterparty can prevent the operator from ending their own session.
438
- let notice = { told: false, reason: "not_attempted" };
439
- try {
440
- // A HARD DEADLINE, not just a catch. The catch covers a throw; it does not cover a hang,
441
- // and `stream.close()` waits for the write buffer to drain with no timeout anywhere in the
442
- // chain — so a half-dead connection could sit here indefinitely on the one call that must
443
- // always return. Force-abandon is the escape hatch; it does not get to be slow either.
444
- notice = await Promise.race([
445
- sessionNodeManager.notifyCounterpartyAbandon(record.agent_name, sessionId, correlationId),
446
- new Promise((resolve) => {
447
- const t = setTimeout(() => resolve({ told: false, reason: "notice_timeout" }), ABANDON_NOTICE_DEADLINE_MS);
448
- t.unref?.();
449
- }),
450
- ]);
451
- }
452
- catch (err) {
453
- logger.warn("session.abandon.notice.threw", {
454
- agentName: record.agent_name, sessionId, correlationId,
455
- error: extractErrorMessage(err),
456
- impact: "the counterparty was not told and may keep calling; the abandon itself proceeds",
457
- });
458
- }
459
- const told = notice.told;
460
- await sessionNodeManager.abandonSession(record.agent_name, sessionId);
461
- /**
462
- * FORGET ANY SEAL FAILURE — review MEDIUM-5.
463
- *
464
- * An abandoned session is TERMINAL: there is no ceremony to retry and no receipt to obtain.
465
- * Leaving the marker made two surfaces contradict each other — `cello_sealed_receipt` reported
466
- * `seal_failed` and told the agent to *"call cello_close_session again"*, while
467
- * `cello_close_session` refused with `session_abandoned` and *"it is terminal… it cannot be
468
- * closed."* A refused loop, and exactly the contradicting-surfaces pair the previous unit's
469
- * review flagged, reintroduced one unit later.
470
- */
471
- sealFailures.clear(record.agent_name, sessionId);
472
- logger.info("session.force_abandoned", {
473
- agentName: record.agent_name,
474
- sessionId,
475
- priorStatus: record.status,
476
- counterpartyNotified: told,
477
- counterpartyNoticeReason: notice.reason,
478
- correlationId,
479
- // Makes the destructive case findable in a log instead of reconstructable by hand, which is
480
- // how the 2026-08-06 incident had to be traced.
481
- mayHaveForfeitedSeal: mayHaveSealed,
482
- });
483
- return {
484
- ok: true,
485
- status: "abandoned",
486
- reason: "force_abandoned",
487
- counterparty_notified: told,
488
- counterparty_notice_reason: notice.reason,
489
- guidance: `Session ${sessionId} was force-abandoned — marked terminal locally with no bilateral seal. Use force only for a half-open session that cannot be sealed; a normal close (no force) still attempts the seal so both parties get a notarized receipt.` +
490
- (told
491
- ? ` The notice was sent — a counterparty running a current client will stop calling this session. There is no acknowledgement for it, so if connection attempts keep arriving they are on an older build that does not understand it.`
492
- : notice.reason === "no_local_node"
493
- ? ` They were NOT told: this side had already torn the session down, so there was nothing to send the notice on. Their half stays open and they may go on retrying delivery and re-dialling until they give up. If connection attempts keep arriving from them, that is why — it is not a network fault.`
494
- : ` They were NOT told (${notice.reason}), so their half stays open: they may go on retrying delivery and re-dialling this session until they give up. If connection attempts keep arriving from them, that is why.`) +
495
- (mayHaveSealed
496
- ? ` This session had reached a seal attempt (prior status: ${record.status}), so if the counterparty did notarize it, this side's half is now PERMANENTLY forfeited and cannot be recovered from here. Their copy, if it exists, is the only remaining one — ask them for their sealed_root and record it with the operator.`
497
- : ""),
498
- };
499
- }
500
- // M12-P14: do not ask for a seal we already know cannot be granted.
501
- //
502
- // Placed AFTER the force branch on purpose — force is the operator's deliberate escape hatch and
503
- // must never be gated — and before every seal path, because both the interrupted and the active
504
- // flows sign this side's frontier.
505
- //
506
- // A short chain produces `leaf_count_mismatch` at the counterparty, which is correct and
507
- // TERMINAL: nothing in the protocol backfills a missing leaf, so the session's only remaining
508
- // exit is a force-abandon with no notarized receipt. Refusing here costs the operator a retry;
509
- // not refusing costs them the receipt permanently.
510
- // Review HIGH-3: scoped to the two statuses that can actually seal. Unconditional, it also fired
511
- // for `seal_interrupted_pending` — displacing that status's accurate answer ("awaiting FROST
512
- // notarization") with `session_incomplete`, and offering force:true against a seal that is
513
- // mid-notarization. That is the same error substitution e3da3b4 exists to remove, reintroduced
514
- // one branch earlier in the same handler. (markInterruptedWithDetails does not evict the caches,
515
- // so the signals survive into that status and the branch was genuinely reachable.)
516
- const sealable = record.status === "active" || record.status === "interrupted";
517
- let readiness = sealable
518
- ? sessionNodeManager.sealReadiness(record.agent_name, sessionId)
519
- // DOD-M15-DIVERGE-1: `diverged: false` for the same reason every other field here is a
520
- // no-op value — this branch is the NOT-sealable statuses, where readiness is never consulted
521
- // and both gates below are skipped. It asserts nothing about the record; it keeps the shape.
522
- // DOD-M15-SEALPRECOND-1: `ownLeavesOrdered: 0` for the same reason `diverged` is false here —
523
- // this is the NOT-sealable branch, where readiness is never consulted and every gate below is
524
- // skipped. It keeps the shape and asserts nothing about the record.
525
- : { ready: true, treeSize: 0, highWaterSeq: -1, heldCount: 0, missingLeaves: 0, heldOwn: 0, heldReceived: 0, diverged: false, ownLeavesOrdered: 0 };
526
- // Review HIGH-1: the guidance used to promise "the daemon pulls missing content automatically"
527
- // while nothing on this path pulled anything — autoRecoverForAgent fires on signaling reconnect,
528
- // seal-upgrade and agent start, none of which a close triggers. So the operator waited for an
529
- // event that would never happen, retried, got the same refusal, and reached for force:true — the
530
- // terminal receipt-less outcome this gate exists to prevent. Drain FIRST, then judge, which is
531
- // the sequence seal-upgrade.ts already documents: "Recover content -> consult the gate -> REFUSE".
532
- // Review LOW-10: skip the drain for a diverged record. `diverged` is now a term in `ready`, so
533
- // without this a diverged session pays a relay round trip that cannot change the answer — the
534
- // gap the drain fills is not the condition that refused it.
535
- if (sealable && !readiness.ready && !readiness.diverged && recoverParkedContent) {
536
- try {
537
- await recoverParkedContent(record.agent_name, "close_readiness_gate");
538
- readiness = sessionNodeManager.sealReadiness(record.agent_name, sessionId);
539
- }
540
- catch (err) {
541
- // A drain failure is not a reason to block the close: re-judging on the pre-drain readiness
542
- // is the same answer we would have given anyway, and it is still the honest one.
543
- logger.warn("session.seal.readiness.drain.failed", {
544
- agentName: record.agent_name, sessionId,
545
- error: extractErrorMessage(err),
546
- });
547
- }
548
- }
549
- /**
550
- * DOD-M15-SEALPRECOND-1 — WAIT FOR THIS SIDE'S OWN RECORD TO STOP MOVING, then judge.
551
- *
552
- * After the drain, before the two refusals: it is the one condition here that clears on its own
553
- * in milliseconds — an in-flight send the relay has ordered whose leaf this tree has not
554
- * written. Signing inside that span produced the 2026-09-11 loss: a two-leaf root against a
555
- * three-leaf relay set, refused by both directories, no second chance for either party.
556
- *
557
- * NOT for a diverged record — divergence never resolves, so waiting would delay a permanent
558
- * answer to report a transient one, the substitution DOD-M15-DIVERGE-1 exists to prevent.
559
- */
560
- if (sealable && !readiness.diverged && readiness.ownLeavesOrdered > 0) {
561
- const settle = await awaitOwnRecordSettled(sessionNodeManager, record.agent_name, sessionId);
562
- readiness = sessionNodeManager.sealReadiness(record.agent_name, sessionId);
563
- if (!settle.settled) {
564
- logger.warn("session.seal.blocked_settling", {
565
- agentName: record.agent_name, sessionId,
566
- treeSize: readiness.treeSize, highWaterSeq: readiness.highWaterSeq,
567
- ownLeavesOrdered: settle.ownLeavesOrdered,
568
- waitedMs: settle.waitedMs,
569
- impact: "a message of this operator's own has a place in the relay's ordering that this side's record has not taken yet, so NOTHING was signed — a root signed short of the relay's leaf set is refused by the directory and the receipt cannot be recovered",
570
- });
571
- return {
572
- ok: false,
573
- reason: "session_record_settling",
574
- own_leaves_settling: settle.ownLeavesOrdered,
575
- waited_ms: settle.waitedMs,
576
- tree_size: readiness.treeSize,
577
- // NAMES THIS SIDE, and only this side (065-SEALREASON). The counterparty has done nothing
578
- // and is owed no part of this sentence; `session_incomplete` next door says the opposite
579
- // thing — that they have not sent something — and borrowing its words here would send two
580
- // operators to argue about a condition neither of them caused.
581
- guidance: `Nothing was signed. ${settle.ownLeavesOrdered} message(s) you sent have a place in the record that this side has not written yet, and a receipt signed without them is refused and cannot be recovered. ` +
582
- `This is your own record catching up — the counterparty is not involved and there is nothing for them to do. ` +
583
- `It normally settles in well under a second, so close again: cello_close_session ${sessionId}. ` +
584
- `If it keeps returning this, cello_status shows the session as still settling and cello_transcript ${sessionId} shows what is already in the record.`,
585
- };
586
- }
587
- logger.info("session.seal.settled", {
588
- agentName: record.agent_name, sessionId, waitedMs: settle.waitedMs,
589
- });
590
- }
591
- /**
592
- * DOD-M15-DIVERGE-1 — a PERMANENT parting gets its own answer, before the transient one.
593
- *
594
- * `#diverged` is set when an ack came back behind this side's frontier, which proves the tree
595
- * and the relay's counter can never agree on a root again. It was detected correctly, logged at
596
- * ERROR, and reached exactly one consumer: the text `cello status` prints. This gate — the one
597
- * place the fact changes an outcome — could not see it, so the operator learned only when the
598
- * counterparty answered `leaf_count_mismatch`, by which point the receipt was gone.
599
- *
600
- * BRANCHED, NOT FOLDED INTO `session_incomplete`, and that is the whole point of the ordering.
601
- * The refusal below tells the operator to "wait a moment and close again" and says the daemon
602
- * just pulled from the relay — true and useful for a session waiting on arrival, and false for
603
- * this one. Nothing backfills a position the relay already assigned to something else, so a
604
- * retry loop on that guidance ends where every one of them ends: `force: true`, terminal, no
605
- * receipt. Substituting a transient explanation for a permanent condition is the error class
606
- * this whole milestone exists to remove; doing it inside its own fix would be the worst place.
607
- *
608
- * The ERROR log at the detection site STAYS. This is the second half of it, not a relocation:
609
- * the log is the forensic record and this is the control. (M15-PROCEDURE §2b, Invariant 2.)
610
- */
611
- if (sealable && readiness.diverged) {
612
- logger.warn("session.seal.blocked_diverged", {
613
- agentName: record.agent_name, sessionId,
614
- treeSize: readiness.treeSize, highWaterSeq: readiness.highWaterSeq,
615
- // Review MEDIUM-5: the same four counters the sibling refusal carries. Without them a
616
- // session that is BOTH diverged and gapped reported its gap on no surface at all — this
617
- // branch preempts the incomplete one, and `sealReadinessView` short-circuits to `unknown`.
618
- heldCount: readiness.heldCount, missingLeaves: readiness.missingLeaves,
619
- heldOwn: readiness.heldOwn, heldReceived: readiness.heldReceived,
620
- impact: "this side's tree holds a leaf at a position the relay assigned to something else; the relay's ordering is what gets notarized, so a bilateral seal may be refused and this side cannot tell in advance",
621
- });
622
- return {
623
- ok: false,
624
- reason: "session_record_diverged",
625
- tree_size: readiness.treeSize,
626
- relay_high_water: readiness.highWaterSeq,
627
- held_messages: readiness.heldCount,
628
- missing_leaves: readiness.missingLeaves,
629
- held_own: readiness.heldOwn,
630
- held_received: readiness.heldReceived,
631
- // Review HIGH-1: SAY WHAT WAS MEASURED, NOT WHAT IS PREDICTED. The earlier wording asserted
632
- // the counterparty would refuse with `leaf_count_mismatch` and that force was the only exit.
633
- // Neither is established here. What is measured is that THIS tree parted from THE RELAY's
634
- // counter. The bilateral check compares LEAF COUNT, not the root (`seal-flows.ts`:
635
- // "Merkle-root agreement is NOT verified at this leaf-exchange layer"), and both sides
636
- // append a behind-frontier leaf at the tail — so if the counterparty skewed the same way
637
- // the counts still agree and the seal can succeed. Predicting their refusal was an
638
- // over-claim, and offering force-abandon as "the only exit" pointed at the one irreversible
639
- // action while the codebase's own mismatch handling leaves the session retryable.
640
- guidance: `This side's record no longer agrees with the relay's ordering — your tree holds ${readiness.treeSize} message(s) and the relay's counter is at ${readiness.highWaterSeq + 1}. ` +
641
- `The relay's ordering is what gets notarized, so sealing now may be refused. It may also succeed: the bilateral check compares message COUNTS, not contents, so if the counterparty's record skewed the same way the counts still match. This side cannot tell which from here. ` +
642
- `What is certain is that this will not resolve on its own — nothing backfills or re-numbers a leaf, so waiting does not change it. ` +
643
- `Compare message counts with the counterparty before deciding; cello_transcript ${sessionId} shows your full record and it stays readable whatever you choose. ` +
644
- `cello_close_session ${sessionId} { force: true } ends the session with NO notarized receipt and cannot be undone — the last resort, not the next step.`,
645
- };
646
- }
647
- if (sealable && !readiness.ready) {
648
- logger.warn("session.seal.blocked_incomplete", {
649
- agentName: record.agent_name, sessionId,
650
- treeSize: readiness.treeSize, highWaterSeq: readiness.highWaterSeq,
651
- heldCount: readiness.heldCount, missingLeaves: readiness.missingLeaves,
652
- heldOwn: readiness.heldOwn, heldReceived: readiness.heldReceived,
653
- });
654
- // DOD-M12B-INDEX-1: SAY WHOSE MESSAGES ARE WAITING. Our own held sends block the seal exactly
655
- // as received ones do, but calling them "received message(s) waiting behind a gap" tells the
656
- // operator to wait for content that is already in hand — and the relay pull this gate performs
657
- // first can never resolve it. They retry, get the identical refusal, and reach for force:true:
658
- // the receipt-less exit this gate exists to prevent.
659
- const waiting = (readiness.heldReceived > 0 ? `, and ${readiness.heldReceived} received message(s) are waiting behind a gap` : "") +
660
- (readiness.heldOwn > 0 ? `, and ${readiness.heldOwn} of YOUR OWN message(s) are waiting for their place in the record (they were delivered — the counterparty has them)` : "");
661
- return {
662
- ok: false,
663
- reason: "session_incomplete",
664
- missing_leaves: readiness.missingLeaves,
665
- held_messages: readiness.heldCount,
666
- held_own: readiness.heldOwn,
667
- held_received: readiness.heldReceived,
668
- guidance: `This side of the conversation is incomplete, so sealing now would produce a chain the counterparty cannot co-sign — and that refusal is terminal, leaving a force-abandon with no notarized receipt as the only way out. ` +
669
- `You hold ${readiness.treeSize} message(s); the relay has witnessed ${readiness.highWaterSeq + 1}` +
670
- waiting +
671
- `. Everything here is waiting on an earlier message from the counterparty that has not arrived; the daemon just pulled from the relay and the gap is still there, so wait a moment and close again. If it does not resolve, cello_transcript ${sessionId} shows what did arrive, and cello_close_session ${sessionId} { force: true } abandons it terminally (no receipt).`,
672
- };
673
- }
674
- /**
675
- * AC-011: a seal attempt is already running for this session.
676
- *
677
- * DOD-M15-CLOSEWAIT-1 review HIGH-3 rewrote this, because the change made it COMMON and the old
678
- * wording did not survive contact:
679
- *
680
- * - it said *"wait for `session.interrupted.sealed` to appear in the daemon logs"*. That event
681
- * is emitted NOWHERE in the tree — grep finds it only inside this string. An operator would
682
- * tail a log for a line that cannot arrive.
683
- * - it named the seal-INTERRUPTED subsystem, but the common case now is an ACTIVE session
684
- * whose background ceremony is mid-flight. Wrong subsystem, on the path an operator most
685
- * often reaches.
686
- * - it was hard to reach before, because the first close held the caller for the whole
687
- * ceremony. Now the operator has their terminal back for up to eleven minutes, and
688
- * re-closing is the obvious move.
689
- */
690
- if (sealInterruptedInProgress.has(sealKey(record.agent_name, sessionId))) {
691
- return {
692
- ok: false,
693
- reason: "seal_in_progress",
694
- seal_status: "committed",
695
- guidance: "A seal ceremony is ALREADY RUNNING for this session — your commitment is recorded and " +
696
- "there is nothing to retry. This is the normal state after a close: the close answers as " +
697
- "soon as the commitment is durable and notarizes in the background, which waits for the " +
698
- "counterparty and can take several minutes. Fetch the result with cello_sealed_receipt " +
699
- `(session_id ${sessionId}); a seal_in_progress answer there means the same thing — still ` +
700
- "running, not failed. Do NOT re-close with { force: true } to hurry it: forcing ABANDONS " +
701
- "the session and permanently forfeits the receipt this ceremony is about to produce.",
702
- };
703
- }
704
- // DB-001: signaling stream reconnecting
705
- if (record.status === "interrupted" && signalingFor(record.agent_name)?.status === "reconnecting") {
706
- return {
707
- ok: false,
708
- reason: "signaling_reconnecting",
709
- guidance: "The directory signaling stream is reconnecting. Wait for directory_signaling to show connected in cello status before initiating seal-interrupted. The daemon reconnects automatically — no manual intervention required.",
710
- };
711
- }
712
- // AC-012 / AC-013: seal-interrupted bilateral flow for interrupted sessions.
713
- // BLOCKING-1 fix: await the flow synchronously so the caller receives the real result
714
- // (counterparty_unavailable, rejected_by_counterparty, or sealed).
715
- // The sealInterruptedInProgress Set still guards concurrent calls (AC-011).
716
- if (record.status === "interrupted") {
717
- // H-1: the Merkle root at interruption is held by the client (the daemon
718
- // does not maintain the session Merkle tree). The client supplies it here
719
- // so both parties co-sign over the same root. Absent → empty string, in
720
- // which case the bilateral commitment binds leafCount only.
721
- const merkleRootAtInterruption = typeof params?.merkleRootAtInterruption === "string" ? params.merkleRootAtInterruption : "";
722
- sealInterruptedInProgress.add(sealKey(record.agent_name, sessionId));
723
- const correlationId = randomUUID();
724
- // CROSS-NODE: dial the broker for the duration of the seal, exactly as the active path does.
725
- // This branch sends the same seal frames and used to send them without the dial, so an
726
- // interrupted close between two agents on different nodes timed out on BOTH sides while each
727
- // counterparty was online — the frames were pushed to a stream the broker did not hold.
728
- let sealBrokerConn = null;
729
- // THE BILATERAL COMMITMENT IS NOT THE SEAL.
730
- //
731
- // Exactly one thing in the system causes a notarization: a SEAL ctrl leaf posted to the
732
- // RELAY, which hands the chain to a directory for the FROST round once both sides have
733
- // posted. The ACTIVE close does that below. This branch never did — it exchanged signed
734
- // leaves with the counterparty over the directory's signaling pass-through, stored both
735
- // halves, set the session to `seal_interrupted_pending` and returned. The directory only
736
- // FORWARDS those frames; it reads nothing out of them and starts nothing on the back of them.
737
- // So an interrupted session reached a mutually signed record that nobody was ever asked to
738
- // notarize, and sat there until the relay swept it.
739
- //
740
- // BEST-EFFORT, deliberately. The commitment has already SUCCEEDED and is durable on both
741
- // sides. The relay drops a session 24 hours after its last message, so this call will often
742
- // arrive to find it gone — and turning that into a reported FAILURE is precisely what sends an
743
- // operator to force:true, which permanently forfeits the half they still hold. A missing
744
- // notarization is a lesser harm than a destroyed commitment, so the close keeps its result and
745
- // the shortfall is logged rather than raised.
746
- //
747
- // No waiter is registered and nothing is awaited: the counterparty may not close for hours, and
748
- // blocking a close on that would be a worse lie than returning the honest pending status. When
749
- // their leaf does land, the already-wired session_sealed listener records the certificate.
750
- //
751
- // DOD-M12B-INTERRUPTED-ESCALATE-1: submitting the leaf was never enough, and this is where
752
- // the receipt actually gets earned. The relay notarizes only once BOTH parties have posted a
753
- // SEAL ctrl leaf, and the responder never posts one — `inbound-seal-request.ts` persists its
754
- // commitment, acks, and stops. So waiting for the relay round here is waiting for something
755
- // that cannot happen. The escalation asks the directory to notarize with the counterparty
756
- // ABSENT, which is precisely what a unilateral seal is for.
757
- const notarize = async (result) => {
758
- // WHEN IT IS ALLOWED TO ESCALATE, and this is a trust decision, not a retry policy:
759
- // result.ok — both sides signed the same root. Agreed.
760
- // seal_interrupted_counterparty_unavailable — they never answered. The exact case the
761
- // unilateral seal exists for.
762
- // NEVER after `seal_interrupted_rejected_by_counterparty`. A rejection means the two trees
763
- // DISAGREE (leaf_count_mismatch, a diverging index). Notarizing our own root over their
764
- // stated objection is the one thing a trust layer must not do, however stuck the session is.
765
- const mayEscalate = result.ok || result.reason === "seal_interrupted_counterparty_unavailable";
766
- if (!mayEscalate)
767
- return result;
768
- // DOD-M12B-SEAL-BILATERAL-FIRST-1 — a BILATERAL seal is already running; do not take the
769
- // worse artifact instead. The counterparty answered that it is on the relay's bilateral
770
- // ceremony and was waiting for our half, which the flow just submitted. Escalating now asks
771
- // the directory to notarize with that counterparty marked ABSENT — for a peer that is
772
- // demonstrably present. The ACTIVE close gives a bilateral round eleven minutes before it
773
- // escalates; this path was giving it none, and for any orphan older than the delivery grace
774
- // (every one of the measured 26) the escalation is allowed, so the downgrade was certain.
775
- if (result.ok && result.ceremony === "relay_bilateral") {
776
- logger.info("session.seal.bilateral.in_flight", {
777
- agentName: record.agent_name, sessionId, correlationId,
778
- impact: "our half is submitted and the relay has both parties' leaves; the bilateral receipt is the better artifact and is already coming",
779
- });
780
- return {
781
- ...result,
782
- seal_receipt: "bilateral_in_flight",
783
- guidance: "The counterparty is running the relay's bilateral seal and was waiting for this side's half, which has now been submitted. That produces a BILATERAL receipt — a better artifact than the unilateral one a close would otherwise take, and it does not record the counterparty as absent. Watch for the seal to land (cello_sessions) and read it with cello_sealed_receipt.",
784
- };
785
- }
786
- const uni = await submitAndEscalate(record, sessionId, correlationId);
787
- if ("escalated" in uni) {
788
- // THE REASON IS IN HAND — do not drop it. `result.ok` is true here (the bilateral
789
- // commitment succeeded), so returning it bare makes the restart-seal resolver log
790
- // `resolved` and dequeue a session that got NO notarization: the system reporting health
791
- // it does not have. Same shape the pending branch was blocked for.
792
- if (!result.ok)
793
- return result;
794
- return {
795
- ...result,
796
- seal_receipt: "outstanding",
797
- seal_pending_reason: uni.reason,
798
- guidance: `The bilateral commitment is recorded, but this side could not post the SEAL leaf a notarization is requested with: ${uni.reason}. ` +
799
- sealSubmitCause(uni.reason),
800
- };
801
- }
802
- if (uni.ok)
803
- return uni; // A REAL RECEIPT — the whole point of this line.
804
- // BEST-EFFORT, and the commitment STANDS. Turning a successful commitment into a reported
805
- // failure is exactly what sends an operator to `force: true`, which permanently forfeits the
806
- // half they still hold — the comment above says so and it is still true. What changes is
807
- // that the answer now says a receipt is OUTSTANDING rather than implying one exists, and
808
- // carries the directory's own countdown so a caller that can wait does not have to guess.
809
- logger.info("session.interrupted.seal.escalation.pending", {
810
- agentName: record.agent_name, sessionId, reason: uni.reason, correlationId,
811
- ...(uni.retry_after_seconds !== undefined ? { retryAfterSeconds: uni.retry_after_seconds } : {}),
812
- });
813
- return {
814
- ...result,
815
- seal_receipt: "outstanding",
816
- seal_pending_reason: uni.reason,
817
- ...(uni.retry_after_seconds !== undefined ? { retry_after_seconds: uni.retry_after_seconds } : {}),
818
- guidance: uni.guidance,
819
- };
820
- };
821
- try {
822
- // Pre-dial ONLY from memory. This costs nothing when the map is empty, so the seal waiter
823
- // still registers immediately — a bilateral seal that lands promptly must not be missed
824
- // because we were off looking something up.
825
- sealBrokerConn = await openSealBrokerConnection(record.agent_name, sessionId, correlationId);
826
- const first = await handleSealInterruptedFlow(sessionId, record, correlationId, merkleRootAtInterruption);
827
- // DISCOVER-AND-RETRY, and only on the one reason that discovery can actually fix. After a
828
- // restart the broker map is empty, so the attempt above went out on the home stream and the
829
- // brokering node had no stream for the counterparty to push to. Asking where they are NOW
830
- // and dialling there is the repair — paid for only on the path that just failed, never on
831
- // the happy path, and never delaying the waiter.
832
- const counterparty = record.counterparty_pubkey;
833
- if (first.ok || first.reason !== "seal_interrupted_counterparty_unavailable" || !counterparty || sealBrokerConn) {
834
- return await notarize(first);
835
- }
836
- sealBrokerConn = await openSealBrokerConnection(record.agent_name, sessionId, correlationId, { counterpartyPubkeyHex: counterparty });
837
- // Escalate even here: discovery found nowhere to dial, which is the STRONGEST case for a
838
- // unilateral seal, not a reason to give up on the receipt.
839
- if (!sealBrokerConn)
840
- return await notarize(first);
841
- logger.info("session.seal.broker.retry_after_discovery", { agentName: record.agent_name, sessionId, correlationId });
842
- // OVER THE VISITING CONNECTION. Dialling their node is only half of it — the request must
843
- // be SENT there too. Sent on the home stream it reaches our own node, which holds no stream
844
- // for a counterparty homed elsewhere, logs "target offline" and answers nothing.
845
- return await notarize(await handleSealInterruptedFlow(sessionId, record, correlationId, merkleRootAtInterruption, sealBrokerConn.mgr));
846
- }
847
- finally {
848
- // Release the transient connection; the seal result stands either way.
849
- if (sealBrokerConn) {
850
- try {
851
- await sealBrokerConn.stop("seal-complete");
852
- }
853
- catch (err) {
854
- logger.warn("session.seal.broker.release_failed", { sessionId, reason: extractErrorMessage(err) });
855
- }
856
- }
857
- sealInterruptedInProgress.delete(sealKey(record.agent_name, sessionId));
858
- }
859
- }
860
- // CELLO-M7-DAEMON-004 (AC-003): ACTIVE session — initiate the active-session
861
- // seal over the daemon's OWN tree root. SI-001: any caller-supplied
862
- // merkleRoot is IGNORED; the daemon signs only the root it built itself.
863
- //
864
- // round-2 finding #4: the active path must take the SAME concurrency guard as
865
- // the interrupted path (the top-of-handler check at sealInterruptedInProgress.has
866
- // rejects re-entry). Without adding to the set here, two concurrent active closes
867
- // would both send a seal_interrupted_request and both await acks (double seal).
868
- if (record.status === "active") {
869
- sealInterruptedInProgress.add(sealKey(record.agent_name, sessionId));
870
- const correlationId = randomUUID();
871
- // Fix #1 (cross-node seal-liveness): if this session was brokered by another node, the seal_verified
872
- // + session_sealed frames are pushed by that BROKER — but the initiator released its visiting
873
- // connection after setup, so on the home stream they never arrive and close times out. Re-open a
874
- // transient visiting connection to the broker (seal-capable — openVisitingConnection now wires the
875
- // seal handlers) for the duration of the seal, then release it in finally. Same-node sessions have
876
- // no entry here and use the home stream unchanged.
877
- let sealBrokerConn = null;
878
- // DOD-M15-CLOSEWAIT-1: set when the seal tail is handed to a background task, so the enclosing
879
- // finally does not release a broker connection that ceremony still needs.
880
- let handedOff = false;
881
- /**
882
- * The caller may still ASK to block. Default is the new contract (answer on commitment);
883
- * `wait_for_seal: true` restores the inline behaviour for a script that genuinely wants the
884
- * receipt in one call and will wait up to eleven minutes for it. Opt-IN rather than opt-out,
885
- * because the default has to be the one that does not freeze an interactive operator.
886
- */
887
- const waitForSeal = params?.["wait_for_seal"] === true;
888
- try {
889
- // Memory only, deliberately. An ACTIVE session has not been through the restart that
890
- // empties the broker map, so the entry is there when it is needed — and a lookup here would
891
- // sit in front of the waiter registration two lines below, which is what must not happen.
892
- sealBrokerConn = await openSealBrokerConnection(record.agent_name, sessionId, correlationId);
893
- // M7 DOD-SPINE-7: relay-mediated bilateral seal. Submit our SEAL ctrl leaf to the
894
- // relay witness; when the counterparty ALSO closes, the relay's #maybeProcessSeal
895
- // fires → directory processSeal rebuilds + verifies the signed chain → FROST
896
- // notarization → session_sealed to BOTH parties. Register the waiter BEFORE
897
- // submitting so the notification can never race ahead of us.
898
- let resolveSeal;
899
- const sealedP = new Promise((r) => { resolveSeal = r; });
900
- pendingSealWaiters.set(sealKey(record.agent_name, sessionId), resolveSeal);
901
- const submit = await sessionNodeManager.submitSealLeaf(record.agent_name, sessionId, correlationId);
902
- // M7-UPGRADE-002: the auto-acknowledge path may have already submitted THIS party's
903
- // responder SEAL leaf (it won the race against this explicit close). That is success, not
904
- // failure — keep the waiter registered and fall through to await session_sealed (the
905
- // auto-ack's submission drives the same bilateral seal).
906
- if (!submit.ok && submit.reason !== "responder_seal_already_submitted") {
907
- pendingSealWaiters.delete(sealKey(record.agent_name, sessionId));
908
- // THE SEAL MAY HAVE ALREADY SUCCEEDED, so returning the raw reason is an exit-point label
909
- // with the wrong diagnosis attached: the guidance below blames relay reachability when the
910
- // relay was fine and the seal is notarized and DURABLE. The operator who asked to close is
911
- // told it failed and gets no root — the receipt being the whole point of closing.
912
- //
913
- // HOW the window opens (corrected — my first version named a mechanism that does not
914
- // exist): it is NOT that teardown precedes the status flip. In `destroySessionNode` the
915
- // flip happens BEFORE `#activeNodes.delete`, so that ordering is safe. The real producers
916
- // are (a) `record` is a snapshot taken at the top of this handler, before a broker dial that
917
- // can take up to 10s, so it can be stale by now; and (b) `retireSessionNode` stops the node
918
- // WITHOUT changing the DB status, and `destroySessionNode` then early-returns above the
919
- // flip. Both parties closing at once is the ordinary case, so whichever call arrives second
920
- // meets it.
921
- //
922
- // SCOPED to that reason. This previously fired on EVERY `!submit.ok`, while justifying
923
- // itself for one — and a future failure (a tree mismatch, a signing failure) would have been
924
- // absorbed into `ok:true` with an old root and never surfaced.
925
- //
926
- // Why this is not the fetched-receipt path wearing a different hat — the distinction is
927
- // narrower than I first wrote, so stated exactly: the stored cert passed the
928
- // field-completeness gate, passed the independent frontier re-derivation (the client never
929
- // takes the directory's word for that value), and where this agent holds the signer key it
930
- // was cryptographically verified. None of those were true of a fetched root. It is also not
931
- // a new claim — `cello_get_sealed_receipt` already returns this exact cert with no status
932
- // gate. Honest caveat: for a NON-initiator the stored cert is recorded `verified:false`,
933
- // so it is ultimately directory-attested over an authenticated channel — the same trust the
934
- // ordinary bilateral close already returns, not a new one introduced here.
935
- // M12-P15 (review HIGH-3): this used to key on `session_node_unavailable` ALONE. That was
936
- // the only reason submitSealLeaf could give when the node was gone — until M12-P15 taught
937
- // it to fall back to a detached transport, after which it can no longer produce that
938
- // string at all and this recovery went DEAD. The ordinary double-close then dialled the
939
- // relay, submitted a second ctrl leaf to a session the relay had already destroyed on
940
- // seal, and told the operator the close failed — the M12 defect back under a new label.
941
- // The question this branch actually asks is "we could not submit; is the seal already
942
- // durable?", so it keys on every reason that means exactly that. A stored certificate is
943
- // the answer either way, and consulting it is cheap.
944
- const SEAL_MAY_ALREADY_BE_DURABLE = new Set([
945
- "session_node_unavailable",
946
- "no_persisted_relay_endpoint",
947
- "standing_receiver_unavailable",
948
- "relay_client_unavailable",
949
- "relay_unavailable",
950
- "relay_session_gone",
951
- "session_not_found",
952
- /**
953
- * `DOD-M15-TOKENSTALE-1` review F1 — these arrive here ONLY since that unit split the
954
- * submit boundary's answer. They used to reach this line as `relay_unavailable` and were
955
- * covered by the entry above; naming them keeps this set answering the question it says
956
- * it answers ("could the seal already be durable?") rather than the narrower one the
957
- * old label happened to encode.
958
- */
959
- "online_token_expired",
960
- "online_token_required",
961
- "online_token_pubkey_mismatch",
962
- ]);
963
- const localCert = SEAL_MAY_ALREADY_BE_DURABLE.has(submit.reason)
964
- ? sessionNodeManager.getSealCertificate(record.agent_name, sessionId)
965
- : null;
966
- if (localCert?.sealed_root) {
967
- logger.info("session.seal.completed", {
968
- sessionId, sealedRoot: localCert.sealed_root, role: "already_sealed_locally", correlationId,
969
- });
970
- crossNodeBrokerBySession.delete(`${record.agent_name}:${sessionId}`);
971
- return { ok: true, sealed_root: localCert.sealed_root, legibility: localCert.legibility };
972
- }
973
- /**
974
- * ⚠️ THE QUESTION IS "COULD WE NOT USE THE RELAY?", NOT "IS THE RELAY DOWN?" —
975
- * `DOD-M15-TOKENSTALE-1` review F1.
976
- *
977
- * This read `submit.reason === "relay_unavailable"` while that string was the only way a
978
- * relay-less submit could be reported. Splitting the boundary so a dead credential names
979
- * itself broke that equivalence, and the consequence was the opposite of the unit's
980
- * intent: an agent with an expired token has BY DEFINITION no relay witness, which is
981
- * precisely the state this fallback exists for — and it would have been the first to stop
982
- * taking it, told instead to "retry once the relay is reachable" about a relay that was
983
- * never the problem.
984
- */
985
- if (submit.reason === "relay_unavailable" || isLocalCredentialRefusal(submit.reason)) {
986
- // No relay witness for this session (direct/interrupted, or our own credential is dead)
987
- // — fall back to the directory-mediated bilateral-ack seal.
988
- return await handleActiveSealFlow(sessionId, record, correlationId);
989
- }
990
- return {
991
- ok: false,
992
- reason: submit.reason,
993
- guidance: "The SEAL leaf could not be submitted to the relay witness. Retry once the relay is reachable (cello status).",
994
- };
995
- }
996
- // Both parties must close for the directory to notarize. Await session_sealed; a
997
- // timeout means the counterparty has not closed yet (our leaf is recorded — the
998
- // session seals when they call cello_close_session). CELLO_SEAL_BILATERAL_TIMEOUT_MS
999
- // tunes how long to wait for the counterparty before escalating to a unilateral seal.
1000
- // DOD-SEAL-BILATERAL-TIMEOUT-1: default is 660 s (11 min) — deliberately just over
1001
- // the directory's deliveryGraceSeconds default (600 s / 10 min), so the bilateral
1002
- // timeout always expires AFTER the grace window; override via the env var for tests or
1003
- // operators who need a shorter window.
1004
- //
1005
- // ⚠️ THIS USED TO SAY seal_unilateral_too_early WAS "STRUCTURALLY UNREACHABLE", AND
1006
- // 070-CARRIEDSEAL MADE THAT FALSE. Rewritten rather than deleted, because a reader who
1007
- // believed it would be surprised by the refusal and go looking for a bug. The zero below
1008
- // skips the window entirely, so a session closed with no relay AND less than the grace
1009
- // period of age now reaches that refusal — which is correct (the directory's floor is a
1010
- // real floor) and is loud, with its own guidance. It is no longer unreachable; it is
1011
- // reachable exactly when there was never a ceremony to wait out.
1012
- /**
1013
- * ⚠️ 070-CARRIEDSEAL — THERE IS NOTHING TO WAIT FOR WHEN NO RELAY TOOK THE LEAF.
1014
- *
1015
- * A bilateral seal is a ceremony the RELAY runs: it watches its own leaf log for both
1016
- * parties' control leaves and drives the directory itself. If no relay answered, our leaf
1017
- * is not in any relay's log, the counterparty's closing leaf can never reach us, and the
1018
- * ceremony cannot begin — so the eleven-minute window is eleven minutes of an operator
1019
- * watching nothing happen before the solo seal that was always the only available outcome.
1020
- *
1021
- * It is also the last live-relay requirement on the close path. The evidence stopped needing
1022
- * the relay when the leaf was signed here; the WAIT still assumed one, and a wait for
1023
- * something that cannot happen is a dependency wearing a stopwatch.
1024
- *
1025
- * Zero, not "shorter": this is not a tuning judgement about how long a counterparty might
1026
- * take. It is the absence of a counterparty channel.
1027
- */
1028
- const bilateralTimeoutMs = submit.viaLocalTerminus === true
1029
- ? 0
1030
- : Number(process.env["CELLO_SEAL_BILATERAL_TIMEOUT_MS"]) || 660_000;
1031
- // DOD-M12B-CLOSE-SILENT-WAIT-1: SAY SO BEFORE THE SILENCE, not after it.
1032
- //
1033
- // This call is about to block for up to eleven minutes and return nothing. Measured
1034
- // 2026-08-17: seal leaf submitted 16:48:55, ceremony completed 17:00:01 — and the operator
1035
- // saw a frozen command for all of it. They concluded it was broken and force-abandoned
1036
- // seventeen sessions, which forfeits the exact receipt this wait is earning. The log is what
1037
- // a second window can read while the first is blocked, so it has to carry both facts: how
1038
- // long this can legitimately take, and what forcing costs.
1039
- logger.warn("session.seal.awaiting_counterparty", {
1040
- sessionId, agentName: record.agent_name, deadlineMs: bilateralTimeoutMs, correlationId,
1041
- impact: bilateralTimeoutMs === 0
1042
- ? "no relay took this side's closing leaf, so no bilateral ceremony can begin and there is nothing to wait for. The seal goes straight to the solo path over the evidence this side already holds, and produces a real receipt. Do NOT force-abandon it — that forfeits the receipt."
1043
- : `the seal is waiting for the counterparty for up to ${Math.round(bilateralTimeoutMs / 60_000)} minutes, then escalates to a unilateral seal and produces a real receipt. It is working. Do NOT force-abandon it — that forfeits the receipt this wait is earning.`,
1044
- });
1045
- /**
1046
- * DOD-M15-CLOSEWAIT-1 — THE TAIL, EXTRACTED SO IT CAN OUTLIVE THE RESPONSE.
1047
- *
1048
- * Everything from here to the escalation is "wait for the counterparty, then escalate if
1049
- * they never came". It ran inline, which is why the caller sat on a frozen command for up to
1050
- * eleven minutes while it worked correctly — and why one operator force-abandoned seventeen
1051
- * sessions, forfeiting the exact receipts the wait was earning.
1052
- *
1053
- * Extracting it changes nothing about WHAT is signed or in what order: same race, same
1054
- * escalation, same leaf. It changes only who waits for it. (Decisions Carried #4.)
1055
- *
1056
- * `rec`/`sid` are captured because TypeScript discards the narrowing of `record` and
1057
- * `sessionId` inside a nested function.
1058
- */
1059
- const rec = record;
1060
- const sid = sessionId;
1061
- const awaitSealAndEscalate = async () => {
1062
- let timer;
1063
- const timeoutP = new Promise((r) => { timer = setTimeout(() => r(null), bilateralTimeoutMs); });
1064
- const sealedCompletion = await Promise.race([sealedP, timeoutP]);
1065
- clearTimeout(timer);
1066
- pendingSealWaiters.delete(sealKey(record.agent_name, sessionId));
1067
- /**
1068
- * THE CERTIFICATE WAS REFUSED — `DOD-M15-SEALWIRE-1` bullet 2 (review F4).
1069
- *
1070
- * The seal arrived, its signature verified, and its root did not describe this conversation.
1071
- * Before this the coordinator logged and dropped the waiter, so control fell through to the
1072
- * eleven-minute timeout and the operator was told the counterparty had not closed — about a
1073
- * counterparty who had, and whose certificate had just been refused here.
1074
- *
1075
- * Answered immediately instead, with what was actually detected. NOT retryable, and it says
1076
- * so: `session_sealed` is delivered once, so a retry re-enters the same wait and then finds
1077
- * the relay session gone.
1078
- */
1079
- const refused = sealedCompletion !== null && "refused" in sealedCompletion ? sealedCompletion : null;
1080
- if (refused) {
1081
- logger.error("session.seal.refused", {
1082
- sessionId, agentName: record.agent_name, correlationId,
1083
- reason: refused.reason, detail: refused.detail, source: refused.source ?? "local",
1084
- });
1085
- }
1086
- /**
1087
- * 🚨 A DIRECTORY REFUSAL IS NOT THE END OF THE CLOSE — `DOD-M15-SEALPARTIES-1`, review F1.
1088
- *
1089
- * This branch was written for ONE producer: this daemon's own root check, where terminating
1090
- * is right because there is nothing further to ask for. `DOD-M15-SEALPARTIES-1` made the
1091
- * DIRECTORY a second producer of the same shape, and it silently inherited the terminal
1092
- * semantics — so every directory refusal (`seal_approval_missing`, `seal_leaves_invalid`,
1093
- * `merkle_root_mismatch`, …) stopped escalating.
1094
- *
1095
- * **What that cost a person:** before, a relay that dropped a leaf meant an eleven-minute
1096
- * wait and then a SOLO receipt. After, the refusal came back in seconds and the close ended
1097
- * — no receipt, in a case where one used to arrive. Faster and worse, which is the trap this
1098
- * order names, reached through the refusal listener rather than through the approval rule.
1099
- *
1100
- * `seal_leaves_invalid` is the sharpest of them: the relay tampered, and a solo seal over
1101
- * this side's OWN carried chain is precisely the remedy. Whether a PRESENT counterparty may
1102
- * be sealed around is the directory's call on the unilateral gate, not this daemon's — so it
1103
- * asks, rather than deciding not to.
1104
- */
1105
- if (refused && (refused.source ?? "local") === "local") {
1106
- return {
1107
- ok: false,
1108
- reason: refused.reason,
1109
- seal_status: "refused",
1110
- ...(refused.ownRootHex ? { own_root: refused.ownRootHex } : {}),
1111
- /**
1112
- * ⚠️ THE REMEDY COMES FROM THE PARTY THAT KNOWS THE CAUSE — `DOD-M15-SEALPARTIES-1`.
1113
- *
1114
- * The text below was written when this branch had exactly one producer: this daemon's
1115
- * own root check, where "the directory signed a root that is not your conversation" is
1116
- * always the right sentence. The directory's own refusals now land here too, and for
1117
- * those it would be a confident falsehood — it names a signature that was never
1118
- * produced. So a refusal that carries its own guidance keeps it, and the local
1119
- * root-mismatch text stays as the fallback for the case it was written for.
1120
- */
1121
- guidance: refused.guidance ??
1122
- "The directory returned a VALIDLY SIGNED seal whose root does not describe this " +
1123
- `conversation (${refused.detail}). This daemon refused it: the session is NOT ` +
1124
- "marked sealed, your transcript is untouched, and nothing was signed with your key. " +
1125
- "Do NOT retry the close — the seal notification is delivered once, so a retry waits the " +
1126
- "full window and then finds the relay session gone. Do NOT force-abandon either: that " +
1127
- "permanently forfeits any receipt. Compare the leaf count and the message list with your " +
1128
- "counterparty, and report the session id — this is a directory or relay fault, not " +
1129
- "something you did.",
1130
- };
1131
- }
1132
- const succeeded = sealedCompletion !== null && !("refused" in sealedCompletion) ? sealedCompletion : null;
1133
- if (succeeded) {
1134
- logger.info("session.seal.completed", { sessionId, sealedRoot: succeeded.rootHex, role: "bilateral", correlationId });
1135
- crossNodeBrokerBySession.delete(`${record.agent_name}:${sessionId}`); // Fix #1 review: evict on terminal seal success (a FAILED close keeps the entry so a retry can still reconnect).
1136
- // M7-SESSION-004 (AC-006): return the legibility certificate on the seal completion so
1137
- // a reader gets it on the same surface that proves the seal — per-party frontiers, attestation modes, and final_message.answered.
1138
- return { ok: true, sealed_root: succeeded.rootHex, legibility: succeeded.legibility };
1139
- }
1140
- // M8B FINDING-1: `responder_seal_already_submitted` has two producers — (a) the auto-ack
1141
- // path submitted our leaf because the COUNTERPARTY's SEAL arrived, or (b) our OWN earlier
1142
- // close submitted it and the counterparty never co-closed. The result now carries the
1143
- // first submit's reportedRootHex/sequenceNumber, so a retry close can still escalate to a
1144
- // unilateral seal (case b — the live-deadlock path). We deliberately do NOT distinguish
1145
- // (a) from (b) client-side: the bilateral wait above already gave case (a) its window, and
1146
- // the directory's grace/already-sealed gates arbitrate a redundant seal_unilateral — that
1147
- // is the sovereign-node-correct shape. Only when the not-ok result carries NO root (the
1148
- // first submit is still in flight) is "pending" the honest terminal answer here.
1149
- const escalation = submit.ok
1150
- ? { reportedRootHex: submit.reportedRootHex, sequenceNumber: submit.sequenceNumber }
1151
- : submit.reason === "responder_seal_already_submitted" &&
1152
- typeof submit.reportedRootHex === "string" &&
1153
- typeof submit.sequenceNumber === "number"
1154
- ? { reportedRootHex: submit.reportedRootHex, sequenceNumber: submit.sequenceNumber }
1155
- : null;
1156
- if (!escalation) {
1157
- /**
1158
- * ⚠️ A REFUSAL WITH NOWHERE TO ESCALATE MUST STILL NAME THE REFUSAL — Invariant 3.
1159
- *
1160
- * `seal_pending_bilateral` says "it did not finalize within the wait window", which for a
1161
- * refused seal is a downstream label pasted over an upstream cause the operator was told
1162
- * one line earlier in the log.
1163
- */
1164
- if (refused) {
1165
- return {
1166
- ok: false,
1167
- reason: refused.reason,
1168
- seal_status: "refused",
1169
- guidance: `${refused.guidance ?? refused.detail} There was no submitted SEAL leaf to escalate from, so no solo seal was attempted either — close again once the cause above is addressed.`,
1170
- };
1171
- }
1172
- return {
1173
- ok: false,
1174
- reason: "seal_pending_bilateral",
1175
- guidance: "Your SEAL leaf is recorded (auto-acknowledged) and the bilateral seal is completing, but it did not finalize within the wait window. Check cello status and the daemon logs; retry cello_close_session if the session remains unsealed.",
1176
- };
1177
- }
1178
- // SESSION-002 (DOD-SEAL): the counterparty did not co-close. Escalate to a UNILATERAL
1179
- // seal. The body now lives in escalateToUnilateralSeal so the INTERRUPTED branch can reach
1180
- // it too — it never could, and that is why an interrupted session could not get a receipt.
1181
- if (refused) {
1182
- logger.warn("session.seal.refused.escalating", {
1183
- sessionId, agentName: record.agent_name, correlationId,
1184
- reason: refused.reason,
1185
- impact: "the bilateral seal was refused by the directory, so this close falls through to " +
1186
- "a SOLO seal over this side's own carried chain rather than ending with no receipt.",
1187
- });
1188
- }
1189
- const escalated = await escalateToUnilateralSeal(rec, sid, escalation, correlationId);
1190
- /**
1191
- * ⚠️ THE UPSTREAM CAUSE SURVIVES THE DOWNSTREAM ANSWER — Invariant 3.
1192
- *
1193
- * If the solo seal ALSO fails, the operator would otherwise read only the unilateral gate's
1194
- * reason (`counterparty_not_absent`, say) and never learn that a bilateral seal was refused
1195
- * first — the two together are the story, and either alone points at the wrong subsystem.
1196
- */
1197
- if (refused && escalated !== null && typeof escalated === "object" && escalated.ok === false) {
1198
- const inner = escalated;
1199
- return {
1200
- ...escalated,
1201
- bilateral_refused_reason: refused.reason,
1202
- bilateral_refused_detail: refused.detail,
1203
- guidance: `${refused.guidance ?? refused.detail} The solo seal was then attempted and did not succeed either: ${typeof inner.guidance === "string" ? inner.guidance : "see the reason on this response."}`,
1204
- };
1205
- }
1206
- return escalated;
1207
- };
1208
- /**
1209
- * OWNERSHIP OF THE BROKER CONNECTION MOVES WITH THE TAIL.
1210
- *
1211
- * This is the one genuinely dangerous part of the change. The enclosing `finally` releases
1212
- * `sealBrokerConn`; if it ran while the tail was still going, the background seal would lose
1213
- * the connection it is waiting on. So the tail releases it itself, and the enclosing
1214
- * `finally` stands down via `handedOff`.
1215
- *
1216
- * PRECISELY WHAT IS LOST, corrected after review: not a corrupted seal. The escalation goes
1217
- * over the HOME stream, so it still completes. What this connection carries is the
1218
- * `seal_verified` / `session_sealed` push for a CROSS-NODE bilateral seal — so releasing it
1219
- * early silently downgrades every cross-node close from a bilateral receipt to a unilateral
1220
- * one, eleven minutes later. Quieter than corruption, and still worth the guard.
1221
- */
1222
- const finishSeal = async () => {
1223
- try {
1224
- return await awaitSealAndEscalate();
1225
- }
1226
- finally {
1227
- if (sealBrokerConn) {
1228
- try {
1229
- await sealBrokerConn.stop("seal-complete");
1230
- }
1231
- catch (err) {
1232
- logger.warn("session.seal.broker.release_failed", { sessionId: sid, reason: extractErrorMessage(err) });
1233
- }
1234
- sealBrokerConn = null;
1235
- }
1236
- sealInterruptedInProgress.delete(sealKey(rec.agent_name, sid));
1237
- }
1238
- };
1239
- /**
1240
- * A NEW CEREMONY IS STARTING, so any previous failure verdict is now false.
1241
- *
1242
- * ABOVE the `waitForSeal` branch — review MEDIUM-4. It used to sit below, so the clear ran
1243
- * only on the handed-off path: a blocking re-close (which the MCP tool exposes and the
1244
- * document-delivery seal uses) left the old marker in place, and a stale `seal_failed` from
1245
- * half an hour earlier was then reported as the current state. That is `STALEROSTER-1`'s
1246
- * defect, in the store whose own docstring says it avoids it.
1247
- */
1248
- sealFailures.clear(rec.agent_name, sid);
1249
- if (waitForSeal)
1250
- return await finishSeal();
1251
- handedOff = true;
1252
- /**
1253
- * Detached deliberately, and never awaited. A rejection must not become an unhandled
1254
- * rejection — and must not be silent either: the operator has already been told the seal is
1255
- * running, so a failure they never hear about is the worst of both worlds.
1256
- */
1257
- const tail = finishSeal();
1258
- /**
1259
- * TRACKED FOR SHUTDOWN — review MEDIUM-6.
1260
- *
1261
- * `stop()` cancels or awaits every other background worker: the reconcile scheduler, the
1262
- * telegram poller, the manifest poll, the roster sweep. `RestartSealResolver.stop()` goes
1263
- * further and AWAITS its in-flight seal, with a comment saying exactly why — *"severing
1264
- * signaling under a half-finished seal-interrupted exchange leaves the counterparty holding
1265
- * a commitment we never acknowledged… permanently divergent."*
1266
- *
1267
- * A detached, untracked ceremony was the one background task that could be cut at an
1268
- * arbitrary point by `cello logout`. Recoverable on the next boot, but recoverable is not
1269
- * the same as not breaking it.
1270
- */
1271
- registerBackgroundSeal?.(tail);
1272
- void tail.then((result) => {
1273
- const r = result;
1274
- if (r?.ok) {
1275
- sealFailures.clear(rec.agent_name, sid);
1276
- logger.info("session.seal.background.completed", { sessionId: sid, agentName: rec.agent_name, correlationId });
1277
- }
1278
- else {
1279
- /**
1280
- * THE BRANCH PRODUCTION ACTUALLY TAKES — review HIGH-1.
1281
- *
1282
- * `escalateToUnilateralSeal` contains zero `throw`s: all nine of its failure paths
1283
- * RESOLVE with `{ ok: false, reason }`. So recording only in the `.catch` below meant
1284
- * every ordinary dead ceremony went unrecorded and `cello_sealed_receipt` kept
1285
- * answering `not_sealed_yet` — the very answer this unit exists to replace. The log
1286
- * line three lines down said so out loud the whole time.
1287
- */
1288
- sealFailures.record(rec.agent_name, sid, r?.reason ?? "seal_unresolved", new Date().toISOString(), "unresolved");
1289
- logger.warn("session.seal.background.unresolved", {
1290
- sessionId: sid, agentName: rec.agent_name, correlationId, reason: r?.reason,
1291
- impact: "the close already answered; this session holds a durable commitment but has no receipt yet.",
1292
- guidance: "cello_sealed_receipt now reports seal_failed with this reason; a daemon restart also retries it.",
1293
- });
1294
- }
1295
- }, (err) => {
1296
- // RECORDED FOR THE RESPONSE, not only the log. The caller already holds `ok: true`, so a
1297
- // failure that lives only in daemon.log is one the agent has no way to discover.
1298
- sealFailures.record(rec.agent_name, sid, extractErrorMessage(err), new Date().toISOString(), "threw");
1299
- logger.error("session.seal.background.failed", {
1300
- sessionId: sid, agentName: rec.agent_name, correlationId,
1301
- error: extractErrorMessage(err),
1302
- impact: "the close already answered ok; the notarization did NOT complete and no receipt exists.",
1303
- guidance: "The commitment is durable — a daemon restart resolves it via the restart seal resolver.",
1304
- });
1305
- });
1306
- // Live test 2026-09-13: closing with a message you never read gave no warning. The close
1307
- // still goes ahead — the message is in the sealed record — but the operator is told.
1308
- const unreadAtClose = sessionNodeManager.getUnreadReceivedCount(rec.agent_name, sid);
1309
- return {
1310
- ...describeSealCommitted({ sessionId: sid, deadlineMs: bilateralTimeoutMs }),
1311
- ...(unreadAtClose > 0 ? {
1312
- unread_count: unreadAtClose,
1313
- unread_warning: `You closed this conversation with ${unreadAtClose} message(s) from the other side that you never read. They are part of the sealed record — read them with cello_transcript ${sid}.`,
1314
- } : {}),
1315
- };
1316
- }
1317
- finally {
1318
- // Fix #1: release the transient broker seal-connection (best-effort; the seal result stands).
1319
- //
1320
- // DOD-M15-CLOSEWAIT-1: skipped when the tail was handed to a background task, which owns
1321
- // this cleanup itself. Releasing here would pull the transport out from under a ceremony
1322
- // still in flight — the one way this change could corrupt a seal rather than merely report
1323
- // it differently.
1324
- if (!handedOff) {
1325
- if (sealBrokerConn) {
1326
- try {
1327
- await sealBrokerConn.stop("seal-complete");
1328
- }
1329
- catch (err) {
1330
- logger.warn("session.seal.broker.release_failed", { sessionId, reason: extractErrorMessage(err) });
1331
- }
1332
- }
1333
- sealInterruptedInProgress.delete(sealKey(record.agent_name, sessionId));
1334
- }
1335
- }
1336
- }
1337
- // DOD-M12B-PENDING-EXIT-1 — `seal_interrupted_pending` had NO exit, and the refusal below told
1338
- // the operator it was "awaiting FROST notarization". It was not awaiting anything: nobody ever
1339
- // requests that notarization. The relay stamps a chain only once BOTH parties have posted a
1340
- // SEAL ctrl leaf, and the responder never posts one — `inbound-seal-request.ts` persists its
1341
- // commitment, acks, and stops. Measured: 26 sessions idle in this status for 0.5 to 10.5 days.
1342
- //
1343
- // WHY THIS IS SAFE, stated accurately — the obvious answer is the wrong one. It is NOT that
1344
- // "both sides signed the same root": the escalation reports the tree root WITH the SEAL ctrl
1345
- // leaf appended, which is by construction not the committed root, and a responder-side row
1346
- // never even receives the initiator's leaf. It is safe because **the directory rebuilds the
1347
- // tree from relay-witnessed leaves and verifies their signatures** — it never consults the
1348
- // commitment. The commitment is what makes it legitimate to ASK, not what makes it verifiable.
1349
- // And re-entering cannot post a second ctrl leaf, because `submitSealLeaf` recovers the one a
1350
- // previous run posted from the durable carry rather than posting again.
1351
- if (record.status === "seal_interrupted_pending") {
1352
- // The AC-011 check at the top of the handler already refuses a concurrent attempt on this
1353
- // same key, for every status — so no second check is needed here, only the add/release pair
1354
- // that makes THIS attempt visible to it.
1355
- const key = sealKey(record.agent_name, sessionId);
1356
- sealInterruptedInProgress.add(key);
1357
- const correlationId = randomUUID();
1358
- try {
1359
- const uni = await submitAndEscalate(record, sessionId, correlationId);
1360
- if ("escalated" in uni) {
1361
- // NAME THE CAUSE `submitSealLeaf` GAVE, never a label invented here. Most of these are
1362
- // transient and local — `standing_receiver_unavailable` is simply "the agent is not
1363
- // started yet", which a freshly booted daemon reports for every session. The genuinely
1364
- // permanent case (the relay released the session) has its own reason,
1365
- // `seal_carry_empty`, raised inside the escalation where it is actually known.
1366
- /**
1367
- * ⚠️ "USUALLY LOCAL AND TEMPORARY" IS NOT TRUE OF EVERY REASON, AND THE ONES IT IS FALSE
1368
- * FOR ARE THE ONES DESIGNED TO FAIL LOUD — review pass 2, MEDIUM-4.
1369
- *
1370
- * The sentence below was fixed text wrapped around whatever reason arrived. That is right
1371
- * for `standing_receiver_unavailable` and a relay this daemon cannot reach. It is wrong
1372
- * for the client-side seal-payload guards, which fire only on a deterministic defect that
1373
- * will never clear — and the operator was being sent to `cello_start_agent`, `cello_status`
1374
- * and an endless retry for a bug in this daemon.
1375
- *
1376
- * A guard built to fail loud, converted back into "try again later" by the sentence that
1377
- * reports it. Enumerated, never pattern-matched: a substring rule would absorb the next
1378
- * reason nobody has considered, which is the same collapse in a new coat.
1379
- */
1380
- const NON_RETRYABLE_SEAL_SUBMIT_REASONS = new Set([
1381
- "seal_payload_not_carried",
1382
- "seal_payload_unbound",
1383
- "seal_payload_invalid",
1384
- "content_not_permitted_for_leaf_kind",
1385
- ]);
1386
- return {
1387
- ok: false,
1388
- reason: uni.reason,
1389
- guidance: NON_RETRYABLE_SEAL_SUBMIT_REASONS.has(uni.reason)
1390
- ? `This session holds a bilateral commitment, but no notarization could be requested for it: ${uni.reason}. ` +
1391
- `This one is NOT transient and retrying will not clear it — it is a defect in this daemon's seal path, refused locally before anything was sent. ` +
1392
- `The conversation is intact and nothing was disclosed. Report the reason code above; the daemon log carries the detail under session.relay.submit.*.`
1393
- : `This session holds a bilateral commitment, but no notarization could be requested for it: ${uni.reason}. ` +
1394
- sealSubmitCause(uni.reason),
1395
- };
1396
- }
1397
- if (uni.ok)
1398
- return uni;
1399
- return {
1400
- ok: false,
1401
- reason: uni.reason,
1402
- ...(uni.retry_after_seconds !== undefined ? { retry_after_seconds: uni.retry_after_seconds } : {}),
1403
- guidance: uni.guidance,
1404
- };
1405
- }
1406
- finally {
1407
- sealInterruptedInProgress.delete(key);
1408
- }
1409
- }
1410
- // Any other status — nothing to do.
1411
- return {
1412
- ok: false,
1413
- reason: "session_not_closeable",
1414
- guidance: `Session is in status '${record.status}', which cannot be closed via cello_close_session. Check cello_sessions.`,
1415
- };
1416
- };
1417
- }
1418
- //# sourceMappingURL=close-session-handler.js.map