@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,1389 @@
1
+ // CLI infrastructure for verified-empty, deferred foreground, and observed handoff authority.
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import path from "node:path";
4
+ import { canonicalJson } from "./canonical-json.js";
5
+ import { cliCwd, cliEnv, cliExit, cliNow, cliPid, cliRuntimeLog as runtimeLog } from "./cli-runtime-context.js";
6
+ import { DeferredForegroundApplicationService } from "./deferred-foreground-application-service.js";
7
+ import { TerminalHandoffApplicationService } from "./terminal-handoff-application-service.js";
8
+ import { bindDeferredForegroundApplicationScope, bindDeferredForegroundWriterScope } from "./deferred-foreground-capability.js";
9
+ import { createDeferredForegroundTransferId, listDeferredForegroundTransfers, loadDeferredForegroundTransfer } from "./deferred-foreground-transfer.js";
10
+ import { isFinalDeferredForegroundTransferStatus } from "./deferred-foreground-transfer-policy.js";
11
+ import { DeferredForegroundRecoveryService } from "./deferred-foreground-recovery-service.js";
12
+ import * as deferredRecoveryAdapter from "./deferred-foreground-recovery-cli-adapter.js";
13
+ import { prepareDeferredForegroundBinding } from "./deferred-foreground-preparation-service.js";
14
+ import { assertDeferredForegroundBoundary, deferredForegroundBoundaryProjection, deferredForegroundConcreteBoundary, deferredForegroundTransferMatchesTerminal, projectDeferredForegroundTerminalFacts } from "./deferred-foreground-preparation-cli-adapter.js";
15
+ import { executorDefinitionForKind } from "./executors.js";
16
+ import { createManagedSessionId, createNativeThreadTransitionId, isExactNativeThreadId, managedSessionBindingToken, managedSessionRevision, nativeThreadCommandFingerprint, terminalBindingFrom } from "./managed-session.js";
17
+ import { withCanonicalMutationLocks } from "./mutation-transaction.js";
18
+ import { claudeComposerEmpty, codexComposerEmpty } from "./native-thread-lifecycle-recovery-adapter.js";
19
+ import { sessionIdForConversation, turnIdForConversation } from "./protocol.js";
20
+ import { commitVerifiedLifecycleTransition, listManagedSessions, loadManagedSession, loadNativeThreadTransition, saveManagedSession, saveNativeThreadTransition, tryLoadManagedSession } from "./session-store.js";
21
+ import { pathsForConversation, pathsForConversationDir } from "./store.js";
22
+ import { activeTurnHandoffDecisionToken as projectActiveTurnHandoffDecisionToken, codexIdentityFence, deferredCodexForegroundBindingToken, exactBoundCodexSendSource, observedHandoffAuthorityToken as projectObservedHandoffAuthorityToken, terminalControlsShareIncarnation, verifiedEmptyCodexHandoffToken } from "./terminal-authority-policy.js";
23
+ import { terminalControlEvidenceMatches } from "./terminal-control-ref.js";
24
+ import { nativeThreadTransitionRevision, terminalSubmissionPayload } from "./terminal-dispatch-execution.js";
25
+ import { nativeThreadLifecycleLedger as lifecycleLedger } from "./terminal-dispatch-ledger-codec.js";
26
+ import { terminalBridgeRequestFingerprint, terminalBridgeSubmission, terminalBridgeSubmissionReceipts, terminalDispatchTextSummary } from "./terminal-dispatch-receipt.js";
27
+ import { captureCodexCandidateSetRolloutAcceptanceAnchor } from "./terminal-submission-acceptance.js";
28
+ import { validTerminalMonitorTimestampMs as validTimestampMs } from "./terminal-monitor-decision-policy.js";
29
+ import { terminalControlFromTakeover } from "./terminal-runtime-cli-adapter.js";
30
+ import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
31
+ const TERMINAL_INPUT_EVIDENCE_FIELDS = ["text_injected_at",
32
+ "enter_dispatched_at", "submitted_at", "agent_accepted_at",
33
+ "not_accepted_at", "uncertain_at", "acceptance_evidence"];
34
+ const HUMAN_OBSERVED_HANDOFF_FINGERPRINT = nativeThreadCommandFingerprint("adopt_external_thread:human_observed:no_terminal_input:v1");
35
+ const handoffApplication = new TerminalHandoffApplicationService();
36
+ const terminalHandoffContext = new AsyncLocalStorage();
37
+ function terminalHandoffRuntime() {
38
+ const runtime = terminalHandoffContext.getStore();
39
+ if (!runtime) {
40
+ throw new Error("Terminal handoff facade runtime is unavailable");
41
+ }
42
+ return runtime;
43
+ }
44
+ function contextualPort(group, name) {
45
+ return ((...arguments_) => {
46
+ const operation = terminalHandoffRuntime().dependencies[group][name];
47
+ return operation(...arguments_);
48
+ });
49
+ }
50
+ const storeDirFromOptions = contextualPort("runtime", "storeDir");
51
+ const createTerminalAgentBridge = contextualPort("runtime", "createBridge");
52
+ const terminalRuntimeForLiveIdentity = contextualPort("identity", "terminalRuntimeForLiveIdentity");
53
+ const agentVersionForRunningProcess = contextualPort("runtime", "agentVersion");
54
+ const required = contextualPort("runtime", "required");
55
+ const textSummary = terminalDispatchTextSummary;
56
+ const isStoreMutationLockTimeout = contextualPort("runtime", "isStoreMutationLockTimeout");
57
+ const codexProcessIncarnationForPid = contextualPort("identity", "codexProcessIncarnationForPid");
58
+ const inspectCodexOpenRootRolloutInventory = contextualPort("acceptance", "inspectCodexOpenRoots");
59
+ const observeCurrentNativeAgentSessionIdentity = contextualPort("acceptance", "observeNativeIdentity");
60
+ const resolveCurrentNativeAgentSessionIdentity = contextualPort("acceptance", "resolveNativeIdentity");
61
+ const exactLifecycleProcessIdentity = contextualPort("identity", "exactLifecycleProcessIdentity");
62
+ const managedBindingConflictKindForResolvedTerminal = contextualPort("identity", "managedBindingConflictKindForResolvedTerminal");
63
+ const assertNativeThreadHasExclusiveOwnership = contextualPort("authority", "assertExclusive");
64
+ const assertCodexComposerReadyForAutomatedInput = contextualPort("identity", "assertCodexComposerReadyForAutomatedInput");
65
+ const assertNativeAgentIdentityForTurn = contextualPort("acceptance", "assertTurnIdentity");
66
+ const assertVerifiedEmptyCodexHandoffBoundary = contextualPort("identity", "assertVerifiedEmptyCodexHandoffBoundary");
67
+ const observeDeferredCodexAuthority = contextualPort("identity", "observeDeferredCodexAuthority");
68
+ const deferredCodexForegroundDispatchSnapshot = contextualPort("identity", "deferredCodexForegroundDispatchSnapshot");
69
+ const codexCandidateInventoryHasNoOtherManagedClaim = contextualPort("identity", "codexCandidateInventoryHasNoOtherManagedClaim");
70
+ const deferredCandidateSourceTurnHistory = contextualPort("identity", "deferredCandidateSourceTurnHistory");
71
+ const explicitlyAbandonedCandidateSourceFingerprint = contextualPort("identity", "explicitlyAbandonedCandidateSourceFingerprint");
72
+ const assertFrozenExplicitlyAbandonedPredecessorAuthority = contextualPort("identity", "assertFrozenExplicitlyAbandonedPredecessorAuthority");
73
+ const deferredCodexPreviousDispatchSnapshotMatches = contextualPort("identity", "deferredCodexPreviousDispatchSnapshotMatches");
74
+ const terminalListCliFacade = {
75
+ assertTerminalIncarnationCanStartTurn: contextualPort("authority", "assertTerminalCanStartTurn"),
76
+ managedSessionHasUnresolvedNativeTransition: contextualPort("authority", "hasUnresolvedTransition")
77
+ };
78
+ const assertManagedSessionCanStartTurn = contextualPort("authority", "assertSessionCanStartTurn");
79
+ const managedTurnsForSession = contextualPort("authority", "turnsForSession");
80
+ const assertTerminalLifecycleReady = contextualPort("authority", "assertTerminalReady");
81
+ const assertSafeTerminalSend = contextualPort("authority", "assertSafeSend");
82
+ const managedSessionStoreDirForConversation = contextualPort("repository", "storeDirForConversation");
83
+ const loadTerminalBridgeDispatchLedger = contextualPort("repository", "loadLedger");
84
+ const saveTerminalBridgeDispatchLedger = contextualPort("repository", "saveLedger");
85
+ const terminalDispatchRecordMatchesControl = contextualPort("repository", "ledgerMatchesControl");
86
+ const terminalBindingLedgerFields = contextualPort("repository", "bindingFields");
87
+ const withNativeAgentSessionIdentity = contextualPort("repository", "withNativeIdentity");
88
+ const withTerminalBridgeSubmission = contextualPort("repository", "withSubmission");
89
+ const saveLifecycleTerminalDispatchLedger = contextualPort("repository", "saveLifecycleLedger");
90
+ const terminalWriterMutationLocks = contextualPort("repository", "mutationLocks");
91
+ const withTerminalDispatchStateScope = contextualPort("repository", "withStateScope");
92
+ const originalExpectedTerminalSelector = {
93
+ has: (options) => terminalHandoffRuntime()
94
+ .originalExpectedTerminalSelector.has(options),
95
+ get: (options) => terminalHandoffRuntime()
96
+ .originalExpectedTerminalSelector.get(options)
97
+ };
98
+ function assertSafeAbortedTerminalRetryBinding({ owner, receipt, storeDir, terminalControl, messageId }) {
99
+ if (!(receipt.status === "aborted" && receipt.safe_to_retry === true)) {
100
+ return undefined;
101
+ }
102
+ const sessionId = sessionIdForConversation(owner);
103
+ const managedSession = tryLoadManagedSession(storeDir, sessionId);
104
+ const binding = managedSession?.binding;
105
+ const receiptBindingId = stringValue(receipt.binding_id);
106
+ const receiptBindingGeneration = Number(receipt.binding_generation);
107
+ const receiptNativeThreadId = stringValue(receipt.native_thread_id);
108
+ const ownerTakeover = isRecord(owner.native_session_takeover)
109
+ ? owner.native_session_takeover
110
+ : undefined;
111
+ const ownerControl = terminalControlFromTakeover(ownerTakeover);
112
+ const ownerNativeThreadId = stringValue(owner.native_thread_id) ??
113
+ stringValue(ownerTakeover?.terminal_agent_session_id) ??
114
+ stringValue(ownerTakeover?.terminal_agent_expected_session_id);
115
+ const ownerAgentPid = Number(ownerTakeover?.terminal_agent_pid);
116
+ if (handoffApplication.authorityChecksPass([
117
+ () => managedSession !== undefined,
118
+ () => managedSession?.status === "bound", () => binding !== undefined,
119
+ () => receiptBindingId !== undefined,
120
+ () => Number.isSafeInteger(receiptBindingGeneration),
121
+ () => receiptNativeThreadId !== undefined,
122
+ () => receiptBindingId === stringValue(owner.terminal_binding_id),
123
+ () => receiptBindingGeneration === Number(owner.terminal_binding_generation),
124
+ () => receiptNativeThreadId === ownerNativeThreadId,
125
+ () => binding.binding_id === receiptBindingId,
126
+ () => binding.generation === receiptBindingGeneration,
127
+ () => binding.native_thread_id === receiptNativeThreadId,
128
+ () => Number.isSafeInteger(ownerAgentPid),
129
+ () => binding.native_process.pid === ownerAgentPid,
130
+ () => ownerControl !== undefined,
131
+ () => terminalControlsShareIncarnation(ownerControl, terminalControl),
132
+ () => terminalControlsShareIncarnation(binding.terminal_control, terminalControl)
133
+ ])) {
134
+ return managedSession;
135
+ }
136
+ const recoveredSource = safeAbortedDeferredRetrySourceSession({
137
+ owner,
138
+ receipt,
139
+ storeDir,
140
+ terminalControl,
141
+ messageId
142
+ });
143
+ if (!recoveredSource) {
144
+ throw new Error(`terminal idempotency key ${messageId} belongs to a safe-aborted Turn ` +
145
+ "whose Session binding is no longer current; no terminal input was sent");
146
+ }
147
+ return recoveredSource;
148
+ }
149
+ function safeAbortedDeferredRetrySourceSession({ owner, receipt, storeDir, terminalControl, messageId }) {
150
+ const takeover = isRecord(owner.native_session_takeover)
151
+ ? owner.native_session_takeover
152
+ : undefined;
153
+ const transferId = stringValue(takeover?.deferred_foreground_transfer_id);
154
+ const ownerControl = terminalControlFromTakeover(takeover);
155
+ if (!transferId || !takeover || !ownerControl) {
156
+ return undefined;
157
+ }
158
+ const transfer = loadDeferredForegroundTransfer(storeDir, transferId);
159
+ const target = tryLoadManagedSession(storeDir, transfer.target_session_id);
160
+ const source = tryLoadManagedSession(storeDir, transfer.source_session_id);
161
+ const submission = terminalBridgeSubmission(owner);
162
+ const matchingReceipts = terminalBridgeSubmissionReceipts(owner).filter((candidate) => stringValue(candidate.message_id) === messageId);
163
+ const canonical = pathsForConversation(owner.conversation_id, storeDir);
164
+ const targetBinding = transfer.abort_target_after_binding;
165
+ const sourceBinding = transfer.abort_source_after_binding;
166
+ const transferDispatchStartedAt = stringValue(transfer.dispatch_started_at);
167
+ const terminalInputNotStartedAt = stringValue(transfer.terminal_input_not_started_at);
168
+ const chronology = handoffApplication.zeroInputChronology({
169
+ inputStage: transfer.input_stage,
170
+ dispatchStartedAt: transferDispatchStartedAt,
171
+ inputNotStartedAt: terminalInputNotStartedAt,
172
+ dispatchStartedAtMs: validTimestampMs(transferDispatchStartedAt),
173
+ inputNotStartedAtMs: validTimestampMs(terminalInputNotStartedAt)
174
+ });
175
+ if (!handoffApplication.authorityChecksPass([
176
+ () => transfer.version === 2, () => transfer.status === "abort_resolved",
177
+ () => chronology.safe, () => transfer.text_injected_at === undefined,
178
+ () => transfer.enter_dispatched_at === undefined,
179
+ () => transfer.agent_accepted_at === undefined,
180
+ () => transfer.target_session_id === sessionIdForConversation(owner),
181
+ () => transfer.turn_id === turnIdForConversation(owner),
182
+ () => transfer.turn_id === owner.conversation_id,
183
+ () => transfer.message_id === messageId,
184
+ () => transfer.terminal_id === stringValue(takeover.native_session_id),
185
+ () => transfer.process_pid === Number(takeover.terminal_agent_pid),
186
+ () => transfer.process_uuid ===
187
+ stringValue(takeover.terminal_agent_process_uuid),
188
+ () => transfer.process_birth ===
189
+ stringValue(takeover.terminal_agent_process_birth),
190
+ () => path.resolve(transfer.workspace) === path.resolve(owner.workspace),
191
+ () => terminalControlsShareIncarnation(ownerControl, terminalControl),
192
+ () => terminalControlEvidenceMatches(transfer.terminal_endpoint, terminalControl),
193
+ () => owner.status === "failed", () => !isRecord(owner.callback_delivery),
194
+ () => !isRecord(owner.terminal_bridge_completion_claim),
195
+ () => !isRecord(takeover.terminal_bridge_completion_claim),
196
+ () => stringValue(takeover.terminal_bridge_message_id) === messageId,
197
+ () => stringValue(takeover.terminal_bridge_request_hash) ===
198
+ transfer.request_hash,
199
+ () => stringValue(takeover.terminal_bridge_request_hash) ===
200
+ stringValue(receipt.request_hash),
201
+ () => stringValue(takeover.deferred_foreground_transfer_id) ===
202
+ transfer.transfer_id,
203
+ () => path.resolve(stringValue(owner.state_path) ?? "") ===
204
+ path.resolve(canonical.statePath),
205
+ () => path.resolve(stringValue(owner.event_log_path) ?? "") ===
206
+ path.resolve(canonical.logPath),
207
+ () => path.resolve(managedSessionStoreDirForConversation(owner) ?? "") ===
208
+ path.resolve(storeDir),
209
+ () => submission !== undefined, () => matchingReceipts.length === 1,
210
+ () => canonicalJson(matchingReceipts[0]) === canonicalJson(submission),
211
+ () => canonicalJson(submission) === canonicalJson(receipt),
212
+ () => submission?.status === "aborted",
213
+ () => submission?.safe_to_retry === true,
214
+ () => stringValue(submission?.last_proven_stage) === "prepared",
215
+ () => Boolean(validTimestampMs(submission?.prepared_at)),
216
+ () => Boolean(validTimestampMs(submission?.aborted_at)),
217
+ () => Date.parse(String(submission?.aborted_at)) >=
218
+ Date.parse(String(submission?.prepared_at)),
219
+ () => !chronology.dispatchIntentProvedNotStarted ||
220
+ stringValue(submission?.aborted_at) === terminalInputNotStartedAt,
221
+ () => !TERMINAL_INPUT_EVIDENCE_FIELDS.some((field) => submission?.[field] !== undefined),
222
+ () => stringValue(submission?.session_id) === transfer.target_session_id,
223
+ () => stringValue(submission?.turn_id) === transfer.turn_id,
224
+ () => stringValue(submission?.message_id) === transfer.message_id,
225
+ () => stringValue(submission?.request_hash) === transfer.request_hash,
226
+ () => stringValue(submission?.binding_id) ===
227
+ transfer.target_before_binding?.binding_id,
228
+ () => Number(submission?.binding_generation) ===
229
+ transfer.target_before_binding?.generation,
230
+ () => stringValue(submission?.native_thread_id) === undefined,
231
+ () => target !== undefined, () => target?.status === "detached",
232
+ () => target?.last_transition_id === transfer.transfer_id,
233
+ () => target?.lineage.transition_id === transfer.transfer_id,
234
+ () => target?.lineage.previous_session_id === transfer.source_session_id,
235
+ () => transfer.abort_target_after_status === "detached",
236
+ () => targetBinding !== undefined,
237
+ () => managedSessionRevision(target) === transfer.abort_target_after_revision,
238
+ () => managedSessionBindingToken(target) ===
239
+ transfer.abort_target_after_binding_token,
240
+ () => JSON.stringify(target?.binding) === JSON.stringify(targetBinding),
241
+ () => source !== undefined, () => source?.status === "bound",
242
+ () => source?.last_transition_id ===
243
+ transfer.source_previous_last_transition_id,
244
+ () => transfer.abort_source_after_status === "bound",
245
+ () => sourceBinding !== undefined,
246
+ () => managedSessionRevision(source) === transfer.abort_source_after_revision,
247
+ () => managedSessionBindingToken(source) ===
248
+ transfer.abort_source_after_binding_token,
249
+ () => JSON.stringify(source?.binding) === JSON.stringify(sourceBinding),
250
+ () => JSON.stringify(sourceBinding) ===
251
+ JSON.stringify(transfer.source_before_binding)
252
+ ])) {
253
+ return undefined;
254
+ }
255
+ const ledger = loadTerminalBridgeDispatchLedger(terminalControl);
256
+ if (!ledger) {
257
+ return undefined;
258
+ }
259
+ deferredRecoveryAdapter.assertDeferredForegroundResolvedZeroInputLedger(deferredForegroundRecoveryAdapterPorts(), {
260
+ storeDir,
261
+ terminal: { terminalControl },
262
+ transfer,
263
+ ledger,
264
+ statePath: canonical.statePath
265
+ });
266
+ if (chronology.dispatchIntentProvedNotStarted &&
267
+ stringValue(ledger.aborted_at) !== terminalInputNotStartedAt) {
268
+ return undefined;
269
+ }
270
+ return source;
271
+ }
272
+ function exactSafeAbortedRecoveredSessionMatches({ owner, receipt, storeDir, terminalControl, messageId, expectedSessionId }) {
273
+ const exactReceipt = receipt ?? (() => {
274
+ const matches = terminalBridgeSubmissionReceipts(owner).filter((candidate) => stringValue(candidate.message_id) === messageId);
275
+ return matches.length === 1 ? matches[0] : undefined;
276
+ })();
277
+ if (!exactReceipt ||
278
+ exactReceipt.status !== "aborted" ||
279
+ exactReceipt.safe_to_retry !== true) {
280
+ return false;
281
+ }
282
+ const recoveredSession = assertSafeAbortedTerminalRetryBinding({
283
+ owner,
284
+ receipt: exactReceipt,
285
+ storeDir,
286
+ terminalControl,
287
+ messageId
288
+ });
289
+ return recoveredSession?.session_id === expectedSessionId;
290
+ }
291
+ async function maybeDetachVerifiedEmptyCodexSource({ options, terminal, sourceSession, observation }) {
292
+ if (terminal.agent !== "codex" ||
293
+ !sourceSession?.binding ||
294
+ observation.status !== "verified_absent") {
295
+ return undefined;
296
+ }
297
+ const processUuid = sourceSession.binding.native_process.process_uuid;
298
+ const processBirth = sourceSession.binding.native_process.process_birth;
299
+ const workspace = terminal.terminalControl.currentPath;
300
+ const liveIncarnation = codexProcessIncarnationForPid(terminal.pid);
301
+ if (!processUuid ||
302
+ !processBirth ||
303
+ !workspace ||
304
+ !exactBoundCodexSendSource({
305
+ kind: "verified_empty",
306
+ sourceSession,
307
+ context: {
308
+ terminalId: terminal.conversationId,
309
+ terminalControl: terminal.terminalControl,
310
+ pid: terminal.pid,
311
+ workspace,
312
+ liveProcessUuid: liveIncarnation.processUuid,
313
+ liveProcessBirth: liveIncarnation.processBirth
314
+ }
315
+ })) {
316
+ return undefined;
317
+ }
318
+ const expectedToken = verifiedEmptyCodexHandoffToken({
319
+ terminalId: terminal.conversationId,
320
+ terminalControl: terminal.terminalControl,
321
+ pid: terminal.pid,
322
+ workspace,
323
+ processUuid,
324
+ processBirth,
325
+ sourceSession
326
+ });
327
+ if (stringValue(options.expectedTerminalToken) !== expectedToken) {
328
+ throw new Error("verified-empty Codex handoff requires the fresh exact terminal token " +
329
+ "advertised by AKK list");
330
+ }
331
+ await assertVerifiedEmptyCodexHandoffBoundary({
332
+ options,
333
+ terminal,
334
+ sourceSession,
335
+ expectedSourceStatus: "bound",
336
+ requireNoDispatch: true
337
+ });
338
+ await assertNativeThreadHasExclusiveOwnership({
339
+ options,
340
+ agent: "codex",
341
+ currentPid: terminal.pid,
342
+ nativeThreadId: sourceSession.binding.native_thread_id,
343
+ storeDir: storeDirFromOptions(options),
344
+ terminalControl: terminal.terminalControl,
345
+ excludedManagedSessionId: sourceSession.session_id
346
+ });
347
+ const detachedAt = cliNow().toISOString();
348
+ const detached = saveManagedSession(storeDirFromOptions(options), {
349
+ ...sourceSession,
350
+ status: "detached",
351
+ detached_at: detachedAt,
352
+ updated_at: detachedAt
353
+ }, {
354
+ expectedRevision: managedSessionRevision(sourceSession)
355
+ });
356
+ runtimeLog("info", "verified_empty_codex_source_detached", {
357
+ terminal_id: terminal.conversationId,
358
+ source_session_id: sourceSession.session_id,
359
+ native_thread_id: sourceSession.binding.native_thread_id,
360
+ process_uuid: processUuid,
361
+ process_birth: processBirth,
362
+ terminal_input_sent: false
363
+ });
364
+ return {
365
+ detached,
366
+ boundary: {
367
+ terminal,
368
+ detachedSourceSessionId: detached.session_id,
369
+ detachedSourceRevision: managedSessionRevision(detached),
370
+ detachedSourceBindingToken: managedSessionBindingToken(detached),
371
+ processUuid,
372
+ processBirth
373
+ }
374
+ };
375
+ }
376
+ async function assertVerifiedEmptyCodexTransportBoundary({ options, boundary, requireEmptyComposer }) {
377
+ const source = loadManagedSession(storeDirFromOptions(options), boundary.detachedSourceSessionId);
378
+ if (source.status !== "detached" ||
379
+ managedSessionRevision(source) !== boundary.detachedSourceRevision ||
380
+ managedSessionBindingToken(source) !==
381
+ boundary.detachedSourceBindingToken ||
382
+ source.binding?.native_process.process_uuid !== boundary.processUuid ||
383
+ source.binding.native_process.process_birth !== boundary.processBirth) {
384
+ throw new Error("verified-empty Codex source authority changed before text injection");
385
+ }
386
+ await assertVerifiedEmptyCodexHandoffBoundary({
387
+ options,
388
+ terminal: boundary.terminal,
389
+ sourceSession: source,
390
+ expectedSourceStatus: "detached",
391
+ requireNoDispatch: false,
392
+ requireEmptyComposer
393
+ });
394
+ }
395
+ function deferredForegroundBoundaryAdapterPorts(options, storeDir) {
396
+ return {
397
+ processIncarnation: codexProcessIncarnationForPid,
398
+ inventory: (boundary) => inspectCodexOpenRootRolloutInventory({
399
+ options,
400
+ pid: boundary.terminal.pid,
401
+ cwd: boundary.terminal.terminalControl.currentPath
402
+ }),
403
+ nativeIdentity: (boundary) => observeCurrentNativeAgentSessionIdentity({
404
+ options,
405
+ agent: "codex",
406
+ pid: boundary.terminal.pid,
407
+ cwd: boundary.terminal.terminalControl.currentPath
408
+ }),
409
+ authority: ({ boundary, sourceAsBound, candidateInventory, expectedSourceStatus }) => observeDeferredCodexAuthority({
410
+ mode: expectedSourceStatus === "bound"
411
+ ? "boundary_bound"
412
+ : "boundary_transitioning",
413
+ storeDir,
414
+ context: {
415
+ terminalId: boundary.terminal.conversationId,
416
+ terminalControl: boundary.terminal.terminalControl,
417
+ pid: boundary.terminal.pid,
418
+ workspace: boundary.terminal.terminalControl.currentPath,
419
+ liveProcessUuid: boundary.processUuid,
420
+ liveProcessBirth: boundary.processBirth
421
+ },
422
+ sourceSession: sourceAsBound,
423
+ candidateInventory,
424
+ abandonment: "never",
425
+ fixedSourceRolloutAuthority: boundary.sourceRolloutAuthority,
426
+ fixedDispatchSnapshot: boundary.previousDispatchSnapshot,
427
+ sourceRevision: boundary.sourceBoundRevision,
428
+ sourceBindingToken: boundary.sourceBoundBindingToken
429
+ }),
430
+ assertNoDispatch: (_scope, boundary) => terminalListCliFacade.assertTerminalIncarnationCanStartTurn(storeDir, boundary.terminal.terminalControl),
431
+ dispatchSnapshot: (boundary) => deferredCodexForegroundDispatchSnapshot(boundary.terminal.terminalControl),
432
+ status: async (boundary) => {
433
+ const status = await createTerminalAgentBridge(options).status("codex", boundary.terminal.terminalControl, {
434
+ runtime: terminalRuntimeForLiveIdentity({
435
+ terminal: boundary.terminal,
436
+ expectedEmptyNativeSession: boundary.candidateAcceptanceAnchor === undefined,
437
+ physicalOnly: boundary.candidateAcceptanceAnchor !== undefined
438
+ })
439
+ });
440
+ return {
441
+ reachable: status.reachable === true,
442
+ approvalBlocked: status.approval_state.blocked === true,
443
+ activityState: status.activity_state,
444
+ activityReason: status.activity_reason
445
+ };
446
+ },
447
+ assertComposerReady: (boundary) => assertCodexComposerReadyForAutomatedInput({
448
+ options,
449
+ terminalControl: boundary.terminal.terminalControl
450
+ }),
451
+ valuesMatch: (left, right) => JSON.stringify(left) === JSON.stringify(right)
452
+ };
453
+ }
454
+ async function assertDeferredCodexForegroundBindingBoundary({ options, scope, boundary, expectedSourceStatus, requireNoDispatch, requireEmptyComposer }) {
455
+ const storeDir = storeDirFromOptions(options);
456
+ return assertDeferredForegroundBoundary({
457
+ scope,
458
+ storeDir,
459
+ boundary,
460
+ applicationBoundary: deferredForegroundBoundaryProjection(boundary),
461
+ expectedSourceStatus,
462
+ requireNoDispatch,
463
+ requireEmptyComposer,
464
+ ports: deferredForegroundBoundaryAdapterPorts(options, storeDir)
465
+ });
466
+ }
467
+ async function prepareDeferredCodexForegroundBinding({ options, scope, terminal, sourceSession, observation, candidateInventory, requestText, allowImplicitFreshAuthority = false }) {
468
+ const storeDir = storeDirFromOptions(options);
469
+ const prepared = await prepareDeferredForegroundBinding({
470
+ scope,
471
+ terminal: projectDeferredForegroundTerminalFacts(terminal),
472
+ sourceSession,
473
+ nativeIdentityVerifiedAbsent: observation.status === "verified_absent",
474
+ candidateInventory,
475
+ requestText,
476
+ expectedTerminalToken: stringValue(options.expectedTerminalToken),
477
+ allowImplicitFreshAuthority
478
+ }, {
479
+ authority: {
480
+ processIncarnation: codexProcessIncarnationForPid,
481
+ observeFresh: ({ sourceSession, candidateInventory, liveIncarnation }) => observeDeferredCodexAuthority({
482
+ mode: "prepare",
483
+ storeDir,
484
+ context: {
485
+ terminalId: terminal.conversationId,
486
+ terminalControl: terminal.terminalControl,
487
+ pid: terminal.pid,
488
+ workspace: terminal.terminalControl.currentPath,
489
+ liveProcessUuid: liveIncarnation.processUuid,
490
+ liveProcessBirth: liveIncarnation.processBirth
491
+ },
492
+ sourceSession,
493
+ candidateInventory,
494
+ abandonment: "missing_inventory_rollout"
495
+ }),
496
+ revalidate: async (activeScope, boundary) => {
497
+ await assertDeferredCodexForegroundBindingBoundary({
498
+ options,
499
+ scope: activeScope,
500
+ boundary: deferredForegroundConcreteBoundary(boundary, terminal),
501
+ expectedSourceStatus: "bound",
502
+ requireNoDispatch: true,
503
+ requireEmptyComposer: true
504
+ });
505
+ },
506
+ assertExclusive: (_activeScope, boundary, nativeThreadId, excludedManagedSessionId) => assertNativeThreadHasExclusiveOwnership({
507
+ options,
508
+ agent: "codex",
509
+ currentPid: boundary.terminal.pid,
510
+ nativeThreadId,
511
+ storeDir,
512
+ terminalControl: terminal.terminalControl,
513
+ excludedManagedSessionId
514
+ }),
515
+ candidateInventoryUnclaimed: ({ sourceSession, inventory, includeDetached }) => codexCandidateInventoryHasNoOtherManagedClaim({
516
+ storeDir,
517
+ inventory,
518
+ sourceSessionId: sourceSession.session_id,
519
+ includeDetached
520
+ }),
521
+ abandonmentStillFresh: ({ sourceSession, sourceTurnHistory, sourceAbandonmentFingerprint, dispatchSnapshot }) => {
522
+ const freshHistory = deferredCandidateSourceTurnHistory(storeDir, sourceSession);
523
+ const freshFingerprint = freshHistory
524
+ ? explicitlyAbandonedCandidateSourceFingerprint({
525
+ storeDir,
526
+ session: sourceSession,
527
+ sourceTurnHistory: freshHistory,
528
+ dispatchSnapshot
529
+ })
530
+ : undefined;
531
+ return JSON.stringify(freshHistory) ===
532
+ JSON.stringify(sourceTurnHistory) &&
533
+ freshFingerprint === sourceAbandonmentFingerprint;
534
+ },
535
+ transferMatchesTerminal: (transfer) => deferredForegroundTransferMatchesTerminal(transfer, terminal)
536
+ },
537
+ identity: {
538
+ targetSessionId: createManagedSessionId,
539
+ transferId: createDeferredForegroundTransferId,
540
+ captureCandidateAnchor: (inventory, now) => captureCodexCandidateSetRolloutAcceptanceAnchor({ inventory, now }),
541
+ bindingToken: ({ sourceSession, authority, candidateInventory }) => deferredCodexForegroundBindingToken({
542
+ terminalId: terminal.conversationId,
543
+ terminalControl: terminal.terminalControl,
544
+ pid: terminal.pid,
545
+ workspace: required(terminal.terminalControl.currentPath, "deferred Codex terminal workspace is unavailable"),
546
+ processUuid: sourceSession.binding.native_process.process_uuid,
547
+ processBirth: sourceSession.binding.native_process.process_birth,
548
+ sourceSession,
549
+ dispatchSnapshot: authority.dispatchSnapshot,
550
+ candidateInventory,
551
+ sourceTurnHistory: authority.sourceTurnHistory,
552
+ sourceRolloutAuthority: authority.sourceRolloutAuthority,
553
+ sourceAbandonmentFingerprint: authority.sourceAbandonmentFingerprint
554
+ }),
555
+ requestHash: (text) => required(terminalBridgeRequestFingerprint(terminalSubmissionPayload(text)), "deferred foreground request hash is unavailable")
556
+ },
557
+ runtime: {
558
+ now: cliNow,
559
+ pid: cliPid,
560
+ log: runtimeLog
561
+ }
562
+ });
563
+ return prepared
564
+ ? deferredForegroundConcreteBoundary(prepared, terminal)
565
+ : undefined;
566
+ }
567
+ function deferredForegroundApplication(options, terminal) {
568
+ const concreteBoundary = (boundary) => deferredForegroundConcreteBoundary(boundary, required(terminal, "deferred foreground terminal authority is unavailable"));
569
+ return new DeferredForegroundApplicationService({
570
+ authority: {
571
+ verifyReservedSource: (scope, boundary) => assertDeferredCodexForegroundBindingBoundary({
572
+ options,
573
+ scope,
574
+ boundary: concreteBoundary(boundary),
575
+ expectedSourceStatus: "bound",
576
+ requireNoDispatch: true,
577
+ requireEmptyComposer: true
578
+ }),
579
+ assertExclusive: (_scope, boundary, request) => assertNativeThreadHasExclusiveOwnership({
580
+ options,
581
+ agent: "codex",
582
+ currentPid: request.processPid,
583
+ nativeThreadId: request.nativeThreadId,
584
+ storeDir: storeDirFromOptions(options),
585
+ terminalControl: concreteBoundary(boundary).terminal.terminalControl,
586
+ excludedManagedSessionId: request.excludedManagedSessionId,
587
+ allowedManagedSessionIds: request.allowedManagedSessionIds
588
+ }),
589
+ assertFrozenPredecessor: (_scope, boundary, transfer) => assertFrozenExplicitlyAbandonedPredecessorAuthority({
590
+ storeDir: storeDirFromOptions(options),
591
+ transfer,
592
+ terminalControl: concreteBoundary(boundary).terminal.terminalControl
593
+ }),
594
+ valuesMatch: (left, right) => JSON.stringify(left) === JSON.stringify(right)
595
+ },
596
+ clock: { now: cliNow },
597
+ runtime: {
598
+ crashAt: (point) => {
599
+ const key = {
600
+ source_session_reserved: "AKK_TEST_EXIT_AFTER_DEFERRED_SOURCE_SESSION_RESERVED",
601
+ source_reserved: "AKK_TEST_EXIT_AFTER_DEFERRED_SOURCE_RESERVED",
602
+ target_prepared: "AKK_TEST_EXIT_AFTER_DEFERRED_TARGET_PREPARED",
603
+ source_scrubbed: "AKK_TEST_EXIT_AFTER_DEFERRED_SOURCE_SCRUBBED",
604
+ target_accepted: "AKK_TEST_EXIT_AFTER_DEFERRED_TARGET_ACCEPTED",
605
+ committed: "AKK_TEST_EXIT_AFTER_DEFERRED_COMMITTED",
606
+ source_detached: "AKK_TEST_EXIT_AFTER_DEFERRED_SOURCE_DETACHED",
607
+ target_bound: "AKK_TEST_EXIT_AFTER_DEFERRED_TARGET_BOUND"
608
+ }[point];
609
+ if (cliEnv()[key] === "1")
610
+ cliExit(86);
611
+ },
612
+ errorReceipt: (reason) => JSON.stringify(textSummary(reason)),
613
+ summary: textSummary,
614
+ log: runtimeLog
615
+ }
616
+ });
617
+ }
618
+ function deferredForegroundRecoveryAdapterPorts() {
619
+ return {
620
+ native: {
621
+ processIncarnation: codexProcessIncarnationForPid,
622
+ inventory: inspectCodexOpenRootRolloutInventory,
623
+ identity: resolveCurrentNativeAgentSessionIdentity
624
+ },
625
+ turn: {
626
+ terminalControl: terminalControlFromTakeover,
627
+ storeDir: managedSessionStoreDirForConversation,
628
+ withIdentity: withNativeAgentSessionIdentity,
629
+ withSubmission: withTerminalBridgeSubmission
630
+ },
631
+ ledger: {
632
+ load: loadTerminalBridgeDispatchLedger,
633
+ save: saveTerminalBridgeDispatchLedger,
634
+ matchesControl: terminalDispatchRecordMatchesControl,
635
+ bindingFields: terminalBindingLedgerFields,
636
+ previousSnapshotMatches: deferredCodexPreviousDispatchSnapshotMatches
637
+ },
638
+ authority: {
639
+ assertFrozen: assertFrozenExplicitlyAbandonedPredecessorAuthority,
640
+ assertTurnIdentity: assertNativeAgentIdentityForTurn
641
+ },
642
+ application: {
643
+ abortBeforeInput: ({ options, scope, boundary, reason, terminalInputNotStartedAt }) => deferredForegroundApplication(options, boundary.terminal).abortBeforeInput({
644
+ scope,
645
+ boundary: deferredForegroundBoundaryProjection(boundary),
646
+ reason,
647
+ terminalInputNotStartedAt
648
+ }),
649
+ commit: ({ options, scope, boundary, identity, acceptedAt }) => deferredForegroundApplication(options, boundary.terminal).commit({
650
+ scope,
651
+ boundary: deferredForegroundBoundaryProjection(boundary),
652
+ identity,
653
+ acceptedAt
654
+ })
655
+ }
656
+ };
657
+ }
658
+ function observedHandoffAuthorityToken({ terminal, identity, sourceSession, target }) {
659
+ const exact = exactLifecycleProcessIdentity(terminal, identity);
660
+ return projectObservedHandoffAuthorityToken({
661
+ terminalId: terminal.conversationId,
662
+ terminalControl: terminal.terminalControl,
663
+ agent: terminal.agent,
664
+ pid: terminal.pid,
665
+ workspace: terminal.terminalControl.currentPath ?? cliCwd(),
666
+ identity: exact,
667
+ sourceSession,
668
+ target
669
+ });
670
+ }
671
+ function activeTurnHandoffDecisionToken({ handoffToken, turn, ledger }) {
672
+ const takeover = isRecord(turn.native_session_takeover)
673
+ ? turn.native_session_takeover
674
+ : undefined;
675
+ const submission = terminalBridgeSubmission(turn);
676
+ return projectActiveTurnHandoffDecisionToken({
677
+ handoffToken,
678
+ sessionId: sessionIdForConversation(turn),
679
+ turnId: turnIdForConversation(turn),
680
+ turnStatus: turn.status,
681
+ turnUpdatedAt: turn.updated_at ?? null,
682
+ currentMessageId: stringValue(takeover?.terminal_bridge_message_id) ??
683
+ stringValue(submission?.message_id) ??
684
+ null,
685
+ ledgerGenerationId: stringValue(ledger?.generation_id) ?? null,
686
+ ledgerMessageId: stringValue(ledger?.message_id) ?? null,
687
+ ledgerStatus: stringValue(ledger?.status) ?? null
688
+ });
689
+ }
690
+ function assertExpectedHandoffTokenUsesExactTerminalSelector({ options, terminal }) {
691
+ if (!stringValue(options.expectedTerminalToken)) {
692
+ return;
693
+ }
694
+ const supplied = originalExpectedTerminalSelector.has(options)
695
+ ? originalExpectedTerminalSelector.get(options)
696
+ : stringValue(options.session ?? options.conversation ?? options.conversationId)?.trim();
697
+ if (supplied !== terminal.conversationId) {
698
+ throw new Error("--expected-terminal-token is valid only with the exact full terminal " +
699
+ "conversation selector advertised by AKK list");
700
+ }
701
+ }
702
+ async function observedExternalHandoffIdentity({ options, terminal, sourceSession, resolvedIdentity, requireSafeTerminal = true }) {
703
+ const bridge = createTerminalAgentBridge(options);
704
+ const status = await bridge.status(terminal.agent, terminal.terminalControl, { runtime: terminalRuntimeForLiveIdentity({ terminal, physicalOnly: true }) });
705
+ if (requireSafeTerminal) {
706
+ assertSafeTerminalSend(terminal.agent, status);
707
+ }
708
+ if (terminal.agent !== "codex") {
709
+ return { identity: resolvedIdentity, status };
710
+ }
711
+ const sourceBinding = sourceSession.binding;
712
+ const statusCard = terminal.adapter.observeThreadLifecycle?.({
713
+ operation: { kind: "new_thread" },
714
+ phase: "before",
715
+ screen: status.screen.excerpt ?? ""
716
+ });
717
+ const statusCardId = statusCard?.status === "observed" &&
718
+ isExactNativeThreadId(statusCard.nativeThreadId)
719
+ ? statusCard.nativeThreadId.toLowerCase()
720
+ : undefined;
721
+ const sourceId = sourceBinding?.native_thread_id?.toLowerCase();
722
+ if (statusCardId && sourceId && statusCardId !== sourceId) {
723
+ const processUuid = sourceBinding?.native_process.process_uuid;
724
+ const processBirth = sourceBinding?.native_process.process_birth;
725
+ if (!processUuid || !processBirth) {
726
+ return { identity: undefined, status };
727
+ }
728
+ const resolvedMatchesStatus = resolvedIdentity?.sessionId.toLowerCase() === statusCardId;
729
+ return {
730
+ identity: {
731
+ sessionId: statusCardId,
732
+ processUuid,
733
+ processBirth,
734
+ rollout: resolvedMatchesStatus ? resolvedIdentity?.rollout : undefined,
735
+ evidence: resolvedMatchesStatus
736
+ ? `${resolvedIdentity?.evidence ?? "native_thread_boundary"}+codex_status_card`
737
+ : statusCard?.evidence ?? "codex_status_card"
738
+ },
739
+ status
740
+ };
741
+ }
742
+ return { identity: resolvedIdentity, status };
743
+ }
744
+ function observedHandoffTargetResolution({ storeDir, agent, workspace, nativeThreadId, sourceSessionId }) {
745
+ const matches = listManagedSessions(storeDir).filter((session) => session.session_id !== sourceSessionId &&
746
+ session.agent === agent &&
747
+ session.binding?.native_thread_id?.toLowerCase() === nativeThreadId &&
748
+ path.resolve(session.workspace) === path.resolve(workspace));
749
+ if (matches.length > 1) {
750
+ return {
751
+ status: "blocked",
752
+ reason: `native thread ${nativeThreadId} is claimed by multiple managed Sessions`
753
+ };
754
+ }
755
+ const target = matches[0];
756
+ if (!target) {
757
+ return { status: "eligible", snapshot: { state: "absent" } };
758
+ }
759
+ if (!target.binding ||
760
+ target.status !== "detached" ||
761
+ terminalListCliFacade.managedSessionHasUnresolvedNativeTransition(storeDir, target)) {
762
+ return {
763
+ status: "blocked",
764
+ reason: `managed Session ${target.session_id} cannot be adopted from ` +
765
+ `${target.status} state or while its lifecycle is unresolved`
766
+ };
767
+ }
768
+ try {
769
+ assertManagedSessionCanStartTurn(managedTurnsForSession(storeDir, target.session_id));
770
+ }
771
+ catch (error) {
772
+ return {
773
+ status: "blocked",
774
+ reason: `managed Session ${target.session_id} has unresolved work: ` +
775
+ `${error instanceof Error ? error.message : String(error)}`
776
+ };
777
+ }
778
+ return {
779
+ status: "eligible",
780
+ session: target,
781
+ snapshot: {
782
+ state: "detached",
783
+ session_id: target.session_id,
784
+ revision: managedSessionRevision(target),
785
+ status: "detached",
786
+ binding_token: managedSessionBindingToken(target)
787
+ }
788
+ };
789
+ }
790
+ async function maybeAdoptObservedExternalThread({ options, terminal, sourceSession, resolvedIdentity, storeDir }) {
791
+ if (!sourceSession?.binding) {
792
+ return { identity: resolvedIdentity, adopted: false };
793
+ }
794
+ const observed = await observedExternalHandoffIdentity({
795
+ options,
796
+ terminal,
797
+ sourceSession,
798
+ resolvedIdentity,
799
+ // Snapshot-bound terminal actions apply their own stricter boundary
800
+ // before any input. In particular, a post-/clear Codex composer can be
801
+ // safely empty while the passive activity classifier is still unknown;
802
+ // do not force the unrelated external-handoff idle check before the
803
+ // deferred candidate boundary gets a chance to revalidate it.
804
+ requireSafeTerminal: !stringValue(options.expectedTerminalToken)
805
+ });
806
+ const identity = observed.identity;
807
+ const conflictKind = managedBindingConflictKindForResolvedTerminal({
808
+ storeDir,
809
+ session: sourceSession,
810
+ terminal,
811
+ identity
812
+ });
813
+ if (conflictKind !== "live_external_thread_change") {
814
+ return { identity, adopted: false };
815
+ }
816
+ assertTerminalLifecycleReady({
817
+ options,
818
+ terminal,
819
+ terminalStatus: observed.status
820
+ });
821
+ if (!identity || !isExactNativeThreadId(identity.sessionId)) {
822
+ throw new Error("the externally selected native thread has no exact supported identity");
823
+ }
824
+ if (terminalListCliFacade.managedSessionHasUnresolvedNativeTransition(storeDir, sourceSession)) {
825
+ throw new Error(`managed Session ${sourceSession.session_id} has an unresolved native-thread transition`);
826
+ }
827
+ assertManagedSessionCanStartTurn(managedTurnsForSession(storeDir, sourceSession.session_id));
828
+ if (terminal.agent === "codex"
829
+ ? !codexComposerEmpty(observed.status.screen.excerpt)
830
+ : !claudeComposerEmpty(observed.status.screen.excerpt)) {
831
+ throw new Error("external handoff adoption requires an exact empty idle composer");
832
+ }
833
+ if (terminal.agent === "codex") {
834
+ await assertCodexComposerReadyForAutomatedInput({
835
+ options,
836
+ terminalControl: terminal.terminalControl
837
+ });
838
+ }
839
+ const targetNativeThreadId = identity.sessionId.toLowerCase();
840
+ const targetResolution = observedHandoffTargetResolution({
841
+ storeDir,
842
+ agent: terminal.agent,
843
+ workspace: terminal.terminalControl.currentPath ?? cliCwd(),
844
+ nativeThreadId: targetNativeThreadId,
845
+ sourceSessionId: sourceSession.session_id
846
+ });
847
+ const expectedTerminalToken = stringValue(options.expectedTerminalToken);
848
+ if (targetResolution.status === "blocked") {
849
+ if (expectedTerminalToken) {
850
+ throw new Error("live source or target Session snapshot changed after the handoff was " +
851
+ "listed; refresh AKK list");
852
+ }
853
+ throw new Error(targetResolution.reason);
854
+ }
855
+ const freshHandoffToken = observedHandoffAuthorityToken({
856
+ terminal,
857
+ identity,
858
+ sourceSession,
859
+ target: targetResolution.snapshot
860
+ });
861
+ if (expectedTerminalToken &&
862
+ expectedTerminalToken !== freshHandoffToken) {
863
+ throw new Error("live source, target, or terminal identity changed after the handoff " +
864
+ "was listed; refresh AKK list");
865
+ }
866
+ const targetSession = targetResolution.session;
867
+ await assertNativeThreadHasExclusiveOwnership({
868
+ options,
869
+ agent: terminal.agent,
870
+ currentPid: terminal.pid,
871
+ nativeThreadId: targetNativeThreadId,
872
+ storeDir,
873
+ terminalControl: terminal.terminalControl,
874
+ excludedManagedSessionId: targetSession?.session_id
875
+ });
876
+ const adapterVersion = required(stringValue(agentVersionForRunningProcess(terminal.agent, terminal.pid, options)), "external handoff adoption requires the exact running agent version");
877
+ const capability = terminal.adapter.probeThreadLifecycle?.(adapterVersion);
878
+ if (capability?.status !== "supported") {
879
+ throw new Error(capability?.reason ?? "external handoff adoption is unsupported for this agent version");
880
+ }
881
+ const now = cliNow();
882
+ const sourceBinding = sourceSession.binding;
883
+ const targetSessionId = targetSession?.session_id ?? createManagedSessionId(now);
884
+ const transitionId = createNativeThreadTransitionId();
885
+ const exactIdentity = exactLifecycleProcessIdentity(terminal, identity);
886
+ const nextBinding = terminalBindingFrom({
887
+ terminalId: terminal.conversationId,
888
+ terminalControl: terminal.terminalControl,
889
+ pid: terminal.pid,
890
+ nativeThreadId: targetNativeThreadId,
891
+ processUuid: exactIdentity.processUuid,
892
+ processBirth: exactIdentity.processBirth,
893
+ rollout: exactIdentity.rollout,
894
+ evidence: `${exactIdentity.evidence}+human_observed`,
895
+ generation: (targetSession?.binding?.generation ?? 0) + 1,
896
+ now
897
+ });
898
+ const previousLedger = loadTerminalBridgeDispatchLedger(terminal.terminalControl);
899
+ let transition = {
900
+ schema: "agent-knock-knock/native-thread-transition",
901
+ version: 1,
902
+ transition_id: transitionId,
903
+ operation: "adopt_external_thread",
904
+ origin: "human_observed",
905
+ terminal_input_sent: false,
906
+ status: "prepared",
907
+ terminal_id: terminal.conversationId,
908
+ agent: terminal.agent,
909
+ workspace: terminal.terminalControl.currentPath ?? cliCwd(),
910
+ source_session_id: sourceSession.session_id,
911
+ source_expected_revision: managedSessionRevision(sourceSession),
912
+ source_previous_last_transition_id: sourceSession.last_transition_id,
913
+ target_session_id: targetSessionId,
914
+ target_expected_revision: targetSession
915
+ ? managedSessionRevision(targetSession)
916
+ : null,
917
+ target_native_thread_id: targetNativeThreadId,
918
+ before_native_thread_id: sourceBinding.native_thread_id,
919
+ before_process_uuid: sourceBinding.native_process.process_uuid,
920
+ before_process_started_at: exactIdentity.processStartedAt,
921
+ before_process_birth: sourceBinding.native_process.process_birth,
922
+ before_process_rollout: sourceBinding.native_process.rollout,
923
+ before_binding: sourceBinding,
924
+ adapter_version: adapterVersion,
925
+ command_fingerprint: HUMAN_OBSERVED_HANDOFF_FINGERPRINT,
926
+ dispatcher_pid: cliPid(),
927
+ prepared_at: now.toISOString()
928
+ };
929
+ transition = saveNativeThreadTransition(storeDir, transition, {
930
+ expectedRevision: null
931
+ });
932
+ if (cliEnv().AKK_TEST_EXIT_AFTER_HANDOFF_TRANSITION_BEFORE_LEDGER === "1") {
933
+ cliExit(88);
934
+ }
935
+ saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, { phase: "prepared", previous: previousLedger, targetNativeThreadId }), { expectedTransitionId: null });
936
+ if (cliEnv().AKK_TEST_EXIT_AFTER_LIFECYCLE_PREPARED === "1") {
937
+ cliExit(86);
938
+ }
939
+ const sourceTransitioning = saveManagedSession(storeDir, {
940
+ ...sourceSession,
941
+ status: "transitioning",
942
+ last_transition_id: transitionId,
943
+ updated_at: now.toISOString()
944
+ }, { expectedRevision: managedSessionRevision(sourceSession) });
945
+ try {
946
+ const reObserved = await observedExternalHandoffIdentity({
947
+ options,
948
+ terminal,
949
+ sourceSession: sourceTransitioning,
950
+ resolvedIdentity: await resolveCurrentNativeAgentSessionIdentity({
951
+ options,
952
+ agent: terminal.agent,
953
+ pid: terminal.pid,
954
+ cwd: terminal.terminalControl.currentPath,
955
+ preferredSessionId: targetNativeThreadId,
956
+ allowedCompanionIdentity: codexIdentityFence({
957
+ sessionId: sourceBinding.native_thread_id,
958
+ processUuid: sourceBinding.native_process.process_uuid,
959
+ processBirth: sourceBinding.native_process.process_birth,
960
+ rollout: sourceBinding.native_process.rollout,
961
+ evidence: sourceBinding.native_process.evidence
962
+ }),
963
+ allowedAdditionalIdentities: []
964
+ })
965
+ });
966
+ const reObservedExact = reObserved.identity
967
+ ? exactLifecycleProcessIdentity(terminal, reObserved.identity)
968
+ : undefined;
969
+ if (reObservedExact?.sessionId.toLowerCase() !== targetNativeThreadId ||
970
+ reObservedExact.processUuid !== nextBinding.native_process.process_uuid ||
971
+ reObservedExact.processBirth !== nextBinding.native_process.process_birth ||
972
+ JSON.stringify(reObservedExact.rollout ?? null) !==
973
+ JSON.stringify(nextBinding.native_process.rollout ?? null)) {
974
+ throw new Error("live native thread changed during external handoff adoption");
975
+ }
976
+ await assertNativeThreadHasExclusiveOwnership({
977
+ options,
978
+ agent: terminal.agent,
979
+ currentPid: terminal.pid,
980
+ nativeThreadId: targetNativeThreadId,
981
+ storeDir,
982
+ terminalControl: terminal.terminalControl,
983
+ excludedManagedSessionId: targetSession?.session_id
984
+ });
985
+ transition = saveNativeThreadTransition(storeDir, {
986
+ ...transition,
987
+ status: "verified",
988
+ after_binding: nextBinding,
989
+ verified_at: cliNow().toISOString()
990
+ }, { expectedRevision: nativeThreadTransitionRevision(transition) });
991
+ if (cliEnv().AKK_TEST_EXIT_AFTER_HANDOFF_VERIFIED_TRANSITION_BEFORE_LEDGER ===
992
+ "1") {
993
+ cliExit(89);
994
+ }
995
+ saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, { phase: "verified", binding: sourceBinding }), {
996
+ expectedTransitionId: transitionId,
997
+ expectedStatus: "prepared"
998
+ });
999
+ if (cliEnv().AKK_TEST_EXIT_AFTER_LIFECYCLE_VERIFIED === "1") {
1000
+ cliExit(87);
1001
+ }
1002
+ const committedTarget = commitVerifiedLifecycleTransition(storeDir, transition, cliNow().toISOString());
1003
+ transition = saveNativeThreadTransition(storeDir, {
1004
+ ...transition,
1005
+ status: "committed",
1006
+ committed_at: cliNow().toISOString()
1007
+ }, { expectedRevision: nativeThreadTransitionRevision(transition) });
1008
+ saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, {
1009
+ phase: "resolved_with_binding", at: cliNow().toISOString(),
1010
+ binding: committedTarget.binding,
1011
+ reason: "verified human-observed native thread handoff committed"
1012
+ }), {
1013
+ expectedTransitionId: transitionId,
1014
+ expectedStatus: "verified"
1015
+ });
1016
+ runtimeLog("info", "human_observed_handoff_adopted", {
1017
+ transition_id: transitionId,
1018
+ terminal_id: terminal.conversationId,
1019
+ source_session_id: sourceSession.session_id,
1020
+ target_session_id: committedTarget.session_id,
1021
+ native_thread_id: targetNativeThreadId,
1022
+ terminal_input_sent: false
1023
+ });
1024
+ return {
1025
+ session: committedTarget,
1026
+ identity: exactIdentity,
1027
+ transition,
1028
+ adopted: true
1029
+ };
1030
+ }
1031
+ catch (error) {
1032
+ const failedAt = cliNow().toISOString();
1033
+ const durable = loadNativeThreadTransition(storeDir, transitionId);
1034
+ if (durable.status === "verified" || durable.status === "committed") {
1035
+ throw error;
1036
+ }
1037
+ const uncertain = saveNativeThreadTransition(storeDir, {
1038
+ ...durable,
1039
+ status: "uncertain",
1040
+ uncertain_at: failedAt,
1041
+ error: error instanceof Error ? error.message : String(error),
1042
+ do_not_retry: true
1043
+ }, { expectedRevision: nativeThreadTransitionRevision(durable) });
1044
+ saveManagedSession(storeDir, {
1045
+ ...sourceTransitioning,
1046
+ status: "quarantined",
1047
+ quarantine_reason: "human-observed handoff could not be revalidated",
1048
+ updated_at: failedAt
1049
+ }, { expectedRevision: managedSessionRevision(sourceTransitioning) });
1050
+ saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(uncertain, storeDir, {
1051
+ phase: "uncertain", at: failedAt,
1052
+ reason: "human-observed handoff could not be revalidated"
1053
+ }), { expectedTransitionId: transitionId });
1054
+ throw error;
1055
+ }
1056
+ }
1057
+ async function assertObservedHandoffTransportBoundary({ options, terminal, transition, requireEmptyComposer }) {
1058
+ const storeDir = storeDirFromOptions(options);
1059
+ const durable = loadNativeThreadTransition(storeDir, transition.transition_id);
1060
+ handoffApplication.assertAuthority([
1061
+ () => durable.operation === "adopt_external_thread",
1062
+ () => durable.origin === "human_observed",
1063
+ () => durable.terminal_input_sent === false,
1064
+ () => durable.status === "committed",
1065
+ () => Boolean(durable.source_session_id),
1066
+ () => durable.before_binding !== undefined,
1067
+ () => durable.after_binding !== undefined,
1068
+ () => JSON.stringify(durable.after_binding) ===
1069
+ JSON.stringify(transition.after_binding)
1070
+ ], "human-observed handoff changed before terminal transport");
1071
+ const sourceSessionId = durable.source_session_id;
1072
+ const beforeBinding = durable.before_binding;
1073
+ const afterBinding = durable.after_binding;
1074
+ const source = loadManagedSession(storeDir, sourceSessionId);
1075
+ const target = loadManagedSession(storeDir, durable.target_session_id);
1076
+ handoffApplication.assertAuthority([
1077
+ () => source.status === "detached",
1078
+ () => source.last_transition_id === durable.transition_id,
1079
+ () => JSON.stringify(source.binding) === JSON.stringify(beforeBinding),
1080
+ () => target.status === "bound",
1081
+ () => target.last_transition_id === durable.transition_id,
1082
+ () => JSON.stringify(target.binding) === JSON.stringify(afterBinding)
1083
+ ], "human-observed handoff Session authority changed before send");
1084
+ const targetId = afterBinding.native_thread_id;
1085
+ if (!targetId) {
1086
+ throw new Error("human-observed handoff target identity is incomplete");
1087
+ }
1088
+ const resolved = await resolveCurrentNativeAgentSessionIdentity({
1089
+ options,
1090
+ agent: terminal.agent,
1091
+ pid: terminal.pid,
1092
+ cwd: terminal.terminalControl.currentPath,
1093
+ preferredSessionId: targetId,
1094
+ allowedCompanionIdentity: codexIdentityFence({
1095
+ sessionId: durable.before_native_thread_id,
1096
+ processUuid: durable.before_process_uuid,
1097
+ processBirth: durable.before_process_birth,
1098
+ rollout: durable.before_process_rollout,
1099
+ evidence: beforeBinding.native_process.evidence
1100
+ }),
1101
+ allowedAdditionalIdentities: []
1102
+ });
1103
+ const bridge = createTerminalAgentBridge(options);
1104
+ const status = await bridge.status(terminal.agent, terminal.terminalControl, { runtime: terminalRuntimeForLiveIdentity({ terminal, physicalOnly: true }) });
1105
+ if (requireEmptyComposer) {
1106
+ assertSafeTerminalSend(terminal.agent, status);
1107
+ }
1108
+ else {
1109
+ const displayName = executorDefinitionForKind(terminal.agent).displayName;
1110
+ const approval = isRecord(status?.approval_state)
1111
+ ? status.approval_state
1112
+ : undefined;
1113
+ if (status?.reachable !== true) {
1114
+ throw new Error(`${displayName} terminal status is unavailable`);
1115
+ }
1116
+ if (approval?.blocked === true) {
1117
+ throw new Error(stringValue(approval.reason) ??
1118
+ `${displayName} is waiting at a permission dialog`);
1119
+ }
1120
+ // With an exact draft in the composer, native TUIs can classify the screen
1121
+ // as `unknown` instead of `idle`. Exact draft materialization is proven by
1122
+ // the bridge before Enter, so only a positively busy state is unsafe here.
1123
+ if (status.activity_state !== "idle" &&
1124
+ status.activity_state !== "unknown") {
1125
+ throw new Error(`${displayName} terminal became ${stringValue(status.activity_state) ?? "unknown"} before handoff submission`);
1126
+ }
1127
+ }
1128
+ let liveIdentity = resolved;
1129
+ if (terminal.agent === "codex") {
1130
+ const foreground = terminal.adapter.observeThreadLifecycle?.({
1131
+ operation: { kind: "new_thread" },
1132
+ phase: "before",
1133
+ screen: status.screen.excerpt ?? ""
1134
+ });
1135
+ const foregroundId = foreground?.status === "observed" &&
1136
+ isExactNativeThreadId(foreground.nativeThreadId)
1137
+ ? foreground.nativeThreadId.toLowerCase()
1138
+ : undefined;
1139
+ if (foregroundId && foregroundId !== targetId.toLowerCase()) {
1140
+ throw new Error("Codex foreground native thread changed after handoff adoption");
1141
+ }
1142
+ if (!afterBinding.native_process.rollout) {
1143
+ if (requireEmptyComposer &&
1144
+ foregroundId !== targetId.toLowerCase()) {
1145
+ throw new Error("status-card-only Codex handoff lost its exact foreground identity");
1146
+ }
1147
+ liveIdentity = resolved?.sessionId.toLowerCase() === targetId.toLowerCase()
1148
+ ? resolved
1149
+ : {
1150
+ sessionId: targetId,
1151
+ processUuid: afterBinding.native_process.process_uuid,
1152
+ processBirth: afterBinding.native_process.process_birth,
1153
+ evidence: foreground?.evidence ?? "codex_status_card"
1154
+ };
1155
+ }
1156
+ else if (!liveIdentity ||
1157
+ liveIdentity.sessionId.toLowerCase() !== targetId.toLowerCase()) {
1158
+ throw new Error("Codex handoff rollout identity changed before terminal transport");
1159
+ }
1160
+ }
1161
+ if (!liveIdentity) {
1162
+ throw new Error("human-observed handoff identity is unavailable before send");
1163
+ }
1164
+ const exact = exactLifecycleProcessIdentity(terminal, liveIdentity);
1165
+ handoffApplication.assertAuthority([
1166
+ () => exact.sessionId.toLowerCase() === targetId.toLowerCase(),
1167
+ () => exact.processUuid === afterBinding.native_process.process_uuid,
1168
+ () => exact.processBirth === afterBinding.native_process.process_birth,
1169
+ () => JSON.stringify(exact.rollout ?? null) ===
1170
+ JSON.stringify(afterBinding.native_process.rollout ?? null)
1171
+ ], "human-observed handoff identity changed before send");
1172
+ if (requireEmptyComposer) {
1173
+ const empty = terminal.agent === "codex"
1174
+ ? codexComposerEmpty(status.screen.excerpt)
1175
+ : claudeComposerEmpty(status.screen.excerpt);
1176
+ if (!empty) {
1177
+ throw new Error("human-observed handoff composer changed before text injection");
1178
+ }
1179
+ if (terminal.agent === "codex") {
1180
+ await assertCodexComposerReadyForAutomatedInput({
1181
+ options,
1182
+ terminalControl: terminal.terminalControl
1183
+ });
1184
+ }
1185
+ }
1186
+ }
1187
+ function assertConversationHasNoNonterminalDeferredForegroundTransfer({ storeDir, conversation, action }) {
1188
+ const turnId = turnIdForConversation(conversation);
1189
+ const sourceTransfer = listDeferredForegroundTransfers(storeDir).find((candidate) => candidate.version === 2 &&
1190
+ candidate.source_kind === "candidate_rollout_quiescent" &&
1191
+ !isFinalDeferredForegroundTransferStatus(candidate.status) &&
1192
+ (candidate.source_turn_history ?? []).some((sourceTurn) => sourceTurn.turn_id === turnId));
1193
+ if (sourceTransfer) {
1194
+ throw new Error(`cannot ${action} Turn ${turnId} while deferred foreground transfer ` +
1195
+ `${sourceTransfer.transfer_id} reserves it as immutable source ` +
1196
+ `history in ${sourceTransfer.status}; dedicated transfer recovery ` +
1197
+ "must finish first");
1198
+ }
1199
+ const takeover = isRecord(conversation.native_session_takeover)
1200
+ ? conversation.native_session_takeover
1201
+ : undefined;
1202
+ const transferId = stringValue(takeover?.deferred_foreground_transfer_id);
1203
+ if (!transferId) {
1204
+ return;
1205
+ }
1206
+ const transfer = loadDeferredForegroundTransfer(storeDir, transferId);
1207
+ if (!isFinalDeferredForegroundTransferStatus(transfer.status)) {
1208
+ throw new Error(`cannot ${action} Turn ${turnIdForConversation(conversation)} while ` +
1209
+ `deferred foreground transfer ${transfer.transfer_id} is ` +
1210
+ `${transfer.status}; dedicated transfer recovery must finish first`);
1211
+ }
1212
+ }
1213
+ function assertTerminalHasNoNonterminalDeferredForegroundTransfer({ storeDir, pid, terminalControl, action }) {
1214
+ const transfer = listDeferredForegroundTransfers(storeDir).find((candidate) => !isFinalDeferredForegroundTransferStatus(candidate.status) &&
1215
+ candidate.process_pid === pid &&
1216
+ terminalControlEvidenceMatches(candidate.terminal_endpoint, terminalControl));
1217
+ if (!transfer) {
1218
+ return;
1219
+ }
1220
+ throw new Error(`cannot ${action} terminal ${terminalControl.target} while deferred ` +
1221
+ `foreground transfer ${transfer.transfer_id} is ${transfer.status}; ` +
1222
+ "dedicated transfer recovery must finish first");
1223
+ }
1224
+ async function recoverDeferredCodexForegroundTransferBeforeMutation({ options, terminal }) {
1225
+ const storeDir = storeDirFromOptions(options);
1226
+ const locks = terminalWriterMutationLocks(storeDir, terminal.terminalControl);
1227
+ await withCanonicalMutationLocks({
1228
+ ...locks,
1229
+ // Every caller already owns this exact terminal lock. The no-op adapter
1230
+ // creates the canonical scope without recursively acquiring the raw lock.
1231
+ acquireTerminal: () => () => { }
1232
+ }, (scopes, resources) => recoverDeferredCodexForegroundTransferWhileWriterLease({
1233
+ options,
1234
+ terminal,
1235
+ storeDir,
1236
+ scopes,
1237
+ resources
1238
+ }));
1239
+ }
1240
+ async function withDeferredForegroundRecoveryScope({ scopes, resources, transfer, operation }) {
1241
+ if (!transfer.state_path) {
1242
+ return operation(bindDeferredForegroundWriterScope(scopes, resources));
1243
+ }
1244
+ const statePath = path.resolve(transfer.state_path);
1245
+ const paths = pathsForConversationDir(path.dirname(statePath));
1246
+ return withTerminalDispatchStateScope(scopes, resources, statePath, paths.logPath, (stateScopes, stateResources) => operation(bindDeferredForegroundApplicationScope(stateScopes, stateResources)));
1247
+ }
1248
+ function matchingDeferredForegroundTransfers(scope, terminal) {
1249
+ return scope.listTransfers().filter((transfer) => transfer.terminal_id === terminal.conversationId &&
1250
+ transfer.process_pid === terminal.pid &&
1251
+ terminalControlEvidenceMatches(transfer.terminal_endpoint, terminal.terminalControl));
1252
+ }
1253
+ async function recoverDeferredCodexForegroundTransferWhileWriterLease({ options, terminal, storeDir, scopes, resources }) {
1254
+ options = Object.freeze({ ...options, storeDir });
1255
+ const service = new DeferredForegroundRecoveryService({
1256
+ transaction: {
1257
+ writerScope: () => bindDeferredForegroundWriterScope(scopes, resources),
1258
+ withTransferScope: (transfer, operation) => withDeferredForegroundRecoveryScope({
1259
+ scopes,
1260
+ resources,
1261
+ transfer,
1262
+ operation
1263
+ })
1264
+ },
1265
+ repository: {
1266
+ all: (scope) => scope.listTransfers(),
1267
+ matching: (scope) => matchingDeferredForegroundTransfers(scope, terminal),
1268
+ load: (scope, transferId) => scope.loadTransfer(transferId),
1269
+ markUncertain: (scope, boundary, reason) => deferredForegroundApplication(options, terminal).markUncertain({
1270
+ scope,
1271
+ boundary,
1272
+ reason
1273
+ })
1274
+ },
1275
+ recovery: {
1276
+ boundary: (transfer) => deferredForegroundBoundaryProjection(deferredRecoveryAdapter.deferredCodexBoundaryFromTransfer(deferredForegroundRecoveryAdapterPorts(), { terminal, transfer })),
1277
+ assertRoute: (scope, transfer, boundary) => deferredForegroundApplication(options, terminal)
1278
+ .assertTransferAuthority(scope, transfer, boundary),
1279
+ finalizeAbort: (scope, transfer) => {
1280
+ deferredForegroundApplication(options).finalizeAbort(scope, transfer);
1281
+ },
1282
+ persistCommitted: (scope, transfer) => deferredRecoveryAdapter.persistCommittedDeferredForegroundTurnAcceptance(deferredForegroundRecoveryAdapterPorts(), {
1283
+ options,
1284
+ scope,
1285
+ storeDir,
1286
+ terminal,
1287
+ transfer
1288
+ }),
1289
+ crashAfterCommittedBackfill: () => {
1290
+ if (cliEnv().AKK_TEST_EXIT_AFTER_DEFERRED_COMMITTED_ACCEPTANCE_BACKFILL ===
1291
+ "1") {
1292
+ cliExit(86);
1293
+ }
1294
+ },
1295
+ resolveCommitted: async (scope, boundary) => {
1296
+ await deferredForegroundApplication(options, terminal).resolve({
1297
+ scope,
1298
+ boundary
1299
+ });
1300
+ },
1301
+ assertAcceptedTurn: (accepted) => assertNativeAgentIdentityForTurn({
1302
+ conversation: accepted.conversation,
1303
+ currentIdentity: accepted.identity,
1304
+ operation: "recover committed deferred foreground binding for"
1305
+ }),
1306
+ abortPrepared: (scope, transfer, boundary, at) => deferredRecoveryAdapter.abortPreparedDeferredForegroundTurn(deferredForegroundRecoveryAdapterPorts(), {
1307
+ options,
1308
+ scope,
1309
+ storeDir,
1310
+ terminal,
1311
+ transfer,
1312
+ boundary: deferredForegroundConcreteBoundary(boundary, terminal),
1313
+ terminalInputNotStartedAt: at
1314
+ }),
1315
+ durableInputNotStartedAt: (scope, transfer) => deferredRecoveryAdapter.deferredCodexDurableInputNotStartedAt(deferredForegroundRecoveryAdapterPorts(), scope, transfer),
1316
+ recoverAccepted: (scope, transfer, boundary) => deferredRecoveryAdapter.recoverAcceptedDeferredForegroundDispatch(deferredForegroundRecoveryAdapterPorts(), {
1317
+ options,
1318
+ scope,
1319
+ storeDir,
1320
+ terminal,
1321
+ transfer,
1322
+ boundary: deferredForegroundConcreteBoundary(boundary, terminal)
1323
+ }),
1324
+ pendingAnchorVersion: (scope, transfer) => deferredRecoveryAdapter.loadDeferredForegroundTurnAuthority(deferredForegroundRecoveryAdapterPorts(), {
1325
+ storeDir,
1326
+ terminal,
1327
+ transfer,
1328
+ scope
1329
+ }).anchor.version
1330
+ },
1331
+ runtime: {
1332
+ terminalTarget: terminal.terminalControl.target,
1333
+ isStoreMutationLockTimeout
1334
+ }
1335
+ });
1336
+ await service.recover();
1337
+ }
1338
+ function bindContextObject(target, call) {
1339
+ return new Proxy(target, {
1340
+ get(object, property) {
1341
+ const value = Reflect.get(object, property, object);
1342
+ if (typeof value === "function") {
1343
+ return (...arguments_) => call(() => Reflect.apply(value, object, arguments_));
1344
+ }
1345
+ return value && typeof value === "object"
1346
+ ? bindContextObject(value, call)
1347
+ : value;
1348
+ }
1349
+ });
1350
+ }
1351
+ const terminalHandoffOperations = {
1352
+ assertSafeAbortedTerminalRetryBinding,
1353
+ exactSafeAbortedRecoveredSessionMatches,
1354
+ maybeDetachVerifiedEmptyCodexSource,
1355
+ assertVerifiedEmptyCodexTransportBoundary,
1356
+ assertDeferredCodexForegroundBindingBoundary,
1357
+ prepareDeferredCodexForegroundBinding,
1358
+ observedHandoffAuthorityToken,
1359
+ activeTurnHandoffDecisionToken,
1360
+ assertExpectedHandoffTokenUsesExactTerminalSelector,
1361
+ observedExternalHandoffIdentity,
1362
+ observedHandoffTargetResolution,
1363
+ maybeAdoptObservedExternalThread,
1364
+ assertObservedHandoffTransportBoundary,
1365
+ assertConversationHasNoNonterminalDeferredForegroundTransfer,
1366
+ assertTerminalHasNoNonterminalDeferredForegroundTransfer,
1367
+ recoverDeferredCodexForegroundTransferBeforeMutation,
1368
+ recoverDeferredCodexForegroundTransferWhileWriterLease
1369
+ };
1370
+ export function createTerminalHandoffCliFacade(dependencies) {
1371
+ const runtime = {
1372
+ dependencies,
1373
+ originalExpectedTerminalSelector: new WeakMap()
1374
+ };
1375
+ const call = (operation) => terminalHandoffContext.run(runtime, operation);
1376
+ const contextualOperations = Object.fromEntries(Object.entries(terminalHandoffOperations).map(([name, operation]) => [
1377
+ name,
1378
+ (...arguments_) => call(() => operation(...arguments_))
1379
+ ]));
1380
+ return Object.freeze({
1381
+ ...contextualOperations,
1382
+ rememberOriginalExpectedTerminalSelector: (options, selector) => {
1383
+ runtime.originalExpectedTerminalSelector.set(options, selector);
1384
+ },
1385
+ deferredForegroundApplication: (...arguments_) => bindContextObject(call(() => deferredForegroundApplication(...arguments_)), call),
1386
+ deferredForegroundRecoveryAdapterPorts: () => bindContextObject(call(() => deferredForegroundRecoveryAdapterPorts()), call)
1387
+ });
1388
+ }
1389
+ //# sourceMappingURL=terminal-handoff-cli-adapter.js.map