@scotthuang/agent-knock-knock 0.12.11 → 0.12.13

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 (344) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +43 -30
  3. package/dist/src/approval-policy.d.ts +11 -2
  4. package/dist/src/approval-policy.js +33 -7
  5. package/dist/src/approval-policy.js.map +1 -1
  6. package/dist/src/callback-cli-adapter.d.ts +47 -0
  7. package/dist/src/callback-cli-adapter.js +156 -0
  8. package/dist/src/callback-cli-adapter.js.map +1 -0
  9. package/dist/src/callback-outbox-policy.d.ts +95 -0
  10. package/dist/src/callback-outbox-policy.js +149 -0
  11. package/dist/src/callback-outbox-policy.js.map +1 -0
  12. package/dist/src/callback-outbox-service.d.ts +247 -0
  13. package/dist/src/callback-outbox-service.js +1053 -0
  14. package/dist/src/callback-outbox-service.js.map +1 -0
  15. package/dist/src/callback-outbox-settlement.d.ts +54 -0
  16. package/dist/src/callback-outbox-settlement.js +241 -0
  17. package/dist/src/callback-outbox-settlement.js.map +1 -0
  18. package/dist/src/canonical-json.d.ts +1 -0
  19. package/dist/src/canonical-json.js +14 -0
  20. package/dist/src/canonical-json.js.map +1 -0
  21. package/dist/src/claude-lifecycle-compatibility.js +15 -0
  22. package/dist/src/claude-lifecycle-compatibility.js.map +1 -1
  23. package/dist/src/claude-local-transcript-provider.d.ts +82 -1
  24. package/dist/src/claude-local-transcript-provider.js +873 -23
  25. package/dist/src/claude-local-transcript-provider.js.map +1 -1
  26. package/dist/src/cli-command-runtime.d.ts +14 -0
  27. package/dist/src/cli-command-runtime.js +164 -0
  28. package/dist/src/cli-command-runtime.js.map +1 -0
  29. package/dist/src/cli-core.d.ts +4 -42
  30. package/dist/src/cli-core.js +958 -28684
  31. package/dist/src/cli-core.js.map +1 -1
  32. package/dist/src/cli-runtime-context.d.ts +57 -0
  33. package/dist/src/cli-runtime-context.js +118 -0
  34. package/dist/src/cli-runtime-context.js.map +1 -0
  35. package/dist/src/codex-lifecycle-compatibility.js +2 -1
  36. package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
  37. package/dist/src/codex-local-session-provider.d.ts +18 -0
  38. package/dist/src/codex-local-session-provider.js +67 -0
  39. package/dist/src/codex-local-session-provider.js.map +1 -1
  40. package/dist/src/codex-session-provider.js +1 -3
  41. package/dist/src/codex-session-provider.js.map +1 -1
  42. package/dist/src/conversation-trace.d.ts +30 -0
  43. package/dist/src/conversation-trace.js +185 -0
  44. package/dist/src/conversation-trace.js.map +1 -0
  45. package/dist/src/deferred-foreground-application-service.d.ts +84 -0
  46. package/dist/src/deferred-foreground-application-service.js +742 -0
  47. package/dist/src/deferred-foreground-application-service.js.map +1 -0
  48. package/dist/src/deferred-foreground-authority-cli-adapter.d.ts +78 -0
  49. package/dist/src/deferred-foreground-authority-cli-adapter.js +795 -0
  50. package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -0
  51. package/dist/src/deferred-foreground-boundary.d.ts +61 -0
  52. package/dist/src/deferred-foreground-boundary.js +2 -0
  53. package/dist/src/deferred-foreground-boundary.js.map +1 -0
  54. package/dist/src/deferred-foreground-capability.d.ts +4 -0
  55. package/dist/src/deferred-foreground-capability.js +184 -0
  56. package/dist/src/deferred-foreground-capability.js.map +1 -0
  57. package/dist/src/deferred-foreground-preparation-cli-adapter.d.ts +53 -0
  58. package/dist/src/deferred-foreground-preparation-cli-adapter.js +210 -0
  59. package/dist/src/deferred-foreground-preparation-cli-adapter.js.map +1 -0
  60. package/dist/src/deferred-foreground-preparation-service.d.ts +72 -0
  61. package/dist/src/deferred-foreground-preparation-service.js +179 -0
  62. package/dist/src/deferred-foreground-preparation-service.js.map +1 -0
  63. package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +139 -0
  64. package/dist/src/deferred-foreground-recovery-cli-adapter.js +1125 -0
  65. package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -0
  66. package/dist/src/deferred-foreground-recovery-service.d.ts +48 -0
  67. package/dist/src/deferred-foreground-recovery-service.js +91 -0
  68. package/dist/src/deferred-foreground-recovery-service.js.map +1 -0
  69. package/dist/src/deferred-foreground-transfer-policy.d.ts +2 -0
  70. package/dist/src/deferred-foreground-transfer-policy.js +10 -0
  71. package/dist/src/deferred-foreground-transfer-policy.js.map +1 -0
  72. package/dist/src/deferred-foreground-transfer.js +46 -105
  73. package/dist/src/deferred-foreground-transfer.js.map +1 -1
  74. package/dist/src/doctor-capabilities.d.ts +9 -0
  75. package/dist/src/doctor-capabilities.js +22 -0
  76. package/dist/src/doctor-capabilities.js.map +1 -1
  77. package/dist/src/durable-json-file.d.ts +22 -0
  78. package/dist/src/durable-json-file.js +127 -0
  79. package/dist/src/durable-json-file.js.map +1 -0
  80. package/dist/src/file-lock-cli-adapter.d.ts +43 -0
  81. package/dist/src/file-lock-cli-adapter.js +150 -0
  82. package/dist/src/file-lock-cli-adapter.js.map +1 -0
  83. package/dist/src/herdr-terminal-control-provider.js +1 -3
  84. package/dist/src/herdr-terminal-control-provider.js.map +1 -1
  85. package/dist/src/install-doctor-command-adapter.d.ts +17 -0
  86. package/dist/src/install-doctor-command-adapter.js +225 -0
  87. package/dist/src/install-doctor-command-adapter.js.map +1 -0
  88. package/dist/src/live-lifecycle-evidence.js +76 -46
  89. package/dist/src/live-lifecycle-evidence.js.map +1 -1
  90. package/dist/src/live-lifecycle-smoke.js +74 -50
  91. package/dist/src/live-lifecycle-smoke.js.map +1 -1
  92. package/dist/src/managed-session.d.ts +2 -0
  93. package/dist/src/managed-session.js +24 -0
  94. package/dist/src/managed-session.js.map +1 -1
  95. package/dist/src/managed-turn-recovery-service.d.ts +86 -0
  96. package/dist/src/managed-turn-recovery-service.js +145 -0
  97. package/dist/src/managed-turn-recovery-service.js.map +1 -0
  98. package/dist/src/mutation-transaction.d.ts +48 -0
  99. package/dist/src/mutation-transaction.js +73 -0
  100. package/dist/src/mutation-transaction.js.map +1 -0
  101. package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +154 -0
  102. package/dist/src/native-thread-lifecycle-cli-adapter.js +786 -0
  103. package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -0
  104. package/dist/src/native-thread-lifecycle-ledger-cli-adapter.d.ts +25 -0
  105. package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js +50 -0
  106. package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js.map +1 -0
  107. package/dist/src/native-thread-lifecycle-query-service.d.ts +75 -0
  108. package/dist/src/native-thread-lifecycle-query-service.js +297 -0
  109. package/dist/src/native-thread-lifecycle-query-service.js.map +1 -0
  110. package/dist/src/native-thread-lifecycle-recovery-adapter.d.ts +45 -0
  111. package/dist/src/native-thread-lifecycle-recovery-adapter.js +154 -0
  112. package/dist/src/native-thread-lifecycle-recovery-adapter.js.map +1 -0
  113. package/dist/src/native-thread-lifecycle-recovery-service.d.ts +211 -0
  114. package/dist/src/native-thread-lifecycle-recovery-service.js +1093 -0
  115. package/dist/src/native-thread-lifecycle-recovery-service.js.map +1 -0
  116. package/dist/src/native-thread-resume-snapshot-policy.d.ts +73 -0
  117. package/dist/src/native-thread-resume-snapshot-policy.js +218 -0
  118. package/dist/src/native-thread-resume-snapshot-policy.js.map +1 -0
  119. package/dist/src/native-thread-resume-snapshot.d.ts +3 -61
  120. package/dist/src/native-thread-resume-snapshot.js +2 -143
  121. package/dist/src/native-thread-resume-snapshot.js.map +1 -1
  122. package/dist/src/native-thread-transition-application.d.ts +147 -0
  123. package/dist/src/native-thread-transition-application.js +974 -0
  124. package/dist/src/native-thread-transition-application.js.map +1 -0
  125. package/dist/src/native-thread-transition-policy.d.ts +127 -0
  126. package/dist/src/native-thread-transition-policy.js +154 -0
  127. package/dist/src/native-thread-transition-policy.js.map +1 -0
  128. package/dist/src/native-thread-transition-resource-adapter.d.ts +12 -0
  129. package/dist/src/native-thread-transition-resource-adapter.js +44 -0
  130. package/dist/src/native-thread-transition-resource-adapter.js.map +1 -0
  131. package/dist/src/native-thread-transition-settlement-service.d.ts +94 -0
  132. package/dist/src/native-thread-transition-settlement-service.js +134 -0
  133. package/dist/src/native-thread-transition-settlement-service.js.map +1 -0
  134. package/dist/src/native-thread-transition-verification-adapter.d.ts +62 -0
  135. package/dist/src/native-thread-transition-verification-adapter.js +451 -0
  136. package/dist/src/native-thread-transition-verification-adapter.js.map +1 -0
  137. package/dist/src/openclaw-callback-transport.d.ts +69 -0
  138. package/dist/src/openclaw-callback-transport.js +583 -0
  139. package/dist/src/openclaw-callback-transport.js.map +1 -0
  140. package/dist/src/openclaw-plugin-callback-adapter.d.ts +1 -0
  141. package/dist/src/openclaw-plugin-callback-adapter.js +423 -0
  142. package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -0
  143. package/dist/src/openclaw-plugin-command-adapter.d.ts +16 -0
  144. package/dist/src/openclaw-plugin-command-adapter.js +1868 -0
  145. package/dist/src/openclaw-plugin-command-adapter.js.map +1 -0
  146. package/dist/src/openclaw-plugin-helpers.d.ts +27 -4
  147. package/dist/src/openclaw-plugin-helpers.js +356 -81
  148. package/dist/src/openclaw-plugin-helpers.js.map +1 -1
  149. package/dist/src/openclaw-plugin-schemas.d.ts +358 -0
  150. package/dist/src/openclaw-plugin-schemas.js +366 -0
  151. package/dist/src/openclaw-plugin-schemas.js.map +1 -0
  152. package/dist/src/openclaw-plugin-supervisor.d.ts +6 -0
  153. package/dist/src/openclaw-plugin-supervisor.js +123 -0
  154. package/dist/src/openclaw-plugin-supervisor.js.map +1 -0
  155. package/dist/src/openclaw-plugin.js +7 -2212
  156. package/dist/src/openclaw-plugin.js.map +1 -1
  157. package/dist/src/openclaw-private-authority-offers.d.ts +22 -0
  158. package/dist/src/openclaw-private-authority-offers.js +177 -0
  159. package/dist/src/openclaw-private-authority-offers.js.map +1 -0
  160. package/dist/src/protocol.d.ts +5 -0
  161. package/dist/src/protocol.js +59 -3
  162. package/dist/src/protocol.js.map +1 -1
  163. package/dist/src/session-store.d.ts +1 -0
  164. package/dist/src/session-store.js +84 -106
  165. package/dist/src/session-store.js.map +1 -1
  166. package/dist/src/store.d.ts +1 -0
  167. package/dist/src/store.js +5 -2
  168. package/dist/src/store.js.map +1 -1
  169. package/dist/src/terminal-acceptance-application-service.d.ts +65 -0
  170. package/dist/src/terminal-acceptance-application-service.js +63 -0
  171. package/dist/src/terminal-acceptance-application-service.js.map +1 -0
  172. package/dist/src/terminal-acceptance-cli-adapter.d.ts +210 -0
  173. package/dist/src/terminal-acceptance-cli-adapter.js +957 -0
  174. package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -0
  175. package/dist/src/terminal-action-projection.d.ts +309 -0
  176. package/dist/src/terminal-action-projection.js +376 -0
  177. package/dist/src/terminal-action-projection.js.map +1 -0
  178. package/dist/src/terminal-agent-bridge.d.ts +3 -0
  179. package/dist/src/terminal-agent-bridge.js +29 -8
  180. package/dist/src/terminal-agent-bridge.js.map +1 -1
  181. package/dist/src/terminal-authority-policy.d.ts +141 -0
  182. package/dist/src/terminal-authority-policy.js +367 -0
  183. package/dist/src/terminal-authority-policy.js.map +1 -0
  184. package/dist/src/terminal-binding-authority.d.ts +118 -0
  185. package/dist/src/terminal-binding-authority.js +245 -0
  186. package/dist/src/terminal-binding-authority.js.map +1 -0
  187. package/dist/src/terminal-binding-reconciliation-service.d.ts +75 -0
  188. package/dist/src/terminal-binding-reconciliation-service.js +73 -0
  189. package/dist/src/terminal-binding-reconciliation-service.js.map +1 -0
  190. package/dist/src/terminal-command-cli-adapter.d.ts +380 -0
  191. package/dist/src/terminal-command-cli-adapter.js +3288 -0
  192. package/dist/src/terminal-command-cli-adapter.js.map +1 -0
  193. package/dist/src/terminal-control-ref.d.ts +4 -0
  194. package/dist/src/terminal-control-ref.js +11 -8
  195. package/dist/src/terminal-control-ref.js.map +1 -1
  196. package/dist/src/terminal-delegate-cli-adapter.d.ts +41 -0
  197. package/dist/src/terminal-delegate-cli-adapter.js +242 -0
  198. package/dist/src/terminal-delegate-cli-adapter.js.map +1 -0
  199. package/dist/src/terminal-dispatch-abort.d.ts +22 -0
  200. package/dist/src/terminal-dispatch-abort.js +30 -0
  201. package/dist/src/terminal-dispatch-abort.js.map +1 -0
  202. package/dist/src/terminal-dispatch-application.d.ts +115 -0
  203. package/dist/src/terminal-dispatch-application.js +478 -0
  204. package/dist/src/terminal-dispatch-application.js.map +1 -0
  205. package/dist/src/terminal-dispatch-capability.d.ts +63 -0
  206. package/dist/src/terminal-dispatch-capability.js +133 -0
  207. package/dist/src/terminal-dispatch-capability.js.map +1 -0
  208. package/dist/src/terminal-dispatch-completion-cli-adapter.d.ts +22 -0
  209. package/dist/src/terminal-dispatch-completion-cli-adapter.js +96 -0
  210. package/dist/src/terminal-dispatch-completion-cli-adapter.js.map +1 -0
  211. package/dist/src/terminal-dispatch-composition.d.ts +79 -0
  212. package/dist/src/terminal-dispatch-composition.js +2 -0
  213. package/dist/src/terminal-dispatch-composition.js.map +1 -0
  214. package/dist/src/terminal-dispatch-execution.d.ts +290 -0
  215. package/dist/src/terminal-dispatch-execution.js +916 -0
  216. package/dist/src/terminal-dispatch-execution.js.map +1 -0
  217. package/dist/src/terminal-dispatch-ledger-codec.d.ts +110 -0
  218. package/dist/src/terminal-dispatch-ledger-codec.js +399 -0
  219. package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -0
  220. package/dist/src/terminal-dispatch-policy.d.ts +93 -0
  221. package/dist/src/terminal-dispatch-policy.js +122 -0
  222. package/dist/src/terminal-dispatch-policy.js.map +1 -0
  223. package/dist/src/terminal-dispatch-presenter.d.ts +65 -0
  224. package/dist/src/terminal-dispatch-presenter.js +258 -0
  225. package/dist/src/terminal-dispatch-presenter.js.map +1 -0
  226. package/dist/src/terminal-dispatch-receipt.d.ts +79 -0
  227. package/dist/src/terminal-dispatch-receipt.js +314 -0
  228. package/dist/src/terminal-dispatch-receipt.js.map +1 -0
  229. package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +132 -0
  230. package/dist/src/terminal-dispatch-recovery-cli-adapter.js +1330 -0
  231. package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -0
  232. package/dist/src/terminal-dispatch-recovery-service.d.ts +271 -0
  233. package/dist/src/terminal-dispatch-recovery-service.js +374 -0
  234. package/dist/src/terminal-dispatch-recovery-service.js.map +1 -0
  235. package/dist/src/terminal-dispatch-repository-cli-adapter.d.ts +35 -0
  236. package/dist/src/terminal-dispatch-repository-cli-adapter.js +483 -0
  237. package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -0
  238. package/dist/src/terminal-handoff-application-service.d.ts +7 -0
  239. package/dist/src/terminal-handoff-application-service.js +18 -0
  240. package/dist/src/terminal-handoff-application-service.js.map +1 -0
  241. package/dist/src/terminal-handoff-cli-adapter.d.ts +229 -0
  242. package/dist/src/terminal-handoff-cli-adapter.js +1389 -0
  243. package/dist/src/terminal-handoff-cli-adapter.js.map +1 -0
  244. package/dist/src/terminal-handoff-facts.d.ts +12 -0
  245. package/dist/src/terminal-handoff-facts.js +16 -0
  246. package/dist/src/terminal-handoff-facts.js.map +1 -0
  247. package/dist/src/terminal-identity-authority-cli-adapter.d.ts +318 -0
  248. package/dist/src/terminal-identity-authority-cli-adapter.js +1072 -0
  249. package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -0
  250. package/dist/src/terminal-identity-authority-service.d.ts +48 -0
  251. package/dist/src/terminal-identity-authority-service.js +45 -0
  252. package/dist/src/terminal-identity-authority-service.js.map +1 -0
  253. package/dist/src/terminal-list-cli-adapter.d.ts +274 -0
  254. package/dist/src/terminal-list-cli-adapter.js +2444 -0
  255. package/dist/src/terminal-list-cli-adapter.js.map +1 -0
  256. package/dist/src/terminal-list-renderer.d.ts +34 -0
  257. package/dist/src/terminal-list-renderer.js +643 -0
  258. package/dist/src/terminal-list-renderer.js.map +1 -0
  259. package/dist/src/terminal-maintenance-cli-adapter.d.ts +102 -0
  260. package/dist/src/terminal-maintenance-cli-adapter.js +1059 -0
  261. package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -0
  262. package/dist/src/terminal-monitor-application-service.d.ts +275 -0
  263. package/dist/src/terminal-monitor-application-service.js +982 -0
  264. package/dist/src/terminal-monitor-application-service.js.map +1 -0
  265. package/dist/src/terminal-monitor-cli-adapter.d.ts +130 -0
  266. package/dist/src/terminal-monitor-cli-adapter.js +442 -0
  267. package/dist/src/terminal-monitor-cli-adapter.js.map +1 -0
  268. package/dist/src/terminal-monitor-decision-policy.d.ts +98 -0
  269. package/dist/src/terminal-monitor-decision-policy.js +246 -0
  270. package/dist/src/terminal-monitor-decision-policy.js.map +1 -0
  271. package/dist/src/terminal-monitor-launch-plan.d.ts +45 -0
  272. package/dist/src/terminal-monitor-launch-plan.js +94 -0
  273. package/dist/src/terminal-monitor-launch-plan.js.map +1 -0
  274. package/dist/src/terminal-monitor-ownership-policy.d.ts +31 -0
  275. package/dist/src/terminal-monitor-ownership-policy.js +64 -0
  276. package/dist/src/terminal-monitor-ownership-policy.js.map +1 -0
  277. package/dist/src/terminal-monitor-poll-policy.d.ts +48 -0
  278. package/dist/src/terminal-monitor-poll-policy.js +67 -0
  279. package/dist/src/terminal-monitor-poll-policy.js.map +1 -0
  280. package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +53 -0
  281. package/dist/src/terminal-monitor-reconciliation-eligibility.js +114 -0
  282. package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -0
  283. package/dist/src/terminal-monitor-state-cli-adapter.d.ts +106 -0
  284. package/dist/src/terminal-monitor-state-cli-adapter.js +1492 -0
  285. package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -0
  286. package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +87 -0
  287. package/dist/src/terminal-monitor-state-reconciliation-service.js +106 -0
  288. package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -0
  289. package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +122 -0
  290. package/dist/src/terminal-monitor-supervision-cli-adapter.js +685 -0
  291. package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -0
  292. package/dist/src/terminal-process-facts.d.ts +2 -0
  293. package/dist/src/terminal-process-facts.js +19 -0
  294. package/dist/src/terminal-process-facts.js.map +1 -0
  295. package/dist/src/terminal-process-source.d.ts +6 -4
  296. package/dist/src/terminal-process-source.js +25 -20
  297. package/dist/src/terminal-process-source.js.map +1 -1
  298. package/dist/src/terminal-runtime-cli-adapter.d.ts +80 -0
  299. package/dist/src/terminal-runtime-cli-adapter.js +488 -0
  300. package/dist/src/terminal-runtime-cli-adapter.js.map +1 -0
  301. package/dist/src/terminal-scoped-approval-authority.d.ts +62 -0
  302. package/dist/src/terminal-scoped-approval-authority.js +281 -0
  303. package/dist/src/terminal-scoped-approval-authority.js.map +1 -0
  304. package/dist/src/terminal-selector-projection-service.d.ts +26 -0
  305. package/dist/src/terminal-selector-projection-service.js +81 -0
  306. package/dist/src/terminal-selector-projection-service.js.map +1 -0
  307. package/dist/src/terminal-status-cli-adapter.d.ts +126 -0
  308. package/dist/src/terminal-status-cli-adapter.js +577 -0
  309. package/dist/src/terminal-status-cli-adapter.js.map +1 -0
  310. package/dist/src/terminal-status-facts.d.ts +56 -0
  311. package/dist/src/terminal-status-facts.js +199 -0
  312. package/dist/src/terminal-status-facts.js.map +1 -0
  313. package/dist/src/terminal-submission-acceptance.d.ts +62 -145
  314. package/dist/src/terminal-submission-acceptance.js +555 -222
  315. package/dist/src/terminal-submission-acceptance.js.map +1 -1
  316. package/dist/src/terminal-submission-facts.d.ts +123 -0
  317. package/dist/src/terminal-submission-facts.js +205 -0
  318. package/dist/src/terminal-submission-facts.js.map +1 -0
  319. package/dist/src/terminal-turn-binding-authority-cli-adapter.d.ts +14 -0
  320. package/dist/src/terminal-turn-binding-authority-cli-adapter.js +90 -0
  321. package/dist/src/terminal-turn-binding-authority-cli-adapter.js.map +1 -0
  322. package/dist/src/terminal-watch-callback-cli-adapter.d.ts +20 -0
  323. package/dist/src/terminal-watch-callback-cli-adapter.js +55 -0
  324. package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -0
  325. package/dist/src/terminal-watch-cli-adapter.d.ts +64 -0
  326. package/dist/src/terminal-watch-cli-adapter.js +508 -0
  327. package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
  328. package/dist/src/terminal-watch-service.d.ts +97 -0
  329. package/dist/src/terminal-watch-service.js +714 -0
  330. package/dist/src/terminal-watch-service.js.map +1 -0
  331. package/dist/src/terminal-watch-store.d.ts +149 -0
  332. package/dist/src/terminal-watch-store.js +861 -0
  333. package/dist/src/terminal-watch-store.js.map +1 -0
  334. package/dist/src/value-guards.d.ts +4 -0
  335. package/dist/src/value-guards.js +12 -0
  336. package/dist/src/value-guards.js.map +1 -0
  337. package/dist/src/verified-dead-agent-policy.d.ts +192 -0
  338. package/dist/src/verified-dead-agent-policy.js +262 -0
  339. package/dist/src/verified-dead-agent-policy.js.map +1 -0
  340. package/docs/quickstart-herdr.md +8 -0
  341. package/docs/quickstart-tmux.md +33 -6
  342. package/openclaw.plugin.json +11 -3
  343. package/package.json +3 -1
  344. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +58 -40
