@scotthuang/agent-knock-knock 0.13.2 → 0.13.4

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 (278) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +27 -6
  3. package/dist/src/callback-outbox-policy.js +27 -6
  4. package/dist/src/callback-outbox-policy.js.map +1 -1
  5. package/dist/src/callback-outbox-service.d.ts +4 -30
  6. package/dist/src/callback-outbox-service.js +21 -154
  7. package/dist/src/callback-outbox-service.js.map +1 -1
  8. package/dist/src/callback-outbox-settlement.js +13 -8
  9. package/dist/src/callback-outbox-settlement.js.map +1 -1
  10. package/dist/src/callback-route-service.d.ts +46 -0
  11. package/dist/src/callback-route-service.js +155 -0
  12. package/dist/src/callback-route-service.js.map +1 -0
  13. package/dist/src/claude-terminal-agent-adapter.js +4 -0
  14. package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
  15. package/dist/src/cli-core.js +9 -1
  16. package/dist/src/cli-core.js.map +1 -1
  17. package/dist/src/codex-process-incarnation.d.ts +8 -0
  18. package/dist/src/codex-process-incarnation.js +36 -0
  19. package/dist/src/codex-process-incarnation.js.map +1 -0
  20. package/dist/src/codex-terminal-agent-adapter.js +4 -0
  21. package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
  22. package/dist/src/deferred-foreground-conflict-policy.d.ts +22 -0
  23. package/dist/src/deferred-foreground-conflict-policy.js +52 -0
  24. package/dist/src/deferred-foreground-conflict-policy.js.map +1 -0
  25. package/dist/src/durable-notification-kernel.d.ts +94 -0
  26. package/dist/src/durable-notification-kernel.js +68 -0
  27. package/dist/src/durable-notification-kernel.js.map +1 -0
  28. package/dist/src/herdr-socket-transport.d.ts +43 -0
  29. package/dist/src/herdr-socket-transport.js +170 -0
  30. package/dist/src/herdr-socket-transport.js.map +1 -0
  31. package/dist/src/herdr-terminal-control-provider.d.ts +2 -45
  32. package/dist/src/herdr-terminal-control-provider.js +8 -173
  33. package/dist/src/herdr-terminal-control-provider.js.map +1 -1
  34. package/dist/src/host-adapter-capabilities.d.ts +35 -0
  35. package/dist/src/host-adapter-capabilities.js +146 -0
  36. package/dist/src/host-adapter-capabilities.js.map +1 -0
  37. package/dist/src/host-adapter.d.ts +9 -3
  38. package/dist/src/host-adapter.js +33 -9
  39. package/dist/src/host-adapter.js.map +1 -1
  40. package/dist/src/host-bridge-tools.d.ts +3 -4
  41. package/dist/src/host-bridge-tools.js +49 -92
  42. package/dist/src/host-bridge-tools.js.map +1 -1
  43. package/dist/src/host-bridge.js +5 -5
  44. package/dist/src/host-bridge.js.map +1 -1
  45. package/dist/src/host-monitor-reconciliation.d.ts +6 -0
  46. package/dist/src/host-monitor-reconciliation.js +96 -0
  47. package/dist/src/host-monitor-reconciliation.js.map +1 -0
  48. package/dist/src/managed-session.js +2 -18
  49. package/dist/src/managed-session.js.map +1 -1
  50. package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +13 -0
  51. package/dist/src/native-thread-lifecycle-cli-adapter.js +635 -3
  52. package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
  53. package/dist/src/openclaw-list-projection.d.ts +2 -0
  54. package/dist/src/openclaw-list-projection.js +3 -0
  55. package/dist/src/openclaw-list-projection.js.map +1 -0
  56. package/dist/src/openclaw-model-facing-field-policy.d.ts +2 -0
  57. package/dist/src/openclaw-model-facing-field-policy.js +3 -0
  58. package/dist/src/openclaw-model-facing-field-policy.js.map +1 -0
  59. package/dist/src/openclaw-plugin-command-adapter.d.ts +6 -29
  60. package/dist/src/openclaw-plugin-command-adapter.js +7 -2370
  61. package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
  62. package/dist/src/openclaw-plugin-helpers.d.ts +2 -121
  63. package/dist/src/openclaw-plugin-helpers.js +2 -1164
  64. package/dist/src/openclaw-plugin-helpers.js.map +1 -1
  65. package/dist/src/openclaw-plugin-schemas.d.ts +2 -510
  66. package/dist/src/openclaw-plugin-schemas.js +2 -519
  67. package/dist/src/openclaw-plugin-schemas.js.map +1 -1
  68. package/dist/src/openclaw-plugin-supervisor.d.ts +4 -5
  69. package/dist/src/openclaw-plugin-supervisor.js +5 -94
  70. package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
  71. package/dist/src/openclaw-plugin-value-helpers.d.ts +2 -0
  72. package/dist/src/openclaw-plugin-value-helpers.js +3 -0
  73. package/dist/src/openclaw-plugin-value-helpers.js.map +1 -0
  74. package/dist/src/openclaw-private-authority-offers.d.ts +4 -39
  75. package/dist/src/openclaw-private-authority-offers.js +3 -258
  76. package/dist/src/openclaw-private-authority-offers.js.map +1 -1
  77. package/dist/src/semantic-private-authority-offers.d.ts +41 -0
  78. package/dist/src/semantic-private-authority-offers.js +261 -0
  79. package/dist/src/semantic-private-authority-offers.js.map +1 -0
  80. package/dist/src/semantic-tool-arguments.d.ts +9 -0
  81. package/dist/src/semantic-tool-arguments.js +64 -0
  82. package/dist/src/semantic-tool-arguments.js.map +1 -0
  83. package/dist/src/semantic-tool-catalog.d.ts +48 -0
  84. package/dist/src/semantic-tool-catalog.js +105 -0
  85. package/dist/src/semantic-tool-catalog.js.map +1 -0
  86. package/dist/src/semantic-tool-command-helpers.d.ts +145 -0
  87. package/dist/src/semantic-tool-command-helpers.js +1272 -0
  88. package/dist/src/semantic-tool-command-helpers.js.map +1 -0
  89. package/dist/src/semantic-tool-list-projection.d.ts +10 -0
  90. package/dist/src/semantic-tool-list-projection.js +497 -0
  91. package/dist/src/semantic-tool-list-projection.js.map +1 -0
  92. package/dist/src/semantic-tool-model-facing-field-policy.d.ts +3 -0
  93. package/dist/src/semantic-tool-model-facing-field-policy.js +66 -0
  94. package/dist/src/semantic-tool-model-facing-field-policy.js.map +1 -0
  95. package/dist/src/semantic-tool-presentation.d.ts +37 -0
  96. package/dist/src/semantic-tool-presentation.js +680 -0
  97. package/dist/src/semantic-tool-presentation.js.map +1 -0
  98. package/dist/src/semantic-tool-private-authority.d.ts +39 -0
  99. package/dist/src/semantic-tool-private-authority.js +602 -0
  100. package/dist/src/semantic-tool-private-authority.js.map +1 -0
  101. package/dist/src/semantic-tool-relay.d.ts +16 -0
  102. package/dist/src/semantic-tool-relay.js +175 -0
  103. package/dist/src/semantic-tool-relay.js.map +1 -0
  104. package/dist/src/semantic-tool-runtime.d.ts +10 -0
  105. package/dist/src/semantic-tool-runtime.js +1157 -0
  106. package/dist/src/semantic-tool-runtime.js.map +1 -0
  107. package/dist/src/semantic-tool-schemas.d.ts +563 -0
  108. package/dist/src/semantic-tool-schemas.js +573 -0
  109. package/dist/src/semantic-tool-schemas.js.map +1 -0
  110. package/dist/src/semantic-tool-value-helpers.d.ts +5 -0
  111. package/dist/src/semantic-tool-value-helpers.js +27 -0
  112. package/dist/src/semantic-tool-value-helpers.js.map +1 -0
  113. package/dist/src/store-event-presentation.d.ts +17 -0
  114. package/dist/src/store-event-presentation.js +33 -0
  115. package/dist/src/store-event-presentation.js.map +1 -0
  116. package/dist/src/store.d.ts +3 -16
  117. package/dist/src/store.js +2 -30
  118. package/dist/src/store.js.map +1 -1
  119. package/dist/src/terminal-action-projection.d.ts +3 -17
  120. package/dist/src/terminal-action-projection.js +13 -41
  121. package/dist/src/terminal-action-projection.js.map +1 -1
  122. package/dist/src/terminal-agent-adapter.d.ts +7 -0
  123. package/dist/src/terminal-agent-adapter.js +8 -0
  124. package/dist/src/terminal-agent-adapter.js.map +1 -1
  125. package/dist/src/terminal-agent-bridge.d.ts +35 -332
  126. package/dist/src/terminal-agent-bridge.js +293 -2798
  127. package/dist/src/terminal-agent-bridge.js.map +1 -1
  128. package/dist/src/terminal-approval-cli-adapter.d.ts +17 -0
  129. package/dist/src/terminal-approval-cli-adapter.js +921 -0
  130. package/dist/src/terminal-approval-cli-adapter.js.map +1 -0
  131. package/dist/src/terminal-command-cli-adapter.d.ts +2 -429
  132. package/dist/src/terminal-command-cli-adapter.js +344 -6061
  133. package/dist/src/terminal-command-cli-adapter.js.map +1 -1
  134. package/dist/src/terminal-command-cli-ports.d.ts +443 -0
  135. package/dist/src/terminal-command-cli-ports.js +2 -0
  136. package/dist/src/terminal-command-cli-ports.js.map +1 -0
  137. package/dist/src/terminal-command-dispatch-preparation.d.ts +81 -0
  138. package/dist/src/terminal-command-dispatch-preparation.js +209 -0
  139. package/dist/src/terminal-command-dispatch-preparation.js.map +1 -0
  140. package/dist/src/terminal-command-dispatch-transport.d.ts +35 -0
  141. package/dist/src/terminal-command-dispatch-transport.js +859 -0
  142. package/dist/src/terminal-command-dispatch-transport.js.map +1 -0
  143. package/dist/src/terminal-command-foreground-proof.d.ts +32 -0
  144. package/dist/src/terminal-command-foreground-proof.js +72 -0
  145. package/dist/src/terminal-command-foreground-proof.js.map +1 -0
  146. package/dist/src/terminal-command-send-preflight.d.ts +41 -0
  147. package/dist/src/terminal-command-send-preflight.js +86 -0
  148. package/dist/src/terminal-command-send-preflight.js.map +1 -0
  149. package/dist/src/terminal-composer-classifier.d.ts +55 -0
  150. package/dist/src/terminal-composer-classifier.js +638 -0
  151. package/dist/src/terminal-composer-classifier.js.map +1 -0
  152. package/dist/src/terminal-control-ref.d.ts +23 -0
  153. package/dist/src/terminal-control-ref.js +32 -0
  154. package/dist/src/terminal-control-ref.js.map +1 -1
  155. package/dist/src/terminal-dispatch-recovery-authority.d.ts +56 -0
  156. package/dist/src/terminal-dispatch-recovery-authority.js +251 -0
  157. package/dist/src/terminal-dispatch-recovery-authority.js.map +1 -0
  158. package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +1 -9
  159. package/dist/src/terminal-dispatch-recovery-cli-adapter.js +5 -240
  160. package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
  161. package/dist/src/terminal-handoff-cli-adapter.d.ts +1 -11
  162. package/dist/src/terminal-handoff-cli-adapter.js +2 -39
  163. package/dist/src/terminal-handoff-cli-adapter.js.map +1 -1
  164. package/dist/src/terminal-human-explicit-send-cli-adapter.d.ts +25 -0
  165. package/dist/src/terminal-human-explicit-send-cli-adapter.js +903 -0
  166. package/dist/src/terminal-human-explicit-send-cli-adapter.js.map +1 -0
  167. package/dist/src/terminal-identity-authority-cli-adapter.d.ts +1 -6
  168. package/dist/src/terminal-identity-authority-cli-adapter.js +3 -33
  169. package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
  170. package/dist/src/terminal-interaction-cli-adapter.js +209 -210
  171. package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
  172. package/dist/src/terminal-interaction-core.d.ts +0 -2
  173. package/dist/src/terminal-interaction-core.js +4 -3
  174. package/dist/src/terminal-interaction-core.js.map +1 -1
  175. package/dist/src/terminal-interaction-protocol.d.ts +21 -0
  176. package/dist/src/terminal-interaction-protocol.js +74 -0
  177. package/dist/src/terminal-interaction-protocol.js.map +1 -1
  178. package/dist/src/terminal-interaction-response-bridge.d.ts +126 -0
  179. package/dist/src/terminal-interaction-response-bridge.js +579 -0
  180. package/dist/src/terminal-interaction-response-bridge.js.map +1 -0
  181. package/dist/src/terminal-interaction-response-transaction.d.ts +44 -0
  182. package/dist/src/terminal-interaction-response-transaction.js +56 -0
  183. package/dist/src/terminal-interaction-response-transaction.js.map +1 -0
  184. package/dist/src/terminal-list-action-policy.d.ts +43 -0
  185. package/dist/src/terminal-list-action-policy.js +164 -0
  186. package/dist/src/terminal-list-action-policy.js.map +1 -0
  187. package/dist/src/terminal-list-cli-adapter.d.ts +6 -36
  188. package/dist/src/terminal-list-cli-adapter.js +314 -1711
  189. package/dist/src/terminal-list-cli-adapter.js.map +1 -1
  190. package/dist/src/terminal-list-facts.d.ts +129 -0
  191. package/dist/src/terminal-list-facts.js +179 -0
  192. package/dist/src/terminal-list-facts.js.map +1 -0
  193. package/dist/src/terminal-list-inventory.d.ts +59 -0
  194. package/dist/src/terminal-list-inventory.js +194 -0
  195. package/dist/src/terminal-list-inventory.js.map +1 -0
  196. package/dist/src/terminal-list-ownership-service.d.ts +118 -0
  197. package/dist/src/terminal-list-ownership-service.js +1003 -0
  198. package/dist/src/terminal-list-ownership-service.js.map +1 -0
  199. package/dist/src/terminal-list-projection-service.d.ts +49 -0
  200. package/dist/src/terminal-list-projection-service.js +407 -0
  201. package/dist/src/terminal-list-projection-service.js.map +1 -0
  202. package/dist/src/terminal-list-renderer.d.ts +12 -11
  203. package/dist/src/terminal-list-renderer.js +237 -104
  204. package/dist/src/terminal-list-renderer.js.map +1 -1
  205. package/dist/src/terminal-managed-send-cli-adapter.d.ts +33 -0
  206. package/dist/src/terminal-managed-send-cli-adapter.js +994 -0
  207. package/dist/src/terminal-managed-send-cli-adapter.js.map +1 -0
  208. package/dist/src/terminal-managed-turn-list-action-policy.d.ts +47 -0
  209. package/dist/src/terminal-managed-turn-list-action-policy.js +64 -0
  210. package/dist/src/terminal-managed-turn-list-action-policy.js.map +1 -0
  211. package/dist/src/terminal-model-control-action-policy.d.ts +60 -0
  212. package/dist/src/terminal-model-control-action-policy.js +96 -0
  213. package/dist/src/terminal-model-control-action-policy.js.map +1 -0
  214. package/dist/src/terminal-model-control-availability.d.ts +68 -0
  215. package/dist/src/terminal-model-control-availability.js +157 -0
  216. package/dist/src/terminal-model-control-availability.js.map +1 -0
  217. package/dist/src/terminal-model-control-bridge.d.ts +48 -0
  218. package/dist/src/terminal-model-control-bridge.js +259 -0
  219. package/dist/src/terminal-model-control-bridge.js.map +1 -0
  220. package/dist/src/terminal-model-control-profile.d.ts +55 -0
  221. package/dist/src/terminal-model-control-profile.js +137 -0
  222. package/dist/src/terminal-model-control-profile.js.map +1 -0
  223. package/dist/src/terminal-model-control-subject.d.ts +34 -0
  224. package/dist/src/terminal-model-control-subject.js +58 -0
  225. package/dist/src/terminal-model-control-subject.js.map +1 -0
  226. package/dist/src/terminal-model-control-transaction.d.ts +19 -0
  227. package/dist/src/terminal-model-control-transaction.js +39 -0
  228. package/dist/src/terminal-model-control-transaction.js.map +1 -0
  229. package/dist/src/terminal-model-control.d.ts +331 -0
  230. package/dist/src/terminal-model-control.js +1686 -0
  231. package/dist/src/terminal-model-control.js.map +1 -0
  232. package/dist/src/terminal-monitor-application-service.js +50 -17
  233. package/dist/src/terminal-monitor-application-service.js.map +1 -1
  234. package/dist/src/terminal-native-inspection-bridge.d.ts +211 -0
  235. package/dist/src/terminal-native-inspection-bridge.js +1127 -0
  236. package/dist/src/terminal-native-inspection-bridge.js.map +1 -0
  237. package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -0
  238. package/dist/src/terminal-runtime-cli-adapter.js +59 -17
  239. package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
  240. package/dist/src/terminal-submission-retry-application.d.ts +24 -0
  241. package/dist/src/terminal-submission-retry-application.js +402 -0
  242. package/dist/src/terminal-submission-retry-application.js.map +1 -0
  243. package/dist/src/terminal-submission-retry-ports.d.ts +30 -0
  244. package/dist/src/terminal-submission-retry-ports.js +2 -0
  245. package/dist/src/terminal-submission-retry-ports.js.map +1 -0
  246. package/dist/src/terminal-submission-retry-reconciliation.d.ts +236 -0
  247. package/dist/src/terminal-submission-retry-reconciliation.js +1130 -0
  248. package/dist/src/terminal-submission-retry-reconciliation.js.map +1 -0
  249. package/dist/src/terminal-submission-retry-transport.d.ts +58 -0
  250. package/dist/src/terminal-submission-retry-transport.js +675 -0
  251. package/dist/src/terminal-submission-retry-transport.js.map +1 -0
  252. package/dist/src/terminal-text-submission-bridge.d.ts +200 -0
  253. package/dist/src/terminal-text-submission-bridge.js +632 -0
  254. package/dist/src/terminal-text-submission-bridge.js.map +1 -0
  255. package/dist/src/terminal-user-explicit-send-policy.d.ts +29 -0
  256. package/dist/src/terminal-user-explicit-send-policy.js +46 -0
  257. package/dist/src/terminal-user-explicit-send-policy.js.map +1 -0
  258. package/dist/src/terminal-watch-cli-adapter.js +91 -143
  259. package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
  260. package/dist/src/terminal-watch-codec.d.ts +9 -0
  261. package/dist/src/terminal-watch-codec.js +1051 -0
  262. package/dist/src/terminal-watch-codec.js.map +1 -0
  263. package/dist/src/terminal-watch-interaction-response-store.d.ts +19 -0
  264. package/dist/src/terminal-watch-interaction-response-store.js +87 -0
  265. package/dist/src/terminal-watch-interaction-response-store.js.map +1 -0
  266. package/dist/src/terminal-watch-record.d.ts +319 -0
  267. package/dist/src/terminal-watch-record.js +742 -0
  268. package/dist/src/terminal-watch-record.js.map +1 -0
  269. package/dist/src/terminal-watch-service.js +48 -27
  270. package/dist/src/terminal-watch-service.js.map +1 -1
  271. package/dist/src/terminal-watch-store.d.ts +3 -279
  272. package/dist/src/terminal-watch-store.js +5 -1779
  273. package/dist/src/terminal-watch-store.js.map +1 -1
  274. package/docs/quickstart-herdr.md +1 -1
  275. package/docs/quickstart-tmux.md +27 -2
  276. package/openclaw.plugin.json +13 -1
  277. package/package.json +7 -2
  278. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +54 -11
