@scotthuang/agent-knock-knock 0.13.3 → 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.
- package/CHANGELOG.md +14 -0
- package/README.md +13 -5
- package/dist/src/callback-outbox-policy.js +27 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +4 -30
- package/dist/src/callback-outbox-service.js +21 -154
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.js +13 -8
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-service.d.ts +46 -0
- package/dist/src/callback-route-service.js +155 -0
- package/dist/src/callback-route-service.js.map +1 -0
- package/dist/src/codex-process-incarnation.d.ts +8 -0
- package/dist/src/codex-process-incarnation.js +36 -0
- package/dist/src/codex-process-incarnation.js.map +1 -0
- package/dist/src/deferred-foreground-conflict-policy.d.ts +22 -0
- package/dist/src/deferred-foreground-conflict-policy.js +52 -0
- package/dist/src/deferred-foreground-conflict-policy.js.map +1 -0
- package/dist/src/durable-notification-kernel.d.ts +94 -0
- package/dist/src/durable-notification-kernel.js +68 -0
- package/dist/src/durable-notification-kernel.js.map +1 -0
- package/dist/src/herdr-socket-transport.d.ts +43 -0
- package/dist/src/herdr-socket-transport.js +170 -0
- package/dist/src/herdr-socket-transport.js.map +1 -0
- package/dist/src/herdr-terminal-control-provider.d.ts +2 -45
- package/dist/src/herdr-terminal-control-provider.js +2 -172
- package/dist/src/herdr-terminal-control-provider.js.map +1 -1
- package/dist/src/host-adapter-capabilities.d.ts +35 -0
- package/dist/src/host-adapter-capabilities.js +146 -0
- package/dist/src/host-adapter-capabilities.js.map +1 -0
- package/dist/src/host-adapter.d.ts +9 -3
- package/dist/src/host-adapter.js +33 -9
- package/dist/src/host-adapter.js.map +1 -1
- package/dist/src/host-bridge-tools.d.ts +3 -4
- package/dist/src/host-bridge-tools.js +49 -92
- package/dist/src/host-bridge-tools.js.map +1 -1
- package/dist/src/host-bridge.js +5 -5
- package/dist/src/host-bridge.js.map +1 -1
- package/dist/src/host-monitor-reconciliation.d.ts +6 -0
- package/dist/src/host-monitor-reconciliation.js +96 -0
- package/dist/src/host-monitor-reconciliation.js.map +1 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +116 -60
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-list-projection.d.ts +2 -0
- package/dist/src/openclaw-list-projection.js +3 -0
- package/dist/src/openclaw-list-projection.js.map +1 -0
- package/dist/src/openclaw-model-facing-field-policy.d.ts +2 -0
- package/dist/src/openclaw-model-facing-field-policy.js +3 -0
- package/dist/src/openclaw-model-facing-field-policy.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.d.ts +6 -29
- package/dist/src/openclaw-plugin-command-adapter.js +7 -2660
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +2 -148
- package/dist/src/openclaw-plugin-helpers.js +2 -1844
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +2 -563
- package/dist/src/openclaw-plugin-schemas.js +2 -572
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-plugin-supervisor.d.ts +4 -5
- package/dist/src/openclaw-plugin-supervisor.js +5 -94
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
- package/dist/src/openclaw-plugin-value-helpers.d.ts +2 -0
- package/dist/src/openclaw-plugin-value-helpers.js +3 -0
- package/dist/src/openclaw-plugin-value-helpers.js.map +1 -0
- package/dist/src/openclaw-private-authority-offers.d.ts +4 -39
- package/dist/src/openclaw-private-authority-offers.js +3 -258
- package/dist/src/openclaw-private-authority-offers.js.map +1 -1
- package/dist/src/semantic-private-authority-offers.d.ts +41 -0
- package/dist/src/semantic-private-authority-offers.js +261 -0
- package/dist/src/semantic-private-authority-offers.js.map +1 -0
- package/dist/src/semantic-tool-arguments.d.ts +9 -0
- package/dist/src/semantic-tool-arguments.js +64 -0
- package/dist/src/semantic-tool-arguments.js.map +1 -0
- package/dist/src/semantic-tool-catalog.d.ts +48 -0
- package/dist/src/semantic-tool-catalog.js +105 -0
- package/dist/src/semantic-tool-catalog.js.map +1 -0
- package/dist/src/semantic-tool-command-helpers.d.ts +145 -0
- package/dist/src/semantic-tool-command-helpers.js +1272 -0
- package/dist/src/semantic-tool-command-helpers.js.map +1 -0
- package/dist/src/semantic-tool-list-projection.d.ts +10 -0
- package/dist/src/semantic-tool-list-projection.js +497 -0
- package/dist/src/semantic-tool-list-projection.js.map +1 -0
- package/dist/src/semantic-tool-model-facing-field-policy.d.ts +3 -0
- package/dist/src/semantic-tool-model-facing-field-policy.js +66 -0
- package/dist/src/semantic-tool-model-facing-field-policy.js.map +1 -0
- package/dist/src/semantic-tool-presentation.d.ts +37 -0
- package/dist/src/semantic-tool-presentation.js +680 -0
- package/dist/src/semantic-tool-presentation.js.map +1 -0
- package/dist/src/semantic-tool-private-authority.d.ts +39 -0
- package/dist/src/semantic-tool-private-authority.js +602 -0
- package/dist/src/semantic-tool-private-authority.js.map +1 -0
- package/dist/src/semantic-tool-relay.d.ts +16 -0
- package/dist/src/semantic-tool-relay.js +175 -0
- package/dist/src/semantic-tool-relay.js.map +1 -0
- package/dist/src/semantic-tool-runtime.d.ts +10 -0
- package/dist/src/semantic-tool-runtime.js +1157 -0
- package/dist/src/semantic-tool-runtime.js.map +1 -0
- package/dist/src/semantic-tool-schemas.d.ts +563 -0
- package/dist/src/semantic-tool-schemas.js +573 -0
- package/dist/src/semantic-tool-schemas.js.map +1 -0
- package/dist/src/semantic-tool-value-helpers.d.ts +5 -0
- package/dist/src/semantic-tool-value-helpers.js +27 -0
- package/dist/src/semantic-tool-value-helpers.js.map +1 -0
- package/dist/src/store-event-presentation.d.ts +17 -0
- package/dist/src/store-event-presentation.js +33 -0
- package/dist/src/store-event-presentation.js.map +1 -0
- package/dist/src/store.d.ts +3 -16
- package/dist/src/store.js +2 -30
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +2 -98
- package/dist/src/terminal-action-projection.js +13 -215
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +14 -340
- package/dist/src/terminal-agent-bridge.js +155 -3226
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-approval-cli-adapter.d.ts +17 -0
- package/dist/src/terminal-approval-cli-adapter.js +921 -0
- package/dist/src/terminal-approval-cli-adapter.js.map +1 -0
- package/dist/src/terminal-command-cli-adapter.d.ts +2 -429
- package/dist/src/terminal-command-cli-adapter.js +344 -6061
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-command-cli-ports.d.ts +443 -0
- package/dist/src/terminal-command-cli-ports.js +2 -0
- package/dist/src/terminal-command-cli-ports.js.map +1 -0
- package/dist/src/terminal-command-dispatch-preparation.d.ts +81 -0
- package/dist/src/terminal-command-dispatch-preparation.js +209 -0
- package/dist/src/terminal-command-dispatch-preparation.js.map +1 -0
- package/dist/src/terminal-command-dispatch-transport.d.ts +35 -0
- package/dist/src/terminal-command-dispatch-transport.js +859 -0
- package/dist/src/terminal-command-dispatch-transport.js.map +1 -0
- package/dist/src/terminal-command-foreground-proof.d.ts +32 -0
- package/dist/src/terminal-command-foreground-proof.js +72 -0
- package/dist/src/terminal-command-foreground-proof.js.map +1 -0
- package/dist/src/terminal-command-send-preflight.d.ts +41 -0
- package/dist/src/terminal-command-send-preflight.js +86 -0
- package/dist/src/terminal-command-send-preflight.js.map +1 -0
- package/dist/src/terminal-composer-classifier.d.ts +55 -0
- package/dist/src/terminal-composer-classifier.js +638 -0
- package/dist/src/terminal-composer-classifier.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-authority.d.ts +56 -0
- package/dist/src/terminal-dispatch-recovery-authority.js +251 -0
- package/dist/src/terminal-dispatch-recovery-authority.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +1 -9
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +5 -240
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-handoff-cli-adapter.d.ts +1 -11
- package/dist/src/terminal-handoff-cli-adapter.js +2 -39
- package/dist/src/terminal-handoff-cli-adapter.js.map +1 -1
- package/dist/src/terminal-human-explicit-send-cli-adapter.d.ts +25 -0
- package/dist/src/terminal-human-explicit-send-cli-adapter.js +903 -0
- package/dist/src/terminal-human-explicit-send-cli-adapter.js.map +1 -0
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +1 -6
- package/dist/src/terminal-identity-authority-cli-adapter.js +3 -33
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-cli-adapter.js +209 -210
- package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-core.d.ts +0 -2
- package/dist/src/terminal-interaction-core.js +4 -3
- package/dist/src/terminal-interaction-core.js.map +1 -1
- package/dist/src/terminal-interaction-protocol.d.ts +21 -0
- package/dist/src/terminal-interaction-protocol.js +74 -0
- package/dist/src/terminal-interaction-protocol.js.map +1 -1
- package/dist/src/terminal-interaction-response-bridge.d.ts +126 -0
- package/dist/src/terminal-interaction-response-bridge.js +579 -0
- package/dist/src/terminal-interaction-response-bridge.js.map +1 -0
- package/dist/src/terminal-interaction-response-transaction.d.ts +44 -0
- package/dist/src/terminal-interaction-response-transaction.js +56 -0
- package/dist/src/terminal-interaction-response-transaction.js.map +1 -0
- package/dist/src/terminal-list-action-policy.d.ts +43 -0
- package/dist/src/terminal-list-action-policy.js +164 -0
- package/dist/src/terminal-list-action-policy.js.map +1 -0
- package/dist/src/terminal-list-cli-adapter.d.ts +6 -36
- package/dist/src/terminal-list-cli-adapter.js +241 -1946
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-facts.d.ts +129 -0
- package/dist/src/terminal-list-facts.js +179 -0
- package/dist/src/terminal-list-facts.js.map +1 -0
- package/dist/src/terminal-list-inventory.d.ts +59 -0
- package/dist/src/terminal-list-inventory.js +194 -0
- package/dist/src/terminal-list-inventory.js.map +1 -0
- package/dist/src/terminal-list-ownership-service.d.ts +118 -0
- package/dist/src/terminal-list-ownership-service.js +1003 -0
- package/dist/src/terminal-list-ownership-service.js.map +1 -0
- package/dist/src/terminal-list-projection-service.d.ts +49 -0
- package/dist/src/terminal-list-projection-service.js +407 -0
- package/dist/src/terminal-list-projection-service.js.map +1 -0
- package/dist/src/terminal-list-renderer.d.ts +11 -11
- package/dist/src/terminal-list-renderer.js +168 -119
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-managed-send-cli-adapter.d.ts +33 -0
- package/dist/src/terminal-managed-send-cli-adapter.js +994 -0
- package/dist/src/terminal-managed-send-cli-adapter.js.map +1 -0
- package/dist/src/terminal-managed-turn-list-action-policy.d.ts +47 -0
- package/dist/src/terminal-managed-turn-list-action-policy.js +64 -0
- package/dist/src/terminal-managed-turn-list-action-policy.js.map +1 -0
- package/dist/src/terminal-model-control-action-policy.d.ts +60 -0
- package/dist/src/terminal-model-control-action-policy.js +96 -0
- package/dist/src/terminal-model-control-action-policy.js.map +1 -0
- package/dist/src/terminal-model-control-availability.d.ts +68 -0
- package/dist/src/terminal-model-control-availability.js +157 -0
- package/dist/src/terminal-model-control-availability.js.map +1 -0
- package/dist/src/terminal-model-control-bridge.d.ts +48 -0
- package/dist/src/terminal-model-control-bridge.js +259 -0
- package/dist/src/terminal-model-control-bridge.js.map +1 -0
- package/dist/src/terminal-model-control-profile.d.ts +55 -0
- package/dist/src/terminal-model-control-profile.js +137 -0
- package/dist/src/terminal-model-control-profile.js.map +1 -0
- package/dist/src/terminal-model-control-subject.d.ts +34 -0
- package/dist/src/terminal-model-control-subject.js +58 -0
- package/dist/src/terminal-model-control-subject.js.map +1 -0
- package/dist/src/terminal-model-control-transaction.d.ts +19 -0
- package/dist/src/terminal-model-control-transaction.js +39 -0
- package/dist/src/terminal-model-control-transaction.js.map +1 -0
- package/dist/src/terminal-model-control.d.ts +37 -18
- package/dist/src/terminal-model-control.js +285 -194
- package/dist/src/terminal-model-control.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.js +50 -17
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-native-inspection-bridge.d.ts +211 -0
- package/dist/src/terminal-native-inspection-bridge.js +1127 -0
- package/dist/src/terminal-native-inspection-bridge.js.map +1 -0
- package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -2
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-retry-application.d.ts +24 -0
- package/dist/src/terminal-submission-retry-application.js +402 -0
- package/dist/src/terminal-submission-retry-application.js.map +1 -0
- package/dist/src/terminal-submission-retry-ports.d.ts +30 -0
- package/dist/src/terminal-submission-retry-ports.js +2 -0
- package/dist/src/terminal-submission-retry-ports.js.map +1 -0
- package/dist/src/terminal-submission-retry-reconciliation.d.ts +236 -0
- package/dist/src/terminal-submission-retry-reconciliation.js +1130 -0
- package/dist/src/terminal-submission-retry-reconciliation.js.map +1 -0
- package/dist/src/terminal-submission-retry-transport.d.ts +58 -0
- package/dist/src/terminal-submission-retry-transport.js +675 -0
- package/dist/src/terminal-submission-retry-transport.js.map +1 -0
- package/dist/src/terminal-text-submission-bridge.d.ts +200 -0
- package/dist/src/terminal-text-submission-bridge.js +632 -0
- package/dist/src/terminal-text-submission-bridge.js.map +1 -0
- package/dist/src/terminal-user-explicit-send-policy.d.ts +29 -0
- package/dist/src/terminal-user-explicit-send-policy.js +46 -0
- package/dist/src/terminal-user-explicit-send-policy.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.js +91 -143
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-codec.d.ts +9 -0
- package/dist/src/terminal-watch-codec.js +1051 -0
- package/dist/src/terminal-watch-codec.js.map +1 -0
- package/dist/src/terminal-watch-interaction-response-store.d.ts +19 -0
- package/dist/src/terminal-watch-interaction-response-store.js +87 -0
- package/dist/src/terminal-watch-interaction-response-store.js.map +1 -0
- package/dist/src/terminal-watch-record.d.ts +319 -0
- package/dist/src/terminal-watch-record.js +742 -0
- package/dist/src/terminal-watch-record.js.map +1 -0
- package/dist/src/terminal-watch-service.js +48 -27
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +3 -279
- package/dist/src/terminal-watch-store.js +5 -1779
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/package.json +7 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +1 -1
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { supersedeUnacceptedCallbackDeliveries } from "./callback-outbox-policy.js";
|
|
6
|
+
import { isSessionSendBlockingStatus, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
|
|
7
|
+
import { appendExplicitUserCloseEvent, defaultStoreDir, ensureDir, listConversations, loadState, pathsForConversation, saveExplicitUserCloseState, withStoreWriterLeaseAsync } from "./store.js";
|
|
8
|
+
import { managedSessionRevision, unmanagedTerminalBindingToken } from "./managed-session.js";
|
|
9
|
+
import { listManagedSessions, saveManagedSession } from "./session-store.js";
|
|
10
|
+
import { isExactClaudeNativeInspectionIdleComposer, TerminalInputNotStartedError } from "./terminal-agent-bridge.js";
|
|
11
|
+
import { terminalControlAliasMatches, terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
|
|
12
|
+
import { terminalDispatchLedgerLooksLifecycle } from "./terminal-dispatch-ledger-codec.js";
|
|
13
|
+
import { assertSafeUserExplicitTerminalSend } from "./terminal-command-send-preflight.js";
|
|
14
|
+
import { cleanupDeferredForegroundUserClose } from "./deferred-foreground-user-close.js";
|
|
15
|
+
import { terminalSubmissionPayload } from "./terminal-dispatch-execution.js";
|
|
16
|
+
import { createTerminalUserSendIntentRepository, TerminalUserSendIntentBoundaryConflictError, TerminalUserSendIntentUncertainError } from "./terminal-user-send-intent.js";
|
|
17
|
+
import { terminalUserExplicitFallbackWatchId } from "./terminal-watch-store.js";
|
|
18
|
+
import { terminalSendResultContract } from "./terminal-dispatch-presenter.js";
|
|
19
|
+
import { expandHome } from "./cli-command-runtime.js";
|
|
20
|
+
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
21
|
+
const humanExplicitSendContext = new AsyncLocalStorage();
|
|
22
|
+
function humanExplicitSendRuntime() {
|
|
23
|
+
const runtime = humanExplicitSendContext.getStore();
|
|
24
|
+
if (!runtime) {
|
|
25
|
+
throw new Error("Terminal human-explicit Send runtime is unavailable");
|
|
26
|
+
}
|
|
27
|
+
return runtime;
|
|
28
|
+
}
|
|
29
|
+
function rawPort(name) {
|
|
30
|
+
return ((...arguments_) => {
|
|
31
|
+
const operation = humanExplicitSendRuntime().ports[name];
|
|
32
|
+
return operation(...arguments_);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const acquireFileLock = rawPort("acquireFileLock");
|
|
36
|
+
const acquireTerminalBridgeSendLock = rawPort("acquireTerminalBridgeSendLock");
|
|
37
|
+
const assertExpectedHandoffTokenUsesExactTerminalSelector = rawPort("assertExpectedHandoffTokenUsesExactTerminalSelector");
|
|
38
|
+
const attachUserExplicitFallbackWatch = rawPort("attachUserExplicitFallbackWatch");
|
|
39
|
+
const createTerminalAgentBridge = rawPort("createTerminalAgentBridge");
|
|
40
|
+
const loadTerminalBridgeDispatchLedger = rawPort("loadTerminalBridgeDispatchLedger");
|
|
41
|
+
const loadTerminalDispatchLedgerOwner = rawPort("loadTerminalDispatchLedgerOwner");
|
|
42
|
+
const prepareUserExplicitFallbackWatch = rawPort("prepareUserExplicitFallbackWatch");
|
|
43
|
+
const processIncarnationForPid = rawPort("processIncarnationForPid");
|
|
44
|
+
const required = rawPort("required");
|
|
45
|
+
const resolveTerminalBridgeDispatchLedger = rawPort("resolveTerminalBridgeDispatchLedger");
|
|
46
|
+
const storeDirFromOptions = rawPort("storeDirFromOptions");
|
|
47
|
+
const terminalBridgeRuntimeKey = rawPort("terminalBridgeRuntimeKey");
|
|
48
|
+
const terminalControlFromTakeover = rawPort("terminalControlFromTakeover");
|
|
49
|
+
const terminalDispatchRecordMatchesControl = rawPort("terminalDispatchRecordMatchesControl");
|
|
50
|
+
const terminalRuntimeForLiveIdentity = rawPort("terminalRuntimeForLiveIdentity");
|
|
51
|
+
const textSummary = rawPort("textSummary");
|
|
52
|
+
const userExplicitFallbackWatchReceipt = rawPort("userExplicitFallbackWatchReceipt");
|
|
53
|
+
const cliEnv = () => humanExplicitSendRuntime().runtime.env();
|
|
54
|
+
const cliNow = () => humanExplicitSendRuntime().runtime.now();
|
|
55
|
+
const runtimeLog = (level, event, fields) => humanExplicitSendRuntime().runtime.log(level, event, fields);
|
|
56
|
+
const printJson = (value) => humanExplicitSendRuntime().runtime.printJson(value);
|
|
57
|
+
const durableTerminalInputDispatched = (conversation) => humanExplicitSendRuntime().runtime
|
|
58
|
+
.durableTerminalInputDispatched(conversation);
|
|
59
|
+
const managedSendAttempt = (options, messageBody, terminal, deferZeroInputFailurePresentation, attempt) => humanExplicitSendRuntime().managedSendAttempt(options, messageBody, terminal, deferZeroInputFailurePresentation, attempt);
|
|
60
|
+
function explicitTerminalSendToken(terminal) {
|
|
61
|
+
const processIncarnation = processIncarnationForPid(terminal.pid);
|
|
62
|
+
return unmanagedTerminalBindingToken({
|
|
63
|
+
terminalId: terminal.conversationId,
|
|
64
|
+
terminalControl: terminal.terminalControl,
|
|
65
|
+
agent: terminal.agent,
|
|
66
|
+
pid: terminal.pid,
|
|
67
|
+
workspace: terminal.terminalControl.currentPath ?? "",
|
|
68
|
+
processUuid: processIncarnation.processUuid,
|
|
69
|
+
processBirth: processIncarnation.processBirth
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function explicitSendTurnReference(candidate) {
|
|
73
|
+
try {
|
|
74
|
+
return turnIdForConversation(candidate);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return stringValue(candidate.conversation_id) ?? "unknown";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function hasFreshExplicitTerminalSendToken(options, terminal) {
|
|
81
|
+
const supplied = stringValue(options.expectedTerminalToken);
|
|
82
|
+
return Boolean(supplied && supplied === explicitTerminalSendToken(terminal));
|
|
83
|
+
}
|
|
84
|
+
function assertFreshUserExplicitTerminalSendToken(options, terminal) {
|
|
85
|
+
const expected = stringValue(options.expectedUserExplicitTerminalToken);
|
|
86
|
+
if (expected && expected !== explicitTerminalSendToken(terminal)) {
|
|
87
|
+
throw new Error("the explicit terminal send token is stale; refresh AKK list");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function assertFreshUserExplicitTerminalSendTargetWhileLocked(options, terminal) {
|
|
91
|
+
if (!stringValue(options.expectedUserExplicitTerminalToken))
|
|
92
|
+
return;
|
|
93
|
+
const resolved = await createTerminalAgentBridge(options)
|
|
94
|
+
.resolveConversationId(terminal.conversationId);
|
|
95
|
+
if (!resolved ||
|
|
96
|
+
resolved.agent !== terminal.agent ||
|
|
97
|
+
resolved.pid !== terminal.pid ||
|
|
98
|
+
!terminalControlsShareIncarnation(resolved.terminalControl, terminal.terminalControl)) {
|
|
99
|
+
throw new Error("the explicitly selected terminal is no longer the same live process");
|
|
100
|
+
}
|
|
101
|
+
assertFreshUserExplicitTerminalSendToken(options, resolved);
|
|
102
|
+
}
|
|
103
|
+
function releaseTerminalDispatchForExplicitSend(input) {
|
|
104
|
+
let ledger;
|
|
105
|
+
try {
|
|
106
|
+
ledger = loadTerminalBridgeDispatchLedger(input.terminal.terminalControl);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
checked: false,
|
|
111
|
+
warnings: [
|
|
112
|
+
`terminal dispatch ownership could not be checked before Session ` +
|
|
113
|
+
`release: ${error instanceof Error ? error.message : String(error)}`
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (!ledger || ledger.status === "resolved") {
|
|
118
|
+
return { checked: true, warnings: [] };
|
|
119
|
+
}
|
|
120
|
+
if (terminalDispatchLedgerLooksLifecycle(ledger)) {
|
|
121
|
+
return { checked: true, lifecycleLedger: ledger, warnings: [] };
|
|
122
|
+
}
|
|
123
|
+
if (!terminalDispatchRecordMatchesControl(ledger, input.terminal.terminalControl)) {
|
|
124
|
+
return { checked: true, warnings: [] };
|
|
125
|
+
}
|
|
126
|
+
const conversationId = stringValue(ledger.conversation_id);
|
|
127
|
+
const expectedMessageId = stringValue(ledger.message_id);
|
|
128
|
+
if (!conversationId || !expectedMessageId) {
|
|
129
|
+
return {
|
|
130
|
+
checked: true,
|
|
131
|
+
warnings: [
|
|
132
|
+
"orphaned terminal dispatch was kept because its exact conversation " +
|
|
133
|
+
"or message generation could not be verified"
|
|
134
|
+
]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (input.conversations.some((conversation) => conversation.conversation_id === conversationId)) {
|
|
138
|
+
return { checked: true, warnings: [] };
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
if (loadTerminalDispatchLedgerOwner(ledger)) {
|
|
142
|
+
return { checked: true, warnings: [] };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
checked: true,
|
|
148
|
+
warnings: [
|
|
149
|
+
`orphaned terminal dispatch owner could not be checked: ${error instanceof Error ? error.message : String(error)}`
|
|
150
|
+
]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const resolved = resolveTerminalBridgeDispatchLedger(input.terminal.terminalControl, {
|
|
155
|
+
conversation: { conversation_id: conversationId },
|
|
156
|
+
expectedMessageId,
|
|
157
|
+
reason: "orphaned management superseded by explicit user Send"
|
|
158
|
+
});
|
|
159
|
+
return resolved
|
|
160
|
+
? { checked: true, warnings: [] }
|
|
161
|
+
: {
|
|
162
|
+
checked: true,
|
|
163
|
+
warnings: [
|
|
164
|
+
"orphaned terminal dispatch changed before explicit Send cleanup"
|
|
165
|
+
]
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
checked: true,
|
|
171
|
+
warnings: [
|
|
172
|
+
`orphaned terminal dispatch could not be released: ${error instanceof Error ? error.message : String(error)}`
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function bestEffortReleaseTerminalManagementForExplicitSend(input) {
|
|
178
|
+
try {
|
|
179
|
+
return await withStoreWriterLeaseAsync(input.storeDir, async () => releaseTerminalManagementForExplicitSendUnderWriter(input), { timeoutMs: 0 });
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return [
|
|
183
|
+
`AKK management writer was unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
184
|
+
];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function releaseTerminalManagementForExplicitSendUnderWriter(input) {
|
|
188
|
+
const warnings = [];
|
|
189
|
+
let conversations;
|
|
190
|
+
try {
|
|
191
|
+
conversations = listConversations(input.storeDir);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return [
|
|
195
|
+
`managed Turn inventory could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
for (const candidate of conversations) {
|
|
199
|
+
const reference = explicitSendTurnReference(candidate);
|
|
200
|
+
try {
|
|
201
|
+
if (!isSessionSendBlockingStatus(candidate.status))
|
|
202
|
+
continue;
|
|
203
|
+
const takeover = isRecord(candidate.native_session_takeover)
|
|
204
|
+
? candidate.native_session_takeover
|
|
205
|
+
: undefined;
|
|
206
|
+
const control = terminalControlFromTakeover(takeover);
|
|
207
|
+
if (!control ||
|
|
208
|
+
!terminalControlsShareIncarnation(control, input.terminal.terminalControl)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const paths = pathsForConversation(candidate.conversation_id, input.storeDir);
|
|
212
|
+
const releaseStateLock = acquireFileLock(`${paths.statePath}.lock`, { timeoutMs: 0 });
|
|
213
|
+
try {
|
|
214
|
+
const current = loadState(paths.statePath);
|
|
215
|
+
if (!isSessionSendBlockingStatus(current.status))
|
|
216
|
+
continue;
|
|
217
|
+
const currentTakeover = isRecord(current.native_session_takeover)
|
|
218
|
+
? current.native_session_takeover
|
|
219
|
+
: undefined;
|
|
220
|
+
const currentControl = terminalControlFromTakeover(currentTakeover);
|
|
221
|
+
if (!currentControl ||
|
|
222
|
+
!terminalControlsShareIncarnation(currentControl, input.terminal.terminalControl)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const now = cliNow().toISOString();
|
|
226
|
+
const closed = {
|
|
227
|
+
...supersedeUnacceptedCallbackDeliveries(current, {
|
|
228
|
+
at: now,
|
|
229
|
+
reason: "superseded_by_user_explicit_send"
|
|
230
|
+
}),
|
|
231
|
+
status: "closed",
|
|
232
|
+
closed_at: now,
|
|
233
|
+
close_reason: "superseded by explicit user Send",
|
|
234
|
+
disposition: "user_abandoned_management",
|
|
235
|
+
callback_expected: false,
|
|
236
|
+
updated_at: now
|
|
237
|
+
};
|
|
238
|
+
delete closed.idle_since;
|
|
239
|
+
saveExplicitUserCloseState(paths.statePath, closed);
|
|
240
|
+
try {
|
|
241
|
+
const transferCleanup = cleanupDeferredForegroundUserClose({
|
|
242
|
+
storeDir: input.storeDir,
|
|
243
|
+
conversation: closed,
|
|
244
|
+
at: now
|
|
245
|
+
});
|
|
246
|
+
warnings.push(...transferCleanup.warnings.map((warning) => `Turn ${explicitSendTurnReference(current)} deferred cleanup: ${warning}`));
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
warnings.push(`Turn ${explicitSendTurnReference(current)} deferred cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const expectedMessageId = stringValue(currentTakeover?.terminal_bridge_message_id);
|
|
253
|
+
const dispatchResolved = resolveTerminalBridgeDispatchLedger(currentControl, {
|
|
254
|
+
conversation: closed,
|
|
255
|
+
expectedMessageId,
|
|
256
|
+
reason: "management superseded by explicit user Send"
|
|
257
|
+
});
|
|
258
|
+
if (expectedMessageId && !dispatchResolved) {
|
|
259
|
+
warnings.push(`Turn ${explicitSendTurnReference(current)} dispatch cleanup ` +
|
|
260
|
+
"did not match its expected ledger");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
warnings.push(`Turn ${explicitSendTurnReference(current)} dispatch cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const terminalInputSent = durableTerminalInputDispatched(current);
|
|
268
|
+
appendExplicitUserCloseEvent(paths.logPath, {
|
|
269
|
+
ts: now,
|
|
270
|
+
conversation_id: current.conversation_id,
|
|
271
|
+
event: "conversation_closed",
|
|
272
|
+
status: "closed",
|
|
273
|
+
reason: closed.close_reason,
|
|
274
|
+
disposition: "user_abandoned_management",
|
|
275
|
+
terminal_input_sent: terminalInputSent,
|
|
276
|
+
terminal_input_dispatched: terminalInputSent,
|
|
277
|
+
coding_agent_stopped: false,
|
|
278
|
+
tmux_pane_closed: false
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
warnings.push(`Turn ${explicitSendTurnReference(current)} close event failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
releaseStateLock();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
warnings.push(`Turn ${reference} could not release AKK ` +
|
|
291
|
+
`management: ${error instanceof Error ? error.message : String(error)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
let currentConversations;
|
|
295
|
+
try {
|
|
296
|
+
currentConversations = listConversations(input.storeDir);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
warnings.push(`managed Turn inventory could not be rechecked before Session release: ${error instanceof Error ? error.message : String(error)}`);
|
|
300
|
+
return warnings;
|
|
301
|
+
}
|
|
302
|
+
const blockingSessionIds = new Set(currentConversations
|
|
303
|
+
.filter((turn) => isSessionSendBlockingStatus(turn.status))
|
|
304
|
+
.map((turn) => sessionIdForConversation(turn)));
|
|
305
|
+
const dispatchRelease = releaseTerminalDispatchForExplicitSend({
|
|
306
|
+
terminal: input.terminal,
|
|
307
|
+
conversations: currentConversations
|
|
308
|
+
});
|
|
309
|
+
warnings.push(...dispatchRelease.warnings);
|
|
310
|
+
if (!dispatchRelease.checked) {
|
|
311
|
+
return warnings;
|
|
312
|
+
}
|
|
313
|
+
let sessions;
|
|
314
|
+
try {
|
|
315
|
+
sessions = listManagedSessions(input.storeDir);
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
warnings.push(`managed Session inventory could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
319
|
+
return warnings;
|
|
320
|
+
}
|
|
321
|
+
const lifecycleLedger = dispatchRelease.lifecycleLedger;
|
|
322
|
+
for (const session of sessions) {
|
|
323
|
+
try {
|
|
324
|
+
const binding = session.binding;
|
|
325
|
+
if (!["bound", "quarantined", "transitioning"].includes(session.status) ||
|
|
326
|
+
session.agent !== input.terminal.agent ||
|
|
327
|
+
!binding ||
|
|
328
|
+
binding.native_process.pid !== input.terminal.pid ||
|
|
329
|
+
!terminalControlAliasMatches(binding.terminal_id, binding.terminal_control, input.terminal.conversationId, input.terminal.terminalControl) ||
|
|
330
|
+
blockingSessionIds.has(session.session_id)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const lifecycleSourceSessionId = stringValue(lifecycleLedger?.source_session_id);
|
|
334
|
+
const lifecycleTargetSessionId = stringValue(lifecycleLedger?.target_session_id);
|
|
335
|
+
const lifecycleTransitionId = stringValue(lifecycleLedger?.transition_id);
|
|
336
|
+
const lifecycleNamesSession = Boolean(lifecycleLedger &&
|
|
337
|
+
(lifecycleSourceSessionId === session.session_id ||
|
|
338
|
+
lifecycleTargetSessionId === session.session_id));
|
|
339
|
+
if (lifecycleNamesSession) {
|
|
340
|
+
const transitionRelationship = session.last_transition_id
|
|
341
|
+
? lifecycleTransitionId === session.last_transition_id
|
|
342
|
+
? `transition ${session.last_transition_id}`
|
|
343
|
+
: `a lifecycle transition that drifted from Session marker ` +
|
|
344
|
+
session.last_transition_id
|
|
345
|
+
: `lifecycle transition ${lifecycleTransitionId ?? "with unknown id"}`;
|
|
346
|
+
warnings.push(`Session ${session.session_id} kept AKK management because ` +
|
|
347
|
+
`unresolved ${transitionRelationship} still names it; explicit ` +
|
|
348
|
+
"Send does not prove that transition's native outcome");
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const detachedAt = cliNow().toISOString();
|
|
352
|
+
saveManagedSession(input.storeDir, {
|
|
353
|
+
...session,
|
|
354
|
+
status: "detached",
|
|
355
|
+
detached_at: detachedAt,
|
|
356
|
+
quarantine_reason: undefined,
|
|
357
|
+
updated_at: detachedAt
|
|
358
|
+
}, { expectedRevision: managedSessionRevision(session) });
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
warnings.push(`Session ${session.session_id} could not release AKK management: ${error instanceof Error ? error.message : String(error)}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return warnings;
|
|
365
|
+
}
|
|
366
|
+
function explicitTerminalSendIntentRuntimeDir() {
|
|
367
|
+
const configured = stringValue(cliEnv().AKK_RUNTIME_DIR);
|
|
368
|
+
return configured
|
|
369
|
+
? path.resolve(required(expandHome(configured), "AKK runtime directory"))
|
|
370
|
+
: path.join(path.dirname(defaultStoreDir()), "runtime-v2");
|
|
371
|
+
}
|
|
372
|
+
function terminalUserSendIntentContext(options, messageBody, terminal) {
|
|
373
|
+
const messageId = required(stringValue(options.messageId), "explicit terminal Send message id is unavailable");
|
|
374
|
+
const payload = terminalSubmissionPayload(String(messageBody));
|
|
375
|
+
return {
|
|
376
|
+
messageId,
|
|
377
|
+
payload,
|
|
378
|
+
boundary: {
|
|
379
|
+
terminalRuntimeKey: terminalBridgeRuntimeKey(terminal.terminalControl),
|
|
380
|
+
physicalToken: explicitTerminalSendToken(terminal),
|
|
381
|
+
messageId,
|
|
382
|
+
requestHash: createHash("sha256").update(payload).digest("hex")
|
|
383
|
+
},
|
|
384
|
+
repository: createTerminalUserSendIntentRepository({
|
|
385
|
+
runtimeDir: explicitTerminalSendIntentRuntimeDir()
|
|
386
|
+
})
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
class TerminalUserSendReplayForbiddenError extends Error {
|
|
390
|
+
constructor(message) {
|
|
391
|
+
super(message);
|
|
392
|
+
this.name = "TerminalUserSendReplayForbiddenError";
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function userExplicitEmergencyTerminalLockPath(terminal) {
|
|
396
|
+
const userScope = createHash("sha256")
|
|
397
|
+
.update(path.dirname(defaultStoreDir()))
|
|
398
|
+
.digest("hex")
|
|
399
|
+
.slice(0, 16);
|
|
400
|
+
const terminalKey = createHash("sha256")
|
|
401
|
+
.update(terminalBridgeRuntimeKey(terminal.terminalControl))
|
|
402
|
+
.digest("hex");
|
|
403
|
+
const directory = path.join(os.tmpdir(), `agent-knock-knock-user-send-${userScope}`);
|
|
404
|
+
ensureDir(directory);
|
|
405
|
+
return path.join(directory, `terminal-${terminalKey}.lock`);
|
|
406
|
+
}
|
|
407
|
+
function acquireUserExplicitTerminalSendLock(options, terminal, warnings) {
|
|
408
|
+
let release;
|
|
409
|
+
try {
|
|
410
|
+
release = acquireTerminalBridgeSendLock(storeDirFromOptions(options), terminal.terminalControl, { timeoutMs: 30_000 });
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
if (isRecord(error) && error.code === "LOCK_TIMEOUT")
|
|
414
|
+
throw error;
|
|
415
|
+
warnings.push(`primary terminal serialization was unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
416
|
+
try {
|
|
417
|
+
release = acquireFileLock(userExplicitEmergencyTerminalLockPath(terminal), { timeoutMs: 30_000 });
|
|
418
|
+
}
|
|
419
|
+
catch (fallbackError) {
|
|
420
|
+
if (isRecord(fallbackError) &&
|
|
421
|
+
fallbackError.code === "LOCK_TIMEOUT") {
|
|
422
|
+
throw fallbackError;
|
|
423
|
+
}
|
|
424
|
+
warnings.push(`emergency terminal serialization was unavailable; proceeding ` +
|
|
425
|
+
`with exact pre-input revalidation: ${fallbackError instanceof Error
|
|
426
|
+
? fallbackError.message
|
|
427
|
+
: String(fallbackError)}`);
|
|
428
|
+
return () => { };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return () => {
|
|
432
|
+
try {
|
|
433
|
+
release?.();
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
const warning = `terminal serialization cleanup failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
437
|
+
warnings.push(warning);
|
|
438
|
+
runtimeLog("warn", "terminal_user_explicit_send_lock_release_failed", {
|
|
439
|
+
terminal_id: terminal.conversationId,
|
|
440
|
+
warning
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function printReplayedUserExplicitSend(options, terminal, intent, deliveryMode) {
|
|
446
|
+
if (deliveryMode === "managed") {
|
|
447
|
+
printJson({
|
|
448
|
+
delivered: true,
|
|
449
|
+
replayed: true,
|
|
450
|
+
status: "submission_pending_acceptance",
|
|
451
|
+
submission_outcome: "pending_acceptance",
|
|
452
|
+
delivery_receipt: "enter_dispatched",
|
|
453
|
+
do_not_retry: true,
|
|
454
|
+
...terminalSendResultContract({
|
|
455
|
+
terminalInputDispatched: true,
|
|
456
|
+
agentAcceptance: "unproven",
|
|
457
|
+
managementMode: "managed",
|
|
458
|
+
observationMode: "none",
|
|
459
|
+
callbackAvailable: false
|
|
460
|
+
}),
|
|
461
|
+
terminal_id: terminal.conversationId,
|
|
462
|
+
message_id: intent.messageId,
|
|
463
|
+
scope: "terminal_user_explicit"
|
|
464
|
+
});
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const watchId = terminalUserExplicitFallbackWatchId({
|
|
468
|
+
messageId: intent.messageId,
|
|
469
|
+
physicalToken: intent.boundary.physicalToken,
|
|
470
|
+
requestHash: intent.boundary.requestHash
|
|
471
|
+
});
|
|
472
|
+
const callbackReceipt = userExplicitFallbackWatchReceipt({
|
|
473
|
+
options,
|
|
474
|
+
watchId
|
|
475
|
+
});
|
|
476
|
+
const callbackAvailable = callbackReceipt?.callback_expected === true;
|
|
477
|
+
printJson({
|
|
478
|
+
delivered: true,
|
|
479
|
+
delivered_unmanaged: true,
|
|
480
|
+
delivery_receipt: "enter_dispatched",
|
|
481
|
+
...(callbackReceipt ?? { callback_expected: false }),
|
|
482
|
+
...terminalSendResultContract({
|
|
483
|
+
terminalInputDispatched: true,
|
|
484
|
+
agentAcceptance: "unproven",
|
|
485
|
+
managementMode: "unmanaged",
|
|
486
|
+
observationMode: callbackAvailable ? "terminal_watch" : "none",
|
|
487
|
+
callbackAvailable,
|
|
488
|
+
interactionNotificationAvailable: callbackAvailable
|
|
489
|
+
}),
|
|
490
|
+
replayed: true,
|
|
491
|
+
terminal_id: terminal.conversationId,
|
|
492
|
+
message_id: intent.messageId,
|
|
493
|
+
scope: "terminal_user_explicit"
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function reserveUserExplicitSendIntent(options, messageBody, terminal) {
|
|
497
|
+
const intent = terminalUserSendIntentContext(options, messageBody, terminal);
|
|
498
|
+
const routingWarning = stringValue(options.terminalUserSendRoutingWarning);
|
|
499
|
+
const warnings = routingWarning ? [routingWarning] : [];
|
|
500
|
+
let reservation;
|
|
501
|
+
try {
|
|
502
|
+
reservation = intent.repository.reserve(intent.boundary);
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
if (error instanceof TerminalUserSendIntentBoundaryConflictError ||
|
|
506
|
+
error instanceof TerminalUserSendIntentUncertainError) {
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
509
|
+
// Runtime receipts strengthen same-id retry safety; their own damage is
|
|
510
|
+
// never authority to suppress a fresh physical user Send.
|
|
511
|
+
warnings.push(`durable user-Send intent unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
512
|
+
return {
|
|
513
|
+
outcome: "proceed",
|
|
514
|
+
lease: { intent, durable: false, warnings }
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
// Presentation is outside the durability catch. A completed same-ID intent
|
|
518
|
+
// remains a replay even when stdout fails (for example EPIPE); it must never
|
|
519
|
+
// degrade into a fresh physical Send.
|
|
520
|
+
if (reservation.outcome === "replay") {
|
|
521
|
+
printReplayedUserExplicitSend(options, terminal, intent, required(reservation.intent.delivery_mode, "completed explicit Send delivery mode is unavailable"));
|
|
522
|
+
return { outcome: "replayed" };
|
|
523
|
+
}
|
|
524
|
+
if (reservation.outcome === "uncertain") {
|
|
525
|
+
throw new TerminalUserSendReplayForbiddenError(`explicit terminal Send ${intent.messageId} already reached ` +
|
|
526
|
+
`${reservation.stage}; automatic replay is forbidden`);
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
outcome: "proceed",
|
|
530
|
+
lease: { intent, durable: true, warnings }
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function cancelProvenZeroInputUserExplicitSendIntent(lease, terminal, reason) {
|
|
534
|
+
if (!lease.durable)
|
|
535
|
+
return;
|
|
536
|
+
try {
|
|
537
|
+
lease.intent.repository.cancelProvenZeroInput(lease.intent.boundary);
|
|
538
|
+
lease.durable = false;
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
const warning = `durable zero-input user-Send reservation cleanup failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
542
|
+
lease.warnings.push(warning);
|
|
543
|
+
runtimeLog("warn", "terminal_user_explicit_send_intent_cancel_failed", {
|
|
544
|
+
terminal_id: terminal.conversationId,
|
|
545
|
+
message_id: lease.intent.messageId,
|
|
546
|
+
zero_input_reason: reason instanceof Error
|
|
547
|
+
? reason.message
|
|
548
|
+
: String(reason),
|
|
549
|
+
error: warning
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function completeUserExplicitSendIntentWhileLocked(lease, deliveryMode) {
|
|
554
|
+
if (!lease.durable)
|
|
555
|
+
return;
|
|
556
|
+
try {
|
|
557
|
+
lease.intent.repository.complete(lease.intent.boundary, deliveryMode);
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
lease.durable = false;
|
|
561
|
+
lease.warnings.push(`durable user-Send completion receipt failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function completeManagedUserExplicitSendIntent(options, terminal, lease) {
|
|
565
|
+
let releaseTerminalLock;
|
|
566
|
+
if (lease.durable) {
|
|
567
|
+
try {
|
|
568
|
+
releaseTerminalLock = acquireTerminalBridgeSendLock(storeDirFromOptions(options), terminal.terminalControl, { timeoutMs: 0 });
|
|
569
|
+
completeUserExplicitSendIntentWhileLocked(lease, "managed");
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
lease.durable = false;
|
|
573
|
+
lease.warnings.push(`durable managed Send completion receipt failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
574
|
+
}
|
|
575
|
+
finally {
|
|
576
|
+
try {
|
|
577
|
+
releaseTerminalLock?.();
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
lease.warnings.push(`managed terminal serialization cleanup failed after Send: ${error instanceof Error ? error.message : String(error)}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (lease.warnings.length > 0) {
|
|
585
|
+
runtimeLog("warn", "terminal_user_explicit_send_intent_warning", {
|
|
586
|
+
terminal_id: terminal.conversationId,
|
|
587
|
+
message_id: lease.intent.messageId,
|
|
588
|
+
warnings: lease.warnings
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async function runUserExplicitTerminalFallback(options, terminal, managedFailure, intentLease) {
|
|
593
|
+
const storeDir = storeDirFromOptions(options);
|
|
594
|
+
let releaseTerminalLock;
|
|
595
|
+
let terminalLockReleased = false;
|
|
596
|
+
try {
|
|
597
|
+
releaseTerminalLock = acquireUserExplicitTerminalSendLock(options, terminal, intentLease.warnings);
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
// Both the failed managed path and this failed lock acquisition precede
|
|
601
|
+
// fallback input. Release the same-id receipt so the user can retry.
|
|
602
|
+
cancelProvenZeroInputUserExplicitSendIntent(intentLease, terminal, error);
|
|
603
|
+
runtimeLog("warn", "terminal_user_explicit_send_zero_input", {
|
|
604
|
+
terminal_id: terminal.conversationId,
|
|
605
|
+
message_id: intentLease.intent.messageId,
|
|
606
|
+
error: error instanceof Error ? error.message : String(error),
|
|
607
|
+
retry_safe: true,
|
|
608
|
+
intent_warnings: intentLease.warnings
|
|
609
|
+
});
|
|
610
|
+
throw error;
|
|
611
|
+
}
|
|
612
|
+
const releaseTerminalLockOnce = () => {
|
|
613
|
+
if (terminalLockReleased)
|
|
614
|
+
return;
|
|
615
|
+
terminalLockReleased = true;
|
|
616
|
+
releaseTerminalLock();
|
|
617
|
+
};
|
|
618
|
+
try {
|
|
619
|
+
let bridge;
|
|
620
|
+
let fresh;
|
|
621
|
+
let runtime;
|
|
622
|
+
const { payload, messageId } = intentLease.intent;
|
|
623
|
+
const intentWarnings = intentLease.warnings;
|
|
624
|
+
try {
|
|
625
|
+
bridge = createTerminalAgentBridge(options);
|
|
626
|
+
const resolved = await bridge.resolveConversationId(terminal.conversationId);
|
|
627
|
+
if (!resolved ||
|
|
628
|
+
resolved.agent !== terminal.agent ||
|
|
629
|
+
resolved.pid !== terminal.pid ||
|
|
630
|
+
!terminalControlsShareIncarnation(resolved.terminalControl, terminal.terminalControl)) {
|
|
631
|
+
throw new Error("the explicitly selected terminal is no longer the same live process");
|
|
632
|
+
}
|
|
633
|
+
fresh = resolved;
|
|
634
|
+
if (!hasFreshExplicitTerminalSendToken(options, fresh)) {
|
|
635
|
+
throw new Error("the explicit terminal send token is stale; refresh AKK list");
|
|
636
|
+
}
|
|
637
|
+
runtime = terminalRuntimeForLiveIdentity({
|
|
638
|
+
terminal: fresh,
|
|
639
|
+
physicalOnly: true
|
|
640
|
+
});
|
|
641
|
+
const status = await bridge.status(fresh.agent, fresh.terminalControl, { runtime, scrollbackLines: Number(options.scrollbackLines ?? 120) });
|
|
642
|
+
assertSafeUserExplicitTerminalSend(status);
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
cancelProvenZeroInputUserExplicitSendIntent(intentLease, terminal, error);
|
|
646
|
+
runtimeLog("warn", "terminal_user_explicit_send_zero_input", {
|
|
647
|
+
terminal_id: terminal.conversationId,
|
|
648
|
+
message_id: messageId,
|
|
649
|
+
error: error instanceof Error ? error.message : String(error),
|
|
650
|
+
retry_safe: true,
|
|
651
|
+
intent_warnings: intentWarnings
|
|
652
|
+
});
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
let preparedCallbackWatch;
|
|
656
|
+
const callbackWarnings = [];
|
|
657
|
+
try {
|
|
658
|
+
preparedCallbackWatch = await prepareUserExplicitFallbackWatch({
|
|
659
|
+
options,
|
|
660
|
+
terminal: fresh,
|
|
661
|
+
requestHash: intentLease.intent.boundary.requestHash,
|
|
662
|
+
messageId,
|
|
663
|
+
physicalToken: intentLease.intent.boundary.physicalToken
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
const warning = `automatic callback Watch preparation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
668
|
+
callbackWarnings.push(warning);
|
|
669
|
+
runtimeLog("warn", "terminal_user_explicit_fallback_watch_prepare_failed", {
|
|
670
|
+
terminal_id: fresh.conversationId,
|
|
671
|
+
message_id: messageId,
|
|
672
|
+
warning
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
let composerDisposition = "replaced_current_composer";
|
|
676
|
+
try {
|
|
677
|
+
const revalidatePhysicalMutation = async (terminalControl) => {
|
|
678
|
+
const currentStatus = await bridge.status(fresh.agent, terminalControl, {
|
|
679
|
+
runtime,
|
|
680
|
+
scrollbackLines: Number(options.scrollbackLines ?? 120)
|
|
681
|
+
});
|
|
682
|
+
assertSafeUserExplicitTerminalSend(currentStatus);
|
|
683
|
+
if (fresh.agent !== "codex" &&
|
|
684
|
+
!isExactClaudeNativeInspectionIdleComposer(currentStatus.screen.excerpt ?? "")) {
|
|
685
|
+
throw new Error("the explicitly selected Claude composer is no longer empty");
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
if (fresh.agent === "codex") {
|
|
689
|
+
const result = await bridge.sendUserExplicitCodex(fresh.terminalControl, payload, {
|
|
690
|
+
runtime,
|
|
691
|
+
beforeMutationReservation: ({ terminalControl }) => revalidatePhysicalMutation(terminalControl),
|
|
692
|
+
onComposerClearDispatched: () => {
|
|
693
|
+
runtimeLog("info", "terminal_user_explicit_composer_cleared", {
|
|
694
|
+
terminal_id: fresh.conversationId,
|
|
695
|
+
terminal_target: fresh.terminalControl.target,
|
|
696
|
+
message_id: messageId
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
composerDisposition = result.disposition;
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
await bridge.send(fresh.agent, fresh.terminalControl, payload, {
|
|
704
|
+
runtime,
|
|
705
|
+
requireExactComposerBeforeEnter: true,
|
|
706
|
+
requireExactEmptyComposerBeforeText: true,
|
|
707
|
+
allowWorkingComposerForUserExplicit: true,
|
|
708
|
+
beforeText: ({ terminalControl }) => revalidatePhysicalMutation(terminalControl)
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
completeUserExplicitSendIntentWhileLocked(intentLease, "unmanaged");
|
|
712
|
+
releaseTerminalLockOnce();
|
|
713
|
+
}
|
|
714
|
+
catch (error) {
|
|
715
|
+
// The user-explicit bridge normalizes only failures before its first
|
|
716
|
+
// physical mutation to this type. A draft-clear, text-delivery, or Enter
|
|
717
|
+
// attempt is uncertain and must retain the same-id intent.
|
|
718
|
+
const zeroInput = error instanceof TerminalInputNotStartedError;
|
|
719
|
+
if (zeroInput) {
|
|
720
|
+
cancelProvenZeroInputUserExplicitSendIntent(intentLease, fresh, error);
|
|
721
|
+
}
|
|
722
|
+
runtimeLog(zeroInput ? "warn" : "error", zeroInput
|
|
723
|
+
? "terminal_user_explicit_send_zero_input"
|
|
724
|
+
: "terminal_user_explicit_send_uncertain", {
|
|
725
|
+
terminal_id: fresh.conversationId,
|
|
726
|
+
terminal_target: fresh.terminalControl.target,
|
|
727
|
+
message_id: messageId,
|
|
728
|
+
error: error instanceof Error ? error.message : String(error),
|
|
729
|
+
...(zeroInput
|
|
730
|
+
? { retry_safe: true }
|
|
731
|
+
: { do_not_retry: true }),
|
|
732
|
+
intent_warnings: intentWarnings
|
|
733
|
+
});
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
let callbackReceipt;
|
|
737
|
+
if (preparedCallbackWatch) {
|
|
738
|
+
try {
|
|
739
|
+
callbackReceipt = await attachUserExplicitFallbackWatch({
|
|
740
|
+
options,
|
|
741
|
+
prepared: preparedCallbackWatch
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
catch (error) {
|
|
745
|
+
const warning = `automatic callback Watch attachment failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
746
|
+
callbackWarnings.push(warning);
|
|
747
|
+
runtimeLog("warn", "terminal_user_explicit_fallback_watch_attach_failed", {
|
|
748
|
+
terminal_id: fresh.conversationId,
|
|
749
|
+
message_id: messageId,
|
|
750
|
+
watch_id: preparedCallbackWatch.watchId,
|
|
751
|
+
warning,
|
|
752
|
+
delivered: true
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
let cleanupWarnings;
|
|
757
|
+
try {
|
|
758
|
+
cleanupWarnings = await bestEffortReleaseTerminalManagementForExplicitSend({
|
|
759
|
+
storeDir,
|
|
760
|
+
terminal: fresh
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
cleanupWarnings = [
|
|
765
|
+
`AKK management release was unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
766
|
+
];
|
|
767
|
+
}
|
|
768
|
+
const fallbackReason = managedFailure instanceof Error
|
|
769
|
+
? managedFailure.message
|
|
770
|
+
: String(managedFailure);
|
|
771
|
+
runtimeLog("warn", "terminal_user_explicit_send_fallback", {
|
|
772
|
+
terminal_id: fresh.conversationId,
|
|
773
|
+
terminal_target: fresh.terminalControl.target,
|
|
774
|
+
message_id: messageId,
|
|
775
|
+
managed_failure: fallbackReason,
|
|
776
|
+
composer_disposition: composerDisposition,
|
|
777
|
+
delivered_unmanaged: true
|
|
778
|
+
});
|
|
779
|
+
const callbackAvailable = callbackReceipt?.callback_expected === true;
|
|
780
|
+
printJson({
|
|
781
|
+
delivered: true,
|
|
782
|
+
delivered_unmanaged: true,
|
|
783
|
+
delivery_receipt: "enter_dispatched",
|
|
784
|
+
cleanup_warnings: cleanupWarnings,
|
|
785
|
+
intent_warnings: intentWarnings,
|
|
786
|
+
callback_warnings: callbackWarnings,
|
|
787
|
+
...(callbackReceipt ?? { callback_expected: false }),
|
|
788
|
+
terminal_id: fresh.conversationId,
|
|
789
|
+
message_id: messageId,
|
|
790
|
+
scope: "terminal_user_explicit",
|
|
791
|
+
...terminalSendResultContract({
|
|
792
|
+
terminalInputDispatched: true,
|
|
793
|
+
agentAcceptance: "unproven",
|
|
794
|
+
managementMode: "unmanaged",
|
|
795
|
+
observationMode: callbackAvailable ? "terminal_watch" : "none",
|
|
796
|
+
callbackAvailable,
|
|
797
|
+
interactionNotificationAvailable: callbackAvailable
|
|
798
|
+
}),
|
|
799
|
+
composer_disposition: composerDisposition,
|
|
800
|
+
composer_cleared_before_send: true,
|
|
801
|
+
// Deprecated compatibility alias. The v22 policy always dispatches
|
|
802
|
+
// C-u, so older readers may conservatively treat the prior draft as
|
|
803
|
+
// replaced without controlling the new behavior.
|
|
804
|
+
replaced_existing_draft: true,
|
|
805
|
+
previous_management_release_attempted: true,
|
|
806
|
+
warning: textSummary(`AKK delivered the user's message after managed-state preparation ` +
|
|
807
|
+
`failed (${fallbackReason}). ` +
|
|
808
|
+
(callbackReceipt
|
|
809
|
+
? `Terminal Watch ${callbackReceipt.watch_id} now provides the ` +
|
|
810
|
+
`completion callback; no managed Turn was claimed.`
|
|
811
|
+
: "No callback Watch could be attached.")),
|
|
812
|
+
next_action: callbackReceipt
|
|
813
|
+
? `wait for Terminal Watch ${callbackReceipt.watch_id} callback; ` +
|
|
814
|
+
"watch-status remains available for recovery"
|
|
815
|
+
: "refresh AKK list; the live coding agent continues independently of AKK callback state"
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
finally {
|
|
819
|
+
releaseTerminalLockOnce();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async function runRawTerminalSend(options, messageBody, terminalConversation) {
|
|
823
|
+
const identifyForeground = options.identifyForeground === true;
|
|
824
|
+
const suppliedExpectedTerminalToken = stringValue(options.expectedTerminalToken);
|
|
825
|
+
if (identifyForeground && terminalConversation.agent !== "codex") {
|
|
826
|
+
throw new Error("atomic foreground identification currently supports only Codex terminals");
|
|
827
|
+
}
|
|
828
|
+
if (identifyForeground && !suppliedExpectedTerminalToken) {
|
|
829
|
+
throw new Error("atomic foreground identification requires --expected-terminal-token");
|
|
830
|
+
}
|
|
831
|
+
const userExplicitAuthorityRequested = Boolean(suppliedExpectedTerminalToken && options.managedOnly !== true);
|
|
832
|
+
const freshExplicitAuthority = userExplicitAuthorityRequested
|
|
833
|
+
? hasFreshExplicitTerminalSendToken(options, terminalConversation)
|
|
834
|
+
: false;
|
|
835
|
+
if (userExplicitAuthorityRequested &&
|
|
836
|
+
!freshExplicitAuthority) {
|
|
837
|
+
throw new Error("the explicit terminal send token is stale; refresh AKK list");
|
|
838
|
+
}
|
|
839
|
+
if (!freshExplicitAuthority) {
|
|
840
|
+
await runManagedRawTerminalSend(options, messageBody, terminalConversation);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
assertExpectedHandoffTokenUsesExactTerminalSelector({
|
|
844
|
+
options,
|
|
845
|
+
terminal: terminalConversation
|
|
846
|
+
});
|
|
847
|
+
const explicitOptions = {
|
|
848
|
+
...options,
|
|
849
|
+
messageId: stringValue(options.messageId) ?? `user-send-${randomUUID()}`
|
|
850
|
+
};
|
|
851
|
+
const reservation = reserveUserExplicitSendIntent(explicitOptions, messageBody, terminalConversation);
|
|
852
|
+
if (reservation.outcome === "replayed")
|
|
853
|
+
return;
|
|
854
|
+
const intentLease = reservation.lease;
|
|
855
|
+
const managedResult = await runManagedRawTerminalSend({
|
|
856
|
+
...explicitOptions,
|
|
857
|
+
expectedUserExplicitTerminalToken: suppliedExpectedTerminalToken,
|
|
858
|
+
expectedTerminalToken: stringValue(explicitOptions.expectedManagedTerminalToken),
|
|
859
|
+
expectedManagedTerminalToken: undefined
|
|
860
|
+
}, messageBody, terminalConversation, true);
|
|
861
|
+
if (managedResult.outcome !== "zero_input") {
|
|
862
|
+
if (managedResult.outcome === "replayed" ||
|
|
863
|
+
managedResult.enterDispatched) {
|
|
864
|
+
completeManagedUserExplicitSendIntent(explicitOptions, terminalConversation, intentLease);
|
|
865
|
+
}
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
if (identifyForeground) {
|
|
869
|
+
// The explicit atomic contract never falls back to unmanaged delivery:
|
|
870
|
+
// doing so would discard the very identity/respond capability the caller
|
|
871
|
+
// asked this operation to establish. The task payload is proven unsent,
|
|
872
|
+
// so release its same-id reservation for an explicit later retry.
|
|
873
|
+
cancelProvenZeroInputUserExplicitSendIntent(intentLease, terminalConversation, managedResult.failure);
|
|
874
|
+
throw managedResult.failure;
|
|
875
|
+
}
|
|
876
|
+
return runUserExplicitTerminalFallback(explicitOptions, terminalConversation, managedResult.failure, intentLease);
|
|
877
|
+
}
|
|
878
|
+
async function runManagedRawTerminalSend(options, messageBody, terminalConversation, deferZeroInputFailurePresentation = false) {
|
|
879
|
+
const attempt = {
|
|
880
|
+
terminalControlSendInvoked: false,
|
|
881
|
+
result: undefined
|
|
882
|
+
};
|
|
883
|
+
try {
|
|
884
|
+
return await managedSendAttempt(options, messageBody, terminalConversation, deferZeroInputFailurePresentation, attempt);
|
|
885
|
+
}
|
|
886
|
+
catch (error) {
|
|
887
|
+
if (!deferZeroInputFailurePresentation)
|
|
888
|
+
throw error;
|
|
889
|
+
if (attempt.result)
|
|
890
|
+
return attempt.result;
|
|
891
|
+
if (!attempt.terminalControlSendInvoked) {
|
|
892
|
+
return { outcome: "zero_input", failure: error };
|
|
893
|
+
}
|
|
894
|
+
throw new Error("managed terminal Send may already have started input; refusing an " +
|
|
895
|
+
`automatic unmanaged fallback: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
export async function runHumanExplicitTerminalSend(dependencies, options, messageBody, terminal) {
|
|
899
|
+
return humanExplicitSendContext.run(dependencies, () => runRawTerminalSend(options, messageBody, terminal));
|
|
900
|
+
}
|
|
901
|
+
export { assertFreshUserExplicitTerminalSendTargetWhileLocked };
|
|
902
|
+
export { assertFreshUserExplicitTerminalSendToken };
|
|
903
|
+
//# sourceMappingURL=terminal-human-explicit-send-cli-adapter.js.map
|