@@ -0,0 +1,1330 @@
1
+ import path from "node:path";
2
+ import { canonicalJson } from "./canonical-json.js";
3
+ import { deterministicTerminalCallbackMessageId } from "./callback-outbox-service.js";
4
+ import { cliEnv, cliExit, cliNow, cliNowMs, cliPid, cliRuntimeLog, cliSleepSync } from "./cli-runtime-context.js";
5
+ import { loadDeferredForegroundTransfer } from "./deferred-foreground-transfer.js";
6
+ import { createFileLockCliAdapter } from "./file-lock-cli-adapter.js";
7
+ import { executorForConversation, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
8
+ import { tryLoadManagedSession } from "./session-store.js";
9
+ import { appendEvent, logPathForStatePath, loadState, pathsForConversationDir, saveState, withStoreWriterLease, withStoreWriterLeaseAsync } from "./store.js";
10
+ import { terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
11
+ import { sameTerminalControlIncarnation, terminalEndpointFromControlRef } from "./terminal-control-ref.js";
12
+ import { sameCanonicalStatePath, terminalDispatchLedgerLooksLifecycle, terminalDispatchReceiptHistory } from "./terminal-dispatch-ledger-codec.js";
13
+ import { isRecoverableTerminalDispatchStatus } from "./terminal-dispatch-policy.js";
14
+ import { TerminalDispatchRecoveryService, assertVerifiedDeadDispatchAuthorityFacts, decideLaggingDispatchRecovery, decidePreparedDispatchRecovery } from "./terminal-dispatch-recovery-service.js";
15
+ import { applyTerminalBridgeSubmission, terminalAcceptanceEvidenceForConversation, terminalBridgeRequestFingerprint, terminalBridgeSubmission, terminalBridgeSubmissionReceipts } from "./terminal-dispatch-receipt.js";
16
+ import { readNdjsonLog } from "./transcript.js";
17
+ import { decideAcceptedTurnDeadAgentStall, reconcileVerifiedDeadAgentAuthority, selectVerifiedDeadAgentEvent, validateStoredVerifiedDeadAgentAuthority, validateVerifiedDeadAgentEventAuthority, verifiedDeadTerminalAgentProcessEvidenceId } from "./verified-dead-agent-policy.js";
18
+ import { isRecord, nonBlankString } from "./value-guards.js";
19
+ class ConcreteRecoveryScope {
20
+ storeDir;
21
+ statePath;
22
+ logPath;
23
+ terminalControl;
24
+ releaseState;
25
+ active = true;
26
+ stateReleased = false;
27
+ localClaim;
28
+ constructor(storeDir, statePath, logPath, terminalControl, releaseState) {
29
+ this.storeDir = storeDir;
30
+ this.statePath = statePath;
31
+ this.logPath = logPath;
32
+ this.terminalControl = terminalControl;
33
+ this.releaseState = releaseState;
34
+ }
35
+ }
36
+ /** Bind typed recovery decisions to the existing CLI transaction boundary. */
37
+ export function createTerminalDispatchRecoveryCliAdapter(dependencies) {
38
+ const application = new TerminalDispatchRecoveryCliApplication(dependencies);
39
+ return Object.freeze({
40
+ loadOwner: (ledger) => application.loadOwner(ledger),
41
+ assertManagedOwner: (input) => application.assertManagedOwner(input),
42
+ assertNativeThreadStoreAuthority: (input) => application.assertNativeThreadStoreAuthority(input),
43
+ orphanedForRecovery: (control) => application.orphanedForRecovery(control),
44
+ bindingFields: (conversation) => application.bindingFields(conversation),
45
+ reconcilePrepared: (control, ledger) => application.reconcilePrepared(control, ledger),
46
+ isVerifiedDead: (conversation) => application.isVerifiedDead(conversation),
47
+ exactVerifiedDeadAuthority: (input) => application.exactVerifiedDeadAuthority(input),
48
+ ensureVerifiedDeadEvent: (input) => application.ensureVerifiedDeadEvent(input),
49
+ ensureVerifiedDeadClosedEvent: (input) => application.ensureVerifiedDeadClosedEvent(input),
50
+ assertVerifiedDeadDispatch: (input) => application.assertVerifiedDeadDispatch(input),
51
+ resolveVerifiedDeadDispatch: (input) => application.resolveVerifiedDeadDispatch(input),
52
+ stallAccepted: (input) => application.stallAccepted(input),
53
+ settleLocalCompletion: (input) => application.settleLocalCompletion(input),
54
+ prepareCompletion: (input) => application.prepareCompletion(input),
55
+ readEvents: (logPath) => application.readEvents(logPath)
56
+ });
57
+ }
58
+ class TerminalDispatchRecoveryCliApplication {
59
+ #dependencies;
60
+ #stateFileLock = createFileLockCliAdapter({
61
+ now: cliNow,
62
+ nowMs: cliNowMs,
63
+ pid: cliPid,
64
+ sleepSync: cliSleepSync
65
+ });
66
+ #optionsByRequest = new WeakMap();
67
+ #optionsByScope = new WeakMap();
68
+ #service;
69
+ constructor(dependencies) {
70
+ this.#dependencies = dependencies;
71
+ this.#service = new TerminalDispatchRecoveryService(this.#servicePorts());
72
+ }
73
+ #servicePorts() {
74
+ return {
75
+ transaction: this.#transactionPorts(),
76
+ authority: this.#authorityPorts(),
77
+ evidence: this.#evidencePorts(),
78
+ state: this.#statePorts(),
79
+ completion: this.#completionPorts()
80
+ };
81
+ }
82
+ #transactionPorts() {
83
+ return {
84
+ verifiedDead: (request, operation) => this.#withVerifiedDeadTransaction(request, operation),
85
+ localCompletion: (request, operation) => this.#withLocalCompletionTransaction(request, operation)
86
+ };
87
+ }
88
+ #authorityPorts() {
89
+ return {
90
+ assertBinding: ({ scope, conversation }) => {
91
+ this.#activeScope(scope);
92
+ this.#dependencies.authority.assertTurnBindingCurrent(conversation, "stall a verified-dead agent Turn for");
93
+ },
94
+ basicAcceptedDispatch: (context) => this.#basicVerifiedDeadDispatchExact(context),
95
+ assertAcceptedDispatch: (context) => {
96
+ const scope = this.#activeScope(context.scope);
97
+ this.assertVerifiedDeadDispatch({
98
+ terminalControl: scope.terminalControl,
99
+ conversation: context.conversation,
100
+ storeDir: scope.storeDir,
101
+ statePath: scope.statePath,
102
+ logPath: scope.logPath,
103
+ expectedMessageId: required(nonBlankString(context.messageId), "verified-dead stall has no terminal message id")
104
+ });
105
+ },
106
+ persistedDeath: (context) => this.exactVerifiedDeadAuthority({
107
+ conversation: context.conversation,
108
+ storeDir: this.#activeScope(context.scope).storeDir,
109
+ terminalControl: context.terminalControl,
110
+ logPath: this.#activeScope(context.scope).logPath
111
+ }),
112
+ observeDeath: (context) => this.#dependencies.observation.process({
113
+ options: this.#scopeOptions(context.scope),
114
+ conversation: context.conversation,
115
+ terminalControl: context.terminalControl
116
+ }),
117
+ priorStall: ({ context, proof, evidenceId, reason }) => this.#verifiedDeadStalledEventDecision({
118
+ logPath: this.#activeScope(context.scope).logPath,
119
+ proof,
120
+ evidenceId,
121
+ reason
122
+ }),
123
+ durableCompletion: (context) => this.#dependencies.observation.completion({
124
+ options: this.#scopeOptions(context.scope),
125
+ conversation: context.conversation,
126
+ terminalControl: context.terminalControl
127
+ }),
128
+ assertLocalCompletion: (context) => this.#assertLocalCompletion(context)
129
+ };
130
+ }
131
+ #evidencePorts() {
132
+ return {
133
+ ensureDeath: ({ context, proof }) => this.ensureVerifiedDeadEvent({
134
+ logPath: this.#activeScope(context.scope).logPath,
135
+ proof,
136
+ action: "monitor_reconciliation"
137
+ }),
138
+ ensureStall: (input) => this.#ensureVerifiedDeadStalledEvent({
139
+ logPath: this.#activeScope(input.context.scope).logPath,
140
+ proof: input.proof,
141
+ evidenceId: input.evidenceId,
142
+ reason: input.reason,
143
+ terminalControl: input.context.terminalControl,
144
+ completionObservation: input.completionObservation
145
+ })
146
+ };
147
+ }
148
+ #statePorts() {
149
+ return {
150
+ save: (context, conversation) => {
151
+ const scope = this.#activeScope(context.scope);
152
+ this.#assertStateHeld(scope);
153
+ saveState(scope.statePath, conversation);
154
+ },
155
+ crashAfterStallEvents: () => {
156
+ if (cliEnv().AKK_TEST_EXIT_AFTER_VERIFIED_DEAD_STALL_EVENTS === "1") {
157
+ cliExit(86);
158
+ }
159
+ },
160
+ logDeath: ({ context, proof, reason }) => {
161
+ this.#activeScope(context.scope);
162
+ cliRuntimeLog("warn", "terminal_agent_process_verified_dead", {
163
+ conversation_id: context.conversation.conversation_id,
164
+ terminal_target: context.terminalControl.target,
165
+ pid: proof.pid,
166
+ reason
167
+ });
168
+ },
169
+ settleLocalCompletion: (context) => this.#persistLocalCompletion(context)
170
+ };
171
+ }
172
+ #completionPorts() {
173
+ return {
174
+ prepareAfterStateRelease: (context, completion) => {
175
+ const scope = this.#activeScope(context.scope);
176
+ this.#releaseStateClaim(scope);
177
+ return this.#dependencies.completion.prepare({
178
+ options: this.#scopeOptions(scope),
179
+ statePath: scope.statePath,
180
+ logPath: scope.logPath,
181
+ conversation: context.conversation,
182
+ executor: executorForConversation(context.conversation),
183
+ terminalControl: context.terminalControl,
184
+ terminalMessageId: required(nonBlankString(context.messageId), "verified-dead completion has no terminal message id"),
185
+ completion
186
+ });
187
+ }
188
+ };
189
+ }
190
+ #scopeOptions(scope) {
191
+ return required(this.#optionsByScope.get(this.#activeScope(scope)), "verified-dead recovery options are unavailable");
192
+ }
193
+ bindingFields(conversation) {
194
+ return { ...this.#bindingFacts(conversation) };
195
+ }
196
+ assertNativeThreadStoreAuthority(input) {
197
+ const ledger = this.#dependencies.repository.reconcileIncarnation(input.terminalControl, this.#dependencies.repository.load(input.terminalControl));
198
+ if (!ledger)
199
+ return;
200
+ if (!this.#dependencies.repository.matchesControl(ledger, input.terminalControl, { requireProcessAnchor: false })) {
201
+ throw new Error(`terminal ${input.terminalControl.target} dispatch ledger selector is invalid`);
202
+ }
203
+ const binding = isRecord(ledger.binding) ? ledger.binding : undefined;
204
+ const authorityIds = new Set([
205
+ nonBlankString(ledger.native_thread_id),
206
+ nonBlankString(binding?.native_thread_id),
207
+ nonBlankString(ledger.before_native_thread_id),
208
+ nonBlankString(ledger.target_native_thread_id)
209
+ ].filter((value) => Boolean(value))
210
+ .map((value) => value.toLowerCase()));
211
+ const nativeThreadId = input.nativeThreadId.toLowerCase();
212
+ if (!authorityIds.has(nativeThreadId))
213
+ return;
214
+ const authorityStoreDir = this.#ledgerStoreDir(ledger);
215
+ if (!authorityStoreDir) {
216
+ throw new Error(`terminal ${input.terminalControl.target} has native-thread authority ` +
217
+ `${nativeThreadId} whose Store cannot be verified`);
218
+ }
219
+ if (path.resolve(authorityStoreDir) !== path.resolve(input.storeDir)) {
220
+ throw new Error(`terminal ${input.terminalControl.target} native thread ` +
221
+ `${nativeThreadId} is authoritative in another Store ` +
222
+ `${path.resolve(authorityStoreDir)}`);
223
+ }
224
+ }
225
+ orphanedForRecovery(terminalControl) {
226
+ try {
227
+ const ledger = this.#dependencies.repository.load(terminalControl);
228
+ const lifecycle = terminalDispatchLedgerLooksLifecycle(ledger);
229
+ const recoveryIdentity = lifecycle
230
+ ? nonBlankString(ledger?.transition_id)
231
+ : nonBlankString(ledger?.message_id);
232
+ if (!ledger ||
233
+ !isRecoverableTerminalDispatchStatus(String(ledger.status)) ||
234
+ !recoveryIdentity ||
235
+ (!lifecycle && this.#dependencies.repository.matchesControl(ledger, terminalControl, { requireProcessAnchor: false }) && !this.#dependencies.repository.matchesControl(ledger, terminalControl)) ||
236
+ (!lifecycle && this.loadOwner(ledger)))
237
+ return undefined;
238
+ return lifecycle ? { ...ledger, kind: "lifecycle" } : ledger;
239
+ }
240
+ catch {
241
+ return undefined;
242
+ }
243
+ }
244
+ #ledgerStoreDir(ledger) {
245
+ const stored = nonBlankString(ledger.store_dir);
246
+ if (stored)
247
+ return stored;
248
+ const statePath = nonBlankString(ledger.state_path);
249
+ if (!statePath)
250
+ return undefined;
251
+ try {
252
+ return pathsForConversationDir(path.dirname(path.resolve(statePath))).storeDir;
253
+ }
254
+ catch {
255
+ return undefined;
256
+ }
257
+ }
258
+ loadOwner(ledger) {
259
+ const statePath = nonBlankString(ledger.state_path);
260
+ if (!statePath)
261
+ return undefined;
262
+ try {
263
+ const conversation = loadState(statePath);
264
+ return conversation.conversation_id === nonBlankString(ledger.conversation_id)
265
+ ? conversation
266
+ : undefined;
267
+ }
268
+ catch {
269
+ return undefined;
270
+ }
271
+ }
272
+ assertManagedOwner(input) {
273
+ this.#dependencies.authority.assertNoDeferredTransfer({
274
+ storeDir: input.storeDir,
275
+ conversation: input.conversation,
276
+ action: input.action
277
+ });
278
+ this.#dependencies.authority.assertTurnBindingCurrent(input.conversation, input.action);
279
+ const nativeTakeover = takeoverFor(input.conversation);
280
+ const messageId = nonBlankString(nativeTakeover?.terminal_bridge_message_id);
281
+ const ledger = this.#dependencies.repository.load(input.terminalControl);
282
+ if (!messageId || !ledger ||
283
+ !["submitted", "agent_accepted"].includes(String(ledger.status)) ||
284
+ nonBlankString(ledger.conversation_id) !==
285
+ input.conversation.conversation_id ||
286
+ nonBlankString(ledger.message_id) !== messageId ||
287
+ (nonBlankString(input.conversation.terminal_binding_id) &&
288
+ (nonBlankString(ledger.binding_id) !==
289
+ nonBlankString(input.conversation.terminal_binding_id) ||
290
+ Number(ledger.binding_generation) !==
291
+ Number(input.conversation.terminal_binding_generation) ||
292
+ nonBlankString(ledger.native_thread_id) !==
293
+ (nonBlankString(input.conversation.native_thread_id) ??
294
+ nonBlankString(nativeTakeover?.terminal_agent_session_id))))) {
295
+ throw new Error(`refusing to ${input.action}: this AKK conversation does not own the ` +
296
+ "current terminal dispatch generation; refresh status and operate on " +
297
+ "the current task");
298
+ }
299
+ }
300
+ isVerifiedDead(conversation) {
301
+ return processDisposition(conversation)?.status === "verified_dead";
302
+ }
303
+ stallAccepted(input) {
304
+ this.#optionsByRequest.set(input, input.options);
305
+ return this.#service.stallAcceptedForVerifiedDead(input).finally(() => {
306
+ this.#optionsByRequest.delete(input);
307
+ });
308
+ }
309
+ settleLocalCompletion(input) {
310
+ return this.#service.settleLocalCompletion(input);
311
+ }
312
+ #bindingFacts(conversation) {
313
+ const takeover = takeoverFor(conversation);
314
+ const bindingId = nonBlankString(conversation.terminal_binding_id);
315
+ const generation = Number(conversation.terminal_binding_generation);
316
+ const nativeThreadId = nonBlankString(conversation.native_thread_id) ??
317
+ nonBlankString(takeover?.terminal_agent_session_id);
318
+ const storeDir = this.#dependencies.authority.storeDirForConversation(conversation);
319
+ const submission = terminalBridgeSubmission(conversation);
320
+ const transferId = nonBlankString(takeover?.deferred_foreground_transfer_id);
321
+ return {
322
+ ...(bindingId ? { binding_id: bindingId } : {}),
323
+ ...(Number.isSafeInteger(generation)
324
+ ? { binding_generation: generation }
325
+ : {}),
326
+ ...(nativeThreadId ? { native_thread_id: nativeThreadId } : {}),
327
+ ...(storeDir ? { store_dir: path.resolve(storeDir) } : {}),
328
+ ...(nonBlankString(submission?.message_type)
329
+ ? { message_type: nonBlankString(submission?.message_type) }
330
+ : {}),
331
+ ...(nonBlankString(submission?.message_body_hash)
332
+ ? { message_body_hash: nonBlankString(submission?.message_body_hash) }
333
+ : {}),
334
+ ...(transferId
335
+ ? { deferred_foreground_transfer_id: transferId }
336
+ : {}),
337
+ executor_kind: executorForConversation(conversation).kind,
338
+ ...(conversation.openclaw_session
339
+ ? { openclaw_session: conversation.openclaw_session }
340
+ : {})
341
+ };
342
+ }
343
+ reconcilePrepared(terminalControl, ledger) {
344
+ if (!ledger || terminalDispatchLedgerLooksLifecycle(ledger))
345
+ return ledger;
346
+ if (ledger.status !== "prepared") {
347
+ return this.#reconcileLagging(terminalControl, ledger);
348
+ }
349
+ const dispatcherPid = Number(ledger.dispatcher_pid);
350
+ const dispatcherActive = Number.isSafeInteger(dispatcherPid) &&
351
+ dispatcherPid > 1 && dispatcherPid !== cliPid() &&
352
+ this.#dependencies.runtime.isProcessAlive(dispatcherPid);
353
+ const statePath = nonBlankString(ledger.state_path);
354
+ const messageId = nonBlankString(ledger.message_id);
355
+ let owner = { status: "unreadable" };
356
+ if (!dispatcherActive && statePath && messageId) {
357
+ owner = this.#preparedOwnerFacts(terminalControl, ledger, statePath);
358
+ }
359
+ const decision = decidePreparedDispatchRecovery({
360
+ ledger: {
361
+ lifecycle: false,
362
+ status: nonBlankString(ledger.status),
363
+ dispatcherPid,
364
+ dispatcherActive,
365
+ statePath,
366
+ eventLogPath: nonBlankString(ledger.event_log_path),
367
+ messageId,
368
+ conversationId: nonBlankString(ledger.conversation_id)
369
+ },
370
+ owner,
371
+ now: () => cliNow().toISOString()
372
+ });
373
+ if (decision.action === "keep")
374
+ return ledger;
375
+ if (decision.action === "reconcile_lagging") {
376
+ return this.#reconcileLagging(terminalControl, ledger);
377
+ }
378
+ this.#dependencies.repository.save(terminalControl, decision.action === "replace_ledger"
379
+ ? decision.mutation
380
+ : { ...ledger, ...decision.mutation });
381
+ return this.#dependencies.repository.load(terminalControl);
382
+ }
383
+ #preparedOwnerFacts(terminalControl, ledger, statePath) {
384
+ let conversation;
385
+ try {
386
+ conversation = loadState(statePath);
387
+ }
388
+ catch (error) {
389
+ const code = error instanceof Error
390
+ ? error.code
391
+ : undefined;
392
+ return code === "ENOENT" ? { status: "missing" } : { status: "unreadable" };
393
+ }
394
+ const takeover = takeoverFor(conversation);
395
+ const storedControl = this.#dependencies.authority.terminalControl(takeover);
396
+ if (conversation.conversation_id !== nonBlankString(ledger.conversation_id) ||
397
+ !storedControl ||
398
+ !sameTerminalControlIncarnation(storedControl, terminalControl)) {
399
+ return { status: "mismatch" };
400
+ }
401
+ const submission = terminalBridgeSubmission(conversation);
402
+ const requestText = String(takeover?.terminal_bridge_request_text ?? conversation.user_request ?? "");
403
+ return {
404
+ status: "loaded",
405
+ conversationId: conversation.conversation_id,
406
+ updatedAt: nonBlankString(conversation.updated_at),
407
+ storedMessageId: nonBlankString(takeover?.terminal_bridge_message_id),
408
+ submission: submissionFacts(submission),
409
+ binding: this.#bindingFacts(conversation),
410
+ requestHash: terminalBridgeRequestFingerprint(requestText),
411
+ statePath,
412
+ eventLogPath: nonBlankString(ledger.event_log_path) ??
413
+ logPathForStatePath(statePath),
414
+ callbackExpected: Boolean(conversation.gateway_method)
415
+ };
416
+ }
417
+ #reconcileLagging(terminalControl, ledger) {
418
+ const projection = this.#laggingProjection(terminalControl, ledger);
419
+ if (!projection || !ledger)
420
+ return ledger;
421
+ const decision = decideLaggingDispatchRecovery(projection.input);
422
+ if (decision.action === "keep")
423
+ return ledger;
424
+ if (decision.action === "save_turn_accepted") {
425
+ const accepted = this.#applySubmissionMutation({
426
+ conversation: projection.conversation,
427
+ messageId: projection.messageId,
428
+ requestText: projection.requestText,
429
+ status: "agent_accepted",
430
+ preparedAt: projection.submission.preparedAt ??
431
+ nonBlankString(ledger.prepared_at) ?? decision.acceptedAt,
432
+ textInjectedAt: projection.submission.textInjectedAt ??
433
+ nonBlankString(ledger.text_injected_at),
434
+ enterDispatchedAt: projection.submission.enterDispatchedAt ??
435
+ nonBlankString(ledger.enter_dispatched_at),
436
+ agentAcceptedAt: decision.acceptedAt,
437
+ acceptanceEvidence: decision.acceptanceEvidence
438
+ });
439
+ saveState(projection.statePath, accepted);
440
+ return ledger;
441
+ }
442
+ if (decision.action === "save_turn_uncertain_and_ledger") {
443
+ const uncertain = this.#applySubmissionMutation({
444
+ conversation: {
445
+ ...projection.conversation,
446
+ status: "stalled",
447
+ stalled_at: decision.uncertainAt,
448
+ stalled_reason: "stored native acceptance evidence is invalid",
449
+ updated_at: decision.uncertainAt
450
+ },
451
+ messageId: projection.messageId,
452
+ requestText: projection.requestText,
453
+ status: "uncertain",
454
+ preparedAt: projection.submission.preparedAt ??
455
+ nonBlankString(ledger.prepared_at) ?? decision.uncertainAt,
456
+ textInjectedAt: projection.submission.textInjectedAt ??
457
+ nonBlankString(ledger.text_injected_at),
458
+ enterDispatchedAt: projection.submission.enterDispatchedAt ??
459
+ nonBlankString(ledger.enter_dispatched_at),
460
+ uncertainAt: decision.uncertainAt,
461
+ error: decision.reason,
462
+ lastProvenStage: "enter_dispatched"
463
+ });
464
+ saveState(projection.statePath, uncertain);
465
+ this.#dependencies.repository.save(terminalControl, {
466
+ ...ledger,
467
+ ...decision.ledger
468
+ });
469
+ return this.#dependencies.repository.load(terminalControl);
470
+ }
471
+ this.#dependencies.repository.save(terminalControl, {
472
+ ...ledger,
473
+ ...decision.mutation
474
+ });
475
+ return this.#dependencies.repository.load(terminalControl);
476
+ }
477
+ #laggingProjection(terminalControl, ledger) {
478
+ if (!ledger || terminalDispatchLedgerLooksLifecycle(ledger) || ![
479
+ "text_injected",
480
+ "enter_dispatched",
481
+ "submitted",
482
+ "agent_accepted",
483
+ "not_accepted",
484
+ "uncertain"
485
+ ].includes(String(ledger.status)))
486
+ return undefined;
487
+ const statePath = nonBlankString(ledger.state_path);
488
+ const messageId = nonBlankString(ledger.message_id);
489
+ if (!statePath || !messageId)
490
+ return undefined;
491
+ let conversation;
492
+ try {
493
+ conversation = loadState(statePath);
494
+ }
495
+ catch {
496
+ return undefined;
497
+ }
498
+ const takeover = takeoverFor(conversation);
499
+ const rawSubmission = terminalBridgeSubmission(conversation);
500
+ const storedControl = this.#dependencies.authority.terminalControl(takeover);
501
+ if (conversation.conversation_id !== nonBlankString(ledger.conversation_id) ||
502
+ nonBlankString(takeover?.terminal_bridge_message_id) !== messageId ||
503
+ nonBlankString(rawSubmission?.message_id) !== messageId ||
504
+ !storedControl ||
505
+ !sameTerminalControlIncarnation(storedControl, terminalControl))
506
+ return undefined;
507
+ const requestText = String(takeover?.terminal_bridge_request_text ?? conversation.user_request ?? "");
508
+ const requestHash = terminalBridgeRequestFingerprint(requestText);
509
+ if (nonBlankString(ledger.request_hash) !== requestHash ||
510
+ nonBlankString(rawSubmission?.request_hash) !== requestHash)
511
+ return undefined;
512
+ const binding = this.#bindingFacts(conversation);
513
+ if (!bindingCompatible(ledger, binding))
514
+ return undefined;
515
+ const submission = submissionFacts(rawSubmission);
516
+ const acceptance = acceptanceProjection(conversation, requestText, String(ledger.status), rawSubmission, ledger);
517
+ return {
518
+ input: {
519
+ eligible: true,
520
+ ledgerStatus: String(ledger.status),
521
+ stateStatus: submission.status,
522
+ stateAcceptance: acceptance.state,
523
+ ledgerAcceptance: acceptance.ledger,
524
+ ledgerAcceptanceError: acceptance.ledgerError,
525
+ ledgerAgentAcceptedAt: nonBlankString(ledger.agent_accepted_at),
526
+ ledgerLastProvenStage: nonBlankString(ledger.last_proven_stage),
527
+ submission,
528
+ binding,
529
+ now: cliNow().toISOString()
530
+ },
531
+ conversation,
532
+ submission,
533
+ statePath,
534
+ messageId,
535
+ requestText
536
+ };
537
+ }
538
+ #withVerifiedDeadTransaction(request, operation) {
539
+ const canonicalStoreDir = pathsForConversationDir(path.dirname(request.statePath)).storeDir;
540
+ if (path.resolve(request.storeDir) !== path.resolve(canonicalStoreDir)) {
541
+ return Promise.resolve(unchangedVerifiedDead(loadState(request.statePath), "dead_process_stall_store_mismatch"));
542
+ }
543
+ const initial = loadState(request.statePath);
544
+ const control = this.#dependencies.authority.terminalControl(takeoverFor(initial));
545
+ if (initial.conversation_id !== request.expectedConversationId ||
546
+ !control ||
547
+ !acceptedTurnCanBeStalled(request.storeDir, initial)) {
548
+ return Promise.resolve(unchangedVerifiedDead(initial, "dead_process_stall_not_applicable"));
549
+ }
550
+ return this.#withVerifiedDeadLocks(request, control, operation);
551
+ }
552
+ async #withVerifiedDeadLocks(request, control, operation) {
553
+ const releaseTerminal = this.#dependencies.repository.acquire(request.storeDir, control, { timeoutMs: 30000 });
554
+ try {
555
+ return await withStoreWriterLeaseAsync(request.storeDir, async () => {
556
+ const releaseState = this.#stateFileLock.acquire(`${request.statePath}.lock`);
557
+ let stateReleased = false;
558
+ const releaseStateOnce = () => {
559
+ if (stateReleased)
560
+ return;
561
+ releaseState();
562
+ stateReleased = true;
563
+ };
564
+ try {
565
+ const current = loadState(request.statePath);
566
+ const takeover = takeoverFor(current);
567
+ const currentControl = this.#dependencies.authority.terminalControl(takeover);
568
+ const messageId = nonBlankString(takeover?.terminal_bridge_message_id);
569
+ if (current.conversation_id !== request.expectedConversationId ||
570
+ (request.expectedMessageId !== undefined &&
571
+ messageId !== request.expectedMessageId) ||
572
+ !currentControl ||
573
+ !terminalControlsShareIncarnation(currentControl, control) ||
574
+ !acceptedTurnCanBeStalled(request.storeDir, current)) {
575
+ return unchangedVerifiedDead(current, "dead_process_stall_generation_changed");
576
+ }
577
+ const scope = new ConcreteRecoveryScope(request.storeDir, request.statePath, request.logPath, currentControl, releaseStateOnce);
578
+ this.#optionsByScope.set(scope, required(this.#optionsByRequest.get(request), "verified-dead recovery options are unavailable"));
579
+ try {
580
+ return await operation({
581
+ scope,
582
+ conversation: current,
583
+ terminalControl: currentControl,
584
+ messageId: messageId ?? ""
585
+ });
586
+ }
587
+ finally {
588
+ scope.active = false;
589
+ if (!scope.stateReleased)
590
+ this.#releaseStateClaim(scope);
591
+ this.#optionsByScope.delete(scope);
592
+ }
593
+ }
594
+ finally {
595
+ releaseStateOnce();
596
+ }
597
+ });
598
+ }
599
+ finally {
600
+ releaseTerminal();
601
+ }
602
+ }
603
+ #withLocalCompletionTransaction(request, operation) {
604
+ const initial = loadState(request.statePath);
605
+ const claim = completionClaim(initial);
606
+ if (nonBlankString(initial.gateway_method) ||
607
+ !claim ||
608
+ !["idle", "failed"].includes(String(initial.status)))
609
+ return localNotApplicable();
610
+ const terminalControl = this.#dependencies.authority.terminalControl(takeoverFor(initial));
611
+ if (!terminalControl) {
612
+ throw new Error(`local terminal completion ${initial.conversation_id} lost its terminal authority`);
613
+ }
614
+ const releaseTerminal = this.#dependencies.repository.acquire(request.storeDir, terminalControl, { timeoutMs: 30000 });
615
+ try {
616
+ return withStoreWriterLease(request.storeDir, () => {
617
+ const releaseState = this.#stateFileLock.acquire(`${request.statePath}.lock`);
618
+ let stateReleased = false;
619
+ const releaseStateOnce = () => {
620
+ if (stateReleased)
621
+ return;
622
+ releaseState();
623
+ stateReleased = true;
624
+ };
625
+ try {
626
+ const conversation = loadState(request.statePath);
627
+ const scope = new ConcreteRecoveryScope(request.storeDir, request.statePath, request.logPath, terminalControl, releaseStateOnce);
628
+ try {
629
+ const context = this.#localCompletionContext(scope, conversation);
630
+ return operation(context);
631
+ }
632
+ finally {
633
+ scope.active = false;
634
+ if (!scope.stateReleased)
635
+ this.#releaseStateClaim(scope);
636
+ }
637
+ }
638
+ finally {
639
+ releaseStateOnce();
640
+ }
641
+ });
642
+ }
643
+ finally {
644
+ releaseTerminal();
645
+ }
646
+ }
647
+ #localCompletionContext(scope, conversation) {
648
+ const takeover = takeoverFor(conversation);
649
+ const claim = completionClaim(conversation);
650
+ const control = this.#dependencies.authority.terminalControl(takeover);
651
+ const terminalMessageId = nonBlankString(takeover?.terminal_bridge_message_id);
652
+ const callbackMessageId = nonBlankString(claim?.callback_message_id);
653
+ const completionFingerprint = nonBlankString(claim?.completion_fingerprint);
654
+ const completionId = nonBlankString(claim?.completion_id);
655
+ const claimedAt = nonBlankString(claim?.claimed_at);
656
+ const outcome = nonBlankString(claim?.outcome);
657
+ const expectedMessageType = outcome === "success"
658
+ ? "done"
659
+ : outcome === "failure" ? "error" : undefined;
660
+ if (nonBlankString(conversation.gateway_method) ||
661
+ conversation.callback_delivery !== undefined ||
662
+ !claim ||
663
+ !control ||
664
+ !terminalControlsShareIncarnation(control, scope.terminalControl) ||
665
+ !terminalMessageId || !callbackMessageId || !completionFingerprint ||
666
+ !completionId || !claimedAt || !validTimestamp(claimedAt) ||
667
+ !expectedMessageType ||
668
+ (outcome === "success" && conversation.status !== "idle") ||
669
+ (outcome === "failure" && conversation.status !== "failed") ||
670
+ deterministicTerminalCallbackMessageId({
671
+ conversationId: conversation.conversation_id,
672
+ terminalMessageId,
673
+ completionFingerprint,
674
+ outcome: outcome
675
+ }) !== callbackMessageId) {
676
+ throw new Error(`local terminal completion ${conversation.conversation_id} has ` +
677
+ "inconsistent claim, Turn phase, or callback authority");
678
+ }
679
+ scope.localClaim = {
680
+ completionFingerprint,
681
+ completionId,
682
+ callbackMessageId,
683
+ claimedAt,
684
+ outcome: outcome,
685
+ expectedMessageType
686
+ };
687
+ return {
688
+ scope,
689
+ conversation,
690
+ terminalControl: control,
691
+ terminalMessageId,
692
+ completionId,
693
+ callbackMessageId,
694
+ outcome: outcome
695
+ };
696
+ }
697
+ #assertLocalCompletion(context) {
698
+ const scope = this.#activeScope(context.scope);
699
+ this.#assertStateHeld(scope);
700
+ const claim = required(scope.localClaim, "local completion claim is unavailable");
701
+ const conversation = context.conversation;
702
+ const submission = terminalBridgeSubmission(conversation);
703
+ if (submission?.status !== "agent_accepted" ||
704
+ nonBlankString(submission.message_id) !== context.terminalMessageId ||
705
+ nonBlankString(submission.session_id) !== sessionIdForConversation(conversation) ||
706
+ nonBlankString(submission.turn_id) !== turnIdForConversation(conversation)) {
707
+ throw new Error(`local terminal completion ${conversation.conversation_id} is ` +
708
+ "not tied to one accepted terminal submission");
709
+ }
710
+ this.#assertLocalCompletionEvents(scope, context, claim);
711
+ const ledger = this.#dependencies.repository.load(context.terminalControl);
712
+ if (!this.#localCompletionLedgerExact(scope, context, ledger)) {
713
+ throw new Error(`local terminal completion ${conversation.conversation_id} has ` +
714
+ "no exact accepted terminal ledger");
715
+ }
716
+ return { ledgerResolved: ledger?.status === "resolved" };
717
+ }
718
+ #assertLocalCompletionEvents(scope, context, claim) {
719
+ const events = this.readEvents(scope.logPath);
720
+ const claimed = events.some((event) => event.event === "terminal_bridge_completion_claimed" &&
721
+ event.conversation_id === context.conversation.conversation_id &&
722
+ event.terminal_bridge_message_id === context.terminalMessageId &&
723
+ event.completion_fingerprint === claim.completionFingerprint &&
724
+ event.completion_id === context.completionId &&
725
+ event.callback_message_id === context.callbackMessageId &&
726
+ event.outcome === context.outcome && event.ts === claim.claimedAt);
727
+ const detected = events.some((event) => event.event === "terminal_bridge_completion_detected" &&
728
+ event.conversation_id === context.conversation.conversation_id &&
729
+ event.terminal_bridge_message_id === context.terminalMessageId &&
730
+ event.completion_id === context.completionId &&
731
+ event.callback_message_id === context.callbackMessageId &&
732
+ event.completion_outcome === context.outcome);
733
+ const messageRecorded = events.some((event) => {
734
+ const message = isRecord(event.message) ? event.message : undefined;
735
+ const metadata = isRecord(message?.metadata) ? message.metadata : undefined;
736
+ return event.event === "message" &&
737
+ event.conversation_id === context.conversation.conversation_id &&
738
+ event.session_id === sessionIdForConversation(context.conversation) &&
739
+ event.turn_id === turnIdForConversation(context.conversation) &&
740
+ message?.id === context.callbackMessageId &&
741
+ message?.type === claim.expectedMessageType &&
742
+ message?.to === "openclaw" && message?.requires_response === false &&
743
+ metadata?.terminal_bridge_message_id === context.terminalMessageId;
744
+ });
745
+ if (!claimed || !detected || !messageRecorded) {
746
+ throw new Error(`local terminal completion ${context.conversation.conversation_id} is ` +
747
+ "missing exact claim, detection, or message evidence");
748
+ }
749
+ }
750
+ #localCompletionLedgerExact(scope, context, ledger) {
751
+ const conversation = context.conversation;
752
+ const bindingId = nonBlankString(conversation.terminal_binding_id);
753
+ const generation = Number(conversation.terminal_binding_generation);
754
+ return Boolean(ledger && ["agent_accepted", "resolved"].includes(String(ledger.status)) &&
755
+ this.#dependencies.repository.matchesControl(ledger, context.terminalControl) &&
756
+ nonBlankString(ledger.conversation_id) === conversation.conversation_id &&
757
+ nonBlankString(ledger.session_id) === sessionIdForConversation(conversation) &&
758
+ nonBlankString(ledger.turn_id) === turnIdForConversation(conversation) &&
759
+ nonBlankString(ledger.message_id) === context.terminalMessageId &&
760
+ nonBlankString(ledger.native_thread_id) ===
761
+ nonBlankString(conversation.native_thread_id) &&
762
+ (bindingId === undefined || nonBlankString(ledger.binding_id) === bindingId) &&
763
+ (!Number.isSafeInteger(generation) || Number(ledger.binding_generation) === generation) &&
764
+ path.resolve(nonBlankString(ledger.store_dir) ?? "") === path.resolve(scope.storeDir) &&
765
+ sameCanonicalStatePath(ledger.state_path, scope.statePath));
766
+ }
767
+ #persistLocalCompletion(context) {
768
+ const scope = this.#activeScope(context.scope);
769
+ this.#assertStateHeld(scope);
770
+ if (!this.#dependencies.repository.resolve(context.terminalControl, {
771
+ conversation: context.conversation,
772
+ expectedMessageId: context.terminalMessageId,
773
+ reason: "callbackless terminal bridge task reached durable completion"
774
+ })) {
775
+ throw new Error(`local terminal completion ${context.conversation.conversation_id} ` +
776
+ "changed before ledger settlement");
777
+ }
778
+ appendEvent(scope.logPath, {
779
+ ts: cliNow().toISOString(),
780
+ conversation_id: context.conversation.conversation_id,
781
+ event: "terminal_bridge_local_completion_settled",
782
+ terminal_bridge_message_id: context.terminalMessageId,
783
+ completion_id: context.completionId,
784
+ callback_message_id: context.callbackMessageId,
785
+ outcome: context.outcome
786
+ });
787
+ }
788
+ exactVerifiedDeadAuthority(input) {
789
+ const stored = this.#storedVerifiedDeadAuthority(input);
790
+ if (stored.status === "invalid")
791
+ return stored;
792
+ return reconcileVerifiedDeadAgentAuthority({
793
+ stored,
794
+ event: this.#eventVerifiedDeadAuthority(input)
795
+ });
796
+ }
797
+ #storedVerifiedDeadAuthority(input) {
798
+ const disposition = processDisposition(input.conversation);
799
+ if (disposition?.status !== "verified_dead")
800
+ return { status: "absent" };
801
+ return validateStoredVerifiedDeadAgentAuthority({
802
+ disposition,
803
+ context: this.#verifiedDeadAuthorityContext(input)
804
+ });
805
+ }
806
+ #eventVerifiedDeadAuthority(input) {
807
+ const candidate = selectVerifiedDeadAgentEvent({
808
+ events: this.readEvents(input.logPath),
809
+ conversationId: input.conversation.conversation_id
810
+ });
811
+ if (candidate.status !== "candidate")
812
+ return candidate;
813
+ return validateVerifiedDeadAgentEventAuthority({
814
+ candidate,
815
+ context: this.#verifiedDeadAuthorityContext(input)
816
+ });
817
+ }
818
+ #verifiedDeadAuthorityContext(input) {
819
+ const { conversation } = input;
820
+ const takeover = takeoverFor(conversation);
821
+ const submission = terminalBridgeSubmission(conversation);
822
+ const session = tryLoadManagedSession(input.storeDir, sessionIdForConversation(conversation));
823
+ const binding = session?.binding;
824
+ return {
825
+ terminalControl: input.terminalControl,
826
+ conversation: {
827
+ agent: executorForConversation(conversation).kind,
828
+ conversationId: conversation.conversation_id,
829
+ sessionId: sessionIdForConversation(conversation),
830
+ turnId: turnIdForConversation(conversation),
831
+ bindingId: nonBlankString(conversation.terminal_binding_id),
832
+ bindingGeneration: Number(conversation.terminal_binding_generation)
833
+ },
834
+ ...(session ? { session: {
835
+ status: session.status,
836
+ agent: session.agent,
837
+ workspaceMatchesConversation: path.resolve(session.workspace) === path.resolve(conversation.workspace),
838
+ ...(binding ? { binding: {
839
+ terminalControl: binding.terminal_control,
840
+ pid: binding.native_process.pid,
841
+ processUuid: binding.native_process.process_uuid,
842
+ processBirth: binding.native_process.process_birth,
843
+ bindingId: binding.binding_id,
844
+ generation: binding.generation
845
+ } } : {})
846
+ } } : {}),
847
+ ...(takeover ? { takeover: {
848
+ pid: Number(takeover.terminal_agent_pid),
849
+ processUuid: nonBlankString(takeover.terminal_agent_process_uuid),
850
+ processBirth: nonBlankString(takeover.terminal_agent_process_birth),
851
+ bindingId: nonBlankString(takeover.terminal_binding_id),
852
+ bindingGeneration: Number(takeover.terminal_binding_generation),
853
+ messageId: nonBlankString(takeover.terminal_bridge_message_id)
854
+ } } : {}),
855
+ ...(submission ? { submission: {
856
+ status: nonBlankString(submission.status),
857
+ sessionId: nonBlankString(submission.session_id),
858
+ turnId: nonBlankString(submission.turn_id),
859
+ messageId: nonBlankString(submission.message_id),
860
+ bindingId: nonBlankString(submission.binding_id),
861
+ bindingGeneration: Number(submission.binding_generation)
862
+ } } : {})
863
+ };
864
+ }
865
+ ensureVerifiedDeadEvent(input) {
866
+ const evidenceId = verifiedDeadTerminalAgentProcessEvidenceId(input.proof);
867
+ const existing = this.readEvents(input.logPath).find((event) => event.event === "terminal_agent_process_verified_dead" &&
868
+ event.conversation_id === input.proof.conversation_id &&
869
+ event.evidence_id === evidenceId);
870
+ if (existing) {
871
+ const proof = isRecord(existing.proof)
872
+ ? existing.proof
873
+ : undefined;
874
+ const recordedAt = nonBlankString(existing.ts);
875
+ if (!proof || !recordedAt || !validTimestamp(recordedAt) ||
876
+ proof.observed_at !== recordedAt ||
877
+ verifiedDeadTerminalAgentProcessEvidenceId(proof) !== evidenceId) {
878
+ throw new Error(`verified-dead process event ${evidenceId} is inconsistent`);
879
+ }
880
+ return { proof, evidenceId, recordedAt };
881
+ }
882
+ appendEvent(input.logPath, {
883
+ ts: input.proof.observed_at,
884
+ conversation_id: input.proof.conversation_id,
885
+ event: "terminal_agent_process_verified_dead",
886
+ evidence_id: evidenceId,
887
+ status: "verified_dead",
888
+ proof: input.proof,
889
+ action: input.action
890
+ });
891
+ return {
892
+ proof: input.proof,
893
+ evidenceId,
894
+ recordedAt: input.proof.observed_at
895
+ };
896
+ }
897
+ #ensureVerifiedDeadStalledEvent(input) {
898
+ const existing = this.readEvents(input.logPath).find((event) => event.event === "conversation_stalled" &&
899
+ event.conversation_id === input.proof.conversation_id &&
900
+ event.evidence_id === input.evidenceId);
901
+ if (existing) {
902
+ if (existing.ts !== input.proof.observed_at ||
903
+ existing.reason !== input.reason ||
904
+ existing.disposition !== "verified_dead_agent_process" ||
905
+ existing.completion_observation !== input.completionObservation) {
906
+ throw new Error(`verified-dead stalled event ${input.evidenceId} is inconsistent`);
907
+ }
908
+ return;
909
+ }
910
+ appendEvent(input.logPath, {
911
+ ts: input.proof.observed_at,
912
+ conversation_id: input.proof.conversation_id,
913
+ event: "conversation_stalled",
914
+ evidence_id: input.evidenceId,
915
+ status: "stalled",
916
+ reason: input.reason,
917
+ terminal_bridge: true,
918
+ terminal_control: input.terminalControl,
919
+ disposition: "verified_dead_agent_process",
920
+ completion_observation: input.completionObservation
921
+ });
922
+ }
923
+ #verifiedDeadStalledEventDecision(input) {
924
+ const candidates = this.readEvents(input.logPath).filter((event) => event.event === "conversation_stalled" &&
925
+ event.conversation_id === input.proof.conversation_id &&
926
+ event.evidence_id === input.evidenceId);
927
+ if (candidates.length === 0)
928
+ return { status: "absent" };
929
+ if (candidates.length !== 1)
930
+ return {
931
+ status: "invalid",
932
+ reason: "the verified-dead stalled event history is ambiguous"
933
+ };
934
+ const event = candidates[0];
935
+ const observation = nonBlankString(event.completion_observation);
936
+ if (event.ts !== input.proof.observed_at || event.status !== "stalled" ||
937
+ event.reason !== input.reason ||
938
+ event.disposition !== "verified_dead_agent_process" ||
939
+ !["absent", "unverifiable"].includes(observation ?? "")) {
940
+ return {
941
+ status: "invalid",
942
+ reason: "the verified-dead stalled event decision is inconsistent"
943
+ };
944
+ }
945
+ return {
946
+ status: "valid",
947
+ completionObservation: observation
948
+ };
949
+ }
950
+ ensureVerifiedDeadClosedEvent(input) {
951
+ const closedAt = required(nonBlankString(input.conversation.closed_at), "verified-dead closed Turn has no closed_at timestamp");
952
+ const reason = required(nonBlankString(input.conversation.close_reason), "verified-dead closed Turn has no close reason");
953
+ if (input.conversation.status !== "closed" || !validTimestamp(closedAt)) {
954
+ throw new Error("verified-dead closed Turn state is inconsistent");
955
+ }
956
+ const existing = this.readEvents(input.logPath).find((event) => event.event === "conversation_closed" &&
957
+ event.conversation_id === input.conversation.conversation_id &&
958
+ event.evidence_id === input.evidenceId);
959
+ if (existing) {
960
+ if (existing.ts !== closedAt || existing.status !== "closed" ||
961
+ existing.reason !== reason ||
962
+ existing.disposition !== "verified_dead_agent_process") {
963
+ throw new Error(`verified-dead close event ${input.evidenceId} is inconsistent`);
964
+ }
965
+ return;
966
+ }
967
+ appendEvent(input.logPath, {
968
+ ts: closedAt,
969
+ conversation_id: input.conversation.conversation_id,
970
+ event: "conversation_closed",
971
+ evidence_id: input.evidenceId,
972
+ status: "closed",
973
+ reason,
974
+ disposition: "verified_dead_agent_process"
975
+ });
976
+ }
977
+ #basicVerifiedDeadDispatchExact(context) {
978
+ const scope = this.#activeScope(context.scope);
979
+ const ledger = this.#dependencies.repository.load(context.terminalControl);
980
+ return Boolean(ledger && nonBlankString(ledger.status) === "agent_accepted" &&
981
+ sameCanonicalStatePath(ledger.state_path, scope.statePath) &&
982
+ path.resolve(nonBlankString(ledger.store_dir) ?? "") ===
983
+ path.resolve(scope.storeDir) &&
984
+ path.resolve(nonBlankString(ledger.event_log_path) ?? "") ===
985
+ path.resolve(scope.logPath) &&
986
+ nonBlankString(ledger.conversation_id) ===
987
+ context.conversation.conversation_id &&
988
+ nonBlankString(ledger.session_id) ===
989
+ sessionIdForConversation(context.conversation) &&
990
+ nonBlankString(ledger.turn_id) ===
991
+ turnIdForConversation(context.conversation) &&
992
+ nonBlankString(ledger.message_id) === context.messageId &&
993
+ this.#dependencies.repository.matchesControl(ledger, context.terminalControl, { requireProcessAnchor: true }) &&
994
+ this.#dependencies.repository.processAnchor(ledger) ===
995
+ terminalEndpointFromControlRef(context.terminalControl).processAnchorPid &&
996
+ nonBlankString(ledger.binding_id) ===
997
+ nonBlankString(context.conversation.terminal_binding_id) &&
998
+ Number(ledger.binding_generation) ===
999
+ Number(context.conversation.terminal_binding_generation));
1000
+ }
1001
+ assertVerifiedDeadDispatch(input) {
1002
+ this.#dependencies.authority.assertTurnBindingCurrent(input.conversation, "resolve a verified-dead agent dispatch for");
1003
+ const takeover = takeoverFor(input.conversation);
1004
+ const submission = terminalBridgeSubmission(input.conversation);
1005
+ const expected = acceptedDispatchExpectation(input, takeover, submission);
1006
+ if (!stateDispatchCoreAuthorityExact(input, submission, expected)) {
1007
+ throw stateDispatchAuthorityError(input.conversation.conversation_id);
1008
+ }
1009
+ const conversationStoreDir = this.#dependencies.authority.storeDirForConversation(input.conversation);
1010
+ if (path.resolve(conversationStoreDir ?? "") !== path.resolve(input.storeDir)) {
1011
+ throw stateDispatchAuthorityError(input.conversation.conversation_id);
1012
+ }
1013
+ const ledger = this.#dependencies.repository.load(input.terminalControl);
1014
+ if (!ledgerDispatchCoreAuthorityExact(input, ledger, expected, this.#dependencies.repository)) {
1015
+ throw ledgerDispatchAuthorityError(input.conversation.conversation_id);
1016
+ }
1017
+ if (!stateDispatchReceiptAuthorityExact(input, submission)) {
1018
+ throw stateDispatchAuthorityError(input.conversation.conversation_id);
1019
+ }
1020
+ const ledgerReceipt = ledgerDispatchReceiptAuthority(input, required(ledger, "verified-dead dispatch ledger is unavailable"), expected, this.#dependencies.repository);
1021
+ if (!ledgerReceipt.exact) {
1022
+ throw ledgerDispatchAuthorityError(input.conversation.conversation_id);
1023
+ }
1024
+ const acceptance = acceptanceAgreement(input.conversation, expected.requestText, submission?.acceptance_evidence, ledger?.acceptance_evidence, ledgerReceipt.receipt?.acceptance_evidence);
1025
+ const result = assertVerifiedDeadDispatchAuthorityFacts({
1026
+ conversationId: input.conversation.conversation_id,
1027
+ stateAuthorityExact: true,
1028
+ ledgerAuthorityExact: true,
1029
+ ledgerResolved: ledger?.status === "resolved",
1030
+ acceptance
1031
+ });
1032
+ return {
1033
+ ledger: required(ledger, "verified-dead dispatch ledger is unavailable"),
1034
+ resolved: result.resolved
1035
+ };
1036
+ }
1037
+ resolveVerifiedDeadDispatch(input) {
1038
+ const authority = this.assertVerifiedDeadDispatch(input);
1039
+ if (authority.resolved)
1040
+ return true;
1041
+ this.#dependencies.repository.save(input.terminalControl, {
1042
+ ...authority.ledger,
1043
+ status: "resolved",
1044
+ resolved_at: cliNow().toISOString(),
1045
+ reason: input.reason
1046
+ });
1047
+ if (!this.assertVerifiedDeadDispatch(input).resolved) {
1048
+ throw new Error(`verified-dead Turn ${input.conversation.conversation_id} dispatch did not resolve`);
1049
+ }
1050
+ return true;
1051
+ }
1052
+ readEvents(logPath) {
1053
+ try {
1054
+ return readNdjsonLog(logPath);
1055
+ }
1056
+ catch (error) {
1057
+ if (isRecord(error) && error.code === "ENOENT")
1058
+ return [];
1059
+ throw error;
1060
+ }
1061
+ }
1062
+ prepareCompletion(input) {
1063
+ const storeDir = pathsForConversationDir(path.dirname(input.statePath)).storeDir;
1064
+ const releaseTerminal = this.#dependencies.repository.acquire(storeDir, input.terminalControl, { timeoutMs: 30000 });
1065
+ try {
1066
+ return withStoreWriterLease(storeDir, () => this.#dependencies.completion.prepare(input));
1067
+ }
1068
+ finally {
1069
+ releaseTerminal();
1070
+ }
1071
+ }
1072
+ #activeScope(scope) {
1073
+ if (!(scope instanceof ConcreteRecoveryScope) || !scope.active) {
1074
+ throw new TypeError("terminal dispatch recovery scope is inactive or foreign");
1075
+ }
1076
+ return scope;
1077
+ }
1078
+ #assertStateHeld(scope) {
1079
+ if (scope.stateReleased) {
1080
+ throw new TypeError("terminal dispatch recovery state scope is released");
1081
+ }
1082
+ }
1083
+ #releaseStateClaim(scope) {
1084
+ if (scope.stateReleased)
1085
+ return;
1086
+ scope.releaseState();
1087
+ scope.stateReleased = true;
1088
+ }
1089
+ #applySubmissionMutation(mutation) {
1090
+ const takeover = takeoverFor(mutation.conversation);
1091
+ return applyTerminalBridgeSubmission(mutation, {
1092
+ dispatcherPid: cliPid(),
1093
+ storeDir: this.#dependencies.authority.storeDirForConversation(mutation.conversation),
1094
+ terminalControl: this.#dependencies.authority.terminalControl(takeover)
1095
+ });
1096
+ }
1097
+ }
1098
+ function submissionFacts(value) {
1099
+ const submission = isRecord(value) ? value : undefined;
1100
+ return {
1101
+ status: nonBlankString(submission?.status),
1102
+ messageId: nonBlankString(submission?.message_id),
1103
+ preparedAt: nonBlankString(submission?.prepared_at),
1104
+ textInjectedAt: nonBlankString(submission?.text_injected_at),
1105
+ enterDispatchedAt: nonBlankString(submission?.enter_dispatched_at),
1106
+ submittedAt: nonBlankString(submission?.submitted_at),
1107
+ agentAcceptedAt: nonBlankString(submission?.agent_accepted_at),
1108
+ notAcceptedAt: nonBlankString(submission?.not_accepted_at),
1109
+ uncertainAt: nonBlankString(submission?.uncertain_at),
1110
+ abortedAt: nonBlankString(submission?.aborted_at),
1111
+ lastProvenStage: nonBlankString(submission?.last_proven_stage),
1112
+ acceptanceEvidence: submission?.acceptance_evidence
1113
+ };
1114
+ }
1115
+ function bindingCompatible(ledger, binding) {
1116
+ for (const key of [
1117
+ "binding_id",
1118
+ "binding_generation",
1119
+ "native_thread_id",
1120
+ "store_dir"
1121
+ ]) {
1122
+ if (ledger[key] !== undefined && binding[key] !== undefined &&
1123
+ String(ledger[key]) !== String(binding[key]))
1124
+ return false;
1125
+ }
1126
+ return true;
1127
+ }
1128
+ function acceptanceProjection(conversation, requestText, ledgerStatus, submission, ledger) {
1129
+ let state;
1130
+ if (submission?.status === "agent_accepted") {
1131
+ try {
1132
+ state = terminalAcceptanceEvidenceForConversation(conversation, requestText, submission.acceptance_evidence);
1133
+ }
1134
+ catch {
1135
+ state = undefined;
1136
+ }
1137
+ }
1138
+ let durable;
1139
+ let ledgerError;
1140
+ if (ledgerStatus === "agent_accepted") {
1141
+ try {
1142
+ durable = terminalAcceptanceEvidenceForConversation(conversation, requestText, ledger.acceptance_evidence);
1143
+ }
1144
+ catch (error) {
1145
+ ledgerError = error instanceof Error ? error.message : String(error);
1146
+ }
1147
+ }
1148
+ return { state, ledger: durable, ledgerError };
1149
+ }
1150
+ function acceptedTurnCanBeStalled(storeDir, conversation) {
1151
+ const takeover = takeoverFor(conversation);
1152
+ const submission = terminalBridgeSubmission(conversation);
1153
+ const messageId = nonBlankString(takeover?.terminal_bridge_message_id);
1154
+ const transferId = nonBlankString(takeover?.deferred_foreground_transfer_id);
1155
+ const base = {
1156
+ conversationStatus: conversation.status,
1157
+ terminalBridge: takeover?.terminal_bridge === true,
1158
+ messageId,
1159
+ submissionStatus: nonBlankString(submission?.status),
1160
+ submissionMessageId: nonBlankString(submission?.message_id),
1161
+ deferredTransferId: transferId
1162
+ };
1163
+ const decision = decideAcceptedTurnDeadAgentStall(base);
1164
+ if (decision.status !== "requires_deferred_transfer") {
1165
+ return decision.status === "applicable";
1166
+ }
1167
+ const transfer = loadDeferredForegroundTransfer(storeDir, decision.transferId);
1168
+ return decideAcceptedTurnDeadAgentStall({
1169
+ ...base,
1170
+ deferredTransferStatus: transfer.status
1171
+ }).status === "applicable";
1172
+ }
1173
+ function unchangedVerifiedDead(conversation, reason) {
1174
+ return { stalled: false, conversation, reason };
1175
+ }
1176
+ function localNotApplicable() {
1177
+ return {
1178
+ handled: false,
1179
+ recovered: false,
1180
+ reason: "local_completion_not_applicable"
1181
+ };
1182
+ }
1183
+ function takeoverFor(conversation) {
1184
+ return isRecord(conversation.native_session_takeover)
1185
+ ? conversation.native_session_takeover
1186
+ : undefined;
1187
+ }
1188
+ function completionClaim(conversation) {
1189
+ const takeover = takeoverFor(conversation);
1190
+ return isRecord(takeover?.terminal_bridge_completion_claim)
1191
+ ? takeover.terminal_bridge_completion_claim
1192
+ : undefined;
1193
+ }
1194
+ function processDisposition(conversation) {
1195
+ return isRecord(conversation.terminal_agent_process_disposition)
1196
+ ? conversation.terminal_agent_process_disposition
1197
+ : undefined;
1198
+ }
1199
+ function validTimestamp(value) {
1200
+ return Number.isFinite(Date.parse(value));
1201
+ }
1202
+ function required(value, message) {
1203
+ if (value === undefined || value === null || value === "") {
1204
+ throw new Error(message);
1205
+ }
1206
+ return value;
1207
+ }
1208
+ function acceptedDispatchExpectation(input, takeover, submission) {
1209
+ const conversation = input.conversation;
1210
+ return {
1211
+ requestText: String(takeover?.terminal_bridge_request_text ?? conversation.user_request ?? ""),
1212
+ sessionId: sessionIdForConversation(conversation),
1213
+ turnId: turnIdForConversation(conversation),
1214
+ bindingId: nonBlankString(conversation.terminal_binding_id),
1215
+ bindingGeneration: Number(conversation.terminal_binding_generation),
1216
+ nativeThreadId: nonBlankString(conversation.native_thread_id) ??
1217
+ nonBlankString(takeover?.terminal_agent_session_id),
1218
+ endpointAnchor: terminalEndpointFromControlRef(input.terminalControl).processAnchorPid,
1219
+ submission
1220
+ };
1221
+ }
1222
+ function stateDispatchCoreAuthorityExact(input, submission, expected) {
1223
+ const requestHash = terminalBridgeRequestFingerprint(expected.requestText);
1224
+ const takeover = takeoverFor(input.conversation);
1225
+ return Boolean(input.expectedMessageId &&
1226
+ nonBlankString(takeover?.terminal_bridge_message_id) === input.expectedMessageId &&
1227
+ submission?.status === "agent_accepted" &&
1228
+ nonBlankString(submission.message_id) === input.expectedMessageId &&
1229
+ nonBlankString(submission.session_id) === expected.sessionId &&
1230
+ nonBlankString(submission.turn_id) === expected.turnId &&
1231
+ nonBlankString(submission.binding_id) === expected.bindingId &&
1232
+ Number(submission.binding_generation) === expected.bindingGeneration &&
1233
+ requestHash && nonBlankString(submission.request_hash) === requestHash &&
1234
+ expected.bindingId && Number.isSafeInteger(expected.bindingGeneration) &&
1235
+ expected.bindingGeneration >= 1 && expected.nativeThreadId &&
1236
+ Number.isSafeInteger(expected.endpointAnchor) && Number(expected.endpointAnchor) >= 1 &&
1237
+ sameCanonicalStatePath(input.conversation.state_path, input.statePath) &&
1238
+ path.resolve(nonBlankString(input.conversation.event_log_path) ?? "") ===
1239
+ path.resolve(input.logPath));
1240
+ }
1241
+ function stateDispatchReceiptAuthorityExact(input, submission) {
1242
+ const takeover = takeoverFor(input.conversation);
1243
+ const rawHistory = takeover?.terminal_bridge_submission_receipts;
1244
+ const rawReceipts = Array.isArray(rawHistory)
1245
+ ? rawHistory.filter((receipt) => isRecord(receipt) &&
1246
+ nonBlankString(receipt.message_id) === input.expectedMessageId)
1247
+ : [];
1248
+ const validatedReceipts = terminalBridgeSubmissionReceipts(input.conversation).filter((receipt) => nonBlankString(receipt.message_id) === input.expectedMessageId);
1249
+ return Boolean(Array.isArray(rawHistory) && rawReceipts.length === 1 &&
1250
+ validatedReceipts.length === 1 &&
1251
+ canonicalJson(rawReceipts[0]) === canonicalJson(submission) &&
1252
+ canonicalJson(validatedReceipts[0]) === canonicalJson(submission));
1253
+ }
1254
+ function ledgerDispatchCoreAuthorityExact(input, ledger, expected, repository) {
1255
+ return Boolean(ledger && !terminalDispatchLedgerLooksLifecycle(ledger) &&
1256
+ ["agent_accepted", "resolved"].includes(String(ledger.status)) &&
1257
+ ledgerDispatchRecordMatches(input, ledger, expected, repository, ledger.status === "resolved" ? "resolved" : "agent_accepted") &&
1258
+ (ledger.status !== "resolved" ||
1259
+ validTimestamp(nonBlankString(ledger.resolved_at) ?? "")));
1260
+ }
1261
+ function ledgerDispatchReceiptAuthority(input, ledger, expected, repository) {
1262
+ const rawHistory = ledger?.terminal_submission_receipts;
1263
+ const rawReceipts = Array.isArray(rawHistory)
1264
+ ? rawHistory.filter((receipt) => isRecord(receipt) &&
1265
+ nonBlankString(receipt.message_id) === input.expectedMessageId)
1266
+ : [];
1267
+ const validated = terminalDispatchReceiptHistory(ledger).filter((receipt) => nonBlankString(receipt.message_id) === input.expectedMessageId);
1268
+ const receipt = rawReceipts[0];
1269
+ return {
1270
+ exact: Boolean(rawReceipts.length === 1 && validated.length === 1 &&
1271
+ canonicalJson(rawReceipts[0]) === canonicalJson(validated[0]) &&
1272
+ ledgerDispatchRecordMatches(input, receipt, expected, repository, "agent_accepted")),
1273
+ receipt
1274
+ };
1275
+ }
1276
+ function ledgerDispatchRecordMatches(input, record, expected, repository, status) {
1277
+ const requestHash = terminalBridgeRequestFingerprint(expected.requestText);
1278
+ return Boolean(record &&
1279
+ record.status === status &&
1280
+ nonBlankString(record.generation_id) === input.expectedMessageId &&
1281
+ nonBlankString(record.conversation_id) === input.conversation.conversation_id &&
1282
+ nonBlankString(record.session_id) === expected.sessionId &&
1283
+ nonBlankString(record.turn_id) === expected.turnId &&
1284
+ nonBlankString(record.message_id) === input.expectedMessageId &&
1285
+ nonBlankString(record.request_hash) === requestHash &&
1286
+ sameCanonicalStatePath(record.state_path, input.statePath) &&
1287
+ path.resolve(nonBlankString(record.store_dir) ?? "") === path.resolve(input.storeDir) &&
1288
+ path.resolve(nonBlankString(record.event_log_path) ?? "") === path.resolve(input.logPath) &&
1289
+ nonBlankString(record.binding_id) === expected.bindingId &&
1290
+ Number(record.binding_generation) === expected.bindingGeneration &&
1291
+ nonBlankString(record.native_thread_id) === expected.nativeThreadId &&
1292
+ nonBlankString(record.executor_kind) === executorForConversation(input.conversation).kind &&
1293
+ (nonBlankString(record.openclaw_session) ?? undefined) ===
1294
+ (nonBlankString(input.conversation.openclaw_session) ?? undefined) &&
1295
+ Boolean(record.callback_expected) === Boolean(input.conversation.gateway_method) &&
1296
+ (nonBlankString(record.message_type) ?? undefined) ===
1297
+ (nonBlankString(expected.submission?.message_type) ?? undefined) &&
1298
+ (nonBlankString(record.message_body_hash) ?? undefined) ===
1299
+ (nonBlankString(expected.submission?.message_body_hash) ?? undefined) &&
1300
+ isRecord(record.terminal_endpoint) &&
1301
+ repository.matchesControl(record, input.terminalControl, {
1302
+ requireProcessAnchor: true
1303
+ }) &&
1304
+ repository.processAnchor(record) === expected.endpointAnchor);
1305
+ }
1306
+ function stateDispatchAuthorityError(conversationId) {
1307
+ return new Error(`verified-dead Turn ${conversationId} has no exact accepted submission authority`);
1308
+ }
1309
+ function ledgerDispatchAuthorityError(conversationId) {
1310
+ return new Error(`verified-dead Turn ${conversationId} no longer owns one exact terminal dispatch receipt`);
1311
+ }
1312
+ function acceptanceAgreement(conversation, requestText, stateValue, ledgerValue, receiptValue) {
1313
+ try {
1314
+ const state = terminalAcceptanceEvidenceForConversation(conversation, requestText, stateValue);
1315
+ const ledger = terminalAcceptanceEvidenceForConversation(conversation, requestText, ledgerValue);
1316
+ const receipt = terminalAcceptanceEvidenceForConversation(conversation, requestText, receiptValue);
1317
+ return {
1318
+ status: "valid",
1319
+ allEqual: canonicalJson(state) === canonicalJson(ledger) &&
1320
+ canonicalJson(state) === canonicalJson(receipt)
1321
+ };
1322
+ }
1323
+ catch (error) {
1324
+ return {
1325
+ status: "invalid",
1326
+ reason: error instanceof Error ? error.message : String(error)
1327
+ };
1328
+ }
1329
+ }
1330
+ //# sourceMappingURL=terminal-dispatch-recovery-cli-adapter.js.map