@scotthuang/agent-knock-knock 0.13.2 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +27 -6
- package/dist/src/callback-outbox-policy.js +27 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +4 -30
- package/dist/src/callback-outbox-service.js +21 -154
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.js +13 -8
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-service.d.ts +46 -0
- package/dist/src/callback-route-service.js +155 -0
- package/dist/src/callback-route-service.js.map +1 -0
- package/dist/src/claude-terminal-agent-adapter.js +4 -0
- package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
- package/dist/src/cli-core.js +9 -1
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-process-incarnation.d.ts +8 -0
- package/dist/src/codex-process-incarnation.js +36 -0
- package/dist/src/codex-process-incarnation.js.map +1 -0
- package/dist/src/codex-terminal-agent-adapter.js +4 -0
- package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-conflict-policy.d.ts +22 -0
- package/dist/src/deferred-foreground-conflict-policy.js +52 -0
- package/dist/src/deferred-foreground-conflict-policy.js.map +1 -0
- package/dist/src/durable-notification-kernel.d.ts +94 -0
- package/dist/src/durable-notification-kernel.js +68 -0
- package/dist/src/durable-notification-kernel.js.map +1 -0
- package/dist/src/herdr-socket-transport.d.ts +43 -0
- package/dist/src/herdr-socket-transport.js +170 -0
- package/dist/src/herdr-socket-transport.js.map +1 -0
- package/dist/src/herdr-terminal-control-provider.d.ts +2 -45
- package/dist/src/herdr-terminal-control-provider.js +8 -173
- package/dist/src/herdr-terminal-control-provider.js.map +1 -1
- package/dist/src/host-adapter-capabilities.d.ts +35 -0
- package/dist/src/host-adapter-capabilities.js +146 -0
- package/dist/src/host-adapter-capabilities.js.map +1 -0
- package/dist/src/host-adapter.d.ts +9 -3
- package/dist/src/host-adapter.js +33 -9
- package/dist/src/host-adapter.js.map +1 -1
- package/dist/src/host-bridge-tools.d.ts +3 -4
- package/dist/src/host-bridge-tools.js +49 -92
- package/dist/src/host-bridge-tools.js.map +1 -1
- package/dist/src/host-bridge.js +5 -5
- package/dist/src/host-bridge.js.map +1 -1
- package/dist/src/host-monitor-reconciliation.d.ts +6 -0
- package/dist/src/host-monitor-reconciliation.js +96 -0
- package/dist/src/host-monitor-reconciliation.js.map +1 -0
- package/dist/src/managed-session.js +2 -18
- package/dist/src/managed-session.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +13 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +635 -3
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-list-projection.d.ts +2 -0
- package/dist/src/openclaw-list-projection.js +3 -0
- package/dist/src/openclaw-list-projection.js.map +1 -0
- package/dist/src/openclaw-model-facing-field-policy.d.ts +2 -0
- package/dist/src/openclaw-model-facing-field-policy.js +3 -0
- package/dist/src/openclaw-model-facing-field-policy.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.d.ts +6 -29
- package/dist/src/openclaw-plugin-command-adapter.js +7 -2370
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +2 -121
- package/dist/src/openclaw-plugin-helpers.js +2 -1164
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +2 -510
- package/dist/src/openclaw-plugin-schemas.js +2 -519
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-plugin-supervisor.d.ts +4 -5
- package/dist/src/openclaw-plugin-supervisor.js +5 -94
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
- package/dist/src/openclaw-plugin-value-helpers.d.ts +2 -0
- package/dist/src/openclaw-plugin-value-helpers.js +3 -0
- package/dist/src/openclaw-plugin-value-helpers.js.map +1 -0
- package/dist/src/openclaw-private-authority-offers.d.ts +4 -39
- package/dist/src/openclaw-private-authority-offers.js +3 -258
- package/dist/src/openclaw-private-authority-offers.js.map +1 -1
- package/dist/src/semantic-private-authority-offers.d.ts +41 -0
- package/dist/src/semantic-private-authority-offers.js +261 -0
- package/dist/src/semantic-private-authority-offers.js.map +1 -0
- package/dist/src/semantic-tool-arguments.d.ts +9 -0
- package/dist/src/semantic-tool-arguments.js +64 -0
- package/dist/src/semantic-tool-arguments.js.map +1 -0
- package/dist/src/semantic-tool-catalog.d.ts +48 -0
- package/dist/src/semantic-tool-catalog.js +105 -0
- package/dist/src/semantic-tool-catalog.js.map +1 -0
- package/dist/src/semantic-tool-command-helpers.d.ts +145 -0
- package/dist/src/semantic-tool-command-helpers.js +1272 -0
- package/dist/src/semantic-tool-command-helpers.js.map +1 -0
- package/dist/src/semantic-tool-list-projection.d.ts +10 -0
- package/dist/src/semantic-tool-list-projection.js +497 -0
- package/dist/src/semantic-tool-list-projection.js.map +1 -0
- package/dist/src/semantic-tool-model-facing-field-policy.d.ts +3 -0
- package/dist/src/semantic-tool-model-facing-field-policy.js +66 -0
- package/dist/src/semantic-tool-model-facing-field-policy.js.map +1 -0
- package/dist/src/semantic-tool-presentation.d.ts +37 -0
- package/dist/src/semantic-tool-presentation.js +680 -0
- package/dist/src/semantic-tool-presentation.js.map +1 -0
- package/dist/src/semantic-tool-private-authority.d.ts +39 -0
- package/dist/src/semantic-tool-private-authority.js +602 -0
- package/dist/src/semantic-tool-private-authority.js.map +1 -0
- package/dist/src/semantic-tool-relay.d.ts +16 -0
- package/dist/src/semantic-tool-relay.js +175 -0
- package/dist/src/semantic-tool-relay.js.map +1 -0
- package/dist/src/semantic-tool-runtime.d.ts +10 -0
- package/dist/src/semantic-tool-runtime.js +1157 -0
- package/dist/src/semantic-tool-runtime.js.map +1 -0
- package/dist/src/semantic-tool-schemas.d.ts +563 -0
- package/dist/src/semantic-tool-schemas.js +573 -0
- package/dist/src/semantic-tool-schemas.js.map +1 -0
- package/dist/src/semantic-tool-value-helpers.d.ts +5 -0
- package/dist/src/semantic-tool-value-helpers.js +27 -0
- package/dist/src/semantic-tool-value-helpers.js.map +1 -0
- package/dist/src/store-event-presentation.d.ts +17 -0
- package/dist/src/store-event-presentation.js +33 -0
- package/dist/src/store-event-presentation.js.map +1 -0
- package/dist/src/store.d.ts +3 -16
- package/dist/src/store.js +2 -30
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +3 -17
- package/dist/src/terminal-action-projection.js +13 -41
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-adapter.d.ts +7 -0
- package/dist/src/terminal-agent-adapter.js +8 -0
- package/dist/src/terminal-agent-adapter.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +35 -332
- package/dist/src/terminal-agent-bridge.js +293 -2798
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-approval-cli-adapter.d.ts +17 -0
- package/dist/src/terminal-approval-cli-adapter.js +921 -0
- package/dist/src/terminal-approval-cli-adapter.js.map +1 -0
- package/dist/src/terminal-command-cli-adapter.d.ts +2 -429
- package/dist/src/terminal-command-cli-adapter.js +344 -6061
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-command-cli-ports.d.ts +443 -0
- package/dist/src/terminal-command-cli-ports.js +2 -0
- package/dist/src/terminal-command-cli-ports.js.map +1 -0
- package/dist/src/terminal-command-dispatch-preparation.d.ts +81 -0
- package/dist/src/terminal-command-dispatch-preparation.js +209 -0
- package/dist/src/terminal-command-dispatch-preparation.js.map +1 -0
- package/dist/src/terminal-command-dispatch-transport.d.ts +35 -0
- package/dist/src/terminal-command-dispatch-transport.js +859 -0
- package/dist/src/terminal-command-dispatch-transport.js.map +1 -0
- package/dist/src/terminal-command-foreground-proof.d.ts +32 -0
- package/dist/src/terminal-command-foreground-proof.js +72 -0
- package/dist/src/terminal-command-foreground-proof.js.map +1 -0
- package/dist/src/terminal-command-send-preflight.d.ts +41 -0
- package/dist/src/terminal-command-send-preflight.js +86 -0
- package/dist/src/terminal-command-send-preflight.js.map +1 -0
- package/dist/src/terminal-composer-classifier.d.ts +55 -0
- package/dist/src/terminal-composer-classifier.js +638 -0
- package/dist/src/terminal-composer-classifier.js.map +1 -0
- package/dist/src/terminal-control-ref.d.ts +23 -0
- package/dist/src/terminal-control-ref.js +32 -0
- package/dist/src/terminal-control-ref.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-authority.d.ts +56 -0
- package/dist/src/terminal-dispatch-recovery-authority.js +251 -0
- package/dist/src/terminal-dispatch-recovery-authority.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +1 -9
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +5 -240
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-handoff-cli-adapter.d.ts +1 -11
- package/dist/src/terminal-handoff-cli-adapter.js +2 -39
- package/dist/src/terminal-handoff-cli-adapter.js.map +1 -1
- package/dist/src/terminal-human-explicit-send-cli-adapter.d.ts +25 -0
- package/dist/src/terminal-human-explicit-send-cli-adapter.js +903 -0
- package/dist/src/terminal-human-explicit-send-cli-adapter.js.map +1 -0
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +1 -6
- package/dist/src/terminal-identity-authority-cli-adapter.js +3 -33
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-cli-adapter.js +209 -210
- package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-core.d.ts +0 -2
- package/dist/src/terminal-interaction-core.js +4 -3
- package/dist/src/terminal-interaction-core.js.map +1 -1
- package/dist/src/terminal-interaction-protocol.d.ts +21 -0
- package/dist/src/terminal-interaction-protocol.js +74 -0
- package/dist/src/terminal-interaction-protocol.js.map +1 -1
- package/dist/src/terminal-interaction-response-bridge.d.ts +126 -0
- package/dist/src/terminal-interaction-response-bridge.js +579 -0
- package/dist/src/terminal-interaction-response-bridge.js.map +1 -0
- package/dist/src/terminal-interaction-response-transaction.d.ts +44 -0
- package/dist/src/terminal-interaction-response-transaction.js +56 -0
- package/dist/src/terminal-interaction-response-transaction.js.map +1 -0
- package/dist/src/terminal-list-action-policy.d.ts +43 -0
- package/dist/src/terminal-list-action-policy.js +164 -0
- package/dist/src/terminal-list-action-policy.js.map +1 -0
- package/dist/src/terminal-list-cli-adapter.d.ts +6 -36
- package/dist/src/terminal-list-cli-adapter.js +314 -1711
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-facts.d.ts +129 -0
- package/dist/src/terminal-list-facts.js +179 -0
- package/dist/src/terminal-list-facts.js.map +1 -0
- package/dist/src/terminal-list-inventory.d.ts +59 -0
- package/dist/src/terminal-list-inventory.js +194 -0
- package/dist/src/terminal-list-inventory.js.map +1 -0
- package/dist/src/terminal-list-ownership-service.d.ts +118 -0
- package/dist/src/terminal-list-ownership-service.js +1003 -0
- package/dist/src/terminal-list-ownership-service.js.map +1 -0
- package/dist/src/terminal-list-projection-service.d.ts +49 -0
- package/dist/src/terminal-list-projection-service.js +407 -0
- package/dist/src/terminal-list-projection-service.js.map +1 -0
- package/dist/src/terminal-list-renderer.d.ts +12 -11
- package/dist/src/terminal-list-renderer.js +237 -104
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-managed-send-cli-adapter.d.ts +33 -0
- package/dist/src/terminal-managed-send-cli-adapter.js +994 -0
- package/dist/src/terminal-managed-send-cli-adapter.js.map +1 -0
- package/dist/src/terminal-managed-turn-list-action-policy.d.ts +47 -0
- package/dist/src/terminal-managed-turn-list-action-policy.js +64 -0
- package/dist/src/terminal-managed-turn-list-action-policy.js.map +1 -0
- package/dist/src/terminal-model-control-action-policy.d.ts +60 -0
- package/dist/src/terminal-model-control-action-policy.js +96 -0
- package/dist/src/terminal-model-control-action-policy.js.map +1 -0
- package/dist/src/terminal-model-control-availability.d.ts +68 -0
- package/dist/src/terminal-model-control-availability.js +157 -0
- package/dist/src/terminal-model-control-availability.js.map +1 -0
- package/dist/src/terminal-model-control-bridge.d.ts +48 -0
- package/dist/src/terminal-model-control-bridge.js +259 -0
- package/dist/src/terminal-model-control-bridge.js.map +1 -0
- package/dist/src/terminal-model-control-profile.d.ts +55 -0
- package/dist/src/terminal-model-control-profile.js +137 -0
- package/dist/src/terminal-model-control-profile.js.map +1 -0
- package/dist/src/terminal-model-control-subject.d.ts +34 -0
- package/dist/src/terminal-model-control-subject.js +58 -0
- package/dist/src/terminal-model-control-subject.js.map +1 -0
- package/dist/src/terminal-model-control-transaction.d.ts +19 -0
- package/dist/src/terminal-model-control-transaction.js +39 -0
- package/dist/src/terminal-model-control-transaction.js.map +1 -0
- package/dist/src/terminal-model-control.d.ts +331 -0
- package/dist/src/terminal-model-control.js +1686 -0
- package/dist/src/terminal-model-control.js.map +1 -0
- package/dist/src/terminal-monitor-application-service.js +50 -17
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-native-inspection-bridge.d.ts +211 -0
- package/dist/src/terminal-native-inspection-bridge.js +1127 -0
- package/dist/src/terminal-native-inspection-bridge.js.map +1 -0
- package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-runtime-cli-adapter.js +59 -17
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-retry-application.d.ts +24 -0
- package/dist/src/terminal-submission-retry-application.js +402 -0
- package/dist/src/terminal-submission-retry-application.js.map +1 -0
- package/dist/src/terminal-submission-retry-ports.d.ts +30 -0
- package/dist/src/terminal-submission-retry-ports.js +2 -0
- package/dist/src/terminal-submission-retry-ports.js.map +1 -0
- package/dist/src/terminal-submission-retry-reconciliation.d.ts +236 -0
- package/dist/src/terminal-submission-retry-reconciliation.js +1130 -0
- package/dist/src/terminal-submission-retry-reconciliation.js.map +1 -0
- package/dist/src/terminal-submission-retry-transport.d.ts +58 -0
- package/dist/src/terminal-submission-retry-transport.js +675 -0
- package/dist/src/terminal-submission-retry-transport.js.map +1 -0
- package/dist/src/terminal-text-submission-bridge.d.ts +200 -0
- package/dist/src/terminal-text-submission-bridge.js +632 -0
- package/dist/src/terminal-text-submission-bridge.js.map +1 -0
- package/dist/src/terminal-user-explicit-send-policy.d.ts +29 -0
- package/dist/src/terminal-user-explicit-send-policy.js +46 -0
- package/dist/src/terminal-user-explicit-send-policy.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.js +91 -143
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-codec.d.ts +9 -0
- package/dist/src/terminal-watch-codec.js +1051 -0
- package/dist/src/terminal-watch-codec.js.map +1 -0
- package/dist/src/terminal-watch-interaction-response-store.d.ts +19 -0
- package/dist/src/terminal-watch-interaction-response-store.js +87 -0
- package/dist/src/terminal-watch-interaction-response-store.js.map +1 -0
- package/dist/src/terminal-watch-record.d.ts +319 -0
- package/dist/src/terminal-watch-record.js +742 -0
- package/dist/src/terminal-watch-record.js.map +1 -0
- package/dist/src/terminal-watch-service.js +48 -27
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +3 -279
- package/dist/src/terminal-watch-store.js +5 -1779
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/quickstart-herdr.md +1 -1
- package/docs/quickstart-tmux.md +27 -2
- package/openclaw.plugin.json +13 -1
- package/package.json +7 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +54 -11
|
@@ -0,0 +1,1127 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { TerminalControlInputNotSentError } from "./terminal-control-provider.js";
|
|
3
|
+
import { sameTerminalControlIncarnation } from "./terminal-control-ref.js";
|
|
4
|
+
import { CODEX_EXACT_CANDIDATE_GRACE_CAPTURES, CODEX_MULTILINE_SETTLE_POLL_MS, CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES, CODEX_MULTILINE_STABLE_CAPTURES, CODEX_PASTE_ENTER_SETTLE_MS } from "./terminal-text-submission-bridge.js";
|
|
5
|
+
export const CODEX_COMPOSER_MARKER = /^[›»](?:\s|$)/u;
|
|
6
|
+
export const CODEX_COMPOSER_FOOTER = /^(?:gpt-[\w.-]+(?:\s|$)|[-\w.]+ default ·)/u;
|
|
7
|
+
// Keep every exact slash-completion shape closed per behavior profile so a
|
|
8
|
+
// version adding another matching command cannot silently become an authorized
|
|
9
|
+
// native command surface.
|
|
10
|
+
const CODEX_NATIVE_STATUS_POPUP_BY_PROFILE = {
|
|
11
|
+
"codex-tui-0.146.0": [
|
|
12
|
+
" /status show current session configuration and token usage"
|
|
13
|
+
],
|
|
14
|
+
"codex-tui-0.146.1": [
|
|
15
|
+
" /status show current session configuration and token usage"
|
|
16
|
+
],
|
|
17
|
+
"codex-tui-0.147.0": [
|
|
18
|
+
" /status show current session configuration and token usage",
|
|
19
|
+
" /statusline configure which items appear in the status line"
|
|
20
|
+
],
|
|
21
|
+
"codex-tui-0.148.0": [
|
|
22
|
+
" /status show current session configuration and token usage",
|
|
23
|
+
" /statusline configure which items appear in the status line"
|
|
24
|
+
],
|
|
25
|
+
"codex-tui-0.149.1": [
|
|
26
|
+
" /status show current session configuration and token usage",
|
|
27
|
+
" /statusline configure which items appear in the status line"
|
|
28
|
+
],
|
|
29
|
+
"codex-tui-0.150.1": [
|
|
30
|
+
" /status show current session configuration and token usage",
|
|
31
|
+
" /statusline configure which items appear in the status line"
|
|
32
|
+
],
|
|
33
|
+
"codex-tui-0.151.0": [
|
|
34
|
+
" /status show current session configuration and token usage",
|
|
35
|
+
" /statusline configure which items appear in the status line"
|
|
36
|
+
],
|
|
37
|
+
"codex-tui-0.153.0": [
|
|
38
|
+
" /status show current session configuration and token usage",
|
|
39
|
+
" /statusline configure which items appear in the status line"
|
|
40
|
+
],
|
|
41
|
+
"codex-tui-0.153.4": [
|
|
42
|
+
" /status show current session configuration and token usage",
|
|
43
|
+
" /statusline configure which items appear in the status line"
|
|
44
|
+
],
|
|
45
|
+
"codex-tui-0.154.0": [
|
|
46
|
+
" /status show current session configuration and token usage",
|
|
47
|
+
" /statusline configure which items appear in the status line"
|
|
48
|
+
],
|
|
49
|
+
"codex-tui-generic-v1": [
|
|
50
|
+
" /status show current session configuration and token usage",
|
|
51
|
+
" /statusline configure which items appear in the status line"
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
// Codex's verified `/status` profiles are exercised against its canonical
|
|
55
|
+
// 80-column status surface. Narrower layouts can truncate the 36-character
|
|
56
|
+
// Session UUID after a dynamically sized label column. Exact provider geometry
|
|
57
|
+
// is required before input; ANSI visible-buffer width is only a conservative
|
|
58
|
+
// fallback diagnostic and never upgrades unknown geometry to safe.
|
|
59
|
+
const CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE = {
|
|
60
|
+
"codex-tui-0.146.0": 80,
|
|
61
|
+
"codex-tui-0.146.1": 80,
|
|
62
|
+
"codex-tui-0.147.0": 80,
|
|
63
|
+
"codex-tui-0.148.0": 80,
|
|
64
|
+
"codex-tui-0.149.1": 80,
|
|
65
|
+
"codex-tui-0.150.1": 80,
|
|
66
|
+
"codex-tui-0.151.0": 80,
|
|
67
|
+
"codex-tui-0.153.0": 80,
|
|
68
|
+
"codex-tui-0.153.4": 80,
|
|
69
|
+
"codex-tui-0.154.0": 80,
|
|
70
|
+
"codex-tui-generic-v1": 80
|
|
71
|
+
};
|
|
72
|
+
const CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE = {
|
|
73
|
+
"claude-code-2.1.218-native-status": [
|
|
74
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
75
|
+
"/statusline Set up Claude Code's status line UI",
|
|
76
|
+
"/ide Manage IDE integrations and show status",
|
|
77
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
78
|
+
],
|
|
79
|
+
"claude-code-2.1.226-native-status": [
|
|
80
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
81
|
+
"/statusline Set up Claude Code's status line UI",
|
|
82
|
+
"/ide Manage IDE integrations and show status",
|
|
83
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
84
|
+
],
|
|
85
|
+
"claude-code-2.1.237-native-status": [
|
|
86
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
87
|
+
"/statusline Set up Claude Code's status line UI",
|
|
88
|
+
"/ide Manage IDE integrations and show status",
|
|
89
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
90
|
+
],
|
|
91
|
+
"claude-code-2.1.251-native-status": [
|
|
92
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
93
|
+
"/statusline Set up Claude Code's status line UI",
|
|
94
|
+
"/ide Manage IDE integrations and show status",
|
|
95
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
96
|
+
],
|
|
97
|
+
"claude-code-2.1.259-native-status": [
|
|
98
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
99
|
+
"/statusline Set up Claude Code's status line UI",
|
|
100
|
+
"/ide Manage IDE integrations and show status",
|
|
101
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
102
|
+
],
|
|
103
|
+
"claude-code-2.1.263-native-status": [
|
|
104
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
105
|
+
"/statusline Set up Claude Code's status line UI",
|
|
106
|
+
"/ide Manage IDE integrations and show status",
|
|
107
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
108
|
+
],
|
|
109
|
+
"claude-code-2.1.266-native-status": [
|
|
110
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
111
|
+
"/statusline Set up Claude Code's status line UI",
|
|
112
|
+
"/ide Manage IDE integrations and show status",
|
|
113
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
114
|
+
],
|
|
115
|
+
"claude-code-2.1.267-native-status": [
|
|
116
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
117
|
+
"/statusline Set up Claude Code's status line UI",
|
|
118
|
+
"/ide Manage IDE integrations and show status",
|
|
119
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
120
|
+
],
|
|
121
|
+
"claude-code-unverified-native-status-v1": [
|
|
122
|
+
"/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
|
|
123
|
+
"/statusline Set up Claude Code's status line UI",
|
|
124
|
+
"/ide Manage IDE integrations and show status",
|
|
125
|
+
"/usage Show session cost, plan usage, and activity stats"
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
const CLAUDE_NATIVE_STATUS_SETTLE_BY_PROFILE = {
|
|
129
|
+
"claude-code-2.1.218-native-status": {
|
|
130
|
+
minimumStableMs: 80,
|
|
131
|
+
maximumSettleMs: 2_000
|
|
132
|
+
},
|
|
133
|
+
"claude-code-2.1.226-native-status": {
|
|
134
|
+
minimumStableMs: 80,
|
|
135
|
+
maximumSettleMs: 5_000
|
|
136
|
+
},
|
|
137
|
+
"claude-code-2.1.237-native-status": {
|
|
138
|
+
minimumStableMs: 80,
|
|
139
|
+
maximumSettleMs: 5_000
|
|
140
|
+
},
|
|
141
|
+
"claude-code-2.1.251-native-status": {
|
|
142
|
+
minimumStableMs: 80,
|
|
143
|
+
maximumSettleMs: 5_000
|
|
144
|
+
},
|
|
145
|
+
"claude-code-2.1.259-native-status": {
|
|
146
|
+
minimumStableMs: 80,
|
|
147
|
+
maximumSettleMs: 5_000
|
|
148
|
+
},
|
|
149
|
+
"claude-code-2.1.263-native-status": {
|
|
150
|
+
minimumStableMs: 80,
|
|
151
|
+
maximumSettleMs: 5_000
|
|
152
|
+
},
|
|
153
|
+
"claude-code-2.1.266-native-status": {
|
|
154
|
+
minimumStableMs: 80,
|
|
155
|
+
maximumSettleMs: 5_000
|
|
156
|
+
},
|
|
157
|
+
"claude-code-2.1.267-native-status": {
|
|
158
|
+
minimumStableMs: 80,
|
|
159
|
+
maximumSettleMs: 5_000
|
|
160
|
+
},
|
|
161
|
+
"claude-code-unverified-native-status-v1": {
|
|
162
|
+
minimumStableMs: 80,
|
|
163
|
+
maximumSettleMs: 5_000
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* A fail-closed native-inspection submission result.
|
|
168
|
+
*
|
|
169
|
+
* Only `not_started` proves that a caller may safely retry. Once text has
|
|
170
|
+
* reached the composer, or Enter has been attempted, automated retries could
|
|
171
|
+
* duplicate input or execute a different command after terminal drift.
|
|
172
|
+
*/
|
|
173
|
+
export class NativeInspectionSubmissionError extends Error {
|
|
174
|
+
stage;
|
|
175
|
+
code = "AKK_NATIVE_INSPECTION_SUBMISSION_FAILED";
|
|
176
|
+
doNotRetry;
|
|
177
|
+
constructor(stage, message, options = {}) {
|
|
178
|
+
super(message, options);
|
|
179
|
+
this.stage = stage;
|
|
180
|
+
this.name = "NativeInspectionSubmissionError";
|
|
181
|
+
this.doNotRetry = stage !== "not_started";
|
|
182
|
+
this.diagnostic = options.diagnostic;
|
|
183
|
+
}
|
|
184
|
+
diagnostic;
|
|
185
|
+
}
|
|
186
|
+
class NativeInspectionDiagnosticError extends Error {
|
|
187
|
+
diagnostic;
|
|
188
|
+
constructor(diagnostic, message, options = {}) {
|
|
189
|
+
super(message, options);
|
|
190
|
+
this.diagnostic = diagnostic;
|
|
191
|
+
this.name = "NativeInspectionDiagnosticError";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** A verified modal could not be dismissed across one exact key attempt. */
|
|
195
|
+
export class NativeInspectionDismissalError extends Error {
|
|
196
|
+
code = "AKK_NATIVE_INSPECTION_DISMISSAL_FAILED";
|
|
197
|
+
doNotRetry = true;
|
|
198
|
+
constructor(message, options = {}) {
|
|
199
|
+
super(message, options);
|
|
200
|
+
this.name = "NativeInspectionDismissalError";
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Closed native-inspection transport. Terminal locking, Store authority, and
|
|
205
|
+
* the caller's one-shot input ledger remain outside this internal service.
|
|
206
|
+
*/
|
|
207
|
+
export class TerminalNativeInspectionBridge {
|
|
208
|
+
registry;
|
|
209
|
+
terminalProvider;
|
|
210
|
+
constructor(options) {
|
|
211
|
+
this.registry = options.registry;
|
|
212
|
+
this.terminalProvider = options.terminalProvider;
|
|
213
|
+
this.runtime = options.runtime;
|
|
214
|
+
}
|
|
215
|
+
runtime;
|
|
216
|
+
captureInspection(adapter, terminalControl, options = {}) {
|
|
217
|
+
return this.runtime.captureInspection(adapter, terminalControl, options);
|
|
218
|
+
}
|
|
219
|
+
verifyTerminalIdentity(agent, terminalControl, runtime) {
|
|
220
|
+
return this.runtime.verifyIdentity(agent, terminalControl, runtime);
|
|
221
|
+
}
|
|
222
|
+
nowMs() {
|
|
223
|
+
return this.runtime.nowMs();
|
|
224
|
+
}
|
|
225
|
+
sleep(milliseconds) {
|
|
226
|
+
return this.runtime.sleep(milliseconds);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Submit one closed, adapter-owned native inspection command.
|
|
230
|
+
*
|
|
231
|
+
* This intentionally does not use `send()`: native slash commands need a
|
|
232
|
+
* stricter composer proof, and failures after text injection must leave the
|
|
233
|
+
* draft untouched instead of issuing the legacy best-effort C-u cleanup.
|
|
234
|
+
*/
|
|
235
|
+
async submitNativeInspection(agent, terminalControl, plan, options = {}) {
|
|
236
|
+
return this.submitClosedNativeInspection(agent, terminalControl, plan, options, { requireCodexReadyComposer: agent === "codex" });
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Submit Codex's closed, version-profiled `/status` probe.
|
|
240
|
+
*
|
|
241
|
+
* Unlike the generic native-inspection entry point, callers provide only
|
|
242
|
+
* the detected Codex version, never a command or plan. The bridge proves an
|
|
243
|
+
* exact empty (or fully dim replace-on-type) ANSI composer before injecting
|
|
244
|
+
* text, then crosses Codex's paste suppression window under the same exact
|
|
245
|
+
* composer and terminal-identity fences used by native inspection.
|
|
246
|
+
*/
|
|
247
|
+
async submitCodexStatusProbe(terminalControl, agentVersion, options = {}) {
|
|
248
|
+
const adapter = this.registry.require("codex");
|
|
249
|
+
let plan;
|
|
250
|
+
try {
|
|
251
|
+
const capability = adapter.probeNativeInspection?.(agentVersion);
|
|
252
|
+
if (capability?.status !== "supported" ||
|
|
253
|
+
capability.statusInspection !== true) {
|
|
254
|
+
throw new NativeInspectionDiagnosticError("unsupported_profile", capability?.reason ??
|
|
255
|
+
`Codex ${agentVersion} has no closed /status behavior profile`);
|
|
256
|
+
}
|
|
257
|
+
const planned = adapter.planNativeInspection?.({ kind: "status" }, capability);
|
|
258
|
+
if (!planned) {
|
|
259
|
+
throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${agentVersion} did not produce a closed /status plan`);
|
|
260
|
+
}
|
|
261
|
+
plan = planned;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
throw nativeInspectionSubmissionError("not_started", error, "unsupported_profile");
|
|
265
|
+
}
|
|
266
|
+
const result = await this.submitClosedNativeInspection("codex", terminalControl, plan, options, { requireCodexReadyComposer: true });
|
|
267
|
+
if (!result.preTextScreenDigest) {
|
|
268
|
+
throw new Error("Codex /status pre-text composer evidence is missing");
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
...result,
|
|
272
|
+
agent: "codex",
|
|
273
|
+
preTextScreenDigest: result.preTextScreenDigest,
|
|
274
|
+
observationBaselineDigest: bareDigestFromNativeInspectionScreenFingerprint(result.preEnterScreenDigest),
|
|
275
|
+
observationScrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async submitClosedNativeInspection(agent, terminalControl, plan, options, safety = {}) {
|
|
279
|
+
const adapter = this.registry.require(agent);
|
|
280
|
+
try {
|
|
281
|
+
assertClosedStatusInspectionPlan(adapter, terminalControl, plan);
|
|
282
|
+
assertTerminalMutationCapabilities({
|
|
283
|
+
provider: this.terminalProvider,
|
|
284
|
+
terminal: this.terminalProvider.endpoint(terminalControl),
|
|
285
|
+
semantic: ["send_keys", "screen_status"],
|
|
286
|
+
transport: [
|
|
287
|
+
"stable_resource_resolution",
|
|
288
|
+
"screen_capture",
|
|
289
|
+
"text_delivery",
|
|
290
|
+
"key_delivery",
|
|
291
|
+
...(safety.requireCodexReadyComposer
|
|
292
|
+
? ["ansi_capture"]
|
|
293
|
+
: [])
|
|
294
|
+
]
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
throw nativeInspectionSubmissionError("not_started", error, "capability_unavailable");
|
|
299
|
+
}
|
|
300
|
+
let verifiedForText;
|
|
301
|
+
try {
|
|
302
|
+
verifiedForText = await this.verifyTerminalIdentity(adapter.agent, terminalControl, options.runtime);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
throw nativeInspectionSubmissionError("not_started", error, "identity_unverified");
|
|
306
|
+
}
|
|
307
|
+
let preTextScreenDigest;
|
|
308
|
+
if (safety.requireCodexReadyComposer) {
|
|
309
|
+
try {
|
|
310
|
+
const ready = await this.captureCodexReadyComposer(adapter, verifiedForText, plan, options.runtime);
|
|
311
|
+
verifiedForText = ready.terminalControl;
|
|
312
|
+
preTextScreenDigest = ready.screenDigest;
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
throw nativeInspectionSubmissionError("not_started", error, "composer_not_ready");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
if (!verifiedForText.capabilities.includes("send_keys")) {
|
|
320
|
+
throw new TerminalControlInputNotSentError(`${adapter.displayName} native inspection input capability changed`);
|
|
321
|
+
}
|
|
322
|
+
await this.terminalProvider.sendText(this.terminalProvider.endpoint(verifiedForText), plan.command);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
if (error instanceof TerminalControlInputNotSentError) {
|
|
326
|
+
throw nativeInspectionSubmissionError("not_started", error, "text_delivery_unproven");
|
|
327
|
+
}
|
|
328
|
+
// The transport cannot prove whether an untyped failure happened before
|
|
329
|
+
// or after tmux accepted the literal input. Fail closed as injected.
|
|
330
|
+
throw nativeInspectionSubmissionError("text_injected", error, "text_delivery_unproven");
|
|
331
|
+
}
|
|
332
|
+
let settled;
|
|
333
|
+
try {
|
|
334
|
+
settled = await this.settleNativeInspectionComposer(adapter, terminalControl, plan, options.runtime);
|
|
335
|
+
await options.beforeEnter?.({
|
|
336
|
+
agent: adapter.agent,
|
|
337
|
+
terminalControl: settled.terminalControl,
|
|
338
|
+
plan,
|
|
339
|
+
preEnterScreenDigest: settled.screenDigest,
|
|
340
|
+
materialization: settled.materialization
|
|
341
|
+
});
|
|
342
|
+
if (safety.requireCodexReadyComposer) {
|
|
343
|
+
settled = {
|
|
344
|
+
...settled,
|
|
345
|
+
terminalControl: await this.assertFinalCodexStatusViewport(settled.terminalControl, plan, options.runtime)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
// Viewport inspection may await provider I/O and the PTY may receive
|
|
349
|
+
// human input while that proof is in flight. Keep the exact composer
|
|
350
|
+
// capture as the final substantive asynchronous evidence before the
|
|
351
|
+
// single Enter attempt.
|
|
352
|
+
settled = await this.revalidateNativeInspectionComposer(adapter, settled.terminalControl, plan, settled.materialization, options.runtime);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
throw nativeInspectionSubmissionError("text_injected", error, "composer_not_exact");
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
// Exactly one Enter attempt. Any error is submission-uncertain and must
|
|
359
|
+
// never trigger a fallback executable or a blind second Enter.
|
|
360
|
+
await this.terminalProvider.sendKeys(this.terminalProvider.endpoint(settled.terminalControl), ["C-m"]);
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
throw nativeInspectionSubmissionError("enter_uncertain", error, "enter_uncertain");
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
stage: "enter_dispatched",
|
|
367
|
+
agent: adapter.agent,
|
|
368
|
+
terminalControl: settled.terminalControl,
|
|
369
|
+
command: plan.command,
|
|
370
|
+
behaviorProfile: plan.behaviorProfile,
|
|
371
|
+
preEnterScreenDigest: settled.screenDigest,
|
|
372
|
+
preEnterEvidenceInventory: settled.evidenceInventory,
|
|
373
|
+
materialization: settled.materialization,
|
|
374
|
+
enterCount: 1,
|
|
375
|
+
...(preTextScreenDigest ? { preTextScreenDigest } : {})
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async observeNativeInspection(agent, terminalControl, request, options = {}) {
|
|
379
|
+
const adapter = this.registry.require(agent);
|
|
380
|
+
if (!adapter.observeNativeInspection) {
|
|
381
|
+
throw new Error(`${adapter.displayName} native inspection observation is not supported`);
|
|
382
|
+
}
|
|
383
|
+
const captured = await this.captureInspection(adapter, terminalControl, {
|
|
384
|
+
runtime: options.runtime,
|
|
385
|
+
scrollbackLines: options.scrollbackLines
|
|
386
|
+
});
|
|
387
|
+
const screenDigest = nativeInspectionScreenFingerprint(captured.screen);
|
|
388
|
+
const adapterObservation = adapter.observeNativeInspection({
|
|
389
|
+
operation: request.operation,
|
|
390
|
+
previousScreenFingerprint: request.previousScreenFingerprint,
|
|
391
|
+
preEnterEvidenceInventory: request.preEnterEvidenceInventory,
|
|
392
|
+
expectedNativeThreadId: request.expectedNativeThreadId,
|
|
393
|
+
expectedAgentVersion: request.expectedAgentVersion,
|
|
394
|
+
expectedCwd: request.expectedCwd,
|
|
395
|
+
screen: captured.screen
|
|
396
|
+
});
|
|
397
|
+
const observation = adapter.agent === "claude" &&
|
|
398
|
+
options.runtime?.requireExactClaudeAgentRow === true &&
|
|
399
|
+
adapterObservation.status === "observed" &&
|
|
400
|
+
adapterObservation.evidence === "claude_status_panel"
|
|
401
|
+
? {
|
|
402
|
+
...adapterObservation,
|
|
403
|
+
evidence: "claude_status_panel+claude_agents_exact_pid"
|
|
404
|
+
}
|
|
405
|
+
: adapterObservation;
|
|
406
|
+
return {
|
|
407
|
+
terminalControl: captured.terminalControl,
|
|
408
|
+
status: this.runtime.statusFromInspection(adapter, captured.terminalControl, captured.inspection, { screen: captured.screen, runtime: options.runtime }),
|
|
409
|
+
screenDigest,
|
|
410
|
+
observation
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Dismiss one exact adapter-owned modal result after re-observing the same
|
|
415
|
+
* evidence under the terminal identity fence. There is exactly one key
|
|
416
|
+
* attempt and no automated retry across an uncertain dismissal boundary.
|
|
417
|
+
*/
|
|
418
|
+
async dismissNativeInspection(agent, terminalControl, plan, request, expectedEvidenceFingerprint, options = {}) {
|
|
419
|
+
const adapter = this.registry.require(agent);
|
|
420
|
+
try {
|
|
421
|
+
assertClosedStatusInspectionPlan(adapter, terminalControl, plan);
|
|
422
|
+
assertClosedNativeInspectionDismissal(plan);
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
throw new NativeInspectionDismissalError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
426
|
+
}
|
|
427
|
+
const observeExactPanel = async (control) => {
|
|
428
|
+
const captured = await this.captureInspection(adapter, control, {
|
|
429
|
+
runtime: options.runtime,
|
|
430
|
+
scrollbackLines: options.scrollbackLines
|
|
431
|
+
});
|
|
432
|
+
const observation = adapter.observeNativeInspection?.({
|
|
433
|
+
operation: request.operation,
|
|
434
|
+
previousScreenFingerprint: request.previousScreenFingerprint,
|
|
435
|
+
preEnterEvidenceInventory: request.preEnterEvidenceInventory,
|
|
436
|
+
expectedNativeThreadId: request.expectedNativeThreadId,
|
|
437
|
+
expectedAgentVersion: request.expectedAgentVersion,
|
|
438
|
+
expectedCwd: request.expectedCwd,
|
|
439
|
+
screen: captured.screen
|
|
440
|
+
});
|
|
441
|
+
if (!observation ||
|
|
442
|
+
observation.status !== "observed" ||
|
|
443
|
+
observation.result?.kind !== "native_status" ||
|
|
444
|
+
observation.evidenceFingerprint !== expectedEvidenceFingerprint) {
|
|
445
|
+
throw new Error(observation?.reason ??
|
|
446
|
+
"the exact fresh native status panel changed before dismissal");
|
|
447
|
+
}
|
|
448
|
+
const verified = await this.verifyTerminalIdentity(adapter.agent, captured.terminalControl, options.runtime);
|
|
449
|
+
if (!sameTerminalControlIdentity(captured.terminalControl, verified)) {
|
|
450
|
+
throw new Error("terminal control identity changed before native status dismissal");
|
|
451
|
+
}
|
|
452
|
+
return verified;
|
|
453
|
+
};
|
|
454
|
+
try {
|
|
455
|
+
let verified = await observeExactPanel(terminalControl);
|
|
456
|
+
await options.beforeDismiss?.({
|
|
457
|
+
agent: adapter.agent,
|
|
458
|
+
terminalControl: verified,
|
|
459
|
+
plan,
|
|
460
|
+
evidenceFingerprint: expectedEvidenceFingerprint
|
|
461
|
+
});
|
|
462
|
+
verified = await observeExactPanel(verified);
|
|
463
|
+
try {
|
|
464
|
+
await this.terminalProvider.sendKeys(this.terminalProvider.endpoint(verified), plan.expectedResult.dismissal.keys);
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
throw new NativeInspectionDismissalError("native status modal dismissal outcome is uncertain; do not retry automatically", { cause: error });
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
stage: "dismiss_dispatched",
|
|
471
|
+
agent: adapter.agent,
|
|
472
|
+
terminalControl: verified,
|
|
473
|
+
keys: plan.expectedResult.dismissal.keys,
|
|
474
|
+
dismissCount: 1
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
if (error instanceof NativeInspectionDismissalError) {
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
throw new NativeInspectionDismissalError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async captureCodexReadyComposer(adapter, terminalControl, plan, runtime) {
|
|
485
|
+
if (adapter.agent !== "codex") {
|
|
486
|
+
throw new NativeInspectionDiagnosticError("unsupported_profile", "the closed Codex /status probe requires the Codex adapter");
|
|
487
|
+
}
|
|
488
|
+
const minimumViewport = CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE[plan.behaviorProfile];
|
|
489
|
+
if (minimumViewport === undefined) {
|
|
490
|
+
throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${plan.behaviorProfile} has no exact /status viewport profile`);
|
|
491
|
+
}
|
|
492
|
+
const captureReady = async (control) => {
|
|
493
|
+
const verified = await this.verifyTerminalIdentity(adapter.agent, control, runtime);
|
|
494
|
+
const endpoint = this.terminalProvider.endpoint(verified);
|
|
495
|
+
const viewportInspector = this.terminalProvider.inspectViewport;
|
|
496
|
+
let exactViewport;
|
|
497
|
+
let viewportUnavailableReason = "terminal provider has no exact viewport inspector";
|
|
498
|
+
if (viewportInspector) {
|
|
499
|
+
let viewport;
|
|
500
|
+
try {
|
|
501
|
+
viewport = await viewportInspector.call(this.terminalProvider, endpoint);
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status viewport inspection failed before terminal input: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
505
|
+
}
|
|
506
|
+
if (viewport) {
|
|
507
|
+
if (!Number.isSafeInteger(viewport.columns) ||
|
|
508
|
+
viewport.columns <= 0 ||
|
|
509
|
+
!Number.isSafeInteger(viewport.rows) ||
|
|
510
|
+
viewport.rows <= 0) {
|
|
511
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status viewport inspector returned invalid geometry");
|
|
512
|
+
}
|
|
513
|
+
exactViewport = viewport.columns;
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
viewportUnavailableReason =
|
|
517
|
+
"terminal provider could not prove exact viewport geometry";
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const styledScreen = await this.terminalProvider.capture(endpoint, { scrollbackLines: 40, preserveEscapes: true });
|
|
521
|
+
const plainScreen = stripTerminalEscapeSequences(styledScreen);
|
|
522
|
+
const inspection = adapter.inspectScreen({ screen: plainScreen, runtime });
|
|
523
|
+
assertNativeInspectionComposerSafe(inspection, adapter.displayName);
|
|
524
|
+
const composer = exactCodexReadyStyledComposerCapture(styledScreen);
|
|
525
|
+
if (!composer) {
|
|
526
|
+
throw new NativeInspectionDiagnosticError("composer_not_ready", "Codex composer contains non-placeholder input or is not at the exact idle prompt");
|
|
527
|
+
}
|
|
528
|
+
const inferredViewport = inferCodexVisibleViewportColumns(styledScreen);
|
|
529
|
+
const observedViewport = exactViewport ?? inferredViewport;
|
|
530
|
+
if ((observedViewport !== undefined && observedViewport < minimumViewport) ||
|
|
531
|
+
hasTruncatedCodexStatusSessionLine(plainScreen)) {
|
|
532
|
+
throw new NativeInspectionDiagnosticError("viewport_too_narrow", `Codex /status requires a proven viewport of at least ` +
|
|
533
|
+
`${minimumViewport} columns to preserve the complete Session UUID` +
|
|
534
|
+
`${observedViewport === undefined
|
|
535
|
+
? ""
|
|
536
|
+
: `; observed ${observedViewport}`}; widen or zoom the pane before retrying`);
|
|
537
|
+
}
|
|
538
|
+
if (exactViewport === undefined) {
|
|
539
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status requires exact terminal viewport geometry before input; ` +
|
|
540
|
+
viewportUnavailableReason +
|
|
541
|
+
`${inferredViewport === undefined
|
|
542
|
+
? ""
|
|
543
|
+
: ` (ANSI fallback estimated ${inferredViewport} columns)`}`);
|
|
544
|
+
}
|
|
545
|
+
const reverified = await this.verifyTerminalIdentity(adapter.agent, verified, runtime);
|
|
546
|
+
if (!sameTerminalControlIdentity(verified, reverified)) {
|
|
547
|
+
throw new NativeInspectionDiagnosticError("identity_unverified", "terminal control identity changed after the Codex pre-text composer capture");
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
terminalControl: reverified,
|
|
551
|
+
screenDigest: nativeInspectionScreenFingerprint(styledScreen),
|
|
552
|
+
composerDigest: composer.digest
|
|
553
|
+
};
|
|
554
|
+
};
|
|
555
|
+
const first = await captureReady(terminalControl);
|
|
556
|
+
const second = await captureReady(first.terminalControl);
|
|
557
|
+
if (second.composerDigest !== first.composerDigest) {
|
|
558
|
+
throw new NativeInspectionDiagnosticError("composer_not_ready", "Codex empty composer changed across its stable pre-text captures");
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
terminalControl: second.terminalControl,
|
|
562
|
+
screenDigest: second.screenDigest
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
async assertFinalCodexStatusViewport(terminalControl, plan, runtime) {
|
|
566
|
+
const minimumViewport = CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE[plan.behaviorProfile];
|
|
567
|
+
if (minimumViewport === undefined) {
|
|
568
|
+
throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${plan.behaviorProfile} has no exact /status viewport profile`);
|
|
569
|
+
}
|
|
570
|
+
const verified = await this.verifyTerminalIdentity("codex", terminalControl, runtime);
|
|
571
|
+
const viewportInspector = this.terminalProvider.inspectViewport;
|
|
572
|
+
if (!viewportInspector) {
|
|
573
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status requires exact terminal viewport geometry immediately before Enter");
|
|
574
|
+
}
|
|
575
|
+
let viewport;
|
|
576
|
+
try {
|
|
577
|
+
viewport = await viewportInspector.call(this.terminalProvider, this.terminalProvider.endpoint(verified));
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status viewport inspection failed immediately before Enter: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
581
|
+
}
|
|
582
|
+
if (!viewport ||
|
|
583
|
+
!Number.isSafeInteger(viewport.columns) ||
|
|
584
|
+
viewport.columns <= 0 ||
|
|
585
|
+
!Number.isSafeInteger(viewport.rows) ||
|
|
586
|
+
viewport.rows <= 0) {
|
|
587
|
+
throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status exact terminal viewport became unavailable immediately before Enter");
|
|
588
|
+
}
|
|
589
|
+
if (viewport.columns < minimumViewport) {
|
|
590
|
+
throw new NativeInspectionDiagnosticError("viewport_too_narrow", `Codex /status viewport narrowed to ${viewport.columns} columns before Enter; ` +
|
|
591
|
+
`at least ${minimumViewport} are required to preserve the complete Session UUID`);
|
|
592
|
+
}
|
|
593
|
+
const reverified = await this.verifyTerminalIdentity("codex", verified, runtime);
|
|
594
|
+
if (!sameTerminalControlIdentity(verified, reverified)) {
|
|
595
|
+
throw new NativeInspectionDiagnosticError("identity_unverified", "terminal control identity changed after the final Codex viewport proof");
|
|
596
|
+
}
|
|
597
|
+
return reverified;
|
|
598
|
+
}
|
|
599
|
+
async settleNativeInspectionComposer(adapter, terminalControl, plan, runtime) {
|
|
600
|
+
const startedAt = this.nowMs();
|
|
601
|
+
const settleTimeoutMs = plan.composer.maximumSettleMs;
|
|
602
|
+
let stableDigest;
|
|
603
|
+
let stableKind;
|
|
604
|
+
let stableSince;
|
|
605
|
+
let stableCaptures = 0;
|
|
606
|
+
let capturesBeyondDeadline = 0;
|
|
607
|
+
let lastMismatchDiagnostic = "composer_not_exact";
|
|
608
|
+
while (true) {
|
|
609
|
+
const captured = await this.captureInspection(adapter, terminalControl, {
|
|
610
|
+
runtime,
|
|
611
|
+
scrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
|
|
612
|
+
});
|
|
613
|
+
assertNativeInspectionComposerSafe(captured.inspection);
|
|
614
|
+
const materialized = exactNativeInspectionComposerCapture(adapter.agent, captured.screen, plan);
|
|
615
|
+
const now = this.nowMs();
|
|
616
|
+
if (materialized) {
|
|
617
|
+
if (materialized.digest === stableDigest &&
|
|
618
|
+
materialized.kind === stableKind) {
|
|
619
|
+
stableCaptures += 1;
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
stableDigest = materialized.digest;
|
|
623
|
+
stableKind = materialized.kind;
|
|
624
|
+
stableSince = now;
|
|
625
|
+
stableCaptures = 1;
|
|
626
|
+
}
|
|
627
|
+
const stableForMs = stableSince === undefined ? 0 : now - stableSince;
|
|
628
|
+
if (stableCaptures >= CODEX_MULTILINE_STABLE_CAPTURES &&
|
|
629
|
+
stableForMs >= plan.composer.minimumStableMs) {
|
|
630
|
+
const evidence = {
|
|
631
|
+
kind: materialized.kind,
|
|
632
|
+
digest: materialized.digest,
|
|
633
|
+
stableForMs,
|
|
634
|
+
stableCaptures
|
|
635
|
+
};
|
|
636
|
+
return this.revalidateNativeInspectionComposer(adapter, captured.terminalControl, plan, evidence, runtime);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
lastMismatchDiagnostic = adapter.agent === "codex"
|
|
641
|
+
? codexNativeInspectionComposerMismatchDiagnostic(captured.screen, plan)
|
|
642
|
+
: "composer_not_exact";
|
|
643
|
+
stableDigest = undefined;
|
|
644
|
+
stableKind = undefined;
|
|
645
|
+
stableSince = undefined;
|
|
646
|
+
stableCaptures = 0;
|
|
647
|
+
}
|
|
648
|
+
const elapsed = this.nowMs() - startedAt;
|
|
649
|
+
const remaining = settleTimeoutMs - elapsed;
|
|
650
|
+
if (remaining <= 0 && !materialized) {
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
if (remaining <= 0) {
|
|
654
|
+
capturesBeyondDeadline += 1;
|
|
655
|
+
if (capturesBeyondDeadline > CODEX_EXACT_CANDIDATE_GRACE_CAPTURES) {
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
const remainingStableMs = plan.composer.minimumStableMs -
|
|
660
|
+
(stableSince === undefined ? 0 : this.nowMs() - stableSince);
|
|
661
|
+
await this.sleep(Math.min(CODEX_MULTILINE_SETTLE_POLL_MS, remaining > 0
|
|
662
|
+
? remaining
|
|
663
|
+
: Math.max(1, remainingStableMs)));
|
|
664
|
+
}
|
|
665
|
+
throw new NativeInspectionDiagnosticError(lastMismatchDiagnostic, lastMismatchDiagnostic === "composer_viewport_truncated"
|
|
666
|
+
? `${adapter.displayName} /status slash popup was truncated by the viewport; widen or zoom the pane before retrying manually`
|
|
667
|
+
: `${adapter.displayName} /status composer did not become exact, idle, and stable before the bounded submit deadline`);
|
|
668
|
+
}
|
|
669
|
+
async revalidateNativeInspectionComposer(adapter, terminalControl, plan, expected, runtime) {
|
|
670
|
+
const captured = await this.captureInspection(adapter, terminalControl, {
|
|
671
|
+
runtime,
|
|
672
|
+
scrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
|
|
673
|
+
});
|
|
674
|
+
assertNativeInspectionComposerSafe(captured.inspection);
|
|
675
|
+
const materialized = exactNativeInspectionComposerCapture(adapter.agent, captured.screen, plan);
|
|
676
|
+
if (!materialized ||
|
|
677
|
+
materialized.digest !== expected.digest ||
|
|
678
|
+
materialized.kind !== expected.kind) {
|
|
679
|
+
throw new NativeInspectionDiagnosticError("composer_drift", `${adapter.displayName} /status composer changed after its stable pre-submit capture`);
|
|
680
|
+
}
|
|
681
|
+
const baseline = adapter.observeNativeInspection?.({
|
|
682
|
+
operation: plan.operation,
|
|
683
|
+
screen: captured.screen
|
|
684
|
+
});
|
|
685
|
+
if (!baseline ||
|
|
686
|
+
baseline.status === "ambiguous" ||
|
|
687
|
+
!Array.isArray(baseline.evidenceInventory)) {
|
|
688
|
+
throw new NativeInspectionDiagnosticError("evidence_unproven", baseline?.reason ??
|
|
689
|
+
`${adapter.displayName} /status pre-Enter evidence inventory was not proven`);
|
|
690
|
+
}
|
|
691
|
+
const verifiedImmediatelyBeforeEnter = await this.verifyTerminalIdentity(adapter.agent, captured.terminalControl, runtime);
|
|
692
|
+
if (!sameTerminalControlIdentity(captured.terminalControl, verifiedImmediatelyBeforeEnter)) {
|
|
693
|
+
throw new NativeInspectionDiagnosticError("identity_unverified", `terminal control identity changed after the final ${adapter.displayName} /status composer capture`);
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
terminalControl: verifiedImmediatelyBeforeEnter,
|
|
697
|
+
screenDigest: nativeInspectionScreenFingerprint(captured.screen),
|
|
698
|
+
evidenceInventory: baseline.evidenceInventory,
|
|
699
|
+
materialization: expected
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function sameTerminalControlIdentity(left, right) {
|
|
704
|
+
return sameTerminalControlIncarnation(left, right);
|
|
705
|
+
}
|
|
706
|
+
export function assertTerminalMutationCapabilities({ provider, terminal, semantic, transport }) {
|
|
707
|
+
const missingSemantic = semantic.filter((capability) => !provider.supportedCapabilities.includes(capability) ||
|
|
708
|
+
!terminal.capabilities.includes(capability));
|
|
709
|
+
const missingTransport = transport.filter((capability) => !provider.providerCapabilities.includes(capability));
|
|
710
|
+
if (missingSemantic.length === 0 && missingTransport.length === 0) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const missing = [
|
|
714
|
+
...missingSemantic.map((value) => `terminal:${value}`),
|
|
715
|
+
...missingTransport.map((value) => `provider:${value}`)
|
|
716
|
+
];
|
|
717
|
+
throw new TerminalControlInputNotSentError(`terminal action capability preflight failed for ` +
|
|
718
|
+
`${terminal.identity.providerKind}:${terminal.route.label}: ` +
|
|
719
|
+
missing.join(", "));
|
|
720
|
+
}
|
|
721
|
+
function nativeInspectionSubmissionError(stage, error, fallbackDiagnostic) {
|
|
722
|
+
const diagnostic = error instanceof NativeInspectionSubmissionError
|
|
723
|
+
? error.diagnostic
|
|
724
|
+
: error instanceof NativeInspectionDiagnosticError
|
|
725
|
+
? error.diagnostic
|
|
726
|
+
: fallbackDiagnostic;
|
|
727
|
+
if (error instanceof NativeInspectionSubmissionError) {
|
|
728
|
+
const stageRank = {
|
|
729
|
+
not_started: 0,
|
|
730
|
+
text_injected: 1,
|
|
731
|
+
enter_uncertain: 2
|
|
732
|
+
};
|
|
733
|
+
if (stageRank[error.stage] >= stageRank[stage]) {
|
|
734
|
+
return error;
|
|
735
|
+
}
|
|
736
|
+
return new NativeInspectionSubmissionError(stage, error.message, {
|
|
737
|
+
cause: error,
|
|
738
|
+
diagnostic
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
return new NativeInspectionSubmissionError(stage, error instanceof Error ? error.message : String(error), { cause: error, diagnostic });
|
|
742
|
+
}
|
|
743
|
+
function assertClosedStatusInspectionPlan(adapter, terminalControl, plan) {
|
|
744
|
+
if (!terminalControl.capabilities.includes("send_keys")) {
|
|
745
|
+
throw new Error(`${adapter.displayName} terminal input is not supported`);
|
|
746
|
+
}
|
|
747
|
+
if (!terminalControl.capabilities.includes("screen_status")) {
|
|
748
|
+
throw new Error(`${adapter.displayName} terminal screen inspection is not supported`);
|
|
749
|
+
}
|
|
750
|
+
const codexProfile = adapter.agent === "codex" &&
|
|
751
|
+
CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile] !== undefined;
|
|
752
|
+
const claudeProfile = adapter.agent === "claude" &&
|
|
753
|
+
CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile] !== undefined;
|
|
754
|
+
const expectedSettle = codexProfile
|
|
755
|
+
? { minimumStableMs: CODEX_PASTE_ENTER_SETTLE_MS, maximumSettleMs: 2_000 }
|
|
756
|
+
: CLAUDE_NATIVE_STATUS_SETTLE_BY_PROFILE[plan.behaviorProfile];
|
|
757
|
+
const exactPresentation = codexProfile
|
|
758
|
+
? plan.expectedResult.presentation === "inline" &&
|
|
759
|
+
plan.expectedResult.dismissal === undefined &&
|
|
760
|
+
plan.composer.minimumStableMs === expectedSettle?.minimumStableMs &&
|
|
761
|
+
plan.composer.maximumSettleMs === expectedSettle.maximumSettleMs
|
|
762
|
+
: claudeProfile
|
|
763
|
+
? plan.expectedResult.presentation === "modal" &&
|
|
764
|
+
plan.composer.minimumStableMs === expectedSettle?.minimumStableMs &&
|
|
765
|
+
plan.composer.maximumSettleMs === expectedSettle.maximumSettleMs &&
|
|
766
|
+
plan.expectedResult.dismissal?.expected === "idle_empty_composer" &&
|
|
767
|
+
JSON.stringify(plan.expectedResult.dismissal.keys) ===
|
|
768
|
+
JSON.stringify(["Escape"])
|
|
769
|
+
: false;
|
|
770
|
+
if (plan.operation.kind !== "status" ||
|
|
771
|
+
plan.command !== "/status" ||
|
|
772
|
+
plan.effect !== "read_only" ||
|
|
773
|
+
plan.requiresIdle !== true ||
|
|
774
|
+
plan.composer.kind !== "exact" ||
|
|
775
|
+
!Number.isFinite(plan.composer.minimumStableMs) ||
|
|
776
|
+
plan.composer.minimumStableMs < 0 ||
|
|
777
|
+
!Number.isFinite(plan.composer.maximumSettleMs) ||
|
|
778
|
+
plan.composer.maximumSettleMs < plan.composer.minimumStableMs ||
|
|
779
|
+
plan.expectedResult.kind !== "native_status" ||
|
|
780
|
+
!exactPresentation) {
|
|
781
|
+
throw new Error("refusing a non-closed native inspection plan");
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function assertClosedNativeInspectionDismissal(plan) {
|
|
785
|
+
if (plan.expectedResult.presentation !== "modal" ||
|
|
786
|
+
plan.expectedResult.dismissal?.expected !== "idle_empty_composer" ||
|
|
787
|
+
JSON.stringify(plan.expectedResult.dismissal.keys) !==
|
|
788
|
+
JSON.stringify(["Escape"])) {
|
|
789
|
+
throw new Error("native inspection has no closed modal dismissal plan");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function assertNativeInspectionComposerSafe(inspection, displayName = "terminal agent") {
|
|
793
|
+
if (inspection.approval.blocked ||
|
|
794
|
+
inspection.activity.state === "awaiting_approval" ||
|
|
795
|
+
inspection.activity.state === "working") {
|
|
796
|
+
throw new NativeInspectionDiagnosticError("composer_not_ready", `${displayName} became busy or blocked while its /status composer was settling`);
|
|
797
|
+
}
|
|
798
|
+
// Codex's generic activity parser deliberately reports a non-empty slash
|
|
799
|
+
// composer as unknown. At this stage the caller has already proved an idle,
|
|
800
|
+
// empty styled composer under the terminal lock; the exact-current composer
|
|
801
|
+
// capture below is the stronger continuation proof after AKK injected only
|
|
802
|
+
// the adapter-owned /status command.
|
|
803
|
+
}
|
|
804
|
+
function exactNativeInspectionComposerCapture(agent, screen, plan) {
|
|
805
|
+
return agent === "codex"
|
|
806
|
+
? exactCodexNativeInspectionComposerCapture(screen, plan)
|
|
807
|
+
: exactClaudeNativeInspectionComposerCapture(screen, plan);
|
|
808
|
+
}
|
|
809
|
+
function exactCodexNativeInspectionComposerCapture(screen, plan) {
|
|
810
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
811
|
+
let currentComposerIndex = -1;
|
|
812
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
813
|
+
if (CODEX_COMPOSER_MARKER.test(lines[index])) {
|
|
814
|
+
currentComposerIndex = index;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (currentComposerIndex < 0) {
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
const composerText = lines[currentComposerIndex]
|
|
822
|
+
.replace(/^[›»]\s?/u, "")
|
|
823
|
+
.trimEnd();
|
|
824
|
+
if (composerText !== plan.command) {
|
|
825
|
+
return undefined;
|
|
826
|
+
}
|
|
827
|
+
const footerIndex = lines.findIndex((line, candidateIndex) => candidateIndex > currentComposerIndex &&
|
|
828
|
+
CODEX_COMPOSER_FOOTER.test(line.trim()));
|
|
829
|
+
const region = lines.slice(currentComposerIndex, footerIndex < 0 ? lines.length : footerIndex);
|
|
830
|
+
while (region.length > 1 && region.at(-1)?.trim() === "") {
|
|
831
|
+
region.pop();
|
|
832
|
+
}
|
|
833
|
+
const popupRows = region.slice(1).filter((line) => line.trim().length > 0);
|
|
834
|
+
let kind;
|
|
835
|
+
if (popupRows.length === 0) {
|
|
836
|
+
kind = "exact_slash_composer";
|
|
837
|
+
}
|
|
838
|
+
else if (JSON.stringify(popupRows.map((line) => line.trimEnd())) ===
|
|
839
|
+
JSON.stringify(CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile])) {
|
|
840
|
+
kind = "exact_slash_popup";
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
return undefined;
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
kind,
|
|
847
|
+
digest: createHash("sha256").update(region.join("\n")).digest("hex")
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function exactClaudeNativeInspectionComposerCapture(screen, plan) {
|
|
851
|
+
const frame = exactClaudeComposerFrame(screen);
|
|
852
|
+
if (!frame) {
|
|
853
|
+
return undefined;
|
|
854
|
+
}
|
|
855
|
+
const { lines, openIndex, closeIndex, composerRows, trailing } = frame;
|
|
856
|
+
if (composerRows.length !== 1 ||
|
|
857
|
+
!/^\s*❯(?:\s|$)/u.test(composerRows[0]) ||
|
|
858
|
+
composerRows[0].replace(/^\s*❯\s?/u, "").trimEnd() !== plan.command) {
|
|
859
|
+
return undefined;
|
|
860
|
+
}
|
|
861
|
+
let kind;
|
|
862
|
+
if (trailing.length === 0 || claudeNativeInspectionTrailingIsFooter(trailing)) {
|
|
863
|
+
kind = "exact_slash_composer";
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
const suggestions = [];
|
|
867
|
+
for (const line of trailing) {
|
|
868
|
+
const trimmed = line.trim();
|
|
869
|
+
if (trimmed.startsWith("/")) {
|
|
870
|
+
suggestions.push(trimmed.replace(/\s+/gu, " "));
|
|
871
|
+
}
|
|
872
|
+
else if (suggestions.length > 0) {
|
|
873
|
+
suggestions[suggestions.length - 1] +=
|
|
874
|
+
` ${trimmed.replace(/\s+/gu, " ")}`;
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
return undefined;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (!closedClaudeNativeStatusSuggestionsMatch(suggestions, CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile])) {
|
|
881
|
+
return undefined;
|
|
882
|
+
}
|
|
883
|
+
kind = "exact_slash_popup";
|
|
884
|
+
}
|
|
885
|
+
return {
|
|
886
|
+
kind,
|
|
887
|
+
digest: createHash("sha256")
|
|
888
|
+
.update(lines.slice(openIndex, closeIndex + 1).concat(trailing).join("\n"))
|
|
889
|
+
.digest("hex")
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Claude truncates a suggestion row with a Unicode ellipsis at narrow pane
|
|
894
|
+
* widths. Keep authorization closed by accepting only an exact ordered row or
|
|
895
|
+
* an explicit ellipsis whose preceding text is an exact, non-trivial prefix of
|
|
896
|
+
* that same profiled row. A caller still cannot introduce, omit, or reorder a
|
|
897
|
+
* slash command.
|
|
898
|
+
*/
|
|
899
|
+
function closedClaudeNativeStatusSuggestionsMatch(observed, expected) {
|
|
900
|
+
if (!expected || observed.length !== expected.length) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
return observed.every((row, index) => {
|
|
904
|
+
const exact = expected[index];
|
|
905
|
+
if (row === exact) {
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
if (!row.endsWith("…")) {
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
const prefix = row.slice(0, -1);
|
|
912
|
+
const commandEnd = exact.indexOf(" ");
|
|
913
|
+
return (commandEnd > 0 &&
|
|
914
|
+
prefix.length >= commandEnd + 12 &&
|
|
915
|
+
exact.startsWith(prefix));
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Prove Claude Code's exact current idle input frame. This is shared by every
|
|
920
|
+
* automated-input path: a loose or historical `❯` prompt is not authority to
|
|
921
|
+
* inject text into the terminal.
|
|
922
|
+
*/
|
|
923
|
+
export function isExactClaudeIdleComposer(screen) {
|
|
924
|
+
const frame = exactClaudeComposerFrame(screen);
|
|
925
|
+
if (!frame) {
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
return (frame.composerRows.length === 1 &&
|
|
929
|
+
/^\s*❯\s*$/u.test(frame.composerRows[0]) &&
|
|
930
|
+
(frame.trailing.length === 0 ||
|
|
931
|
+
claudeNativeInspectionTrailingIsFooter(frame.trailing)));
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Compatibility export retained for callers that adopted the native-status
|
|
935
|
+
* name before the same exact-frame proof was reused by lifecycle handoff.
|
|
936
|
+
*/
|
|
937
|
+
export function isExactClaudeNativeInspectionIdleComposer(screen) {
|
|
938
|
+
return isExactClaudeIdleComposer(screen);
|
|
939
|
+
}
|
|
940
|
+
export function exactClaudeComposerFrame(screen) {
|
|
941
|
+
const lines = screen.replace(/\r\n?/gu, "\n").replace(/\u00a0/gu, " ")
|
|
942
|
+
.split("\n");
|
|
943
|
+
const dividerIndexes = lines
|
|
944
|
+
.map((line, index) => /^\s*[─━]{8,}\s*$/u.test(line) ? index : -1)
|
|
945
|
+
.filter((index) => index >= 0);
|
|
946
|
+
if (dividerIndexes.length < 2) {
|
|
947
|
+
return undefined;
|
|
948
|
+
}
|
|
949
|
+
const closeIndex = dividerIndexes.at(-1);
|
|
950
|
+
const openIndex = dividerIndexes.at(-2);
|
|
951
|
+
return {
|
|
952
|
+
lines,
|
|
953
|
+
openIndex,
|
|
954
|
+
closeIndex,
|
|
955
|
+
composerRows: lines.slice(openIndex + 1, closeIndex)
|
|
956
|
+
.filter((line) => line.trim().length > 0),
|
|
957
|
+
trailing: lines.slice(closeIndex + 1)
|
|
958
|
+
.filter((line) => line.trim().length > 0)
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
export function claudeNativeInspectionTrailingIsFooter(lines) {
|
|
962
|
+
return lines.length <= 2 && lines.every(isClaudeNativeComposerFooterLine);
|
|
963
|
+
}
|
|
964
|
+
function isClaudeNativeComposerFooterLine(line) {
|
|
965
|
+
return /^\s*(?:[⏵⏴⏸]{1,2}|\?)\s*.*(?:manual mode|shift\+tab|accept edits|bypass permissions|for shortcuts|← for agents)/iu
|
|
966
|
+
.test(line);
|
|
967
|
+
}
|
|
968
|
+
function nativeInspectionScreenFingerprint(screen) {
|
|
969
|
+
return `sha256:${createHash("sha256").update(screen).digest("hex")}`;
|
|
970
|
+
}
|
|
971
|
+
function bareDigestFromNativeInspectionScreenFingerprint(fingerprint) {
|
|
972
|
+
const match = /^sha256:([0-9a-f]{64})$/u.exec(fingerprint);
|
|
973
|
+
if (!match) {
|
|
974
|
+
throw new Error("native inspection screen fingerprint is malformed");
|
|
975
|
+
}
|
|
976
|
+
return match[1];
|
|
977
|
+
}
|
|
978
|
+
export function stripTerminalEscapeSequences(value) {
|
|
979
|
+
return value.replace(/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/gu, "");
|
|
980
|
+
}
|
|
981
|
+
export function exactCodexReadyStyledComposerCapture(screen) {
|
|
982
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
983
|
+
while (lines.length > 0 &&
|
|
984
|
+
stripTerminalEscapeSequences(lines.at(-1) ?? "").trim().length === 0) {
|
|
985
|
+
lines.pop();
|
|
986
|
+
}
|
|
987
|
+
const composerLine = [...lines.slice(-12)].reverse().find((line) => CODEX_COMPOSER_MARKER.test(stripTerminalEscapeSequences(line).trimEnd()));
|
|
988
|
+
if (composerLine === undefined) {
|
|
989
|
+
return undefined;
|
|
990
|
+
}
|
|
991
|
+
let dim = false;
|
|
992
|
+
const visible = [];
|
|
993
|
+
for (let index = 0; index < composerLine.length;) {
|
|
994
|
+
if (composerLine[index] === "\x1b") {
|
|
995
|
+
const escape = /^(?:\x1B\[([0-9;]*)m|\x1B\][^\x07]*(?:\x07|\x1B\\))/u
|
|
996
|
+
.exec(composerLine.slice(index));
|
|
997
|
+
if (escape) {
|
|
998
|
+
if (escape[1] !== undefined) {
|
|
999
|
+
const codes = escape[1] === ""
|
|
1000
|
+
? [0]
|
|
1001
|
+
: escape[1].split(";").map((value) => Number(value));
|
|
1002
|
+
for (let codeIndex = 0; codeIndex < codes.length; codeIndex += 1) {
|
|
1003
|
+
const code = codes[codeIndex];
|
|
1004
|
+
if ([38, 48, 58].includes(code) &&
|
|
1005
|
+
codes[codeIndex + 1] === 2) {
|
|
1006
|
+
codeIndex += 4;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if ([38, 48, 58].includes(code) &&
|
|
1010
|
+
codes[codeIndex + 1] === 5) {
|
|
1011
|
+
codeIndex += 2;
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (code === 0 || code === 22) {
|
|
1015
|
+
dim = false;
|
|
1016
|
+
}
|
|
1017
|
+
else if (code === 2) {
|
|
1018
|
+
dim = true;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
index += escape[0].length;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
const codePoint = composerLine.codePointAt(index);
|
|
1027
|
+
if (codePoint === undefined) {
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
|
+
const character = String.fromCodePoint(codePoint);
|
|
1031
|
+
visible.push({ character, dim });
|
|
1032
|
+
index += character.length;
|
|
1033
|
+
}
|
|
1034
|
+
const promptIndex = visible.findIndex(({ character }) => character === "›" || character === "»");
|
|
1035
|
+
if (promptIndex < 0) {
|
|
1036
|
+
return undefined;
|
|
1037
|
+
}
|
|
1038
|
+
const content = visible.slice(promptIndex + 1)
|
|
1039
|
+
.filter(({ character }) => !/^\s$/u.test(character));
|
|
1040
|
+
if (content.length > 0 && !content.every((entry) => entry.dim)) {
|
|
1041
|
+
return undefined;
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
digest: createHash("sha256").update(composerLine).digest("hex")
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Infer a viewport only from fixed-width visible-buffer rows. Trimmed captures
|
|
1049
|
+
* deliberately return undefined: a short content row is not proof of a short
|
|
1050
|
+
* terminal. This keeps the fallback provider-neutral and fail-closed only on
|
|
1051
|
+
* positive geometry evidence.
|
|
1052
|
+
*/
|
|
1053
|
+
export function inferCodexVisibleViewportColumns(screen) {
|
|
1054
|
+
const rows = screen.replace(/\r\n?/gu, "\n").split("\n")
|
|
1055
|
+
.map(stripTerminalEscapeSequences);
|
|
1056
|
+
const widthOneRows = rows.filter((row) => /^[\x20-\x7e›»·─━╭╮╰╯│]*$/u.test(row));
|
|
1057
|
+
const maxWidth = widthOneRows.reduce((maximum, row) => Math.max(maximum, Array.from(row).length), 0);
|
|
1058
|
+
if (maxWidth < 20) {
|
|
1059
|
+
return undefined;
|
|
1060
|
+
}
|
|
1061
|
+
const paddedAtMax = widthOneRows.filter((row) => row.endsWith(" ") && Array.from(row).length === maxWidth);
|
|
1062
|
+
const composerAtMax = paddedAtMax.some((row) => CODEX_COMPOSER_MARKER.test(row.trimEnd()));
|
|
1063
|
+
return paddedAtMax.length >= 3 && composerAtMax
|
|
1064
|
+
? maxWidth
|
|
1065
|
+
: undefined;
|
|
1066
|
+
}
|
|
1067
|
+
function hasTruncatedCodexStatusSessionLine(screen) {
|
|
1068
|
+
return screen.replace(/\r\n?/gu, "\n").split("\n").some((line) => {
|
|
1069
|
+
const match = /^\s*│\s*Session:\s*([^│\s]+).*│?\s*$/iu.exec(line);
|
|
1070
|
+
if (!match) {
|
|
1071
|
+
return false;
|
|
1072
|
+
}
|
|
1073
|
+
return !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu
|
|
1074
|
+
.test(match[1]);
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
function codexNativeInspectionComposerMismatchDiagnostic(screen, plan) {
|
|
1078
|
+
const expectedRows = CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile];
|
|
1079
|
+
if (!expectedRows) {
|
|
1080
|
+
return "composer_not_exact";
|
|
1081
|
+
}
|
|
1082
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
1083
|
+
let composerIndex = -1;
|
|
1084
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1085
|
+
if (CODEX_COMPOSER_MARKER.test(lines[index])) {
|
|
1086
|
+
composerIndex = index;
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (composerIndex < 0 ||
|
|
1091
|
+
lines[composerIndex].replace(/^[›»]\s?/u, "").trimEnd() !== plan.command) {
|
|
1092
|
+
return "composer_not_exact";
|
|
1093
|
+
}
|
|
1094
|
+
const footerIndex = lines.findIndex((line, index) => index > composerIndex && CODEX_COMPOSER_FOOTER.test(line.trim()));
|
|
1095
|
+
const popupRows = lines.slice(composerIndex + 1, footerIndex < 0 ? lines.length : footerIndex).filter((line) => line.trim().length > 0);
|
|
1096
|
+
if (popupRows.length === 0) {
|
|
1097
|
+
return "composer_not_exact";
|
|
1098
|
+
}
|
|
1099
|
+
const logicalRows = [];
|
|
1100
|
+
let observedTruncation = false;
|
|
1101
|
+
for (const row of popupRows) {
|
|
1102
|
+
const trimmed = row.trim().replace(/\s+/gu, " ");
|
|
1103
|
+
if (trimmed.startsWith("/")) {
|
|
1104
|
+
logicalRows.push(trimmed);
|
|
1105
|
+
}
|
|
1106
|
+
else if (logicalRows.length > 0) {
|
|
1107
|
+
logicalRows[logicalRows.length - 1] += ` ${trimmed}`;
|
|
1108
|
+
observedTruncation = true;
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
return "composer_not_exact";
|
|
1112
|
+
}
|
|
1113
|
+
observedTruncation ||= trimmed.endsWith("…");
|
|
1114
|
+
}
|
|
1115
|
+
const normalizedExpected = expectedRows.map((row) => row.trim().replace(/\s+/gu, " "));
|
|
1116
|
+
const everyKnownPrefix = logicalRows.length <= normalizedExpected.length &&
|
|
1117
|
+
logicalRows.every((row, index) => {
|
|
1118
|
+
const withoutEllipsis = row.endsWith("…")
|
|
1119
|
+
? row.slice(0, -1).trimEnd()
|
|
1120
|
+
: row;
|
|
1121
|
+
return normalizedExpected[index]?.startsWith(withoutEllipsis) === true;
|
|
1122
|
+
});
|
|
1123
|
+
return observedTruncation && everyKnownPrefix
|
|
1124
|
+
? "composer_viewport_truncated"
|
|
1125
|
+
: "composer_not_exact";
|
|
1126
|
+
}
|
|
1127
|
+
//# sourceMappingURL=terminal-native-inspection-bridge.js.map
|