@@ -0,0 +1,1130 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { callbackExpectedForConversation, callbackRouteFingerprintForConversation } from "./callback-route-authority.js";
4
+ import { deferredForegroundActiveMessageId, isDeferredForegroundSubmissionRetryPending } from "./deferred-foreground-transfer.js";
5
+ import { effectiveTurnStatus, executorForConversation, isTerminalDispatchOwnerReleasedStatus, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
6
+ import { appendEvent, loadState, saveState } from "./store.js";
7
+ import { TERMINAL_SUBMISSION_RETRY_SCHEMA, TERMINAL_SUBMISSION_RETRY_VERSION, loadTerminalSubmissionRetry, projectTerminalSubmissionRetryPending, saveTerminalSubmissionRetry, terminalSubmissionRetryLedgerFields } from "./terminal-submission-retry-service.js";
8
+ import { terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
9
+ import { assertTerminalDispatchRouteMatches, bindTerminalDispatchRoute } from "./terminal-dispatch-capability.js";
10
+ import { terminalMonitorDeadlineAt as deadlineAt } from "./terminal-monitor-decision-policy.js";
11
+ import { sameCanonicalStatePath, terminalDispatchLedgerLooksLifecycle } from "./terminal-dispatch-ledger-codec.js";
12
+ import { bindDeferredForegroundApplicationScope } from "./deferred-foreground-capability.js";
13
+ import * as deferredRecoveryAdapter from "./deferred-foreground-recovery-cli-adapter.js";
14
+ import { deferredForegroundBoundaryProjection } from "./deferred-foreground-preparation-cli-adapter.js";
15
+ import { terminalSubmissionPayload } from "./terminal-dispatch-execution.js";
16
+ import { terminalSendEnterDispatched, terminalSendResultContract } from "./terminal-dispatch-presenter.js";
17
+ import * as dispatchReceipt from "./terminal-dispatch-receipt.js";
18
+ import { writeCliJson as printJson } from "./cli-command-runtime.js";
19
+ import { cliNow } from "./cli-runtime-context.js";
20
+ import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
21
+ const DEFAULT_AGENT_TIMEOUT_MINUTES = 60;
22
+ const DEFAULT_AGENT_HARD_TIMEOUT_MINUTES = 720;
23
+ const terminalBridgeSubmission = dispatchReceipt.terminalBridgeSubmission;
24
+ const TERMINAL_SUBMISSION_RETRY_LEDGER_KEYS = [
25
+ "submission_retry_attempt_id",
26
+ "submission_retry_mode",
27
+ "submission_retry_state",
28
+ "submission_retry_revision",
29
+ "submission_retry_original_message_id",
30
+ "submission_retry_active_message_id",
31
+ "submission_retry_reserved_at",
32
+ "submission_retry_replacement_text_reserved_at",
33
+ "submission_retry_replacement_text_injected_at",
34
+ "submission_retry_enter_reserved_at",
35
+ "submission_retry_enter_dispatched_at"
36
+ ];
37
+ export class TerminalSubmissionRetryReconciliation {
38
+ ports;
39
+ constructor(ports) {
40
+ this.ports = ports;
41
+ }
42
+ terminalSubmissionRetryBaseRecord(input) {
43
+ return {
44
+ schema: TERMINAL_SUBMISSION_RETRY_SCHEMA,
45
+ version: TERMINAL_SUBMISSION_RETRY_VERSION,
46
+ revision: 1,
47
+ attempt_id: input.attemptId,
48
+ mode: input.mode,
49
+ state: input.state,
50
+ store_dir: path.resolve(input.storeDir),
51
+ state_path: path.resolve(input.statePath),
52
+ session_id: sessionIdForConversation(input.conversation),
53
+ turn_id: turnIdForConversation(input.conversation),
54
+ original_message_id: input.originalMessageId,
55
+ active_message_id: input.activeMessageId,
56
+ request_hash: input.requestHash,
57
+ terminal_target: input.terminalControl.target,
58
+ callback_route_fingerprint: callbackRouteFingerprintForConversation(input.conversation) ?? null,
59
+ deferred_foreground_transfer_id: input.deferredTransferId ?? null,
60
+ reserved_at: input.at,
61
+ updated_at: input.at
62
+ };
63
+ }
64
+ assertTerminalSubmissionRetryAttemptIdentity(input) {
65
+ const attempt = input.attempt;
66
+ if (!attempt)
67
+ return;
68
+ const callbackFingerprint = callbackRouteFingerprintForConversation(input.conversation) ?? null;
69
+ if (attempt.store_dir !== path.resolve(input.storeDir) ||
70
+ attempt.state_path !== path.resolve(input.statePath) ||
71
+ attempt.session_id !== sessionIdForConversation(input.conversation) ||
72
+ attempt.turn_id !== turnIdForConversation(input.conversation) ||
73
+ attempt.original_message_id !== input.originalMessageId ||
74
+ attempt.request_hash !== input.requestHash ||
75
+ attempt.terminal_target !== input.terminalControl.target ||
76
+ attempt.callback_route_fingerprint !== callbackFingerprint ||
77
+ attempt.deferred_foreground_transfer_id !==
78
+ (input.deferredTransferId ?? null)) {
79
+ throw new Error("terminal submission retry authority changed; no terminal input was sent");
80
+ }
81
+ }
82
+ terminalSubmissionRetryMessageType(submission) {
83
+ const messageType = stringValue(submission.message_type);
84
+ if (messageType !== "task" && messageType !== "answer") {
85
+ throw new Error("terminal submission retry message type is unavailable; no terminal input was sent");
86
+ }
87
+ return messageType;
88
+ }
89
+ withDeferredTransferSubmissionAuthority(conversation, messageId, transferId) {
90
+ const takeover = isRecord(conversation.native_session_takeover)
91
+ ? conversation.native_session_takeover
92
+ : undefined;
93
+ const submission = terminalBridgeSubmission(conversation);
94
+ if (!takeover || !submission || submission.message_id !== messageId) {
95
+ throw new Error("cannot upgrade deferred transfer authority on a changed submission");
96
+ }
97
+ const existing = stringValue(submission.deferred_foreground_transfer_id);
98
+ if (existing && existing !== transferId) {
99
+ throw new Error("deferred transfer receipt authority conflicts");
100
+ }
101
+ const receiptsValue = takeover.terminal_bridge_submission_receipts;
102
+ if (receiptsValue !== undefined && !Array.isArray(receiptsValue)) {
103
+ throw new Error("terminal submission receipt history is malformed");
104
+ }
105
+ const receipts = (Array.isArray(receiptsValue) ? receiptsValue : []).map((receipt) => {
106
+ if (!isRecord(receipt) || !stringValue(receipt.message_id)) {
107
+ throw new Error("terminal submission receipt history is malformed");
108
+ }
109
+ if (receipt.message_id !== messageId)
110
+ return receipt;
111
+ const receiptTransferId = stringValue(receipt.deferred_foreground_transfer_id);
112
+ if (receiptTransferId && receiptTransferId !== transferId) {
113
+ throw new Error("deferred transfer receipt history conflicts");
114
+ }
115
+ return {
116
+ ...receipt,
117
+ deferred_foreground_transfer_id: transferId
118
+ };
119
+ });
120
+ return {
121
+ ...conversation,
122
+ native_session_takeover: {
123
+ ...takeover,
124
+ deferred_foreground_transfer_id: transferId,
125
+ terminal_bridge_submission: {
126
+ ...submission,
127
+ deferred_foreground_transfer_id: transferId
128
+ },
129
+ terminal_bridge_submission_receipts: receipts
130
+ }
131
+ };
132
+ }
133
+ terminalSubmissionRetryUnstalled(conversation) {
134
+ const next = {
135
+ ...conversation,
136
+ status: "waiting_for_agent"
137
+ };
138
+ delete next.stalled_at;
139
+ delete next.stalled_reason;
140
+ delete next.failed_at;
141
+ delete next.failure_reason;
142
+ delete next.idle_since;
143
+ return next;
144
+ }
145
+ withTerminalSubmissionRetryMonitorEpoch(conversation, at) {
146
+ const takeover = isRecord(conversation.native_session_takeover)
147
+ ? conversation.native_session_takeover
148
+ : {};
149
+ const configuredInactivity = Number(takeover.terminal_bridge_inactivity_timeout_minutes);
150
+ const configuredHard = Number(takeover.terminal_bridge_hard_timeout_minutes);
151
+ const inactivityMinutes = Number.isFinite(configuredInactivity) &&
152
+ configuredInactivity > 0
153
+ ? configuredInactivity
154
+ : DEFAULT_AGENT_TIMEOUT_MINUTES;
155
+ const hardMinutes = Number.isFinite(configuredHard) && configuredHard > 0
156
+ ? configuredHard
157
+ : DEFAULT_AGENT_HARD_TIMEOUT_MINUTES;
158
+ return {
159
+ ...conversation,
160
+ native_session_takeover: {
161
+ ...takeover,
162
+ terminal_bridge_started_at: at,
163
+ terminal_bridge_monitor_started_at: at,
164
+ terminal_bridge_last_activity_at: at,
165
+ terminal_bridge_last_activity_reason: "submission retry Enter dispatched",
166
+ terminal_bridge_inactivity_timeout_minutes: inactivityMinutes,
167
+ terminal_bridge_hard_timeout_minutes: hardMinutes,
168
+ terminal_bridge_inactivity_deadline_at: deadlineAt(at, inactivityMinutes),
169
+ terminal_bridge_hard_deadline_at: deadlineAt(at, hardMinutes)
170
+ },
171
+ updated_at: at
172
+ };
173
+ }
174
+ terminalSubmissionRetryAccepted(input) {
175
+ const currentConversation = this.assertTerminalSubmissionRetryTurnOpen({
176
+ statePath: input.statePath,
177
+ exactTurnId: turnIdForConversation(input.conversation)
178
+ });
179
+ const messageId = this.ports.required(stringValue(input.submission.message_id), "terminal submission retry message id is unavailable");
180
+ const preparedAt = this.ports.required(stringValue(input.submission.prepared_at), "terminal submission retry prepared timestamp is unavailable");
181
+ const acceptedConversation = this.ports.withTerminalBridgeSubmission({
182
+ conversation: this.terminalSubmissionRetryUnstalled(currentConversation),
183
+ messageId,
184
+ messageType: this.terminalSubmissionRetryMessageType(input.submission),
185
+ requestText: input.requestText,
186
+ status: "agent_accepted",
187
+ preparedAt,
188
+ textInjectedAt: stringValue(input.submission.text_injected_at),
189
+ enterDispatchedAt: stringValue(input.submission.enter_dispatched_at),
190
+ agentAcceptedAt: input.at,
191
+ acceptanceEvidence: input.evidence,
192
+ lastProvenStage: "agent_accepted"
193
+ });
194
+ saveState(input.statePath, acceptedConversation);
195
+ let acceptedAttempt = input.attempt;
196
+ if (acceptedAttempt && acceptedAttempt.state !== "agent_accepted") {
197
+ acceptedAttempt = this.saveTerminalSubmissionRetryForOpenTurn(input.statePath, {
198
+ ...acceptedAttempt,
199
+ state: "agent_accepted",
200
+ agent_accepted_at: input.at,
201
+ updated_at: input.at
202
+ }, acceptedAttempt.revision);
203
+ }
204
+ this.ports.mutationDispatchLedger.save(input.scopes, input.resources, {
205
+ ...input.ledger,
206
+ ...terminalSubmissionRetryLedgerFields(acceptedAttempt),
207
+ status: "agent_accepted",
208
+ text_injected_at: stringValue(input.ledger.text_injected_at),
209
+ enter_dispatched_at: stringValue(input.ledger.enter_dispatched_at),
210
+ agent_accepted_at: input.at,
211
+ acceptance_evidence: input.evidence,
212
+ dispatcher_pid: null
213
+ });
214
+ appendEvent(input.logPath, {
215
+ ts: input.at,
216
+ conversation_id: acceptedConversation.conversation_id,
217
+ event: "terminal_submission_retry_agent_accepted",
218
+ message_id: messageId,
219
+ terminal_input_sent: input.terminalInputSent
220
+ });
221
+ return { conversation: acceptedConversation, attempt: acceptedAttempt };
222
+ }
223
+ terminalSubmissionRetryTerminalOutcome(input) {
224
+ const currentConversation = this.assertTerminalSubmissionRetryTurnOpen({
225
+ statePath: input.statePath,
226
+ exactTurnId: turnIdForConversation(input.conversation)
227
+ });
228
+ const messageId = this.ports.required(stringValue(input.submission.message_id), "terminal submission retry message id is unavailable");
229
+ const preparedAt = this.ports.required(stringValue(input.submission.prepared_at), "terminal submission retry prepared timestamp is unavailable");
230
+ const stalled = {
231
+ ...currentConversation,
232
+ status: "stalled",
233
+ stalled_at: input.at,
234
+ stalled_reason: input.reason,
235
+ updated_at: input.at
236
+ };
237
+ const conversation = this.ports.withTerminalBridgeSubmission({
238
+ conversation: stalled,
239
+ messageId,
240
+ messageType: this.terminalSubmissionRetryMessageType(input.submission),
241
+ requestText: input.requestText,
242
+ status: input.outcome,
243
+ preparedAt,
244
+ textInjectedAt: stringValue(input.submission.text_injected_at),
245
+ enterDispatchedAt: stringValue(input.submission.enter_dispatched_at),
246
+ ...(input.outcome === "not_accepted"
247
+ ? { notAcceptedAt: input.at }
248
+ : {
249
+ uncertainAt: input.at,
250
+ error: input.reason,
251
+ safeToRetry: false
252
+ }),
253
+ lastProvenStage: "enter_dispatched"
254
+ });
255
+ saveState(input.statePath, conversation);
256
+ this.ports.mutationDispatchLedger.save(input.scopes, input.resources, {
257
+ ...input.ledger,
258
+ ...terminalSubmissionRetryLedgerFields(input.attempt),
259
+ status: input.outcome,
260
+ ...(input.outcome === "not_accepted"
261
+ ? {
262
+ not_accepted_at: input.at,
263
+ uncertain_at: undefined,
264
+ error: undefined,
265
+ safe_to_retry: undefined
266
+ }
267
+ : {
268
+ not_accepted_at: undefined,
269
+ uncertain_at: input.at,
270
+ error: input.reason,
271
+ safe_to_retry: false
272
+ }),
273
+ acceptance_evidence: undefined,
274
+ agent_accepted_at: undefined,
275
+ dispatcher_pid: null
276
+ });
277
+ appendEvent(input.logPath, {
278
+ ts: input.at,
279
+ conversation_id: conversation.conversation_id,
280
+ event: `terminal_submission_retry_${input.outcome}`,
281
+ message_id: messageId,
282
+ terminal_input_sent: true,
283
+ reason: input.reason,
284
+ do_not_retry: true
285
+ });
286
+ return conversation;
287
+ }
288
+ finalizeDeferredTerminalSubmissionRetryAccepted(input) {
289
+ const conversation = loadState(input.statePath);
290
+ if (conversation.status === "closed") {
291
+ throw new Error(`cannot finalize submission retry for closed Turn ` +
292
+ `${turnIdForConversation(conversation)}; no retry state was changed`);
293
+ }
294
+ if (input.attempt &&
295
+ turnIdForConversation(conversation) !== input.attempt.turn_id) {
296
+ throw new Error("Turn identity changed during submission retry finalization; no retry " +
297
+ "state was changed");
298
+ }
299
+ const submission = this.ports.required(terminalBridgeSubmission(conversation), "deferred submission retry acceptance lost its Turn receipt");
300
+ const ledger = this.ports.required(this.ports.mutationDispatchLedger.load(input.scopes, input.resources), "deferred submission retry acceptance lost its dispatch ledger");
301
+ if (submission.status !== "agent_accepted" ||
302
+ ledger.status !== "agent_accepted") {
303
+ throw new Error("deferred submission retry acceptance was not durably finalized");
304
+ }
305
+ let attempt = input.attempt;
306
+ if (attempt && attempt.state !== "agent_accepted") {
307
+ const acceptedAt = stringValue(submission.agent_accepted_at) ??
308
+ stringValue(ledger.agent_accepted_at) ?? cliNow().toISOString();
309
+ attempt = this.saveTerminalSubmissionRetryForOpenTurn(input.statePath, {
310
+ ...attempt,
311
+ state: "agent_accepted",
312
+ agent_accepted_at: acceptedAt,
313
+ updated_at: acceptedAt
314
+ }, attempt.revision);
315
+ }
316
+ if (attempt) {
317
+ this.ports.mutationDispatchLedger.save(input.scopes, input.resources, {
318
+ ...ledger,
319
+ ...terminalSubmissionRetryLedgerFields(attempt)
320
+ });
321
+ }
322
+ return { conversation, attempt };
323
+ }
324
+ reconcileTerminalSubmissionRetryPending(input) {
325
+ const currentConversation = this.assertTerminalSubmissionRetryTurnOpen({
326
+ statePath: input.statePath,
327
+ exactTurnId: turnIdForConversation(input.conversation)
328
+ });
329
+ const { attempt, submission, ledger } = input;
330
+ const projection = projectTerminalSubmissionRetryPending({
331
+ attempt,
332
+ submission,
333
+ ledger
334
+ });
335
+ const { messageId, preparedAt, textInjectedAt, enterDispatchedAt } = projection;
336
+ if (input.deferred) {
337
+ const transfer = input.deferred.scope.loadTransfer(input.deferred.transfer.transfer_id);
338
+ if (!isDeferredForegroundSubmissionRetryPending(transfer)) {
339
+ throw new Error("deferred submission retry lacks exact pending transfer authority");
340
+ }
341
+ input.deferred.transfer = transfer;
342
+ }
343
+ const enteredConversation = this.ports.withTerminalBridgeSubmission({
344
+ conversation: this.withTerminalSubmissionRetryMonitorEpoch(this.terminalSubmissionRetryUnstalled(currentConversation), enterDispatchedAt),
345
+ messageId,
346
+ messageType: this.terminalSubmissionRetryMessageType(submission),
347
+ requestText: input.requestText,
348
+ status: "enter_dispatched",
349
+ preparedAt,
350
+ textInjectedAt,
351
+ enterDispatchedAt,
352
+ lastProvenStage: "enter_dispatched"
353
+ });
354
+ saveState(input.statePath, enteredConversation);
355
+ this.ports.mutationDispatchLedger.save(input.scopes, input.resources, {
356
+ ...ledger,
357
+ ...terminalSubmissionRetryLedgerFields(attempt),
358
+ status: "enter_dispatched",
359
+ text_injected_at: textInjectedAt,
360
+ enter_dispatched_at: enterDispatchedAt,
361
+ enter_not_attempted_at: undefined,
362
+ enter_not_attempted_reason: undefined,
363
+ uncertain_at: undefined,
364
+ safe_to_retry: undefined,
365
+ acceptance_evidence: undefined,
366
+ agent_accepted_at: undefined,
367
+ not_accepted_at: undefined,
368
+ dispatcher_pid: null
369
+ });
370
+ appendEvent(input.logPath, {
371
+ ts: enterDispatchedAt,
372
+ conversation_id: enteredConversation.conversation_id,
373
+ event: "terminal_submission_retry_pending_reconciled",
374
+ message_id: messageId,
375
+ terminal_input_sent: false
376
+ });
377
+ return enteredConversation;
378
+ }
379
+ assertTerminalSubmissionRetryDeferredMirror(input) {
380
+ const { attempt, transfer, submission } = input;
381
+ const replacement = attempt.mode === "replacement_send";
382
+ const stateRank = this.terminalSubmissionRetryTransportRank(attempt);
383
+ const expectedPreparedAt = stringValue(submission.prepared_at);
384
+ if (transfer.status !== "uncertain" ||
385
+ transfer.message_id !== attempt.original_message_id ||
386
+ transfer.submission_retry_attempt_id !== attempt.attempt_id ||
387
+ transfer.submission_retry_mode !== attempt.mode ||
388
+ transfer.submission_retry_message_id !== attempt.active_message_id ||
389
+ transfer.submission_retry_message_id !== transfer.message_id ||
390
+ transfer.submission_retry_prepared_at !== expectedPreparedAt ||
391
+ transfer.submission_retry_prepared_at !== transfer.prepared_at ||
392
+ transfer.submission_retry_text_reserved_at !==
393
+ (replacement && stateRank >= 1
394
+ ? attempt.replacement_text_reserved_at
395
+ : undefined) ||
396
+ transfer.submission_retry_text_injected_at !==
397
+ (replacement && stateRank >= 2
398
+ ? attempt.replacement_text_injected_at
399
+ : undefined) ||
400
+ transfer.submission_retry_enter_reserved_at !==
401
+ (stateRank >= (replacement ? 3 : 0)
402
+ ? attempt.enter_reserved_at
403
+ : undefined) ||
404
+ transfer.submission_retry_enter_dispatched_at !==
405
+ (stateRank >= (replacement ? 4 : 1)
406
+ ? attempt.enter_dispatched_at
407
+ : undefined) ||
408
+ (attempt.state === "enter_dispatched"
409
+ ? transfer.input_stage !== "enter_dispatched" ||
410
+ transfer.enter_dispatched_at !== attempt.enter_dispatched_at
411
+ : transfer.input_stage !== "text_injected")) {
412
+ throw new Error("deferred submission retry mirror conflicts with its durable attempt");
413
+ }
414
+ }
415
+ assertTerminalSubmissionRetryDeferredMirrorCanReconcile(input) {
416
+ const { attempt, transfer, submission } = input;
417
+ const expectedPreparedAt = stringValue(submission.prepared_at);
418
+ const retryFields = [
419
+ transfer.submission_retry_attempt_id,
420
+ transfer.submission_retry_mode,
421
+ transfer.submission_retry_message_id,
422
+ transfer.submission_retry_prepared_at,
423
+ transfer.submission_retry_text_reserved_at,
424
+ transfer.submission_retry_text_injected_at,
425
+ transfer.submission_retry_enter_reserved_at,
426
+ transfer.submission_retry_enter_dispatched_at
427
+ ];
428
+ if (transfer.submission_retry_attempt_id === undefined) {
429
+ const initialAttempt = (attempt.mode === "replacement_send" &&
430
+ attempt.state === "replacement_reserved") ||
431
+ (attempt.mode === "exact_draft_enter" &&
432
+ attempt.state === "enter_reserved");
433
+ if (!initialAttempt || retryFields.some((value) => value !== undefined)) {
434
+ throw new Error("deferred submission retry mirror is missing beyond its first recoverable write");
435
+ }
436
+ return;
437
+ }
438
+ if (transfer.submission_retry_attempt_id !== attempt.attempt_id ||
439
+ transfer.submission_retry_mode !== attempt.mode ||
440
+ transfer.submission_retry_message_id !== attempt.active_message_id ||
441
+ transfer.submission_retry_message_id !== transfer.message_id ||
442
+ transfer.submission_retry_prepared_at !== expectedPreparedAt ||
443
+ transfer.submission_retry_prepared_at !== transfer.prepared_at) {
444
+ throw new Error("deferred submission retry mirror identity conflicts with its durable attempt");
445
+ }
446
+ const expectedStages = {
447
+ submission_retry_text_reserved_at: attempt.mode === "replacement_send"
448
+ ? attempt.replacement_text_reserved_at
449
+ : undefined,
450
+ submission_retry_text_injected_at: attempt.mode === "replacement_send"
451
+ ? attempt.replacement_text_injected_at
452
+ : undefined,
453
+ submission_retry_enter_reserved_at: attempt.enter_reserved_at,
454
+ submission_retry_enter_dispatched_at: attempt.enter_dispatched_at
455
+ };
456
+ for (const field of Object.keys(expectedStages)) {
457
+ const actual = transfer[field];
458
+ const expected = expectedStages[field];
459
+ if (actual !== undefined && actual !== expected) {
460
+ throw new Error("deferred submission retry mirror stage conflicts with its durable attempt");
461
+ }
462
+ }
463
+ const transferRank = attempt.mode === "replacement_send"
464
+ ? transfer.submission_retry_enter_dispatched_at !== undefined
465
+ ? 4
466
+ : transfer.submission_retry_enter_reserved_at !== undefined
467
+ ? 3
468
+ : transfer.submission_retry_text_injected_at !== undefined
469
+ ? 2
470
+ : transfer.submission_retry_text_reserved_at !== undefined
471
+ ? 1
472
+ : 0
473
+ : transfer.submission_retry_enter_dispatched_at !== undefined
474
+ ? 1
475
+ : transfer.submission_retry_enter_reserved_at !== undefined
476
+ ? 0
477
+ : -1;
478
+ const attemptRank = this.terminalSubmissionRetryTransportRank(attempt);
479
+ if (transferRank > attemptRank || attemptRank - transferRank > 1) {
480
+ throw new Error("deferred submission retry mirror is ahead or more than one stage behind");
481
+ }
482
+ }
483
+ terminalSubmissionRetryTransportRank(attempt) {
484
+ const order = attempt.mode === "replacement_send"
485
+ ? [
486
+ "replacement_reserved", "replacement_text_reserved",
487
+ "replacement_text_injected", "enter_reserved", "enter_dispatched"
488
+ ]
489
+ : ["enter_reserved", "enter_dispatched"];
490
+ return order.indexOf(attempt.state);
491
+ }
492
+ reconcileTerminalSubmissionRetryLedgerPrefix(input) {
493
+ const { attempt, ledger } = input;
494
+ if (attempt.state === "agent_accepted") {
495
+ return { ledger, changed: false };
496
+ }
497
+ const currentAttemptId = stringValue(ledger.submission_retry_attempt_id);
498
+ const currentHasRetryFields = TERMINAL_SUBMISSION_RETRY_LEDGER_KEYS.some((key) => ledger[key] !== undefined);
499
+ if (!currentAttemptId) {
500
+ if (currentHasRetryFields || attempt.revision !== 1) {
501
+ throw new Error("terminal submission retry ledger has an unsafe missing retry prefix");
502
+ }
503
+ return {
504
+ ledger: { ...ledger, ...terminalSubmissionRetryLedgerFields(attempt) },
505
+ changed: true
506
+ };
507
+ }
508
+ const currentState = stringValue(ledger.submission_retry_state);
509
+ const currentRevision = Number(ledger.submission_retry_revision);
510
+ const allowedStates = this.terminalSubmissionRetryLedgerStates(attempt);
511
+ const same = currentState === attempt.state &&
512
+ currentRevision === attempt.revision;
513
+ const immediatelyLagging = currentState === allowedStates.previous &&
514
+ currentRevision === attempt.revision - 1;
515
+ if (!same && !immediatelyLagging) {
516
+ throw new Error("terminal submission retry ledger is ahead, conflicting, or more than one stage behind");
517
+ }
518
+ const expectedCurrent = this.terminalSubmissionRetryLedgerFieldsAtState(attempt, currentState, currentRevision);
519
+ if (TERMINAL_SUBMISSION_RETRY_LEDGER_KEYS.some((key) => JSON.stringify(ledger[key]) !== JSON.stringify(expectedCurrent[key]))) {
520
+ throw new Error("terminal submission retry ledger prefix conflicts with its durable attempt");
521
+ }
522
+ return same
523
+ ? { ledger, changed: false }
524
+ : {
525
+ ledger: { ...ledger, ...terminalSubmissionRetryLedgerFields(attempt) },
526
+ changed: true
527
+ };
528
+ }
529
+ terminalSubmissionRetryLedgerStates(attempt) {
530
+ const order = attempt.mode === "replacement_send"
531
+ ? [
532
+ "replacement_reserved", "replacement_text_reserved",
533
+ "replacement_text_injected", "enter_reserved", "enter_dispatched"
534
+ ]
535
+ : ["enter_reserved", "enter_dispatched"];
536
+ const index = order.indexOf(attempt.state);
537
+ return { previous: index > 0 ? order[index - 1] : undefined };
538
+ }
539
+ terminalSubmissionRetryLedgerFieldsAtState(attempt, state, revision) {
540
+ const fields = {
541
+ ...terminalSubmissionRetryLedgerFields(attempt),
542
+ submission_retry_state: state,
543
+ submission_retry_revision: revision
544
+ };
545
+ const rank = {
546
+ replacement_reserved: 0,
547
+ replacement_text_reserved: 1,
548
+ replacement_text_injected: 2,
549
+ enter_reserved: 3,
550
+ enter_dispatched: 4,
551
+ agent_accepted: 5
552
+ };
553
+ if (rank[state] < rank.replacement_text_reserved) {
554
+ delete fields.submission_retry_replacement_text_reserved_at;
555
+ }
556
+ if (rank[state] < rank.replacement_text_injected) {
557
+ delete fields.submission_retry_replacement_text_injected_at;
558
+ }
559
+ if (rank[state] < rank.enter_reserved) {
560
+ delete fields.submission_retry_enter_reserved_at;
561
+ }
562
+ if (rank[state] < rank.enter_dispatched) {
563
+ delete fields.submission_retry_enter_dispatched_at;
564
+ }
565
+ return fields;
566
+ }
567
+ printTerminalSubmissionRetryOutcome(input) {
568
+ const submission = terminalBridgeSubmission(input.conversation);
569
+ const enterDispatched = input.terminalInputSent ||
570
+ ["agent_accepted", "enter_dispatched", "not_accepted"].includes(input.outcome) ||
571
+ terminalSendEnterDispatched(submission) ||
572
+ ["enter_dispatched", "agent_accepted"].includes(String(input.attempt?.state ?? ""));
573
+ const terminalInputDispatched = enterDispatched ||
574
+ this.ports.durableTerminalInputDispatched(input.conversation);
575
+ const accepted = input.outcome === "agent_accepted";
576
+ const pending = input.outcome === "enter_dispatched";
577
+ const callbackAvailable = !isTerminalDispatchOwnerReleasedStatus(input.conversation.status) &&
578
+ (accepted || pending) &&
579
+ callbackExpectedForConversation(input.conversation);
580
+ printJson({
581
+ session_id: sessionIdForConversation(input.conversation),
582
+ turn_id: turnIdForConversation(input.conversation),
583
+ conversation: input.conversation,
584
+ delivered: enterDispatched,
585
+ delivery_receipt: accepted
586
+ ? "agent_accepted"
587
+ : input.outcome === "enter_dispatched" ||
588
+ input.outcome === "not_accepted"
589
+ ? "enter_dispatched"
590
+ : "uncertain",
591
+ status: input.outcome === "agent_accepted"
592
+ ? "async_pending"
593
+ : input.outcome === "enter_dispatched"
594
+ ? "submission_pending"
595
+ : input.outcome === "not_accepted"
596
+ ? "submission_not_accepted"
597
+ : "submission_uncertain",
598
+ submission_outcome: input.outcome === "enter_dispatched"
599
+ ? "pending_acceptance"
600
+ : input.outcome === "not_accepted"
601
+ ? "not_accepted"
602
+ : input.outcome === "refused"
603
+ ? "uncertain"
604
+ : "agent_accepted",
605
+ replayed: true,
606
+ terminal_control: input.terminalControl,
607
+ terminal_input_sent: input.terminalInputSent,
608
+ callback_expected: callbackAvailable,
609
+ ...terminalSendResultContract({
610
+ terminalInputDispatched,
611
+ agentAcceptance: accepted ? "proven" : "unproven",
612
+ managementMode: "managed",
613
+ observationMode: callbackAvailable ? "managed_monitor" : "none",
614
+ callbackAvailable,
615
+ interactionNotificationAvailable: accepted,
616
+ interactionResponseAvailable: accepted
617
+ }),
618
+ ...(input.attempt
619
+ ? {
620
+ submission_retry_attempt_id: input.attempt.attempt_id,
621
+ submission_retry_state: input.attempt.state
622
+ }
623
+ : {}),
624
+ do_not_retry: input.outcome !== "agent_accepted",
625
+ reason: input.reason
626
+ });
627
+ }
628
+ loadExactTerminalSubmissionRetryTurn(input) {
629
+ const conversation = loadState(input.statePath);
630
+ if (turnIdForConversation(conversation) !== input.exactTurnId) {
631
+ throw new Error("Turn identity changed during submission retry; no terminal input was " +
632
+ "sent and no retry state was changed");
633
+ }
634
+ return conversation;
635
+ }
636
+ assertTerminalSubmissionRetryTurnOpen(input) {
637
+ const conversation = this.loadExactTerminalSubmissionRetryTurn(input);
638
+ if (conversation.status === "closed") {
639
+ throw new Error(`cannot retry submission for closed Turn ${input.exactTurnId}; no ` +
640
+ "terminal input was sent and no retry state was changed");
641
+ }
642
+ return conversation;
643
+ }
644
+ saveTerminalSubmissionRetryForOpenTurn(statePath, candidate, expectedRevision) {
645
+ this.assertTerminalSubmissionRetryTurnOpen({
646
+ statePath,
647
+ exactTurnId: candidate.turn_id
648
+ });
649
+ return saveTerminalSubmissionRetry(statePath, candidate, expectedRevision);
650
+ }
651
+ loadTerminalSubmissionRetryLockedAuthority(input) {
652
+ const { options, exactTurnId, statePath, logPath, storeDir, terminalControl } = input.invocation;
653
+ const route = bindTerminalDispatchRoute(input.scopes, input.resources);
654
+ assertTerminalDispatchRouteMatches(route, {
655
+ terminalControl,
656
+ terminalKey: this.ports.terminalBridgeRuntimeKey(terminalControl),
657
+ storeDir,
658
+ statePath,
659
+ logPath
660
+ });
661
+ let conversation = this.assertTerminalSubmissionRetryTurnOpen({
662
+ statePath,
663
+ exactTurnId
664
+ });
665
+ const takeover = isRecord(conversation.native_session_takeover)
666
+ ? conversation.native_session_takeover
667
+ : undefined;
668
+ const lockedControl = this.ports.terminalControlFromTakeover(takeover);
669
+ if (turnIdForConversation(conversation) !== exactTurnId ||
670
+ executorForConversation(conversation).kind !== "codex" ||
671
+ !lockedControl ||
672
+ !terminalControlsShareIncarnation(lockedControl, terminalControl)) {
673
+ throw new Error("Turn or terminal authority changed before submission retry; no terminal input was sent");
674
+ }
675
+ let submission = terminalBridgeSubmission(conversation);
676
+ if (!submission) {
677
+ throw new Error("Turn has no terminal submission receipt to recover; no terminal input was sent");
678
+ }
679
+ const currentMessageId = this.ports.required(stringValue(submission.message_id), "terminal submission retry message id is unavailable");
680
+ const attempt = loadTerminalSubmissionRetry(statePath);
681
+ const originalMessageId = attempt?.original_message_id ?? currentMessageId;
682
+ const requestText = terminalSubmissionPayload(this.ports.required(stringValue(takeover?.terminal_bridge_request_text), "terminal submission retry request text is unavailable"));
683
+ const requestHash = this.ports.required(this.ports.terminalBridgeRequestFingerprint(requestText), "terminal submission retry request hash is unavailable");
684
+ const messageType = this.terminalSubmissionRetryMessageType(submission);
685
+ const messageBodyHash = createHash("sha256").update(requestText).digest("hex");
686
+ this.ports.validateStoredTerminalSubmissionMatch({
687
+ owner: conversation,
688
+ receipt: submission,
689
+ options,
690
+ terminalControl,
691
+ requestText,
692
+ requestHash,
693
+ expectedStoreDir: storeDir,
694
+ expectedSessionId: sessionIdForConversation(conversation),
695
+ expectedTurnId: exactTurnId,
696
+ expectedMessageType: messageType,
697
+ expectedStatePath: statePath
698
+ });
699
+ const ledger = this.ports.mutationDispatchLedger.load(input.scopes, input.resources);
700
+ this.assertTerminalSubmissionRetryLedgerAuthority({
701
+ ledger,
702
+ conversation,
703
+ exactTurnId,
704
+ currentMessageId,
705
+ messageType,
706
+ messageBodyHash,
707
+ requestHash,
708
+ storeDir,
709
+ statePath,
710
+ terminalControl
711
+ });
712
+ let currentLedger = ledger;
713
+ const takeoverDeferredTransferId = stringValue(takeover?.deferred_foreground_transfer_id);
714
+ const ledgerDeferredTransferId = stringValue(currentLedger.deferred_foreground_transfer_id);
715
+ if (takeoverDeferredTransferId &&
716
+ takeoverDeferredTransferId === ledgerDeferredTransferId &&
717
+ stringValue(submission.deferred_foreground_transfer_id) === undefined) {
718
+ conversation = this.withDeferredTransferSubmissionAuthority(conversation, currentMessageId, takeoverDeferredTransferId);
719
+ saveState(statePath, conversation);
720
+ submission = this.ports.required(terminalBridgeSubmission(conversation), "deferred terminal submission receipt upgrade failed");
721
+ }
722
+ const expectedCallbackFingerprint = callbackRouteFingerprintForConversation(conversation) ?? null;
723
+ if ((currentLedger.callback_route_fingerprint ?? null) !==
724
+ expectedCallbackFingerprint ||
725
+ (submission.callback_route_fingerprint ?? null) !==
726
+ expectedCallbackFingerprint) {
727
+ throw new Error("terminal submission callback route changed; no terminal input was sent");
728
+ }
729
+ const deferredTransferId = this.exactTerminalSubmissionRetryDeferredTransferId({
730
+ takeover,
731
+ submission,
732
+ ledger: currentLedger
733
+ });
734
+ this.assertTerminalSubmissionRetryAttemptIdentity({
735
+ attempt,
736
+ conversation,
737
+ storeDir,
738
+ statePath,
739
+ originalMessageId,
740
+ requestHash,
741
+ terminalControl,
742
+ deferredTransferId
743
+ });
744
+ this.assertTerminalSubmissionRetryGeneration({
745
+ attempt,
746
+ takeover,
747
+ currentMessageId,
748
+ originalMessageId
749
+ });
750
+ const callbackDelivery = isRecord(conversation.callback_delivery)
751
+ ? conversation.callback_delivery
752
+ : undefined;
753
+ return {
754
+ conversation,
755
+ takeover,
756
+ submission,
757
+ ledger: currentLedger,
758
+ attempt,
759
+ currentMessageId,
760
+ originalMessageId,
761
+ requestText,
762
+ requestHash,
763
+ deferredTransferId,
764
+ lifecycleSettled: callbackDelivery?.status === "delivered" ||
765
+ isTerminalDispatchOwnerReleasedStatus(effectiveTurnStatus(conversation))
766
+ };
767
+ }
768
+ assertTerminalSubmissionRetryLedgerAuthority(input) {
769
+ const { ledger } = input;
770
+ if (!ledger || terminalDispatchLedgerLooksLifecycle(ledger) ||
771
+ stringValue(ledger.conversation_id) !== input.conversation.conversation_id ||
772
+ stringValue(ledger.session_id) !== sessionIdForConversation(input.conversation) ||
773
+ stringValue(ledger.turn_id) !== input.exactTurnId ||
774
+ stringValue(ledger.generation_id) !== input.currentMessageId ||
775
+ stringValue(ledger.message_id) !== input.currentMessageId ||
776
+ stringValue(ledger.message_type) !== input.messageType ||
777
+ stringValue(ledger.message_body_hash) !== input.messageBodyHash ||
778
+ stringValue(ledger.executor_kind) !== "codex" ||
779
+ stringValue(ledger.request_hash) !== input.requestHash ||
780
+ path.resolve(stringValue(ledger.store_dir) ?? "") !==
781
+ path.resolve(input.storeDir) ||
782
+ !sameCanonicalStatePath(ledger.state_path, input.statePath) ||
783
+ !this.ports.terminalDispatchRecordMatchesControl(ledger, input.terminalControl)) {
784
+ throw new Error("terminal dispatch ledger does not match the exact Turn generation; no terminal input was sent");
785
+ }
786
+ }
787
+ exactTerminalSubmissionRetryDeferredTransferId(input) {
788
+ const references = [
789
+ stringValue(input.takeover?.deferred_foreground_transfer_id),
790
+ stringValue(input.submission.deferred_foreground_transfer_id),
791
+ stringValue(input.ledger.deferred_foreground_transfer_id)
792
+ ];
793
+ const hasDeferred = references.some((value) => value !== undefined);
794
+ if (hasDeferred &&
795
+ (references.some((value) => value === undefined) ||
796
+ new Set(references).size !== 1)) {
797
+ throw new Error("deferred transfer references disagree across Turn and ledger; no terminal input was sent");
798
+ }
799
+ return hasDeferred ? references[0] : undefined;
800
+ }
801
+ assertTerminalSubmissionRetryGeneration(input) {
802
+ const currentGenerationEligible = input.attempt?.mode === "replacement_send"
803
+ ? input.attempt.state === "replacement_reserved"
804
+ ? [input.attempt.original_message_id, input.attempt.active_message_id]
805
+ .includes(input.currentMessageId)
806
+ : input.currentMessageId === input.attempt.active_message_id
807
+ : input.currentMessageId === input.originalMessageId;
808
+ if (stringValue(input.takeover?.terminal_bridge_message_id) !==
809
+ input.currentMessageId ||
810
+ !currentGenerationEligible) {
811
+ throw new Error("terminal submission retry generation changed; no terminal input was sent");
812
+ }
813
+ }
814
+ reconcileTerminalSubmissionRetryDeferredTransfer(input) {
815
+ const { attempt } = input;
816
+ if (!attempt)
817
+ return input.transfer;
818
+ const application = this.ports.deferredForegroundApplication(input.invocation.options, input.invocation.live);
819
+ const boundary = deferredForegroundBoundaryProjection(input.boundary);
820
+ let transfer = input.transfer;
821
+ if (transfer.submission_retry_attempt_id === undefined) {
822
+ if (attempt.mode === "replacement_send" &&
823
+ attempt.state === "replacement_reserved") {
824
+ transfer = application.reserveSubmissionRetry({
825
+ scope: input.scope,
826
+ boundary,
827
+ attemptId: attempt.attempt_id,
828
+ mode: attempt.mode,
829
+ messageId: attempt.active_message_id,
830
+ preparedAt: this.ports.required(stringValue(input.submission.prepared_at), "deferred retry prepared timestamp is unavailable")
831
+ });
832
+ }
833
+ else if (attempt.mode === "exact_draft_enter" &&
834
+ attempt.state === "enter_reserved") {
835
+ transfer = application.reserveSubmissionRetry({
836
+ scope: input.scope,
837
+ boundary,
838
+ attemptId: attempt.attempt_id,
839
+ mode: attempt.mode,
840
+ messageId: attempt.active_message_id,
841
+ preparedAt: this.ports.required(stringValue(input.submission.prepared_at), "deferred retry prepared timestamp is unavailable")
842
+ });
843
+ transfer = application.advanceSubmissionRetry({
844
+ scope: input.scope,
845
+ boundary,
846
+ attemptId: attempt.attempt_id,
847
+ messageId: attempt.active_message_id,
848
+ stage: "enter_reserved",
849
+ at: attempt.enter_reserved_at
850
+ });
851
+ }
852
+ else {
853
+ throw new Error("deferred retry mirror is incomplete after an irreversible boundary; no terminal input was sent");
854
+ }
855
+ }
856
+ if (!transfer.submission_retry_attempt_id)
857
+ return transfer;
858
+ if (attempt.mode === "replacement_send" &&
859
+ [
860
+ "replacement_text_reserved", "replacement_text_injected",
861
+ "enter_reserved", "enter_dispatched"
862
+ ].includes(attempt.state) &&
863
+ !transfer.submission_retry_text_reserved_at) {
864
+ transfer = application.advanceSubmissionRetry({
865
+ scope: input.scope,
866
+ boundary,
867
+ attemptId: attempt.attempt_id,
868
+ messageId: attempt.active_message_id,
869
+ stage: "text_reserved",
870
+ at: attempt.replacement_text_reserved_at
871
+ });
872
+ }
873
+ if (["replacement_text_injected", "enter_reserved", "enter_dispatched"]
874
+ .includes(attempt.state) &&
875
+ !transfer.submission_retry_text_injected_at) {
876
+ transfer = application.advanceSubmissionRetry({
877
+ scope: input.scope,
878
+ boundary,
879
+ attemptId: attempt.attempt_id,
880
+ messageId: attempt.active_message_id,
881
+ stage: "text_injected",
882
+ at: attempt.replacement_text_injected_at ??
883
+ attempt.enter_reserved_at ?? attempt.reserved_at
884
+ });
885
+ }
886
+ if (["enter_reserved", "enter_dispatched"].includes(attempt.state) &&
887
+ !transfer.submission_retry_enter_reserved_at) {
888
+ transfer = application.advanceSubmissionRetry({
889
+ scope: input.scope,
890
+ boundary,
891
+ attemptId: attempt.attempt_id,
892
+ messageId: attempt.active_message_id,
893
+ stage: "enter_reserved",
894
+ at: attempt.enter_reserved_at
895
+ });
896
+ }
897
+ if (attempt.state === "enter_dispatched" &&
898
+ !transfer.submission_retry_enter_dispatched_at) {
899
+ transfer = application.advanceSubmissionRetry({
900
+ scope: input.scope,
901
+ boundary,
902
+ attemptId: attempt.attempt_id,
903
+ messageId: attempt.active_message_id,
904
+ stage: "enter_dispatched",
905
+ at: attempt.enter_dispatched_at
906
+ });
907
+ }
908
+ return transfer;
909
+ }
910
+ prepareTerminalSubmissionRetryDeferredContext(input) {
911
+ this.assertTerminalSubmissionRetryTurnOpen({
912
+ statePath: input.invocation.statePath,
913
+ exactTurnId: input.invocation.exactTurnId
914
+ });
915
+ const { deferredTransferId, attempt, submission } = input.authority;
916
+ if (!deferredTransferId)
917
+ return { ledger: input.authority.ledger };
918
+ const scope = bindDeferredForegroundApplicationScope(input.scopes, input.resources);
919
+ const transfer = scope.loadTransfer(deferredTransferId);
920
+ const boundary = deferredRecoveryAdapter.deferredCodexBoundaryFromTransfer(this.ports.deferredForegroundRecoveryAdapterPorts(), { terminal: input.invocation.live, transfer });
921
+ const projectedBoundary = deferredForegroundBoundaryProjection(boundary);
922
+ scope.assertBoundary(projectedBoundary);
923
+ if (attempt && attempt.state !== "agent_accepted") {
924
+ this.assertTerminalSubmissionRetryDeferredMirrorCanReconcile({
925
+ attempt,
926
+ transfer,
927
+ submission
928
+ });
929
+ }
930
+ const ledgerMirror = attempt && attempt.state !== "agent_accepted"
931
+ ? this.reconcileTerminalSubmissionRetryLedgerPrefix({
932
+ attempt,
933
+ ledger: input.authority.ledger
934
+ })
935
+ : undefined;
936
+ const currentTransfer = this.reconcileTerminalSubmissionRetryDeferredTransfer({
937
+ invocation: input.invocation,
938
+ scope,
939
+ boundary,
940
+ transfer,
941
+ attempt,
942
+ submission
943
+ });
944
+ this.assertTerminalSubmissionRetryDeferredTransferAuthority({
945
+ invocation: input.invocation,
946
+ authority: input.authority,
947
+ transfer: currentTransfer,
948
+ attempt
949
+ });
950
+ this.ports.deferredForegroundApplication(input.invocation.options, input.invocation.live).assertTransferAuthority(scope, currentTransfer, projectedBoundary);
951
+ let ledger = input.authority.ledger;
952
+ if (attempt && attempt.state !== "agent_accepted") {
953
+ this.assertTerminalSubmissionRetryDeferredMirror({
954
+ attempt,
955
+ transfer: currentTransfer,
956
+ submission
957
+ });
958
+ if (ledgerMirror?.changed) {
959
+ this.ports.mutationDispatchLedger.save(input.scopes, input.resources, ledgerMirror.ledger);
960
+ ledger = ledgerMirror.ledger;
961
+ }
962
+ }
963
+ return {
964
+ deferred: { scope, transfer: currentTransfer, boundary },
965
+ ledger
966
+ };
967
+ }
968
+ assertTerminalSubmissionRetryDeferredTransferAuthority(input) {
969
+ const { transfer, attempt } = input;
970
+ const plannedReplacement = Boolean(attempt?.mode === "replacement_send" &&
971
+ attempt.state === "replacement_reserved" &&
972
+ transfer.submission_retry_text_reserved_at === undefined);
973
+ const activeMessageId = deferredForegroundActiveMessageId(transfer);
974
+ const currentMessageId = input.authority.currentMessageId;
975
+ if (transfer.turn_id !== input.invocation.exactTurnId ||
976
+ transfer.target_session_id !==
977
+ sessionIdForConversation(input.authority.conversation) ||
978
+ transfer.request_hash !== input.authority.requestHash ||
979
+ !sameCanonicalStatePath(transfer.state_path, input.invocation.statePath) ||
980
+ transfer.message_id !== input.authority.originalMessageId ||
981
+ (plannedReplacement
982
+ ? ![input.authority.originalMessageId, attempt.active_message_id]
983
+ .includes(currentMessageId)
984
+ : activeMessageId !== currentMessageId) ||
985
+ (attempt
986
+ ? transfer.submission_retry_attempt_id !== attempt.attempt_id ||
987
+ transfer.submission_retry_message_id !== attempt.active_message_id
988
+ : transfer.submission_retry_attempt_id !== undefined)) {
989
+ throw new Error("deferred submission retry transfer authority changed; no terminal input was sent");
990
+ }
991
+ }
992
+ reconcileTerminalSubmissionRetryDeferredPending(context, state) {
993
+ if (!context.deferred || state.attempt?.state !== "enter_dispatched" ||
994
+ state.submission.status === "agent_accepted" ||
995
+ state.ledger.status === "agent_accepted") {
996
+ return false;
997
+ }
998
+ state.conversation = this.reconcileTerminalSubmissionRetryPending({
999
+ conversation: state.conversation,
1000
+ submission: state.submission,
1001
+ ledger: state.ledger,
1002
+ attempt: state.attempt,
1003
+ requestText: context.requestText,
1004
+ statePath: context.invocation.statePath,
1005
+ logPath: context.invocation.logPath,
1006
+ scopes: context.scopes,
1007
+ resources: context.resources,
1008
+ deferred: context.deferred
1009
+ });
1010
+ state.submission = this.ports.required(terminalBridgeSubmission(state.conversation), "terminal submission retry receipt disappeared during pending reconciliation");
1011
+ state.ledger = this.ports.required(this.ports.mutationDispatchLedger.load(context.scopes, context.resources), "terminal submission retry ledger disappeared during pending reconciliation");
1012
+ return true;
1013
+ }
1014
+ async recoverPartialTerminalSubmissionRetryAcceptance(context, state) {
1015
+ this.assertTerminalSubmissionRetryTurnOpen({
1016
+ statePath: context.invocation.statePath,
1017
+ exactTurnId: context.invocation.exactTurnId
1018
+ });
1019
+ const partialAcceptance = state.submission.status === "agent_accepted" ||
1020
+ state.ledger.status === "agent_accepted" ||
1021
+ state.attempt?.state === "agent_accepted";
1022
+ if (!partialAcceptance)
1023
+ return false;
1024
+ const { options, terminalControl, live, statePath, logPath, storeDir } = context.invocation;
1025
+ if (context.deferred &&
1026
+ ["uncertain", "dispatch_started"].includes(context.deferred.transfer.status)) {
1027
+ const recovered = await deferredRecoveryAdapter
1028
+ .recoverAcceptedDeferredForegroundDispatch(this.ports.deferredForegroundRecoveryAdapterPorts(), {
1029
+ options,
1030
+ scope: context.deferred.scope,
1031
+ storeDir,
1032
+ terminal: live,
1033
+ transfer: context.deferred.transfer,
1034
+ boundary: context.deferred.boundary
1035
+ });
1036
+ this.assertTerminalSubmissionRetryTurnOpen({
1037
+ statePath,
1038
+ exactTurnId: context.invocation.exactTurnId
1039
+ });
1040
+ if (!recovered) {
1041
+ this.printTerminalSubmissionRetryOutcome({
1042
+ conversation: state.conversation,
1043
+ terminalControl,
1044
+ attempt: state.attempt,
1045
+ outcome: "refused",
1046
+ terminalInputSent: false,
1047
+ reason: "Partial deferred acceptance lacks current native evidence; no terminal input was sent."
1048
+ });
1049
+ return true;
1050
+ }
1051
+ const finalized = this.finalizeDeferredTerminalSubmissionRetryAccepted({
1052
+ statePath,
1053
+ scopes: context.scopes,
1054
+ resources: context.resources,
1055
+ attempt: state.attempt
1056
+ });
1057
+ state.conversation = finalized.conversation;
1058
+ state.attempt = finalized.attempt;
1059
+ this.ports.startTerminalBridgeMonitorForConversation({
1060
+ conversation: state.conversation,
1061
+ statePath,
1062
+ logPath,
1063
+ options
1064
+ });
1065
+ this.printTerminalSubmissionRetryOutcome({
1066
+ conversation: state.conversation,
1067
+ terminalControl,
1068
+ attempt: state.attempt,
1069
+ outcome: "agent_accepted",
1070
+ terminalInputSent: false,
1071
+ reason: "Deferred native acceptance, source/target Sessions, and monitoring were reconciled without terminal input."
1072
+ });
1073
+ return true;
1074
+ }
1075
+ const durableEvidence = await context.execution.detectAcceptance({
1076
+ executor: "codex",
1077
+ conversation: state.conversation,
1078
+ terminalControl,
1079
+ ...this.ports.terminalAcceptanceCompanionFences(state.conversation, terminalControl)
1080
+ });
1081
+ this.assertTerminalSubmissionRetryTurnOpen({
1082
+ statePath,
1083
+ exactTurnId: context.invocation.exactTurnId
1084
+ });
1085
+ if (!durableEvidence) {
1086
+ this.printTerminalSubmissionRetryOutcome({
1087
+ conversation: state.conversation,
1088
+ terminalControl,
1089
+ attempt: state.attempt,
1090
+ outcome: "refused",
1091
+ terminalInputSent: false,
1092
+ reason: "Partial acceptance metadata lacks current native evidence; no terminal input was sent."
1093
+ });
1094
+ return true;
1095
+ }
1096
+ const repaired = this.terminalSubmissionRetryAccepted({
1097
+ conversation: state.conversation,
1098
+ submission: state.submission,
1099
+ ledger: state.ledger,
1100
+ evidence: durableEvidence,
1101
+ requestText: context.requestText,
1102
+ at: stringValue(state.submission.agent_accepted_at) ??
1103
+ stringValue(state.ledger.agent_accepted_at) ?? cliNow().toISOString(),
1104
+ statePath,
1105
+ logPath,
1106
+ scopes: context.scopes,
1107
+ resources: context.resources,
1108
+ attempt: state.attempt,
1109
+ terminalInputSent: false
1110
+ });
1111
+ state.conversation = repaired.conversation;
1112
+ state.attempt = repaired.attempt;
1113
+ this.ports.startTerminalBridgeMonitorForConversation({
1114
+ conversation: state.conversation,
1115
+ statePath,
1116
+ logPath,
1117
+ options
1118
+ });
1119
+ this.printTerminalSubmissionRetryOutcome({
1120
+ conversation: state.conversation,
1121
+ terminalControl,
1122
+ attempt: state.attempt,
1123
+ outcome: "agent_accepted",
1124
+ terminalInputSent: false,
1125
+ reason: "Partial durable acceptance was reconciled without terminal input and monitoring was ensured."
1126
+ });
1127
+ return true;
1128
+ }
1129
+ }
1130
+ //# sourceMappingURL=terminal-submission-retry-reconciliation.js.map