@scotthuang/agent-knock-knock 0.12.13 → 0.12.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +13 -13
- package/dist/src/callback-cli-adapter.d.ts +9 -6
- package/dist/src/callback-cli-adapter.js +119 -30
- package/dist/src/callback-cli-adapter.js.map +1 -1
- package/dist/src/callback-outbox-policy.d.ts +39 -0
- package/dist/src/callback-outbox-policy.js +163 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +28 -1
- package/dist/src/callback-outbox-service.js +413 -82
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.d.ts +14 -1
- package/dist/src/callback-outbox-settlement.js +151 -30
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-authority.d.ts +59 -0
- package/dist/src/callback-route-authority.js +123 -0
- package/dist/src/callback-route-authority.js.map +1 -0
- package/dist/src/callback-transport.d.ts +134 -0
- package/dist/src/callback-transport.js +256 -0
- package/dist/src/callback-transport.js.map +1 -0
- package/dist/src/cli-core.js +56 -18
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/cli-runtime-context.d.ts +2 -0
- package/dist/src/cli-runtime-context.js.map +1 -1
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/deferred-foreground-application-service.d.ts +18 -0
- package/dist/src/deferred-foreground-application-service.js +72 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +10 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +108 -37
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-service.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-service.js +10 -3
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -1
- package/dist/src/deferred-foreground-transfer-policy.d.ts +1 -1
- package/dist/src/deferred-foreground-transfer-policy.js +3 -2
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.d.ts +17 -1
- package/dist/src/deferred-foreground-transfer.js +244 -35
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/deferred-foreground-user-close.d.ts +18 -0
- package/dist/src/deferred-foreground-user-close.js +180 -0
- package/dist/src/deferred-foreground-user-close.js.map +1 -0
- package/dist/src/install-doctor-command-adapter.js +1 -1
- package/dist/src/install-doctor-command-adapter.js.map +1 -1
- package/dist/src/openclaw-callback-transport.d.ts +4 -1
- package/dist/src/openclaw-callback-transport.js +403 -10
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/openclaw-doctor.js +16 -2
- package/dist/src/openclaw-doctor.js.map +1 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.d.ts +23 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js +77 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.js +93 -39
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +1 -0
- package/dist/src/openclaw-plugin-helpers.js +9 -1
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +20 -1
- package/dist/src/openclaw-plugin-schemas.js +27 -3
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/protocol.d.ts +3 -0
- package/dist/src/protocol.js +4 -0
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/store.d.ts +12 -1
- package/dist/src/store.js +81 -3
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +62 -12
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +31 -3
- package/dist/src/terminal-action-projection.js +64 -9
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +82 -0
- package/dist/src/terminal-agent-bridge.js +553 -109
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +26 -2
- package/dist/src/terminal-command-cli-adapter.js +3162 -214
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.d.ts +4 -22
- package/dist/src/terminal-delegate-cli-adapter.js +239 -192
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-send-binding.d.ts +63 -0
- package/dist/src/terminal-delegate-send-binding.js +360 -0
- package/dist/src/terminal-delegate-send-binding.js.map +1 -0
- package/dist/src/terminal-dispatch-application.d.ts +4 -1
- package/dist/src/terminal-dispatch-application.js +11 -2
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-composition.d.ts +5 -0
- package/dist/src/terminal-dispatch-execution.js +3 -1
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +3 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +46 -19
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.js +5 -3
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +3 -0
- package/dist/src/terminal-dispatch-receipt.js +15 -1
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +68 -7
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-service.d.ts +2 -0
- package/dist/src/terminal-dispatch-recovery-service.js +9 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
- package/dist/src/terminal-dispatch-repository-cli-adapter.d.ts +4 -2
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +10 -3
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +34 -27
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +6 -0
- package/dist/src/terminal-list-cli-adapter.js +391 -177
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +9 -1
- package/dist/src/terminal-list-renderer.js +226 -104
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +253 -326
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +5 -0
- package/dist/src/terminal-monitor-application-service.js +5 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +25 -1
- package/dist/src/terminal-monitor-cli-adapter.js +170 -27
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +220 -32
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +808 -147
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +2 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js +12 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +31 -11
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -1
- package/dist/src/terminal-process-source.d.ts +13 -0
- package/dist/src/terminal-process-source.js +37 -0
- package/dist/src/terminal-process-source.js.map +1 -1
- package/dist/src/terminal-runtime-cli-adapter.js +14 -2
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +1 -0
- package/dist/src/terminal-status-cli-adapter.js +30 -28
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.js +78 -25
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-retry-service.d.ts +114 -0
- package/dist/src/terminal-submission-retry-service.js +589 -0
- package/dist/src/terminal-submission-retry-service.js.map +1 -0
- package/dist/src/terminal-user-send-intent.d.ts +65 -0
- package/dist/src/terminal-user-send-intent.js +317 -0
- package/dist/src/terminal-user-send-intent.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +10 -2
- package/dist/src/terminal-watch-callback-cli-adapter.js +76 -37
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-adapter.js +41 -17
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-service.d.ts +11 -6
- package/dist/src/terminal-watch-service.js +210 -41
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +23 -0
- package/dist/src/terminal-watch-store.js +162 -4
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/quickstart-herdr.md +1 -1
- package/docs/quickstart-tmux.md +3 -3
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +14 -11
|
@@ -9,16 +9,16 @@ import { executorForConversation, isSessionSendBlockingStatus, isTerminalDispatc
|
|
|
9
9
|
import { resolveSessionSelector, sessionShortRef } from "./session-selector.js";
|
|
10
10
|
import { projectSessionSelectorCandidate } from "./terminal-selector-projection-service.js";
|
|
11
11
|
import { listManagedSessions, loadNativeThreadTransition, nativeThreadTransitionsDir, tryLoadManagedSession } from "./session-store.js";
|
|
12
|
-
import { defaultStoreDir, ensureStoreWritable, inspectStoreCompatibility, listConversations, STORE_SESSION_AUTHORITY_PROTOCOL } from "./store.js";
|
|
12
|
+
import { defaultStoreDir, ensureStoreWritable, inspectStoreCompatibility, listConversations, storeManifestPath, STORE_SESSION_AUTHORITY_PROTOCOL } from "./store.js";
|
|
13
13
|
import { exactCodexReadyStyledComposerCapture } from "./terminal-agent-bridge.js";
|
|
14
14
|
import { captureCodexHumanStartedActiveTaskAnchor } from "./terminal-submission-acceptance.js";
|
|
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
|
+
import { applySessionAuthorityToDispatch, authoritativeTerminalIdentity, compareManagedConversationRecency, decideManagedTerminalAssociation, decideLocalTerminalDispatchOwnership, decideTerminalSendAuthority, decideTerminalSessionAuthorityConflict, decideTerminalUserExplicitSendAuthority, managedTurnNeedsAttention as terminalManagedTurnNeedsAttention, nonOwnerTerminalActions, projectBlockingTurn, projectHandoffDecision, projectHandoffPresentation, projectPublicManagementConflict, projectReconcileBindingAction, projectTerminalDispatchConflict, projectTerminalManagement, selectManagedTerminalHistory, selectTerminalAvailableActions } from "./terminal-action-projection.js";
|
|
16
16
|
import { childProcessIdsForRoot, decideManagedBindingConflict, deferredCodexForegroundBindingToken, exactBoundCodexSendSource, isCompleteNativeRollout, nativeAgentIdentityMatchesTurn, processIncarnationRelationship, selectRootTerminalProcesses, terminalControlAliasMatches, terminalControlsShareIncarnation, verifiedEmptyCodexHandoffToken } from "./terminal-authority-policy.js";
|
|
17
17
|
import { decideTerminalBindingMatch, terminalObservationFromListEntry } from "./terminal-binding-authority.js";
|
|
18
18
|
import { decideTerminalScopedCodexApprovalAuthority, terminalScopedCodexApprovalPromptSnapshot } from "./terminal-scoped-approval-authority.js";
|
|
19
19
|
import { sameCanonicalStatePath } from "./terminal-dispatch-ledger-codec.js";
|
|
20
20
|
import * as dispatch from "./terminal-dispatch-policy.js";
|
|
21
|
-
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";
|
|
22
22
|
import { hasCanonicalTerminalEndpoint, terminalControlEvidenceMatches } from "./terminal-control-ref.js";
|
|
23
23
|
import { validTerminalMonitorTimestampMs as validTimestampMs } from "./terminal-monitor-decision-policy.js";
|
|
24
24
|
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
@@ -65,13 +65,29 @@ function projectSelectorCandidate(entry, commandName, observedAtMs, options) {
|
|
|
65
65
|
}
|
|
66
66
|
async function runList(options) {
|
|
67
67
|
const storeDir = expandHome(options.storeDir ?? options.logDir ?? defaultStoreDir(cliCwd()));
|
|
68
|
-
const store =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
const store = inspectStoreCompatibilityForTerminalList(storeDir);
|
|
69
|
+
let reconciliation;
|
|
70
|
+
if (options.reconcile === true) {
|
|
71
|
+
try {
|
|
72
|
+
reconciliation = await reconcileStoreForList(storeDir, options);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
reconciliation = {
|
|
76
|
+
status: "failed",
|
|
77
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
78
|
+
};
|
|
79
|
+
runtimeLog("warn", "terminal_list_reconciliation_failed", {
|
|
80
|
+
store_dir: storeDir,
|
|
81
|
+
error: reconciliation.reason
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
reconciliation = {
|
|
72
87
|
status: "disabled",
|
|
73
88
|
reason: "standalone list is read-only unless --reconcile is supplied"
|
|
74
89
|
};
|
|
90
|
+
}
|
|
75
91
|
const agentFilter = options.agent ? resolveExecutor({ kind: options.agent }).kind : undefined;
|
|
76
92
|
const statusFilter = options.status;
|
|
77
93
|
const terminalScan = await buildTerminalListGroup({ options, agentFilter, statusFilter });
|
|
@@ -107,14 +123,75 @@ async function runList(options) {
|
|
|
107
123
|
reconciliation
|
|
108
124
|
});
|
|
109
125
|
}
|
|
126
|
+
function inspectStoreCompatibilityForTerminalList(storeDir) {
|
|
127
|
+
try {
|
|
128
|
+
return inspectStoreCompatibility(storeDir);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
status: "incompatible",
|
|
133
|
+
store_dir: storeDir,
|
|
134
|
+
manifest_path: storeManifestPath(storeDir),
|
|
135
|
+
readable: false,
|
|
136
|
+
writable: false,
|
|
137
|
+
reason: `AKK Store metadata is unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function physicalOnlyTerminalProjection(terminals, reason) {
|
|
142
|
+
return {
|
|
143
|
+
terminals: terminals.map((terminal) => {
|
|
144
|
+
const { _automated_input_composer_ready: _composer, _codex_open_root_rollout_inventory: _inventory, _codex_latent_clear_resume: _resume, _terminal_user_explicit_send_action: terminalUserExplicitSendAction, _terminal_status_snapshot: _status, ...publicTerminal } = terminal;
|
|
145
|
+
const actions = isRecord(publicTerminal.available_actions)
|
|
146
|
+
? publicTerminal.available_actions
|
|
147
|
+
: {};
|
|
148
|
+
const send = isRecord(terminalUserExplicitSendAction)
|
|
149
|
+
? terminalUserExplicitSendAction
|
|
150
|
+
: undefined;
|
|
151
|
+
const status = isRecord(actions.status) ? actions.status : undefined;
|
|
152
|
+
return {
|
|
153
|
+
...publicTerminal,
|
|
154
|
+
management_state: "unavailable",
|
|
155
|
+
management_unavailable: { reason },
|
|
156
|
+
managed: {
|
|
157
|
+
session_id: null,
|
|
158
|
+
session_short_ref: null,
|
|
159
|
+
current_turn: null,
|
|
160
|
+
recent_turn: null,
|
|
161
|
+
turn_count: 0,
|
|
162
|
+
hidden_turn_count: 0,
|
|
163
|
+
session_count: 0
|
|
164
|
+
},
|
|
165
|
+
available_actions: {
|
|
166
|
+
...(status ? { status } : {}),
|
|
167
|
+
...(send ? { send } : {})
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}),
|
|
171
|
+
unavailableManagedTurns: []
|
|
172
|
+
};
|
|
173
|
+
}
|
|
110
174
|
function projectTerminalListScan(input) {
|
|
111
175
|
const { options, storeDir, store, terminalScan, agentFilter, statusFilter, tolerateInvalidWatchRecords } = input;
|
|
112
176
|
const includeAll = Boolean(options.all);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
177
|
+
const managementErrors = [];
|
|
178
|
+
let allManagedConversations = [];
|
|
179
|
+
try {
|
|
180
|
+
allManagedConversations = listConversations(storeDir)
|
|
181
|
+
.filter(terminalListRuntime().isDiscoverableTmuxConversation);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
managementErrors.push(`managed Turn inventory is unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
185
|
+
}
|
|
186
|
+
let managedSessions = [];
|
|
187
|
+
if (store.readable) {
|
|
188
|
+
try {
|
|
189
|
+
managedSessions = listManagedSessions(storeDir);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
managementErrors.push(`managed Session inventory is unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
118
195
|
const displayedConversations = allManagedConversations
|
|
119
196
|
.filter((conversation) => includeAll || terminalListRuntime().isActiveStatus(conversation.status))
|
|
120
197
|
.filter((conversation) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, conversation.workspace))
|
|
@@ -122,26 +199,48 @@ function projectTerminalListScan(input) {
|
|
|
122
199
|
.filter((conversation) => !statusFilter || conversation.status === statusFilter);
|
|
123
200
|
const workspaceConversations = allManagedConversations.filter((conversation) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, conversation.workspace));
|
|
124
201
|
const physicalTerminals = terminalScan.terminalControlled.filter((entry) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, entry.workspace ?? entry.cwd));
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
202
|
+
let projection;
|
|
203
|
+
if (managementErrors.length === 0) {
|
|
204
|
+
try {
|
|
205
|
+
projection = terminalFirstListProjection({
|
|
206
|
+
storeDir,
|
|
207
|
+
terminals: physicalTerminals,
|
|
208
|
+
managedSessions,
|
|
209
|
+
sessionAuthorityRequired: Number(store.writer_protocol) >= STORE_SESSION_AUTHORITY_PROTOCOL,
|
|
210
|
+
allConversations: workspaceConversations,
|
|
211
|
+
displayedConversations,
|
|
212
|
+
includeAll,
|
|
213
|
+
managedOnly: options.managedOnly === true,
|
|
214
|
+
statusFilter,
|
|
215
|
+
mutationsAllowed: store.writable === true
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
managementErrors.push(`managed terminal projection is unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
220
|
+
projection = physicalOnlyTerminalProjection(physicalTerminals, managementErrors.join("; "));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
projection = physicalOnlyTerminalProjection(physicalTerminals, managementErrors.join("; "));
|
|
225
|
+
}
|
|
137
226
|
const exactWatchScan = terminalListRuntime()
|
|
138
227
|
.scanTerminalWatchesForExactObservation;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
228
|
+
let watchObservation;
|
|
229
|
+
try {
|
|
230
|
+
watchObservation = tolerateInvalidWatchRecords && exactWatchScan
|
|
231
|
+
? exactWatchScan(storeDir, { includeAll })
|
|
232
|
+
: {
|
|
233
|
+
watches: terminalListRuntime().listTerminalWatches?.(storeDir, { includeAll }) ?? [],
|
|
234
|
+
activeOverlayTrusted: true
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
runtimeLog("warn", "terminal_watch_inventory_unavailable", {
|
|
239
|
+
store_dir: storeDir,
|
|
240
|
+
error: error instanceof Error ? error.message : String(error)
|
|
241
|
+
});
|
|
242
|
+
watchObservation = { watches: [], activeOverlayTrusted: false };
|
|
243
|
+
}
|
|
145
244
|
const observedTerminalWatches = watchObservation.watches;
|
|
146
245
|
const terminalWatches = options.managedOnly
|
|
147
246
|
? []
|
|
@@ -167,7 +266,7 @@ function projectTerminalListScan(input) {
|
|
|
167
266
|
async function observeExactTerminal(request) {
|
|
168
267
|
const { options, terminalId } = request;
|
|
169
268
|
const storeDir = expandHome(options.storeDir ?? options.logDir ?? defaultStoreDir(cliCwd()));
|
|
170
|
-
const store =
|
|
269
|
+
const store = inspectStoreCompatibilityForTerminalList(storeDir);
|
|
171
270
|
const scan = await buildTerminalListGroup({ options, terminalId });
|
|
172
271
|
const matches = scan.terminalControlled.filter((terminal) => stringValue(terminal.id) === terminalId);
|
|
173
272
|
if (matches.length !== 1) {
|
|
@@ -340,9 +439,10 @@ function managedTurnListActionFacts(task, conversation) {
|
|
|
340
439
|
const terminalBridgeReady = Boolean(conversation &&
|
|
341
440
|
terminalListRuntime().terminalBridgeEnabled(conversation) &&
|
|
342
441
|
terminalListRuntime().terminalControlFromTakeover(nativeTakeover) !== undefined);
|
|
442
|
+
const submission = terminalListRuntime().terminalBridgeSubmission(conversation);
|
|
343
443
|
const renewEligible = Boolean(terminalBridgeReady &&
|
|
344
444
|
task.status === "stalled" &&
|
|
345
|
-
|
|
445
|
+
submission?.status !== "uncertain" &&
|
|
346
446
|
!terminalListRuntime().isVerifiedDeadTerminalAgentProcess(conversation ?? {}));
|
|
347
447
|
const callbackDelivery = isRecord(conversation?.callback_delivery)
|
|
348
448
|
? conversation.callback_delivery
|
|
@@ -351,11 +451,18 @@ function managedTurnListActionFacts(task, conversation) {
|
|
|
351
451
|
conversation.legacy_callback_status_error === undefined &&
|
|
352
452
|
terminalListRuntime().callbackRetryDisposition(callbackDelivery).state ===
|
|
353
453
|
"retryable");
|
|
454
|
+
const retrySubmissionCandidate = Boolean(conversation &&
|
|
455
|
+
executorForConversation(conversation).kind === "codex" &&
|
|
456
|
+
terminalBridgeReady &&
|
|
457
|
+
task.status === "stalled" &&
|
|
458
|
+
submission?.status === "uncertain" &&
|
|
459
|
+
stringValue(submission.last_proven_stage) === "text_injected");
|
|
354
460
|
return {
|
|
355
461
|
terminalBridgeReady,
|
|
356
462
|
managedApprovalPending,
|
|
357
463
|
renewEligible,
|
|
358
|
-
retryCallbackEligible
|
|
464
|
+
retryCallbackEligible,
|
|
465
|
+
retrySubmissionCandidate
|
|
359
466
|
};
|
|
360
467
|
}
|
|
361
468
|
async function terminalControlledListEntry(session, activeSessions, options, bridge = terminalListRuntime().createTerminalAgentBridge(options)) {
|
|
@@ -436,15 +543,35 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
436
543
|
});
|
|
437
544
|
const codexLifecycleIncarnationAvailable = session.agent !== "codex" ||
|
|
438
545
|
Boolean(nativeProcessUuid && nativeProcessBirth);
|
|
439
|
-
|
|
546
|
+
let terminalHasBlockingTurn = true;
|
|
547
|
+
try {
|
|
548
|
+
terminalHasBlockingTurn = terminalIncarnationBlockingTurns(terminalListRuntime().storeDirFromOptions(options), terminalControl).length > 0;
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
runtimeLog("warn", "terminal_managed_turn_inventory_unavailable", {
|
|
552
|
+
terminal_target: terminalControl.target,
|
|
553
|
+
error: error instanceof Error ? error.message : String(error)
|
|
554
|
+
});
|
|
555
|
+
}
|
|
440
556
|
const automatedInputComposerReady = await observeAutomatedInputComposerReady({
|
|
441
557
|
session,
|
|
442
558
|
terminalControl,
|
|
443
559
|
terminalState: effectiveTerminalState,
|
|
444
|
-
nativeIdentityObservation,
|
|
445
|
-
codexOpenRootRolloutInventory,
|
|
446
560
|
options
|
|
447
561
|
});
|
|
562
|
+
let physicalProcessIncarnation;
|
|
563
|
+
try {
|
|
564
|
+
physicalProcessIncarnation =
|
|
565
|
+
terminalListRuntime().processIncarnationForPid(session.pid);
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
runtimeLog("warn", "terminal_physical_process_incarnation_unavailable", {
|
|
569
|
+
agent: session.agent,
|
|
570
|
+
terminal_target: terminalControl.target,
|
|
571
|
+
pid: session.pid,
|
|
572
|
+
error: error instanceof Error ? error.message : String(error)
|
|
573
|
+
});
|
|
574
|
+
}
|
|
448
575
|
const entry = {
|
|
449
576
|
id: bridge.terminalConversationId(session),
|
|
450
577
|
short_ref: sessionShortRef(bridge.terminalConversationId(session)),
|
|
@@ -534,11 +661,38 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
534
661
|
terminalHasBlockingTurn
|
|
535
662
|
})
|
|
536
663
|
};
|
|
537
|
-
const
|
|
664
|
+
const renderedActions = renderAvailableListActions(entry);
|
|
665
|
+
const terminalUserExplicitSendAuthority = decideTerminalUserExplicitSendAuthority({
|
|
666
|
+
exactTerminalRow: true,
|
|
667
|
+
terminalId: entry.id,
|
|
668
|
+
processState: entry.process_state,
|
|
669
|
+
terminalControl,
|
|
670
|
+
agent: session.agent,
|
|
671
|
+
pid: session.pid,
|
|
672
|
+
processUuid: physicalProcessIncarnation?.processUuid,
|
|
673
|
+
processBirth: physicalProcessIncarnation?.processBirth,
|
|
674
|
+
approvalScanned: effectiveTerminalState.approval_state.scanned === true,
|
|
675
|
+
approvalBlocked: effectiveTerminalState.approval_state.blocked === true,
|
|
676
|
+
exactEmptyComposer: automatedInputComposerReady
|
|
677
|
+
});
|
|
678
|
+
const terminalUserExplicitSendAction = terminalUserExplicitSendAuthority.eligible
|
|
679
|
+
? {
|
|
680
|
+
tool: "agent_knock_knock_send",
|
|
681
|
+
arguments: {
|
|
682
|
+
selector: terminalUserExplicitSendAuthority.terminalId,
|
|
683
|
+
expected_terminal_token: terminalUserExplicitSendAuthority.expectedTerminalToken
|
|
684
|
+
},
|
|
685
|
+
missing_required: ["request"],
|
|
686
|
+
scope: "terminal_user_explicit"
|
|
687
|
+
}
|
|
688
|
+
: undefined;
|
|
538
689
|
const { commands: _commands, ...publicEntry } = entry;
|
|
539
690
|
return {
|
|
540
691
|
...publicEntry,
|
|
541
|
-
|
|
692
|
+
...(terminalUserExplicitSendAction
|
|
693
|
+
? { _terminal_user_explicit_send_action: terminalUserExplicitSendAction }
|
|
694
|
+
: {}),
|
|
695
|
+
available_actions: renderedActions
|
|
542
696
|
};
|
|
543
697
|
}
|
|
544
698
|
function terminalListCommands(input) {
|
|
@@ -651,7 +805,17 @@ function terminalBridgeStatusWithActivity(status, activityState, activityReason)
|
|
|
651
805
|
return Object.create(Object.getPrototypeOf(status), descriptors);
|
|
652
806
|
}
|
|
653
807
|
async function observeTerminalNativeListIdentity(session, terminalControl, options, bridge) {
|
|
654
|
-
|
|
808
|
+
let orphanedDispatch;
|
|
809
|
+
try {
|
|
810
|
+
orphanedDispatch =
|
|
811
|
+
terminalListRuntime().orphanedTerminalDispatchForRecovery(terminalControl);
|
|
812
|
+
}
|
|
813
|
+
catch (error) {
|
|
814
|
+
runtimeLog("warn", "terminal_orphaned_dispatch_observation_unavailable", {
|
|
815
|
+
terminal_target: terminalControl.target,
|
|
816
|
+
error: error instanceof Error ? error.message : String(error)
|
|
817
|
+
});
|
|
818
|
+
}
|
|
655
819
|
let codexIdentityContext;
|
|
656
820
|
if (session.agent === "codex") {
|
|
657
821
|
try {
|
|
@@ -677,15 +841,25 @@ async function observeTerminalNativeListIdentity(session, terminalControl, optio
|
|
|
677
841
|
});
|
|
678
842
|
}
|
|
679
843
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
844
|
+
let nativeIdentityObservation;
|
|
845
|
+
try {
|
|
846
|
+
nativeIdentityObservation =
|
|
847
|
+
await terminalListRuntime().observeCurrentNativeAgentSessionIdentity({
|
|
848
|
+
options,
|
|
849
|
+
agent: session.agent,
|
|
850
|
+
pid: session.pid,
|
|
851
|
+
cwd: session.cwd ?? terminalControl.currentPath,
|
|
852
|
+
preferredSessionId: codexIdentityContext?.preferredSessionId,
|
|
853
|
+
allowedCompanionIdentity: codexIdentityContext?.companions.primary,
|
|
854
|
+
allowedAdditionalIdentities: codexIdentityContext?.companions.additional
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
catch (error) {
|
|
858
|
+
nativeIdentityObservation = {
|
|
859
|
+
status: "unavailable",
|
|
860
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
861
|
+
};
|
|
862
|
+
}
|
|
689
863
|
const nativeAgentIdentity = nativeIdentityObservation.status === "resolved"
|
|
690
864
|
? nativeIdentityObservation.identity
|
|
691
865
|
: undefined;
|
|
@@ -752,13 +926,9 @@ async function observeTerminalNativeListIdentity(session, terminalControl, optio
|
|
|
752
926
|
nativeProcessEvidence
|
|
753
927
|
};
|
|
754
928
|
}
|
|
755
|
-
async function observeAutomatedInputComposerReady({ session, terminalControl, terminalState,
|
|
929
|
+
async function observeAutomatedInputComposerReady({ session, terminalControl, terminalState, options }) {
|
|
756
930
|
let ready = terminalListRuntime().nativeInspectionComposerEmpty(session.agent, terminalState.screen_excerpt);
|
|
757
931
|
if (session.agent !== "codex" ||
|
|
758
|
-
(terminalState.activity_state !== "idle" &&
|
|
759
|
-
(terminalState.activity_state !== "unknown" ||
|
|
760
|
-
(nativeIdentityObservation.status !== "verified_absent" &&
|
|
761
|
-
codexOpenRootRolloutInventory === undefined))) ||
|
|
762
932
|
terminalState.approval_state.blocked === true ||
|
|
763
933
|
!terminalControl.capabilities.includes("send_keys") ||
|
|
764
934
|
!terminalControl.capabilities.includes("screen_status")) {
|
|
@@ -779,7 +949,7 @@ async function observeAutomatedInputComposerReady({ session, terminalControl, te
|
|
|
779
949
|
}
|
|
780
950
|
function observeTerminalListBindingAuthority(terminal, context) {
|
|
781
951
|
const { storeDir, managedSessions, allConversations, displayedConversations, mutationsAllowed, nonterminalDeferredTransfers } = context;
|
|
782
|
-
const { _automated_input_composer_ready: automatedInputComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, _terminal_status_snapshot: _terminalStatusSnapshot, ...publicTerminal } = terminal;
|
|
952
|
+
const { _automated_input_composer_ready: automatedInputComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, _terminal_user_explicit_send_action: terminalUserExplicitSendAction, _terminal_status_snapshot: _terminalStatusSnapshot, ...publicTerminal } = terminal;
|
|
783
953
|
const codexOpenRootRolloutInventory = isRecord(codexOpenRootRolloutInventoryValue)
|
|
784
954
|
? codexOpenRootRolloutInventoryValue
|
|
785
955
|
: undefined;
|
|
@@ -938,6 +1108,7 @@ function observeTerminalListBindingAuthority(terminal, context) {
|
|
|
938
1108
|
automatedInputComposerReady,
|
|
939
1109
|
codexOpenRootRolloutInventory,
|
|
940
1110
|
codexLatentClearResumeValue,
|
|
1111
|
+
terminalUserExplicitSendAction,
|
|
941
1112
|
publicTerminal,
|
|
942
1113
|
terminalControl,
|
|
943
1114
|
terminalHasNonterminalDeferredTransfer,
|
|
@@ -1235,14 +1406,9 @@ function observeTerminalHandoffAuthority(terminal, context, observation) {
|
|
|
1235
1406
|
})
|
|
1236
1407
|
: undefined;
|
|
1237
1408
|
const blockingHandoffTurnIds = new Set(handoffSourceBlockingTurns.map((turn) => turn.conversation_id));
|
|
1238
|
-
//
|
|
1239
|
-
//
|
|
1240
|
-
|
|
1241
|
-
// the live native thread or Turn generation changed. `blocking_turns` is
|
|
1242
|
-
// reserved for other same-incarnation blockers such as legacy collateral
|
|
1243
|
-
// stalls; clear those first, refresh, and only then decide the handoff.
|
|
1244
|
-
const terminalRecoveryBlockingTurns = terminalBlockingTurns.filter((turn) => !blockingHandoffTurnIds.has(turn.conversation_id) &&
|
|
1245
|
-
!conversationHasNonterminalDeferredTransfer(turn));
|
|
1409
|
+
// A blocking managed Turn always remains explicitly closable. Close releases
|
|
1410
|
+
// AKK management only; it does not send input or stop the coding agent.
|
|
1411
|
+
const terminalRecoveryBlockingTurns = terminalBlockingTurns;
|
|
1246
1412
|
return {
|
|
1247
1413
|
...observation,
|
|
1248
1414
|
reconcileBindingAction,
|
|
@@ -1329,7 +1495,7 @@ function observeTerminalListActionAuthority(terminal, context, binding) {
|
|
|
1329
1495
|
}
|
|
1330
1496
|
function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
1331
1497
|
const { sessionAuthorityRequired, includeAll, mutationsAllowed, conversationHasNonterminalDeferredTransfer } = context;
|
|
1332
|
-
const { automatedInputComposerReady, publicTerminal, allRelated, displayedRelated, relatedSessions, authoritativeSession, ownership, rawSendAction, sessionAwareRawActions, externalHandoffDetected, externalHandoffSnapshotToken, handoffSourceBlockingTurns, verifiedEmptyRawSendAction, verifiedEmptyCodexSnapshotToken, deferredCodexSourceRolloutAuthority, deferredCodexForegroundToken, reconcileBindingAction, externalHandoffAdoptable, handoffDecision, blockingHandoffTurnIds, terminalRecoveryBlockingTurns, terminalScopedCodexApprovalAction, rolloutBackedCodexSession } = observation;
|
|
1498
|
+
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;
|
|
1333
1499
|
const association = decideManagedTerminalAssociation({
|
|
1334
1500
|
allRelated,
|
|
1335
1501
|
displayedRelated,
|
|
@@ -1351,11 +1517,14 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1351
1517
|
const currentTurnValue = ownership.state === "current"
|
|
1352
1518
|
? currentManagedTurnForTerminal(ownership.conversation, terminal, sessionAwareRawActions)
|
|
1353
1519
|
: undefined;
|
|
1354
|
-
const currentTurnProjection = currentTurnValue
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1520
|
+
const currentTurnProjection = currentTurnValue
|
|
1521
|
+
? !mutationsAllowed
|
|
1522
|
+
? readOnlyManagedTurn(currentTurnValue)
|
|
1523
|
+
: ownership.state === "current" &&
|
|
1524
|
+
conversationHasNonterminalDeferredTransfer(ownership.conversation)
|
|
1525
|
+
? userReleasableManagedTurn(currentTurnValue)
|
|
1526
|
+
: currentTurnValue
|
|
1527
|
+
: undefined;
|
|
1359
1528
|
const currentTurn = currentTurnProjection
|
|
1360
1529
|
? withoutGenericHandoffSourceClose(currentTurnProjection, blockingHandoffTurnIds)
|
|
1361
1530
|
: undefined;
|
|
@@ -1372,20 +1541,24 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1372
1541
|
const recentTurnValue = recentConversation
|
|
1373
1542
|
? historicalManagedTurnForTerminal(recentConversation)
|
|
1374
1543
|
: undefined;
|
|
1375
|
-
const recentTurnProjection = recentTurnValue
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1544
|
+
const recentTurnProjection = recentTurnValue
|
|
1545
|
+
? !mutationsAllowed
|
|
1546
|
+
? readOnlyManagedTurn(recentTurnValue)
|
|
1547
|
+
: recentConversation &&
|
|
1548
|
+
conversationHasNonterminalDeferredTransfer(recentConversation)
|
|
1549
|
+
? userReleasableManagedTurn(recentTurnValue)
|
|
1550
|
+
: recentTurnValue
|
|
1551
|
+
: undefined;
|
|
1380
1552
|
const recentTurn = recentTurnProjection
|
|
1381
1553
|
? withoutGenericHandoffSourceClose(recentTurnProjection, blockingHandoffTurnIds)
|
|
1382
1554
|
: undefined;
|
|
1383
1555
|
const history = historyConversations.map((conversation) => {
|
|
1384
1556
|
const turn = historicalManagedTurnForTerminal(conversation);
|
|
1385
|
-
return withoutGenericHandoffSourceClose(mutationsAllowed
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1557
|
+
return withoutGenericHandoffSourceClose(!mutationsAllowed
|
|
1558
|
+
? readOnlyManagedTurn(turn)
|
|
1559
|
+
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
1560
|
+
? userReleasableManagedTurn(turn)
|
|
1561
|
+
: turn, blockingHandoffTurnIds);
|
|
1389
1562
|
});
|
|
1390
1563
|
const visibleTurnIds = new Set([currentTurn, recentTurn, ...history]
|
|
1391
1564
|
.map((turn) => stringValue(turn?.conversation_id))
|
|
@@ -1446,6 +1619,20 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1446
1619
|
}
|
|
1447
1620
|
}
|
|
1448
1621
|
: undefined;
|
|
1622
|
+
const managedFastPathToken = "token" in sendAuthority
|
|
1623
|
+
? sendAuthority.token
|
|
1624
|
+
: undefined;
|
|
1625
|
+
const prioritizedTerminalUserExplicitSendAction = terminalUserExplicitSendAction && managedFastPathToken
|
|
1626
|
+
? {
|
|
1627
|
+
...terminalUserExplicitSendAction,
|
|
1628
|
+
arguments: {
|
|
1629
|
+
...(isRecord(terminalUserExplicitSendAction.arguments)
|
|
1630
|
+
? terminalUserExplicitSendAction.arguments
|
|
1631
|
+
: {}),
|
|
1632
|
+
expected_managed_terminal_token: managedFastPathToken
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
: terminalUserExplicitSendAction;
|
|
1449
1636
|
const availableActions = selectTerminalAvailableActions({
|
|
1450
1637
|
ownership: ownership.state,
|
|
1451
1638
|
currentActions: ownership.state === "current"
|
|
@@ -1454,6 +1641,7 @@ function renderTerminalFirstListEntry(terminal, context, observation) {
|
|
|
1454
1641
|
sessionAwareRawActions: sessionAwareRawActions,
|
|
1455
1642
|
nonOwnerRawActions,
|
|
1456
1643
|
authoritativeSendAction,
|
|
1644
|
+
terminalUserExplicitSendAction: prioritizedTerminalUserExplicitSendAction,
|
|
1457
1645
|
reconcileBindingAction,
|
|
1458
1646
|
terminalScopedApprovalAction: terminalScopedCodexApprovalAction,
|
|
1459
1647
|
isAction: isRecord
|
|
@@ -1562,14 +1750,17 @@ function terminalFirstListProjection({ storeDir, terminals, managedSessions, ses
|
|
|
1562
1750
|
});
|
|
1563
1751
|
return {
|
|
1564
1752
|
...managedTurn,
|
|
1565
|
-
available_actions: mutationsAllowed
|
|
1566
|
-
|
|
1567
|
-
? safeUnavailableManagedTurnActions(isRecord(managedTurn.available_actions)
|
|
1753
|
+
available_actions: !mutationsAllowed
|
|
1754
|
+
? readOnlyListActions(isRecord(managedTurn.available_actions)
|
|
1568
1755
|
? managedTurn.available_actions
|
|
1569
1756
|
: {})
|
|
1570
|
-
:
|
|
1571
|
-
? managedTurn.available_actions
|
|
1572
|
-
|
|
1757
|
+
: conversationHasNonterminalDeferredTransfer(conversation)
|
|
1758
|
+
? userReleaseListActions(isRecord(managedTurn.available_actions)
|
|
1759
|
+
? managedTurn.available_actions
|
|
1760
|
+
: {}, turnIdForConversation(conversation))
|
|
1761
|
+
: safeUnavailableManagedTurnActions(isRecord(managedTurn.available_actions)
|
|
1762
|
+
? managedTurn.available_actions
|
|
1763
|
+
: {}),
|
|
1573
1764
|
terminal_availability: {
|
|
1574
1765
|
available: false,
|
|
1575
1766
|
reason: managedOnly
|
|
@@ -2266,6 +2457,12 @@ function managedListApprovalState(conversation) {
|
|
|
2266
2457
|
}
|
|
2267
2458
|
async function resolveConversationSelectorOption(commandName, options) {
|
|
2268
2459
|
const sendOperation = commandName === "send";
|
|
2460
|
+
// Submission retry is already bound to one authoritative managed Turn.
|
|
2461
|
+
// Keep it out of ordinary-send selector discovery so an omitted Session
|
|
2462
|
+
// target cannot be inferred and mixed into the exact `send --turn` form.
|
|
2463
|
+
if (sendOperation && stringValue(options.turn)) {
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2269
2466
|
if (stringValue(options.expectedTerminalToken)) {
|
|
2270
2467
|
// Selector resolution may replace an alias (or an omitted selector) with a
|
|
2271
2468
|
// discovered full terminal id. Preserve the caller's actual authority so
|
|
@@ -2282,11 +2479,54 @@ async function resolveConversationSelectorOption(commandName, options) {
|
|
|
2282
2479
|
const supplied = stringValue(sendOperation
|
|
2283
2480
|
? options.session ?? options.conversation ?? options.conversationId
|
|
2284
2481
|
: options.turn ?? options.conversation ?? options.conversationId)?.trim();
|
|
2285
|
-
if (
|
|
2482
|
+
if (sendOperation &&
|
|
2483
|
+
options.managedOnly === true &&
|
|
2484
|
+
supplied?.startsWith("terminal:v") &&
|
|
2485
|
+
!stringValue(options.expectedTerminalToken)) {
|
|
2486
|
+
// `--managed-only` is an explicit diagnostic/test request for the legacy
|
|
2487
|
+
// managed state machine. Keep an exact physical id on that path without
|
|
2488
|
+
// upgrading it to the default user-priority Send authority. Ordinary user
|
|
2489
|
+
// Send never sets this option and continues to discover and bind fresh
|
|
2490
|
+
// physical authority below.
|
|
2491
|
+
options.session = supplied;
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
if (supplied &&
|
|
2495
|
+
!isSessionSelectorSyntax(supplied) &&
|
|
2496
|
+
!(sendOperation &&
|
|
2497
|
+
supplied.startsWith("terminal:v") &&
|
|
2498
|
+
!stringValue(options.expectedTerminalToken))) {
|
|
2286
2499
|
// Full authoritative IDs keep their existing command-specific validation
|
|
2287
2500
|
// path. This avoids a discovery scan before option validation and preserves
|
|
2288
2501
|
// precise downstream errors for closed or currently non-actionable state.
|
|
2289
2502
|
if (sendOperation) {
|
|
2503
|
+
if (supplied.startsWith("terminal:v") &&
|
|
2504
|
+
stringValue(options.expectedTerminalToken)) {
|
|
2505
|
+
try {
|
|
2506
|
+
const observed = await observeExactTerminal({
|
|
2507
|
+
options,
|
|
2508
|
+
terminalId: supplied
|
|
2509
|
+
});
|
|
2510
|
+
const actions = observed.state === "available" &&
|
|
2511
|
+
isRecord(observed.terminal.available_actions)
|
|
2512
|
+
? observed.terminal.available_actions
|
|
2513
|
+
: {};
|
|
2514
|
+
const send = isRecord(actions.send) ? actions.send : {};
|
|
2515
|
+
const argumentsValue = isRecord(send.arguments)
|
|
2516
|
+
? send.arguments
|
|
2517
|
+
: {};
|
|
2518
|
+
options.expectedManagedTerminalToken =
|
|
2519
|
+
stringValue(argumentsValue.expected_terminal_token) ===
|
|
2520
|
+
stringValue(options.expectedTerminalToken)
|
|
2521
|
+
? stringValue(argumentsValue.expected_managed_terminal_token)
|
|
2522
|
+
: undefined;
|
|
2523
|
+
}
|
|
2524
|
+
catch {
|
|
2525
|
+
// Managed fast-path discovery is optional. The physical Send token
|
|
2526
|
+
// is still revalidated by execution and may fall back without Store.
|
|
2527
|
+
options.expectedManagedTerminalToken = undefined;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2290
2530
|
options.session = supplied;
|
|
2291
2531
|
}
|
|
2292
2532
|
else {
|
|
@@ -2299,12 +2539,24 @@ async function resolveConversationSelectorOption(commandName, options) {
|
|
|
2299
2539
|
operation: commandName
|
|
2300
2540
|
});
|
|
2301
2541
|
if (sendOperation) {
|
|
2302
|
-
|
|
2542
|
+
const physical = resolution.candidate.userExplicitTerminalSend;
|
|
2543
|
+
if (!physical) {
|
|
2544
|
+
throw new Error("the selected terminal no longer advertises user-priority Send");
|
|
2545
|
+
}
|
|
2546
|
+
// Preserve a token-bearing caller's original selector for the exact fence.
|
|
2547
|
+
if (!stringValue(options.expectedTerminalToken)) {
|
|
2548
|
+
terminalListRuntime().rememberOriginalExpectedTerminalSelector(options, physical.terminalId);
|
|
2549
|
+
}
|
|
2550
|
+
options.expectedTerminalToken = physical.expectedTerminalToken;
|
|
2551
|
+
options.expectedManagedTerminalToken =
|
|
2552
|
+
physical.expectedManagedTerminalToken;
|
|
2553
|
+
options.session = physical.terminalId;
|
|
2554
|
+
options.conversation = physical.terminalId;
|
|
2303
2555
|
}
|
|
2304
2556
|
else {
|
|
2305
2557
|
options.turn = resolution.id;
|
|
2558
|
+
options.conversation = resolution.id;
|
|
2306
2559
|
}
|
|
2307
|
-
options.conversation = resolution.id;
|
|
2308
2560
|
delete options.conversationId;
|
|
2309
2561
|
}
|
|
2310
2562
|
function isSessionSelectorSyntax(value) {
|
|
@@ -2312,6 +2564,59 @@ function isSessionSelectorSyntax(value) {
|
|
|
2312
2564
|
/^@[0-9a-f]+$/iu.test(value));
|
|
2313
2565
|
}
|
|
2314
2566
|
async function sessionSelectorCandidates(commandName, options) {
|
|
2567
|
+
const terminalScan = await buildTerminalListGroup({
|
|
2568
|
+
options: {
|
|
2569
|
+
...options,
|
|
2570
|
+
noApprovalScan: commandName === "send"
|
|
2571
|
+
? false
|
|
2572
|
+
: ["approve", "cancel"].includes(commandName)
|
|
2573
|
+
? options.noApprovalScan
|
|
2574
|
+
: true
|
|
2575
|
+
},
|
|
2576
|
+
agentFilter: undefined,
|
|
2577
|
+
statusFilter: undefined
|
|
2578
|
+
});
|
|
2579
|
+
const observedAtMs = cliNowMs();
|
|
2580
|
+
if (commandName === "send") {
|
|
2581
|
+
return terminalScan.terminalControlled
|
|
2582
|
+
.filter((entry) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, entry.workspace ?? entry.cwd))
|
|
2583
|
+
.flatMap((entry) => {
|
|
2584
|
+
const action = isRecord(entry._terminal_user_explicit_send_action)
|
|
2585
|
+
? entry._terminal_user_explicit_send_action
|
|
2586
|
+
: undefined;
|
|
2587
|
+
const argumentsValue = isRecord(action?.arguments)
|
|
2588
|
+
? action.arguments
|
|
2589
|
+
: undefined;
|
|
2590
|
+
const terminalId = stringValue(entry.id);
|
|
2591
|
+
const expectedTerminalToken = stringValue(argumentsValue?.expected_terminal_token);
|
|
2592
|
+
if (action?.scope !== "terminal_user_explicit" ||
|
|
2593
|
+
!terminalId ||
|
|
2594
|
+
stringValue(argumentsValue?.selector ?? argumentsValue?.terminal_id) !== terminalId ||
|
|
2595
|
+
!expectedTerminalToken) {
|
|
2596
|
+
return [];
|
|
2597
|
+
}
|
|
2598
|
+
const candidate = projectSelectorCandidate({
|
|
2599
|
+
...entry,
|
|
2600
|
+
available_actions: { send: action }
|
|
2601
|
+
}, commandName, observedAtMs, {
|
|
2602
|
+
defaultActionable: true,
|
|
2603
|
+
// Physical Send authority is independent of Store writability.
|
|
2604
|
+
mutationsAllowed: true
|
|
2605
|
+
});
|
|
2606
|
+
return [{
|
|
2607
|
+
...candidate,
|
|
2608
|
+
userExplicitTerminalSend: {
|
|
2609
|
+
terminalId,
|
|
2610
|
+
expectedTerminalToken,
|
|
2611
|
+
...(stringValue(argumentsValue?.expected_managed_terminal_token) === undefined
|
|
2612
|
+
? {}
|
|
2613
|
+
: {
|
|
2614
|
+
expectedManagedTerminalToken: stringValue(argumentsValue?.expected_managed_terminal_token)
|
|
2615
|
+
})
|
|
2616
|
+
}
|
|
2617
|
+
}];
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2315
2620
|
const storeDir = terminalListRuntime().storeDirFromOptions(options);
|
|
2316
2621
|
const mutationsAllowed = inspectStoreCompatibility(storeDir).writable === true;
|
|
2317
2622
|
const selectorStore = inspectStoreCompatibility(storeDir);
|
|
@@ -2325,16 +2630,6 @@ async function sessionSelectorCandidates(commandName, options) {
|
|
|
2325
2630
|
approvalState: managedListApprovalState(conversation),
|
|
2326
2631
|
conversation
|
|
2327
2632
|
}));
|
|
2328
|
-
const terminalScan = await buildTerminalListGroup({
|
|
2329
|
-
options: {
|
|
2330
|
-
...options,
|
|
2331
|
-
noApprovalScan: ["send", "approve", "cancel"].includes(commandName)
|
|
2332
|
-
? options.noApprovalScan
|
|
2333
|
-
: true
|
|
2334
|
-
},
|
|
2335
|
-
agentFilter: undefined,
|
|
2336
|
-
statusFilter: undefined
|
|
2337
|
-
});
|
|
2338
2633
|
const terminalProjection = terminalFirstListProjection({
|
|
2339
2634
|
storeDir,
|
|
2340
2635
|
terminals: terminalScan.terminalControlled.filter((entry) => terminalListRuntime().matchesConfiguredWorkspace(options.workspace, entry.workspace ?? entry.cwd)),
|
|
@@ -2349,87 +2644,6 @@ async function sessionSelectorCandidates(commandName, options) {
|
|
|
2349
2644
|
statusFilter: undefined,
|
|
2350
2645
|
mutationsAllowed
|
|
2351
2646
|
});
|
|
2352
|
-
const observedAtMs = cliNowMs();
|
|
2353
|
-
if (commandName === "send") {
|
|
2354
|
-
const sessionEntries = terminalProjection.terminals.flatMap((entry) => {
|
|
2355
|
-
const managedState = isRecord(entry.managed) ? entry.managed : undefined;
|
|
2356
|
-
const recentTurn = isRecord(managedState?.recent_turn)
|
|
2357
|
-
? managedState.recent_turn
|
|
2358
|
-
: undefined;
|
|
2359
|
-
const currentTurn = isRecord(managedState?.current_turn)
|
|
2360
|
-
? managedState.current_turn
|
|
2361
|
-
: undefined;
|
|
2362
|
-
const sessionId = stringValue(managedState?.session_id);
|
|
2363
|
-
const actions = isRecord(entry.available_actions)
|
|
2364
|
-
? entry.available_actions
|
|
2365
|
-
: undefined;
|
|
2366
|
-
const sendAction = isRecord(actions?.send)
|
|
2367
|
-
? actions.send
|
|
2368
|
-
: undefined;
|
|
2369
|
-
const sendArguments = isRecord(sendAction?.arguments)
|
|
2370
|
-
? sendAction.arguments
|
|
2371
|
-
: undefined;
|
|
2372
|
-
if (!sessionId ||
|
|
2373
|
-
!sendAction ||
|
|
2374
|
-
stringValue(sendArguments?.session_id) !== sessionId) {
|
|
2375
|
-
return [];
|
|
2376
|
-
}
|
|
2377
|
-
const commonEntry = {
|
|
2378
|
-
agent: entry.agent,
|
|
2379
|
-
status: "idle",
|
|
2380
|
-
workspace: entry.workspace ?? entry.cwd,
|
|
2381
|
-
updated_at: recentTurn?.updated_at ??
|
|
2382
|
-
currentTurn?.updated_at,
|
|
2383
|
-
available_actions: {
|
|
2384
|
-
send: sendAction
|
|
2385
|
-
}
|
|
2386
|
-
};
|
|
2387
|
-
return [
|
|
2388
|
-
{
|
|
2389
|
-
...commonEntry,
|
|
2390
|
-
id: sessionId,
|
|
2391
|
-
short_ref: sessionShortRef(sessionId),
|
|
2392
|
-
source: "managed_session"
|
|
2393
|
-
},
|
|
2394
|
-
{
|
|
2395
|
-
...commonEntry,
|
|
2396
|
-
id: String(entry.id),
|
|
2397
|
-
short_ref: stringValue(entry.short_ref) ??
|
|
2398
|
-
sessionShortRef(String(entry.id)),
|
|
2399
|
-
source: "managed_session_terminal_alias"
|
|
2400
|
-
}
|
|
2401
|
-
];
|
|
2402
|
-
});
|
|
2403
|
-
const rawTerminalEntries = terminalProjection.terminals.filter((entry) => {
|
|
2404
|
-
const managedState = isRecord(entry.managed) ? entry.managed : undefined;
|
|
2405
|
-
const actions = isRecord(entry.available_actions)
|
|
2406
|
-
? entry.available_actions
|
|
2407
|
-
: undefined;
|
|
2408
|
-
const sendAction = isRecord(actions?.send) ? actions.send : undefined;
|
|
2409
|
-
const sendArguments = isRecord(sendAction?.arguments)
|
|
2410
|
-
? sendAction.arguments
|
|
2411
|
-
: undefined;
|
|
2412
|
-
return (!stringValue(managedState?.session_id) ||
|
|
2413
|
-
!stringValue(sendArguments?.session_id));
|
|
2414
|
-
});
|
|
2415
|
-
return [
|
|
2416
|
-
...managed.map((entry) => projectSelectorCandidate(entry, commandName, observedAtMs, {
|
|
2417
|
-
defaultActionable: false,
|
|
2418
|
-
mutationsAllowed
|
|
2419
|
-
})),
|
|
2420
|
-
...sessionEntries.map((entry) => projectSelectorCandidate(entry, commandName, observedAtMs, {
|
|
2421
|
-
// A managed terminal's full id/@short-ref is an explicit alias for
|
|
2422
|
-
// its Session send target. It must not duplicate the Session in
|
|
2423
|
-
// omitted, only, latest, or agent-name selection.
|
|
2424
|
-
defaultActionable: entry.source !== "managed_session_terminal_alias",
|
|
2425
|
-
mutationsAllowed
|
|
2426
|
-
})),
|
|
2427
|
-
...rawTerminalEntries.map((entry) => projectSelectorCandidate(entry, commandName, observedAtMs, {
|
|
2428
|
-
defaultActionable: true,
|
|
2429
|
-
mutationsAllowed
|
|
2430
|
-
}))
|
|
2431
|
-
];
|
|
2432
|
-
}
|
|
2433
2647
|
return [
|
|
2434
2648
|
...managed.map((entry) => projectSelectorCandidate(entry, commandName, observedAtMs, {
|
|
2435
2649
|
defaultActionable: options.managedOnly === true,
|