@scotthuang/agent-knock-knock 0.12.12 → 0.12.13
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 +22 -0
- package/README.md +39 -26
- 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/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 +85 -0
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +90 -19
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.js +3 -4
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/openclaw-callback-transport.d.ts +5 -0
- package/dist/src/openclaw-callback-transport.js +1 -1
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- 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 +548 -107
- 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 +309 -44
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +44 -68
- package/dist/src/openclaw-plugin-schemas.js +63 -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/store.d.ts +1 -0
- package/dist/src/store.js +3 -1
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +19 -1
- package/dist/src/terminal-action-projection.js +23 -0
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +5 -0
- package/dist/src/terminal-command-cli-adapter.js +249 -89
- package/dist/src/terminal-command-cli-adapter.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-list-cli-adapter.d.ts +31 -7
- package/dist/src/terminal-list-cli-adapter.js +285 -67
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +2 -0
- package/dist/src/terminal-list-renderer.js +116 -41
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.js +1 -1
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +6 -15
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +10 -0
- package/dist/src/terminal-status-cli-adapter.js +89 -7
- 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 +560 -0
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +20 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js +55 -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 +508 -0
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-service.d.ts +97 -0
- package/dist/src/terminal-watch-service.js +714 -0
- package/dist/src/terminal-watch-service.js.map +1 -0
- package/dist/src/terminal-watch-store.d.ts +149 -0
- package/dist/src/terminal-watch-store.js +861 -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 +57 -39
|
@@ -11,6 +11,7 @@ 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";
|
|
@@ -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) {
|
|
@@ -272,17 +376,28 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
272
376
|
terminalTarget: terminalControl.target
|
|
273
377
|
});
|
|
274
378
|
const { orphanedDispatch, nativeIdentityObservation, nativeAgentIdentity, codexOpenRootRolloutInventory, nativeProcessUuid, nativeProcessBirth, nativeProcessEvidence } = await observeTerminalNativeListIdentity(session, terminalControl, options, bridge);
|
|
379
|
+
const effectiveTerminalState = effectiveTerminalListState({
|
|
380
|
+
session,
|
|
381
|
+
terminalState,
|
|
382
|
+
nativeIdentityObservation,
|
|
383
|
+
nativeAgentIdentity,
|
|
384
|
+
nativeProcessUuid,
|
|
385
|
+
nativeProcessBirth
|
|
386
|
+
});
|
|
387
|
+
const terminalStatusSnapshot = effectiveTerminalState._terminal_status_snapshot
|
|
388
|
+
? terminalBridgeStatusWithActivity(effectiveTerminalState._terminal_status_snapshot, effectiveTerminalState.activity_state, effectiveTerminalState.activity_reason)
|
|
389
|
+
: undefined;
|
|
275
390
|
const statusCardObservation = session.agent === "codex" &&
|
|
276
|
-
typeof
|
|
391
|
+
typeof effectiveTerminalState.screen_excerpt === "string"
|
|
277
392
|
? bridge.registry.require("codex").observeThreadLifecycle?.({
|
|
278
393
|
operation: { kind: "new_thread" },
|
|
279
394
|
phase: "before",
|
|
280
|
-
screen:
|
|
395
|
+
screen: effectiveTerminalState.screen_excerpt
|
|
281
396
|
})
|
|
282
397
|
: undefined;
|
|
283
398
|
const statusCardNativeThreadId = statusCardObservation?.status === "observed" &&
|
|
284
|
-
|
|
285
|
-
|
|
399
|
+
effectiveTerminalState.activity_state === "idle" &&
|
|
400
|
+
effectiveTerminalState.approval_state.blocked !== true &&
|
|
286
401
|
isExactNativeThreadId(statusCardObservation.nativeThreadId)
|
|
287
402
|
? statusCardObservation.nativeThreadId
|
|
288
403
|
: undefined;
|
|
@@ -304,7 +419,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
304
419
|
};
|
|
305
420
|
const codexLatentClearResumeObservationValue = session.agent === "codex"
|
|
306
421
|
? terminalListRuntime().codexLatentClearResumeObservation({
|
|
307
|
-
screen:
|
|
422
|
+
screen: effectiveTerminalState.screen_excerpt,
|
|
308
423
|
agentVersion
|
|
309
424
|
})
|
|
310
425
|
: undefined;
|
|
@@ -325,7 +440,7 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
325
440
|
const automatedInputComposerReady = await observeAutomatedInputComposerReady({
|
|
326
441
|
session,
|
|
327
442
|
terminalControl,
|
|
328
|
-
terminalState,
|
|
443
|
+
terminalState: effectiveTerminalState,
|
|
329
444
|
nativeIdentityObservation,
|
|
330
445
|
codexOpenRootRolloutInventory,
|
|
331
446
|
options
|
|
@@ -363,9 +478,15 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
363
478
|
confidence: session.confidence,
|
|
364
479
|
reason: session.reason,
|
|
365
480
|
terminal_control: terminalControl,
|
|
366
|
-
approval_state:
|
|
367
|
-
activity_state:
|
|
368
|
-
activity_reason:
|
|
481
|
+
approval_state: effectiveTerminalState.approval_state,
|
|
482
|
+
activity_state: effectiveTerminalState.activity_state,
|
|
483
|
+
activity_reason: effectiveTerminalState.activity_reason,
|
|
484
|
+
// Internal exact-observation evidence. The public projection strips this
|
|
485
|
+
// object; raw terminal status reuses it so screen, approval, and activity
|
|
486
|
+
// all describe the same capture.
|
|
487
|
+
...(terminalStatusSnapshot
|
|
488
|
+
? { _terminal_status_snapshot: terminalStatusSnapshot }
|
|
489
|
+
: {}),
|
|
369
490
|
// Internal action-projection evidence. terminalFirstListProjection strips
|
|
370
491
|
// this field after gating every automated-input action that can follow a
|
|
371
492
|
// human native-thread switch.
|
|
@@ -398,33 +519,20 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
398
519
|
}
|
|
399
520
|
}
|
|
400
521
|
: {}),
|
|
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
|
-
}
|
|
522
|
+
commands: terminalListCommands({
|
|
523
|
+
agent: session.agent,
|
|
524
|
+
terminalControl,
|
|
525
|
+
terminalState: effectiveTerminalState,
|
|
526
|
+
lifecycleCapability,
|
|
527
|
+
nativeInspectionCapability,
|
|
528
|
+
nativeAgentIdentity,
|
|
529
|
+
nativeProcessUuid,
|
|
530
|
+
nativeProcessBirth,
|
|
531
|
+
codexLifecycleIncarnationAvailable,
|
|
532
|
+
automatedInputComposerReady,
|
|
533
|
+
hasOrphanedDispatch: orphanedDispatch !== undefined,
|
|
534
|
+
terminalHasBlockingTurn
|
|
535
|
+
})
|
|
428
536
|
};
|
|
429
537
|
const availableActions = renderAvailableListActions(entry);
|
|
430
538
|
const { commands: _commands, ...publicEntry } = entry;
|
|
@@ -433,6 +541,115 @@ async function terminalControlledListEntry(session, activeSessions, options, bri
|
|
|
433
541
|
available_actions: availableActions
|
|
434
542
|
};
|
|
435
543
|
}
|
|
544
|
+
function terminalListCommands(input) {
|
|
545
|
+
const { agent, terminalControl, terminalState, lifecycleCapability, nativeInspectionCapability, nativeAgentIdentity, nativeProcessUuid, nativeProcessBirth, codexLifecycleIncarnationAvailable, automatedInputComposerReady, hasOrphanedDispatch, terminalHasBlockingTurn } = input;
|
|
546
|
+
return {
|
|
547
|
+
send: !terminalHasBlockingTurn,
|
|
548
|
+
approve: terminalControl.capabilities.includes("terminal_approval") &&
|
|
549
|
+
terminalState.approval_state.approvable === true,
|
|
550
|
+
status: true,
|
|
551
|
+
cancel: terminalControl.capabilities.includes("terminal_cancel"),
|
|
552
|
+
close: hasOrphanedDispatch,
|
|
553
|
+
new_thread: lifecycleCapability.status === "supported" &&
|
|
554
|
+
lifecycleCapability.newThread === true &&
|
|
555
|
+
codexLifecycleIncarnationAvailable &&
|
|
556
|
+
!terminalHasBlockingTurn,
|
|
557
|
+
list_resumable_threads: lifecycleCapability.status === "supported" &&
|
|
558
|
+
lifecycleCapability.resumeExact === true &&
|
|
559
|
+
codexLifecycleIncarnationAvailable,
|
|
560
|
+
native_inspect: nativeInspectionCapability.status === "supported" &&
|
|
561
|
+
nativeInspectionCapability.statusInspection === true &&
|
|
562
|
+
terminalState.activity_state === "idle" &&
|
|
563
|
+
automatedInputComposerReady &&
|
|
564
|
+
terminalControl.capabilities.includes("send_keys") &&
|
|
565
|
+
terminalControl.capabilities.includes("screen_status") &&
|
|
566
|
+
(agent === "codex"
|
|
567
|
+
? codexLifecycleIncarnationAvailable
|
|
568
|
+
: Boolean(nativeAgentIdentity?.sessionId &&
|
|
569
|
+
nativeAgentIdentity.processUuid)) &&
|
|
570
|
+
!hasOrphanedDispatch &&
|
|
571
|
+
!terminalHasBlockingTurn,
|
|
572
|
+
watch: lifecycleCapability.status === "supported" &&
|
|
573
|
+
(terminalState.activity_state === "working" ||
|
|
574
|
+
terminalState.activity_state === "awaiting_approval") &&
|
|
575
|
+
Boolean(nativeAgentIdentity?.sessionId) &&
|
|
576
|
+
(agent !== "codex" || Boolean(nativeAgentIdentity?.rollout)) &&
|
|
577
|
+
Boolean(nativeProcessUuid) &&
|
|
578
|
+
Boolean(nativeProcessBirth) &&
|
|
579
|
+
!hasOrphanedDispatch &&
|
|
580
|
+
!terminalHasBlockingTurn
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function effectiveTerminalListState(input) {
|
|
584
|
+
const { session, terminalState, nativeIdentityObservation, nativeAgentIdentity, nativeProcessUuid, nativeProcessBirth } = input;
|
|
585
|
+
if (session.agent !== "codex" ||
|
|
586
|
+
terminalState.activity_state === "working" ||
|
|
587
|
+
terminalState.activity_state === "awaiting_approval" ||
|
|
588
|
+
terminalState.approval_state.blocked === true) {
|
|
589
|
+
return terminalState;
|
|
590
|
+
}
|
|
591
|
+
if (nativeIdentityObservation.status === "verified_absent") {
|
|
592
|
+
return terminalState;
|
|
593
|
+
}
|
|
594
|
+
if (nativeIdentityObservation.status !== "resolved" ||
|
|
595
|
+
!nativeAgentIdentity?.rollout ||
|
|
596
|
+
!nativeAgentIdentity.sessionId ||
|
|
597
|
+
!nativeProcessUuid ||
|
|
598
|
+
!nativeProcessBirth) {
|
|
599
|
+
return terminalListStateWithUnavailableDurableActivity(terminalState, nativeIdentityObservation.status === "unavailable"
|
|
600
|
+
? nativeIdentityObservation.reason ??
|
|
601
|
+
"exact Codex identity observation failed"
|
|
602
|
+
: "exact Codex rollout/process identity is incomplete");
|
|
603
|
+
}
|
|
604
|
+
const currentIdentity = {
|
|
605
|
+
sessionId: nativeAgentIdentity.sessionId,
|
|
606
|
+
processUuid: nativeProcessUuid,
|
|
607
|
+
processBirth: nativeProcessBirth,
|
|
608
|
+
rollout: nativeAgentIdentity.rollout
|
|
609
|
+
};
|
|
610
|
+
try {
|
|
611
|
+
if (!captureCodexHumanStartedActiveTaskAnchor({ currentIdentity })) {
|
|
612
|
+
return terminalState;
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
...terminalState,
|
|
616
|
+
activity_state: "working",
|
|
617
|
+
activity_reason: "Codex rollout contains an exact unfinished human-started task"
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
622
|
+
runtimeLog("warn", "terminal_durable_activity_unavailable", {
|
|
623
|
+
agent: session.agent,
|
|
624
|
+
pid: session.pid,
|
|
625
|
+
reason
|
|
626
|
+
});
|
|
627
|
+
return terminalListStateWithUnavailableDurableActivity(terminalState, reason);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function terminalListStateWithUnavailableDurableActivity(terminalState, reason) {
|
|
631
|
+
return {
|
|
632
|
+
...terminalState,
|
|
633
|
+
activity_state: "unknown",
|
|
634
|
+
activity_reason: `durable Codex activity evidence is unavailable: ${reason}`
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function terminalBridgeStatusWithActivity(status, activityState, activityReason) {
|
|
638
|
+
const descriptors = Object.getOwnPropertyDescriptors(status);
|
|
639
|
+
descriptors.activity_state = {
|
|
640
|
+
configurable: true,
|
|
641
|
+
enumerable: true,
|
|
642
|
+
value: activityState,
|
|
643
|
+
writable: true
|
|
644
|
+
};
|
|
645
|
+
descriptors.activity_reason = {
|
|
646
|
+
configurable: true,
|
|
647
|
+
enumerable: true,
|
|
648
|
+
value: activityReason,
|
|
649
|
+
writable: true
|
|
650
|
+
};
|
|
651
|
+
return Object.create(Object.getPrototypeOf(status), descriptors);
|
|
652
|
+
}
|
|
436
653
|
async function observeTerminalNativeListIdentity(session, terminalControl, options, bridge) {
|
|
437
654
|
const orphanedDispatch = terminalListRuntime().orphanedTerminalDispatchForRecovery(terminalControl);
|
|
438
655
|
let codexIdentityContext;
|
|
@@ -562,7 +779,7 @@ async function observeAutomatedInputComposerReady({ session, terminalControl, te
|
|
|
562
779
|
}
|
|
563
780
|
function observeTerminalListBindingAuthority(terminal, context) {
|
|
564
781
|
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;
|
|
782
|
+
const { _automated_input_composer_ready: automatedInputComposerReady, _codex_open_root_rollout_inventory: codexOpenRootRolloutInventoryValue, _codex_latent_clear_resume: codexLatentClearResumeValue, _terminal_status_snapshot: _terminalStatusSnapshot, ...publicTerminal } = terminal;
|
|
566
783
|
const codexOpenRootRolloutInventory = isRecord(codexOpenRootRolloutInventoryValue)
|
|
567
784
|
? codexOpenRootRolloutInventoryValue
|
|
568
785
|
: undefined;
|
|
@@ -1995,6 +2212,7 @@ async function listStateForTerminal(agent, terminalControl, options, bridge = te
|
|
|
1995
2212
|
activity_state: status.activity_state,
|
|
1996
2213
|
activity_reason: status.activity_reason,
|
|
1997
2214
|
capability_limitation: status.capability_limitation,
|
|
2215
|
+
_terminal_status_snapshot: status,
|
|
1998
2216
|
// Internal projection evidence; terminalControlledListEntry selects all
|
|
1999
2217
|
// public fields explicitly and never exposes the pane excerpt itself.
|
|
2000
2218
|
screen_excerpt: status.screen.excerpt
|