@scotthuang/agent-knock-knock 0.12.13 → 0.12.15
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.
- package/CHANGELOG.md +44 -0
- package/README.md +13 -13
- package/dist/src/callback-cli-adapter.d.ts +9 -6
- package/dist/src/callback-cli-adapter.js +119 -30
- package/dist/src/callback-cli-adapter.js.map +1 -1
- package/dist/src/callback-outbox-policy.d.ts +39 -0
- package/dist/src/callback-outbox-policy.js +163 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +28 -1
- package/dist/src/callback-outbox-service.js +413 -82
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.d.ts +14 -1
- package/dist/src/callback-outbox-settlement.js +151 -30
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-authority.d.ts +59 -0
- package/dist/src/callback-route-authority.js +123 -0
- package/dist/src/callback-route-authority.js.map +1 -0
- package/dist/src/callback-transport.d.ts +134 -0
- package/dist/src/callback-transport.js +256 -0
- package/dist/src/callback-transport.js.map +1 -0
- package/dist/src/cli-core.js +56 -18
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/cli-runtime-context.d.ts +2 -0
- package/dist/src/cli-runtime-context.js.map +1 -1
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/deferred-foreground-application-service.d.ts +18 -0
- package/dist/src/deferred-foreground-application-service.js +72 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +10 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +108 -37
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-service.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-service.js +10 -3
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -1
- package/dist/src/deferred-foreground-transfer-policy.d.ts +1 -1
- package/dist/src/deferred-foreground-transfer-policy.js +3 -2
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.d.ts +17 -1
- package/dist/src/deferred-foreground-transfer.js +244 -35
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/deferred-foreground-user-close.d.ts +18 -0
- package/dist/src/deferred-foreground-user-close.js +180 -0
- package/dist/src/deferred-foreground-user-close.js.map +1 -0
- package/dist/src/install-doctor-command-adapter.js +1 -1
- package/dist/src/install-doctor-command-adapter.js.map +1 -1
- package/dist/src/openclaw-callback-transport.d.ts +4 -1
- package/dist/src/openclaw-callback-transport.js +403 -10
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/openclaw-doctor.js +16 -2
- package/dist/src/openclaw-doctor.js.map +1 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.d.ts +23 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js +77 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.js +93 -39
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +1 -0
- package/dist/src/openclaw-plugin-helpers.js +9 -1
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +20 -1
- package/dist/src/openclaw-plugin-schemas.js +27 -3
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/protocol.d.ts +3 -0
- package/dist/src/protocol.js +4 -0
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/store.d.ts +12 -1
- package/dist/src/store.js +81 -3
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +62 -12
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +31 -3
- package/dist/src/terminal-action-projection.js +64 -9
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +82 -0
- package/dist/src/terminal-agent-bridge.js +553 -109
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +26 -2
- package/dist/src/terminal-command-cli-adapter.js +3162 -214
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.d.ts +4 -22
- package/dist/src/terminal-delegate-cli-adapter.js +239 -192
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-send-binding.d.ts +63 -0
- package/dist/src/terminal-delegate-send-binding.js +360 -0
- package/dist/src/terminal-delegate-send-binding.js.map +1 -0
- package/dist/src/terminal-dispatch-application.d.ts +4 -1
- package/dist/src/terminal-dispatch-application.js +11 -2
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-composition.d.ts +5 -0
- package/dist/src/terminal-dispatch-execution.js +3 -1
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +3 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +46 -19
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.js +5 -3
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +3 -0
- package/dist/src/terminal-dispatch-receipt.js +15 -1
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +68 -7
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-service.d.ts +2 -0
- package/dist/src/terminal-dispatch-recovery-service.js +9 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
- package/dist/src/terminal-dispatch-repository-cli-adapter.d.ts +4 -2
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +10 -3
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +34 -27
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +6 -0
- package/dist/src/terminal-list-cli-adapter.js +391 -177
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +9 -1
- package/dist/src/terminal-list-renderer.js +226 -104
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +253 -326
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +5 -0
- package/dist/src/terminal-monitor-application-service.js +5 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +25 -1
- package/dist/src/terminal-monitor-cli-adapter.js +170 -27
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +220 -32
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +808 -147
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +2 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js +12 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +31 -11
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -1
- package/dist/src/terminal-process-source.d.ts +13 -0
- package/dist/src/terminal-process-source.js +37 -0
- package/dist/src/terminal-process-source.js.map +1 -1
- package/dist/src/terminal-runtime-cli-adapter.js +14 -2
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +1 -0
- package/dist/src/terminal-status-cli-adapter.js +30 -28
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.js +78 -25
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-retry-service.d.ts +114 -0
- package/dist/src/terminal-submission-retry-service.js +589 -0
- package/dist/src/terminal-submission-retry-service.js.map +1 -0
- package/dist/src/terminal-user-send-intent.d.ts +65 -0
- package/dist/src/terminal-user-send-intent.js +317 -0
- package/dist/src/terminal-user-send-intent.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +10 -2
- package/dist/src/terminal-watch-callback-cli-adapter.js +76 -37
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-adapter.js +41 -17
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-service.d.ts +11 -6
- package/dist/src/terminal-watch-service.js +210 -41
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +23 -0
- package/dist/src/terminal-watch-store.js +162 -4
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/quickstart-herdr.md +1 -1
- package/docs/quickstart-tmux.md +3 -3
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +14 -11
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// Raw CLI composition for renew, cancel, and close maintenance commands.
|
|
2
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { supersedeCallbackNotificationDelivery, supersedeUnacceptedCallbackDeliveries } from "./callback-outbox-policy.js";
|
|
4
5
|
import { withCanonicalMutationLocks } from "./mutation-transaction.js";
|
|
5
6
|
import { budgetAction, executorForConversation, sessionIdForConversation, isSessionSendBlockingStatus, turnIdForConversation } from "./protocol.js";
|
|
6
|
-
import { appendEvent, loadState, logPathForStatePath, pathsForConversationDir, saveState, withStoreWriterLeaseAsync } from "./store.js";
|
|
7
|
+
import { appendEvent, appendExplicitUserCloseEvent, loadState, logPathForStatePath, pathsForConversationDir, saveExplicitUserCloseState, saveState, withStoreWriterLeaseAsync } from "./store.js";
|
|
7
8
|
import { loadManagedSession, tryLoadManagedSession } from "./session-store.js";
|
|
8
9
|
import { TerminalControlUnavailableError } from "./terminal-control-provider.js";
|
|
9
10
|
import { terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
|
|
@@ -13,11 +14,12 @@ import { terminalBridgeSubmission } from "./terminal-dispatch-receipt.js";
|
|
|
13
14
|
import { terminalDispatchLedgerLooksLifecycle } from "./terminal-dispatch-ledger-codec.js";
|
|
14
15
|
import { loadDeferredForegroundTransfer } from "./deferred-foreground-transfer.js";
|
|
15
16
|
import { isFinalDeferredForegroundTransferStatus } from "./deferred-foreground-transfer-policy.js";
|
|
17
|
+
import { cleanupDeferredForegroundUserClose } from "./deferred-foreground-user-close.js";
|
|
16
18
|
import { isRecoverableTerminalDispatchStatus } from "./terminal-dispatch-policy.js";
|
|
17
19
|
import { decideVerifiedDeadAgentProcess } from "./verified-dead-agent-policy.js";
|
|
18
20
|
import { terminalMonitorDeadlineAt as deadlineAt } from "./terminal-monitor-decision-policy.js";
|
|
19
21
|
import { writeCliJson as printJson } from "./cli-command-runtime.js";
|
|
20
|
-
import { cliCwd,
|
|
22
|
+
import { cliCwd, cliNow, cliNowMs, cliRuntimeLog as runtimeLog } from "./cli-runtime-context.js";
|
|
21
23
|
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
22
24
|
const terminalMaintenanceContext = new AsyncLocalStorage();
|
|
23
25
|
function terminalMaintenanceRuntime() {
|
|
@@ -137,94 +139,100 @@ async function runRenew(options) {
|
|
|
137
139
|
let renewedTerminalControl = terminalControl;
|
|
138
140
|
let inactivityTimeoutMinutes = 0;
|
|
139
141
|
let hardTimeoutMinutes = 0;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
const writerStoreDir = pathsForConversationDir(path.dirname(statePath)).storeDir;
|
|
143
|
+
await withStoreWriterLease(writerStoreDir, async () => {
|
|
144
|
+
const releaseStateLock = acquireFileLock(`${statePath}.lock`);
|
|
145
|
+
try {
|
|
146
|
+
const current = loadState(statePath);
|
|
147
|
+
if (current.status !== "stalled") {
|
|
148
|
+
throw new Error(`cannot renew ${current.conversation_id}; conversation is ${current.status}, not stalled`);
|
|
149
|
+
}
|
|
150
|
+
if (isVerifiedDeadTerminalAgentProcess(current)) {
|
|
151
|
+
throw new Error(`cannot renew ${current.conversation_id}; its bound terminal agent ` +
|
|
152
|
+
"process is verified dead");
|
|
153
|
+
}
|
|
154
|
+
if (terminalBridgeSubmission(current)?.status === "uncertain") {
|
|
155
|
+
throw new Error(`cannot renew ${current.conversation_id}; its terminal submission is uncertain`);
|
|
156
|
+
}
|
|
157
|
+
const currentTakeover = isRecord(current.native_session_takeover)
|
|
158
|
+
? current.native_session_takeover
|
|
159
|
+
: undefined;
|
|
160
|
+
const currentControl = terminalControlFromTakeover(currentTakeover);
|
|
161
|
+
if (!currentControl || currentTakeover?.terminal_bridge !== true) {
|
|
162
|
+
throw new Error(`cannot renew ${current.conversation_id}; conversation is not a terminal bridge task`);
|
|
163
|
+
}
|
|
164
|
+
if (current.conversation_id !== conversation.conversation_id ||
|
|
165
|
+
!terminalControlsShareIncarnation(currentControl, terminalControl) ||
|
|
166
|
+
stringValue(currentTakeover.terminal_bridge_message_id) !== expectedMessageId ||
|
|
167
|
+
stringValue(currentTakeover.terminal_bridge_started_at) !== expectedStartedAt) {
|
|
168
|
+
throw new Error("conversation changed while waiting to renew; refresh status and retry");
|
|
169
|
+
}
|
|
170
|
+
assertTurnBindingCurrent(current, "renew");
|
|
171
|
+
renewedTerminalControl = currentControl;
|
|
172
|
+
inactivityTimeoutMinutes = positiveMinutes(options.minutes ??
|
|
173
|
+
options.agentTimeoutMinutes ??
|
|
174
|
+
currentTakeover.terminal_bridge_inactivity_timeout_minutes ??
|
|
175
|
+
terminalMaintenanceRuntime().dependencies.runtime
|
|
176
|
+
.defaultAgentTimeoutMinutes, "--minutes");
|
|
177
|
+
hardTimeoutMinutes = positiveMinutes(currentTakeover.terminal_bridge_hard_timeout_minutes ??
|
|
178
|
+
terminalMaintenanceRuntime().dependencies.runtime
|
|
179
|
+
.defaultAgentHardTimeoutMinutes, "--agent-hard-timeout-minutes");
|
|
180
|
+
const startedAt = stringValue(currentTakeover.terminal_bridge_started_at);
|
|
181
|
+
const startedAtMs = startedAt ? Date.parse(startedAt) : NaN;
|
|
182
|
+
if (Number.isFinite(startedAtMs) &&
|
|
183
|
+
cliNowMs() - startedAtMs >= hardTimeoutMinutes * 60 * 1000) {
|
|
184
|
+
throw new Error(`cannot renew ${current.conversation_id}; terminal bridge hard lifetime of ${hardTimeoutMinutes} minutes has elapsed`);
|
|
185
|
+
}
|
|
186
|
+
const now = cliNow().toISOString();
|
|
187
|
+
const hardDeadline = deadlineAt(startedAt ?? now, hardTimeoutMinutes) ??
|
|
188
|
+
new Date(cliNowMs() + hardTimeoutMinutes * 60 * 1000).toISOString();
|
|
189
|
+
const inactivityDeadline = deadlineAt(now, inactivityTimeoutMinutes) ??
|
|
190
|
+
new Date(cliNowMs() + inactivityTimeoutMinutes * 60 * 1000).toISOString();
|
|
191
|
+
renewed = {
|
|
192
|
+
...supersedeCallbackNotificationDelivery(current, {
|
|
193
|
+
at: now,
|
|
194
|
+
reason: "superseded_by_terminal_monitor_renewal"
|
|
195
|
+
}),
|
|
196
|
+
status: "waiting_for_agent",
|
|
197
|
+
native_session_takeover: {
|
|
198
|
+
...currentTakeover,
|
|
199
|
+
terminal_bridge_monitor_lock_version: terminalMaintenanceRuntime().dependencies.runtime.monitorLockVersion,
|
|
200
|
+
terminal_bridge_monitor_started_at: now,
|
|
201
|
+
terminal_bridge_last_activity_at: now,
|
|
202
|
+
terminal_bridge_inactivity_timeout_minutes: inactivityTimeoutMinutes,
|
|
203
|
+
terminal_bridge_hard_timeout_minutes: hardTimeoutMinutes,
|
|
204
|
+
terminal_bridge_inactivity_deadline_at: inactivityDeadline,
|
|
205
|
+
terminal_bridge_hard_deadline_at: hardDeadline,
|
|
206
|
+
terminal_bridge_renewed_at: now
|
|
207
|
+
},
|
|
208
|
+
updated_at: now
|
|
209
|
+
};
|
|
210
|
+
Reflect.deleteProperty(renewed, "stalled_at");
|
|
211
|
+
Reflect.deleteProperty(renewed, "stalled_reason");
|
|
212
|
+
Reflect.deleteProperty(renewed, "stalled_notification_sent_at");
|
|
213
|
+
Reflect.deleteProperty(renewed, "stalled_notification_message_id");
|
|
214
|
+
saveState(statePath, renewed);
|
|
215
|
+
appendEvent(logPath, {
|
|
216
|
+
ts: now,
|
|
217
|
+
conversation_id: current.conversation_id,
|
|
218
|
+
event: "terminal_bridge_renewed",
|
|
219
|
+
previous_status: current.status,
|
|
220
|
+
terminal_control: currentControl,
|
|
221
|
+
agent_timeout_minutes: inactivityTimeoutMinutes,
|
|
222
|
+
agent_hard_timeout_minutes: hardTimeoutMinutes,
|
|
223
|
+
last_activity_at: now
|
|
224
|
+
});
|
|
225
|
+
runtimeLog("info", "terminal_bridge_renewed", {
|
|
226
|
+
conversation_id: current.conversation_id,
|
|
227
|
+
terminal_target: currentControl.target,
|
|
228
|
+
agent_timeout_minutes: inactivityTimeoutMinutes,
|
|
229
|
+
agent_hard_timeout_minutes: hardTimeoutMinutes
|
|
230
|
+
});
|
|
165
231
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
inactivityTimeoutMinutes = positiveMinutes(options.minutes ??
|
|
169
|
-
options.agentTimeoutMinutes ??
|
|
170
|
-
currentTakeover.terminal_bridge_inactivity_timeout_minutes ??
|
|
171
|
-
terminalMaintenanceRuntime().dependencies.runtime
|
|
172
|
-
.defaultAgentTimeoutMinutes, "--minutes");
|
|
173
|
-
hardTimeoutMinutes = positiveMinutes(currentTakeover.terminal_bridge_hard_timeout_minutes ??
|
|
174
|
-
terminalMaintenanceRuntime().dependencies.runtime
|
|
175
|
-
.defaultAgentHardTimeoutMinutes, "--agent-hard-timeout-minutes");
|
|
176
|
-
const startedAt = stringValue(currentTakeover.terminal_bridge_started_at);
|
|
177
|
-
const startedAtMs = startedAt ? Date.parse(startedAt) : NaN;
|
|
178
|
-
if (Number.isFinite(startedAtMs) &&
|
|
179
|
-
cliNowMs() - startedAtMs >= hardTimeoutMinutes * 60 * 1000) {
|
|
180
|
-
throw new Error(`cannot renew ${current.conversation_id}; terminal bridge hard lifetime of ${hardTimeoutMinutes} minutes has elapsed`);
|
|
232
|
+
finally {
|
|
233
|
+
releaseStateLock();
|
|
181
234
|
}
|
|
182
|
-
|
|
183
|
-
const hardDeadline = deadlineAt(startedAt ?? now, hardTimeoutMinutes) ??
|
|
184
|
-
new Date(cliNowMs() + hardTimeoutMinutes * 60 * 1000).toISOString();
|
|
185
|
-
const inactivityDeadline = deadlineAt(now, inactivityTimeoutMinutes) ??
|
|
186
|
-
new Date(cliNowMs() + inactivityTimeoutMinutes * 60 * 1000).toISOString();
|
|
187
|
-
renewed = {
|
|
188
|
-
...current,
|
|
189
|
-
status: "waiting_for_agent",
|
|
190
|
-
native_session_takeover: {
|
|
191
|
-
...currentTakeover,
|
|
192
|
-
terminal_bridge_monitor_lock_version: terminalMaintenanceRuntime().dependencies.runtime.monitorLockVersion,
|
|
193
|
-
terminal_bridge_monitor_started_at: now,
|
|
194
|
-
terminal_bridge_last_activity_at: now,
|
|
195
|
-
terminal_bridge_inactivity_timeout_minutes: inactivityTimeoutMinutes,
|
|
196
|
-
terminal_bridge_hard_timeout_minutes: hardTimeoutMinutes,
|
|
197
|
-
terminal_bridge_inactivity_deadline_at: inactivityDeadline,
|
|
198
|
-
terminal_bridge_hard_deadline_at: hardDeadline,
|
|
199
|
-
terminal_bridge_renewed_at: now
|
|
200
|
-
},
|
|
201
|
-
updated_at: now
|
|
202
|
-
};
|
|
203
|
-
Reflect.deleteProperty(renewed, "stalled_at");
|
|
204
|
-
Reflect.deleteProperty(renewed, "stalled_reason");
|
|
205
|
-
Reflect.deleteProperty(renewed, "stalled_notification_sent_at");
|
|
206
|
-
Reflect.deleteProperty(renewed, "stalled_notification_message_id");
|
|
207
|
-
saveState(statePath, renewed);
|
|
208
|
-
appendEvent(logPath, {
|
|
209
|
-
ts: now,
|
|
210
|
-
conversation_id: current.conversation_id,
|
|
211
|
-
event: "terminal_bridge_renewed",
|
|
212
|
-
previous_status: current.status,
|
|
213
|
-
terminal_control: currentControl,
|
|
214
|
-
agent_timeout_minutes: inactivityTimeoutMinutes,
|
|
215
|
-
agent_hard_timeout_minutes: hardTimeoutMinutes,
|
|
216
|
-
last_activity_at: now
|
|
217
|
-
});
|
|
218
|
-
runtimeLog("info", "terminal_bridge_renewed", {
|
|
219
|
-
conversation_id: current.conversation_id,
|
|
220
|
-
terminal_target: currentControl.target,
|
|
221
|
-
agent_timeout_minutes: inactivityTimeoutMinutes,
|
|
222
|
-
agent_hard_timeout_minutes: hardTimeoutMinutes
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
finally {
|
|
226
|
-
releaseStateLock();
|
|
227
|
-
}
|
|
235
|
+
});
|
|
228
236
|
const monitor = startTerminalBridgeMonitorForConversation({
|
|
229
237
|
conversation: renewed,
|
|
230
238
|
statePath,
|
|
@@ -336,90 +344,89 @@ async function runTerminalConversationCancel({ options, conversationId, agent, t
|
|
|
336
344
|
}
|
|
337
345
|
async function runTerminalControlCancel({ options, statePath, logPath, agent, terminalControl }) {
|
|
338
346
|
const releaseTerminalLock = acquireTerminalBridgeSendLock(storeDirFromOptions(options), terminalControl, { timeoutMs: 30000 });
|
|
339
|
-
let releaseStateLock;
|
|
340
347
|
try {
|
|
341
|
-
releaseStateLock = acquireFileLock(`${statePath}.lock`);
|
|
342
348
|
const writerStoreDir = pathsForConversationDir(path.dirname(statePath)).storeDir;
|
|
343
349
|
return await withStoreWriterLease(writerStoreDir, async () => {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
action: "cancel"
|
|
361
|
-
});
|
|
362
|
-
const cancellation = await createTerminalAgentBridge(options).cancel(agent, currentControl, {
|
|
363
|
-
runtime: terminalRuntimeIdentityForConversation(currentConversation, currentControl),
|
|
364
|
-
scrollbackLines: Number(options.scrollbackLines ?? 120)
|
|
365
|
-
});
|
|
366
|
-
if (!cancellation.cancelRequested) {
|
|
367
|
-
printJson({
|
|
350
|
+
const releaseStateLock = acquireFileLock(`${statePath}.lock`);
|
|
351
|
+
try {
|
|
352
|
+
const currentConversation = loadState(statePath);
|
|
353
|
+
if (!["waiting_for_agent", "waiting_for_openclaw"].includes(currentConversation.status)) {
|
|
354
|
+
throw new Error(`cannot cancel ${currentConversation.conversation_id}; conversation is ${currentConversation.status}`);
|
|
355
|
+
}
|
|
356
|
+
const currentTakeover = isRecord(currentConversation.native_session_takeover)
|
|
357
|
+
? currentConversation.native_session_takeover
|
|
358
|
+
: undefined;
|
|
359
|
+
const currentControl = terminalControlFromTakeover(currentTakeover);
|
|
360
|
+
if (!currentControl ||
|
|
361
|
+
!terminalControlsShareIncarnation(currentControl, terminalControl)) {
|
|
362
|
+
throw new Error("terminal control changed while waiting to cancel; refresh status and retry");
|
|
363
|
+
}
|
|
364
|
+
assertManagedTerminalDispatchOwner({
|
|
365
|
+
storeDir: writerStoreDir,
|
|
368
366
|
conversation: currentConversation,
|
|
369
|
-
|
|
370
|
-
|
|
367
|
+
terminalControl: currentControl,
|
|
368
|
+
action: "cancel"
|
|
369
|
+
});
|
|
370
|
+
const cancellation = await createTerminalAgentBridge(options).cancel(agent, currentControl, {
|
|
371
|
+
runtime: terminalRuntimeIdentityForConversation(currentConversation, currentControl),
|
|
372
|
+
scrollbackLines: Number(options.scrollbackLines ?? 120)
|
|
373
|
+
});
|
|
374
|
+
if (!cancellation.cancelRequested) {
|
|
375
|
+
printJson({
|
|
376
|
+
conversation: currentConversation,
|
|
377
|
+
cancel_requested: false,
|
|
378
|
+
reason: cancellation.reason,
|
|
379
|
+
terminal_control: currentControl,
|
|
380
|
+
budget: budgetAction(currentConversation)
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const now = cliNow().toISOString();
|
|
385
|
+
appendEvent(logPath, {
|
|
386
|
+
ts: now,
|
|
387
|
+
conversation_id: currentConversation.conversation_id,
|
|
388
|
+
event: "terminal_cancel_requested",
|
|
371
389
|
terminal_control: currentControl,
|
|
372
|
-
|
|
390
|
+
key: cancellation.key,
|
|
391
|
+
keys: cancellation.keys,
|
|
392
|
+
denied_approval: cancellation.deniedApproval,
|
|
393
|
+
request_id: cancellation.requestId
|
|
394
|
+
});
|
|
395
|
+
runtimeLog("info", "terminal_cancel_requested", {
|
|
396
|
+
conversation_id: currentConversation.conversation_id,
|
|
397
|
+
agent,
|
|
398
|
+
terminal_target: currentControl.target,
|
|
399
|
+
key: cancellation.key,
|
|
400
|
+
keys: cancellation.keys,
|
|
401
|
+
denied_approval: cancellation.deniedApproval,
|
|
402
|
+
request_id: cancellation.requestId
|
|
403
|
+
});
|
|
404
|
+
const nextConversation = {
|
|
405
|
+
...currentConversation,
|
|
406
|
+
status: "cancelled",
|
|
407
|
+
cancelled_at: now,
|
|
408
|
+
terminal_cancel_requested_at: now,
|
|
409
|
+
updated_at: now
|
|
410
|
+
};
|
|
411
|
+
saveState(statePath, nextConversation);
|
|
412
|
+
printJson({
|
|
413
|
+
conversation: nextConversation,
|
|
414
|
+
cancel_requested: true,
|
|
415
|
+
terminal_control: currentControl,
|
|
416
|
+
key: cancellation.key,
|
|
417
|
+
keys: cancellation.keys,
|
|
418
|
+
denied_approval: cancellation.deniedApproval,
|
|
419
|
+
request_id: cancellation.requestId,
|
|
420
|
+
budget: budgetAction(nextConversation)
|
|
373
421
|
});
|
|
374
|
-
return;
|
|
375
422
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
conversation_id: currentConversation.conversation_id,
|
|
380
|
-
event: "terminal_cancel_requested",
|
|
381
|
-
terminal_control: currentControl,
|
|
382
|
-
key: cancellation.key,
|
|
383
|
-
keys: cancellation.keys,
|
|
384
|
-
denied_approval: cancellation.deniedApproval,
|
|
385
|
-
request_id: cancellation.requestId
|
|
386
|
-
});
|
|
387
|
-
runtimeLog("info", "terminal_cancel_requested", {
|
|
388
|
-
conversation_id: currentConversation.conversation_id,
|
|
389
|
-
agent,
|
|
390
|
-
terminal_target: currentControl.target,
|
|
391
|
-
key: cancellation.key,
|
|
392
|
-
keys: cancellation.keys,
|
|
393
|
-
denied_approval: cancellation.deniedApproval,
|
|
394
|
-
request_id: cancellation.requestId
|
|
395
|
-
});
|
|
396
|
-
const nextConversation = {
|
|
397
|
-
...currentConversation,
|
|
398
|
-
status: "cancelled",
|
|
399
|
-
cancelled_at: now,
|
|
400
|
-
terminal_cancel_requested_at: now,
|
|
401
|
-
updated_at: now
|
|
402
|
-
};
|
|
403
|
-
saveState(statePath, nextConversation);
|
|
404
|
-
printJson({
|
|
405
|
-
conversation: nextConversation,
|
|
406
|
-
cancel_requested: true,
|
|
407
|
-
terminal_control: currentControl,
|
|
408
|
-
key: cancellation.key,
|
|
409
|
-
keys: cancellation.keys,
|
|
410
|
-
denied_approval: cancellation.deniedApproval,
|
|
411
|
-
request_id: cancellation.requestId,
|
|
412
|
-
budget: budgetAction(nextConversation)
|
|
413
|
-
});
|
|
423
|
+
finally {
|
|
424
|
+
releaseStateLock();
|
|
425
|
+
}
|
|
414
426
|
});
|
|
415
427
|
}
|
|
416
428
|
finally {
|
|
417
|
-
|
|
418
|
-
releaseStateLock?.();
|
|
419
|
-
}
|
|
420
|
-
finally {
|
|
421
|
-
releaseTerminalLock();
|
|
422
|
-
}
|
|
429
|
+
releaseTerminalLock();
|
|
423
430
|
}
|
|
424
431
|
}
|
|
425
432
|
async function runObservedHandoffClose({ options, statePath, logPath, initialConversation }) {
|
|
@@ -538,7 +545,10 @@ async function runObservedHandoffClose({ options, statePath, logPath, initialCon
|
|
|
538
545
|
}
|
|
539
546
|
const now = cliNow().toISOString();
|
|
540
547
|
const closed = {
|
|
541
|
-
...conversation,
|
|
548
|
+
...supersedeCallbackNotificationDelivery(conversation, {
|
|
549
|
+
at: now,
|
|
550
|
+
reason: "superseded_by_human_context_switch"
|
|
551
|
+
}),
|
|
542
552
|
status: "closed",
|
|
543
553
|
closed_at: now,
|
|
544
554
|
close_reason: "superseded_by_human_context_switch",
|
|
@@ -595,187 +605,99 @@ async function runClose(options) {
|
|
|
595
605
|
return;
|
|
596
606
|
}
|
|
597
607
|
const loaded = loadConversationFromOptions(options);
|
|
598
|
-
if (stringValue(options.expectedHandoffToken)) {
|
|
599
|
-
await runObservedHandoffClose({
|
|
600
|
-
options,
|
|
601
|
-
statePath: loaded.statePath,
|
|
602
|
-
logPath: loaded.logPath,
|
|
603
|
-
initialConversation: loaded.conversation
|
|
604
|
-
});
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
if (stringValue(options.reason) === "superseded_by_human_context_switch") {
|
|
608
|
-
throw new Error("reason superseded_by_human_context_switch requires the fresh " +
|
|
609
|
-
"expected_handoff_token advertised by AKK list");
|
|
610
|
-
}
|
|
611
608
|
const { statePath, logPath } = loaded;
|
|
612
609
|
const closeStoreDir = pathsForConversationDir(path.dirname(statePath)).storeDir;
|
|
613
|
-
const nativeTakeover = isRecord(loaded.conversation.native_session_takeover)
|
|
614
|
-
? loaded.conversation.native_session_takeover
|
|
615
|
-
: undefined;
|
|
616
|
-
const terminalControl = terminalControlFromTakeover(nativeTakeover);
|
|
617
|
-
const releaseTerminalLock = terminalControl
|
|
618
|
-
? acquireTerminalBridgeSendLock(closeStoreDir, terminalControl, { timeoutMs: 30000 })
|
|
619
|
-
: () => { };
|
|
620
610
|
const closeWithFreshState = async () => {
|
|
621
611
|
const releaseStateLock = acquireFileLock(`${statePath}.lock`);
|
|
622
612
|
try {
|
|
623
|
-
const conversation =
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
613
|
+
const conversation = {
|
|
614
|
+
...loadState(statePath),
|
|
615
|
+
store_dir: path.resolve(closeStoreDir),
|
|
616
|
+
conversation_dir: path.resolve(path.dirname(statePath)),
|
|
617
|
+
state_path: path.resolve(statePath),
|
|
618
|
+
event_log_path: path.resolve(logPath)
|
|
619
|
+
};
|
|
630
620
|
const currentTakeover = isRecord(conversation.native_session_takeover)
|
|
631
621
|
? conversation.native_session_takeover
|
|
632
622
|
: undefined;
|
|
633
623
|
const currentTerminalControl = terminalControlFromTakeover(currentTakeover);
|
|
634
|
-
if (terminalControl &&
|
|
635
|
-
!terminalControlsShareIncarnation(currentTerminalControl, terminalControl)) {
|
|
636
|
-
throw new Error("terminal control changed after the close action was listed; refresh AKK list");
|
|
637
|
-
}
|
|
638
|
-
if (conversation.status === "closed" && currentTerminalControl) {
|
|
639
|
-
const recoveredAuthority = exactVerifiedDeadTerminalAgentProcessAuthority({
|
|
640
|
-
conversation,
|
|
641
|
-
storeDir: closeStoreDir,
|
|
642
|
-
terminalControl: currentTerminalControl,
|
|
643
|
-
logPath
|
|
644
|
-
});
|
|
645
|
-
const recoveredDecision = decideVerifiedDeadAgentProcess({
|
|
646
|
-
persistedAuthority: recoveredAuthority
|
|
647
|
-
});
|
|
648
|
-
if (recoveredDecision.status === "invalid") {
|
|
649
|
-
throw new Error(`cannot finish close recovery for ${conversation.conversation_id}; ` +
|
|
650
|
-
recoveredDecision.reason);
|
|
651
|
-
}
|
|
652
|
-
if (recoveredDecision.status === "verified_dead") {
|
|
653
|
-
const audit = ensureVerifiedDeadTerminalAgentProcessEvent({
|
|
654
|
-
logPath,
|
|
655
|
-
proof: recoveredDecision.proof,
|
|
656
|
-
action: "managed_close"
|
|
657
|
-
});
|
|
658
|
-
const expectedMessageId = required(stringValue(currentTakeover?.terminal_bridge_message_id), "verified-dead close recovery has no terminal message id");
|
|
659
|
-
const dispatchLedgerResolved = resolveVerifiedDeadTerminalBridgeDispatchLedger({
|
|
660
|
-
terminalControl: currentTerminalControl,
|
|
661
|
-
conversation,
|
|
662
|
-
storeDir: closeStoreDir,
|
|
663
|
-
statePath,
|
|
664
|
-
logPath,
|
|
665
|
-
expectedMessageId,
|
|
666
|
-
reason: "conversation explicitly closed by request"
|
|
667
|
-
});
|
|
668
|
-
ensureVerifiedDeadConversationClosedEvent({
|
|
669
|
-
logPath,
|
|
670
|
-
conversation,
|
|
671
|
-
evidenceId: audit.evidenceId
|
|
672
|
-
});
|
|
673
|
-
printJson({
|
|
674
|
-
conversation,
|
|
675
|
-
closed: true,
|
|
676
|
-
recovered: true,
|
|
677
|
-
terminal_dispatch_resolved: dispatchLedgerResolved
|
|
678
|
-
});
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
if (terminalControl && currentTerminalControl) {
|
|
683
|
-
verifiedDeadProcess =
|
|
684
|
-
await assertGenericCloseDoesNotBypassObservedHandoff({
|
|
685
|
-
options,
|
|
686
|
-
storeDir: closeStoreDir,
|
|
687
|
-
conversation,
|
|
688
|
-
terminalControl: currentTerminalControl
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
624
|
const now = cliNow().toISOString();
|
|
692
|
-
const
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
logPath,
|
|
696
|
-
proof: verifiedDeadProcess,
|
|
697
|
-
action: "managed_close"
|
|
698
|
-
})
|
|
699
|
-
: undefined;
|
|
700
|
-
const verifiedDeadMessageId = verifiedDeadAudit
|
|
701
|
-
? required(stringValue(currentTakeover?.terminal_bridge_message_id), "verified-dead close has no terminal message id")
|
|
702
|
-
: undefined;
|
|
703
|
-
if (verifiedDeadAudit && currentTerminalControl) {
|
|
704
|
-
assertVerifiedDeadTerminalBridgeDispatchAuthority({
|
|
705
|
-
terminalControl: currentTerminalControl,
|
|
706
|
-
conversation,
|
|
707
|
-
storeDir: closeStoreDir,
|
|
708
|
-
statePath,
|
|
709
|
-
logPath,
|
|
710
|
-
expectedMessageId: verifiedDeadMessageId
|
|
711
|
-
});
|
|
712
|
-
}
|
|
625
|
+
const closedAt = stringValue(conversation.closed_at) ?? now;
|
|
626
|
+
const closeReason = stringValue(options.reason) ??
|
|
627
|
+
stringValue(conversation.close_reason) ?? "closed by request";
|
|
713
628
|
const closed = {
|
|
714
|
-
...conversation,
|
|
629
|
+
...supersedeUnacceptedCallbackDeliveries(conversation, {
|
|
630
|
+
at: now,
|
|
631
|
+
reason: "superseded_by_conversation_close"
|
|
632
|
+
}),
|
|
715
633
|
status: "closed",
|
|
716
|
-
closed_at:
|
|
634
|
+
closed_at: closedAt,
|
|
717
635
|
close_reason: closeReason,
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
terminal_agent_process_disposition: {
|
|
721
|
-
status: "verified_dead",
|
|
722
|
-
proof: verifiedDeadAudit.proof,
|
|
723
|
-
evidence_id: verifiedDeadAudit.evidenceId,
|
|
724
|
-
recorded_at: verifiedDeadAudit.recordedAt
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
: {}),
|
|
636
|
+
disposition: "user_abandoned_management",
|
|
637
|
+
callback_expected: false,
|
|
728
638
|
updated_at: now
|
|
729
639
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
640
|
+
delete closed.idle_since;
|
|
641
|
+
saveExplicitUserCloseState(statePath, closed);
|
|
642
|
+
let transferCleanup;
|
|
643
|
+
try {
|
|
644
|
+
transferCleanup = cleanupDeferredForegroundUserClose({
|
|
645
|
+
storeDir: closeStoreDir,
|
|
646
|
+
conversation: closed,
|
|
647
|
+
at: now
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
transferCleanup = {
|
|
652
|
+
transfer_finalized: false,
|
|
653
|
+
detached_session_ids: [],
|
|
654
|
+
warnings: [
|
|
655
|
+
`deferred_transfer_cleanup: ${error instanceof Error ? error.message : String(error)}`
|
|
656
|
+
]
|
|
657
|
+
};
|
|
734
658
|
}
|
|
735
659
|
let dispatchLedgerResolved = false;
|
|
736
660
|
let dispatchLedgerWarning;
|
|
737
|
-
|
|
661
|
+
const ledgerTerminalControl = currentTerminalControl;
|
|
662
|
+
if (ledgerTerminalControl) {
|
|
738
663
|
try {
|
|
739
|
-
dispatchLedgerResolved =
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
statePath,
|
|
745
|
-
logPath,
|
|
746
|
-
expectedMessageId: verifiedDeadMessageId,
|
|
747
|
-
reason: "conversation explicitly closed by request"
|
|
748
|
-
})
|
|
749
|
-
: resolveTerminalBridgeDispatchLedger(currentTerminalControl, {
|
|
750
|
-
conversation: closed,
|
|
751
|
-
expectedMessageId: stringValue(currentTakeover?.terminal_bridge_message_id),
|
|
752
|
-
reason: "conversation explicitly closed by request"
|
|
753
|
-
});
|
|
664
|
+
dispatchLedgerResolved = resolveTerminalBridgeDispatchLedger(ledgerTerminalControl, {
|
|
665
|
+
conversation: closed,
|
|
666
|
+
expectedMessageId: stringValue(currentTakeover?.terminal_bridge_message_id),
|
|
667
|
+
reason: "conversation explicitly closed by request"
|
|
668
|
+
});
|
|
754
669
|
}
|
|
755
670
|
catch (error) {
|
|
756
671
|
dispatchLedgerWarning =
|
|
757
672
|
error instanceof Error ? error.message : String(error);
|
|
758
673
|
runtimeLog("error", "terminal_dispatch_ledger_resolve_failed", {
|
|
759
674
|
conversation_id: closed.conversation_id,
|
|
760
|
-
terminal_target:
|
|
675
|
+
terminal_target: ledgerTerminalControl.target,
|
|
761
676
|
error: dispatchLedgerWarning
|
|
762
677
|
});
|
|
763
678
|
}
|
|
764
679
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
evidenceId: verifiedDeadAudit.evidenceId
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
else {
|
|
773
|
-
appendEvent(logPath, {
|
|
774
|
-
ts: now,
|
|
680
|
+
let closeEventWarning;
|
|
681
|
+
try {
|
|
682
|
+
appendExplicitUserCloseEvent(logPath, {
|
|
683
|
+
ts: closedAt,
|
|
775
684
|
conversation_id: conversation.conversation_id,
|
|
776
685
|
event: "conversation_closed",
|
|
777
686
|
status: "closed",
|
|
778
|
-
reason: closed.close_reason
|
|
687
|
+
reason: closed.close_reason,
|
|
688
|
+
disposition: "user_abandoned_management",
|
|
689
|
+
terminal_input_sent: false,
|
|
690
|
+
coding_agent_stopped: false,
|
|
691
|
+
tmux_pane_closed: false
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
closeEventWarning = error instanceof Error
|
|
696
|
+
? error.message
|
|
697
|
+
: String(error);
|
|
698
|
+
runtimeLog("warn", "conversation_close_event_append_failed", {
|
|
699
|
+
conversation_id: closed.conversation_id,
|
|
700
|
+
error: closeEventWarning
|
|
779
701
|
});
|
|
780
702
|
}
|
|
781
703
|
runtimeLog("info", "conversation_closed", {
|
|
@@ -788,11 +710,26 @@ async function runClose(options) {
|
|
|
788
710
|
printJson({
|
|
789
711
|
conversation: closed,
|
|
790
712
|
closed: true,
|
|
713
|
+
management_released: true,
|
|
714
|
+
terminal_input_sent: false,
|
|
715
|
+
coding_agent_stopped: false,
|
|
716
|
+
tmux_pane_closed: false,
|
|
717
|
+
next_action: "refresh list; if the coding agent is still working, use the fresh Watch action",
|
|
718
|
+
deferred_transfer: transferCleanup,
|
|
791
719
|
terminal_dispatch_resolved: dispatchLedgerResolved,
|
|
792
|
-
...(dispatchLedgerWarning
|
|
720
|
+
...(dispatchLedgerWarning ||
|
|
721
|
+
closeEventWarning ||
|
|
722
|
+
transferCleanup.warnings.length > 0
|
|
793
723
|
? {
|
|
794
|
-
|
|
795
|
-
|
|
724
|
+
warnings: [
|
|
725
|
+
...transferCleanup.warnings,
|
|
726
|
+
...(dispatchLedgerWarning
|
|
727
|
+
? [`terminal_dispatch: ${dispatchLedgerWarning}`]
|
|
728
|
+
: []),
|
|
729
|
+
...(closeEventWarning
|
|
730
|
+
? [`close_event: ${closeEventWarning}`]
|
|
731
|
+
: [])
|
|
732
|
+
].map((warning) => textSummary(warning))
|
|
796
733
|
}
|
|
797
734
|
: {})
|
|
798
735
|
});
|
|
@@ -801,17 +738,7 @@ async function runClose(options) {
|
|
|
801
738
|
releaseStateLock();
|
|
802
739
|
}
|
|
803
740
|
};
|
|
804
|
-
|
|
805
|
-
if (terminalControl) {
|
|
806
|
-
await withStoreWriterLeaseAsync(closeStoreDir, closeWithFreshState);
|
|
807
|
-
}
|
|
808
|
-
else {
|
|
809
|
-
await closeWithFreshState();
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
finally {
|
|
813
|
-
releaseTerminalLock();
|
|
814
|
-
}
|
|
741
|
+
await withStoreWriterLeaseAsync(closeStoreDir, closeWithFreshState);
|
|
815
742
|
}
|
|
816
743
|
async function assertGenericCloseDoesNotBypassObservedHandoff({ options, storeDir, conversation, terminalControl }) {
|
|
817
744
|
if (!isSessionSendBlockingStatus(conversation.status)) {
|