@scotthuang/agent-knock-knock 0.12.12 → 0.12.14
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 +40 -0
- package/README.md +40 -27
- package/dist/src/approval-policy.d.ts +11 -2
- package/dist/src/approval-policy.js +33 -7
- package/dist/src/approval-policy.js.map +1 -1
- package/dist/src/callback-cli-adapter.d.ts +9 -6
- package/dist/src/callback-cli-adapter.js +119 -30
- package/dist/src/callback-cli-adapter.js.map +1 -1
- package/dist/src/callback-outbox-policy.d.ts +39 -0
- package/dist/src/callback-outbox-policy.js +163 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +28 -1
- package/dist/src/callback-outbox-service.js +413 -82
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.d.ts +14 -1
- package/dist/src/callback-outbox-settlement.js +151 -30
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-authority.d.ts +59 -0
- package/dist/src/callback-route-authority.js +123 -0
- package/dist/src/callback-route-authority.js.map +1 -0
- package/dist/src/callback-transport.d.ts +134 -0
- package/dist/src/callback-transport.js +256 -0
- package/dist/src/callback-transport.js.map +1 -0
- package/dist/src/claude-local-transcript-provider.d.ts +81 -0
- package/dist/src/claude-local-transcript-provider.js +854 -18
- package/dist/src/claude-local-transcript-provider.js.map +1 -1
- package/dist/src/cli-core.js +102 -4
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/deferred-foreground-application-service.d.ts +18 -0
- package/dist/src/deferred-foreground-application-service.js +72 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +92 -20
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +108 -37
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-service.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-service.js +10 -3
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -1
- package/dist/src/deferred-foreground-transfer-policy.d.ts +1 -1
- package/dist/src/deferred-foreground-transfer-policy.js +3 -2
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.d.ts +17 -1
- package/dist/src/deferred-foreground-transfer.js +247 -39
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/deferred-foreground-user-close.d.ts +18 -0
- package/dist/src/deferred-foreground-user-close.js +180 -0
- package/dist/src/deferred-foreground-user-close.js.map +1 -0
- package/dist/src/openclaw-callback-transport.d.ts +9 -1
- package/dist/src/openclaw-callback-transport.js +404 -11
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.d.ts +23 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js +77 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-callback-adapter.js +96 -14
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +547 -109
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +27 -4
- package/dist/src/openclaw-plugin-helpers.js +317 -44
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +61 -66
- package/dist/src/openclaw-plugin-schemas.js +87 -91
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-plugin-supervisor.js +48 -10
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +22 -0
- package/dist/src/openclaw-private-authority-offers.js +177 -0
- package/dist/src/openclaw-private-authority-offers.js.map +1 -0
- package/dist/src/protocol.d.ts +3 -0
- package/dist/src/protocol.js +4 -0
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/store.d.ts +10 -0
- package/dist/src/store.js +82 -2
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +62 -12
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +19 -2
- package/dist/src/terminal-action-projection.js +27 -5
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +82 -0
- package/dist/src/terminal-agent-bridge.js +539 -111
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +14 -0
- package/dist/src/terminal-command-cli-adapter.js +2435 -150
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-application.d.ts +4 -1
- package/dist/src/terminal-dispatch-application.js +11 -2
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-execution.js +3 -1
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +3 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +46 -19
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.js +5 -3
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +3 -0
- package/dist/src/terminal-dispatch-receipt.js +15 -1
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +68 -7
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-service.d.ts +2 -0
- package/dist/src/terminal-dispatch-recovery-service.js +9 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +305 -79
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +30 -27
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +32 -7
- package/dist/src/terminal-list-cli-adapter.js +335 -98
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +11 -1
- package/dist/src/terminal-list-renderer.js +328 -132
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +253 -326
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +5 -0
- package/dist/src/terminal-monitor-application-service.js +5 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +25 -1
- package/dist/src/terminal-monitor-cli-adapter.js +171 -28
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +220 -32
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +814 -162
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +2 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js +12 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +31 -11
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +11 -0
- package/dist/src/terminal-status-cli-adapter.js +119 -35
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +60 -1
- package/dist/src/terminal-submission-acceptance.js +613 -0
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-retry-service.d.ts +114 -0
- package/dist/src/terminal-submission-retry-service.js +589 -0
- package/dist/src/terminal-submission-retry-service.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +28 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js +94 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.d.ts +64 -0
- package/dist/src/terminal-watch-cli-adapter.js +532 -0
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-service.d.ts +102 -0
- package/dist/src/terminal-watch-service.js +883 -0
- package/dist/src/terminal-watch-service.js.map +1 -0
- package/dist/src/terminal-watch-store.d.ts +172 -0
- package/dist/src/terminal-watch-store.js +1019 -0
- package/dist/src/terminal-watch-store.js.map +1 -0
- package/docs/quickstart-herdr.md +8 -0
- package/docs/quickstart-tmux.md +33 -6
- package/openclaw.plugin.json +11 -3
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +61 -40
|
@@ -11,13 +11,14 @@ import { projectSessionSelectorCandidate } from "./terminal-selector-projection-
|
|
|
11
11
|
import { listManagedSessions, loadNativeThreadTransition, nativeThreadTransitionsDir, tryLoadManagedSession } from "./session-store.js";
|
|
12
12
|
import { defaultStoreDir, ensureStoreWritable, inspectStoreCompatibility, listConversations, STORE_SESSION_AUTHORITY_PROTOCOL } from "./store.js";
|
|
13
13
|
import { exactCodexReadyStyledComposerCapture } from "./terminal-agent-bridge.js";
|
|
14
|
+
import { captureCodexHumanStartedActiveTaskAnchor } from "./terminal-submission-acceptance.js";
|
|
14
15
|
import { applySessionAuthorityToDispatch, authoritativeTerminalIdentity, compareManagedConversationRecency, decideManagedTerminalAssociation, decideLocalTerminalDispatchOwnership, decideTerminalSendAuthority, decideTerminalSessionAuthorityConflict, managedTurnNeedsAttention as terminalManagedTurnNeedsAttention, nonOwnerTerminalActions, projectBlockingTurn, projectHandoffDecision, projectHandoffPresentation, projectPublicManagementConflict, projectReconcileBindingAction, projectTerminalDispatchConflict, projectTerminalManagement, selectManagedTerminalHistory, selectTerminalAvailableActions } from "./terminal-action-projection.js";
|
|
15
16
|
import { childProcessIdsForRoot, decideManagedBindingConflict, deferredCodexForegroundBindingToken, exactBoundCodexSendSource, isCompleteNativeRollout, nativeAgentIdentityMatchesTurn, processIncarnationRelationship, selectRootTerminalProcesses, terminalControlAliasMatches, terminalControlsShareIncarnation, verifiedEmptyCodexHandoffToken } from "./terminal-authority-policy.js";
|
|
16
17
|
import { decideTerminalBindingMatch, terminalObservationFromListEntry } from "./terminal-binding-authority.js";
|
|
17
18
|
import { decideTerminalScopedCodexApprovalAuthority, terminalScopedCodexApprovalPromptSnapshot } from "./terminal-scoped-approval-authority.js";
|
|
18
19
|
import { sameCanonicalStatePath } from "./terminal-dispatch-ledger-codec.js";
|
|
19
20
|
import * as dispatch from "./terminal-dispatch-policy.js";
|
|
20
|
-
import { actionsForManagedSessionBinding, currentTerminalActions, listActionContracts, readOnlyListActions, readOnlyManagedTurn, renderAvailableListActions, renderCurrentManagedTurn, renderHistoricalManagedTurn, renderManagedTurnListEntry, safeUnavailableManagedTurnActions, sendActionForManagedSession, withoutGenericHandoffSourceClose } from "./terminal-list-renderer.js";
|
|
21
|
+
import { actionsForManagedSessionBinding, currentTerminalActions, listActionContracts, readOnlyListActions, readOnlyManagedTurn, userReleaseListActions, userReleasableManagedTurn, renderAvailableListActions, renderCurrentManagedTurn, renderHistoricalManagedTurn, renderManagedTurnListEntry, safeUnavailableManagedTurnActions, sendActionForManagedSession, withoutGenericHandoffSourceClose } from "./terminal-list-renderer.js";
|
|
21
22
|
import { hasCanonicalTerminalEndpoint, terminalControlEvidenceMatches } from "./terminal-control-ref.js";
|
|
22
23
|
import { validTerminalMonitorTimestampMs as validTimestampMs } from "./terminal-monitor-decision-policy.js";
|
|
23
24
|
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
@@ -46,6 +47,7 @@ export function createTerminalListCliFacade(dependencies) {
|
|
|
46
47
|
return {
|
|
47
48
|
runList: (options) => call(() => runList(options)),
|
|
48
49
|
buildTerminalListGroup: (request) => call(() => buildTerminalListGroup(request)),
|
|
50
|
+
observeExactTerminal: (request) => call(() => observeExactTerminal(request)),
|
|
49
51
|
provisionalManagedBindingTurnCount: (storeDir, session) => call(() => provisionalManagedBindingTurnCount(storeDir, session)),
|
|
50
52
|
managedSessionHasUnresolvedNativeTransition: (storeDir, session) => call(() => managedSessionHasUnresolvedNativeTransition(storeDir, session)),
|
|
51
53
|
managedSessionHasAnyNativeTransition: (storeDir, session) => call(() => managedSessionHasAnyNativeTransition(storeDir, session)),
|
|
@@ -70,56 +72,149 @@ async function runList(options) {
|
|
|
70
72
|
status: "disabled",
|
|
71
73
|
reason: "standalone list is read-only unless --reconcile is supplied"
|
|
72
74
|
};
|
|
73
|
-
const includeAll = Boolean(options.all);
|
|
74
75
|
const agentFilter = options.agent ? resolveExecutor({ kind: options.agent }).kind : undefined;
|
|
75
76
|
const statusFilter = options.status;
|
|
76
|
-
const
|
|
77
|
-
const
|
|
77
|
+
const terminalScan = await buildTerminalListGroup({ options, agentFilter, statusFilter });
|
|
78
|
+
const projected = projectTerminalListScan({
|
|
79
|
+
options,
|
|
80
|
+
storeDir,
|
|
81
|
+
store,
|
|
82
|
+
terminalScan,
|
|
83
|
+
agentFilter,
|
|
84
|
+
statusFilter
|
|
85
|
+
});
|
|
86
|
+
printJson({
|
|
87
|
+
store_dir: storeDir,
|
|
88
|
+
store,
|
|
89
|
+
reconciliation,
|
|
90
|
+
action_contracts: listActionContracts(),
|
|
91
|
+
terminals: projected.terminals,
|
|
92
|
+
terminal_watches: projected.terminalWatches,
|
|
93
|
+
unavailable_managed_turns: projected.unavailableManagedTurns,
|
|
94
|
+
terminal_scan: {
|
|
95
|
+
...terminalScan.summary,
|
|
96
|
+
terminal_count: projected.terminals.length
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
runtimeLog("info", "terminals_listed", {
|
|
100
|
+
store_dir: storeDir,
|
|
101
|
+
terminal_count: projected.terminals.length,
|
|
102
|
+
unavailable_managed_turn_count: projected.unavailableManagedTurns.length,
|
|
103
|
+
terminal_scan_error: terminalScan.summary.error,
|
|
104
|
+
include_all: projected.includeAll,
|
|
105
|
+
agent_filter: agentFilter,
|
|
106
|
+
status_filter: statusFilter,
|
|
107
|
+
reconciliation
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function projectTerminalListScan(input) {
|
|
111
|
+
const { options, storeDir, store, terminalScan, agentFilter, statusFilter, tolerateInvalidWatchRecords } = input;
|
|
112
|
+
const includeAll = Boolean(options.all);
|
|
113
|
+
const allManagedConversations = listConversations(storeDir)
|
|
78
114
|
.filter(terminalListRuntime().isDiscoverableTmuxConversation);
|
|
79
115
|
const managedSessions = store.readable
|
|
80
116
|
? listManagedSessions(storeDir)
|
|
81
117
|
: [];
|
|
82
|
-
const
|
|
118
|
+
const displayedConversations = allManagedConversations
|
|
83
119
|
.filter((conversation) => includeAll || terminalListRuntime().isActiveStatus(conversation.status))
|
|
84
120
|
.filter((conversation) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, conversation.workspace))
|
|
85
121
|
.filter((conversation) => !agentFilter || executorForConversation(conversation).kind === agentFilter)
|
|
86
122
|
.filter((conversation) => !statusFilter || conversation.status === statusFilter);
|
|
87
|
-
const
|
|
123
|
+
const workspaceConversations = allManagedConversations.filter((conversation) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, conversation.workspace));
|
|
88
124
|
const physicalTerminals = terminalScan.terminalControlled.filter((entry) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, entry.workspace ?? entry.cwd));
|
|
89
125
|
const projection = terminalFirstListProjection({
|
|
90
126
|
storeDir,
|
|
91
127
|
terminals: physicalTerminals,
|
|
92
128
|
managedSessions,
|
|
93
129
|
sessionAuthorityRequired: Number(store.writer_protocol) >= STORE_SESSION_AUTHORITY_PROTOCOL,
|
|
94
|
-
allConversations:
|
|
95
|
-
displayedConversations
|
|
130
|
+
allConversations: workspaceConversations,
|
|
131
|
+
displayedConversations,
|
|
96
132
|
includeAll,
|
|
97
133
|
managedOnly: options.managedOnly === true,
|
|
98
134
|
statusFilter,
|
|
99
135
|
mutationsAllowed: store.writable === true
|
|
100
136
|
});
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
const exactWatchScan = terminalListRuntime()
|
|
138
|
+
.scanTerminalWatchesForExactObservation;
|
|
139
|
+
const watchObservation = tolerateInvalidWatchRecords && exactWatchScan
|
|
140
|
+
? exactWatchScan(storeDir, { includeAll })
|
|
141
|
+
: {
|
|
142
|
+
watches: terminalListRuntime().listTerminalWatches?.(storeDir, { includeAll }) ?? [],
|
|
143
|
+
activeOverlayTrusted: true
|
|
144
|
+
};
|
|
145
|
+
const observedTerminalWatches = watchObservation.watches;
|
|
146
|
+
const terminalWatches = options.managedOnly
|
|
147
|
+
? []
|
|
148
|
+
: observedTerminalWatches
|
|
149
|
+
.filter((watch) => !agentFilter || watch.agent === agentFilter)
|
|
150
|
+
.filter((watch) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, watch.workspace))
|
|
151
|
+
.filter((watch) => !statusFilter || watch.status === statusFilter);
|
|
152
|
+
const activeWatchedTerminals = new Set(observedTerminalWatches
|
|
153
|
+
.filter((watch) => watch.status === "active")
|
|
154
|
+
.map((watch) => stringValue(watch.terminal_id))
|
|
155
|
+
.filter((terminalId) => terminalId !== undefined));
|
|
156
|
+
const terminals = projection.terminals.map((terminal) => !watchObservation.activeOverlayTrusted ||
|
|
157
|
+
activeWatchedTerminals.has(stringValue(terminal.id) ?? "")
|
|
158
|
+
? withoutTerminalWatchAuthority(terminal)
|
|
159
|
+
: terminal);
|
|
160
|
+
return {
|
|
161
|
+
includeAll,
|
|
162
|
+
terminals,
|
|
163
|
+
terminalWatches,
|
|
164
|
+
unavailableManagedTurns: projection.unavailableManagedTurns
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function observeExactTerminal(request) {
|
|
168
|
+
const { options, terminalId } = request;
|
|
169
|
+
const storeDir = expandHome(options.storeDir ?? options.logDir ?? defaultStoreDir(cliCwd()));
|
|
170
|
+
const store = inspectStoreCompatibility(storeDir);
|
|
171
|
+
const scan = await buildTerminalListGroup({ options, terminalId });
|
|
172
|
+
const matches = scan.terminalControlled.filter((terminal) => stringValue(terminal.id) === terminalId);
|
|
173
|
+
if (matches.length !== 1) {
|
|
174
|
+
return scan.summary.error
|
|
175
|
+
? {
|
|
176
|
+
state: "unavailable",
|
|
177
|
+
reason: scan.summary.error,
|
|
178
|
+
summary: scan.summary
|
|
179
|
+
}
|
|
180
|
+
: { state: "absent", summary: scan.summary };
|
|
181
|
+
}
|
|
182
|
+
const projected = projectTerminalListScan({
|
|
183
|
+
options,
|
|
184
|
+
storeDir,
|
|
103
185
|
store,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
terminals: projection.terminals,
|
|
107
|
-
unavailable_managed_turns: projection.unavailableManagedTurns,
|
|
108
|
-
terminal_scan: {
|
|
109
|
-
...terminalScan.summary,
|
|
110
|
-
terminal_count: projection.terminals.length
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
runtimeLog("info", "terminals_listed", {
|
|
114
|
-
store_dir: storeDir,
|
|
115
|
-
terminal_count: projection.terminals.length,
|
|
116
|
-
unavailable_managed_turn_count: projection.unavailableManagedTurns.length,
|
|
117
|
-
terminal_scan_error: terminalScan.summary.error,
|
|
118
|
-
include_all: includeAll,
|
|
119
|
-
agent_filter: agentFilter,
|
|
120
|
-
status_filter: statusFilter,
|
|
121
|
-
reconciliation
|
|
186
|
+
terminalScan: scan,
|
|
187
|
+
tolerateInvalidWatchRecords: true
|
|
122
188
|
});
|
|
189
|
+
const projectedMatches = projected.terminals.filter((terminal) => stringValue(terminal.id) === terminalId);
|
|
190
|
+
if (projectedMatches.length !== 1) {
|
|
191
|
+
return {
|
|
192
|
+
state: "unavailable",
|
|
193
|
+
reason: "the exact terminal could not be projected authoritatively",
|
|
194
|
+
summary: scan.summary
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
state: "available",
|
|
199
|
+
rawTerminal: matches[0],
|
|
200
|
+
terminal: projectedMatches[0],
|
|
201
|
+
summary: scan.summary
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function withoutAvailableAction(terminal, action) {
|
|
205
|
+
if (!isRecord(terminal.available_actions))
|
|
206
|
+
return terminal;
|
|
207
|
+
const actions = { ...terminal.available_actions };
|
|
208
|
+
delete actions[action];
|
|
209
|
+
return { ...terminal, available_actions: actions };
|
|
210
|
+
}
|
|
211
|
+
function withoutTerminalWatchAuthority(terminal) {
|
|
212
|
+
const projected = withoutAvailableAction(terminal, "watch");
|
|
213
|
+
if (!Object.hasOwn(projected, "terminal_watch_hint"))
|
|
214
|
+
return projected;
|
|
215
|
+
const safe = { ...projected };
|
|
216
|
+
delete safe.terminal_watch_hint;
|
|
217
|
+
return safe;
|
|
123
218
|
}
|
|
124
219
|
async function reconcileStoreForList(storeDir, options) {
|
|
125
220
|
try {
|
|
@@ -157,7 +252,7 @@ async function reconcileStoreForList(storeDir, options) {
|
|
|
157
252
|
idle_timeout_minutes: idle.idle_timeout_minutes
|
|
158
253
|
};
|
|
159
254
|
}
|
|
160
|
-
async function buildTerminalListGroup({ options, agentFilter, statusFilter }) {
|
|
255
|
+
async function buildTerminalListGroup({ options, agentFilter, statusFilter, terminalId }) {
|
|
161
256
|
const empty = {
|
|
162
257
|
terminalControlled: [],
|
|
163
258
|
summary: {
|
|
@@ -198,8 +293,17 @@ async function buildTerminalListGroup({ options, agentFilter, statusFilter }) {
|
|
|
198
293
|
const rootSessions = selectRootTerminalProcesses(activeSessions);
|
|
199
294
|
const controlledSessions = rootSessions.filter((session) => session.terminalControl !== undefined);
|
|
200
295
|
activeCount = controlledSessions.length;
|
|
201
|
-
|
|
202
|
-
|
|
296
|
+
const selectedSessions = terminalId
|
|
297
|
+
? controlledSessions.filter((session) => bridge.terminalConversationId(session) === terminalId)
|
|
298
|
+
: controlledSessions;
|
|
299
|
+
for (const session of selectedSessions) {
|
|
300
|
+
try {
|
|
301
|
+
terminalControlled.push(await terminalControlledListEntry(session, activeSessions, options, bridge));
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
errors.push(`terminal process ${session.pid}: ` +
|
|
305
|
+
(error instanceof Error ? error.message : String(error)));
|
|
306
|
+
}
|
|
203
307
|
}
|
|
204
308
|
}
|
|
205
309
|
catch (error) {
|
|
@@ -236,9 +340,10 @@ function managedTurnListActionFacts(task, conversation) {
|
|
|
236
340
|
const terminalBridgeReady = Boolean(conversation &&
|
|
237
341
|
terminalListRuntime().terminalBridgeEnabled(conversation) &&
|
|
238
342
|
terminalListRuntime().terminalControlFromTakeover(nativeTakeover) !== undefined);
|
|
343
|
+
const submission = terminalListRuntime().terminalBridgeSubmission(conversation);
|
|
239
344
|
const renewEligible = Boolean(terminalBridgeReady &&
|
|
240
345
|
task.status === "stalled" &&
|
|
241
|
-
|
|
346
|
+
submission?.status !== "uncertain" &&
|
|
242
347
|
!terminalListRuntime().isVerifiedDeadTerminalAgentProcess(conversation ?? {}));
|
|
243
348
|
const callbackDelivery = isRecord(conversation?.callback_delivery)
|
|
244
349
|
? conversation.callback_delivery
|
|
@@ -247,11 +352,18 @@ function managedTurnListActionFacts(task, conversation) {
|
|
|
247
352
|
conversation.legacy_callback_status_error === undefined &&
|
|
248
353
|
terminalListRuntime().callbackRetryDisposition(callbackDelivery).state ===
|
|
249
354
|
"retryable");
|
|
355
|
+
const retrySubmissionCandidate = Boolean(conversation &&
|
|
356
|
+
executorForConversation(conversation).kind === "codex" &&
|
|
357
|
+
terminalBridgeReady &&
|
|
358
|
+
task.status === "stalled" &&
|
|
359
|
+
submission?.status === "uncertain" &&
|
|
360
|
+
stringValue(submission.last_proven_stage) === "text_injected");
|
|
250
361
|
return {
|
|
251
362
|
terminalBridgeReady,
|
|
252
363
|
managedApprovalPending,
|
|
253
364
|
renewEligible,
|
|
254
|
-
retryCallbackEligible
|
|
365
|
+
retryCallbackEligible,
|
|
366
|
+
retrySubmissionCandidate
|
|
255
367
|
};
|
|
256
368
|
}
|
|
257
369
|
async function terminalControlledListEntry(session, activeSessions, options, bridge = terminalListRuntime().createTerminalAgentBridge(options)) {
|
|
@@ -272,17 +384,28 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
272
384
|
terminalTarget: terminalControl.target
|
|
273
385
|
});
|
|
274
386
|
const { orphanedDispatch, nativeIdentityObservation, nativeAgentIdentity, codexOpenRootRolloutInventory, nativeProcessUuid, nativeProcessBirth, nativeProcessEvidence } = await observeTerminalNativeListIdentity(session, terminalControl, options, bridge);
|
|
387
|
+
const effectiveTerminalState = effectiveTerminalListState({
|
|
388
|
+
session,
|
|
389
|
+
terminalState,
|
|
390
|
+
nativeIdentityObservation,
|
|
391
|
+
nativeAgentIdentity,
|
|
392
|
+
nativeProcessUuid,
|
|
393
|
+
nativeProcessBirth
|
|
394
|
+
});
|
|
395
|
+
const terminalStatusSnapshot = effectiveTerminalState._terminal_status_snapshot
|
|
396
|
+
? terminalBridgeStatusWithActivity(effectiveTerminalState._terminal_status_snapshot, effectiveTerminalState.activity_state, effectiveTerminalState.activity_reason)
|
|
397
|
+
: undefined;
|
|
275
398
|
const statusCardObservation = session.agent === "codex" &&
|
|
276
|
-
typeof
|
|
399
|
+
typeof effectiveTerminalState.screen_excerpt === "string"
|
|
277
400
|
? bridge.registry.require("codex").observeThreadLifecycle?.({
|
|
278
401
|
operation: { kind: "new_thread" },
|
|
279
402
|
phase: "before",
|
|
280
|
-
screen:
|
|
403
|
+
screen: effectiveTerminalState.screen_excerpt
|
|
281
404
|
})
|
|
282
405
|
: undefined;
|
|
283
406
|
const statusCardNativeThreadId = statusCardObservation?.status === "observed" &&
|
|
284
|
-
|
|
285
|
-
|
|
407
|
+
effectiveTerminalState.activity_state === "idle" &&
|
|
408
|
+
effectiveTerminalState.approval_state.blocked !== true &&
|
|
286
409
|
isExactNativeThreadId(statusCardObservation.nativeThreadId)
|
|
287
410
|
? statusCardObservation.nativeThreadId
|
|
288
411
|
: undefined;
|
|
@@ -304,7 +427,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
304
427
|
};
|
|
305
428
|
const codexLatentClearResumeObservationValue = session.agent === "codex"
|
|
306
429
|
? terminalListRuntime().codexLatentClearResumeObservation({
|
|
307
|
-
screen:
|
|
430
|
+
screen: effectiveTerminalState.screen_excerpt,
|
|
308
431
|
agentVersion
|
|
309
432
|
})
|
|
310
433
|
: undefined;
|
|
@@ -325,7 +448,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
325
448
|
const automatedInputComposerReady = await observeAutomatedInputComposerReady({
|
|
326
449
|
session,
|
|
327
450
|
terminalControl,
|
|
328
|
-
terminalState,
|
|
451
|
+
terminalState: effectiveTerminalState,
|
|
329
452
|
nativeIdentityObservation,
|
|
330
453
|
codexOpenRootRolloutInventory,
|
|
331
454
|
options
|
|
@@ -363,9 +486,15 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
363
486
|
confidence: session.confidence,
|
|
364
487
|
reason: session.reason,
|
|
365
488
|
terminal_control: terminalControl,
|
|
366
|
-
approval_state:
|
|
367
|
-
activity_state:
|
|
368
|
-
activity_reason:
|
|
489
|
+
approval_state: effectiveTerminalState.approval_state,
|
|
490
|
+
activity_state: effectiveTerminalState.activity_state,
|
|
491
|
+
activity_reason: effectiveTerminalState.activity_reason,
|
|
492
|
+
// Internal exact-observation evidence. The public projection strips this
|
|
493
|
+
// object; raw terminal status reuses it so screen, approval, and activity
|
|
494
|
+
// all describe the same capture.
|
|
495
|
+
...(terminalStatusSnapshot
|
|
496
|
+
? { _terminal_status_snapshot: terminalStatusSnapshot }
|
|
497
|
+
: {}),
|
|
369
498
|
// Internal action-projection evidence. terminalFirstListProjection strips
|
|
370
499
|
// this field after gating every automated-input action that can follow a
|
|
371
500
|
// human native-thread switch.
|
|
@@ -398,33 +527,20 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
398
527
|
}
|
|
399
528
|
}
|
|
400
529
|
: {}),
|
|
401
|
-
commands: {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
native_inspect: nativeInspectionCapability.status === "supported" &&
|
|
416
|
-
nativeInspectionCapability.statusInspection === true &&
|
|
417
|
-
terminalState.activity_state === "idle" &&
|
|
418
|
-
automatedInputComposerReady &&
|
|
419
|
-
terminalControl.capabilities.includes("send_keys") &&
|
|
420
|
-
terminalControl.capabilities.includes("screen_status") &&
|
|
421
|
-
(session.agent === "codex"
|
|
422
|
-
? codexLifecycleIncarnationAvailable
|
|
423
|
-
: Boolean(nativeAgentIdentity?.sessionId &&
|
|
424
|
-
nativeAgentIdentity.processUuid)) &&
|
|
425
|
-
orphanedDispatch === undefined &&
|
|
426
|
-
!terminalHasBlockingTurn
|
|
427
|
-
}
|
|
530
|
+
commands: terminalListCommands({
|
|
531
|
+
agent: session.agent,
|
|
532
|
+
terminalControl,
|
|
533
|
+
terminalState: effectiveTerminalState,
|
|
534
|
+
lifecycleCapability,
|
|
535
|
+
nativeInspectionCapability,
|
|
536
|
+
nativeAgentIdentity,
|
|
537
|
+
nativeProcessUuid,
|
|
538
|
+
nativeProcessBirth,
|
|
539
|
+
codexLifecycleIncarnationAvailable,
|
|
540
|
+
automatedInputComposerReady,
|
|
541
|
+
hasOrphanedDispatch: orphanedDispatch !== undefined,
|
|
542
|
+
terminalHasBlockingTurn
|
|
543
|
+
})
|
|
428
544
|
};
|
|
429
545
|
const availableActions = renderAvailableListActions(entry);
|
|
430
546
|
const { commands: _commands, ...publicEntry } = entry;
|
|
@@ -433,6 +549,115 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
433
549
|
available_actions: availableActions
|
|
434
550
|
};
|
|
435
551
|
}
|
|
552
|
+
function terminalListCommands(input) {
|
|
553
|
+
const { agent, terminalControl, terminalState, lifecycleCapability, nativeInspectionCapability, nativeAgentIdentity, nativeProcessUuid, nativeProcessBirth, codexLifecycleIncarnationAvailable, automatedInputComposerReady, hasOrphanedDispatch, terminalHasBlockingTurn } = input;
|
|
554
|
+
return {
|
|
555
|
+
send: !terminalHasBlockingTurn,
|
|
556
|
+
approve: terminalControl.capabilities.includes("terminal_approval") &&
|
|
557
|
+
terminalState.approval_state.approvable === true,
|
|
558
|
+
status: true,
|
|
559
|
+
cancel: terminalControl.capabilities.includes("terminal_cancel"),
|
|
560
|
+
close: hasOrphanedDispatch,
|
|
561
|
+
new_thread: lifecycleCapability.status === "supported" &&
|
|
562
|
+
lifecycleCapability.newThread === true &&
|
|
563
|
+
codexLifecycleIncarnationAvailable &&
|
|
564
|
+
!terminalHasBlockingTurn,
|
|
565
|
+
list_resumable_threads: lifecycleCapability.status === "supported" &&
|
|
566
|
+
lifecycleCapability.resumeExact === true &&
|
|
567
|
+
codexLifecycleIncarnationAvailable,
|
|
568
|
+
native_inspect: nativeInspectionCapability.status === "supported" &&
|
|
569
|
+
nativeInspectionCapability.statusInspection === true &&
|
|
570
|
+
terminalState.activity_state === "idle" &&
|
|
571
|
+
automatedInputComposerReady &&
|
|
572
|
+
terminalControl.capabilities.includes("send_keys") &&
|
|
573
|
+
terminalControl.capabilities.includes("screen_status") &&
|
|
574
|
+
(agent === "codex"
|
|
575
|
+
? codexLifecycleIncarnationAvailable
|
|
576
|
+
: Boolean(nativeAgentIdentity?.sessionId &&
|
|
577
|
+
nativeAgentIdentity.processUuid)) &&
|
|
578
|
+
!hasOrphanedDispatch &&
|
|
579
|
+
!terminalHasBlockingTurn,
|
|
580
|
+
watch: lifecycleCapability.status === "supported" &&
|
|
581
|
+
(terminalState.activity_state === "working" ||
|
|
582
|
+
terminalState.activity_state === "awaiting_approval") &&
|
|
583
|
+
Boolean(nativeAgentIdentity?.sessionId) &&
|
|
584
|
+
(agent !== "codex" || Boolean(nativeAgentIdentity?.rollout)) &&
|
|
585
|
+
Boolean(nativeProcessUuid) &&
|
|
586
|
+
Boolean(nativeProcessBirth) &&
|
|
587
|
+
!hasOrphanedDispatch &&
|
|
588
|
+
!terminalHasBlockingTurn
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function effectiveTerminalListState(input) {
|
|
592
|
+
const { session, terminalState, nativeIdentityObservation, nativeAgentIdentity, nativeProcessUuid, nativeProcessBirth } = input;
|
|
593
|
+
if (session.agent !== "codex" ||
|
|
594
|
+
terminalState.activity_state === "working" ||
|
|
595
|
+
terminalState.activity_state === "awaiting_approval" ||
|
|
596
|
+
terminalState.approval_state.blocked === true) {
|
|
597
|
+
return terminalState;
|
|
598
|
+
}
|
|
599
|
+
if (nativeIdentityObservation.status === "verified_absent") {
|
|
600
|
+
return terminalState;
|
|
601
|
+
}
|
|
602
|
+
if (nativeIdentityObservation.status !== "resolved" ||
|
|
603
|
+
!nativeAgentIdentity?.rollout ||
|
|
604
|
+
!nativeAgentIdentity.sessionId ||
|
|
605
|
+
!nativeProcessUuid ||
|
|
606
|
+
!nativeProcessBirth) {
|
|
607
|
+
return terminalListStateWithUnavailableDurableActivity(terminalState, nativeIdentityObservation.status === "unavailable"
|
|
608
|
+
? nativeIdentityObservation.reason ??
|
|
609
|
+
"exact Codex identity observation failed"
|
|
610
|
+
: "exact Codex rollout/process identity is incomplete");
|
|
611
|
+
}
|
|
612
|
+
const currentIdentity = {
|
|
613
|
+
sessionId: nativeAgentIdentity.sessionId,
|
|
614
|
+
processUuid: nativeProcessUuid,
|
|
615
|
+
processBirth: nativeProcessBirth,
|
|
616
|
+
rollout: nativeAgentIdentity.rollout
|
|
617
|
+
};
|
|
618
|
+
try {
|
|
619
|
+
if (!captureCodexHumanStartedActiveTaskAnchor({ currentIdentity })) {
|
|
620
|
+
return terminalState;
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
...terminalState,
|
|
624
|
+
activity_state: "working",
|
|
625
|
+
activity_reason: "Codex rollout contains an exact unfinished human-started task"
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
630
|
+
runtimeLog("warn", "terminal_durable_activity_unavailable", {
|
|
631
|
+
agent: session.agent,
|
|
632
|
+
pid: session.pid,
|
|
633
|
+
reason
|
|
634
|
+
});
|
|
635
|
+
return terminalListStateWithUnavailableDurableActivity(terminalState, reason);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function terminalListStateWithUnavailableDurableActivity(terminalState, reason) {
|
|
639
|
+
return {
|
|
640
|
+
...terminalState,
|
|
641
|
+
activity_state: "unknown",
|
|
642
|
+
activity_reason: `durable Codex activity evidence is unavailable: ${reason}`
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function terminalBridgeStatusWithActivity(status, activityState, activityReason) {
|
|
646
|
+
const descriptors = Object.getOwnPropertyDescriptors(status);
|
|
647
|
+
descriptors.activity_state = {
|
|
648
|
+
configurable: true,
|
|
649
|
+
enumerable: true,
|
|
650
|
+
value: activityState,
|
|
651
|
+
writable: true
|
|
652
|
+
};
|
|
653
|
+
descriptors.activity_reason = {
|
|
654
|
+
configurable: true,
|
|
655
|
+
enumerable: true,
|
|
656
|
+
value: activityReason,
|
|
657
|
+
writable: true
|
|
658
|
+
};
|
|
659
|
+
return Object.create(Object.getPrototypeOf(status), descriptors);
|
|
660
|
+
}
|
|
436
661
|
async function observeTerminalNativeListIdentity(session, terminalControl, options, bridge) {
|
|
437
662
|
const orphanedDispatch = terminalListRuntime().orphanedTerminalDispatchForRecovery(terminalControl);
|
|
438
663
|
let codexIdentityContext;
|
|
@@ -562,7 +787,7 @@ async function observeAutomatedInputComposerReady({ session, terminalControl, te
|
|
|
562
787
|
}
|
|
563
788
|
function observeTerminalListBindingAuthority(terminal, context) {
|
|
564
789
|
const { storeDir, managedSessions, allConversations, displayedConversations, mutationsAllowed, nonterminalDeferredTransfers } = context;
|
|
565
|
-
const { _automated_input_composer_ready: automatedInputComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, ...publicTerminal } = terminal;
|
|
790
|
+
const { _automated_input_composer_ready: automatedInputComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, _terminal_status_snapshot: _terminalStatusSnapshot, ...publicTerminal } = terminal;
|
|
566
791
|
const codexOpenRootRolloutInventory = isRecord(codexOpenRootRolloutInventoryValue)
|
|
567
792
|
? codexOpenRootRolloutInventoryValue
|
|
568
793
|
: undefined;
|
|
@@ -1018,14 +1243,9 @@ function observeTerminalHandoffAuthority(terminal, context, observation) {
|
|
|
1018
1243
|
})
|
|
1019
1244
|
: undefined;
|
|
1020
1245
|
const blockingHandoffTurnIds = new Set(handoffSourceBlockingTurns.map((turn) => turn.conversation_id));
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
1023
|
-
|
|
1024
|
-
// the live native thread or Turn generation changed. `blocking_turns` is
|
|
1025
|
-
// reserved for other same-incarnation blockers such as legacy collateral
|
|
1026
|
-
// stalls; clear those first, refresh, and only then decide the handoff.
|
|
1027
|
-
const terminalRecoveryBlockingTurns = terminalBlockingTurns.filter((turn) => !blockingHandoffTurnIds.has(turn.conversation_id) &&
|
|
1028
|
-
!conversationHasNonterminalDeferredTransfer(turn));
|
|
1246
|
+
// A blocking managed Turn always remains explicitly closable. Close releases
|
|
1247
|
+
// AKK management only; it does not send input or stop the coding agent.
|
|
1248
|
+
const terminalRecoveryBlockingTurns = terminalBlockingTurns;
|
|
1029
1249
|
return {
|
|
1030
1250
|
...observation,
|
|
1031
1251
|
reconcileBindingAction,
|
|
@@ -1134,11 +1354,14 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1134
1354
|
const currentTurnValue = ownership.state === "current"
|
|
1135
1355
|
? currentManagedTurnForTerminal(ownership.conversation, terminal, sessionAwareRawActions)
|
|
1136
1356
|
: undefined;
|
|
1137
|
-
const currentTurnProjection = currentTurnValue
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1357
|
+
const currentTurnProjection = currentTurnValue
|
|
1358
|
+
? !mutationsAllowed
|
|
1359
|
+
? readOnlyManagedTurn(currentTurnValue)
|
|
1360
|
+
: ownership.state === "current" &&
|
|
1361
|
+
conversationHasNonterminalDeferredTransfer(ownership.conversation)
|
|
1362
|
+
? userReleasableManagedTurn(currentTurnValue)
|
|
1363
|
+
: currentTurnValue
|
|
1364
|
+
: undefined;
|
|
1142
1365
|
const currentTurn = currentTurnProjection
|
|
1143
1366
|
? withoutGenericHandoffSourceClose(currentTurnProjection, blockingHandoffTurnIds)
|
|
1144
1367
|
: undefined;
|
|
@@ -1155,20 +1378,24 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1155
1378
|
const recentTurnValue = recentConversation
|
|
1156
1379
|
? historicalManagedTurnForTerminal(recentConversation)
|
|
1157
1380
|
: undefined;
|
|
1158
|
-
const recentTurnProjection = recentTurnValue
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1381
|
+
const recentTurnProjection = recentTurnValue
|
|
1382
|
+
? !mutationsAllowed
|
|
1383
|
+
? readOnlyManagedTurn(recentTurnValue)
|
|
1384
|
+
: recentConversation &&
|
|
1385
|
+
conversationHasNonterminalDeferredTransfer(recentConversation)
|
|
1386
|
+
? userReleasableManagedTurn(recentTurnValue)
|
|
1387
|
+
: recentTurnValue
|
|
1388
|
+
: undefined;
|
|
1163
1389
|
const recentTurn = recentTurnProjection
|
|
1164
1390
|
? withoutGenericHandoffSourceClose(recentTurnProjection, blockingHandoffTurnIds)
|
|
1165
1391
|
: undefined;
|
|
1166
1392
|
const history = historyConversations.map((conversation) => {
|
|
1167
1393
|
const turn = historicalManagedTurnForTerminal(conversation);
|
|
1168
|
-
return withoutGenericHandoffSourceClose(mutationsAllowed
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1394
|
+
return withoutGenericHandoffSourceClose(!mutationsAllowed
|
|
1395
|
+
? readOnlyManagedTurn(turn)
|
|
1396
|
+
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
1397
|
+
? userReleasableManagedTurn(turn)
|
|
1398
|
+
: turn, blockingHandoffTurnIds);
|
|
1172
1399
|
});
|
|
1173
1400
|
const visibleTurnIds = new Set([currentTurn, recentTurn, ...history]
|
|
1174
1401
|
.map((turn) => stringValue(turn?.conversation_id))
|
|
@@ -1345,14 +1572,17 @@ function terminalFirstListProjection({ storeDir, terminals, managedSessions, ses
|
|
|
1345
1572
|
});
|
|
1346
1573
|
return {
|
|
1347
1574
|
...managedTurn,
|
|
1348
|
-
available_actions: mutationsAllowed
|
|
1349
|
-
|
|
1350
|
-
? safeUnavailableManagedTurnActions(isRecord(managedTurn.available_actions)
|
|
1575
|
+
available_actions: !mutationsAllowed
|
|
1576
|
+
? readOnlyListActions(isRecord(managedTurn.available_actions)
|
|
1351
1577
|
? managedTurn.available_actions
|
|
1352
1578
|
: {})
|
|
1353
|
-
:
|
|
1354
|
-
? managedTurn.available_actions
|
|
1355
|
-
|
|
1579
|
+
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
1580
|
+
? userReleaseListActions(isRecord(managedTurn.available_actions)
|
|
1581
|
+
? managedTurn.available_actions
|
|
1582
|
+
: {}, turnIdForConversation(conversation))
|
|
1583
|
+
: safeUnavailableManagedTurnActions(isRecord(managedTurn.available_actions)
|
|
1584
|
+
? managedTurn.available_actions
|
|
1585
|
+
: {}),
|
|
1356
1586
|
terminal_availability: {
|
|
1357
1587
|
available: false,
|
|
1358
1588
|
reason: managedOnly
|
|
@@ -1995,6 +2225,7 @@ async function listStateForTerminal(agent, terminalControl, options, bridge = te
|
|
|
1995
2225
|
activity_state: status.activity_state,
|
|
1996
2226
|
activity_reason: status.activity_reason,
|
|
1997
2227
|
capability_limitation: status.capability_limitation,
|
|
2228
|
+
_terminal_status_snapshot: status,
|
|
1998
2229
|
// Internal projection evidence; terminalControlledListEntry selects all
|
|
1999
2230
|
// public fields explicitly and never exposes the pane excerpt itself.
|
|
2000
2231
|
screen_excerpt: status.screen.excerpt
|
|
@@ -2048,6 +2279,12 @@ function managedListApprovalState(conversation) {
|
|
|
2048
2279
|
}
|
|
2049
2280
|
async function resolveConversationSelectorOption(commandName, options) {
|
|
2050
2281
|
const sendOperation = commandName === "send";
|
|
2282
|
+
// Submission retry is already bound to one authoritative managed Turn.
|
|
2283
|
+
// Keep it out of ordinary-send selector discovery so an omitted Session
|
|
2284
|
+
// target cannot be inferred and mixed into the exact `send --turn` form.
|
|
2285
|
+
if (sendOperation && stringValue(options.turn)) {
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2051
2288
|
if (stringValue(options.expectedTerminalToken)) {
|
|
2052
2289
|
// Selector resolution may replace an alias (or an omitted selector) with a
|
|
2053
2290
|
// discovered full terminal id. Preserve the caller's actual authority so
|