@scotthuang/agent-knock-knock 0.13.2 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +27 -6
- 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/claude-terminal-agent-adapter.js +4 -0
- package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
- package/dist/src/cli-core.js +9 -1
- package/dist/src/cli-core.js.map +1 -1
- 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/codex-terminal-agent-adapter.js +4 -0
- package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
- 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 +8 -173
- 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/managed-session.js +2 -18
- package/dist/src/managed-session.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +13 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +635 -3
- 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 -2370
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +2 -121
- package/dist/src/openclaw-plugin-helpers.js +2 -1164
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +2 -510
- package/dist/src/openclaw-plugin-schemas.js +2 -519
- 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 +3 -17
- package/dist/src/terminal-action-projection.js +13 -41
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-adapter.d.ts +7 -0
- package/dist/src/terminal-agent-adapter.js +8 -0
- package/dist/src/terminal-agent-adapter.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +35 -332
- package/dist/src/terminal-agent-bridge.js +293 -2798
- 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-control-ref.d.ts +23 -0
- package/dist/src/terminal-control-ref.js +32 -0
- package/dist/src/terminal-control-ref.js.map +1 -1
- 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 +314 -1711
- 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 +12 -11
- package/dist/src/terminal-list-renderer.js +237 -104
- 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 +331 -0
- package/dist/src/terminal-model-control.js +1686 -0
- package/dist/src/terminal-model-control.js.map +1 -0
- 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 -0
- package/dist/src/terminal-runtime-cli-adapter.js +59 -17
- 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/docs/quickstart-herdr.md +1 -1
- package/docs/quickstart-tmux.md +27 -2
- package/openclaw.plugin.json +13 -1
- package/package.json +7 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +54 -11
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
// Infrastructure composition for terminal list discovery and selector projection.
|
|
2
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
-
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { listDeferredForegroundTransfers } from "./deferred-foreground-transfer.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { executorForConversation, isSessionSendBlockingStatus, isTerminalDispatchOwnerReleasedStatus, resolveExecutor, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
|
|
5
|
+
import { isExactNativeThreadId, unmanagedTerminalBindingToken } from "./managed-session.js";
|
|
6
|
+
import { executorForConversation, resolveExecutor, sessionIdForConversation } from "./protocol.js";
|
|
9
7
|
import { resolveSessionSelector, sessionShortRef } from "./session-selector.js";
|
|
10
8
|
import { projectSessionSelectorCandidate } from "./terminal-selector-projection-service.js";
|
|
11
|
-
import { listManagedSessions,
|
|
9
|
+
import { listManagedSessions, tryLoadManagedSession } from "./session-store.js";
|
|
12
10
|
import { defaultStoreDir, ensureStoreWritable, inspectStoreCompatibility, listConversations, storeManifestPath, STORE_SESSION_AUTHORITY_PROTOCOL } from "./store.js";
|
|
13
|
-
import { parseTerminalConversationId } from "./terminal-agent-adapter.js";
|
|
14
11
|
import { exactCodexReadyStyledComposerCapture } from "./terminal-agent-bridge.js";
|
|
15
12
|
import { captureCodexHumanStartedActiveTaskAnchor } from "./terminal-submission-acceptance.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
13
|
+
import { decideTerminalUserExplicitSendAuthority } from "./terminal-action-projection.js";
|
|
14
|
+
import { materializeModelControlAvailability } from "./terminal-model-control-availability.js";
|
|
15
|
+
import { terminalModelControlPlanConforms } from "./terminal-model-control.js";
|
|
16
|
+
import { childProcessIdsForRoot, terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
|
|
17
|
+
import { terminalScopedCodexApprovalPromptSnapshot } from "./terminal-scoped-approval-authority.js";
|
|
18
|
+
import { listActionContracts, renderAvailableListActions, renderTerminalModelControlActions } from "./terminal-list-renderer.js";
|
|
19
|
+
import { collectTerminalListInventory } from "./terminal-list-inventory.js";
|
|
20
|
+
import { collectTerminalListTerminalFacts } from "./terminal-list-facts.js";
|
|
21
|
+
import { decideTerminalListActions, modelControlPolicyFacts } from "./terminal-list-action-policy.js";
|
|
22
|
+
import { createTerminalListOwnershipService } from "./terminal-list-ownership-service.js";
|
|
23
|
+
import { createTerminalListProjectionService } from "./terminal-list-projection-service.js";
|
|
25
24
|
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
26
25
|
import { expandHome, writeCliJson as printJson } from "./cli-command-runtime.js";
|
|
27
26
|
import { cliCwd, cliNowMs, cliRuntimeLog as runtimeLog } from "./cli-runtime-context.js";
|
|
@@ -33,16 +32,43 @@ function terminalListRuntime() {
|
|
|
33
32
|
}
|
|
34
33
|
return runtime;
|
|
35
34
|
}
|
|
35
|
+
function terminalListOwnershipService() {
|
|
36
|
+
return terminalListRuntime().ownershipService;
|
|
37
|
+
}
|
|
38
|
+
function terminalListProjectionService() {
|
|
39
|
+
return terminalListRuntime().projectionService;
|
|
40
|
+
}
|
|
36
41
|
function withTerminalListRuntime(runtime, operation) {
|
|
37
42
|
return terminalListRuntimeContext.run(runtime, operation);
|
|
38
43
|
}
|
|
39
44
|
export function createTerminalListCliFacade(dependencies) {
|
|
45
|
+
const ownershipService = createTerminalListOwnershipService({
|
|
46
|
+
...dependencies.store,
|
|
47
|
+
...dependencies.authority,
|
|
48
|
+
codexProcessIncarnationForPid: dependencies.discovery.codexProcessIncarnationForPid,
|
|
49
|
+
currentWorkingDirectory: cliCwd,
|
|
50
|
+
runtimeLog
|
|
51
|
+
});
|
|
52
|
+
const projectionService = createTerminalListProjectionService({
|
|
53
|
+
approvalTtlMs: dependencies.policy.approvalTtlMs,
|
|
54
|
+
callbackRetryDisposition: dependencies.store.callbackRetryDisposition,
|
|
55
|
+
isVerifiedDeadTerminalAgentProcess: dependencies.store.isVerifiedDeadTerminalAgentProcess,
|
|
56
|
+
listDeferredForegroundTransfers,
|
|
57
|
+
nowMs: cliNowMs,
|
|
58
|
+
ownershipService,
|
|
59
|
+
summarizeConversation: dependencies.store.summarizeConversation,
|
|
60
|
+
terminalBridgeEnabled: dependencies.store.terminalBridgeEnabled,
|
|
61
|
+
terminalBridgeSubmission: dependencies.store.terminalBridgeSubmission,
|
|
62
|
+
terminalControlFromTakeover: dependencies.store.terminalControlFromTakeover
|
|
63
|
+
});
|
|
40
64
|
const runtime = {
|
|
41
65
|
...dependencies.reconciliation,
|
|
42
66
|
...dependencies.discovery,
|
|
43
67
|
...dependencies.store,
|
|
44
68
|
...dependencies.authority,
|
|
45
|
-
...dependencies.policy
|
|
69
|
+
...dependencies.policy,
|
|
70
|
+
ownershipService,
|
|
71
|
+
projectionService
|
|
46
72
|
};
|
|
47
73
|
const call = (operation) => withTerminalListRuntime(runtime, operation);
|
|
48
74
|
return {
|
|
@@ -203,7 +229,7 @@ function projectTerminalListScan(input) {
|
|
|
203
229
|
let projection;
|
|
204
230
|
if (managementErrors.length === 0) {
|
|
205
231
|
try {
|
|
206
|
-
projection =
|
|
232
|
+
projection = terminalListProjectionService().projectTerminalFirstList({
|
|
207
233
|
storeDir,
|
|
208
234
|
terminals: physicalTerminals,
|
|
209
235
|
managedSessions,
|
|
@@ -350,290 +376,36 @@ async function reconcileStoreForList(storeDir, options) {
|
|
|
350
376
|
idle_timeout_minutes: idle.idle_timeout_minutes
|
|
351
377
|
};
|
|
352
378
|
}
|
|
353
|
-
async function buildTerminalListGroup({ options, agentFilter, statusFilter, terminalId }) {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const adapters = agentFilter
|
|
367
|
-
? [registry.get(agentFilter)].filter((adapter) => adapter !== undefined)
|
|
368
|
-
: registry.list();
|
|
369
|
-
if (agentFilter && adapters.length === 0) {
|
|
370
|
-
return {
|
|
371
|
-
...empty,
|
|
372
|
-
summary: {
|
|
373
|
-
enabled: true,
|
|
374
|
-
agents: [],
|
|
375
|
-
skipped: `terminal agent adapter is not registered for ${agentFilter}`
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
const terminalProvider = terminalListRuntime().createTerminalControlProvider(options);
|
|
380
|
-
const bridge = terminalListRuntime().createTerminalAgentBridge(options, terminalProvider, registry);
|
|
381
|
-
const terminalDiagnostics = options.terminalDebug
|
|
382
|
-
? await terminalControlDiagnostics(terminalProvider)
|
|
383
|
-
: undefined;
|
|
384
|
-
const exactTarget = terminalId
|
|
385
|
-
? exactTerminalTargetDiagnostic(terminalId)
|
|
386
|
-
: undefined;
|
|
387
|
-
if (terminalId) {
|
|
388
|
-
runtimeLog("info", "terminal_exact_scan", {
|
|
389
|
-
stage: "started",
|
|
390
|
-
terminal_id: terminalId,
|
|
391
|
-
target: exactTarget
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
const terminalControlled = [];
|
|
395
|
-
let activeCount = 0;
|
|
396
|
-
const errors = [];
|
|
397
|
-
try {
|
|
398
|
-
const processSource = terminalListRuntime().createTerminalProcessSource(options);
|
|
399
|
-
const snapshots = await processSource.listProcessSnapshots((snapshot) => adapters.some((adapter) => adapter.capabilities.processDiscovery && adapter.classifyProcess(snapshot) !== undefined), { includeAncestors: true });
|
|
400
|
-
if (terminalId) {
|
|
401
|
-
const targetPid = exactTarget?.pid;
|
|
402
|
-
runtimeLog("info", "terminal_exact_scan", {
|
|
403
|
-
stage: "process_inventory",
|
|
404
|
-
terminal_id: terminalId,
|
|
405
|
-
target_pid: targetPid,
|
|
406
|
-
snapshot_count: snapshots.length,
|
|
407
|
-
target_process_found: targetPid === undefined
|
|
408
|
-
? undefined
|
|
409
|
-
: snapshots.some((snapshot) => snapshot.pid === targetPid),
|
|
410
|
-
target_process_snapshots: targetPid === undefined
|
|
411
|
-
? []
|
|
412
|
-
: snapshots
|
|
413
|
-
.filter((snapshot) => snapshot.pid === targetPid)
|
|
414
|
-
.map((snapshot) => ({ pid: snapshot.pid, ppid: snapshot.ppid }))
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
const activeSessions = await bridge.listProcesses(snapshots, adapters.map((adapter) => adapter.agent));
|
|
418
|
-
const rootSessions = selectRootTerminalProcesses(activeSessions, snapshots);
|
|
419
|
-
const controlledSessions = rootSessions.filter((session) => session.terminalControl !== undefined);
|
|
420
|
-
if (terminalId) {
|
|
421
|
-
const targetPid = exactTarget?.pid;
|
|
422
|
-
const targetSessions = targetPid === undefined
|
|
423
|
-
? []
|
|
424
|
-
: activeSessions.filter((session) => session.pid === targetPid);
|
|
425
|
-
runtimeLog("info", "terminal_exact_scan", {
|
|
426
|
-
stage: "process_classification_and_association",
|
|
427
|
-
terminal_id: terminalId,
|
|
428
|
-
target_pid: targetPid,
|
|
429
|
-
classified_process_count: activeSessions.length,
|
|
430
|
-
root_process_count: rootSessions.length,
|
|
431
|
-
controlled_process_count: controlledSessions.length,
|
|
432
|
-
target_classified_count: targetSessions.length,
|
|
433
|
-
target_controlled_count: targetSessions.filter((session) => session.terminalControl !== undefined).length,
|
|
434
|
-
target_sessions: targetSessions.map((session) => ({
|
|
435
|
-
agent: session.agent,
|
|
436
|
-
pid: session.pid,
|
|
437
|
-
ppid: session.ppid,
|
|
438
|
-
kind: session.kind,
|
|
439
|
-
confidence: session.confidence,
|
|
440
|
-
selected_as_root: rootSessions.includes(session),
|
|
441
|
-
terminal_conversation_id: session.terminalControl
|
|
442
|
-
? bridge.terminalConversationId(session)
|
|
443
|
-
: undefined,
|
|
444
|
-
terminal_control: terminalControlScanDiagnostic(session.terminalControl)
|
|
445
|
-
}))
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
activeCount = controlledSessions.length;
|
|
449
|
-
const selectedSessions = terminalId
|
|
450
|
-
? activeSessions.filter((session) => session.terminalControl !== undefined &&
|
|
451
|
-
bridge.terminalConversationId(session) === terminalId)
|
|
452
|
-
: controlledSessions;
|
|
453
|
-
for (const session of selectedSessions) {
|
|
454
|
-
try {
|
|
455
|
-
terminalControlled.push(await terminalControlledListEntry(session, activeSessions, options, bridge));
|
|
456
|
-
}
|
|
457
|
-
catch (error) {
|
|
458
|
-
errors.push(`terminal process ${session.pid}: ` +
|
|
459
|
-
(error instanceof Error ? error.message : String(error)));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
catch (error) {
|
|
464
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
465
|
-
}
|
|
466
|
-
if (terminalId) {
|
|
467
|
-
runtimeLog("info", "terminal_exact_scan", {
|
|
468
|
-
stage: "complete",
|
|
469
|
-
terminal_id: terminalId,
|
|
470
|
-
controlled_process_count: activeCount,
|
|
471
|
-
terminal_row_count: terminalControlled.length,
|
|
472
|
-
terminal_row_ids: terminalControlled.map((terminal) => terminal.id),
|
|
473
|
-
errors
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
return {
|
|
477
|
-
terminalControlled,
|
|
478
|
-
summary: {
|
|
479
|
-
enabled: true,
|
|
480
|
-
agents: adapters.map((adapter) => adapter.agent),
|
|
481
|
-
active_count: activeCount,
|
|
482
|
-
terminal_count: terminalControlled.length,
|
|
483
|
-
approval_scan: options.noApprovalScan ? "disabled" : "enabled",
|
|
484
|
-
diagnostics: terminalDiagnostics,
|
|
485
|
-
error: errors.length > 0 ? errors.join("; ") : undefined
|
|
379
|
+
async function buildTerminalListGroup({ options, agentFilter, statusFilter: _statusFilter, terminalId }) {
|
|
380
|
+
const runtime = terminalListRuntime();
|
|
381
|
+
return collectTerminalListInventory({
|
|
382
|
+
options,
|
|
383
|
+
agentFilter,
|
|
384
|
+
terminalId,
|
|
385
|
+
ports: {
|
|
386
|
+
createRegistry: (currentOptions) => runtime.createRuntimeTerminalAgentRegistry(currentOptions),
|
|
387
|
+
createBridge: (currentOptions, provider, registry) => runtime.createTerminalAgentBridge(currentOptions, provider, registry),
|
|
388
|
+
createProvider: (currentOptions) => runtime.createTerminalControlProvider(currentOptions),
|
|
389
|
+
createProcessSource: (currentOptions) => runtime.createTerminalProcessSource(currentOptions),
|
|
390
|
+
projectTerminal: ({ session, activeSessions, options: currentOptions, bridge }) => terminalControlledListEntry(session, activeSessions, currentOptions, bridge),
|
|
391
|
+
log: runtimeLog
|
|
486
392
|
}
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
function exactTerminalTargetDiagnostic(terminalId) {
|
|
490
|
-
try {
|
|
491
|
-
const parsed = parseTerminalConversationId(terminalId);
|
|
492
|
-
return parsed ? {
|
|
493
|
-
provider: parsed.kind,
|
|
494
|
-
agent: parsed.agent,
|
|
495
|
-
route: parsed.target,
|
|
496
|
-
pid: parsed.pid
|
|
497
|
-
} : undefined;
|
|
498
|
-
}
|
|
499
|
-
catch (error) {
|
|
500
|
-
return {
|
|
501
|
-
parse_error: error instanceof Error ? error.message : String(error)
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
function terminalControlScanDiagnostic(terminalControl) {
|
|
506
|
-
if (!terminalControl) {
|
|
507
|
-
return undefined;
|
|
508
|
-
}
|
|
509
|
-
const control = terminalControl;
|
|
510
|
-
return {
|
|
511
|
-
kind: terminalControl.kind,
|
|
512
|
-
target: terminalControl.target,
|
|
513
|
-
pane_pid: control.panePid,
|
|
514
|
-
process_anchor_pid: control.processAnchorPid,
|
|
515
|
-
endpoint_key: control.endpointKey,
|
|
516
|
-
resource_key: control.resourceKey,
|
|
517
|
-
terminal_id: control.terminalId
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
async function terminalControlDiagnostics(provider) {
|
|
521
|
-
return provider.diagnostics();
|
|
522
|
-
}
|
|
523
|
-
function managedTurnListEntry(task, { terminalBridge = false, approvalState, conversation } = {}) {
|
|
524
|
-
return renderManagedTurnListEntry(task, {
|
|
525
|
-
terminalBridge,
|
|
526
|
-
approvalState,
|
|
527
|
-
actionFacts: managedTurnListActionFacts(task, conversation)
|
|
528
393
|
});
|
|
529
394
|
}
|
|
530
|
-
function managedTurnListActionFacts(task, conversation) {
|
|
531
|
-
const nativeTakeover = isRecord(conversation?.native_session_takeover)
|
|
532
|
-
? conversation.native_session_takeover
|
|
533
|
-
: undefined;
|
|
534
|
-
const managedApprovalPending = isRecord(nativeTakeover?.terminal_bridge_approval);
|
|
535
|
-
const terminalBridgeReady = Boolean(conversation &&
|
|
536
|
-
terminalListRuntime().terminalBridgeEnabled(conversation) &&
|
|
537
|
-
terminalListRuntime().terminalControlFromTakeover(nativeTakeover) !== undefined);
|
|
538
|
-
const submission = terminalListRuntime().terminalBridgeSubmission(conversation);
|
|
539
|
-
const renewEligible = Boolean(terminalBridgeReady &&
|
|
540
|
-
task.status === "stalled" &&
|
|
541
|
-
submission?.status !== "uncertain" &&
|
|
542
|
-
!terminalListRuntime().isVerifiedDeadTerminalAgentProcess(conversation ?? {}));
|
|
543
|
-
const callbackDelivery = isRecord(conversation?.callback_delivery)
|
|
544
|
-
? conversation.callback_delivery
|
|
545
|
-
: undefined;
|
|
546
|
-
const retryCallbackEligible = Boolean(conversation &&
|
|
547
|
-
conversation.legacy_callback_status_error === undefined &&
|
|
548
|
-
terminalListRuntime().callbackRetryDisposition(callbackDelivery).state ===
|
|
549
|
-
"retryable");
|
|
550
|
-
const retrySubmissionCandidate = Boolean(conversation &&
|
|
551
|
-
executorForConversation(conversation).kind === "codex" &&
|
|
552
|
-
terminalBridgeReady &&
|
|
553
|
-
task.status === "stalled" &&
|
|
554
|
-
submission?.status === "uncertain" &&
|
|
555
|
-
stringValue(submission.last_proven_stage) === "text_injected");
|
|
556
|
-
return {
|
|
557
|
-
terminalBridgeReady,
|
|
558
|
-
managedApprovalPending,
|
|
559
|
-
renewEligible,
|
|
560
|
-
retryCallbackEligible,
|
|
561
|
-
retrySubmissionCandidate
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
395
|
async function terminalControlledListEntry(session, activeSessions, options, bridge = terminalListRuntime().createTerminalAgentBridge(options)) {
|
|
565
396
|
const terminalControl = session.terminalControl;
|
|
566
397
|
if (!terminalControl) {
|
|
567
398
|
throw new Error(`process ${session.pid} is not terminal-controlled`);
|
|
568
399
|
}
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
...(session.agent === "codex"
|
|
577
|
-
? { conversationId: bridge.terminalConversationId(session) }
|
|
578
|
-
: { sessionId: session.sessionId }),
|
|
579
|
-
terminalTarget: terminalControl.target
|
|
580
|
-
});
|
|
581
|
-
const { orphanedDispatch, nativeIdentityObservation, nativeAgentIdentity, authorityNativeIdentityObservation, authorityNativeAgentIdentity, codexOpenRootRolloutInventory, nativeProcessUuid, nativeProcessBirth, nativeProcessEvidence } = await observeTerminalNativeListIdentity(session, terminalControl, options, bridge);
|
|
582
|
-
const effectiveTerminalState = effectiveTerminalListState({
|
|
583
|
-
session,
|
|
584
|
-
terminalState,
|
|
585
|
-
nativeIdentityObservation,
|
|
586
|
-
nativeAgentIdentity,
|
|
587
|
-
nativeIdentityAuthorityObservation: authorityNativeIdentityObservation,
|
|
588
|
-
codexOpenRootRolloutInventory,
|
|
589
|
-
nativeProcessUuid,
|
|
590
|
-
nativeProcessBirth
|
|
591
|
-
});
|
|
592
|
-
const terminalStatusSnapshot = effectiveTerminalState._terminal_status_snapshot
|
|
593
|
-
? terminalBridgeStatusWithProjection(effectiveTerminalState._terminal_status_snapshot, effectiveTerminalState)
|
|
594
|
-
: undefined;
|
|
595
|
-
const statusCardObservation = session.agent === "codex" &&
|
|
596
|
-
typeof effectiveTerminalState.screen_excerpt === "string"
|
|
597
|
-
? bridge.registry.require("codex").observeThreadLifecycle?.({
|
|
598
|
-
operation: { kind: "new_thread" },
|
|
599
|
-
phase: "before",
|
|
600
|
-
screen: effectiveTerminalState.screen_excerpt
|
|
601
|
-
})
|
|
602
|
-
: undefined;
|
|
603
|
-
const statusCardNativeThreadId = statusCardObservation?.status === "observed" &&
|
|
604
|
-
effectiveTerminalState.activity_state === "idle" &&
|
|
605
|
-
effectiveTerminalState.approval_state.blocked !== true &&
|
|
606
|
-
isExactNativeThreadId(statusCardObservation.nativeThreadId)
|
|
607
|
-
? statusCardObservation.nativeThreadId
|
|
608
|
-
: undefined;
|
|
609
|
-
const agentVersion = terminalListRuntime().agentVersionForRunningProcess(session.agent, session.pid, options);
|
|
610
|
-
const lifecycleCapability = bridge.registry.require(session.agent)
|
|
611
|
-
.probeThreadLifecycle?.(agentVersion) ?? {
|
|
612
|
-
status: "unsupported",
|
|
613
|
-
agentVersion,
|
|
614
|
-
newThread: false,
|
|
615
|
-
resumeExact: false,
|
|
616
|
-
reason: "native thread lifecycle is unavailable"
|
|
617
|
-
};
|
|
618
|
-
const nativeInspectionCapability = bridge.registry.require(session.agent)
|
|
619
|
-
.probeNativeInspection?.(agentVersion) ?? {
|
|
620
|
-
status: "unsupported",
|
|
621
|
-
agentVersion,
|
|
622
|
-
statusInspection: false,
|
|
623
|
-
reason: "native inspection is unavailable"
|
|
624
|
-
};
|
|
625
|
-
const compatibilityWarnings = [...new Set([
|
|
626
|
-
lifecycleCapability.compatibilityWarning,
|
|
627
|
-
nativeInspectionCapability.compatibilityWarning
|
|
628
|
-
].filter((warning) => typeof warning === "string" && warning.trim().length > 0))];
|
|
629
|
-
const codexLatentClearResumeObservationValue = session.agent === "codex"
|
|
630
|
-
? terminalListRuntime().codexLatentClearResumeObservation({
|
|
631
|
-
screen: effectiveTerminalState.screen_excerpt,
|
|
632
|
-
agentVersion
|
|
633
|
-
})
|
|
634
|
-
: undefined;
|
|
400
|
+
const facts = await collectTerminalFactsForList(session, activeSessions, options, bridge, terminalControl);
|
|
401
|
+
const { orphanedDispatch, nativeIdentityObservation, nativeAgentIdentity, authorityNativeIdentityObservation, authorityNativeAgentIdentity, codexOpenRootRolloutInventory, nativeProcessUuid, nativeProcessBirth, nativeProcessEvidence } = facts.native;
|
|
402
|
+
const { observed: _observedTerminalState, effective: effectiveTerminalState, projected: projectedTerminalState, snapshot: terminalStatusSnapshot, statusCardNativeThreadId } = facts.status;
|
|
403
|
+
const { agentVersion, lifecycleCapability, nativeInspectionCapability, modelControlCapability, compatibilityWarnings } = facts.runtime;
|
|
404
|
+
const { automatedInputComposerReady, userExplicitComposerReady } = facts.composer;
|
|
405
|
+
const { terminalId, childPids, processIncarnation: physicalProcessIncarnation } = facts.physical;
|
|
406
|
+
const { latentClearResume: codexLatentClearResumeObservationValue } = facts.codex;
|
|
635
407
|
const lifecycleBindingToken = unmanagedTerminalBindingToken({
|
|
636
|
-
terminalId
|
|
408
|
+
terminalId,
|
|
637
409
|
terminalControl,
|
|
638
410
|
agent: session.agent,
|
|
639
411
|
pid: session.pid,
|
|
@@ -644,7 +416,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
644
416
|
rollout: nativeAgentIdentity?.rollout
|
|
645
417
|
});
|
|
646
418
|
const authorityLifecycleBindingToken = unmanagedTerminalBindingToken({
|
|
647
|
-
terminalId
|
|
419
|
+
terminalId,
|
|
648
420
|
terminalControl,
|
|
649
421
|
agent: session.agent,
|
|
650
422
|
pid: session.pid,
|
|
@@ -654,62 +426,26 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
654
426
|
processBirth: nativeProcessBirth,
|
|
655
427
|
rollout: authorityNativeAgentIdentity?.rollout
|
|
656
428
|
});
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
try {
|
|
661
|
-
terminalHasBlockingTurn = terminalIncarnationBlockingTurns(terminalListRuntime().storeDirFromOptions(options), terminalControl).length > 0;
|
|
662
|
-
}
|
|
663
|
-
catch (error) {
|
|
664
|
-
runtimeLog("warn", "terminal_managed_turn_inventory_unavailable", {
|
|
665
|
-
terminal_target: terminalControl.target,
|
|
666
|
-
error: error instanceof Error ? error.message : String(error)
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
const composerAuthority = await observeAutomatedInputComposerReady({
|
|
670
|
-
session,
|
|
671
|
-
terminalControl,
|
|
672
|
-
terminalState: effectiveTerminalState,
|
|
673
|
-
options
|
|
674
|
-
});
|
|
675
|
-
const automatedInputComposerReady = composerAuthority.automatedInputComposerReady;
|
|
676
|
-
const userExplicitComposerReady = composerAuthority.userExplicitComposerReady;
|
|
677
|
-
let physicalProcessIncarnation;
|
|
678
|
-
try {
|
|
679
|
-
physicalProcessIncarnation =
|
|
680
|
-
terminalListRuntime().processIncarnationForPid(session.pid);
|
|
681
|
-
}
|
|
682
|
-
catch (error) {
|
|
683
|
-
runtimeLog("warn", "terminal_physical_process_incarnation_unavailable", {
|
|
684
|
-
agent: session.agent,
|
|
685
|
-
terminal_target: terminalControl.target,
|
|
686
|
-
pid: session.pid,
|
|
687
|
-
error: error instanceof Error ? error.message : String(error)
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
const commands = terminalListCommands({
|
|
429
|
+
const actionSubject = {
|
|
430
|
+
exactTerminalRow: true,
|
|
431
|
+
processState: "active",
|
|
691
432
|
agent: session.agent,
|
|
692
433
|
terminalControl,
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
nativeProcessBirth,
|
|
699
|
-
codexLifecycleIncarnationAvailable,
|
|
700
|
-
automatedInputComposerReady,
|
|
701
|
-
hasOrphanedDispatch: orphanedDispatch !== undefined,
|
|
702
|
-
terminalHasBlockingTurn,
|
|
703
|
-
terminalHasInteraction: terminalStatusSnapshot?.interaction_state !== undefined
|
|
434
|
+
pid: session.pid
|
|
435
|
+
};
|
|
436
|
+
const actionDecisions = decideTerminalListActions({
|
|
437
|
+
subject: actionSubject,
|
|
438
|
+
facts
|
|
704
439
|
});
|
|
440
|
+
const commands = actionDecisions.commands;
|
|
705
441
|
const entry = {
|
|
706
|
-
id:
|
|
707
|
-
short_ref: sessionShortRef(
|
|
442
|
+
id: terminalId,
|
|
443
|
+
short_ref: sessionShortRef(terminalId),
|
|
708
444
|
source: "terminal",
|
|
709
445
|
agent: session.agent,
|
|
710
446
|
process_state: "active",
|
|
711
447
|
pid: session.pid,
|
|
712
|
-
child_pids:
|
|
448
|
+
child_pids: childPids,
|
|
713
449
|
command: session.command,
|
|
714
450
|
cwd: session.cwd,
|
|
715
451
|
workspace: session.cwd,
|
|
@@ -731,6 +467,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
731
467
|
agent_version: agentVersion,
|
|
732
468
|
native_thread_lifecycle: lifecycleCapability,
|
|
733
469
|
native_inspection: nativeInspectionCapability,
|
|
470
|
+
model_control: modelControlCapability,
|
|
734
471
|
...(compatibilityWarnings.length > 0
|
|
735
472
|
? { compatibility_warnings: compatibilityWarnings }
|
|
736
473
|
: {}),
|
|
@@ -738,23 +475,23 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
738
475
|
confidence: session.confidence,
|
|
739
476
|
reason: session.reason,
|
|
740
477
|
terminal_control: terminalControl,
|
|
741
|
-
approval_state:
|
|
742
|
-
activity_state:
|
|
743
|
-
activity_reason:
|
|
744
|
-
screen_state:
|
|
745
|
-
screen_reason:
|
|
746
|
-
native_identity_state:
|
|
747
|
-
durable_activity_state:
|
|
748
|
-
durable_activity_reason:
|
|
478
|
+
approval_state: projectedTerminalState.approval_state,
|
|
479
|
+
activity_state: projectedTerminalState.activity_state,
|
|
480
|
+
activity_reason: projectedTerminalState.activity_reason,
|
|
481
|
+
screen_state: projectedTerminalState.screen_state,
|
|
482
|
+
screen_reason: projectedTerminalState.screen_reason,
|
|
483
|
+
native_identity_state: projectedTerminalState.native_identity_state,
|
|
484
|
+
durable_activity_state: projectedTerminalState.durable_activity_state,
|
|
485
|
+
durable_activity_reason: projectedTerminalState.durable_activity_reason,
|
|
749
486
|
// Internal exact-observation evidence. The public projection strips this
|
|
750
487
|
// object; raw terminal status reuses it so screen, approval, and activity
|
|
751
488
|
// all describe the same capture.
|
|
752
489
|
...(terminalStatusSnapshot
|
|
753
490
|
? { _terminal_status_snapshot: terminalStatusSnapshot }
|
|
754
491
|
: {}),
|
|
755
|
-
// Internal action-projection evidence.
|
|
756
|
-
// this field after gating every automated-input action that
|
|
757
|
-
// human native-thread switch.
|
|
492
|
+
// Internal action-projection evidence. The terminal-first projection
|
|
493
|
+
// service strips this field after gating every automated-input action that
|
|
494
|
+
// can follow a human native-thread switch.
|
|
758
495
|
_automated_input_composer_ready: automatedInputComposerReady,
|
|
759
496
|
_user_explicit_composer_ready: userExplicitComposerReady,
|
|
760
497
|
_native_identity_authority: {
|
|
@@ -783,7 +520,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
783
520
|
owner_conversation_id: stringValue(orphanedDispatch.conversation_id),
|
|
784
521
|
message_id: stringValue(orphanedDispatch.message_id),
|
|
785
522
|
transition_id: stringValue(orphanedDispatch.transition_id),
|
|
786
|
-
recovery: `/akk close ${
|
|
523
|
+
recovery: `/akk close ${terminalId} ` +
|
|
787
524
|
(orphanedDispatch.kind === "lifecycle"
|
|
788
525
|
? `--expected-transition-id ${String(orphanedDispatch.transition_id)}`
|
|
789
526
|
: `--expected-message-id ${String(orphanedDispatch.message_id)}`)
|
|
@@ -793,23 +530,25 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
793
530
|
commands
|
|
794
531
|
};
|
|
795
532
|
const renderedActions = renderAvailableListActions(entry);
|
|
796
|
-
const terminalUserExplicitSendAuthority =
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
533
|
+
const terminalUserExplicitSendAuthority = actionDecisions.terminalUserExplicitSend.eligible
|
|
534
|
+
? decideTerminalUserExplicitSendAuthority({
|
|
535
|
+
exactTerminalRow: true,
|
|
536
|
+
terminalId: entry.id,
|
|
537
|
+
processState: entry.process_state,
|
|
538
|
+
terminalControl,
|
|
539
|
+
agent: session.agent,
|
|
540
|
+
pid: session.pid,
|
|
541
|
+
processUuid: physicalProcessIncarnation?.processUuid,
|
|
542
|
+
processBirth: physicalProcessIncarnation?.processBirth,
|
|
543
|
+
approvalScanned: projectedTerminalState.approval_state.scanned === true,
|
|
544
|
+
approvalBlocked: projectedTerminalState.approval_state.blocked === true,
|
|
545
|
+
// Codex user-explicit Send treats this observation as advisory: an
|
|
546
|
+
// off-screen or truncated composer must not hide the physical action.
|
|
547
|
+
// Claude Code still consumes the exact-composer result in the shared
|
|
548
|
+
// authority decision below.
|
|
549
|
+
userExplicitComposerReady
|
|
550
|
+
})
|
|
551
|
+
: { eligible: false };
|
|
813
552
|
const terminalUserExplicitSendAction = terminalUserExplicitSendAuthority.eligible
|
|
814
553
|
? {
|
|
815
554
|
tool: "agent_knock_knock_send",
|
|
@@ -826,6 +565,27 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
826
565
|
: {})
|
|
827
566
|
}
|
|
828
567
|
: undefined;
|
|
568
|
+
const modelControlFacts = modelControlPolicyFacts({
|
|
569
|
+
subject: actionSubject,
|
|
570
|
+
facts
|
|
571
|
+
});
|
|
572
|
+
const modelControlAvailability = materializeModelControlAvailability({
|
|
573
|
+
...modelControlFacts,
|
|
574
|
+
nativeAuthority: modelControlFacts.nativeAuthority.kind ===
|
|
575
|
+
"exact_session" && lifecycleBindingToken
|
|
576
|
+
? {
|
|
577
|
+
kind: "exact_session",
|
|
578
|
+
ordinaryBindingToken: lifecycleBindingToken
|
|
579
|
+
}
|
|
580
|
+
: modelControlFacts.nativeAuthority.kind === "verified_zero_rollout"
|
|
581
|
+
? { kind: "verified_zero_rollout" }
|
|
582
|
+
: { kind: "unavailable" }
|
|
583
|
+
}, actionDecisions.modelControl);
|
|
584
|
+
const modelControlActions = renderTerminalModelControlActions({
|
|
585
|
+
renderedActions,
|
|
586
|
+
availability: modelControlAvailability,
|
|
587
|
+
mutationScope: modelControlCapability.scope
|
|
588
|
+
});
|
|
829
589
|
const foregroundIdentificationActions = terminalUserExplicitSendAuthority.eligible &&
|
|
830
590
|
commands.identify_foreground === true
|
|
831
591
|
? {
|
|
@@ -858,59 +618,176 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
858
618
|
? { _terminal_user_explicit_send_action: terminalUserExplicitSendAction }
|
|
859
619
|
: {}),
|
|
860
620
|
available_actions: {
|
|
861
|
-
...
|
|
621
|
+
...modelControlActions,
|
|
862
622
|
...foregroundIdentificationActions
|
|
863
623
|
}
|
|
864
624
|
};
|
|
865
625
|
}
|
|
866
|
-
function
|
|
867
|
-
const
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
626
|
+
async function collectTerminalFactsForList(session, activeSessions, options, bridge, terminalControl) {
|
|
627
|
+
const terminalId = bridge.terminalConversationId(session);
|
|
628
|
+
const adapter = bridge.registry.require(session.agent);
|
|
629
|
+
return collectTerminalListTerminalFacts({
|
|
630
|
+
session,
|
|
631
|
+
terminalControl,
|
|
632
|
+
terminalId,
|
|
633
|
+
childPids: childProcessIdsForRoot(session, activeSessions),
|
|
634
|
+
adapter,
|
|
635
|
+
ports: terminalListFactPortsFor({
|
|
636
|
+
session,
|
|
637
|
+
terminalControl,
|
|
638
|
+
terminalId,
|
|
639
|
+
options,
|
|
640
|
+
bridge
|
|
641
|
+
})
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
function terminalListFactPortsFor(input) {
|
|
645
|
+
const { session, terminalControl, terminalId, options, bridge } = input;
|
|
882
646
|
return {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
647
|
+
observeStatus: () => listStateForTerminal(session.agent, terminalControl, options, bridge, {
|
|
648
|
+
pid: session.pid,
|
|
649
|
+
cwd: session.cwd,
|
|
650
|
+
// Status and approval share this exact full terminal identity.
|
|
651
|
+
...(session.agent === "codex"
|
|
652
|
+
? { conversationId: terminalId }
|
|
653
|
+
: { sessionId: session.sessionId }),
|
|
654
|
+
terminalTarget: terminalControl.target
|
|
655
|
+
}),
|
|
656
|
+
observeNativeIdentity: (observedTerminalId) => observeTerminalNativeListIdentity(session, terminalControl, options, bridge, observedTerminalId),
|
|
657
|
+
projectEffectiveState: ({ terminalState, native }) => effectiveTerminalListState({
|
|
658
|
+
session,
|
|
659
|
+
terminalState,
|
|
660
|
+
nativeIdentityObservation: native.nativeIdentityObservation,
|
|
661
|
+
nativeAgentIdentity: native.nativeAgentIdentity,
|
|
662
|
+
nativeIdentityAuthorityObservation: native.authorityNativeIdentityObservation,
|
|
663
|
+
codexOpenRootRolloutInventory: native.codexOpenRootRolloutInventory,
|
|
664
|
+
nativeProcessUuid: native.nativeProcessUuid,
|
|
665
|
+
nativeProcessBirth: native.nativeProcessBirth
|
|
666
|
+
}),
|
|
667
|
+
observeAgentVersion: () => terminalListRuntime().agentVersionForRunningProcess(session.agent, session.pid, options),
|
|
668
|
+
observeTerminalHasBlockingTurn: () => observeTerminalHasBlockingTurnForList(options, terminalControl),
|
|
669
|
+
observeComposer: (terminalState) => observeAutomatedInputComposerReady({
|
|
670
|
+
session,
|
|
671
|
+
terminalControl,
|
|
672
|
+
terminalState,
|
|
673
|
+
options
|
|
674
|
+
}),
|
|
675
|
+
observePhysicalProcessIncarnation: () => observePhysicalProcessIncarnationForList(session, terminalControl),
|
|
676
|
+
observeLatentClearResume: (request) => terminalListRuntime().codexLatentClearResumeObservation(request),
|
|
677
|
+
observeModelControlResidual: ({ agentVersion, modelControlCapability, modelControlProfile, native, zeroRolloutVerified, effectiveTerminalState, terminalHasInteraction, terminalHasBlockingTurn, hasOrphanedDispatch }) => observeModelControlResidualForList({
|
|
678
|
+
bridge,
|
|
679
|
+
session,
|
|
680
|
+
terminalControl,
|
|
681
|
+
agentVersion,
|
|
682
|
+
modelControlCapability,
|
|
683
|
+
modelControlProfile,
|
|
684
|
+
nativeAgentIdentity: native.nativeAgentIdentity,
|
|
685
|
+
zeroRolloutVerified,
|
|
686
|
+
nativeIdentityEligible: (session.agent === "codex" &&
|
|
687
|
+
isExactNativeThreadId(native.nativeAgentIdentity?.sessionId)) ||
|
|
688
|
+
zeroRolloutVerified,
|
|
689
|
+
effectiveTerminalState,
|
|
690
|
+
terminalHasInteraction,
|
|
691
|
+
terminalHasBlockingTurn,
|
|
692
|
+
hasOrphanedDispatch
|
|
693
|
+
}),
|
|
694
|
+
projectStatusSnapshot: terminalBridgeStatusWithProjection
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function observeTerminalHasBlockingTurnForList(options, terminalControl) {
|
|
698
|
+
try {
|
|
699
|
+
return terminalIncarnationBlockingTurns(terminalListRuntime().storeDirFromOptions(options), terminalControl).length > 0;
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
runtimeLog("warn", "terminal_managed_turn_inventory_unavailable", {
|
|
703
|
+
terminal_target: terminalControl.target,
|
|
704
|
+
error: error instanceof Error ? error.message : String(error)
|
|
705
|
+
});
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function observePhysicalProcessIncarnationForList(session, terminalControl) {
|
|
710
|
+
try {
|
|
711
|
+
return terminalListRuntime().processIncarnationForPid(session.pid);
|
|
712
|
+
}
|
|
713
|
+
catch (error) {
|
|
714
|
+
runtimeLog("warn", "terminal_physical_process_incarnation_unavailable", {
|
|
715
|
+
agent: session.agent,
|
|
716
|
+
terminal_target: terminalControl.target,
|
|
717
|
+
pid: session.pid,
|
|
718
|
+
error: error instanceof Error ? error.message : String(error)
|
|
719
|
+
});
|
|
720
|
+
return undefined;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
async function observeModelControlResidualForList(input) {
|
|
724
|
+
const inspect = input.bridge.inspectModelControlResidual;
|
|
725
|
+
const profile = input.modelControlProfile;
|
|
726
|
+
if (typeof inspect !== "function" ||
|
|
727
|
+
input.session.agent !== "codex" ||
|
|
728
|
+
!profile?.supportsResidualRepair ||
|
|
729
|
+
input.modelControlCapability.status !== "supported" ||
|
|
730
|
+
input.modelControlCapability.behaviorProfile !==
|
|
731
|
+
profile.behaviorProfile ||
|
|
732
|
+
!input.nativeIdentityEligible ||
|
|
733
|
+
input.effectiveTerminalState.activity_state === "working" ||
|
|
734
|
+
input.effectiveTerminalState.activity_state === "awaiting_approval" ||
|
|
735
|
+
input.effectiveTerminalState.approval_state.scanned !== true ||
|
|
736
|
+
input.effectiveTerminalState.approval_state.blocked === true ||
|
|
737
|
+
input.terminalHasInteraction ||
|
|
738
|
+
input.terminalHasBlockingTurn ||
|
|
739
|
+
input.hasOrphanedDispatch) {
|
|
740
|
+
return undefined;
|
|
741
|
+
}
|
|
742
|
+
const adapter = input.bridge.registry.require("codex");
|
|
743
|
+
const plan = input.modelControlCapability.status === "supported"
|
|
744
|
+
? adapter.planModelControl?.(input.modelControlCapability)
|
|
745
|
+
: undefined;
|
|
746
|
+
if (!plan || !terminalModelControlPlanConforms({
|
|
747
|
+
agent: input.session.agent,
|
|
748
|
+
agentVersion: input.agentVersion,
|
|
749
|
+
plan
|
|
750
|
+
})) {
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
const identity = input.nativeAgentIdentity;
|
|
754
|
+
const runtime = {
|
|
755
|
+
pid: input.session.pid,
|
|
756
|
+
agentVersion: input.agentVersion,
|
|
757
|
+
cwd: input.terminalControl.currentPath,
|
|
758
|
+
terminalTarget: input.terminalControl.target,
|
|
759
|
+
...(input.zeroRolloutVerified
|
|
760
|
+
? { expectedEmptyNativeSession: true }
|
|
761
|
+
: {}),
|
|
762
|
+
...(identity?.sessionId
|
|
763
|
+
? {
|
|
764
|
+
nativeSessionId: identity.sessionId,
|
|
765
|
+
expectedNativeSessionId: identity.sessionId
|
|
766
|
+
}
|
|
767
|
+
: {}),
|
|
768
|
+
...(identity?.processUuid
|
|
769
|
+
? { nativeProcessUuid: identity.processUuid }
|
|
770
|
+
: {}),
|
|
771
|
+
...(identity?.processBirth
|
|
772
|
+
? { nativeProcessBirth: identity.processBirth }
|
|
773
|
+
: {}),
|
|
774
|
+
...(identity?.rollout
|
|
775
|
+
? {
|
|
776
|
+
requireNativeRolloutIdentity: true,
|
|
777
|
+
nativeRollout: identity.rollout
|
|
778
|
+
}
|
|
779
|
+
: {})
|
|
913
780
|
};
|
|
781
|
+
try {
|
|
782
|
+
return await inspect.call(input.bridge, "codex", input.terminalControl, profile.agentVersion, plan, { runtime, beforeInput: () => undefined });
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
runtimeLog("warn", "terminal_model_control_residual_probe_unavailable", {
|
|
786
|
+
terminal_target: input.terminalControl.target,
|
|
787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
788
|
+
});
|
|
789
|
+
return undefined;
|
|
790
|
+
}
|
|
914
791
|
}
|
|
915
792
|
function effectiveTerminalListState(input) {
|
|
916
793
|
const { session, terminalState, nativeIdentityObservation, nativeAgentIdentity, nativeIdentityAuthorityObservation, codexOpenRootRolloutInventory, nativeProcessUuid, nativeProcessBirth } = input;
|
|
@@ -1073,7 +950,7 @@ function terminalBridgeStatusWithProjection(status, projection) {
|
|
|
1073
950
|
}
|
|
1074
951
|
return Object.create(Object.getPrototypeOf(status), descriptors);
|
|
1075
952
|
}
|
|
1076
|
-
async function observeTerminalNativeListIdentity(session, terminalControl, options, bridge) {
|
|
953
|
+
async function observeTerminalNativeListIdentity(session, terminalControl, options, bridge, terminalId) {
|
|
1077
954
|
let orphanedDispatch;
|
|
1078
955
|
try {
|
|
1079
956
|
orphanedDispatch =
|
|
@@ -1091,7 +968,7 @@ async function observeTerminalNativeListIdentity(session, terminalControl, optio
|
|
|
1091
968
|
codexIdentityContext = terminalListRuntime().codexManagedIdentityResolutionContext({
|
|
1092
969
|
storeDir: terminalListRuntime().storeDirFromOptions(options),
|
|
1093
970
|
terminal: {
|
|
1094
|
-
conversationId:
|
|
971
|
+
conversationId: terminalId,
|
|
1095
972
|
agent: session.agent,
|
|
1096
973
|
pid: session.pid,
|
|
1097
974
|
terminalControl
|
|
@@ -1262,1197 +1139,41 @@ async function observeAutomatedInputComposerReady({ session, terminalControl, te
|
|
|
1262
1139
|
userExplicitComposerReady: userExplicitReady
|
|
1263
1140
|
};
|
|
1264
1141
|
}
|
|
1265
|
-
function observeTerminalListBindingAuthority(listedTerminal, context) {
|
|
1266
|
-
const { storeDir, managedSessions, allConversations, displayedConversations, mutationsAllowed, nonterminalDeferredTransfers } = context;
|
|
1267
|
-
const { _automated_input_composer_ready: automatedInputComposerReady, _user_explicit_composer_ready: _userExplicitComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, _terminal_user_explicit_send_action: terminalUserExplicitSendAction, _terminal_status_snapshot: _terminalStatusSnapshot, _native_identity_authority: nativeIdentityAuthorityValue, ...publicTerminal } = listedTerminal;
|
|
1268
|
-
const terminal = terminalIdentityAuthorityView(listedTerminal, nativeIdentityAuthorityValue);
|
|
1269
|
-
const codexOpenRootRolloutInventory = isRecord(codexOpenRootRolloutInventoryValue)
|
|
1270
|
-
? codexOpenRootRolloutInventoryValue
|
|
1271
|
-
: undefined;
|
|
1272
|
-
const terminalControl = isRecord(terminal.terminal_control)
|
|
1273
|
-
? terminal.terminal_control
|
|
1274
|
-
: undefined;
|
|
1275
|
-
const terminalHasNonterminalDeferredTransfer = Boolean(terminalControl && nonterminalDeferredTransfers.some((transfer) => transfer.terminal_id === String(terminal.id) &&
|
|
1276
|
-
transfer.process_pid === Number(terminal.pid) &&
|
|
1277
|
-
terminalControlEvidenceMatches(transfer.terminal_endpoint, terminalControl)));
|
|
1278
|
-
const allRelated = terminalControl
|
|
1279
|
-
? allConversations.filter((conversation) => terminalControlsShareIncarnation(terminalControlForManagedConversation(conversation), terminalControl))
|
|
1280
|
-
: [];
|
|
1281
|
-
const displayedRelated = terminalControl
|
|
1282
|
-
? displayedConversations.filter((conversation) => terminalControlsShareIncarnation(terminalControlForManagedConversation(conversation), terminalControl))
|
|
1283
|
-
: [];
|
|
1284
|
-
const relatedSessions = terminalControl
|
|
1285
|
-
? managedSessions.filter((session) => terminalControlsShareIncarnation(session.binding?.terminal_control, terminalControl))
|
|
1286
|
-
: [];
|
|
1287
|
-
const matchingSessions = relatedSessions.filter((session) => managedSessionMatchesLiveTerminalEntry(session, terminal, storeDir));
|
|
1288
|
-
const conflictingBoundSessionClaims = relatedSessions.flatMap((session) => {
|
|
1289
|
-
const kind = managedBindingConflictKindForLiveTerminalEntry({
|
|
1290
|
-
storeDir,
|
|
1291
|
-
session,
|
|
1292
|
-
terminal
|
|
1293
|
-
});
|
|
1294
|
-
return kind && kind !== "stale_process_incarnation"
|
|
1295
|
-
? [{ session, kind }]
|
|
1296
|
-
: [];
|
|
1297
|
-
});
|
|
1298
|
-
const unresolvedSessionClaims = relatedSessions.filter((session) => ["transitioning", "quarantined"].includes(session.status) &&
|
|
1299
|
-
managedSessionClaimsLiveTerminalEntry(session, terminal));
|
|
1300
|
-
const sessionAuthorityConflict = decideTerminalSessionAuthorityConflict({
|
|
1301
|
-
unresolvedSessionClaims,
|
|
1302
|
-
conflictingBoundSessionClaims,
|
|
1303
|
-
matchingSessions
|
|
1304
|
-
});
|
|
1305
|
-
const authoritativeSession = matchingSessions[0];
|
|
1306
|
-
const discoveredOwnership = terminalControl
|
|
1307
|
-
? terminalDispatchOwnership(terminalControl)
|
|
1308
|
-
: { state: "none" };
|
|
1309
|
-
const localOwnership = discoveredOwnership.state === "current"
|
|
1310
|
-
? localTerminalDispatchOwnership(discoveredOwnership.conversation, allRelated, terminal)
|
|
1311
|
-
: discoveredOwnership;
|
|
1312
|
-
const dispatchOwnerMismatch = !sessionAuthorityConflict &&
|
|
1313
|
-
localOwnership.state === "current" &&
|
|
1314
|
-
authoritativeSession &&
|
|
1315
|
-
sessionIdForConversation(localOwnership.conversation) !==
|
|
1316
|
-
authoritativeSession.session_id
|
|
1317
|
-
? {
|
|
1318
|
-
ownerSessionId: sessionIdForConversation(discoveredOwnership.state === "current"
|
|
1319
|
-
? discoveredOwnership.conversation
|
|
1320
|
-
: localOwnership.conversation)
|
|
1321
|
-
}
|
|
1322
|
-
: undefined;
|
|
1323
|
-
const ownership = applySessionAuthorityToDispatch({
|
|
1324
|
-
localOwnership,
|
|
1325
|
-
sessionAuthorityConflict,
|
|
1326
|
-
authoritativeSession,
|
|
1327
|
-
dispatchOwnerMismatch
|
|
1328
|
-
});
|
|
1329
|
-
const discoveredRawActions = isRecord(terminal.available_actions)
|
|
1330
|
-
? terminal.available_actions
|
|
1331
|
-
: {};
|
|
1332
|
-
const rawActions = mutationsAllowed
|
|
1333
|
-
? discoveredRawActions
|
|
1334
|
-
: readOnlyListActions(discoveredRawActions);
|
|
1335
|
-
const rawSendAction = isRecord(rawActions.send)
|
|
1336
|
-
? rawActions.send
|
|
1337
|
-
: {};
|
|
1338
|
-
const bindingAwareRawActions = authoritativeSession
|
|
1339
|
-
? actionsForManagedSessionBinding(rawActions, authoritativeSession)
|
|
1340
|
-
: rawActions;
|
|
1341
|
-
const sessionAwareRawActionsBase = authoritativeSession &&
|
|
1342
|
-
managedSessionHasUnresolvedNativeTransition(storeDir, authoritativeSession)
|
|
1343
|
-
? Object.fromEntries(Object.entries(bindingAwareRawActions).filter(([actionName]) => actionName !== "native_inspect"))
|
|
1344
|
-
: bindingAwareRawActions;
|
|
1345
|
-
const sessionAwareRawActions = terminalHasNonterminalDeferredTransfer
|
|
1346
|
-
? readOnlyListActions(sessionAwareRawActionsBase)
|
|
1347
|
-
: sessionAwareRawActionsBase;
|
|
1348
|
-
const soleBindingConflict = conflictingBoundSessionClaims.length === 1
|
|
1349
|
-
? conflictingBoundSessionClaims[0]
|
|
1350
|
-
: undefined;
|
|
1351
|
-
const externalHandoffDetected = conflictingBoundSessionClaims.some(({ kind }) => kind === "live_external_thread_change");
|
|
1352
|
-
const conflictingSessionRevision = Number(soleBindingConflict?.session.revision);
|
|
1353
|
-
const conflictingSessionTurns = soleBindingConflict
|
|
1354
|
-
? terminalListRuntime().managedTurnsForSession(storeDir, soleBindingConflict.session.session_id)
|
|
1355
|
-
: [];
|
|
1356
|
-
const expectedTerminalToken = stringValue(terminal.lifecycle_binding_token);
|
|
1357
|
-
const externalHandoffNativeThreadId = stringValue(terminal.native_agent_status_card_session_id) ?? stringValue(terminal.native_agent_session_id);
|
|
1358
|
-
const resolvedNativeThreadId = stringValue(terminal.native_agent_session_id);
|
|
1359
|
-
const externalHandoffTerminalToken = terminalControl &&
|
|
1360
|
-
externalHandoffNativeThreadId &&
|
|
1361
|
-
isExactNativeThreadId(externalHandoffNativeThreadId)
|
|
1362
|
-
? unmanagedTerminalBindingToken({
|
|
1363
|
-
terminalId: stringValue(terminal.id),
|
|
1364
|
-
terminalControl,
|
|
1365
|
-
agent: terminal.agent,
|
|
1366
|
-
pid: Number(terminal.pid),
|
|
1367
|
-
workspace: terminal.workspace ?? terminal.cwd ?? cliCwd(),
|
|
1368
|
-
nativeThreadId: externalHandoffNativeThreadId,
|
|
1369
|
-
processUuid: stringValue(terminal.native_agent_process_uuid),
|
|
1370
|
-
processBirth: stringValue(terminal.native_agent_process_birth),
|
|
1371
|
-
rollout: resolvedNativeThreadId === externalHandoffNativeThreadId &&
|
|
1372
|
-
isRecord(terminal.native_agent_rollout)
|
|
1373
|
-
? terminal.native_agent_rollout
|
|
1374
|
-
: undefined
|
|
1375
|
-
})
|
|
1376
|
-
: undefined;
|
|
1377
|
-
const externalHandoffTarget = soleBindingConflict?.kind === "live_external_thread_change" &&
|
|
1378
|
-
externalHandoffNativeThreadId &&
|
|
1379
|
-
isExactNativeThreadId(externalHandoffNativeThreadId)
|
|
1380
|
-
? terminalListRuntime().observedHandoffTargetResolution({
|
|
1381
|
-
storeDir,
|
|
1382
|
-
agent: terminal.agent,
|
|
1383
|
-
workspace: terminal.workspace ?? terminal.cwd ?? cliCwd(),
|
|
1384
|
-
nativeThreadId: externalHandoffNativeThreadId.toLowerCase(),
|
|
1385
|
-
sourceSessionId: soleBindingConflict.session.session_id
|
|
1386
|
-
})
|
|
1387
|
-
: undefined;
|
|
1388
|
-
const externalHandoffSnapshotToken = externalHandoffTerminalToken &&
|
|
1389
|
-
soleBindingConflict?.kind === "live_external_thread_change" &&
|
|
1390
|
-
externalHandoffTarget?.status === "eligible"
|
|
1391
|
-
? humanObservedHandoffBindingToken({
|
|
1392
|
-
terminal_token: externalHandoffTerminalToken,
|
|
1393
|
-
source_session_id: soleBindingConflict.session.session_id,
|
|
1394
|
-
source_revision: managedSessionRevision(soleBindingConflict.session),
|
|
1395
|
-
source_binding_token: managedSessionBindingToken(soleBindingConflict.session),
|
|
1396
|
-
target: externalHandoffTarget.snapshot
|
|
1397
|
-
})
|
|
1398
|
-
: undefined;
|
|
1399
|
-
const blockingHandoffTurns = conflictingSessionTurns.filter((turn) => isSessionSendBlockingStatus(turn.status));
|
|
1400
|
-
const terminalBlockingTurns = terminalControl
|
|
1401
|
-
? terminalIncarnationBlockingTurns(storeDir, terminalControl, allRelated)
|
|
1402
|
-
: [];
|
|
1403
|
-
const externalHandoffSourceSessionIds = new Set(conflictingBoundSessionClaims
|
|
1404
|
-
.filter(({ kind }) => kind === "live_external_thread_change")
|
|
1405
|
-
.map(({ session }) => session.session_id));
|
|
1406
|
-
const handoffSourceBlockingTurns = terminalBlockingTurns.filter((turn) => externalHandoffSourceSessionIds.has(sessionIdForConversation(turn)));
|
|
1407
|
-
const nativeIdentityObservation = isRecord(terminal.native_agent_identity_observation)
|
|
1408
|
-
? terminal.native_agent_identity_observation
|
|
1409
|
-
: undefined;
|
|
1410
|
-
const codexProcessUuid = stringValue(terminal.native_agent_process_uuid);
|
|
1411
|
-
const codexProcessBirth = stringValue(terminal.native_agent_process_birth);
|
|
1412
|
-
const codexWorkspace = stringValue(terminal.workspace ?? terminal.cwd);
|
|
1413
|
-
const codexSendAuthorityContext = terminalControl
|
|
1414
|
-
? {
|
|
1415
|
-
terminalId: String(terminal.id),
|
|
1416
|
-
terminalControl,
|
|
1417
|
-
pid: Number(terminal.pid),
|
|
1418
|
-
workspace: codexWorkspace,
|
|
1419
|
-
liveProcessUuid: codexProcessUuid,
|
|
1420
|
-
liveProcessBirth: codexProcessBirth
|
|
1421
|
-
}
|
|
1422
|
-
: undefined;
|
|
1423
|
-
return {
|
|
1424
|
-
authorityTerminal: terminal,
|
|
1425
|
-
automatedInputComposerReady,
|
|
1426
|
-
codexOpenRootRolloutInventory,
|
|
1427
|
-
codexLatentClearResumeValue,
|
|
1428
|
-
terminalUserExplicitSendAction,
|
|
1429
|
-
publicTerminal,
|
|
1430
|
-
terminalControl,
|
|
1431
|
-
terminalHasNonterminalDeferredTransfer,
|
|
1432
|
-
allRelated,
|
|
1433
|
-
displayedRelated,
|
|
1434
|
-
relatedSessions,
|
|
1435
|
-
matchingSessions,
|
|
1436
|
-
conflictingBoundSessionClaims,
|
|
1437
|
-
unresolvedSessionClaims,
|
|
1438
|
-
sessionAuthorityConflict,
|
|
1439
|
-
authoritativeSession,
|
|
1440
|
-
discoveredOwnership,
|
|
1441
|
-
ownership,
|
|
1442
|
-
rawActions,
|
|
1443
|
-
rawSendAction,
|
|
1444
|
-
sessionAwareRawActions,
|
|
1445
|
-
soleBindingConflict,
|
|
1446
|
-
externalHandoffDetected,
|
|
1447
|
-
conflictingSessionRevision,
|
|
1448
|
-
conflictingSessionTurns,
|
|
1449
|
-
expectedTerminalToken,
|
|
1450
|
-
externalHandoffNativeThreadId,
|
|
1451
|
-
externalHandoffTarget,
|
|
1452
|
-
externalHandoffSnapshotToken,
|
|
1453
|
-
blockingHandoffTurns,
|
|
1454
|
-
terminalBlockingTurns,
|
|
1455
|
-
handoffSourceBlockingTurns,
|
|
1456
|
-
nativeIdentityObservation,
|
|
1457
|
-
codexProcessUuid,
|
|
1458
|
-
codexProcessBirth,
|
|
1459
|
-
codexWorkspace,
|
|
1460
|
-
codexSendAuthorityContext
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
function terminalIdentityAuthorityView(terminal, authorityValue) {
|
|
1464
|
-
if (!isRecord(authorityValue)) {
|
|
1465
|
-
return terminal;
|
|
1466
|
-
}
|
|
1467
|
-
const observation = isRecord(authorityValue.observation)
|
|
1468
|
-
? authorityValue.observation
|
|
1469
|
-
: { status: "not_observed" };
|
|
1470
|
-
const identity = isRecord(authorityValue.identity)
|
|
1471
|
-
? authorityValue.identity
|
|
1472
|
-
: undefined;
|
|
1473
|
-
return {
|
|
1474
|
-
...terminal,
|
|
1475
|
-
native_agent_session_id: stringValue(identity?.sessionId),
|
|
1476
|
-
native_agent_rollout: isRecord(identity?.rollout)
|
|
1477
|
-
? identity.rollout
|
|
1478
|
-
: undefined,
|
|
1479
|
-
native_agent_identity_observation: observation,
|
|
1480
|
-
lifecycle_binding_token: stringValue(authorityValue.lifecycle_binding_token) ??
|
|
1481
|
-
terminal.lifecycle_binding_token
|
|
1482
|
-
};
|
|
1483
|
-
}
|
|
1484
|
-
function observeVerifiedEmptyTerminalAuthority(terminal, context, binding) {
|
|
1485
|
-
const { storeDir, terminals, mutationsAllowed } = context;
|
|
1486
|
-
const { automatedInputComposerReady, terminalControl, matchingSessions, unresolvedSessionClaims, discoveredOwnership, rawActions, soleBindingConflict, blockingHandoffTurns, terminalBlockingTurns, nativeIdentityObservation, codexProcessUuid, codexProcessBirth, codexWorkspace, codexSendAuthorityContext } = binding;
|
|
1487
|
-
const verifiedEmptySourceNativeThreadId = stringValue(soleBindingConflict?.session.binding?.native_thread_id)?.toLowerCase();
|
|
1488
|
-
const verifiedEmptySourceActiveElsewhere = Boolean(verifiedEmptySourceNativeThreadId &&
|
|
1489
|
-
terminals.some((candidate) => stringValue(candidate.id) !== stringValue(terminal.id) &&
|
|
1490
|
-
candidate.agent === "codex" &&
|
|
1491
|
-
stringValue(candidate.native_agent_session_id)?.toLowerCase() ===
|
|
1492
|
-
verifiedEmptySourceNativeThreadId));
|
|
1493
|
-
const verifiedEmptyRawSendAction = isRecord(rawActions.send)
|
|
1494
|
-
? rawActions.send
|
|
1495
|
-
: {
|
|
1496
|
-
tool: "agent_knock_knock_send",
|
|
1497
|
-
arguments: { selector: stringValue(terminal.id) },
|
|
1498
|
-
missing_required: ["request"]
|
|
1499
|
-
};
|
|
1500
|
-
const verifiedEmptyCodexHandoffEligible = Boolean(mutationsAllowed &&
|
|
1501
|
-
terminal.agent === "codex" &&
|
|
1502
|
-
terminalControl &&
|
|
1503
|
-
discoveredOwnership.state === "none" &&
|
|
1504
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1505
|
-
matchingSessions.length === 0 &&
|
|
1506
|
-
soleBindingConflict?.kind === "unverifiable" &&
|
|
1507
|
-
nativeIdentityObservation?.status === "verified_absent" &&
|
|
1508
|
-
codexSendAuthorityContext &&
|
|
1509
|
-
codexProcessUuid &&
|
|
1510
|
-
codexProcessBirth &&
|
|
1511
|
-
codexWorkspace &&
|
|
1512
|
-
exactBoundCodexSendSource({
|
|
1513
|
-
kind: "verified_empty",
|
|
1514
|
-
sourceSession: soleBindingConflict.session,
|
|
1515
|
-
context: codexSendAuthorityContext
|
|
1516
|
-
}) &&
|
|
1517
|
-
["idle", "unknown"].includes(String(terminal.activity_state)) &&
|
|
1518
|
-
automatedInputComposerReady === true &&
|
|
1519
|
-
!(isRecord(terminal.approval_state) &&
|
|
1520
|
-
terminal.approval_state.blocked === true) &&
|
|
1521
|
-
blockingHandoffTurns.length === 0 &&
|
|
1522
|
-
terminalBlockingTurns.length === 0 &&
|
|
1523
|
-
!managedSessionHasUnresolvedNativeTransition(storeDir, soleBindingConflict.session) &&
|
|
1524
|
-
!verifiedEmptySourceActiveElsewhere &&
|
|
1525
|
-
terminal.orphaned_terminal_dispatch === undefined &&
|
|
1526
|
-
terminalControl?.capabilities.includes("send_keys") &&
|
|
1527
|
-
terminalControl.capabilities.includes("screen_status"));
|
|
1528
|
-
const verifiedEmptyCodexSnapshotToken = verifiedEmptyCodexHandoffEligible &&
|
|
1529
|
-
codexSendAuthorityContext &&
|
|
1530
|
-
soleBindingConflict &&
|
|
1531
|
-
codexProcessUuid &&
|
|
1532
|
-
codexProcessBirth &&
|
|
1533
|
-
codexWorkspace
|
|
1534
|
-
? verifiedEmptyCodexHandoffToken({
|
|
1535
|
-
terminalId: String(terminal.id),
|
|
1536
|
-
terminalControl: codexSendAuthorityContext.terminalControl,
|
|
1537
|
-
pid: Number(terminal.pid),
|
|
1538
|
-
workspace: codexWorkspace,
|
|
1539
|
-
processUuid: codexProcessUuid,
|
|
1540
|
-
processBirth: codexProcessBirth,
|
|
1541
|
-
sourceSession: soleBindingConflict.session
|
|
1542
|
-
})
|
|
1543
|
-
: undefined;
|
|
1544
|
-
return {
|
|
1545
|
-
...binding,
|
|
1546
|
-
verifiedEmptyRawSendAction,
|
|
1547
|
-
verifiedEmptyCodexSnapshotToken
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
function observeDeferredSourceAuthority(terminal, context, observation) {
|
|
1551
|
-
const { terminals } = context;
|
|
1552
|
-
const { codexLatentClearResumeValue, codexOpenRootRolloutInventory, authoritativeSession, matchingSessions, conflictingBoundSessionClaims, unresolvedSessionClaims, soleBindingConflict, nativeIdentityObservation } = observation;
|
|
1553
|
-
const deferredCodexCandidateInventory = codexOpenRootRolloutInventory &&
|
|
1554
|
-
codexOpenRootRolloutInventory.roots.length > 0
|
|
1555
|
-
? codexOpenRootRolloutInventory
|
|
1556
|
-
: undefined;
|
|
1557
|
-
const abandonedConflictSource = !authoritativeSession &&
|
|
1558
|
-
soleBindingConflict?.kind === "unverifiable" &&
|
|
1559
|
-
matchingSessions.length === 0 &&
|
|
1560
|
-
conflictingBoundSessionClaims.length === 1 &&
|
|
1561
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1562
|
-
terminal.agent === "codex"
|
|
1563
|
-
? soleBindingConflict.session
|
|
1564
|
-
: undefined;
|
|
1565
|
-
const deferredCodexSource = (authoritativeSession ??
|
|
1566
|
-
abandonedConflictSource) &&
|
|
1567
|
-
terminal.agent === "codex" &&
|
|
1568
|
-
(nativeIdentityObservation?.status === "verified_absent" ||
|
|
1569
|
-
deferredCodexCandidateInventory !== undefined)
|
|
1570
|
-
? authoritativeSession ?? abandonedConflictSource
|
|
1571
|
-
: undefined;
|
|
1572
|
-
const deferredCodexSourceNativeThreadId = stringValue(deferredCodexSource?.binding?.native_thread_id)?.toLowerCase();
|
|
1573
|
-
const deferredCodexLatentClearResumeFingerprint = isRecord(codexLatentClearResumeValue) &&
|
|
1574
|
-
stringValue(codexLatentClearResumeValue.source_native_thread_id)?.toLowerCase() === deferredCodexSourceNativeThreadId
|
|
1575
|
-
? stringValue(codexLatentClearResumeValue.fingerprint)
|
|
1576
|
-
: undefined;
|
|
1577
|
-
if (deferredCodexLatentClearResumeFingerprint) {
|
|
1578
|
-
// The resume hint is useful operational context, but it is not durable
|
|
1579
|
-
// foreground authority: it can scroll away while the latent thread is
|
|
1580
|
-
// still current. Candidate routing and its token rely on the complete
|
|
1581
|
-
// rollout inventory and Store authority below instead.
|
|
1582
|
-
runtimeLog("info", "terminal_codex_latent_clear_hint_observed", {
|
|
1583
|
-
terminal_id: String(terminal.id),
|
|
1584
|
-
source_session_id: deferredCodexSource?.session_id,
|
|
1585
|
-
source_native_thread_id: deferredCodexSourceNativeThreadId
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
1588
|
-
const deferredCodexSourceActiveElsewhere = Boolean(deferredCodexSourceNativeThreadId &&
|
|
1589
|
-
terminals.some((candidate) => stringValue(candidate.id) !== stringValue(terminal.id) &&
|
|
1590
|
-
candidate.agent === "codex" &&
|
|
1591
|
-
stringValue(candidate.native_agent_session_id)?.toLowerCase() ===
|
|
1592
|
-
deferredCodexSourceNativeThreadId));
|
|
1593
|
-
return {
|
|
1594
|
-
...observation,
|
|
1595
|
-
deferredCodexCandidateInventory,
|
|
1596
|
-
abandonedConflictSource,
|
|
1597
|
-
deferredCodexSource,
|
|
1598
|
-
deferredCodexSourceActiveElsewhere
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
function observeDeferredTerminalAuthority(terminal, context, observation) {
|
|
1602
|
-
const { storeDir, mutationsAllowed } = context;
|
|
1603
|
-
const { automatedInputComposerReady, terminalControl, terminalHasNonterminalDeferredTransfer, matchingSessions, conflictingBoundSessionClaims, unresolvedSessionClaims, discoveredOwnership, terminalBlockingTurns, codexProcessUuid, codexProcessBirth, codexWorkspace, codexSendAuthorityContext, deferredCodexCandidateInventory, abandonedConflictSource, deferredCodexSource, deferredCodexSourceActiveElsewhere } = observation;
|
|
1604
|
-
const deferredCodexAuthority = codexSendAuthorityContext
|
|
1605
|
-
? terminalListRuntime().observeDeferredCodexAuthority({
|
|
1606
|
-
mode: "list",
|
|
1607
|
-
storeDir,
|
|
1608
|
-
context: codexSendAuthorityContext,
|
|
1609
|
-
sourceSession: deferredCodexSource,
|
|
1610
|
-
candidateInventory: deferredCodexCandidateInventory,
|
|
1611
|
-
abandonment: abandonedConflictSource
|
|
1612
|
-
? "missing_rollout"
|
|
1613
|
-
: "never",
|
|
1614
|
-
requireUnclaimedCandidate: true
|
|
1615
|
-
})
|
|
1616
|
-
: undefined;
|
|
1617
|
-
const deferredCodexSourceRolloutAuthority = deferredCodexAuthority?.sourceRolloutAuthority ?? "present";
|
|
1618
|
-
const deferredCodexDispatchSnapshot = deferredCodexAuthority?.dispatchSnapshot;
|
|
1619
|
-
const deferredCodexForegroundEligible = Boolean(mutationsAllowed &&
|
|
1620
|
-
!terminalHasNonterminalDeferredTransfer &&
|
|
1621
|
-
deferredCodexSource &&
|
|
1622
|
-
terminalControl &&
|
|
1623
|
-
hasCanonicalTerminalEndpoint(terminalControl) &&
|
|
1624
|
-
discoveredOwnership.state === "none" &&
|
|
1625
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1626
|
-
((matchingSessions.length === 1 &&
|
|
1627
|
-
conflictingBoundSessionClaims.length === 0) ||
|
|
1628
|
-
(deferredCodexSourceRolloutAuthority ===
|
|
1629
|
-
"explicitly_abandoned_predecessor" &&
|
|
1630
|
-
matchingSessions.length === 0 &&
|
|
1631
|
-
conflictingBoundSessionClaims.length === 1)) &&
|
|
1632
|
-
codexProcessUuid &&
|
|
1633
|
-
codexProcessBirth &&
|
|
1634
|
-
codexWorkspace &&
|
|
1635
|
-
deferredCodexAuthority?.exactSource &&
|
|
1636
|
-
terminalBlockingTurns.length === 0 &&
|
|
1637
|
-
terminal.orphaned_terminal_dispatch === undefined &&
|
|
1638
|
-
deferredCodexDispatchSnapshot &&
|
|
1639
|
-
["idle", "unknown"].includes(String(terminal.activity_state)) &&
|
|
1640
|
-
automatedInputComposerReady === true &&
|
|
1641
|
-
!(isRecord(terminal.approval_state) &&
|
|
1642
|
-
terminal.approval_state.blocked === true) &&
|
|
1643
|
-
!deferredCodexSourceActiveElsewhere &&
|
|
1644
|
-
terminalControl.capabilities.includes("send_keys") &&
|
|
1645
|
-
terminalControl.capabilities.includes("screen_status"));
|
|
1646
|
-
const deferredCodexForegroundToken = deferredCodexForegroundEligible &&
|
|
1647
|
-
deferredCodexSource &&
|
|
1648
|
-
terminalControl &&
|
|
1649
|
-
codexProcessUuid &&
|
|
1650
|
-
codexProcessBirth &&
|
|
1651
|
-
codexWorkspace &&
|
|
1652
|
-
deferredCodexDispatchSnapshot
|
|
1653
|
-
? deferredCodexForegroundBindingToken({
|
|
1654
|
-
terminalId: String(terminal.id),
|
|
1655
|
-
terminalControl,
|
|
1656
|
-
pid: Number(terminal.pid),
|
|
1657
|
-
workspace: codexWorkspace,
|
|
1658
|
-
processUuid: codexProcessUuid,
|
|
1659
|
-
processBirth: codexProcessBirth,
|
|
1660
|
-
sourceSession: deferredCodexSource,
|
|
1661
|
-
dispatchSnapshot: deferredCodexDispatchSnapshot,
|
|
1662
|
-
sourceTurnHistory: deferredCodexAuthority?.sourceTurnHistory,
|
|
1663
|
-
sourceRolloutAuthority: deferredCodexSourceRolloutAuthority,
|
|
1664
|
-
sourceAbandonmentFingerprint: deferredCodexAuthority?.sourceAbandonmentFingerprint,
|
|
1665
|
-
...(deferredCodexCandidateInventory
|
|
1666
|
-
? { candidateInventory: deferredCodexCandidateInventory }
|
|
1667
|
-
: {})
|
|
1668
|
-
})
|
|
1669
|
-
: undefined;
|
|
1670
|
-
return {
|
|
1671
|
-
...observation,
|
|
1672
|
-
deferredCodexSourceRolloutAuthority,
|
|
1673
|
-
deferredCodexForegroundToken
|
|
1674
|
-
};
|
|
1675
|
-
}
|
|
1676
|
-
function observeTerminalHandoffAuthority(terminal, context, observation) {
|
|
1677
|
-
const { storeDir, mutationsAllowed, conversationHasNonterminalDeferredTransfer } = context;
|
|
1678
|
-
const { automatedInputComposerReady, terminalControl, matchingSessions, unresolvedSessionClaims, discoveredOwnership, rawActions, soleBindingConflict, conflictingSessionRevision, conflictingSessionTurns, expectedTerminalToken, externalHandoffNativeThreadId, externalHandoffTarget, externalHandoffSnapshotToken, blockingHandoffTurns, terminalBlockingTurns, handoffSourceBlockingTurns } = observation;
|
|
1679
|
-
const reconcileBindingAction = mutationsAllowed &&
|
|
1680
|
-
discoveredOwnership.state === "none" &&
|
|
1681
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1682
|
-
matchingSessions.length === 0 &&
|
|
1683
|
-
soleBindingConflict &&
|
|
1684
|
-
soleBindingConflict.kind !== "unverifiable" &&
|
|
1685
|
-
Number.isSafeInteger(conflictingSessionRevision) &&
|
|
1686
|
-
conflictingSessionRevision > 0 &&
|
|
1687
|
-
expectedTerminalToken &&
|
|
1688
|
-
terminal.activity_state === "idle" &&
|
|
1689
|
-
!(isRecord(terminal.approval_state) &&
|
|
1690
|
-
terminal.approval_state.blocked === true) &&
|
|
1691
|
-
!conflictingSessionTurns.some((turn) => isSessionSendBlockingStatus(turn.status)) &&
|
|
1692
|
-
terminalBlockingTurns.length === 0 &&
|
|
1693
|
-
!managedSessionHasUnresolvedNativeTransition(storeDir, soleBindingConflict.session)
|
|
1694
|
-
? projectReconcileBindingAction({
|
|
1695
|
-
terminalId: stringValue(terminal.id),
|
|
1696
|
-
conflictingSession: soleBindingConflict.session,
|
|
1697
|
-
conflictingSessionRevision,
|
|
1698
|
-
expectedTerminalToken
|
|
1699
|
-
})
|
|
1700
|
-
: undefined;
|
|
1701
|
-
const externalHandoffAdoptable = Boolean(mutationsAllowed &&
|
|
1702
|
-
discoveredOwnership.state === "none" &&
|
|
1703
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1704
|
-
soleBindingConflict?.kind === "live_external_thread_change" &&
|
|
1705
|
-
terminal.activity_state === "idle" &&
|
|
1706
|
-
automatedInputComposerReady === true &&
|
|
1707
|
-
!(isRecord(terminal.approval_state) &&
|
|
1708
|
-
terminal.approval_state.blocked === true) &&
|
|
1709
|
-
!conflictingSessionTurns.some((turn) => isSessionSendBlockingStatus(turn.status)) &&
|
|
1710
|
-
terminalBlockingTurns.length === 0 &&
|
|
1711
|
-
!managedSessionHasUnresolvedNativeTransition(storeDir, soleBindingConflict.session) &&
|
|
1712
|
-
externalHandoffTarget?.status === "eligible" &&
|
|
1713
|
-
isRecord(rawActions.send) &&
|
|
1714
|
-
Boolean(externalHandoffSnapshotToken));
|
|
1715
|
-
const handoffDecisionTurn = mutationsAllowed &&
|
|
1716
|
-
soleBindingConflict?.kind === "live_external_thread_change" &&
|
|
1717
|
-
externalHandoffTarget?.status === "eligible" &&
|
|
1718
|
-
terminal.activity_state === "idle" &&
|
|
1719
|
-
!(isRecord(terminal.approval_state) &&
|
|
1720
|
-
terminal.approval_state.blocked === true) &&
|
|
1721
|
-
!managedSessionHasUnresolvedNativeTransition(storeDir, soleBindingConflict.session) &&
|
|
1722
|
-
blockingHandoffTurns.length === 1 &&
|
|
1723
|
-
terminalBlockingTurns.every((turn) => turn.conversation_id === blockingHandoffTurns[0].conversation_id)
|
|
1724
|
-
? blockingHandoffTurns[0]
|
|
1725
|
-
: undefined;
|
|
1726
|
-
const handoffDecisionToken = handoffDecisionTurn &&
|
|
1727
|
-
externalHandoffSnapshotToken &&
|
|
1728
|
-
terminalControl
|
|
1729
|
-
? terminalListRuntime().activeTurnHandoffDecisionToken({
|
|
1730
|
-
handoffToken: externalHandoffSnapshotToken,
|
|
1731
|
-
turn: handoffDecisionTurn,
|
|
1732
|
-
ledger: terminalListRuntime().loadTerminalBridgeDispatchLedger(terminalControl)
|
|
1733
|
-
})
|
|
1734
|
-
: undefined;
|
|
1735
|
-
const handoffDecision = handoffDecisionTurn &&
|
|
1736
|
-
handoffDecisionToken &&
|
|
1737
|
-
externalHandoffNativeThreadId
|
|
1738
|
-
? projectHandoffDecision({
|
|
1739
|
-
sourceSessionId: soleBindingConflict?.session.session_id,
|
|
1740
|
-
sourceTurnId: turnIdForConversation(handoffDecisionTurn),
|
|
1741
|
-
liveNativeThreadId: externalHandoffNativeThreadId,
|
|
1742
|
-
handoffDecisionToken,
|
|
1743
|
-
actionTurnId: turnIdForConversation(handoffDecisionTurn)
|
|
1744
|
-
})
|
|
1745
|
-
: undefined;
|
|
1746
|
-
const blockingHandoffTurnIds = new Set(handoffSourceBlockingTurns.map((turn) => turn.conversation_id));
|
|
1747
|
-
// A blocking managed Turn always remains explicitly closable. Close releases
|
|
1748
|
-
// AKK management only; it does not send input or stop the coding agent.
|
|
1749
|
-
const terminalRecoveryBlockingTurns = terminalBlockingTurns;
|
|
1750
|
-
return {
|
|
1751
|
-
...observation,
|
|
1752
|
-
reconcileBindingAction,
|
|
1753
|
-
externalHandoffAdoptable,
|
|
1754
|
-
handoffDecision,
|
|
1755
|
-
blockingHandoffTurnIds,
|
|
1756
|
-
terminalRecoveryBlockingTurns
|
|
1757
|
-
};
|
|
1758
|
-
}
|
|
1759
|
-
function observeTerminalScopedApprovalAuthority(terminal, context, observation) {
|
|
1760
|
-
const { storeDir, mutationsAllowed } = context;
|
|
1761
|
-
const { terminalControl, terminalHasNonterminalDeferredTransfer, conflictingBoundSessionClaims, unresolvedSessionClaims, sessionAuthorityConflict, authoritativeSession, discoveredOwnership, ownership, rawActions } = observation;
|
|
1762
|
-
let terminalScopedCodexApprovalAction;
|
|
1763
|
-
const terminalScopedCodexApprovalPrompt = terminalScopedCodexApprovalPromptSnapshot(terminal.approval_state);
|
|
1764
|
-
if (mutationsAllowed &&
|
|
1765
|
-
terminal.agent === "codex" &&
|
|
1766
|
-
terminalControl &&
|
|
1767
|
-
hasCanonicalTerminalEndpoint(terminalControl) &&
|
|
1768
|
-
authoritativeSession &&
|
|
1769
|
-
!sessionAuthorityConflict &&
|
|
1770
|
-
!terminalHasNonterminalDeferredTransfer &&
|
|
1771
|
-
unresolvedSessionClaims.length === 0 &&
|
|
1772
|
-
conflictingBoundSessionClaims.length === 0 &&
|
|
1773
|
-
terminalScopedCodexApprovalPrompt &&
|
|
1774
|
-
isRecord(rawActions.approve)) {
|
|
1775
|
-
try {
|
|
1776
|
-
const ledger = terminalListRuntime().loadTerminalBridgeDispatchLedger(terminalControl);
|
|
1777
|
-
const boundary = ownership.state === "conflict" &&
|
|
1778
|
-
discoveredOwnership.state === "current" &&
|
|
1779
|
-
ledger
|
|
1780
|
-
? terminalScopedCodexApprovalBoundary({
|
|
1781
|
-
storeDir,
|
|
1782
|
-
terminal,
|
|
1783
|
-
owner: discoveredOwnership.conversation,
|
|
1784
|
-
session: authoritativeSession,
|
|
1785
|
-
ledger,
|
|
1786
|
-
approval: terminalScopedCodexApprovalPrompt
|
|
1787
|
-
})
|
|
1788
|
-
: ownership.state === "none" &&
|
|
1789
|
-
discoveredOwnership.state === "none"
|
|
1790
|
-
? terminalScopedCodexApprovalBoundary({
|
|
1791
|
-
storeDir,
|
|
1792
|
-
terminal,
|
|
1793
|
-
session: authoritativeSession,
|
|
1794
|
-
ledger,
|
|
1795
|
-
approval: terminalScopedCodexApprovalPrompt
|
|
1796
|
-
})
|
|
1797
|
-
: undefined;
|
|
1798
|
-
if (!boundary) {
|
|
1799
|
-
throw new Error("terminal-scoped Codex approval has no eligible managed authority");
|
|
1800
|
-
}
|
|
1801
|
-
terminalScopedCodexApprovalAction = {
|
|
1802
|
-
...rawActions.approve,
|
|
1803
|
-
arguments: {
|
|
1804
|
-
conversation_id: String(terminal.id),
|
|
1805
|
-
expected_terminal_token: boundary.token
|
|
1806
|
-
},
|
|
1807
|
-
scope: "terminal_current_prompt",
|
|
1808
|
-
authority: boundary.authority.kind,
|
|
1809
|
-
managed_state_unchanged: true,
|
|
1810
|
-
automatic_approval_eligible: false,
|
|
1811
|
-
durable_dispatch_receipt: false,
|
|
1812
|
-
uncertain_outcome_recovery: "refresh status and inspect the live prompt; do not retry blindly"
|
|
1813
|
-
};
|
|
1814
|
-
}
|
|
1815
|
-
catch (error) {
|
|
1816
|
-
runtimeLog("info", "terminal_scoped_codex_approval_not_advertised", {
|
|
1817
|
-
terminal_id: String(terminal.id),
|
|
1818
|
-
terminal_target: terminalControl.target,
|
|
1819
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
1820
|
-
});
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
const rolloutBackedCodexSession = Boolean(authoritativeSession?.agent === "codex" &&
|
|
1824
|
-
isCompleteNativeRollout(authoritativeSession.binding?.native_process.rollout));
|
|
1825
|
-
return {
|
|
1826
|
-
...observation,
|
|
1827
|
-
terminalScopedCodexApprovalAction,
|
|
1828
|
-
rolloutBackedCodexSession
|
|
1829
|
-
};
|
|
1830
|
-
}
|
|
1831
|
-
function observeTerminalListActionAuthority(terminal, context, binding) {
|
|
1832
|
-
return observeTerminalScopedApprovalAuthority(terminal, context, observeTerminalHandoffAuthority(terminal, context, observeDeferredTerminalAuthority(terminal, context, observeDeferredSourceAuthority(terminal, context, observeVerifiedEmptyTerminalAuthority(terminal, context, binding)))));
|
|
1833
|
-
}
|
|
1834
|
-
function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
1835
|
-
const { sessionAuthorityRequired, includeAll, mutationsAllowed, conversationHasNonterminalDeferredTransfer } = context;
|
|
1836
|
-
const { automatedInputComposerReady, terminalUserExplicitSendAction, publicTerminal, terminalControl, allRelated, displayedRelated, relatedSessions, authoritativeSession, ownership, rawSendAction, sessionAwareRawActions, externalHandoffDetected, externalHandoffSnapshotToken, handoffSourceBlockingTurns, verifiedEmptyRawSendAction, verifiedEmptyCodexSnapshotToken, deferredCodexSourceRolloutAuthority, deferredCodexForegroundToken, reconcileBindingAction, externalHandoffAdoptable, handoffDecision, blockingHandoffTurnIds, terminalRecoveryBlockingTurns, terminalScopedCodexApprovalAction, rolloutBackedCodexSession } = observation;
|
|
1837
|
-
const association = decideManagedTerminalAssociation({
|
|
1838
|
-
allRelated,
|
|
1839
|
-
displayedRelated,
|
|
1840
|
-
authoritativeSession,
|
|
1841
|
-
sessionAuthorityRequired,
|
|
1842
|
-
currentOwner: ownership.state === "current"
|
|
1843
|
-
? ownership.conversation
|
|
1844
|
-
: undefined
|
|
1845
|
-
});
|
|
1846
|
-
const { managedSessionId, sessionIds, sessionAllRelated, sessionDisplayedRelated } = association;
|
|
1847
|
-
// History remains useful when a pane has restarted and therefore no
|
|
1848
|
-
// first-class Session is authoritative for the new process incarnation.
|
|
1849
|
-
// Keep that display-only association separate from the send target: under
|
|
1850
|
-
// protocol 3, only authoritativeSession may populate managedSessionId.
|
|
1851
|
-
const sessionBindingMatchesLiveTerminal = authoritativeSession
|
|
1852
|
-
? true
|
|
1853
|
-
: Boolean(!sessionAuthorityRequired &&
|
|
1854
|
-
sessionAllRelated.some((turn) => managedTurnMatchesLiveTerminal(turn, terminal)));
|
|
1855
|
-
const currentTurnValue = ownership.state === "current"
|
|
1856
|
-
? currentManagedTurnForTerminal(ownership.conversation, terminal, sessionAwareRawActions)
|
|
1857
|
-
: undefined;
|
|
1858
|
-
const currentTurnProjection = currentTurnValue
|
|
1859
|
-
? !mutationsAllowed
|
|
1860
|
-
? readOnlyManagedTurn(currentTurnValue)
|
|
1861
|
-
: ownership.state === "current" &&
|
|
1862
|
-
conversationHasNonterminalDeferredTransfer(ownership.conversation)
|
|
1863
|
-
? userReleasableManagedTurn(currentTurnValue)
|
|
1864
|
-
: currentTurnValue
|
|
1865
|
-
: undefined;
|
|
1866
|
-
const currentTurn = currentTurnProjection
|
|
1867
|
-
? withoutGenericHandoffSourceClose(currentTurnProjection, blockingHandoffTurnIds)
|
|
1868
|
-
: undefined;
|
|
1869
|
-
const nonOwnerRawActions = nonOwnerTerminalActions(sessionAwareRawActions, {
|
|
1870
|
-
hasAuthoritativeSession: Boolean(authoritativeSession),
|
|
1871
|
-
rolloutBackedCodexSession
|
|
1872
|
-
});
|
|
1873
|
-
const { recentConversation, historyConversations } = selectManagedTerminalHistory({
|
|
1874
|
-
displayedRelated: sessionDisplayedRelated,
|
|
1875
|
-
currentConversationId: stringValue(currentTurn?.conversation_id),
|
|
1876
|
-
hasCurrentTurn: Boolean(currentTurn),
|
|
1877
|
-
includeAll
|
|
1878
|
-
});
|
|
1879
|
-
const recentTurnValue = recentConversation
|
|
1880
|
-
? historicalManagedTurnForTerminal(recentConversation)
|
|
1881
|
-
: undefined;
|
|
1882
|
-
const recentTurnProjection = recentTurnValue
|
|
1883
|
-
? !mutationsAllowed
|
|
1884
|
-
? readOnlyManagedTurn(recentTurnValue)
|
|
1885
|
-
: recentConversation &&
|
|
1886
|
-
conversationHasNonterminalDeferredTransfer(recentConversation)
|
|
1887
|
-
? userReleasableManagedTurn(recentTurnValue)
|
|
1888
|
-
: recentTurnValue
|
|
1889
|
-
: undefined;
|
|
1890
|
-
const recentTurn = recentTurnProjection
|
|
1891
|
-
? withoutGenericHandoffSourceClose(recentTurnProjection, blockingHandoffTurnIds)
|
|
1892
|
-
: undefined;
|
|
1893
|
-
const history = historyConversations.map((conversation) => {
|
|
1894
|
-
const turn = historicalManagedTurnForTerminal(conversation);
|
|
1895
|
-
return withoutGenericHandoffSourceClose(!mutationsAllowed
|
|
1896
|
-
? readOnlyManagedTurn(turn)
|
|
1897
|
-
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
1898
|
-
? userReleasableManagedTurn(turn)
|
|
1899
|
-
: turn, blockingHandoffTurnIds);
|
|
1900
|
-
});
|
|
1901
|
-
const visibleTurnIds = new Set([currentTurn, recentTurn, ...history]
|
|
1902
|
-
.map((turn) => stringValue(turn?.conversation_id))
|
|
1903
|
-
.filter((id) => id !== undefined));
|
|
1904
|
-
const managedSessionShortReference = managedSessionId
|
|
1905
|
-
? sessionShortRef(managedSessionId)
|
|
1906
|
-
: null;
|
|
1907
|
-
const management = projectTerminalManagement({
|
|
1908
|
-
managedSessionId,
|
|
1909
|
-
managedSessionShortRef: managedSessionShortReference,
|
|
1910
|
-
currentTurn,
|
|
1911
|
-
recentTurn,
|
|
1912
|
-
sessionAllRelatedCount: sessionAllRelated.length,
|
|
1913
|
-
hiddenTurnCount: sessionAllRelated.filter((conversation) => !visibleTurnIds.has(conversation.conversation_id)).length,
|
|
1914
|
-
sessionCount: new Set([
|
|
1915
|
-
...sessionIds,
|
|
1916
|
-
...relatedSessions.map((session) => session.session_id)
|
|
1917
|
-
]).size,
|
|
1918
|
-
authoritativeSession,
|
|
1919
|
-
history: includeAll ? history : undefined
|
|
1920
|
-
});
|
|
1921
|
-
const sendAuthority = decideTerminalSendAuthority({
|
|
1922
|
-
ownership: ownership.state,
|
|
1923
|
-
verifiedEmptyToken: verifiedEmptyCodexSnapshotToken,
|
|
1924
|
-
externalToken: externalHandoffAdoptable
|
|
1925
|
-
? externalHandoffSnapshotToken
|
|
1926
|
-
: undefined,
|
|
1927
|
-
deferredToken: ownership.state !== "conflict" ||
|
|
1928
|
-
deferredCodexSourceRolloutAuthority ===
|
|
1929
|
-
"explicitly_abandoned_predecessor"
|
|
1930
|
-
? deferredCodexForegroundToken
|
|
1931
|
-
: undefined,
|
|
1932
|
-
managedSendSessionId: managedSessionId &&
|
|
1933
|
-
!rolloutBackedCodexSession &&
|
|
1934
|
-
sessionBindingMatchesLiveTerminal &&
|
|
1935
|
-
isRecord(sessionAwareRawActions.send)
|
|
1936
|
-
? managedSessionId
|
|
1937
|
-
: undefined
|
|
1938
|
-
});
|
|
1939
|
-
const tokenSendAction = sendAuthority.mode === "external_handoff"
|
|
1940
|
-
? rawSendAction
|
|
1941
|
-
: sendAuthority.mode === "verified_empty" ||
|
|
1942
|
-
sendAuthority.mode === "deferred"
|
|
1943
|
-
? verifiedEmptyRawSendAction
|
|
1944
|
-
: undefined;
|
|
1945
|
-
const authoritativeSendAction = sendAuthority.mode === "managed" && isRecord(sessionAwareRawActions.send)
|
|
1946
|
-
? sendActionForManagedSession(sessionAwareRawActions.send, sendAuthority.sessionId)
|
|
1947
|
-
: tokenSendAction &&
|
|
1948
|
-
"token" in sendAuthority &&
|
|
1949
|
-
sendAuthority.token
|
|
1950
|
-
? {
|
|
1951
|
-
...tokenSendAction,
|
|
1952
|
-
arguments: {
|
|
1953
|
-
...(isRecord(tokenSendAction.arguments)
|
|
1954
|
-
? tokenSendAction.arguments
|
|
1955
|
-
: {}),
|
|
1956
|
-
expected_terminal_token: sendAuthority.token
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
: undefined;
|
|
1960
|
-
const managedFastPathToken = "token" in sendAuthority
|
|
1961
|
-
? sendAuthority.token
|
|
1962
|
-
: undefined;
|
|
1963
|
-
const prioritizedTerminalUserExplicitSendAction = terminalUserExplicitSendAction && managedFastPathToken
|
|
1964
|
-
? {
|
|
1965
|
-
...terminalUserExplicitSendAction,
|
|
1966
|
-
arguments: {
|
|
1967
|
-
...(isRecord(terminalUserExplicitSendAction.arguments)
|
|
1968
|
-
? terminalUserExplicitSendAction.arguments
|
|
1969
|
-
: {}),
|
|
1970
|
-
expected_managed_terminal_token: managedFastPathToken
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
: terminalUserExplicitSendAction;
|
|
1974
|
-
const availableActions = selectTerminalAvailableActions({
|
|
1975
|
-
ownership: ownership.state,
|
|
1976
|
-
currentActions: ownership.state === "current"
|
|
1977
|
-
? currentTerminalActions(currentTurn)
|
|
1978
|
-
: {},
|
|
1979
|
-
sessionAwareRawActions: sessionAwareRawActions,
|
|
1980
|
-
nonOwnerRawActions,
|
|
1981
|
-
authoritativeSendAction,
|
|
1982
|
-
terminalUserExplicitSendAction: prioritizedTerminalUserExplicitSendAction,
|
|
1983
|
-
reconcileBindingAction,
|
|
1984
|
-
terminalScopedApprovalAction: terminalScopedCodexApprovalAction,
|
|
1985
|
-
isAction: isRecord
|
|
1986
|
-
});
|
|
1987
|
-
// An explicit undefined rollout prevents a lingering resolver rollout from
|
|
1988
|
-
// being presented as the authoritative status-card thread.
|
|
1989
|
-
const authoritativeIdentity = authoritativeTerminalIdentity(authoritativeSession);
|
|
1990
|
-
const publicManagementConflict = ownership.state === "conflict"
|
|
1991
|
-
? projectPublicManagementConflict({
|
|
1992
|
-
conflict: ownership.conflict,
|
|
1993
|
-
verifiedEmptyToken: verifiedEmptyCodexSnapshotToken,
|
|
1994
|
-
deferredToken: deferredCodexForegroundToken,
|
|
1995
|
-
explicitlyAbandonedPredecessor: deferredCodexSourceRolloutAuthority ===
|
|
1996
|
-
"explicitly_abandoned_predecessor"
|
|
1997
|
-
})
|
|
1998
|
-
: undefined;
|
|
1999
|
-
const handoffPresentation = projectHandoffPresentation({
|
|
2000
|
-
externalHandoffDetected,
|
|
2001
|
-
externalHandoffAdoptable,
|
|
2002
|
-
recoveryBlockingTurnCount: terminalRecoveryBlockingTurns.length,
|
|
2003
|
-
hasHandoffDecision: Boolean(handoffDecision),
|
|
2004
|
-
sourceBlockingTurnCount: handoffSourceBlockingTurns.length,
|
|
2005
|
-
automatedInputComposerReady: automatedInputComposerReady === true,
|
|
2006
|
-
verifiedEmptyToken: verifiedEmptyCodexSnapshotToken
|
|
2007
|
-
});
|
|
2008
|
-
return {
|
|
2009
|
-
...publicTerminal,
|
|
2010
|
-
...authoritativeIdentity,
|
|
2011
|
-
management_state: ownership.state === "conflict"
|
|
2012
|
-
? "conflict"
|
|
2013
|
-
: ownership.state === "current" || Boolean(authoritativeSession)
|
|
2014
|
-
? "managed"
|
|
2015
|
-
: "unmanaged",
|
|
2016
|
-
...(ownership.state === "conflict"
|
|
2017
|
-
? { management_conflict: publicManagementConflict }
|
|
2018
|
-
: {}),
|
|
2019
|
-
...handoffPresentation,
|
|
2020
|
-
...(handoffDecision ? { handoff_decision: handoffDecision } : {}),
|
|
2021
|
-
...(terminalRecoveryBlockingTurns.length > 0
|
|
2022
|
-
? {
|
|
2023
|
-
blocking_turns: terminalRecoveryBlockingTurns.map((turn) => projectBlockingTurn({
|
|
2024
|
-
sessionId: sessionIdForConversation(turn),
|
|
2025
|
-
turnId: turnIdForConversation(turn),
|
|
2026
|
-
status: turn.status,
|
|
2027
|
-
recoveryTurnId: turnIdForConversation(turn)
|
|
2028
|
-
}))
|
|
2029
|
-
}
|
|
2030
|
-
: {}),
|
|
2031
|
-
managed: management,
|
|
2032
|
-
available_actions: availableActions
|
|
2033
|
-
};
|
|
2034
|
-
}
|
|
2035
|
-
function terminalFirstListProjection({ storeDir, terminals, managedSessions, sessionAuthorityRequired, allConversations, displayedConversations, includeAll, managedOnly, statusFilter, mutationsAllowed }) {
|
|
2036
|
-
const nonterminalDeferredTransfers = listDeferredForegroundTransfers(storeDir).filter((transfer) => !isFinalDeferredForegroundTransferStatus(transfer.status));
|
|
2037
|
-
const nonterminalDeferredTransferIds = new Set(nonterminalDeferredTransfers.map((transfer) => transfer.transfer_id));
|
|
2038
|
-
const nonterminalDeferredSourceTurnIds = new Set(nonterminalDeferredTransfers.flatMap((transfer) => transfer.version === 2 &&
|
|
2039
|
-
transfer.source_kind === "candidate_rollout_quiescent"
|
|
2040
|
-
? (transfer.source_turn_history ?? []).map((turn) => turn.turn_id)
|
|
2041
|
-
: []));
|
|
2042
|
-
const conversationHasNonterminalDeferredTransfer = (conversation) => {
|
|
2043
|
-
if (nonterminalDeferredSourceTurnIds.has(turnIdForConversation(conversation))) {
|
|
2044
|
-
return true;
|
|
2045
|
-
}
|
|
2046
|
-
const takeover = isRecord(conversation.native_session_takeover)
|
|
2047
|
-
? conversation.native_session_takeover
|
|
2048
|
-
: undefined;
|
|
2049
|
-
const transferId = stringValue(takeover?.deferred_foreground_transfer_id);
|
|
2050
|
-
return Boolean(transferId && nonterminalDeferredTransferIds.has(transferId));
|
|
2051
|
-
};
|
|
2052
|
-
const discoveredTerminalControls = terminals.flatMap((terminal) => {
|
|
2053
|
-
const control = isRecord(terminal.terminal_control)
|
|
2054
|
-
? terminal.terminal_control
|
|
2055
|
-
: undefined;
|
|
2056
|
-
return control ? [control] : [];
|
|
2057
|
-
});
|
|
2058
|
-
const projectionContext = {
|
|
2059
|
-
storeDir,
|
|
2060
|
-
terminals,
|
|
2061
|
-
managedSessions,
|
|
2062
|
-
sessionAuthorityRequired,
|
|
2063
|
-
allConversations,
|
|
2064
|
-
displayedConversations,
|
|
2065
|
-
includeAll,
|
|
2066
|
-
mutationsAllowed,
|
|
2067
|
-
nonterminalDeferredTransfers,
|
|
2068
|
-
conversationHasNonterminalDeferredTransfer
|
|
2069
|
-
};
|
|
2070
|
-
const projectedTerminals = terminals.map((terminal) => {
|
|
2071
|
-
const binding = observeTerminalListBindingAuthority(terminal, projectionContext);
|
|
2072
|
-
return renderTerminalFirstListEntry(binding.authorityTerminal, projectionContext, observeTerminalListActionAuthority(binding.authorityTerminal, projectionContext, binding));
|
|
2073
|
-
});
|
|
2074
|
-
const unavailableManagedTurns = displayedConversations
|
|
2075
|
-
.filter((conversation) => {
|
|
2076
|
-
const managedControl = terminalControlForManagedConversation(conversation);
|
|
2077
|
-
if (discoveredTerminalControls.some((control) => terminalControlsShareIncarnation(managedControl, control))) {
|
|
2078
|
-
return false;
|
|
2079
|
-
}
|
|
2080
|
-
return (includeAll ||
|
|
2081
|
-
managedOnly ||
|
|
2082
|
-
statusFilter !== undefined ||
|
|
2083
|
-
managedTurnNeedsAttention(conversation));
|
|
2084
|
-
})
|
|
2085
|
-
.sort(compareManagedConversationRecency)
|
|
2086
|
-
.map((conversation) => {
|
|
2087
|
-
const managedTurn = managedTurnListEntry(terminalListRuntime().summarizeConversation(conversation), {
|
|
2088
|
-
terminalBridge: terminalListRuntime().terminalBridgeEnabled(conversation),
|
|
2089
|
-
approvalState: managedListApprovalState(conversation),
|
|
2090
|
-
conversation
|
|
2091
|
-
});
|
|
2092
|
-
return {
|
|
2093
|
-
...managedTurn,
|
|
2094
|
-
available_actions: !mutationsAllowed
|
|
2095
|
-
? readOnlyListActions(isRecord(managedTurn.available_actions)
|
|
2096
|
-
? managedTurn.available_actions
|
|
2097
|
-
: {})
|
|
2098
|
-
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
2099
|
-
? userReleaseListActions(isRecord(managedTurn.available_actions)
|
|
2100
|
-
? managedTurn.available_actions
|
|
2101
|
-
: {}, turnIdForConversation(conversation))
|
|
2102
|
-
: safeUnavailableManagedTurnActions(isRecord(managedTurn.available_actions)
|
|
2103
|
-
? managedTurn.available_actions
|
|
2104
|
-
: {}),
|
|
2105
|
-
terminal_availability: {
|
|
2106
|
-
available: false,
|
|
2107
|
-
reason: managedOnly
|
|
2108
|
-
? "terminal discovery was disabled by --managed-only"
|
|
2109
|
-
: "the referenced terminal pane is not currently available"
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2112
|
-
});
|
|
2113
|
-
return {
|
|
2114
|
-
terminals: projectedTerminals,
|
|
2115
|
-
unavailableManagedTurns
|
|
2116
|
-
};
|
|
2117
|
-
}
|
|
2118
1142
|
function managedSessionMatchesLiveTerminalEntry(session, terminal, storeDir) {
|
|
2119
|
-
|
|
2120
|
-
const liveControl = isRecord(terminal.terminal_control)
|
|
2121
|
-
? terminal.terminal_control
|
|
2122
|
-
: undefined;
|
|
2123
|
-
if (session.status !== "bound" ||
|
|
2124
|
-
!binding ||
|
|
2125
|
-
session.agent !== terminal.agent ||
|
|
2126
|
-
binding.native_process.pid !== Number(terminal.pid)) {
|
|
2127
|
-
return false;
|
|
2128
|
-
}
|
|
2129
|
-
const terminalAliasMatches = terminalControlAliasMatches(binding.terminal_id, binding.terminal_control, terminal.id, liveControl);
|
|
2130
|
-
if (!terminalAliasMatches) {
|
|
2131
|
-
return false;
|
|
2132
|
-
}
|
|
2133
|
-
const workspaceMatches = terminalListRuntime().matchesConfiguredWorkspace(session.workspace, terminal.workspace ?? terminal.cwd);
|
|
2134
|
-
if (!workspaceMatches) {
|
|
2135
|
-
return false;
|
|
2136
|
-
}
|
|
2137
|
-
const observation = terminalObservationFromListEntry(terminal, session.agent);
|
|
2138
|
-
const evidence = {
|
|
2139
|
-
terminalAliasMatches,
|
|
2140
|
-
workspaceMatches
|
|
2141
|
-
};
|
|
2142
|
-
let decision = decideTerminalBindingMatch(session, observation, evidence);
|
|
2143
|
-
if (decision.state === "not_exact" &&
|
|
2144
|
-
decision.reason === "native_identity_mismatch" &&
|
|
2145
|
-
session.agent === "codex" &&
|
|
2146
|
-
observation.nativeIdentity.status === "resolved") {
|
|
2147
|
-
decision = decideTerminalBindingMatch(session, observation, {
|
|
2148
|
-
...evidence,
|
|
2149
|
-
codexLingeringBeforeMatches: terminalListRuntime().codexLingeringBeforeIdentityMatchesSession({
|
|
2150
|
-
storeDir,
|
|
2151
|
-
session,
|
|
2152
|
-
identity: observation.nativeIdentity.identity
|
|
2153
|
-
})
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
return decision.state === "exact";
|
|
2157
|
-
}
|
|
2158
|
-
function managedSessionClaimsLiveTerminalEntry(session, terminal) {
|
|
2159
|
-
const binding = session.binding;
|
|
2160
|
-
const liveControl = isRecord(terminal.terminal_control)
|
|
2161
|
-
? terminal.terminal_control
|
|
2162
|
-
: undefined;
|
|
2163
|
-
return Boolean(binding &&
|
|
2164
|
-
session.agent === terminal.agent &&
|
|
2165
|
-
binding.native_process.pid === Number(terminal.pid) &&
|
|
2166
|
-
terminalControlsShareIncarnation(binding.terminal_control, liveControl));
|
|
2167
|
-
}
|
|
2168
|
-
function listedTerminalProcessIncarnation(terminal) {
|
|
2169
|
-
const processUuid = stringValue(terminal.native_agent_process_uuid);
|
|
2170
|
-
const processBirth = stringValue(terminal.native_agent_process_birth);
|
|
2171
|
-
if (terminal.agent !== "codex" ||
|
|
2172
|
-
(processUuid && processBirth)) {
|
|
2173
|
-
return { processUuid, processBirth };
|
|
2174
|
-
}
|
|
2175
|
-
const pid = Number(terminal.pid);
|
|
2176
|
-
if (!Number.isSafeInteger(pid) || pid <= 1) {
|
|
2177
|
-
return { processUuid, processBirth };
|
|
2178
|
-
}
|
|
2179
|
-
try {
|
|
2180
|
-
const incarnation = terminalListRuntime().codexProcessIncarnationForPid(pid);
|
|
2181
|
-
return {
|
|
2182
|
-
processUuid: processUuid ?? incarnation.processUuid,
|
|
2183
|
-
processBirth: processBirth ?? incarnation.processBirth
|
|
2184
|
-
};
|
|
2185
|
-
}
|
|
2186
|
-
catch {
|
|
2187
|
-
return { processUuid, processBirth };
|
|
2188
|
-
}
|
|
2189
|
-
}
|
|
2190
|
-
function managedBindingConflictKindForLiveTerminalEntry({ storeDir, session, terminal }) {
|
|
2191
|
-
const binding = session.binding;
|
|
2192
|
-
if (session.status !== "bound" ||
|
|
2193
|
-
!binding ||
|
|
2194
|
-
!managedSessionClaimsLiveTerminalEntry(session, terminal) ||
|
|
2195
|
-
managedSessionMatchesLiveTerminalEntry(session, terminal, storeDir)) {
|
|
2196
|
-
return undefined;
|
|
2197
|
-
}
|
|
2198
|
-
const livePid = Number(terminal.pid);
|
|
2199
|
-
const incarnation = listedTerminalProcessIncarnation(terminal);
|
|
2200
|
-
const relationship = processIncarnationRelationship({
|
|
2201
|
-
binding,
|
|
2202
|
-
livePid,
|
|
2203
|
-
liveProcessUuid: incarnation.processUuid,
|
|
2204
|
-
liveProcessBirth: incarnation.processBirth
|
|
2205
|
-
});
|
|
2206
|
-
if (relationship === "different") {
|
|
2207
|
-
return "stale_process_incarnation";
|
|
2208
|
-
}
|
|
2209
|
-
if (!terminalControlAliasMatches(binding.terminal_id, binding.terminal_control, terminal.id, isRecord(terminal.terminal_control)
|
|
2210
|
-
? terminal.terminal_control
|
|
2211
|
-
: undefined) ||
|
|
2212
|
-
!terminalListRuntime().matchesConfiguredWorkspace(session.workspace, terminal.workspace ?? terminal.cwd)) {
|
|
2213
|
-
return "unverifiable";
|
|
2214
|
-
}
|
|
2215
|
-
return decideManagedBindingConflict({
|
|
2216
|
-
session,
|
|
2217
|
-
claimsTerminal: true,
|
|
2218
|
-
exactBinding: false,
|
|
2219
|
-
ownerConclusivelyInactive: false,
|
|
2220
|
-
processRelationship: relationship,
|
|
2221
|
-
liveNativeThreadId: stringValue(terminal.native_agent_session_id),
|
|
2222
|
-
statusCardNativeThreadId: stringValue(terminal.native_agent_status_card_session_id),
|
|
2223
|
-
managedTurnCount: provisionalManagedBindingTurnCount(storeDir, session)
|
|
2224
|
-
});
|
|
1143
|
+
return terminalListOwnershipService().managedSessionMatchesLiveTerminalEntry(session, terminal, storeDir);
|
|
2225
1144
|
}
|
|
2226
1145
|
function provisionalManagedBindingTurnCount(storeDir, session) {
|
|
2227
|
-
|
|
2228
|
-
return binding && session.lineage.created_by === "attach" &&
|
|
2229
|
-
!session.last_transition_id &&
|
|
2230
|
-
!binding.native_thread_id &&
|
|
2231
|
-
!binding.native_process.rollout
|
|
2232
|
-
? terminalListRuntime().managedTurnsForSession(storeDir, session.session_id).length
|
|
2233
|
-
: undefined;
|
|
1146
|
+
return terminalListOwnershipService().provisionalManagedBindingTurnCount(storeDir, session);
|
|
2234
1147
|
}
|
|
2235
1148
|
function managedSessionHasUnresolvedNativeTransition(storeDir, session) {
|
|
2236
|
-
|
|
2237
|
-
if (!fs.existsSync(root)) {
|
|
2238
|
-
return false;
|
|
2239
|
-
}
|
|
2240
|
-
let entries;
|
|
2241
|
-
try {
|
|
2242
|
-
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
2243
|
-
}
|
|
2244
|
-
catch {
|
|
2245
|
-
return true;
|
|
2246
|
-
}
|
|
2247
|
-
for (const entry of entries) {
|
|
2248
|
-
if (!entry.isDirectory()) {
|
|
2249
|
-
continue;
|
|
2250
|
-
}
|
|
2251
|
-
let transition;
|
|
2252
|
-
try {
|
|
2253
|
-
transition = loadNativeThreadTransition(storeDir, entry.name);
|
|
2254
|
-
}
|
|
2255
|
-
catch {
|
|
2256
|
-
return true;
|
|
2257
|
-
}
|
|
2258
|
-
if (transition.source_session_id !== session.session_id &&
|
|
2259
|
-
transition.target_session_id !== session.session_id) {
|
|
2260
|
-
continue;
|
|
2261
|
-
}
|
|
2262
|
-
if (!["committed", "aborted"].includes(transition.status)) {
|
|
2263
|
-
return true;
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
return false;
|
|
1149
|
+
return terminalListOwnershipService().managedSessionHasUnresolvedNativeTransition(storeDir, session);
|
|
2267
1150
|
}
|
|
2268
1151
|
function managedSessionHasAnyNativeTransition(storeDir, session) {
|
|
2269
|
-
|
|
2270
|
-
if (!fs.existsSync(root)) {
|
|
2271
|
-
return false;
|
|
2272
|
-
}
|
|
2273
|
-
let entries;
|
|
2274
|
-
try {
|
|
2275
|
-
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
2276
|
-
}
|
|
2277
|
-
catch {
|
|
2278
|
-
return true;
|
|
2279
|
-
}
|
|
2280
|
-
for (const entry of entries) {
|
|
2281
|
-
if (!entry.isDirectory()) {
|
|
2282
|
-
continue;
|
|
2283
|
-
}
|
|
2284
|
-
let transition;
|
|
2285
|
-
try {
|
|
2286
|
-
transition = loadNativeThreadTransition(storeDir, entry.name);
|
|
2287
|
-
}
|
|
2288
|
-
catch {
|
|
2289
|
-
return true;
|
|
2290
|
-
}
|
|
2291
|
-
if (transition.source_session_id === session.session_id ||
|
|
2292
|
-
transition.target_session_id === session.session_id) {
|
|
2293
|
-
return true;
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
return false;
|
|
1152
|
+
return terminalListOwnershipService().managedSessionHasAnyNativeTransition(storeDir, session);
|
|
2297
1153
|
}
|
|
2298
1154
|
function terminalControlForManagedConversation(conversation) {
|
|
2299
|
-
return
|
|
2300
|
-
? conversation.native_session_takeover
|
|
2301
|
-
: undefined);
|
|
1155
|
+
return terminalListOwnershipService().terminalControlForManagedConversation(conversation);
|
|
2302
1156
|
}
|
|
2303
1157
|
function terminalIncarnationBlockingTurns(storeDir, terminalControl, conversations = listConversations(storeDir)) {
|
|
2304
|
-
return conversations
|
|
2305
|
-
.filter(terminalListRuntime().isDiscoverableTmuxConversation)
|
|
2306
|
-
.filter((turn) => terminalControlsShareIncarnation(terminalControlForManagedConversation(turn), terminalControl) &&
|
|
2307
|
-
isSessionSendBlockingStatus(turn.status))
|
|
2308
|
-
.sort(compareManagedConversationRecency);
|
|
1158
|
+
return terminalListOwnershipService().terminalIncarnationBlockingTurns(storeDir, terminalControl, conversations);
|
|
2309
1159
|
}
|
|
2310
1160
|
function managedTurnNeedsAttention(conversation) {
|
|
2311
|
-
return
|
|
2312
|
-
status: conversation.status,
|
|
2313
|
-
get callbackDeliveryStatus() {
|
|
2314
|
-
const delivery = isRecord(conversation.callback_delivery)
|
|
2315
|
-
? conversation.callback_delivery
|
|
2316
|
-
: undefined;
|
|
2317
|
-
return String(delivery?.status ?? "");
|
|
2318
|
-
}
|
|
2319
|
-
});
|
|
1161
|
+
return terminalListOwnershipService().managedTurnNeedsAttention(conversation);
|
|
2320
1162
|
}
|
|
2321
1163
|
function assertTerminalIncarnationCanStartTurn(storeDir, terminalControl) {
|
|
2322
|
-
|
|
2323
|
-
if (!blocker) {
|
|
2324
|
-
return;
|
|
2325
|
-
}
|
|
2326
|
-
throw new Error(`terminal ${terminalControl.target} still has unresolved Turn ` +
|
|
2327
|
-
`${turnIdForConversation(blocker)} (${blocker.status})`);
|
|
1164
|
+
return terminalListOwnershipService().assertTerminalIncarnationCanStartTurn(storeDir, terminalControl);
|
|
2328
1165
|
}
|
|
2329
1166
|
function terminalDispatchOwnership(terminalControl) {
|
|
2330
|
-
|
|
2331
|
-
try {
|
|
2332
|
-
ledger = terminalListRuntime().loadTerminalBridgeDispatchLedger(terminalControl);
|
|
2333
|
-
}
|
|
2334
|
-
catch (error) {
|
|
2335
|
-
const decision = dispatch.decideTerminalDispatchOwnership("unreadable");
|
|
2336
|
-
return {
|
|
2337
|
-
state: "conflict",
|
|
2338
|
-
conflict: {
|
|
2339
|
-
reason: decision.code === "ledger_unreadable"
|
|
2340
|
-
? error instanceof Error ? error.message : String(error)
|
|
2341
|
-
: "terminal dispatch ledger is unreadable",
|
|
2342
|
-
recovery: "inspect the shared terminal pane before performing a side effect"
|
|
2343
|
-
}
|
|
2344
|
-
};
|
|
2345
|
-
}
|
|
2346
|
-
const ledgerAuthority = !ledger
|
|
2347
|
-
? "absent"
|
|
2348
|
-
: ledger.status === "resolved"
|
|
2349
|
-
? "resolved"
|
|
2350
|
-
: terminalListRuntime().terminalDispatchRecordMatchesControl(ledger, terminalControl, {
|
|
2351
|
-
requireProcessAnchor: false
|
|
2352
|
-
}) && !terminalListRuntime().terminalDispatchRecordMatchesControl(ledger, terminalControl)
|
|
2353
|
-
? "stale_process_incarnation"
|
|
2354
|
-
: dispatch.isActiveTerminalDispatchStatus(String(ledger.status))
|
|
2355
|
-
? "active"
|
|
2356
|
-
: "inactive_status";
|
|
2357
|
-
let decision = dispatch.decideTerminalDispatchOwnership(ledgerAuthority);
|
|
2358
|
-
if (decision.state === "none" || !ledger)
|
|
2359
|
-
return { state: "none" };
|
|
2360
|
-
const owner = terminalListRuntime().loadTerminalDispatchLedgerOwner(ledger);
|
|
2361
|
-
const ledgerMessageId = stringValue(ledger.message_id);
|
|
2362
|
-
const ownerAuthority = !owner
|
|
2363
|
-
? "unavailable"
|
|
2364
|
-
: isTerminalDispatchOwnerReleasedStatus(owner.status)
|
|
2365
|
-
? "released"
|
|
2366
|
-
: !terminalControlsShareIncarnation(terminalControlForManagedConversation(owner), terminalControl)
|
|
2367
|
-
? "terminal_mismatch"
|
|
2368
|
-
: ledgerMessageId && stringValue(isRecord(owner.native_session_takeover)
|
|
2369
|
-
? owner.native_session_takeover.terminal_bridge_message_id
|
|
2370
|
-
: undefined) !== ledgerMessageId
|
|
2371
|
-
? "generation_mismatch"
|
|
2372
|
-
: "current";
|
|
2373
|
-
decision = dispatch.decideTerminalDispatchOwnership(ledgerAuthority, ownerAuthority);
|
|
2374
|
-
if (decision.state === "none")
|
|
2375
|
-
return { state: "none" };
|
|
2376
|
-
if (decision.state === "current" && owner) {
|
|
2377
|
-
return { state: "current", conversation: owner };
|
|
2378
|
-
}
|
|
2379
|
-
const reason = decision.state === "conflict" &&
|
|
2380
|
-
decision.code === "owner_terminal_mismatch"
|
|
2381
|
-
? "dispatch owner does not reference this terminal pane incarnation"
|
|
2382
|
-
: decision.state === "conflict" &&
|
|
2383
|
-
decision.code === "owner_generation_mismatch"
|
|
2384
|
-
? "dispatch generation does not match the owner state"
|
|
2385
|
-
: "dispatch owner state is unavailable";
|
|
2386
|
-
return {
|
|
2387
|
-
state: "conflict",
|
|
2388
|
-
conflict: projectTerminalDispatchConflict({
|
|
2389
|
-
reason,
|
|
2390
|
-
dispatchStatus: stringValue(ledger.status),
|
|
2391
|
-
ownerConversationId: stringValue(ledger.conversation_id),
|
|
2392
|
-
messageId: stringValue(ledger.message_id)
|
|
2393
|
-
})
|
|
2394
|
-
};
|
|
2395
|
-
}
|
|
2396
|
-
function localTerminalDispatchOwnership(ledgerOwner, localConversations, terminal) {
|
|
2397
|
-
const localOwner = localConversations.find((conversation) => conversation.conversation_id === ledgerOwner.conversation_id &&
|
|
2398
|
-
sameCanonicalStatePath(conversation.state_path, ledgerOwner.state_path));
|
|
2399
|
-
return decideLocalTerminalDispatchOwnership({
|
|
2400
|
-
ledgerOwnerId: ledgerOwner.conversation_id,
|
|
2401
|
-
localOwner,
|
|
2402
|
-
localOwnerMatchesLiveTerminal: localOwner
|
|
2403
|
-
? managedTurnMatchesLiveTerminal(localOwner, terminal)
|
|
2404
|
-
: false
|
|
2405
|
-
});
|
|
1167
|
+
return terminalListOwnershipService().terminalDispatchOwnership(terminalControl);
|
|
2406
1168
|
}
|
|
2407
1169
|
function terminalScopedCodexApprovalBoundary({ storeDir, terminal, session, ledger, approval, owner }) {
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
.filter((candidate) => candidate.status === "bound" &&
|
|
2416
|
-
candidate.agent === "codex" &&
|
|
2417
|
-
candidate.binding?.native_process.pid === Number(terminalRecord.pid) &&
|
|
2418
|
-
terminalControlsShareIncarnation(candidate.binding?.terminal_control, terminalControl))
|
|
2419
|
-
.map((candidate) => candidate.session_id);
|
|
2420
|
-
const blockingTurnIds = () => terminalIncarnationBlockingTurns(storeDir, control)
|
|
2421
|
-
.map((turn) => turnIdForConversation(turn));
|
|
2422
|
-
const hasDeferredRecovery = () => listDeferredForegroundTransfers(storeDir).some((transfer) => !isFinalDeferredForegroundTransferStatus(transfer.status) &&
|
|
2423
|
-
(transfer.source_session_id === session.session_id ||
|
|
2424
|
-
transfer.target_session_id === session.session_id ||
|
|
2425
|
-
(transfer.terminal_id === terminalId &&
|
|
2426
|
-
terminalControlEvidenceMatches(transfer.terminal_endpoint, control))));
|
|
2427
|
-
const commonChecks = {
|
|
2428
|
-
relatedBoundSessionIds, blockingTurnIds,
|
|
2429
|
-
hasNativeTransition: () => managedSessionHasAnyNativeTransition(storeDir, session),
|
|
2430
|
-
hasDeferredRecovery,
|
|
2431
|
-
ledgerMatchesTerminal: () => terminalListRuntime().terminalDispatchRecordMatchesControl(ledger, control)
|
|
2432
|
-
};
|
|
2433
|
-
if (owner && ledger) {
|
|
2434
|
-
return decideTerminalScopedCodexApprovalAuthority({
|
|
2435
|
-
kind: "current_dispatch_owner", storeDir, terminal, owner, session,
|
|
2436
|
-
ledger, approval,
|
|
2437
|
-
checks: {
|
|
2438
|
-
...commonChecks,
|
|
2439
|
-
assertDispatchOwner: () => terminalListRuntime().assertManagedTerminalDispatchOwner({
|
|
2440
|
-
storeDir, conversation: owner, terminalControl: control, action: "approve"
|
|
2441
|
-
}),
|
|
2442
|
-
ownerMatchesNativeIdentity: (identity) => nativeAgentIdentityMatchesTurn(owner, identity
|
|
2443
|
-
? { ...identity, evidence: "terminal_scoped_approval" }
|
|
2444
|
-
: undefined)
|
|
2445
|
-
}
|
|
2446
|
-
});
|
|
2447
|
-
}
|
|
2448
|
-
return decideTerminalScopedCodexApprovalAuthority({
|
|
2449
|
-
kind: "managed_session_no_dispatch_owner", storeDir, terminal, session,
|
|
2450
|
-
ledger, approval,
|
|
2451
|
-
checks: {
|
|
2452
|
-
...commonChecks,
|
|
2453
|
-
dispatchOwnershipIsNone: () => terminalDispatchOwnership(control).state === "none",
|
|
2454
|
-
hasOrphanedDispatch: () => Boolean(terminalListRuntime().orphanedTerminalDispatchForRecovery(control))
|
|
2455
|
-
}
|
|
1170
|
+
return terminalListOwnershipService().terminalScopedCodexApprovalBoundary({
|
|
1171
|
+
storeDir,
|
|
1172
|
+
terminal,
|
|
1173
|
+
session,
|
|
1174
|
+
ledger,
|
|
1175
|
+
approval,
|
|
1176
|
+
owner
|
|
2456
1177
|
});
|
|
2457
1178
|
}
|
|
2458
1179
|
async function resolveTerminalScopedCodexApproval({ options, terminal, approvalSnapshot }) {
|
|
@@ -2634,90 +1355,6 @@ async function resolveTerminalScopedCodexApproval({ options, terminal, approvalS
|
|
|
2634
1355
|
};
|
|
2635
1356
|
}
|
|
2636
1357
|
}
|
|
2637
|
-
function historicalManagedTurnForTerminal(conversation) {
|
|
2638
|
-
return renderHistoricalManagedTurn(managedTurnListEntry(terminalListRuntime().summarizeConversation(conversation), {
|
|
2639
|
-
terminalBridge: terminalListRuntime().terminalBridgeEnabled(conversation),
|
|
2640
|
-
approvalState: managedListApprovalState(conversation),
|
|
2641
|
-
conversation
|
|
2642
|
-
}));
|
|
2643
|
-
}
|
|
2644
|
-
function managedTurnMatchesLiveTerminal(conversation, terminal) {
|
|
2645
|
-
const takeover = isRecord(conversation.native_session_takeover)
|
|
2646
|
-
? conversation.native_session_takeover
|
|
2647
|
-
: undefined;
|
|
2648
|
-
const liveControl = isRecord(terminal.terminal_control)
|
|
2649
|
-
? terminal.terminal_control
|
|
2650
|
-
: undefined;
|
|
2651
|
-
const storedControl = terminalListRuntime().terminalControlFromTakeover(takeover);
|
|
2652
|
-
const livePid = Number(terminal.pid);
|
|
2653
|
-
const storedPid = Number(takeover?.terminal_agent_pid);
|
|
2654
|
-
if (executorForConversation(conversation).kind !== terminal.agent ||
|
|
2655
|
-
!Number.isSafeInteger(livePid) ||
|
|
2656
|
-
livePid <= 1 ||
|
|
2657
|
-
storedPid !== livePid ||
|
|
2658
|
-
!terminalControlAliasMatches(stringValue(takeover?.native_session_id), storedControl, stringValue(terminal.id), liveControl) ||
|
|
2659
|
-
!terminalControlsShareIncarnation(storedControl, liveControl)) {
|
|
2660
|
-
return false;
|
|
2661
|
-
}
|
|
2662
|
-
const liveSessionId = stringValue(terminal.native_agent_session_id);
|
|
2663
|
-
const liveProcessUuid = stringValue(terminal.native_agent_process_uuid);
|
|
2664
|
-
const liveProcessBirth = stringValue(terminal.native_agent_process_birth);
|
|
2665
|
-
const liveRollout = isRecord(terminal.native_agent_rollout)
|
|
2666
|
-
? terminal.native_agent_rollout
|
|
2667
|
-
: undefined;
|
|
2668
|
-
const liveNativeIdentity = liveSessionId
|
|
2669
|
-
? {
|
|
2670
|
-
sessionId: liveSessionId,
|
|
2671
|
-
...(liveProcessUuid ? { processUuid: liveProcessUuid } : {}),
|
|
2672
|
-
...(liveProcessBirth ? { processBirth: liveProcessBirth } : {}),
|
|
2673
|
-
...(liveRollout
|
|
2674
|
-
? {
|
|
2675
|
-
rollout: {
|
|
2676
|
-
fd: String(liveRollout.fd ?? ""),
|
|
2677
|
-
device: String(liveRollout.device ?? ""),
|
|
2678
|
-
inode: String(liveRollout.inode ?? ""),
|
|
2679
|
-
path: String(liveRollout.path ?? "")
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
: {}),
|
|
2683
|
-
evidence: "live_terminal"
|
|
2684
|
-
}
|
|
2685
|
-
: undefined;
|
|
2686
|
-
if (!nativeAgentIdentityMatchesTurn(conversation, liveNativeIdentity)) {
|
|
2687
|
-
return false;
|
|
2688
|
-
}
|
|
2689
|
-
const liveWorkspace = terminal.workspace ?? terminal.cwd;
|
|
2690
|
-
if (!terminalListRuntime().matchesConfiguredWorkspace(conversation.workspace, liveWorkspace)) {
|
|
2691
|
-
return false;
|
|
2692
|
-
}
|
|
2693
|
-
const livePanePath = liveControl?.currentPath;
|
|
2694
|
-
if (livePanePath !== undefined &&
|
|
2695
|
-
!terminalListRuntime().matchesConfiguredWorkspace(conversation.workspace, livePanePath)) {
|
|
2696
|
-
return false;
|
|
2697
|
-
}
|
|
2698
|
-
return true;
|
|
2699
|
-
}
|
|
2700
|
-
function currentManagedTurnForTerminal(conversation, terminal, rawTerminalActions) {
|
|
2701
|
-
const managedTurn = managedTurnListEntry(terminalListRuntime().summarizeConversation(conversation), {
|
|
2702
|
-
terminalBridge: terminalListRuntime().terminalBridgeEnabled(conversation),
|
|
2703
|
-
approvalState: managedListApprovalState(conversation),
|
|
2704
|
-
conversation
|
|
2705
|
-
});
|
|
2706
|
-
const rawApproval = isRecord(rawTerminalActions.approve)
|
|
2707
|
-
? rawTerminalActions.approve
|
|
2708
|
-
: undefined;
|
|
2709
|
-
if (!rawApproval || executorForConversation(conversation).kind !== "codex") {
|
|
2710
|
-
return managedTurn;
|
|
2711
|
-
}
|
|
2712
|
-
return renderCurrentManagedTurn(managedTurn, {
|
|
2713
|
-
isCodex: true,
|
|
2714
|
-
ownerId: conversation.conversation_id,
|
|
2715
|
-
rawApproval,
|
|
2716
|
-
terminalApprovalState: () => isRecord(terminal.approval_state)
|
|
2717
|
-
? terminal.approval_state
|
|
2718
|
-
: undefined
|
|
2719
|
-
});
|
|
2720
|
-
}
|
|
2721
1358
|
async function listStateForTerminal(agent, terminalControl, options, bridge = terminalListRuntime().createTerminalAgentBridge(options), runtime) {
|
|
2722
1359
|
if (options.noApprovalScan) {
|
|
2723
1360
|
return {
|
|
@@ -2769,39 +1406,6 @@ async function listStateForTerminal(agent, terminalControl, options, bridge = te
|
|
|
2769
1406
|
};
|
|
2770
1407
|
}
|
|
2771
1408
|
}
|
|
2772
|
-
function managedListApprovalState(conversation) {
|
|
2773
|
-
if (!terminalListRuntime().terminalBridgeEnabled(conversation) ||
|
|
2774
|
-
!["waiting_for_agent", "waiting_for_openclaw"].includes(String(conversation.status))) {
|
|
2775
|
-
return undefined;
|
|
2776
|
-
}
|
|
2777
|
-
const nativeTakeover = isRecord(conversation.native_session_takeover)
|
|
2778
|
-
? conversation.native_session_takeover
|
|
2779
|
-
: undefined;
|
|
2780
|
-
if (!terminalListRuntime().terminalControlFromTakeover(nativeTakeover) ||
|
|
2781
|
-
!stringValue(nativeTakeover?.terminal_bridge_message_id)) {
|
|
2782
|
-
return undefined;
|
|
2783
|
-
}
|
|
2784
|
-
const approval = isRecord(nativeTakeover?.terminal_bridge_approval)
|
|
2785
|
-
? nativeTakeover.terminal_bridge_approval
|
|
2786
|
-
: undefined;
|
|
2787
|
-
const approvalState = isRecord(approval?.approval_state)
|
|
2788
|
-
? approval.approval_state
|
|
2789
|
-
: undefined;
|
|
2790
|
-
const fingerprint = stringValue(approval?.fingerprint);
|
|
2791
|
-
const notifiedAt = stringValue(approval?.notified_at);
|
|
2792
|
-
const notifiedAtMs = validTimestampMs(notifiedAt);
|
|
2793
|
-
if (!approvalState ||
|
|
2794
|
-
!fingerprint ||
|
|
2795
|
-
notifiedAtMs === undefined ||
|
|
2796
|
-
cliNowMs() - notifiedAtMs > terminalListRuntime().approvalTtlMs) {
|
|
2797
|
-
return undefined;
|
|
2798
|
-
}
|
|
2799
|
-
return {
|
|
2800
|
-
...approvalState,
|
|
2801
|
-
fingerprint,
|
|
2802
|
-
notified_at: notifiedAt
|
|
2803
|
-
};
|
|
2804
|
-
}
|
|
2805
1409
|
async function resolveConversationSelectorOption(commandName, options) {
|
|
2806
1410
|
const sendOperation = commandName === "send";
|
|
2807
1411
|
// Submission retry is already bound to one authoritative managed Turn.
|
|
@@ -2972,12 +1576,11 @@ async function sessionSelectorCandidates(commandName, options) {
|
|
|
2972
1576
|
.filter((conversation) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, conversation.workspace));
|
|
2973
1577
|
const discoverableWorkspaceConversations = workspaceConversations
|
|
2974
1578
|
.filter(terminalListRuntime().isDiscoverableTmuxConversation);
|
|
2975
|
-
const managed = discoverableWorkspaceConversations.map((conversation) => managedTurnListEntry(terminalListRuntime().summarizeConversation(conversation), {
|
|
1579
|
+
const managed = discoverableWorkspaceConversations.map((conversation) => terminalListProjectionService().managedTurnListEntry(terminalListRuntime().summarizeConversation(conversation), {
|
|
2976
1580
|
terminalBridge: terminalListRuntime().terminalBridgeEnabled(conversation),
|
|
2977
|
-
approvalState: managedListApprovalState(conversation),
|
|
2978
1581
|
conversation
|
|
2979
1582
|
}));
|
|
2980
|
-
const terminalProjection =
|
|
1583
|
+
const terminalProjection = terminalListProjectionService().projectTerminalFirstList({
|
|
2981
1584
|
storeDir,
|
|
2982
1585
|
terminals: terminalScan.terminalControlled.filter((entry) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, entry.workspace ?? entry.cwd)),
|
|
2983
1586
|
managedSessions: selectorStore.readable
|