@scotthuang/agent-knock-knock 0.13.16 → 0.13.18

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 (184) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +4 -4
  3. package/dist/src/claude-terminal-agent-adapter.js +1 -1
  4. package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
  5. package/dist/src/codex-app-server-interaction-client.js +68 -26
  6. package/dist/src/codex-app-server-interaction-client.js.map +1 -1
  7. package/dist/src/codex-app-server-read-client.js +52 -20
  8. package/dist/src/codex-app-server-read-client.js.map +1 -1
  9. package/dist/src/codex-astra-composer-proof.js +1 -1
  10. package/dist/src/codex-astra-composer-proof.js.map +1 -1
  11. package/dist/src/codex-async-question-adapter.js +14 -4
  12. package/dist/src/codex-async-question-adapter.js.map +1 -1
  13. package/dist/src/codex-lifecycle-compatibility.d.ts +1 -1
  14. package/dist/src/codex-lifecycle-compatibility.js +9 -3
  15. package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
  16. package/dist/src/codex-open-rollout-inventory.d.ts +51 -0
  17. package/dist/src/codex-open-rollout-inventory.js +365 -0
  18. package/dist/src/codex-open-rollout-inventory.js.map +1 -0
  19. package/dist/src/codex-paginated-thread-binding.js +1 -1
  20. package/dist/src/codex-paginated-thread-binding.js.map +1 -1
  21. package/dist/src/codex-permission-surface.d.ts +1 -1
  22. package/dist/src/codex-permission-surface.js +1 -1
  23. package/dist/src/codex-permission-surface.js.map +1 -1
  24. package/dist/src/codex-rollout-bound-completion.d.ts +44 -0
  25. package/dist/src/codex-rollout-bound-completion.js +357 -0
  26. package/dist/src/codex-rollout-bound-completion.js.map +1 -0
  27. package/dist/src/codex-rollout-file.d.ts +15 -0
  28. package/dist/src/codex-rollout-file.js +98 -0
  29. package/dist/src/codex-rollout-file.js.map +1 -0
  30. package/dist/src/codex-rollout-human-task.d.ts +61 -0
  31. package/dist/src/codex-rollout-human-task.js +555 -0
  32. package/dist/src/codex-rollout-human-task.js.map +1 -0
  33. package/dist/src/codex-rollout-interaction-evidence.d.ts +48 -0
  34. package/dist/src/codex-rollout-interaction-evidence.js +521 -0
  35. package/dist/src/codex-rollout-interaction-evidence.js.map +1 -0
  36. package/dist/src/codex-rollout-inventory.d.ts +5 -0
  37. package/dist/src/codex-rollout-inventory.js +81 -0
  38. package/dist/src/codex-rollout-inventory.js.map +1 -0
  39. package/dist/src/codex-rollout-records.d.ts +49 -0
  40. package/dist/src/codex-rollout-records.js +298 -0
  41. package/dist/src/codex-rollout-records.js.map +1 -0
  42. package/dist/src/codex-sqlite-thread-query.d.ts +85 -0
  43. package/dist/src/codex-sqlite-thread-query.js +440 -0
  44. package/dist/src/codex-sqlite-thread-query.js.map +1 -0
  45. package/dist/src/codex-status-history-navigation.d.ts +1 -1
  46. package/dist/src/codex-status-history-navigation.js +1 -1
  47. package/dist/src/codex-status-history-navigation.js.map +1 -1
  48. package/dist/src/codex-store-adapter.d.ts +3 -50
  49. package/dist/src/codex-store-adapter.js +5 -796
  50. package/dist/src/codex-store-adapter.js.map +1 -1
  51. package/dist/src/codex-terminal-agent-adapter.js +4 -2
  52. package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
  53. package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +4 -134
  54. package/dist/src/native-thread-lifecycle-cli-adapter.js +19 -633
  55. package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
  56. package/dist/src/native-thread-lifecycle-cli-contract.d.ts +138 -0
  57. package/dist/src/native-thread-lifecycle-cli-contract.js +2 -0
  58. package/dist/src/native-thread-lifecycle-cli-contract.js.map +1 -0
  59. package/dist/src/semantic-permission-control.js +33 -6
  60. package/dist/src/semantic-permission-control.js.map +1 -1
  61. package/dist/src/semantic-tool-descriptions.d.ts +26 -0
  62. package/dist/src/semantic-tool-descriptions.js +259 -0
  63. package/dist/src/semantic-tool-descriptions.js.map +1 -0
  64. package/dist/src/semantic-tool-runtime.js +45 -37
  65. package/dist/src/semantic-tool-runtime.js.map +1 -1
  66. package/dist/src/semantic-tool-schemas.js +134 -33
  67. package/dist/src/semantic-tool-schemas.js.map +1 -1
  68. package/dist/src/terminal-action-contracts.d.ts +4 -0
  69. package/dist/src/terminal-action-contracts.js +695 -0
  70. package/dist/src/terminal-action-contracts.js.map +1 -0
  71. package/dist/src/terminal-composer-classifier.d.ts +1 -1
  72. package/dist/src/terminal-composer-classifier.js +2 -2
  73. package/dist/src/terminal-composer-classifier.js.map +1 -1
  74. package/dist/src/terminal-interaction-response-bridge.js +1 -1
  75. package/dist/src/terminal-interaction-response-bridge.js.map +1 -1
  76. package/dist/src/terminal-list-renderer.d.ts +1 -2
  77. package/dist/src/terminal-list-renderer.js +8 -424
  78. package/dist/src/terminal-list-renderer.js.map +1 -1
  79. package/dist/src/terminal-model-control-action-policy.d.ts +1 -1
  80. package/dist/src/terminal-model-control-availability.d.ts +1 -1
  81. package/dist/src/terminal-model-control-availability.js +1 -1
  82. package/dist/src/terminal-model-control-availability.js.map +1 -1
  83. package/dist/src/terminal-model-control-cli-adapter.d.ts +34 -0
  84. package/dist/src/terminal-model-control-cli-adapter.js +669 -0
  85. package/dist/src/terminal-model-control-cli-adapter.js.map +1 -0
  86. package/dist/src/terminal-model-control-contract.d.ts +198 -0
  87. package/dist/src/terminal-model-control-contract.js +15 -0
  88. package/dist/src/terminal-model-control-contract.js.map +1 -0
  89. package/dist/src/terminal-model-control-input.d.ts +29 -0
  90. package/dist/src/terminal-model-control-input.js +86 -0
  91. package/dist/src/terminal-model-control-input.js.map +1 -0
  92. package/dist/src/terminal-model-control-profile.d.ts +5 -1
  93. package/dist/src/terminal-model-control-profile.js +21 -7
  94. package/dist/src/terminal-model-control-profile.js.map +1 -1
  95. package/dist/src/terminal-model-control-subject.d.ts +52 -0
  96. package/dist/src/terminal-model-control-subject.js +73 -0
  97. package/dist/src/terminal-model-control-subject.js.map +1 -1
  98. package/dist/src/terminal-model-control-surface.d.ts +82 -0
  99. package/dist/src/terminal-model-control-surface.js +688 -0
  100. package/dist/src/terminal-model-control-surface.js.map +1 -0
  101. package/dist/src/terminal-model-control.d.ts +6 -270
  102. package/dist/src/terminal-model-control.js +11 -988
  103. package/dist/src/terminal-model-control.js.map +1 -1
  104. package/dist/src/terminal-model-selection.d.ts +33 -0
  105. package/dist/src/terminal-model-selection.js +143 -0
  106. package/dist/src/terminal-model-selection.js.map +1 -0
  107. package/dist/src/terminal-monitor-application-service.js +38 -59
  108. package/dist/src/terminal-monitor-application-service.js.map +1 -1
  109. package/dist/src/terminal-monitor-attention-store.d.ts +65 -0
  110. package/dist/src/terminal-monitor-attention-store.js +518 -0
  111. package/dist/src/terminal-monitor-attention-store.js.map +1 -0
  112. package/dist/src/terminal-monitor-collateral-recovery.d.ts +12 -0
  113. package/dist/src/terminal-monitor-collateral-recovery.js +415 -0
  114. package/dist/src/terminal-monitor-collateral-recovery.js.map +1 -0
  115. package/dist/src/terminal-monitor-interaction-projection.d.ts +19 -0
  116. package/dist/src/terminal-monitor-interaction-projection.js +48 -0
  117. package/dist/src/terminal-monitor-interaction-projection.js.map +1 -0
  118. package/dist/src/terminal-monitor-startup-recovery.d.ts +15 -0
  119. package/dist/src/terminal-monitor-startup-recovery.js +767 -0
  120. package/dist/src/terminal-monitor-startup-recovery.js.map +1 -0
  121. package/dist/src/terminal-monitor-state-cli-adapter.d.ts +2 -104
  122. package/dist/src/terminal-monitor-state-cli-adapter.js +28 -1640
  123. package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
  124. package/dist/src/terminal-monitor-state-contract.d.ts +108 -0
  125. package/dist/src/terminal-monitor-state-contract.js +8 -0
  126. package/dist/src/terminal-monitor-state-contract.js.map +1 -0
  127. package/dist/src/terminal-native-inspection-bridge.d.ts +4 -153
  128. package/dist/src/terminal-native-inspection-bridge.js +7 -646
  129. package/dist/src/terminal-native-inspection-bridge.js.map +1 -1
  130. package/dist/src/terminal-native-inspection-contract.d.ts +128 -0
  131. package/dist/src/terminal-native-inspection-contract.js +38 -0
  132. package/dist/src/terminal-native-inspection-contract.js.map +1 -0
  133. package/dist/src/terminal-native-inspection-profile.d.ts +15 -0
  134. package/dist/src/terminal-native-inspection-profile.js +189 -0
  135. package/dist/src/terminal-native-inspection-profile.js.map +1 -0
  136. package/dist/src/terminal-native-inspection-proof.d.ts +62 -0
  137. package/dist/src/terminal-native-inspection-proof.js +362 -0
  138. package/dist/src/terminal-native-inspection-proof.js.map +1 -0
  139. package/dist/src/terminal-permission-control-bridge.js +1 -1
  140. package/dist/src/terminal-permission-control-bridge.js.map +1 -1
  141. package/dist/src/terminal-permission-control.js +5 -1
  142. package/dist/src/terminal-permission-control.js.map +1 -1
  143. package/dist/src/terminal-questionnaire-adapter.js +5 -2
  144. package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
  145. package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -1
  146. package/dist/src/terminal-runtime-cli-adapter.js +1 -1
  147. package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
  148. package/dist/src/terminal-submission-acceptance.d.ts +5 -148
  149. package/dist/src/terminal-submission-acceptance.js +21 -1845
  150. package/dist/src/terminal-submission-acceptance.js.map +1 -1
  151. package/dist/src/terminal-watch-anchor-capture.d.ts +9 -0
  152. package/dist/src/terminal-watch-anchor-capture.js +139 -0
  153. package/dist/src/terminal-watch-anchor-capture.js.map +1 -0
  154. package/dist/src/terminal-watch-cli-adapter.d.ts +3 -117
  155. package/dist/src/terminal-watch-cli-adapter.js +10 -1992
  156. package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
  157. package/dist/src/terminal-watch-cli-contract.d.ts +117 -0
  158. package/dist/src/terminal-watch-cli-contract.js +2 -0
  159. package/dist/src/terminal-watch-cli-contract.js.map +1 -0
  160. package/dist/src/terminal-watch-fallback-observation.d.ts +15 -0
  161. package/dist/src/terminal-watch-fallback-observation.js +249 -0
  162. package/dist/src/terminal-watch-fallback-observation.js.map +1 -0
  163. package/dist/src/terminal-watch-interaction-observation.d.ts +26 -0
  164. package/dist/src/terminal-watch-interaction-observation.js +262 -0
  165. package/dist/src/terminal-watch-interaction-observation.js.map +1 -0
  166. package/dist/src/terminal-watch-observation-adapter.d.ts +4 -0
  167. package/dist/src/terminal-watch-observation-adapter.js +307 -0
  168. package/dist/src/terminal-watch-observation-adapter.js.map +1 -0
  169. package/dist/src/terminal-watch-observation-evidence.d.ts +25 -0
  170. package/dist/src/terminal-watch-observation-evidence.js +184 -0
  171. package/dist/src/terminal-watch-observation-evidence.js.map +1 -0
  172. package/dist/src/terminal-watch-response-authority.d.ts +6 -0
  173. package/dist/src/terminal-watch-response-authority.js +333 -0
  174. package/dist/src/terminal-watch-response-authority.js.map +1 -0
  175. package/dist/src/terminal-watch-response-cli-adapter.d.ts +3 -0
  176. package/dist/src/terminal-watch-response-cli-adapter.js +230 -0
  177. package/dist/src/terminal-watch-response-cli-adapter.js.map +1 -0
  178. package/dist/src/terminal-watch-terminal-identity.d.ts +39 -0
  179. package/dist/src/terminal-watch-terminal-identity.js +380 -0
  180. package/dist/src/terminal-watch-terminal-identity.js.map +1 -0
  181. package/docs/codex-0.160.0-compatibility.md +126 -0
  182. package/docs/codex-permissions.md +12 -1
  183. package/package.json +3 -2
  184. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +3 -3
