@scotthuang/agent-knock-knock 0.12.36 → 0.13.0
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 +3 -0
- package/dist/src/agent-session-provider.d.ts +5 -0
- package/dist/src/agent-session-provider.js +10 -1
- package/dist/src/agent-session-provider.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +2 -0
- package/dist/src/callback-outbox-service.js +1 -1
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-transport.d.ts +7 -0
- package/dist/src/callback-transport.js +1 -1
- package/dist/src/callback-transport.js.map +1 -1
- package/dist/src/cli-core.js +35 -32
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-store-adapter.d.ts +1 -1
- package/dist/src/codex-store-adapter.js +35 -8
- package/dist/src/codex-store-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.d.ts +20 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js +216 -8
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/host-adapter.d.ts +1 -1
- package/dist/src/host-adapter.js +1 -1
- package/dist/src/host-bridge-tools.js +2 -2
- package/dist/src/managed-turn-recovery-service.d.ts +8 -1
- package/dist/src/managed-turn-recovery-service.js +55 -13
- package/dist/src/managed-turn-recovery-service.js.map +1 -1
- package/dist/src/mutation-transaction.d.ts +1 -0
- package/dist/src/mutation-transaction.js +19 -15
- package/dist/src/mutation-transaction.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +39 -3
- package/dist/src/native-thread-lifecycle-cli-adapter.js +307 -7
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-callback-adapter.js +17 -7
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +299 -79
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +57 -0
- package/dist/src/openclaw-plugin-schemas.js +59 -1
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +17 -0
- package/dist/src/openclaw-private-authority-offers.js +75 -3
- package/dist/src/openclaw-private-authority-offers.js.map +1 -1
- package/dist/src/store.d.ts +1 -1
- package/dist/src/store.js +6 -4
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-application-service.js +2 -2
- package/dist/src/terminal-acceptance-application-service.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +11 -1
- package/dist/src/terminal-acceptance-cli-adapter.js +290 -16
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +1 -1
- package/dist/src/terminal-action-projection.js +6 -0
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-adapter.d.ts +9 -0
- package/dist/src/terminal-agent-adapter.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +52 -8
- package/dist/src/terminal-agent-bridge.js +133 -104
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-authority-policy.js +3 -0
- package/dist/src/terminal-authority-policy.js.map +1 -1
- package/dist/src/terminal-binding-authority.d.ts +2 -0
- package/dist/src/terminal-binding-authority.js +8 -0
- package/dist/src/terminal-binding-authority.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +17 -1
- package/dist/src/terminal-command-cli-adapter.js +784 -286
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.js +21 -7
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-application.d.ts +3 -1
- package/dist/src/terminal-dispatch-application.js +13 -5
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-composition.d.ts +43 -0
- package/dist/src/terminal-dispatch-execution.d.ts +2 -2
- package/dist/src/terminal-dispatch-execution.js +53 -3
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-policy.d.ts +20 -0
- package/dist/src/terminal-dispatch-policy.js +33 -1
- package/dist/src/terminal-dispatch-policy.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.d.ts +45 -0
- package/dist/src/terminal-dispatch-presenter.js +220 -18
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +5 -1
- package/dist/src/terminal-dispatch-receipt.js +26 -3
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.js +103 -4
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-authority.d.ts +48 -1
- package/dist/src/terminal-interaction-authority.js +30 -0
- package/dist/src/terminal-interaction-authority.js.map +1 -1
- package/dist/src/terminal-interaction-cli-adapter.js +19 -9
- package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-core.d.ts +104 -0
- package/dist/src/terminal-interaction-core.js +407 -0
- package/dist/src/terminal-interaction-core.js.map +1 -0
- package/dist/src/terminal-interaction-protocol.d.ts +95 -0
- package/dist/src/terminal-interaction-protocol.js +201 -1
- package/dist/src/terminal-interaction-protocol.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +5 -0
- package/dist/src/terminal-list-cli-adapter.js +190 -40
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.js +60 -7
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +14 -3
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +1 -0
- package/dist/src/terminal-monitor-application-service.js +72 -10
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +3 -0
- package/dist/src/terminal-monitor-cli-adapter.js +24 -8
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +15 -2
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-mutation-cli-runtime.d.ts +11 -1
- package/dist/src/terminal-mutation-cli-runtime.js +9 -4
- package/dist/src/terminal-mutation-cli-runtime.js.map +1 -1
- package/dist/src/terminal-questionnaire-adapter.d.ts +1 -1
- package/dist/src/terminal-questionnaire-adapter.js +106 -39
- package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +1 -7
- package/dist/src/terminal-status-cli-adapter.js +29 -3
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-facts.d.ts +17 -1
- package/dist/src/terminal-status-facts.js +59 -0
- package/dist/src/terminal-status-facts.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +37 -2
- package/dist/src/terminal-submission-acceptance.js +633 -30
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-facts.d.ts +5 -0
- package/dist/src/terminal-submission-facts.js.map +1 -1
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +2 -1
- package/dist/src/terminal-watch-callback-cli-adapter.js +2 -1
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-adapter.d.ts +6 -2
- package/dist/src/terminal-watch-cli-adapter.js +1051 -15
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-service.d.ts +15 -1
- package/dist/src/terminal-watch-service.js +169 -13
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +72 -3
- package/dist/src/terminal-watch-store.js +407 -27
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/host-bridge-profiles.md +2 -2
- package/docs/quickstart-herdr.md +5 -3
- package/docs/quickstart-tmux.md +7 -5
- package/openclaw.plugin.json +8 -0
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +15 -11
|
@@ -49,21 +49,25 @@ export function capabilityGatedRepositoryPairOperation(required, bound, operatio
|
|
|
49
49
|
/** Acquire terminal -> writer -> optional state and release in reverse. */
|
|
50
50
|
export async function withCanonicalMutationLocks(ports, operation) {
|
|
51
51
|
const transaction = {};
|
|
52
|
-
return withAcquiredScope(transaction, "terminal", ports.resources.terminal, ports.acquireTerminal,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
return withAcquiredScope(transaction, "terminal", ports.resources.terminal, ports.acquireTerminal, async (terminal) => {
|
|
53
|
+
await ports.afterTerminalAcquired?.();
|
|
54
|
+
return ports.withStoreWriter(async () => {
|
|
55
|
+
const storeWriter = createScope(transaction, "storeWriter", ports.resources.storeWriter);
|
|
56
|
+
const invoke = (state) => operation({ terminal, storeWriter, ...(state ? { state } : {}) }, ports.resources);
|
|
57
|
+
try {
|
|
58
|
+
if (!ports.acquireState)
|
|
59
|
+
return await invoke();
|
|
60
|
+
const stateResource = ports.resources.state;
|
|
61
|
+
if (!stateResource) {
|
|
62
|
+
throw new Error("state mutation lock requires a canonical resource");
|
|
63
|
+
}
|
|
64
|
+
return await withAcquiredScope(transaction, "state", stateResource, ports.acquireState, invoke);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
expireScope(storeWriter);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
67
71
|
}
|
|
68
72
|
/** Add one state scope to the currently active terminal + writer transaction. */
|
|
69
73
|
export async function withCanonicalStateMutationLock(scopes, resources, state, operation) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-transaction.js","sourceRoot":"","sources":["../../src/mutation-transaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mutation-transaction.js","sourceRoot":"","sources":["../../src/mutation-transaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,12 +4,41 @@ import { type ManagedSessionState } from "./managed-session.js";
|
|
|
4
4
|
import { type LifecycleTerminalObservation, type NativeThreadLifecycleQueryPorts } from "./native-thread-lifecycle-query-service.js";
|
|
5
5
|
import { type Conversation } from "./protocol.js";
|
|
6
6
|
import type { TerminalAgentAdapter, TerminalControlRef, TerminalRuntimeIdentity, TerminalThreadLifecycleCapabilities } from "./terminal-agent-adapter.js";
|
|
7
|
-
import { type ResolvedTerminalConversation } from "./terminal-agent-bridge.js";
|
|
7
|
+
import { type ResolvedTerminalConversation, type TerminalAgentBridge } from "./terminal-agent-bridge.js";
|
|
8
8
|
import { type CodexAllowedCompanionSet } from "./terminal-authority-policy.js";
|
|
9
9
|
import type { TerminalNativeIdentity } from "./terminal-binding-authority.js";
|
|
10
10
|
import type { TerminalDispatchLedgerDocument } from "./terminal-dispatch-ledger-codec.js";
|
|
11
11
|
import type { TerminalRuntimeCliAdapter } from "./terminal-runtime-cli-adapter.js";
|
|
12
12
|
export type NativeLifecycleCliOptions = Readonly<Record<string, unknown>>;
|
|
13
|
+
export declare const CODEX_FOREGROUND_IDENTIFICATION_TTL_MS = 30000;
|
|
14
|
+
export declare const CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES = 240;
|
|
15
|
+
/**
|
|
16
|
+
* Short-lived diagnostic evidence returned only to the in-process caller.
|
|
17
|
+
*
|
|
18
|
+
* This is deliberately not a Store binding or an authorization token. The
|
|
19
|
+
* caller must remain inside the same terminal lock and must still use the
|
|
20
|
+
* ordinary request-acceptance path before creating durable Session authority.
|
|
21
|
+
*/
|
|
22
|
+
export interface CodexForegroundIdentificationProof {
|
|
23
|
+
readonly terminalId: string;
|
|
24
|
+
readonly pid: number;
|
|
25
|
+
readonly processUuid: string;
|
|
26
|
+
readonly processBirth: string;
|
|
27
|
+
readonly realCwd: string;
|
|
28
|
+
readonly nativeThreadId: string;
|
|
29
|
+
readonly agentVersion: string;
|
|
30
|
+
readonly behaviorProfile: string;
|
|
31
|
+
readonly evidenceFingerprint: string;
|
|
32
|
+
readonly postProbeScreenDigest: string;
|
|
33
|
+
readonly observationScrollbackLines: typeof CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES;
|
|
34
|
+
readonly observedAt: string;
|
|
35
|
+
readonly expiresAt: string;
|
|
36
|
+
readonly terminalSubmission: {
|
|
37
|
+
readonly command: "/status";
|
|
38
|
+
readonly enterCount: 1;
|
|
39
|
+
readonly materialization: NativeInspectionSubmission["materialization"];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
13
42
|
export interface NativeLifecycleSnapshot {
|
|
14
43
|
readonly identity?: TerminalNativeIdentity;
|
|
15
44
|
readonly runtimeIdentity?: TerminalNativeIdentity;
|
|
@@ -67,7 +96,7 @@ interface NativeLifecycleIdentityPorts {
|
|
|
67
96
|
processBirth: string;
|
|
68
97
|
};
|
|
69
98
|
runtimeForLiveIdentity(input: {
|
|
70
|
-
terminal:
|
|
99
|
+
terminal: LifecycleTerminalObservation;
|
|
71
100
|
identity?: TerminalNativeIdentity;
|
|
72
101
|
expectedEmptyNativeSession?: boolean;
|
|
73
102
|
physicalOnly?: boolean;
|
|
@@ -140,7 +169,13 @@ export interface NativeThreadLifecycleCliFacade {
|
|
|
140
169
|
agentAdapter(options: NativeLifecycleCliOptions, agent: ExecutorKind): TerminalAgentAdapter;
|
|
141
170
|
runList(options: NativeLifecycleCliOptions): Promise<void>;
|
|
142
171
|
runInspect(options: NativeLifecycleCliOptions): Promise<void>;
|
|
143
|
-
|
|
172
|
+
runIdentifyForeground(options: NativeLifecycleCliOptions): Promise<void>;
|
|
173
|
+
identifyCodexForegroundWhileLocked(input: {
|
|
174
|
+
options: NativeLifecycleCliOptions;
|
|
175
|
+
terminal: LifecycleTerminalObservation;
|
|
176
|
+
expectedTerminalToken: string;
|
|
177
|
+
}): Promise<CodexForegroundIdentificationProof>;
|
|
178
|
+
assertSameInspectionTerminal(expected: LifecycleTerminalObservation, actual: LifecycleTerminalObservation, stage: string): void;
|
|
144
179
|
codexLatentClearResumeObservation(input: {
|
|
145
180
|
screen?: string;
|
|
146
181
|
agentVersion?: string;
|
|
@@ -151,4 +186,5 @@ export interface NativeThreadLifecycleCliFacade {
|
|
|
151
186
|
nativeInspectionComposerEmpty(agent: ExecutorKind, screen?: string): boolean;
|
|
152
187
|
}
|
|
153
188
|
export declare function createNativeThreadLifecycleCliAdapter(ports: CreateNativeThreadLifecycleCliAdapterInput): NativeThreadLifecycleCliFacade;
|
|
189
|
+
type NativeInspectionSubmission = Awaited<ReturnType<TerminalAgentBridge["submitNativeInspection"]>>;
|
|
154
190
|
export {};
|
|
@@ -13,6 +13,8 @@ import { listManagedSessions, loadNativeThreadTransition } from "./session-store
|
|
|
13
13
|
import { isExactClaudeNativeInspectionIdleComposer, NativeInspectionSubmissionError } from "./terminal-agent-bridge.js";
|
|
14
14
|
import { terminalControlAliasMatches, selectRootTerminalProcesses, withCodexCompanionFences } from "./terminal-authority-policy.js";
|
|
15
15
|
import { nonBlankString } from "./value-guards.js";
|
|
16
|
+
export const CODEX_FOREGROUND_IDENTIFICATION_TTL_MS = 30_000;
|
|
17
|
+
export const CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES = 240;
|
|
16
18
|
export function createNativeThreadLifecycleCliAdapter(ports) {
|
|
17
19
|
const app = new NativeThreadLifecycleCliApplication(ports);
|
|
18
20
|
return Object.freeze({
|
|
@@ -24,6 +26,8 @@ export function createNativeThreadLifecycleCliAdapter(ports) {
|
|
|
24
26
|
agentAdapter: (options, agent) => app.agentAdapter(options, agent),
|
|
25
27
|
runList: (options) => app.runList(options),
|
|
26
28
|
runInspect: (options) => app.runInspect(options),
|
|
29
|
+
runIdentifyForeground: (options) => app.runIdentifyForeground(options),
|
|
30
|
+
identifyCodexForegroundWhileLocked: (input) => app.identifyCodexForegroundWhileLocked(input),
|
|
27
31
|
assertSameInspectionTerminal: (expected, actual, stage) => app.assertSameInspectionTerminal(expected, actual, stage),
|
|
28
32
|
codexLatentClearResumeObservation,
|
|
29
33
|
nativeInspectionComposerEmpty
|
|
@@ -323,6 +327,10 @@ class NativeThreadLifecycleCliApplication {
|
|
|
323
327
|
}
|
|
324
328
|
assertInspectionReady(input) {
|
|
325
329
|
const { options, terminal, terminalStatus, session } = input;
|
|
330
|
+
if (terminalStatus?.interaction_state !== undefined) {
|
|
331
|
+
throw new Error(`terminal ${terminal.terminalControl.target} is waiting for a native ` +
|
|
332
|
+
"questionnaire response; answer or cancel it before native inspection");
|
|
333
|
+
}
|
|
326
334
|
if (terminalStatus && (terminalStatus.reachable !== true ||
|
|
327
335
|
terminalStatus.activity_state !== "idle" ||
|
|
328
336
|
terminalStatus.approval_state.blocked === true)) {
|
|
@@ -462,6 +470,271 @@ class NativeThreadLifecycleCliApplication {
|
|
|
462
470
|
throw new Error("Claude composer contains input or is not at the exact idle frame; refusing automated terminal input");
|
|
463
471
|
}
|
|
464
472
|
}
|
|
473
|
+
validateForegroundIdentificationOptions(options) {
|
|
474
|
+
if (options.command !== undefined || options.message !== undefined) {
|
|
475
|
+
throw new Error("foreground identification does not accept a command or message payload");
|
|
476
|
+
}
|
|
477
|
+
return required(nonBlankString(options.expectedTerminalToken), "--expected-terminal-token is required");
|
|
478
|
+
}
|
|
479
|
+
async runIdentifyForeground(options) {
|
|
480
|
+
const expectedTerminalToken = this.validateForegroundIdentificationOptions(options);
|
|
481
|
+
const terminal = await this.resolveLifecycleTerminal(options);
|
|
482
|
+
if (terminal.agent !== "codex") {
|
|
483
|
+
throw new Error("foreground identification currently supports only Codex terminals");
|
|
484
|
+
}
|
|
485
|
+
const storeDir = this.ports.state.storeDir(options);
|
|
486
|
+
const release = this.ports.state.acquireTerminal(storeDir, terminal.terminalControl, { timeoutMs: 30_000 });
|
|
487
|
+
try {
|
|
488
|
+
const proof = await this.identifyCodexForegroundWhileLocked({
|
|
489
|
+
options,
|
|
490
|
+
terminal,
|
|
491
|
+
expectedTerminalToken
|
|
492
|
+
});
|
|
493
|
+
this.ports.output.print({
|
|
494
|
+
status: "observed",
|
|
495
|
+
inspection: "identify_foreground",
|
|
496
|
+
terminal_id: proof.terminalId,
|
|
497
|
+
agent: "codex",
|
|
498
|
+
agent_version: proof.agentVersion,
|
|
499
|
+
behavior_profile: proof.behaviorProfile,
|
|
500
|
+
native_thread_id: proof.nativeThreadId,
|
|
501
|
+
foreground_proof: {
|
|
502
|
+
scope: "ephemeral_diagnostic_only",
|
|
503
|
+
observed_at: proof.observedAt,
|
|
504
|
+
expires_at: proof.expiresAt,
|
|
505
|
+
ttl_ms: CODEX_FOREGROUND_IDENTIFICATION_TTL_MS,
|
|
506
|
+
grants_authority: false
|
|
507
|
+
},
|
|
508
|
+
terminal_submission: {
|
|
509
|
+
command: proof.terminalSubmission.command,
|
|
510
|
+
enter_count: proof.terminalSubmission.enterCount,
|
|
511
|
+
materialization_kind: proof.terminalSubmission.materialization.kind
|
|
512
|
+
},
|
|
513
|
+
store_mutation: false,
|
|
514
|
+
session_created: false,
|
|
515
|
+
turn_created: false,
|
|
516
|
+
receipt_created: false,
|
|
517
|
+
monitor_created: false,
|
|
518
|
+
callback_created: false
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
finally {
|
|
522
|
+
release();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async identifyCodexForegroundWhileLocked(input) {
|
|
526
|
+
if (input.terminal.agent !== "codex") {
|
|
527
|
+
throw new Error("foreground identification currently supports only Codex terminals");
|
|
528
|
+
}
|
|
529
|
+
if (!nonBlankString(input.expectedTerminalToken)) {
|
|
530
|
+
throw new Error("foreground identification requires a terminal token");
|
|
531
|
+
}
|
|
532
|
+
const bridge = this.ports.runtime.forOptions(input.options).createBridge();
|
|
533
|
+
const physicalRuntime = this.ports.identity.runtimeForLiveIdentity({
|
|
534
|
+
terminal: input.terminal,
|
|
535
|
+
physicalOnly: true
|
|
536
|
+
});
|
|
537
|
+
const terminal = await bridge.resolveStoredTerminal("codex", input.terminal.pid, input.terminal.terminalControl, physicalRuntime);
|
|
538
|
+
this.assertSameInspectionTerminal(input.terminal, terminal, "while waiting for foreground-identification control");
|
|
539
|
+
const realCwd = this.realForegroundWorkspace(terminal);
|
|
540
|
+
const processIncarnation = this.ports.identity.processIncarnation(terminal.pid);
|
|
541
|
+
this.assertForegroundPhysicalToken({
|
|
542
|
+
terminal,
|
|
543
|
+
processIncarnation,
|
|
544
|
+
expectedTerminalToken: input.expectedTerminalToken
|
|
545
|
+
});
|
|
546
|
+
const runtimeFacade = this.ports.runtime.forOptions(input.options);
|
|
547
|
+
const agentVersion = required(nonBlankString(runtimeFacade.agentVersionForRunningProcess("codex", terminal.pid)), "Codex foreground identification requires a verified running version");
|
|
548
|
+
const adapter = runtimeFacade.createAgentRegistry().require("codex");
|
|
549
|
+
const capability = adapter.probeNativeInspection?.(agentVersion);
|
|
550
|
+
if (capability?.status !== "supported" ||
|
|
551
|
+
capability.statusInspection !== true) {
|
|
552
|
+
throw new Error(capability?.reason ??
|
|
553
|
+
"Codex foreground identification has no supported /status profile");
|
|
554
|
+
}
|
|
555
|
+
const plan = adapter.planNativeInspection?.({ kind: "status" }, capability);
|
|
556
|
+
if (!plan ||
|
|
557
|
+
plan.operation.kind !== "status" ||
|
|
558
|
+
plan.command !== "/status" ||
|
|
559
|
+
plan.effect !== "read_only" ||
|
|
560
|
+
plan.expectedResult.presentation !== "inline") {
|
|
561
|
+
throw new Error("the Codex adapter did not produce the closed inline /status plan");
|
|
562
|
+
}
|
|
563
|
+
const initialStatus = await bridge.status("codex", terminal.terminalControl, {
|
|
564
|
+
runtime: physicalRuntime,
|
|
565
|
+
scrollbackLines: CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES
|
|
566
|
+
});
|
|
567
|
+
this.assertInspectionReady({
|
|
568
|
+
options: input.options,
|
|
569
|
+
terminal,
|
|
570
|
+
terminalStatus: initialStatus
|
|
571
|
+
});
|
|
572
|
+
this.assertForegroundHasNoLifecycleTransition(input.options, terminal);
|
|
573
|
+
await this.assertInspectionComposerReady({
|
|
574
|
+
options: input.options,
|
|
575
|
+
terminal
|
|
576
|
+
});
|
|
577
|
+
const context = {
|
|
578
|
+
options: input.options,
|
|
579
|
+
terminal,
|
|
580
|
+
snapshot: {
|
|
581
|
+
codexCompanions: { additional: [] },
|
|
582
|
+
capabilities: adapter.probeThreadLifecycle?.(agentVersion) ?? {
|
|
583
|
+
status: "unknown",
|
|
584
|
+
agentVersion,
|
|
585
|
+
newThread: false,
|
|
586
|
+
resumeExact: false,
|
|
587
|
+
reason: "thread lifecycle capability is outside this diagnostic"
|
|
588
|
+
},
|
|
589
|
+
version: agentVersion,
|
|
590
|
+
bindingToken: input.expectedTerminalToken,
|
|
591
|
+
bindingTokens: [input.expectedTerminalToken]
|
|
592
|
+
},
|
|
593
|
+
bridge,
|
|
594
|
+
plan,
|
|
595
|
+
runtime: physicalRuntime,
|
|
596
|
+
expectedBindingToken: input.expectedTerminalToken,
|
|
597
|
+
foregroundBoundary: {
|
|
598
|
+
processUuid: processIncarnation.processUuid,
|
|
599
|
+
processBirth: processIncarnation.processBirth,
|
|
600
|
+
realCwd,
|
|
601
|
+
agentVersion,
|
|
602
|
+
expectedTerminalToken: input.expectedTerminalToken
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
const submission = await this.submitInspection(context);
|
|
606
|
+
try {
|
|
607
|
+
const observed = await this.observeStableInspection(context, submission);
|
|
608
|
+
const finalTerminal = await bridge.resolveStoredTerminal("codex", terminal.pid, terminal.terminalControl, physicalRuntime);
|
|
609
|
+
const finalStatus = required(await this.assertFreshForegroundBoundary({
|
|
610
|
+
context,
|
|
611
|
+
terminal: finalTerminal,
|
|
612
|
+
stage: "after foreground identification",
|
|
613
|
+
requireReadyScreen: true
|
|
614
|
+
}), "Codex foreground status revalidation is unavailable");
|
|
615
|
+
const nativeThreadId = required(isExactNativeThreadId(observed.observation.nativeThreadId)
|
|
616
|
+
? observed.observation.nativeThreadId
|
|
617
|
+
: undefined, "Codex /status did not prove one exact foreground native thread");
|
|
618
|
+
const evidenceFingerprint = required(nonBlankString(observed.observation.evidenceFingerprint), "Codex /status did not produce exact evidence");
|
|
619
|
+
const postProbeScreenDigest = required(nonBlankString(finalStatus.screen.digest), "Codex foreground screen digest is unavailable");
|
|
620
|
+
const observedScreenDigest = required(bareNativeInspectionScreenDigest(observed.screenDigest), "Codex foreground observation screen digest is malformed");
|
|
621
|
+
if (observedScreenDigest !== postProbeScreenDigest) {
|
|
622
|
+
throw new Error("Codex screen changed after the stable /status observation");
|
|
623
|
+
}
|
|
624
|
+
const observedAt = new Date();
|
|
625
|
+
const proof = {
|
|
626
|
+
terminalId: terminal.conversationId,
|
|
627
|
+
pid: terminal.pid,
|
|
628
|
+
processUuid: processIncarnation.processUuid,
|
|
629
|
+
processBirth: processIncarnation.processBirth,
|
|
630
|
+
realCwd,
|
|
631
|
+
nativeThreadId,
|
|
632
|
+
agentVersion,
|
|
633
|
+
behaviorProfile: plan.behaviorProfile,
|
|
634
|
+
evidenceFingerprint,
|
|
635
|
+
postProbeScreenDigest,
|
|
636
|
+
observationScrollbackLines: CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES,
|
|
637
|
+
observedAt: observedAt.toISOString(),
|
|
638
|
+
expiresAt: new Date(observedAt.getTime() + CODEX_FOREGROUND_IDENTIFICATION_TTL_MS).toISOString(),
|
|
639
|
+
terminalSubmission: Object.freeze({
|
|
640
|
+
command: "/status",
|
|
641
|
+
enterCount: 1,
|
|
642
|
+
materialization: submission.materialization
|
|
643
|
+
})
|
|
644
|
+
};
|
|
645
|
+
return Object.freeze(proof);
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
649
|
+
if (/do not retry automatically/iu.test(detail))
|
|
650
|
+
throw error;
|
|
651
|
+
throw new Error("Codex foreground-identification Enter was dispatched exactly once, " +
|
|
652
|
+
"but its postcondition became uncertain; do not retry automatically: " +
|
|
653
|
+
detail);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async assertFreshForegroundBoundary(input) {
|
|
657
|
+
const boundary = required(input.context.foregroundBoundary, "foreground-identification boundary is missing");
|
|
658
|
+
this.assertSameInspectionTerminal(input.context.terminal, input.terminal, input.stage);
|
|
659
|
+
if (this.realForegroundWorkspace(input.terminal) !== boundary.realCwd) {
|
|
660
|
+
throw new Error(`terminal real cwd changed ${input.stage}; refresh AKK list`);
|
|
661
|
+
}
|
|
662
|
+
const processIncarnation = this.ports.identity.processIncarnation(input.terminal.pid);
|
|
663
|
+
if (processIncarnation.processUuid !== boundary.processUuid ||
|
|
664
|
+
processIncarnation.processBirth !== boundary.processBirth) {
|
|
665
|
+
throw new Error(`Codex process incarnation changed ${input.stage}; refresh AKK list`);
|
|
666
|
+
}
|
|
667
|
+
this.assertForegroundPhysicalToken({
|
|
668
|
+
terminal: input.terminal,
|
|
669
|
+
processIncarnation,
|
|
670
|
+
expectedTerminalToken: boundary.expectedTerminalToken
|
|
671
|
+
});
|
|
672
|
+
const currentVersion = this.ports.runtime.forOptions(input.context.options)
|
|
673
|
+
.agentVersionForRunningProcess("codex", input.terminal.pid);
|
|
674
|
+
if (currentVersion !== boundary.agentVersion) {
|
|
675
|
+
throw new Error(`Codex version changed ${input.stage}; refresh AKK list`);
|
|
676
|
+
}
|
|
677
|
+
this.assertInspectionReady({
|
|
678
|
+
options: input.context.options,
|
|
679
|
+
terminal: input.terminal
|
|
680
|
+
});
|
|
681
|
+
this.assertForegroundHasNoLifecycleTransition(input.context.options, input.terminal);
|
|
682
|
+
if (!input.requireReadyScreen) {
|
|
683
|
+
return undefined;
|
|
684
|
+
}
|
|
685
|
+
const status = await input.context.bridge.status("codex", input.terminal.terminalControl, {
|
|
686
|
+
runtime: input.context.runtime,
|
|
687
|
+
scrollbackLines: CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES
|
|
688
|
+
});
|
|
689
|
+
this.assertInspectionReady({
|
|
690
|
+
options: input.context.options,
|
|
691
|
+
terminal: input.terminal,
|
|
692
|
+
terminalStatus: status
|
|
693
|
+
});
|
|
694
|
+
await this.assertInspectionComposerReady({
|
|
695
|
+
options: input.context.options,
|
|
696
|
+
terminal: input.terminal
|
|
697
|
+
});
|
|
698
|
+
return status;
|
|
699
|
+
}
|
|
700
|
+
assertForegroundPhysicalToken(input) {
|
|
701
|
+
const actual = unmanagedTerminalBindingToken({
|
|
702
|
+
terminalId: input.terminal.conversationId,
|
|
703
|
+
terminalControl: input.terminal.terminalControl,
|
|
704
|
+
agent: "codex",
|
|
705
|
+
pid: input.terminal.pid,
|
|
706
|
+
workspace: input.terminal.terminalControl.currentPath ?? "",
|
|
707
|
+
processUuid: input.processIncarnation.processUuid,
|
|
708
|
+
processBirth: input.processIncarnation.processBirth
|
|
709
|
+
});
|
|
710
|
+
if (actual !== input.expectedTerminalToken) {
|
|
711
|
+
throw new Error("the physical terminal token is stale; refresh AKK list");
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
realForegroundWorkspace(terminal) {
|
|
715
|
+
const cwd = required(nonBlankString(terminal.terminalControl.currentPath), "Codex terminal cwd is unavailable");
|
|
716
|
+
let realCwd;
|
|
717
|
+
try {
|
|
718
|
+
realCwd = fs.realpathSync(cwd);
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
throw new Error("Codex terminal cwd cannot be resolved exactly");
|
|
722
|
+
}
|
|
723
|
+
if (!fs.statSync(realCwd).isDirectory()) {
|
|
724
|
+
throw new Error("Codex terminal cwd is not a directory");
|
|
725
|
+
}
|
|
726
|
+
return realCwd;
|
|
727
|
+
}
|
|
728
|
+
assertForegroundHasNoLifecycleTransition(options, terminal) {
|
|
729
|
+
const storeDir = this.ports.state.storeDir(options);
|
|
730
|
+
const related = listManagedSessions(storeDir).filter((session) => Boolean(session.binding && terminalControlAliasMatches(session.binding.terminal_id, session.binding.terminal_control, terminal.conversationId, terminal.terminalControl)));
|
|
731
|
+
const unresolved = related.find((session) => ["transitioning", "quarantined"].includes(session.status) ||
|
|
732
|
+
this.ports.state.hasUnresolvedTransition(storeDir, session));
|
|
733
|
+
if (unresolved) {
|
|
734
|
+
throw new Error(`managed Session ${unresolved.session_id} has an unresolved ` +
|
|
735
|
+
"native-thread transition");
|
|
736
|
+
}
|
|
737
|
+
}
|
|
465
738
|
validateInspectionOptions(options) {
|
|
466
739
|
const inspection = required(nonBlankString(options.inspection), "--inspection is required");
|
|
467
740
|
if (inspection !== "status") {
|
|
@@ -551,10 +824,20 @@ class NativeThreadLifecycleCliApplication {
|
|
|
551
824
|
runtime: context.runtime,
|
|
552
825
|
beforeEnter: async () => {
|
|
553
826
|
const terminal = await context.bridge.resolveStoredTerminal(context.terminal.agent, context.terminal.pid, context.terminal.terminalControl, context.runtime);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
827
|
+
if (context.foregroundBoundary) {
|
|
828
|
+
await this.assertFreshForegroundBoundary({
|
|
829
|
+
context,
|
|
830
|
+
terminal,
|
|
831
|
+
stage: "immediately before native status submission",
|
|
832
|
+
requireReadyScreen: false
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
await this.assertFreshInspectionBoundary({
|
|
837
|
+
context, terminal,
|
|
838
|
+
stage: "immediately before native status submission"
|
|
839
|
+
});
|
|
840
|
+
}
|
|
558
841
|
}
|
|
559
842
|
});
|
|
560
843
|
}
|
|
@@ -610,9 +893,15 @@ class NativeThreadLifecycleCliApplication {
|
|
|
610
893
|
: context.runtime;
|
|
611
894
|
let fingerprint;
|
|
612
895
|
let stable;
|
|
896
|
+
let stableScreenDigest;
|
|
613
897
|
let count = 0;
|
|
614
898
|
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
615
|
-
const observed = await context.bridge.observeNativeInspection(context.terminal.agent, context.terminal.terminalControl, request, {
|
|
899
|
+
const observed = await context.bridge.observeNativeInspection(context.terminal.agent, context.terminal.terminalControl, request, {
|
|
900
|
+
runtime: postEnterRuntime,
|
|
901
|
+
scrollbackLines: context.foregroundBoundary
|
|
902
|
+
? CODEX_FOREGROUND_IDENTIFICATION_SCROLLBACK_LINES
|
|
903
|
+
: 240
|
|
904
|
+
});
|
|
616
905
|
if (freshInspectionObservation(context.plan, submission.preEnterScreenDigest, observed)) {
|
|
617
906
|
if (fingerprint === observed.observation.evidenceFingerprint)
|
|
618
907
|
count += 1;
|
|
@@ -621,20 +910,27 @@ class NativeThreadLifecycleCliApplication {
|
|
|
621
910
|
count = 1;
|
|
622
911
|
}
|
|
623
912
|
stable = observed.observation;
|
|
913
|
+
stableScreenDigest = observed.screenDigest;
|
|
624
914
|
if (count >= 2)
|
|
625
915
|
break;
|
|
626
916
|
}
|
|
627
917
|
else {
|
|
628
918
|
fingerprint = undefined;
|
|
629
919
|
stable = undefined;
|
|
920
|
+
stableScreenDigest = undefined;
|
|
630
921
|
count = 0;
|
|
631
922
|
}
|
|
632
923
|
await this.ports.runtime.sleep(100);
|
|
633
924
|
}
|
|
634
|
-
if (!stable || count < 2) {
|
|
925
|
+
if (!stable || !stableScreenDigest || count < 2) {
|
|
635
926
|
throw new Error("native status inspection Enter was dispatched exactly once, but a fresh exact status result was not proven; do not retry automatically");
|
|
636
927
|
}
|
|
637
|
-
return {
|
|
928
|
+
return {
|
|
929
|
+
observation: stable,
|
|
930
|
+
request,
|
|
931
|
+
postEnterRuntime,
|
|
932
|
+
screenDigest: stableScreenDigest
|
|
933
|
+
};
|
|
638
934
|
}
|
|
639
935
|
async dismissInspection(context, observed) {
|
|
640
936
|
if (context.plan.expectedResult.presentation !== "modal")
|
|
@@ -724,12 +1020,16 @@ class NativeThreadLifecycleCliApplication {
|
|
|
724
1020
|
});
|
|
725
1021
|
}
|
|
726
1022
|
}
|
|
1023
|
+
function bareNativeInspectionScreenDigest(fingerprint) {
|
|
1024
|
+
return /^sha256:([0-9a-f]{64})$/u.exec(fingerprint)?.[1];
|
|
1025
|
+
}
|
|
727
1026
|
function freshInspectionObservation(plan, preEnterScreenDigest, observed) {
|
|
728
1027
|
return observed.status.reachable === true &&
|
|
729
1028
|
(plan.expectedResult.presentation === "inline"
|
|
730
1029
|
? observed.status.activity_state === "idle"
|
|
731
1030
|
: !["working", "awaiting_approval"].includes(observed.status.activity_state)) &&
|
|
732
1031
|
observed.status.approval_state.blocked !== true &&
|
|
1032
|
+
observed.status.interaction_state === undefined &&
|
|
733
1033
|
observed.screenDigest !== preEnterScreenDigest &&
|
|
734
1034
|
observed.observation.status === "observed" &&
|
|
735
1035
|
observed.observation.result?.kind === "native_status" &&
|