@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,1093 @@
1
+ import { isExactNativeThreadId, managedSessionRevision, nativeThreadCommandFingerprint, nativeThreadTransitionRevision, terminalBindingFrom } from "./managed-session.js";
2
+ import { classifyCodexLifecyclePostcondition } from "./native-thread-lifecycle-policy.js";
3
+ import { terminalDispatchLedgerLooksLifecycle } from "./terminal-dispatch-ledger-codec.js";
4
+ import { terminalNativeIdentityFence as codexIdentityFence } from "./terminal-binding-authority.js";
5
+ import { nonBlankString as stringValue } from "./value-guards.js";
6
+ const HUMAN_OBSERVED_HANDOFF_FINGERPRINT = nativeThreadCommandFingerprint("adopt_external_thread:human_observed:no_terminal_input:v1");
7
+ function bindRecoveryPorts(scopes, resources, ports) {
8
+ const authority = ports.authority.bind(scopes, resources);
9
+ const repository = ports.persistence;
10
+ const terminal = ports.terminal;
11
+ return {
12
+ ...authority,
13
+ ports: {
14
+ repository: {
15
+ listNativeThreadTransitions: () => repository.listNativeThreadTransitions(scopes, resources),
16
+ loadManagedSession: (id) => repository.loadManagedSession(scopes, resources, id),
17
+ tryLoadManagedSession: (id) => repository.tryLoadManagedSession(scopes, resources, id),
18
+ loadNativeThreadTransition: (id) => repository.loadNativeThreadTransition(scopes, resources, id),
19
+ saveManagedSession: (state, expectation) => repository.saveManagedSession(scopes, resources, state, expectation),
20
+ saveNativeThreadTransition: (transition, expectation) => repository.saveNativeThreadTransition(scopes, resources, transition, expectation),
21
+ commitVerified: (transition, at) => repository.commitVerified(scopes, resources, transition, at)
22
+ },
23
+ ledger: {
24
+ load: () => repository.loadLedger(scopes, resources),
25
+ build: (transition, phase) => repository.buildLedger(scopes, resources, transition, phase),
26
+ save: (transition, phase, expectation) => repository.saveLedger(scopes, resources, transition, phase, expectation),
27
+ saveFailClosed: (ledger, transitionId, now, reason) => repository.saveFailClosedLedger(scopes, resources, ledger, transitionId, now, reason)
28
+ },
29
+ terminal: {
30
+ sameTerminalIncarnation: (right) => terminal.sameTerminalIncarnation(scopes, resources, right),
31
+ recoverDeferred: () => terminal.recoverDeferred(scopes, resources),
32
+ aliasMatches: (storedId, storedControl, currentId) => terminal.aliasMatches(scopes, resources, storedId, storedControl, currentId),
33
+ workspaceMatches: (expected) => terminal.workspaceMatches(scopes, resources, expected),
34
+ resolveIdentity: (preferred, companions) => terminal.resolveIdentity(scopes, resources, preferred, companions),
35
+ observeExternalHandoff: (source, resolved) => terminal.observeExternalHandoff(scopes, resources, source, resolved),
36
+ assertExclusive: (nativeThreadId, excluded) => terminal.assertExclusive(scopes, resources, nativeThreadId, excluded),
37
+ assertTargetExclusive: (transition) => terminal.assertTargetExclusive(scopes, resources, transition),
38
+ exactIdentity: (identity) => terminal.exactIdentity(scopes, resources, identity),
39
+ isProcessAlive: (pid) => terminal.isProcessAlive(scopes, resources, pid),
40
+ recordedStoreMatches: (recorded) => terminal.recordedStoreMatches(scopes, resources, recorded),
41
+ recordMatchesControl: (ledger) => terminal.recordMatchesControl(scopes, resources, ledger),
42
+ runningVersion: () => terminal.runningVersion(scopes, resources),
43
+ probeThreadLifecycle: (version) => terminal.probeThreadLifecycle(scopes, resources, version),
44
+ planThreadLifecycle: (operation, capability) => terminal.planThreadLifecycle(scopes, resources, operation, capability),
45
+ observeThreadLifecycle: (request) => terminal.observeThreadLifecycle(scopes, resources, request),
46
+ prepareProbeBridge: () => terminal.prepareProbeBridge(scopes, resources),
47
+ status: (context, scrollbackLines) => terminal.status(scopes, resources, context, scrollbackLines),
48
+ clearInputLine: (context) => terminal.clearInputLine(scopes, resources, context),
49
+ submitCodexStatusProbe: (version, context) => terminal.submitCodexStatusProbe(scopes, resources, version, context),
50
+ assertResumedCodexCandidate: (identity, expected) => terminal.assertResumedCodexCandidate(scopes, resources, identity, expected),
51
+ codexKnownRoots: (transition) => terminal.codexKnownRoots(scopes, resources, transition),
52
+ codexCompanionsExcludingPreferred: (roots, preferred) => terminal.codexCompanionsExcludingPreferred(scopes, resources, roots, preferred),
53
+ codexProcessBirth: () => terminal.codexProcessBirth(scopes, resources),
54
+ nativeIdentityMatches: (identity, expected) => terminal.nativeIdentityMatches(scopes, resources, identity, expected)
55
+ },
56
+ runtime: ports.runtime
57
+ }
58
+ };
59
+ }
60
+ function saveLifecycleSession(ports, state, expectedRevision) {
61
+ return ports.repository.saveManagedSession(state, { expectedRevision });
62
+ }
63
+ function saveLifecycleTransition(ports, transition, expectedRevision) {
64
+ return ports.repository.saveNativeThreadTransition(transition, { expectedRevision });
65
+ }
66
+ export async function recoverLifecycleFenceBeforeMutation(request, scopes, resources, recoveryPorts) {
67
+ const bound = bindRecoveryPorts(scopes, resources, recoveryPorts);
68
+ return recoverLifecycleFenceBeforeMutationBound(request.terminal, bound.terminalControl, bound.ports);
69
+ }
70
+ async function recoverLifecycleFenceBeforeMutationBound(terminal, terminalControl, ports) {
71
+ if (terminal.terminalControl !== terminalControl) {
72
+ terminal = { ...terminal, terminalControl };
73
+ }
74
+ await ports.terminal.recoverDeferred();
75
+ let ledger = ports.ledger.load();
76
+ if (!ledger || ledger.status === "resolved") {
77
+ ledger = rebuildObservedHandoffLedgerFromTransition({
78
+ terminal,
79
+ previousLedger: ledger,
80
+ ports
81
+ });
82
+ if (!ledger) {
83
+ return;
84
+ }
85
+ }
86
+ if (!terminalDispatchLedgerLooksLifecycle(ledger)) {
87
+ return;
88
+ }
89
+ if (ledger.kind !== "lifecycle") {
90
+ throw new Error(`terminal ${terminal.terminalControl.target} has a malformed lifecycle ` +
91
+ "dispatch fence; no Session or Turn was created and manual recovery is required");
92
+ }
93
+ const recovered = await reconcileLifecycleDispatchLedgerBound({
94
+ terminalControl,
95
+ terminal,
96
+ ledger
97
+ }, ports);
98
+ if (recovered.status !== "resolved") {
99
+ throw new Error(`terminal ${terminal.terminalControl.target} has unresolved lifecycle ` +
100
+ `transition ${stringValue(recovered.transition_id) ?? "with invalid identity"} ` +
101
+ `(${String(recovered.status)}: ` +
102
+ `${stringValue(recovered.reason) ?? "recovery evidence unavailable"}); ` +
103
+ "refresh AKK list and use its exact " +
104
+ "expected-transition-id recovery action");
105
+ }
106
+ }
107
+ function rebuildObservedHandoffLedgerFromTransition({ terminal, previousLedger, ports }) {
108
+ const candidates = ports.repository.listNativeThreadTransitions().filter((transition) => transition.operation === "adopt_external_thread" &&
109
+ transition.origin === "human_observed" &&
110
+ transition.terminal_input_sent === false &&
111
+ ["prepared", "verified"].includes(transition.status) &&
112
+ transition.agent === terminal.agent &&
113
+ transition.before_binding?.native_process.pid === terminal.pid &&
114
+ ports.terminal.aliasMatches(transition.terminal_id, transition.before_binding?.terminal_control, terminal.conversationId) &&
115
+ ports.terminal.workspaceMatches(transition.workspace));
116
+ if (candidates.length === 0) {
117
+ return undefined;
118
+ }
119
+ if (candidates.length !== 1) {
120
+ throw new Error(`terminal ${terminal.terminalControl.target} has multiple unresolved ` +
121
+ "human-observed handoff transitions; refusing to infer one ledger owner");
122
+ }
123
+ const transition = candidates[0];
124
+ if (previousLedger?.status === "resolved" &&
125
+ stringValue(previousLedger.transition_id) === transition.transition_id) {
126
+ throw new Error(`human-observed handoff transition ${transition.transition_id} is ` +
127
+ "unresolved but its terminal ledger is already resolved");
128
+ }
129
+ const rebuilt = ports.ledger.build(transition, {
130
+ phase: "rebuild",
131
+ control: terminal.terminalControl,
132
+ previous: previousLedger
133
+ });
134
+ // Validate the complete transition/terminal/adapter relationship before
135
+ // replacing a missing or older resolved ledger. Rebuilding a fence is a
136
+ // Store-side recovery operation only and never sends terminal input.
137
+ assertLifecycleLedgerMatchesTransition({
138
+ terminal,
139
+ ledger: rebuilt,
140
+ transition,
141
+ ports
142
+ });
143
+ ports.ledger.save(transition, {
144
+ phase: "rebuild",
145
+ control: terminal.terminalControl,
146
+ previous: previousLedger
147
+ }, { expectedTransitionId: null });
148
+ return ports.ledger.load();
149
+ }
150
+ async function recoverPreparedObservedHandoff({ terminal, ledger, transition, now, ports }) {
151
+ if (transition.operation !== "adopt_external_thread" ||
152
+ transition.status !== "prepared" ||
153
+ ledger.status !== "prepared" ||
154
+ !transition.source_session_id ||
155
+ transition.source_expected_revision === undefined ||
156
+ !transition.before_binding ||
157
+ !transition.target_native_thread_id) {
158
+ return transition;
159
+ }
160
+ let source = ports.repository.loadManagedSession(transition.source_session_id);
161
+ if (source.status === "bound" &&
162
+ source.revision === transition.source_expected_revision &&
163
+ JSON.stringify(source.binding) === JSON.stringify(transition.before_binding)) {
164
+ source = saveLifecycleSession(ports, {
165
+ ...source,
166
+ status: "transitioning",
167
+ last_transition_id: transition.transition_id,
168
+ updated_at: now
169
+ }, managedSessionRevision(source));
170
+ }
171
+ else if (source.status !== "transitioning" ||
172
+ source.last_transition_id !== transition.transition_id ||
173
+ source.revision !== transition.source_expected_revision + 1 ||
174
+ JSON.stringify(source.binding) !== JSON.stringify(transition.before_binding)) {
175
+ throw new Error("human-observed handoff source changed before recovery");
176
+ }
177
+ const before = transition.before_binding;
178
+ const resolved = await ports.terminal.resolveIdentity(transition.target_native_thread_id, { primary: codexIdentityFence({
179
+ sessionId: transition.before_native_thread_id,
180
+ processUuid: transition.before_process_uuid,
181
+ processBirth: transition.before_process_birth,
182
+ rollout: transition.before_process_rollout,
183
+ evidence: before.native_process.evidence
184
+ }), additional: [] });
185
+ const observed = await ports.terminal.observeExternalHandoff(source, resolved);
186
+ const liveId = observed?.sessionId.toLowerCase();
187
+ if (liveId === transition.before_native_thread_id.toLowerCase()) {
188
+ transition = saveLifecycleTransition(ports, {
189
+ ...transition,
190
+ status: "aborted",
191
+ aborted_at: now,
192
+ error: "recovery observed the exact source native thread"
193
+ }, nativeThreadTransitionRevision(transition));
194
+ restorePreparedLifecycleSource(transition, now, ports);
195
+ ports.ledger.save(transition, {
196
+ phase: "resolved",
197
+ at: now,
198
+ reason: "human-observed handoff rolled back to its exact source"
199
+ }, { expectedTransitionId: transition.transition_id });
200
+ return transition;
201
+ }
202
+ if (!observed ||
203
+ liveId !== transition.target_native_thread_id.toLowerCase()) {
204
+ throw new Error("human-observed handoff recovery found neither its exact source nor target");
205
+ }
206
+ const target = ports.repository.tryLoadManagedSession(transition.target_session_id);
207
+ if ((target?.revision ?? null) !== transition.target_expected_revision) {
208
+ throw new Error("human-observed handoff target changed before recovery");
209
+ }
210
+ await ports.terminal.assertExclusive(transition.target_native_thread_id, target?.session_id);
211
+ const exact = ports.terminal.exactIdentity(observed);
212
+ const afterBinding = terminalBindingFrom({
213
+ terminalId: terminal.conversationId,
214
+ terminalControl: terminal.terminalControl,
215
+ pid: terminal.pid,
216
+ nativeThreadId: transition.target_native_thread_id,
217
+ processUuid: exact.processUuid,
218
+ processBirth: exact.processBirth,
219
+ rollout: exact.rollout,
220
+ evidence: `${exact.evidence}+human_observed_recovery`,
221
+ generation: (target?.binding?.generation ?? 0) + 1,
222
+ now: new Date(now)
223
+ });
224
+ transition = saveLifecycleTransition(ports, {
225
+ ...transition,
226
+ status: "verified",
227
+ verified_at: now,
228
+ after_binding: afterBinding
229
+ }, nativeThreadTransitionRevision(transition));
230
+ ports.ledger.save(transition, {
231
+ phase: "verified",
232
+ binding: before
233
+ }, {
234
+ expectedTransitionId: transition.transition_id,
235
+ expectedStatus: "prepared"
236
+ });
237
+ return transition;
238
+ }
239
+ export async function reconcileLifecycleDispatchLedger(request, scopes, resources, recoveryPorts) {
240
+ const bound = bindRecoveryPorts(scopes, resources, recoveryPorts);
241
+ return reconcileLifecycleDispatchLedgerBound({
242
+ terminalControl: bound.terminalControl,
243
+ terminal: request.terminal,
244
+ ledger: request.ledger,
245
+ authority: request.authority
246
+ }, bound.ports);
247
+ }
248
+ async function reconcileLifecycleDispatchLedgerBound(request, ports) {
249
+ const { terminalControl, ledger: initialLedger, authority = { kind: "automatic" } } = request;
250
+ let { terminal } = request;
251
+ let ledger = initialLedger;
252
+ if (terminal.terminalControl !== terminalControl)
253
+ terminal = { ...terminal, terminalControl };
254
+ if (ledger.status === "resolved") {
255
+ return ledger;
256
+ }
257
+ const dispatcherPid = Number(ledger.dispatcher_pid);
258
+ if (authority.kind === "automatic" &&
259
+ Number.isSafeInteger(dispatcherPid) &&
260
+ dispatcherPid > 1 &&
261
+ ports.terminal.isProcessAlive(dispatcherPid)) {
262
+ return ledger;
263
+ }
264
+ const recordedStoreDir = stringValue(ledger.store_dir);
265
+ const transitionId = stringValue(ledger.transition_id);
266
+ if (!recordedStoreDir ||
267
+ !transitionId ||
268
+ stringValue(ledger.generation_id) !== transitionId ||
269
+ (authority.kind === "manual" &&
270
+ authority.expectedTransitionId !== transitionId)) {
271
+ return ledger;
272
+ }
273
+ if (!ports.terminal.recordedStoreMatches(recordedStoreDir)) {
274
+ return failClosedLifecycleLedger({
275
+ ledger,
276
+ ports,
277
+ reason: "native thread transition belongs to another Store"
278
+ });
279
+ }
280
+ let transition;
281
+ try {
282
+ transition = ports.repository.loadNativeThreadTransition(transitionId);
283
+ }
284
+ catch (error) {
285
+ return failClosedLifecycleLedger({
286
+ ledger,
287
+ ports,
288
+ reason: `native thread transition state is unavailable: ` +
289
+ `${error instanceof Error ? error.message : String(error)}`
290
+ });
291
+ }
292
+ const now = ports.runtime.now().toISOString();
293
+ try {
294
+ assertLifecycleLedgerMatchesTransition({
295
+ terminal,
296
+ ledger,
297
+ transition,
298
+ ports
299
+ });
300
+ if (transition.operation === "adopt_external_thread" &&
301
+ transition.status === "verified" &&
302
+ ledger.status === "prepared") {
303
+ ports.ledger.save(transition, {
304
+ phase: "verified_with_previous",
305
+ binding: transition.before_binding,
306
+ previousGenerationId: stringValue(ledger.previous_generation_id)
307
+ }, {
308
+ expectedTransitionId: transition.transition_id,
309
+ expectedStatus: "prepared"
310
+ });
311
+ ledger = ports.ledger.load();
312
+ }
313
+ if (transition.operation === "adopt_external_thread" &&
314
+ transition.status === "prepared") {
315
+ transition = await recoverPreparedObservedHandoff({
316
+ terminal,
317
+ ledger,
318
+ transition,
319
+ now,
320
+ ports
321
+ });
322
+ if (["aborted", "committed"].includes(transition.status)) {
323
+ return ports.ledger.load();
324
+ }
325
+ }
326
+ if (transition.status === "prepared" && ledger.status === "prepared") {
327
+ await ports.terminal.assertExclusive(transition.before_native_thread_id, transition.source_session_id);
328
+ restorePreparedLifecycleSource(transition, now, ports);
329
+ transition = saveLifecycleTransition(ports, {
330
+ ...transition,
331
+ status: "aborted",
332
+ aborted_at: now,
333
+ error: "dispatcher exited before lifecycle input began"
334
+ }, nativeThreadTransitionRevision(transition));
335
+ ports.ledger.save(transition, {
336
+ phase: "resolved",
337
+ at: now,
338
+ reason: "lifecycle dispatcher exited while durably prepared; no terminal input was possible"
339
+ }, { expectedTransitionId: transitionId });
340
+ return ports.ledger.load();
341
+ }
342
+ if (transition.status === "verified") {
343
+ await verifyRecoveredLifecycleAfterBinding({
344
+ terminal,
345
+ transition,
346
+ ports
347
+ });
348
+ await ports.terminal.assertTargetExclusive(transition);
349
+ const savedTarget = ports.repository.commitVerified(transition, now);
350
+ transition = saveLifecycleTransition(ports, {
351
+ ...transition,
352
+ status: "committed",
353
+ committed_at: now
354
+ }, nativeThreadTransitionRevision(transition));
355
+ ports.ledger.save(transition, {
356
+ phase: "resolved_with_binding",
357
+ at: now,
358
+ binding: savedTarget.binding,
359
+ reason: "revalidated and rolled forward a verified native thread transition"
360
+ }, { expectedTransitionId: transitionId });
361
+ return ports.ledger.load();
362
+ }
363
+ if (transition.status === "committed") {
364
+ await verifyRecoveredLifecycleAfterBinding({
365
+ terminal,
366
+ transition,
367
+ ports
368
+ });
369
+ await ports.terminal.assertTargetExclusive(transition);
370
+ assertCommittedLifecycleTarget(transition, ports);
371
+ ports.ledger.save(transition, {
372
+ phase: "resolved_with_binding",
373
+ at: now,
374
+ binding: transition.after_binding,
375
+ reason: "revalidated an already committed native thread transition"
376
+ }, { expectedTransitionId: transitionId });
377
+ return ports.ledger.load();
378
+ }
379
+ if (transition.status === "aborted") {
380
+ await ports.terminal.assertExclusive(transition.before_native_thread_id, transition.source_session_id);
381
+ restorePreparedLifecycleSource(transition, now, ports);
382
+ ports.ledger.save(transition, {
383
+ phase: "resolved",
384
+ at: now,
385
+ reason: "native thread transition is already aborted"
386
+ }, { expectedTransitionId: transitionId });
387
+ return ports.ledger.load();
388
+ }
389
+ if (["dispatching", "submitted"].includes(transition.status)) {
390
+ transition = saveLifecycleTransition(ports, {
391
+ ...transition,
392
+ status: "uncertain",
393
+ uncertain_at: now,
394
+ error: "lifecycle dispatcher exited after terminal input may have started",
395
+ do_not_retry: true
396
+ }, nativeThreadTransitionRevision(transition));
397
+ }
398
+ if (transition.status === "uncertain" && authority.kind === "manual") {
399
+ return await reconcileUncertainLifecycleTransition({
400
+ terminal,
401
+ ledger,
402
+ transition,
403
+ now,
404
+ ports
405
+ });
406
+ }
407
+ quarantineLifecycleSource(transition, now, ports, "native thread lifecycle dispatcher exited after terminal input may have started");
408
+ ports.ledger.save(transition, {
409
+ phase: "uncertain",
410
+ at: transition.uncertain_at ?? now,
411
+ reason: "lifecycle dispatcher exited after terminal input may have started"
412
+ }, { expectedTransitionId: transitionId });
413
+ return ports.ledger.load();
414
+ }
415
+ catch (error) {
416
+ quarantineLifecycleSource(transition, now, ports, "native thread lifecycle recovery evidence did not match the live terminal");
417
+ return failClosedLifecycleLedger({
418
+ ledger,
419
+ ports,
420
+ reason: `native thread lifecycle recovery failed closed: ` +
421
+ `${error instanceof Error ? error.message : String(error)}`
422
+ });
423
+ }
424
+ }
425
+ function assertLifecycleLedgerMatchesTransition({ terminal, ledger, transition, ports }) {
426
+ if (lifecycleLedgerStoredFactsDisagree(ledger, transition) ||
427
+ terminal.agent !== transition.agent ||
428
+ !ports.terminal.recordMatchesControl(ledger) ||
429
+ terminal.pid !== transition.before_binding?.native_process.pid &&
430
+ transition.before_binding !== undefined ||
431
+ !ports.terminal.sameTerminalIncarnation(transition.before_binding?.terminal_control ?? terminal.terminalControl) ||
432
+ (transition.before_binding !== undefined &&
433
+ !ports.terminal.aliasMatches(transition.terminal_id, transition.before_binding.terminal_control, terminal.conversationId)) ||
434
+ !ports.terminal.workspaceMatches(transition.workspace) ||
435
+ JSON.stringify(ledger.binding ?? null) !==
436
+ JSON.stringify(transition.before_binding ?? null)) {
437
+ throw new Error("lifecycle ledger, transition, and live terminal identities disagree");
438
+ }
439
+ const currentVersion = ports.terminal.runningVersion();
440
+ const capability = ports.terminal.probeThreadLifecycle(currentVersion);
441
+ if (transition.operation === "adopt_external_thread") {
442
+ if (currentVersion !== transition.adapter_version ||
443
+ capability?.status !== "supported" ||
444
+ transition.origin !== "human_observed" ||
445
+ transition.terminal_input_sent !== false ||
446
+ transition.command_fingerprint !== HUMAN_OBSERVED_HANDOFF_FINGERPRINT) {
447
+ throw new Error("human-observed handoff adapter profile changed during recovery");
448
+ }
449
+ return;
450
+ }
451
+ const operation = transition.operation === "resume_thread"
452
+ ? {
453
+ kind: "resume_thread",
454
+ nativeThreadId: transition.target_native_thread_id
455
+ }
456
+ : { kind: "new_thread" };
457
+ const plan = capability?.status === "supported"
458
+ ? ports.terminal.planThreadLifecycle(operation, capability)
459
+ : undefined;
460
+ if (currentVersion !== transition.adapter_version ||
461
+ !plan ||
462
+ nativeThreadCommandFingerprint(JSON.stringify(plan.steps)) !==
463
+ transition.command_fingerprint) {
464
+ throw new Error("lifecycle adapter version or command plan changed during recovery");
465
+ }
466
+ }
467
+ function lifecycleLedgerStoredFactsDisagree(ledger, transition) {
468
+ const operationTarget = transition.operation === "new_thread"
469
+ ? undefined
470
+ : transition.target_native_thread_id;
471
+ const allowedLedgerStatuses = {
472
+ prepared: ["prepared", "uncertain"],
473
+ dispatching: ["prepared", "dispatching", "uncertain"],
474
+ submitted: ["dispatching", "submitted", "uncertain"],
475
+ uncertain: ["dispatching", "submitted", "uncertain"],
476
+ verified: transition.operation === "adopt_external_thread"
477
+ ? ["prepared", "verified", "uncertain"]
478
+ : ["submitted", "verified", "uncertain"],
479
+ committed: transition.operation === "adopt_external_thread"
480
+ ? ["verified", "uncertain"]
481
+ : ["submitted", "verified", "uncertain"],
482
+ aborted: transition.reconciled_outcome === "before"
483
+ ? ["dispatching", "submitted", "uncertain"]
484
+ : ["prepared", "dispatching"]
485
+ };
486
+ return (ledger.kind !== "lifecycle" ||
487
+ stringValue(ledger.transition_id) !== transition.transition_id ||
488
+ stringValue(ledger.generation_id) !== transition.transition_id ||
489
+ stringValue(ledger.operation) !== transition.operation ||
490
+ stringValue(ledger.origin) !== transition.origin ||
491
+ ledger.terminal_input_sent !== transition.terminal_input_sent ||
492
+ stringValue(ledger.terminal_id) !== transition.terminal_id ||
493
+ stringValue(ledger.agent) !== transition.agent ||
494
+ stringValue(ledger.workspace) !== transition.workspace ||
495
+ stringValue(ledger.source_session_id) !== transition.source_session_id ||
496
+ stringValue(ledger.target_session_id) !== transition.target_session_id ||
497
+ stringValue(ledger.target_native_thread_id) !== operationTarget ||
498
+ JSON.stringify(ledger.target_candidate_file_identity ?? null) !==
499
+ JSON.stringify(transition.target_candidate_file_identity ?? null) ||
500
+ stringValue(ledger.before_native_thread_id) !==
501
+ transition.before_native_thread_id ||
502
+ !isExactNativeThreadId(transition.before_native_thread_id) ||
503
+ stringValue(ledger.before_process_uuid) !==
504
+ transition.before_process_uuid ||
505
+ (ledger.before_process_started_at === undefined
506
+ ? undefined
507
+ : Number(ledger.before_process_started_at)) !== transition.before_process_started_at ||
508
+ stringValue(ledger.before_process_birth) !==
509
+ transition.before_process_birth ||
510
+ JSON.stringify(ledger.before_process_rollout ?? null) !==
511
+ JSON.stringify(transition.before_process_rollout ?? null) ||
512
+ Number(ledger.dispatcher_pid) !== transition.dispatcher_pid ||
513
+ stringValue(ledger.prepared_at) !== transition.prepared_at ||
514
+ !allowedLedgerStatuses[transition.status].includes(String(ledger.status)) ||
515
+ (transition.status === "aborted" &&
516
+ transition.reconciled_outcome === undefined &&
517
+ !["prepared", "dispatching"].includes(String(ledger.status))) ||
518
+ stringValue(ledger.adapter_version) !== transition.adapter_version ||
519
+ stringValue(ledger.command_fingerprint) !== transition.command_fingerprint);
520
+ }
521
+ async function verifyRecoveredLifecycleAfterBinding({ terminal, transition, ports }) {
522
+ const binding = transition.after_binding;
523
+ if (!binding ||
524
+ !binding.native_thread_id ||
525
+ binding.native_process.pid !== terminal.pid ||
526
+ !ports.terminal.aliasMatches(binding.terminal_id, binding.terminal_control, terminal.conversationId)) {
527
+ throw new Error("verified after_binding no longer matches the terminal or pid");
528
+ }
529
+ if (transition.operation === "adopt_external_thread") {
530
+ if (transition.origin !== "human_observed" ||
531
+ transition.terminal_input_sent !== false ||
532
+ !transition.source_session_id ||
533
+ !transition.before_binding) {
534
+ throw new Error("human-observed handoff recovery evidence is incomplete");
535
+ }
536
+ const source = ports.repository.loadManagedSession(transition.source_session_id);
537
+ const resolved = await ports.terminal.resolveIdentity(binding.native_thread_id, { primary: codexIdentityFence({
538
+ sessionId: transition.before_native_thread_id,
539
+ processUuid: transition.before_process_uuid,
540
+ processBirth: transition.before_process_birth,
541
+ rollout: transition.before_process_rollout,
542
+ evidence: transition.before_binding.native_process.evidence
543
+ }), additional: [] });
544
+ const observed = await ports.terminal.observeExternalHandoff(source, resolved);
545
+ if (!observed) {
546
+ throw new Error("human-observed handoff target identity is unavailable during recovery");
547
+ }
548
+ const exact = ports.terminal.exactIdentity(observed);
549
+ if (exact.sessionId.toLowerCase() !== binding.native_thread_id.toLowerCase() ||
550
+ exact.processUuid !== binding.native_process.process_uuid ||
551
+ exact.processBirth !== binding.native_process.process_birth ||
552
+ JSON.stringify(exact.rollout ?? null) !==
553
+ JSON.stringify(binding.native_process.rollout ?? null)) {
554
+ throw new Error("human-observed handoff target identity changed during recovery");
555
+ }
556
+ return;
557
+ }
558
+ ports.terminal.prepareProbeBridge();
559
+ const status = await ports.terminal.status({ kind: "physical" });
560
+ if (!status.reachable ||
561
+ status.activityState !== "idle" ||
562
+ status.approvalBlocked) {
563
+ throw new Error("live terminal is not at a verified idle prompt during recovery");
564
+ }
565
+ let identity;
566
+ if (terminal.agent === "claude") {
567
+ identity = await probeManualClaudeLifecycleRecoveryIdentity({
568
+ terminal,
569
+ transition,
570
+ clearInput: false,
571
+ ports
572
+ });
573
+ }
574
+ else if (binding.native_process.rollout) {
575
+ // Resolver failures are unknown evidence, never permission to fall back to
576
+ // a possibly stale status card.
577
+ const knownRoots = ports.terminal.codexKnownRoots(transition);
578
+ const companions = ports.terminal.codexCompanionsExcludingPreferred(knownRoots, binding.native_thread_id);
579
+ identity = await ports.terminal.resolveIdentity(binding.native_thread_id, companions);
580
+ if (!identity) {
581
+ throw new Error("Codex rollout identity is unavailable during verified recovery");
582
+ }
583
+ identity = ports.terminal.exactIdentity(identity);
584
+ }
585
+ else {
586
+ identity = await probeManualCodexLifecycleRecoveryIdentity({
587
+ terminal,
588
+ transition,
589
+ clearInput: false,
590
+ ports
591
+ });
592
+ }
593
+ if (identity.sessionId !== binding.native_thread_id) {
594
+ throw new Error("live native thread does not match verified after_binding");
595
+ }
596
+ if (terminal.agent === "codex") {
597
+ if (binding.native_process.process_birth !== identity.processBirth ||
598
+ binding.native_process.process_uuid !== identity.processUuid) {
599
+ throw new Error("Codex process incarnation changed during lifecycle recovery");
600
+ }
601
+ }
602
+ else if (!identity.processUuid ||
603
+ binding.native_process.process_uuid !== identity.processUuid) {
604
+ throw new Error("Claude process incarnation changed during lifecycle recovery");
605
+ }
606
+ const rollout = binding.native_process.rollout;
607
+ if (rollout &&
608
+ (rollout.fd !== identity?.rollout?.fd ||
609
+ rollout.device !== identity.rollout.device ||
610
+ rollout.inode !== identity.rollout.inode ||
611
+ rollout.path !== identity.rollout.path)) {
612
+ throw new Error("native rollout incarnation changed during lifecycle recovery");
613
+ }
614
+ if (terminal.agent === "codex" &&
615
+ transition.operation === "resume_thread") {
616
+ ports.terminal.assertResumedCodexCandidate(identity, transition.target_candidate_file_identity);
617
+ }
618
+ }
619
+ async function reconcileUncertainLifecycleTransition({ terminal, transition, now, ports }) {
620
+ if (transition.operation === "adopt_external_thread") {
621
+ throw new Error("uncertain human-observed handoff remains quarantined because recovery cannot send terminal input");
622
+ }
623
+ const live = terminal.agent === "claude"
624
+ ? await probeManualClaudeLifecycleRecoveryIdentity({
625
+ terminal,
626
+ transition,
627
+ clearInput: !transition.submitted_at,
628
+ ports
629
+ })
630
+ : await probeManualCodexLifecycleRecoveryIdentity({
631
+ terminal,
632
+ transition,
633
+ clearInput: !transition.submitted_at,
634
+ ports
635
+ });
636
+ assertManualLifecycleProcessIncarnation(transition, live);
637
+ if (live.sessionId === transition.before_native_thread_id) {
638
+ assertLifecycleRolloutMatches(transition.before_process_rollout, live.rollout, "before-thread rollout");
639
+ await ports.terminal.assertExclusive(transition.before_native_thread_id, transition.source_session_id);
640
+ transition = saveLifecycleTransition(ports, {
641
+ ...transition,
642
+ status: "aborted",
643
+ aborted_at: now,
644
+ reconciled_outcome: "before",
645
+ reconciled_at: now,
646
+ error: "operator-authorized recovery observed the exact before-thread identity"
647
+ }, nativeThreadTransitionRevision(transition));
648
+ restorePreparedLifecycleSource(transition, now, ports);
649
+ ports.ledger.save(transition, {
650
+ phase: "resolved",
651
+ at: now,
652
+ reason: "operator-authorized recovery observed exact before identity and rolled back"
653
+ }, { expectedTransitionId: transition.transition_id });
654
+ return ports.ledger.load();
655
+ }
656
+ if (!["resume_thread", "adopt_external_thread"].includes(transition.operation) ||
657
+ live.sessionId !== transition.target_native_thread_id) {
658
+ throw new Error("fresh native thread identity matches neither the recorded before identity " +
659
+ "nor a durably known exact after identity");
660
+ }
661
+ if (transition.operation === "resume_thread" &&
662
+ terminal.agent === "codex" &&
663
+ !live.rollout) {
664
+ throw new Error("Codex resume recovery requires an exact live rollout incarnation");
665
+ }
666
+ if (transition.operation === "resume_thread" &&
667
+ terminal.agent === "codex") {
668
+ ports.terminal.assertResumedCodexCandidate(live, transition.target_candidate_file_identity);
669
+ }
670
+ const targetAtPrepare = ports.repository.tryLoadManagedSession(transition.target_session_id);
671
+ if ((targetAtPrepare?.revision ?? null) !== transition.target_expected_revision) {
672
+ throw new Error("lifecycle target Session changed before manual roll-forward");
673
+ }
674
+ const verifiedAt = new Date(now);
675
+ const afterBinding = terminalBindingFrom({
676
+ terminalId: terminal.conversationId,
677
+ terminalControl: terminal.terminalControl,
678
+ pid: terminal.pid,
679
+ nativeThreadId: live.sessionId,
680
+ processUuid: live.processUuid,
681
+ processBirth: live.processBirth,
682
+ rollout: live.rollout,
683
+ evidence: `${live.evidence}+operator_recovery`,
684
+ generation: (targetAtPrepare?.binding?.generation ?? 0) + 1,
685
+ now: verifiedAt
686
+ });
687
+ transition = saveLifecycleTransition(ports, {
688
+ ...transition,
689
+ status: "verified",
690
+ submitted_at: transition.submitted_at ?? now,
691
+ verified_at: now,
692
+ after_binding: afterBinding,
693
+ reconciled_outcome: "after",
694
+ reconciled_at: now,
695
+ error: undefined,
696
+ do_not_retry: undefined
697
+ }, nativeThreadTransitionRevision(transition));
698
+ await ports.terminal.assertTargetExclusive(transition);
699
+ const savedTarget = ports.repository.commitVerified(transition, now);
700
+ transition = saveLifecycleTransition(ports, {
701
+ ...transition,
702
+ status: "committed",
703
+ committed_at: now
704
+ }, nativeThreadTransitionRevision(transition));
705
+ ports.ledger.save(transition, {
706
+ phase: "resolved_with_binding",
707
+ at: now,
708
+ binding: savedTarget.binding,
709
+ reason: "operator-authorized recovery observed exact after identity and rolled forward"
710
+ }, { expectedTransitionId: transition.transition_id });
711
+ return ports.ledger.load();
712
+ }
713
+ async function probeManualClaudeLifecycleRecoveryIdentity({ terminal, transition, clearInput = true, ports }) {
714
+ const startedAt = transition.before_process_started_at;
715
+ if (!startedAt) {
716
+ throw new Error("Claude manual lifecycle recovery lacks prepare-time process incarnation");
717
+ }
718
+ ports.terminal.prepareProbeBridge();
719
+ const operation = transition.operation === "resume_thread"
720
+ ? {
721
+ kind: "resume_thread",
722
+ nativeThreadId: transition.target_native_thread_id
723
+ }
724
+ : { kind: "new_thread" };
725
+ const initialObservation = (await ports.terminal.observeThreadLifecycle({
726
+ kind: "claude_agents",
727
+ operation,
728
+ pid: terminal.pid,
729
+ processStartedAt: startedAt,
730
+ cwd: transition.workspace
731
+ })).observation;
732
+ const initialStatus = await ports.terminal.status({ kind: "physical" });
733
+ if (initialObservation?.status !== "observed" ||
734
+ initialObservation.idle !== true ||
735
+ !isExactNativeThreadId(initialObservation.nativeThreadId) ||
736
+ !initialStatus.reachable ||
737
+ initialStatus.approvalBlocked ||
738
+ (clearInput
739
+ ? (initialStatus.activityState === "working" ||
740
+ initialStatus.activityState === "awaiting_approval" ||
741
+ !initialStatus.composerVisible)
742
+ : (initialStatus.activityState !== "idle" ||
743
+ !initialStatus.composerEmpty))) {
744
+ throw new Error("manual lifecycle recovery requires one exact idle Claude process and composer");
745
+ }
746
+ const processUuid = `claude-pid:${terminal.pid}:started:${startedAt}`;
747
+ if (processUuid !== transition.before_process_uuid) {
748
+ throw new Error("Claude process incarnation changed before manual lifecycle recovery");
749
+ }
750
+ if (clearInput) {
751
+ await ports.terminal.clearInputLine({ kind: "physical" });
752
+ }
753
+ let stableSessionId;
754
+ let stableCount = 0;
755
+ let lastReason = "no exact idle Claude agents observation was available";
756
+ for (let attempt = 0; attempt < 12; attempt += 1) {
757
+ const observed = (await ports.terminal.observeThreadLifecycle({
758
+ kind: "claude_agents",
759
+ operation,
760
+ pid: terminal.pid,
761
+ processStartedAt: startedAt,
762
+ cwd: transition.workspace
763
+ })).observation;
764
+ const status = await ports.terminal.status({ kind: "physical" });
765
+ if (observed?.status === "observed" &&
766
+ observed.idle === true &&
767
+ isExactNativeThreadId(observed.nativeThreadId) &&
768
+ status.reachable &&
769
+ status.activityState === "idle" &&
770
+ !status.approvalBlocked &&
771
+ status.composerEmpty) {
772
+ if (stableSessionId === observed.nativeThreadId) {
773
+ stableCount += 1;
774
+ }
775
+ else {
776
+ stableSessionId = observed.nativeThreadId;
777
+ stableCount = 1;
778
+ }
779
+ if (stableCount >= 2) {
780
+ return {
781
+ sessionId: observed.nativeThreadId,
782
+ processStartedAt: startedAt,
783
+ processUuid,
784
+ evidence: observed.evidence ?? "claude_agents_exact_pid"
785
+ };
786
+ }
787
+ lastReason = "only one stable Claude agents observation was available";
788
+ }
789
+ else {
790
+ stableSessionId = undefined;
791
+ stableCount = 0;
792
+ lastReason =
793
+ observed?.reason ??
794
+ "Claude process or terminal was not exact, idle, and unblocked";
795
+ }
796
+ await ports.runtime.sleep(100);
797
+ }
798
+ throw new Error(`Claude manual lifecycle recovery identity was not stable: ${lastReason}`);
799
+ }
800
+ async function resolveManualCodexLifecycleStatusIdentity({ observedNativeThreadId, observedEvidence, transition, recoveryKnownRoots, ports }) {
801
+ let resolved;
802
+ try {
803
+ const companions = ports.terminal.codexCompanionsExcludingPreferred(recoveryKnownRoots, observedNativeThreadId);
804
+ resolved = await ports.terminal.resolveIdentity(observedNativeThreadId, companions);
805
+ }
806
+ catch (error) {
807
+ throw new Error(`Codex resolver failed during manual lifecycle recovery: ${error instanceof Error ? error.message : String(error)}`);
808
+ }
809
+ const matchedKnownBefore = [
810
+ recoveryKnownRoots.primary,
811
+ ...recoveryKnownRoots.additional
812
+ ].find((candidate) => ports.terminal.nativeIdentityMatches(resolved, candidate));
813
+ const observedRecordedBefore = observedNativeThreadId === transition.before_native_thread_id;
814
+ const recordedBeforeFence = transition.before_process_rollout
815
+ ? {
816
+ sessionId: transition.before_native_thread_id,
817
+ processUuid: transition.before_process_uuid,
818
+ processBirth: transition.before_process_birth,
819
+ rollout: transition.before_process_rollout
820
+ }
821
+ : undefined;
822
+ const postconditionEvidence = observedRecordedBefore
823
+ ? recordedBeforeFence
824
+ ? ports.terminal.nativeIdentityMatches(resolved, recordedBeforeFence)
825
+ ? "matching_after"
826
+ : "invalid"
827
+ : resolved === undefined || matchedKnownBefore
828
+ ? "no_rollout"
829
+ : "invalid"
830
+ : classifyCodexLifecyclePostcondition({
831
+ operation: transition.operation === "resume_thread"
832
+ ? "resume_thread"
833
+ : "new_thread",
834
+ parsedNativeThreadId: observedNativeThreadId,
835
+ observationSucceeded: true,
836
+ observedIdentity: resolved,
837
+ beforeIdentity: matchedKnownBefore ?? {
838
+ sessionId: transition.before_native_thread_id,
839
+ processUuid: transition.before_process_uuid,
840
+ processBirth: transition.before_process_birth,
841
+ rollout: transition.before_process_rollout
842
+ }
843
+ });
844
+ if (postconditionEvidence === "invalid") {
845
+ throw new Error("Codex status card and rollout resolver disagree during recovery");
846
+ }
847
+ const sanitized = postconditionEvidence === "matching_after"
848
+ ? resolved
849
+ : {
850
+ sessionId: observedNativeThreadId,
851
+ processUuid: transition.before_process_uuid,
852
+ processBirth: transition.before_process_birth,
853
+ evidence: observedEvidence ?? "codex_status_card"
854
+ };
855
+ return ports.terminal.exactIdentity({
856
+ ...sanitized,
857
+ sessionId: observedNativeThreadId,
858
+ evidence: observedEvidence ?? "codex_status_card"
859
+ });
860
+ }
861
+ async function probeManualCodexLifecycleRecoveryIdentity({ terminal, transition, clearInput = true, ports }) {
862
+ if (terminal.agent !== "codex") {
863
+ throw new Error("manual uncertain lifecycle recovery currently requires Codex exact-status evidence");
864
+ }
865
+ ports.terminal.prepareProbeBridge();
866
+ const recoveryKnownRoots = ports.terminal.codexKnownRoots(transition);
867
+ const recoveryPreferredSessionId = transition.operation === "resume_thread"
868
+ ? transition.target_native_thread_id
869
+ : transition.after_binding?.native_thread_id ??
870
+ transition.before_native_thread_id;
871
+ const recoveryCompanions = ports.terminal.codexCompanionsExcludingPreferred(recoveryKnownRoots, recoveryPreferredSessionId);
872
+ const recoveryContext = {
873
+ kind: "codex_recovery",
874
+ processUuid: transition.before_process_uuid,
875
+ processBirth: transition.before_process_birth,
876
+ expectedSessionId: recoveryPreferredSessionId,
877
+ companions: recoveryCompanions
878
+ };
879
+ // Before any recovery key is sent, prove that every open root is either the
880
+ // exact expected before/after thread or a complete managed companion. This
881
+ // turns an unknown third root into a poisoned recovery instead of allowing
882
+ // C-u or /status to act on an unowned foreground context.
883
+ await ports.terminal.resolveIdentity(recoveryPreferredSessionId, recoveryCompanions);
884
+ const initial = await ports.terminal.status(recoveryContext);
885
+ if (!initial.reachable ||
886
+ initial.approvalBlocked ||
887
+ (clearInput
888
+ ? (initial.activityState === "working" ||
889
+ initial.activityState === "awaiting_approval" ||
890
+ !initial.composerVisible)
891
+ : (initial.activityState !== "idle" ||
892
+ !initial.composerVisible ||
893
+ !initial.composerEmpty))) {
894
+ throw new Error("manual lifecycle recovery requires a reachable non-working Codex composer");
895
+ }
896
+ const birthBeforeClear = ports.terminal.codexProcessBirth();
897
+ const processUuidBeforeClear = `codex-pid:${terminal.pid}:birth:${birthBeforeClear}`;
898
+ if (birthBeforeClear !== transition.before_process_birth ||
899
+ processUuidBeforeClear !== transition.before_process_uuid) {
900
+ throw new Error("Codex process incarnation changed before manual lifecycle recovery");
901
+ }
902
+ // A dispatcher can die after sendText() but before Enter. Explicit close is
903
+ // the operator authority to clear that unsubmitted composer before probing.
904
+ let cleared = initial;
905
+ if (clearInput) {
906
+ await ports.terminal.clearInputLine(recoveryContext);
907
+ cleared = undefined;
908
+ for (let attempt = 0; attempt < 20; attempt += 1) {
909
+ const candidate = await ports.terminal.status(recoveryContext);
910
+ if (candidate.reachable &&
911
+ candidate.activityState === "idle" &&
912
+ !candidate.approvalBlocked &&
913
+ candidate.composerEmpty) {
914
+ cleared = candidate;
915
+ break;
916
+ }
917
+ await ports.runtime.sleep(50);
918
+ }
919
+ }
920
+ if (!cleared?.screenDigest) {
921
+ throw new Error("Codex composer did not become empty after recovery clear-line");
922
+ }
923
+ const agentVersion = ports.terminal.runningVersion();
924
+ if (!agentVersion) {
925
+ throw new Error("Codex lifecycle recovery /status requires the exact running version before terminal input");
926
+ }
927
+ const submission = await ports.terminal.submitCodexStatusProbe(agentVersion, recoveryContext);
928
+ const baselineDigest = submission.observationBaselineDigest;
929
+ let stable;
930
+ let stableCount = 0;
931
+ let lastObservationReason = "no fresh idle status screen was captured";
932
+ for (let attempt = 0; attempt < 12; attempt += 1) {
933
+ const sampled = await ports.terminal.observeThreadLifecycle({
934
+ kind: "codex_status",
935
+ operation: { kind: "new_thread" },
936
+ context: recoveryContext,
937
+ observationBaselineDigest: baselineDigest,
938
+ observationScrollbackLines: submission.observationScrollbackLines
939
+ });
940
+ if (sampled.kind !== "codex_status") {
941
+ throw new Error("Codex lifecycle observer returned the wrong result kind");
942
+ }
943
+ const status = sampled.status;
944
+ if (status.reachable &&
945
+ status.activityState === "idle" &&
946
+ !status.approvalBlocked &&
947
+ status.screenDigest &&
948
+ status.screenDigest !== baselineDigest) {
949
+ const observed = sampled.observation;
950
+ if (observed?.status === "observed" &&
951
+ isExactNativeThreadId(observed.nativeThreadId)) {
952
+ const exact = await resolveManualCodexLifecycleStatusIdentity({
953
+ observedNativeThreadId: observed.nativeThreadId,
954
+ observedEvidence: observed.evidence,
955
+ transition,
956
+ recoveryKnownRoots,
957
+ ports
958
+ });
959
+ const key = JSON.stringify({
960
+ sessionId: exact.sessionId,
961
+ processUuid: exact.processUuid,
962
+ processBirth: exact.processBirth,
963
+ rollout: exact.rollout ?? null
964
+ });
965
+ const priorKey = stable
966
+ ? JSON.stringify({
967
+ sessionId: stable.sessionId,
968
+ processUuid: stable.processUuid,
969
+ processBirth: stable.processBirth,
970
+ rollout: stable.rollout ?? null
971
+ })
972
+ : undefined;
973
+ if (priorKey === key) {
974
+ stableCount += 1;
975
+ }
976
+ else {
977
+ stable = exact;
978
+ stableCount = 1;
979
+ }
980
+ if (stableCount >= 2) {
981
+ return exact;
982
+ }
983
+ lastObservationReason =
984
+ "only one stable status/process observation was available";
985
+ }
986
+ else {
987
+ lastObservationReason =
988
+ observed?.reason ?? "Codex status card did not expose one exact UUID";
989
+ stable = undefined;
990
+ stableCount = 0;
991
+ }
992
+ }
993
+ else {
994
+ lastObservationReason =
995
+ "terminal status was not fresh, idle, reachable, and unblocked";
996
+ }
997
+ await ports.runtime.sleep(100);
998
+ }
999
+ throw new Error("fresh Codex /status did not produce a stable exact lifecycle identity: " +
1000
+ lastObservationReason);
1001
+ }
1002
+ function assertManualLifecycleProcessIncarnation(transition, live) {
1003
+ if (live.processUuid !== transition.before_process_uuid ||
1004
+ live.processStartedAt !== transition.before_process_started_at ||
1005
+ live.processBirth !== transition.before_process_birth) {
1006
+ throw new Error("live process incarnation does not match lifecycle prepare evidence");
1007
+ }
1008
+ }
1009
+ function assertLifecycleRolloutMatches(expected, actual, label) {
1010
+ if (expected &&
1011
+ (expected.device !== actual?.device ||
1012
+ expected.inode !== actual?.inode ||
1013
+ expected.path !== actual?.path)) {
1014
+ throw new Error(`${label} changed during lifecycle recovery`);
1015
+ }
1016
+ }
1017
+ function restorePreparedLifecycleSource(transition, now, ports) {
1018
+ if (transition.target_session_id !== transition.source_session_id) {
1019
+ const target = ports.repository.tryLoadManagedSession(transition.target_session_id);
1020
+ if ((target?.revision ?? null) !== transition.target_expected_revision ||
1021
+ (target?.status === "bound" &&
1022
+ target.last_transition_id === transition.transition_id)) {
1023
+ throw new Error("lifecycle target Session changed before before-identity rollback");
1024
+ }
1025
+ }
1026
+ if (!transition.source_session_id) {
1027
+ return;
1028
+ }
1029
+ const source = ports.repository.tryLoadManagedSession(transition.source_session_id);
1030
+ if (!source || transition.source_expected_revision === undefined) {
1031
+ throw new Error("prepared lifecycle source Session is unavailable");
1032
+ }
1033
+ if (source.status === "bound" &&
1034
+ JSON.stringify(source.binding) === JSON.stringify(transition.before_binding) &&
1035
+ source.last_transition_id === transition.source_previous_last_transition_id) {
1036
+ return;
1037
+ }
1038
+ if (Number(source.revision) <= transition.source_expected_revision ||
1039
+ !["transitioning", "quarantined"].includes(source.status) ||
1040
+ source.last_transition_id !== transition.transition_id ||
1041
+ JSON.stringify(source.binding) !== JSON.stringify(transition.before_binding)) {
1042
+ throw new Error("prepared lifecycle source Session changed before recovery");
1043
+ }
1044
+ saveLifecycleSession(ports, {
1045
+ ...source,
1046
+ status: "bound",
1047
+ quarantine_reason: undefined,
1048
+ last_transition_id: transition.source_previous_last_transition_id,
1049
+ updated_at: now
1050
+ }, managedSessionRevision(source));
1051
+ }
1052
+ function assertCommittedLifecycleTarget(transition, ports) {
1053
+ const target = ports.repository.tryLoadManagedSession(transition.target_session_id);
1054
+ if (!target ||
1055
+ target.status !== "bound" ||
1056
+ target.last_transition_id !== transition.transition_id ||
1057
+ JSON.stringify(target.binding) !== JSON.stringify(transition.after_binding)) {
1058
+ throw new Error("committed lifecycle target Session is missing or mismatched");
1059
+ }
1060
+ }
1061
+ function quarantineLifecycleSource(transition, now, ports, reason) {
1062
+ if (!transition.source_session_id) {
1063
+ return;
1064
+ }
1065
+ const source = ports.repository.tryLoadManagedSession(transition.source_session_id);
1066
+ if (!source?.binding ||
1067
+ source.last_transition_id !== transition.transition_id ||
1068
+ source.status === "quarantined" ||
1069
+ source.status === "detached") {
1070
+ return;
1071
+ }
1072
+ saveLifecycleSession(ports, {
1073
+ ...source,
1074
+ status: "quarantined",
1075
+ quarantine_reason: reason,
1076
+ updated_at: now
1077
+ }, managedSessionRevision(source));
1078
+ }
1079
+ function failClosedLifecycleLedger({ ledger, ports, reason }) {
1080
+ const transitionId = stringValue(ledger.transition_id);
1081
+ if (!transitionId) {
1082
+ return ledger;
1083
+ }
1084
+ const now = ports.runtime.now().toISOString();
1085
+ try {
1086
+ ports.ledger.saveFailClosed(ledger, transitionId, now, reason);
1087
+ }
1088
+ catch {
1089
+ return ledger;
1090
+ }
1091
+ return ports.ledger.load();
1092
+ }
1093
+ //# sourceMappingURL=native-thread-lifecycle-recovery-service.js.map