@@ -0,0 +1,767 @@
1
+ /** Reconcile startup receipts without sending terminal input; preserve terminal/Store/state lock order. */
2
+ import { createHash } from "node:crypto";
3
+ import path from "node:path";
4
+ import { validateCodexRolloutAcceptanceAnchor } from "./terminal-submission-acceptance.js";
5
+ import { deferredForegroundActiveEnterDispatchedAt, deferredForegroundActiveMessageId, deferredForegroundActivePreparedAt, deferredForegroundActiveTextInjectedAt, isDeferredForegroundSubmissionRetryPending, loadDeferredForegroundTransfer, saveDeferredForegroundTransfer } from "./deferred-foreground-transfer.js";
6
+ import { isFinalDeferredForegroundTransferStatus } from "./deferred-foreground-transfer-policy.js";
7
+ import { executorForConversation, effectiveTurnStatus, isExplicitUserAbandonedManagementTurn, isTerminalDispatchOwnerReleasedStatus, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
8
+ import { appendEvent, loadState, pathsForConversationDir, saveState, withStoreWriterLeaseAsync } from "./store.js";
9
+ import { cliPid } from "./cli-runtime-context.js";
10
+ import { terminalControlFromTakeover } from "./terminal-runtime-cli-adapter.js";
11
+ import { terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
12
+ import { terminalControlEvidenceMatches } from "./terminal-control-ref.js";
13
+ import { applyTerminalBridgeSubmission, terminalAcceptanceEvidenceForConversation, terminalBridgeRequestFingerprint, terminalBridgeSubmission } from "./terminal-dispatch-receipt.js";
14
+ import { terminalMonitorDeadlineAt } from "./terminal-monitor-decision-policy.js";
15
+ import { sameCanonicalStatePath } from "./terminal-dispatch-ledger-codec.js";
16
+ import { terminalSubmissionPayload } from "./terminal-dispatch-execution.js";
17
+ import { loadTerminalSubmissionRetry, decideTerminalSubmissionRetryStartup, projectTerminalSubmissionRetryPending, saveTerminalSubmissionRetry, terminalSubmissionRetryLedgerFields } from "./terminal-submission-retry-service.js";
18
+ import { callbackExpectedForConversation, callbackRouteFingerprintForConversation } from "./callback-route-authority.js";
19
+ import { canonicalJson } from "./canonical-json.js";
20
+ import { isRecord, nonBlankString } from "./value-guards.js";
21
+ import { takeoverFor } from "./terminal-monitor-state-contract.js";
22
+ export class TerminalMonitorStartupRecovery {
23
+ #dependencies;
24
+ #stateFileLock;
25
+ constructor(dependencies, stateFileLock) {
26
+ this.#dependencies = dependencies;
27
+ this.#stateFileLock = stateFileLock;
28
+ }
29
+ async recoverSubmissionRetry(storeDir, initialConversation, paths) {
30
+ const initialAttempt = loadTerminalSubmissionRetry(paths.statePath);
31
+ if (!initialAttempt) {
32
+ return initialConversation;
33
+ }
34
+ const initialTakeover = takeoverFor(initialConversation);
35
+ const initialControl = terminalControlFromTakeover(initialTakeover);
36
+ if (executorForConversation(initialConversation).kind !== "codex" ||
37
+ !initialControl) {
38
+ throw new Error("submission retry startup recovery lost its exact Codex terminal authority");
39
+ }
40
+ const canonical = pathsForConversationDir(path.dirname(paths.statePath));
41
+ if (path.resolve(canonical.storeDir) !== path.resolve(storeDir) ||
42
+ path.resolve(canonical.statePath) !== path.resolve(paths.statePath) ||
43
+ path.resolve(canonical.logPath) !== path.resolve(paths.logPath)) {
44
+ throw new Error("submission retry startup recovery is outside its canonical Turn Store");
45
+ }
46
+ const releaseTerminal = this.#dependencies.dispatch.repository.acquire(storeDir, initialControl, { timeoutMs: 30000 });
47
+ try {
48
+ return await withStoreWriterLeaseAsync(storeDir, async () => {
49
+ const releaseState = this.#stateFileLock.acquire(`${paths.statePath}.lock`);
50
+ try {
51
+ let conversation = loadState(paths.statePath);
52
+ const takeover = takeoverFor(conversation);
53
+ const control = terminalControlFromTakeover(takeover);
54
+ const attempt = loadTerminalSubmissionRetry(paths.statePath);
55
+ if (!attempt) {
56
+ return conversation;
57
+ }
58
+ if (executorForConversation(conversation).kind !== "codex" ||
59
+ !control ||
60
+ !terminalControlsShareIncarnation(control, initialControl)) {
61
+ throw new Error("submission retry startup recovery terminal authority changed");
62
+ }
63
+ const callbackDelivery = isRecord(conversation.callback_delivery)
64
+ ? conversation.callback_delivery
65
+ : undefined;
66
+ if (callbackDelivery?.status === "delivered" ||
67
+ isTerminalDispatchOwnerReleasedStatus(effectiveTurnStatus(conversation))) {
68
+ return conversation;
69
+ }
70
+ const submission = terminalBridgeSubmission(conversation);
71
+ const ledger = this.#dependencies.dispatch.repository.load(control);
72
+ if (!submission || !ledger) {
73
+ throw new Error("submission retry startup recovery lacks its Turn or ledger receipt");
74
+ }
75
+ if (!["agent_accepted"].includes(String(submission.status)) &&
76
+ !["agent_accepted"].includes(String(ledger.status)) &&
77
+ !["enter_dispatched", "agent_accepted"].includes(attempt.state)) {
78
+ return conversation;
79
+ }
80
+ const authority = assertTerminalSubmissionRetryStartupAuthority({
81
+ storeDir,
82
+ paths,
83
+ conversation,
84
+ takeover,
85
+ submission,
86
+ ledger,
87
+ attempt,
88
+ control,
89
+ ledgerMatchesControl: this.#dependencies.dispatch.repository
90
+ .matchesControl(ledger, control)
91
+ });
92
+ const startup = decideTerminalSubmissionRetryStartup({
93
+ attempt,
94
+ submission,
95
+ ledger
96
+ });
97
+ if (startup.action === "refuse") {
98
+ throw new Error(startup.reason);
99
+ }
100
+ if (startup.action === "finalize_accepted") {
101
+ return finalizeTerminalSubmissionRetryStartupAccepted({
102
+ storeDir,
103
+ paths,
104
+ conversation,
105
+ submission,
106
+ ledger,
107
+ attempt,
108
+ control,
109
+ authority,
110
+ saveLedger: (next) => this.#dependencies.dispatch.repository
111
+ .save(control, next)
112
+ });
113
+ }
114
+ if (startup.action === "repair_terminal_ledger") {
115
+ reconcileTerminalSubmissionRetryStartupTerminalLedger({
116
+ paths,
117
+ conversation,
118
+ ledger,
119
+ attempt,
120
+ startup,
121
+ saveLedger: (next) => this.#dependencies.dispatch.repository
122
+ .save(control, next)
123
+ });
124
+ return conversation;
125
+ }
126
+ if (startup.action === "repair_terminal_state") {
127
+ return reconcileTerminalSubmissionRetryStartupTerminalState({
128
+ storeDir,
129
+ paths,
130
+ conversation,
131
+ submission,
132
+ attempt,
133
+ control,
134
+ authority,
135
+ startup
136
+ });
137
+ }
138
+ if (startup.action === "no_change")
139
+ return conversation;
140
+ const projection = authority.projection;
141
+ if (!projection) {
142
+ return conversation;
143
+ }
144
+ const receiptAlreadyPending = submission.status === "enter_dispatched";
145
+ if (receiptAlreadyPending
146
+ ? conversation.status !== "waiting_for_agent"
147
+ : !["stalled", "waiting_for_agent"].includes(String(conversation.status))) {
148
+ return conversation;
149
+ }
150
+ let repairedTransfer = false;
151
+ if (authority.deferredTransferId) {
152
+ repairedTransfer = mirrorDeferredSubmissionRetryEnter({
153
+ storeDir,
154
+ transferId: authority.deferredTransferId,
155
+ attempt,
156
+ messageId: projection.messageId,
157
+ preparedAt: projection.preparedAt,
158
+ textInjectedAt: projection.textInjectedAt,
159
+ requestHash: authority.requestHash,
160
+ sessionId: sessionIdForConversation(conversation),
161
+ turnId: turnIdForConversation(conversation),
162
+ statePath: paths.statePath
163
+ });
164
+ }
165
+ const stateNeedsRepair = !receiptAlreadyPending;
166
+ if (receiptAlreadyPending && !terminalSubmissionRetryStateIsPending({
167
+ conversation,
168
+ submission,
169
+ projection
170
+ })) {
171
+ return conversation;
172
+ }
173
+ if (stateNeedsRepair) {
174
+ const epochConversation = terminalSubmissionRetryMonitorEpoch(conversation, projection.enterDispatchedAt);
175
+ conversation = applyTerminalBridgeSubmission({
176
+ conversation: epochConversation,
177
+ messageId: projection.messageId,
178
+ messageType: authority.messageType,
179
+ requestText: authority.requestText,
180
+ status: "enter_dispatched",
181
+ preparedAt: projection.preparedAt,
182
+ textInjectedAt: projection.textInjectedAt,
183
+ enterDispatchedAt: projection.enterDispatchedAt,
184
+ lastProvenStage: "enter_dispatched"
185
+ }, {
186
+ dispatcherPid: cliPid(),
187
+ storeDir,
188
+ terminalControl: control
189
+ });
190
+ saveState(paths.statePath, conversation);
191
+ }
192
+ const ledgerNeedsRepair = ledger.status !== "enter_dispatched" ||
193
+ nonBlankString(ledger.text_injected_at) !==
194
+ projection.textInjectedAt ||
195
+ nonBlankString(ledger.enter_dispatched_at) !==
196
+ projection.enterDispatchedAt ||
197
+ ledger.submission_retry_state !== "enter_dispatched" ||
198
+ ledger.submission_retry_revision !== attempt.revision;
199
+ if (ledgerNeedsRepair) {
200
+ this.#dependencies.dispatch.repository.save(control, {
201
+ ...ledger,
202
+ ...terminalSubmissionRetryLedgerFields(attempt),
203
+ status: "enter_dispatched",
204
+ text_injected_at: projection.textInjectedAt,
205
+ enter_dispatched_at: projection.enterDispatchedAt,
206
+ enter_not_attempted_at: undefined,
207
+ enter_not_attempted_reason: undefined,
208
+ uncertain_at: undefined,
209
+ safe_to_retry: undefined,
210
+ acceptance_evidence: undefined,
211
+ agent_accepted_at: undefined,
212
+ not_accepted_at: undefined,
213
+ dispatcher_pid: null
214
+ });
215
+ }
216
+ if (repairedTransfer || stateNeedsRepair || ledgerNeedsRepair) {
217
+ appendEvent(paths.logPath, {
218
+ ts: projection.enterDispatchedAt,
219
+ conversation_id: conversation.conversation_id,
220
+ event: "terminal_submission_retry_startup_reconciled",
221
+ message_id: projection.messageId,
222
+ terminal_input_sent: false
223
+ });
224
+ }
225
+ return loadState(paths.statePath);
226
+ }
227
+ finally {
228
+ releaseState();
229
+ }
230
+ });
231
+ }
232
+ finally {
233
+ releaseTerminal();
234
+ }
235
+ }
236
+ async recoverDeferred(options, storeDir, initialConversation, paths) {
237
+ if (isExplicitUserAbandonedManagementTurn(initialConversation)) {
238
+ return initialConversation;
239
+ }
240
+ const takeover = takeoverFor(initialConversation);
241
+ const transferId = nonBlankString(takeover?.deferred_foreground_transfer_id);
242
+ if (!transferId) {
243
+ return initialConversation;
244
+ }
245
+ const initialControl = terminalControlFromTakeover(takeover);
246
+ const initialPid = Number(takeover?.terminal_agent_pid);
247
+ if (!initialControl ||
248
+ !Number.isSafeInteger(initialPid) ||
249
+ initialPid <= 1) {
250
+ throw new Error(`deferred foreground Turn ${initialConversation.conversation_id} ` +
251
+ "lost its exact terminal process authority");
252
+ }
253
+ const releaseTerminal = this.#dependencies.dispatch.repository.acquire(storeDir, initialControl, { timeoutMs: 30000 });
254
+ try {
255
+ return await withStoreWriterLeaseAsync(storeDir, async () => {
256
+ const releaseState = this.#stateFileLock.acquire(`${paths.statePath}.lock`);
257
+ let conversation;
258
+ try {
259
+ conversation = loadState(paths.statePath);
260
+ if (isExplicitUserAbandonedManagementTurn(conversation)) {
261
+ return conversation;
262
+ }
263
+ }
264
+ finally {
265
+ releaseState();
266
+ }
267
+ const currentTakeover = takeoverFor(conversation);
268
+ const currentTransferId = nonBlankString(currentTakeover.deferred_foreground_transfer_id);
269
+ const currentControl = terminalControlFromTakeover(currentTakeover);
270
+ const currentPid = Number(currentTakeover.terminal_agent_pid);
271
+ if (currentTransferId !== transferId ||
272
+ !currentControl ||
273
+ !terminalControlsShareIncarnation(currentControl, initialControl) ||
274
+ !Number.isSafeInteger(currentPid) ||
275
+ currentPid !== initialPid) {
276
+ throw new Error(`deferred foreground Turn ${conversation.conversation_id} ` +
277
+ "changed its exact terminal process authority during recovery");
278
+ }
279
+ const transfer = loadDeferredForegroundTransfer(storeDir, transferId);
280
+ if (isFinalDeferredForegroundTransferStatus(transfer.status)) {
281
+ return conversation;
282
+ }
283
+ const terminal = await this.#dependencies.authority
284
+ .createBridge(options)
285
+ .resolveStoredTerminal("codex", currentPid, currentControl, { pid: currentPid });
286
+ await this.#dependencies.authority.handoff
287
+ .recoverDeferredCodexForegroundTransferBeforeMutation({
288
+ options,
289
+ terminal: terminal
290
+ });
291
+ const releaseFinalState = this.#stateFileLock.acquire(`${paths.statePath}.lock`);
292
+ try {
293
+ conversation = loadState(paths.statePath);
294
+ this.#assertExactPendingDeferred(conversation, storeDir, transferId);
295
+ return conversation;
296
+ }
297
+ finally {
298
+ releaseFinalState();
299
+ }
300
+ });
301
+ }
302
+ finally {
303
+ releaseTerminal();
304
+ }
305
+ }
306
+ #assertExactPendingDeferred(conversation, storeDir, transferId) {
307
+ const transfer = loadDeferredForegroundTransfer(storeDir, transferId);
308
+ if (isFinalDeferredForegroundTransferStatus(transfer.status)) {
309
+ return;
310
+ }
311
+ const takeover = takeoverFor(conversation);
312
+ const submission = terminalBridgeSubmission(conversation);
313
+ const anchor = validateCodexRolloutAcceptanceAnchor(takeover?.codex_rollout_acceptance_anchor);
314
+ const activeMessageId = deferredForegroundActiveMessageId(transfer);
315
+ const exactRetryPending = isDeferredForegroundSubmissionRetryPending(transfer) &&
316
+ activeMessageId === transfer.submission_retry_message_id &&
317
+ nonBlankString(submission?.prepared_at) ===
318
+ deferredForegroundActivePreparedAt(transfer) &&
319
+ nonBlankString(submission?.text_injected_at) ===
320
+ deferredForegroundActiveTextInjectedAt(transfer) &&
321
+ nonBlankString(submission?.enter_dispatched_at) ===
322
+ deferredForegroundActiveEnterDispatchedAt(transfer);
323
+ if (anchor?.version !== 3 ||
324
+ conversation.status !== "waiting_for_agent" ||
325
+ submission?.status !== "enter_dispatched" ||
326
+ nonBlankString(submission.message_id) !==
327
+ nonBlankString(takeover?.terminal_bridge_message_id) ||
328
+ nonBlankString(submission.message_id) !== activeMessageId ||
329
+ !(transfer.status === "dispatch_started" || exactRetryPending)) {
330
+ throw new Error(`deferred foreground Turn ${conversation.conversation_id} ` +
331
+ "is not an exact pending candidate-set acceptance");
332
+ }
333
+ }
334
+ }
335
+ function terminalSubmissionRetryStartupAuthorityContext(input) {
336
+ const sessionId = sessionIdForConversation(input.conversation);
337
+ const turnId = turnIdForConversation(input.conversation);
338
+ const messageId = nonBlankString(input.submission.message_id);
339
+ const requestText = terminalSubmissionPayload(nonBlankString(input.takeover.terminal_bridge_request_text) ?? "");
340
+ const requestHash = terminalBridgeRequestFingerprint(requestText);
341
+ const messageType = nonBlankString(input.submission.message_type);
342
+ const messageBodyHash = createHash("sha256")
343
+ .update(requestText)
344
+ .digest("hex");
345
+ const callbackFingerprint = callbackRouteFingerprintForConversation(input.conversation) ?? null;
346
+ const deferredReferences = [
347
+ nonBlankString(input.takeover.deferred_foreground_transfer_id),
348
+ nonBlankString(input.submission.deferred_foreground_transfer_id),
349
+ nonBlankString(input.ledger.deferred_foreground_transfer_id),
350
+ input.attempt.deferred_foreground_transfer_id ?? undefined
351
+ ];
352
+ const presentDeferredReferences = deferredReferences.filter((value) => value !== undefined);
353
+ const deferredTransferId = presentDeferredReferences[0];
354
+ const submissionTerminalEvidence = input.submission.terminal_endpoint !== undefined
355
+ ? input.submission.terminal_endpoint
356
+ : input.submission.terminal_control;
357
+ const statePath = nonBlankString(input.conversation.state_path);
358
+ const logPath = nonBlankString(input.conversation.event_log_path);
359
+ const bindingId = nonBlankString(input.conversation.terminal_binding_id);
360
+ const bindingGeneration = Number(input.conversation.terminal_binding_generation);
361
+ return {
362
+ sessionId,
363
+ turnId,
364
+ messageId,
365
+ requestText,
366
+ requestHash,
367
+ messageType,
368
+ messageBodyHash,
369
+ callbackFingerprint,
370
+ deferredReferences,
371
+ presentDeferredReferences,
372
+ deferredTransferId,
373
+ submissionTerminalEvidence,
374
+ statePath,
375
+ logPath,
376
+ bindingId,
377
+ bindingGeneration
378
+ };
379
+ }
380
+ function terminalSubmissionRetryStartupIdentityMatches(input, context) {
381
+ return !(!context.requestText || !context.requestHash || !context.messageId ||
382
+ (context.messageType !== "task" && context.messageType !== "answer") ||
383
+ nonBlankString(input.takeover.terminal_bridge_message_id) !==
384
+ context.messageId ||
385
+ input.attempt.session_id !== context.sessionId ||
386
+ input.attempt.turn_id !== context.turnId ||
387
+ input.attempt.original_message_id !== context.messageId ||
388
+ input.attempt.active_message_id !== context.messageId ||
389
+ input.attempt.request_hash !== context.requestHash ||
390
+ input.attempt.terminal_target !== input.control.target ||
391
+ input.attempt.callback_route_fingerprint !== context.callbackFingerprint);
392
+ }
393
+ function terminalSubmissionRetryStartupSubmissionPathMatches(input) {
394
+ return path.resolve(nonBlankString(input.submission.store_dir) ?? "") ===
395
+ path.resolve(input.storeDir);
396
+ }
397
+ function terminalSubmissionRetryStartupSubmissionMatches(input, context) {
398
+ return !(nonBlankString(input.submission.session_id) !== context.sessionId ||
399
+ nonBlankString(input.submission.turn_id) !== context.turnId ||
400
+ nonBlankString(input.submission.request_hash) !== context.requestHash ||
401
+ nonBlankString(input.submission.message_body_hash) !==
402
+ context.messageBodyHash ||
403
+ nonBlankString(input.submission.executor_kind) !== "codex" ||
404
+ !terminalSubmissionRetryStartupSubmissionPathMatches(input) ||
405
+ (input.submission.callback_route_fingerprint ?? null) !==
406
+ context.callbackFingerprint ||
407
+ !terminalControlEvidenceMatches(context.submissionTerminalEvidence, input.control));
408
+ }
409
+ function terminalSubmissionRetryStartupLedgerPathMatches(input, context) {
410
+ return !(path.resolve(nonBlankString(input.ledger.store_dir) ?? "") !==
411
+ path.resolve(input.storeDir) ||
412
+ !sameCanonicalStatePath(input.ledger.state_path, input.paths.statePath) ||
413
+ !sameCanonicalStatePath(context.statePath, input.paths.statePath) ||
414
+ !sameCanonicalStatePath(input.ledger.event_log_path, input.paths.logPath) ||
415
+ !sameCanonicalStatePath(context.logPath, input.paths.logPath));
416
+ }
417
+ function terminalSubmissionRetryStartupLedgerMatches(input, context) {
418
+ return !(nonBlankString(input.ledger.generation_id) !== context.messageId ||
419
+ nonBlankString(input.ledger.message_id) !== context.messageId ||
420
+ nonBlankString(input.ledger.conversation_id) !==
421
+ input.conversation.conversation_id ||
422
+ nonBlankString(input.ledger.session_id) !== context.sessionId ||
423
+ nonBlankString(input.ledger.turn_id) !== context.turnId ||
424
+ nonBlankString(input.ledger.message_type) !== context.messageType ||
425
+ nonBlankString(input.ledger.message_body_hash) !==
426
+ context.messageBodyHash ||
427
+ nonBlankString(input.ledger.executor_kind) !== "codex" ||
428
+ nonBlankString(input.ledger.request_hash) !== context.requestHash ||
429
+ !terminalSubmissionRetryStartupLedgerPathMatches(input, context) ||
430
+ (input.ledger.callback_route_fingerprint ?? null) !==
431
+ context.callbackFingerprint ||
432
+ input.ledger.callback_expected !==
433
+ callbackExpectedForConversation(input.conversation) ||
434
+ !input.ledgerMatchesControl ||
435
+ (context.bindingId !== undefined &&
436
+ nonBlankString(input.ledger.binding_id) !== context.bindingId) ||
437
+ (Number.isSafeInteger(context.bindingGeneration) &&
438
+ Number(input.ledger.binding_generation) !== context.bindingGeneration));
439
+ }
440
+ function terminalSubmissionRetryStartupDeferredMatches(input, context) {
441
+ return !((context.deferredTransferId !== undefined &&
442
+ (context.presentDeferredReferences.length !==
443
+ context.deferredReferences.length ||
444
+ new Set(context.presentDeferredReferences).size !== 1)) ||
445
+ (context.deferredTransferId === undefined &&
446
+ input.attempt.deferred_foreground_transfer_id !== null));
447
+ }
448
+ function terminalSubmissionRetryStartupProjectionFields(input) {
449
+ if (input.attempt.state === "agent_accepted" ||
450
+ ["agent_accepted", "not_accepted"].includes(String(input.submission.status)) ||
451
+ ["agent_accepted", "not_accepted"].includes(String(input.ledger.status))) {
452
+ return {};
453
+ }
454
+ return {
455
+ projection: projectTerminalSubmissionRetryPending({
456
+ attempt: input.attempt,
457
+ submission: input.submission,
458
+ ledger: input.ledger
459
+ })
460
+ };
461
+ }
462
+ function assertTerminalSubmissionRetryStartupAuthority(input) {
463
+ const context = terminalSubmissionRetryStartupAuthorityContext(input);
464
+ if (!terminalSubmissionRetryStartupIdentityMatches(input, context) ||
465
+ !terminalSubmissionRetryStartupSubmissionMatches(input, context) ||
466
+ !terminalSubmissionRetryStartupLedgerMatches(input, context) ||
467
+ !terminalSubmissionRetryStartupDeferredMatches(input, context)) {
468
+ throw new Error("submission retry startup recovery authority disagrees across Turn and ledger");
469
+ }
470
+ return {
471
+ requestText: context.requestText,
472
+ requestHash: context.requestHash,
473
+ messageType: context.messageType,
474
+ deferredTransferId: context.deferredTransferId,
475
+ ...terminalSubmissionRetryStartupProjectionFields(input)
476
+ };
477
+ }
478
+ function finalizeTerminalSubmissionRetryStartupAccepted(input) {
479
+ const stateAccepted = input.submission.status === "agent_accepted";
480
+ const ledgerAccepted = input.ledger.status === "agent_accepted";
481
+ if (!stateAccepted && !ledgerAccepted) {
482
+ throw new Error("accepted submission retry sidecar lacks native acceptance evidence");
483
+ }
484
+ const stateEvidence = stateAccepted
485
+ ? terminalAcceptanceEvidenceForConversation(input.conversation, input.authority.requestText, input.submission.acceptance_evidence)
486
+ : undefined;
487
+ const ledgerEvidence = ledgerAccepted
488
+ ? terminalAcceptanceEvidenceForConversation(input.conversation, input.authority.requestText, input.ledger.acceptance_evidence)
489
+ : undefined;
490
+ if (stateEvidence && ledgerEvidence &&
491
+ canonicalJson(stateEvidence) !== canonicalJson(ledgerEvidence)) {
492
+ throw new Error("accepted submission retry evidence conflicts across Turn and ledger");
493
+ }
494
+ const evidence = stateEvidence ?? ledgerEvidence;
495
+ const stateAcceptedAt = stateAccepted
496
+ ? nonBlankString(input.submission.agent_accepted_at)
497
+ : undefined;
498
+ const ledgerAcceptedAt = ledgerAccepted
499
+ ? nonBlankString(input.ledger.agent_accepted_at)
500
+ : undefined;
501
+ if (!evidence || !stateAcceptedAt && !ledgerAcceptedAt ||
502
+ stateAcceptedAt && ledgerAcceptedAt && stateAcceptedAt !== ledgerAcceptedAt) {
503
+ throw new Error("accepted submission retry timestamp conflicts across Turn and ledger");
504
+ }
505
+ const acceptedAt = (stateAcceptedAt ?? ledgerAcceptedAt);
506
+ if (input.authority.deferredTransferId) {
507
+ const transfer = loadDeferredForegroundTransfer(input.storeDir, input.authority.deferredTransferId);
508
+ if (transfer.status !== "resolved") {
509
+ if (isFinalDeferredForegroundTransferStatus(transfer.status)) {
510
+ throw new Error("accepted submission retry conflicts with an aborted deferred transfer");
511
+ }
512
+ return input.conversation;
513
+ }
514
+ if (transfer.agent_accepted_at !== acceptedAt ||
515
+ transfer.message_id !== input.attempt.active_message_id) {
516
+ throw new Error("accepted submission retry conflicts with its resolved deferred transfer");
517
+ }
518
+ }
519
+ if (input.attempt.state === "agent_accepted" &&
520
+ input.attempt.agent_accepted_at !== acceptedAt) {
521
+ throw new Error("accepted submission retry sidecar timestamp conflicts with native evidence");
522
+ }
523
+ let conversation = input.conversation;
524
+ const stateNeedsRepair = !stateAccepted;
525
+ if (stateNeedsRepair) {
526
+ const acceptedOwner = {
527
+ ...conversation,
528
+ status: "waiting_for_agent",
529
+ updated_at: acceptedAt
530
+ };
531
+ delete acceptedOwner.stalled_at;
532
+ delete acceptedOwner.stalled_reason;
533
+ delete acceptedOwner.failed_at;
534
+ delete acceptedOwner.failure_reason;
535
+ delete acceptedOwner.idle_since;
536
+ conversation = applyTerminalBridgeSubmission({
537
+ conversation: acceptedOwner,
538
+ messageId: input.attempt.active_message_id,
539
+ messageType: input.authority.messageType,
540
+ requestText: input.authority.requestText,
541
+ status: "agent_accepted",
542
+ preparedAt: nonBlankString(input.submission.prepared_at),
543
+ textInjectedAt: nonBlankString(input.submission.text_injected_at) ??
544
+ nonBlankString(input.ledger.text_injected_at),
545
+ enterDispatchedAt: nonBlankString(input.submission.enter_dispatched_at) ??
546
+ nonBlankString(input.ledger.enter_dispatched_at),
547
+ agentAcceptedAt: acceptedAt,
548
+ acceptanceEvidence: evidence,
549
+ lastProvenStage: "agent_accepted"
550
+ }, {
551
+ dispatcherPid: cliPid(),
552
+ storeDir: input.storeDir,
553
+ terminalControl: input.control
554
+ });
555
+ saveState(input.paths.statePath, conversation);
556
+ }
557
+ let attempt = input.attempt;
558
+ const attemptNeedsRepair = attempt.state !== "agent_accepted";
559
+ if (attemptNeedsRepair) {
560
+ attempt = saveTerminalSubmissionRetry(input.paths.statePath, {
561
+ ...attempt,
562
+ state: "agent_accepted",
563
+ agent_accepted_at: acceptedAt,
564
+ updated_at: acceptedAt
565
+ }, attempt.revision);
566
+ }
567
+ const ledgerNeedsRepair = !ledgerAccepted ||
568
+ input.ledger.submission_retry_state !== "agent_accepted" ||
569
+ input.ledger.submission_retry_revision !== attempt.revision;
570
+ if (ledgerNeedsRepair) {
571
+ input.saveLedger({
572
+ ...input.ledger,
573
+ ...terminalSubmissionRetryLedgerFields(attempt),
574
+ status: "agent_accepted",
575
+ agent_accepted_at: acceptedAt,
576
+ acceptance_evidence: evidence,
577
+ not_accepted_at: undefined,
578
+ uncertain_at: undefined,
579
+ safe_to_retry: undefined,
580
+ dispatcher_pid: null
581
+ });
582
+ }
583
+ if (stateNeedsRepair || attemptNeedsRepair || ledgerNeedsRepair) {
584
+ appendEvent(input.paths.logPath, {
585
+ ts: acceptedAt,
586
+ conversation_id: conversation.conversation_id,
587
+ event: "terminal_submission_retry_startup_accepted_reconciled",
588
+ message_id: input.attempt.active_message_id,
589
+ terminal_input_sent: false
590
+ });
591
+ }
592
+ return loadState(input.paths.statePath);
593
+ }
594
+ function reconcileTerminalSubmissionRetryStartupTerminalLedger(input) {
595
+ const notAccepted = input.startup.outcome === "not_accepted";
596
+ input.saveLedger({
597
+ ...input.ledger,
598
+ ...terminalSubmissionRetryLedgerFields(input.attempt),
599
+ status: input.startup.outcome,
600
+ ...(notAccepted
601
+ ? {
602
+ not_accepted_at: input.startup.at,
603
+ uncertain_at: undefined,
604
+ error: undefined,
605
+ safe_to_retry: undefined
606
+ }
607
+ : {
608
+ not_accepted_at: undefined,
609
+ uncertain_at: input.startup.at,
610
+ error: input.startup.reason,
611
+ safe_to_retry: false
612
+ }),
613
+ acceptance_evidence: undefined,
614
+ agent_accepted_at: undefined,
615
+ dispatcher_pid: null
616
+ });
617
+ appendEvent(input.paths.logPath, {
618
+ ts: input.startup.at,
619
+ conversation_id: input.conversation.conversation_id,
620
+ event: `terminal_submission_retry_startup_${input.startup.outcome}_reconciled`,
621
+ message_id: input.attempt.active_message_id,
622
+ terminal_input_sent: false,
623
+ do_not_retry: true
624
+ });
625
+ }
626
+ function reconcileTerminalSubmissionRetryStartupTerminalState(input) {
627
+ const stalled = {
628
+ ...input.conversation,
629
+ status: "stalled",
630
+ stalled_at: input.startup.at,
631
+ stalled_reason: input.startup.reason ??
632
+ "submission retry native acceptance became uncertain",
633
+ updated_at: input.startup.at
634
+ };
635
+ const conversation = applyTerminalBridgeSubmission({
636
+ conversation: stalled,
637
+ messageId: input.attempt.active_message_id,
638
+ messageType: input.authority.messageType,
639
+ requestText: input.authority.requestText,
640
+ status: "uncertain",
641
+ preparedAt: nonBlankString(input.submission.prepared_at),
642
+ textInjectedAt: nonBlankString(input.submission.text_injected_at),
643
+ enterDispatchedAt: nonBlankString(input.submission.enter_dispatched_at),
644
+ uncertainAt: input.startup.at,
645
+ error: input.startup.reason,
646
+ safeToRetry: false,
647
+ lastProvenStage: "enter_dispatched"
648
+ }, {
649
+ dispatcherPid: cliPid(),
650
+ storeDir: input.storeDir,
651
+ terminalControl: input.control
652
+ });
653
+ saveState(input.paths.statePath, conversation);
654
+ appendEvent(input.paths.logPath, {
655
+ ts: input.startup.at,
656
+ conversation_id: conversation.conversation_id,
657
+ event: "terminal_submission_retry_startup_uncertain_reconciled",
658
+ message_id: input.attempt.active_message_id,
659
+ terminal_input_sent: false,
660
+ do_not_retry: true
661
+ });
662
+ return conversation;
663
+ }
664
+ function mirrorDeferredSubmissionRetryEnter(input) {
665
+ const transfer = loadDeferredForegroundTransfer(input.storeDir, input.transferId);
666
+ assertDeferredSubmissionRetryMirrorAuthority(transfer, input);
667
+ if (isFinalDeferredForegroundTransferStatus(transfer.status)) {
668
+ return false;
669
+ }
670
+ if (transfer.submission_retry_enter_dispatched_at) {
671
+ if (!isDeferredForegroundSubmissionRetryPending(transfer)) {
672
+ throw new Error("deferred submission retry startup authority is not exactly pending");
673
+ }
674
+ return false;
675
+ }
676
+ if (transfer.status !== "uncertain" ||
677
+ transfer.input_stage !== "text_injected" ||
678
+ transfer.enter_dispatched_at !== undefined) {
679
+ throw new Error("deferred submission retry startup recovery found an unsafe stage lag");
680
+ }
681
+ const repaired = saveDeferredForegroundTransfer(input.storeDir, {
682
+ ...transfer,
683
+ input_stage: "enter_dispatched",
684
+ enter_dispatched_at: input.attempt.enter_dispatched_at,
685
+ submission_retry_enter_dispatched_at: input.attempt.enter_dispatched_at
686
+ }, {
687
+ expectedRevision: Number(transfer.revision)
688
+ });
689
+ if (!isDeferredForegroundSubmissionRetryPending(repaired)) {
690
+ throw new Error("deferred submission retry startup recovery did not produce exact pending authority");
691
+ }
692
+ return true;
693
+ }
694
+ function assertDeferredSubmissionRetryMirrorAuthority(transfer, input) {
695
+ const exactMode = input.attempt.mode === "exact_draft_enter";
696
+ if (transfer.transfer_id !== input.transferId ||
697
+ transfer.turn_id !== input.turnId ||
698
+ transfer.target_session_id !== input.sessionId ||
699
+ !sameCanonicalStatePath(transfer.state_path, input.statePath) ||
700
+ transfer.request_hash !== input.requestHash ||
701
+ transfer.message_id !== input.messageId ||
702
+ transfer.prepared_at !== input.preparedAt ||
703
+ transfer.submission_retry_attempt_id !== input.attempt.attempt_id ||
704
+ transfer.submission_retry_mode !== input.attempt.mode ||
705
+ transfer.submission_retry_message_id !== input.messageId ||
706
+ transfer.submission_retry_prepared_at !== input.preparedAt ||
707
+ transfer.submission_retry_enter_reserved_at !==
708
+ input.attempt.enter_reserved_at ||
709
+ transfer.submission_retry_enter_dispatched_at !== undefined &&
710
+ transfer.submission_retry_enter_dispatched_at !==
711
+ input.attempt.enter_dispatched_at ||
712
+ transfer.submission_retry_enter_dispatched_at !== undefined &&
713
+ (transfer.input_stage !== "enter_dispatched" ||
714
+ transfer.enter_dispatched_at !== input.attempt.enter_dispatched_at) ||
715
+ (exactMode
716
+ ? transfer.submission_retry_text_reserved_at !== undefined ||
717
+ transfer.submission_retry_text_injected_at !== undefined ||
718
+ transfer.text_injected_at !== input.textInjectedAt
719
+ : transfer.submission_retry_text_reserved_at !==
720
+ input.attempt.replacement_text_reserved_at ||
721
+ transfer.submission_retry_text_injected_at !==
722
+ input.attempt.replacement_text_injected_at)) {
723
+ throw new Error("deferred submission retry startup authority disagrees with its attempt");
724
+ }
725
+ }
726
+ function terminalSubmissionRetryMonitorEpoch(conversation, at) {
727
+ const takeover = takeoverFor(conversation);
728
+ const inactivityMinutes = Number(takeover.terminal_bridge_inactivity_timeout_minutes);
729
+ const hardMinutes = Number(takeover.terminal_bridge_hard_timeout_minutes);
730
+ const inactivityDeadlineAt = terminalMonitorDeadlineAt(at, inactivityMinutes);
731
+ const hardDeadlineAt = terminalMonitorDeadlineAt(at, hardMinutes);
732
+ if (!inactivityDeadlineAt || !hardDeadlineAt) {
733
+ throw new Error("submission retry startup recovery lacks its configured monitor timeouts");
734
+ }
735
+ const next = {
736
+ ...conversation,
737
+ status: "waiting_for_agent",
738
+ native_session_takeover: {
739
+ ...takeover,
740
+ terminal_bridge_started_at: at,
741
+ terminal_bridge_monitor_started_at: at,
742
+ terminal_bridge_last_activity_at: at,
743
+ terminal_bridge_last_activity_reason: "submission retry Enter dispatched",
744
+ terminal_bridge_inactivity_deadline_at: inactivityDeadlineAt,
745
+ terminal_bridge_hard_deadline_at: hardDeadlineAt
746
+ },
747
+ updated_at: at
748
+ };
749
+ delete next.stalled_at;
750
+ delete next.stalled_reason;
751
+ delete next.failed_at;
752
+ delete next.failure_reason;
753
+ delete next.idle_since;
754
+ return next;
755
+ }
756
+ function terminalSubmissionRetryStateIsPending(input) {
757
+ return input.conversation.status === "waiting_for_agent" &&
758
+ input.submission.status === "enter_dispatched" &&
759
+ nonBlankString(input.submission.message_id) === input.projection.messageId &&
760
+ nonBlankString(input.submission.prepared_at) ===
761
+ input.projection.preparedAt &&
762
+ nonBlankString(input.submission.text_injected_at) ===
763
+ input.projection.textInjectedAt &&
764
+ nonBlankString(input.submission.enter_dispatched_at) ===
765
+ input.projection.enterDispatchedAt;
766
+ }
767
+ //# sourceMappingURL=terminal-monitor-startup-recovery.js.map