@scotthuang/agent-knock-knock 0.13.3 → 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 +14 -0
- package/README.md +13 -5
- 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/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/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 +2 -172
- 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/native-thread-lifecycle-cli-adapter.js +116 -60
- 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 -2660
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +2 -148
- package/dist/src/openclaw-plugin-helpers.js +2 -1844
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +2 -563
- package/dist/src/openclaw-plugin-schemas.js +2 -572
- 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 +2 -98
- package/dist/src/terminal-action-projection.js +13 -215
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +14 -340
- package/dist/src/terminal-agent-bridge.js +155 -3226
- 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-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 +241 -1946
- 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 +11 -11
- package/dist/src/terminal-list-renderer.js +168 -119
- 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 +37 -18
- package/dist/src/terminal-model-control.js +285 -194
- package/dist/src/terminal-model-control.js.map +1 -1
- 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 -2
- 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/package.json +7 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +1 -1
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { CLAUDE_INJECTED_PASTE_FRAME_PROFILE } from "./claude-injected-paste-proof.js";
|
|
3
|
+
import { isTerminalModelControlPlanForAgent } from "./terminal-model-control.js";
|
|
4
|
+
import { claudeNativeInspectionTrailingIsFooter, CODEX_COMPOSER_FOOTER, CODEX_COMPOSER_MARKER, exactCodexReadyStyledComposerCapture, exactClaudeComposerFrame, inferCodexVisibleViewportColumns, stripTerminalEscapeSequences } from "./terminal-native-inspection-bridge.js";
|
|
5
|
+
const CODEX_COMPLETE_COMPOSER_FOOTER = /^(?:gpt-[\w.-]+(?:\s+\S+)?|[-\w.]+ default)\s+·\s+\S.*$/u;
|
|
6
|
+
const CODEX_LARGE_PASTE_CHAR_THRESHOLD = 1_000;
|
|
7
|
+
function exactClaudeModelControlComposerCapture(screen, plan, expectedText) {
|
|
8
|
+
if (!isTerminalModelControlPlanForAgent(plan, "claude") ||
|
|
9
|
+
expectedText !== plan.command ||
|
|
10
|
+
expectedText !== "/model")
|
|
11
|
+
return undefined;
|
|
12
|
+
const frame = exactClaudeComposerFrame(screen);
|
|
13
|
+
if (!frame ||
|
|
14
|
+
frame.composerRows.length !== 1 ||
|
|
15
|
+
frame.composerRows[0].replace(/^\s*❯\s?/u, "").trimEnd() !== expectedText ||
|
|
16
|
+
frame.trailing.length > 2 ||
|
|
17
|
+
!claudeNativeInspectionTrailingIsFooter(frame.trailing))
|
|
18
|
+
return undefined;
|
|
19
|
+
const beforeComposer = frame.lines.slice(0, frame.openIndex);
|
|
20
|
+
while (beforeComposer.length > 0 &&
|
|
21
|
+
beforeComposer.at(-1)?.trim() === "") {
|
|
22
|
+
beforeComposer.pop();
|
|
23
|
+
}
|
|
24
|
+
let popupStart = beforeComposer.length;
|
|
25
|
+
while (popupStart > 0) {
|
|
26
|
+
const line = beforeComposer[popupStart - 1];
|
|
27
|
+
if (/^\s*(?:(?:❯|›)\s*)?\/[a-z][a-z0-9-]*(?:\s{2,}|\s*$)/iu.test(line) ||
|
|
28
|
+
/^\s{2,}\S/u.test(line)) {
|
|
29
|
+
popupStart -= 1;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const popupRows = beforeComposer.slice(popupStart);
|
|
35
|
+
if (popupRows.length === 0 || popupRows.length > 32)
|
|
36
|
+
return undefined;
|
|
37
|
+
const suggestions = [];
|
|
38
|
+
for (const [lineIndex, line] of popupRows.entries()) {
|
|
39
|
+
const match = /^\s*(?:(❯|›)\s*)?(\/[a-z][a-z0-9-]*)(?:\s{2,}|\s*$)(.*)$/iu
|
|
40
|
+
.exec(line);
|
|
41
|
+
if (match) {
|
|
42
|
+
suggestions.push({
|
|
43
|
+
lineIndex,
|
|
44
|
+
selected: Boolean(match[1]),
|
|
45
|
+
command: match[2],
|
|
46
|
+
normalized: `${match[2]} ${match[3]}`.trim().replace(/\s+/gu, " ")
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (suggestions.length === 0 || !/^\s{2,}\S/u.test(line)) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const previous = suggestions.at(-1);
|
|
54
|
+
previous.normalized = `${previous.normalized} ${line.trim()}`
|
|
55
|
+
.replace(/\s+/gu, " ");
|
|
56
|
+
}
|
|
57
|
+
if (suggestions.length === 0 || suggestions[0].lineIndex !== 0) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const selected = suggestions.filter((row) => row.selected);
|
|
61
|
+
if (selected.length > 1 ||
|
|
62
|
+
selected.length === 1 && selected[0].lineIndex !== 0 ||
|
|
63
|
+
suggestions.slice(1).some((row) => row.command === "/model"))
|
|
64
|
+
return undefined;
|
|
65
|
+
const first = suggestions[0].normalized;
|
|
66
|
+
if (!/^\/model Set the AI model for Claude Code \(currently [A-Za-z0-9][A-Za-z0-9._:/+\[\]\- ]{0,127}\)$/u.test(first)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
digest: createHash("sha256")
|
|
71
|
+
.update(frame.lines.slice(frame.openIndex).join("\n"))
|
|
72
|
+
.digest("hex")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function codexBlockingModalVisible(screen) {
|
|
76
|
+
const tail = screen.replace(/\r\n?/gu, "\n").split("\n").slice(-80)
|
|
77
|
+
.join("\n");
|
|
78
|
+
return /\b(?:press|use)\s+(?:esc|escape)\s+to\s+(?:cancel|close|dismiss)\b/iu
|
|
79
|
+
.test(tail) ||
|
|
80
|
+
/\besc\s+to\s+cancel\b/iu.test(tail) ||
|
|
81
|
+
codexActiveWriterViewerVisible(tail) ||
|
|
82
|
+
["expanded", "ambiguous"].includes(inspectCodexAsyncQuestionInputMode(tail));
|
|
83
|
+
}
|
|
84
|
+
function codexActiveWriterViewerVisible(styledScreen) {
|
|
85
|
+
const lines = stripTerminalEscapeSequences(styledScreen)
|
|
86
|
+
.replace(/\r\n?/gu, "\n")
|
|
87
|
+
.split("\n")
|
|
88
|
+
.slice(-80);
|
|
89
|
+
let titleIndex = -1;
|
|
90
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
91
|
+
if (/^ {2}🔒(?:\s|$)/u.test(lines[index])) {
|
|
92
|
+
titleIndex = index;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (titleIndex < 0) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const laterMainComposer = lines.findIndex((line, index) => index > titleIndex && CODEX_COMPOSER_MARKER.test(line));
|
|
100
|
+
if (laterMainComposer >= 0) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const frame = lines.slice(titleIndex, titleIndex + 12)
|
|
104
|
+
.map((line) => line.trim().replace(/\s+/gu, " "))
|
|
105
|
+
.filter((line) => line.length > 0)
|
|
106
|
+
.join(" ");
|
|
107
|
+
return /🔒\s+This conversation is open in another app\s+\S+ to Retry/iu
|
|
108
|
+
.test(frame) &&
|
|
109
|
+
/Close it there and press\s+\S+\s+to continue here\./iu.test(frame) &&
|
|
110
|
+
/\S+ retry\s+\S+ exit(?:\s+\S+ transcript)?/iu.test(frame);
|
|
111
|
+
}
|
|
112
|
+
const CODEX_ASYNC_QUESTION_FOOTER_LABEL = /\s(submit|skip|main prompt|prev question|next question|queued messages)(?=\s{2,}|\s*$)/giu;
|
|
113
|
+
function codexStructuredAsyncFooterEvidence(lines, evidenceFloor) {
|
|
114
|
+
let strongest = { index: -1, labelCount: 0 };
|
|
115
|
+
for (let start = evidenceFloor + 1; start < lines.length; start += 1) {
|
|
116
|
+
const labels = new Set();
|
|
117
|
+
for (let end = start; end < Math.min(lines.length, start + 5); end += 1) {
|
|
118
|
+
const line = lines[end];
|
|
119
|
+
if (!/^ {2}(?![ ↳?])\S/u.test(line)) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
for (const match of line.matchAll(CODEX_ASYNC_QUESTION_FOOTER_LABEL)) {
|
|
123
|
+
labels.add(match[1].toLowerCase());
|
|
124
|
+
}
|
|
125
|
+
if (labels.size > strongest.labelCount) {
|
|
126
|
+
strongest = { index: end, labelCount: labels.size };
|
|
127
|
+
}
|
|
128
|
+
if (labels.size >= 2) {
|
|
129
|
+
return strongest;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return strongest;
|
|
134
|
+
}
|
|
135
|
+
function codexMainComposerFooterIndex(lines, composerIndex) {
|
|
136
|
+
for (let index = lines.length - 1; index > composerIndex; index -= 1) {
|
|
137
|
+
// A real statusline starts at column zero. Draft continuations are inset,
|
|
138
|
+
// even when their text happens to look exactly like a model statusline.
|
|
139
|
+
if (CODEX_COMPLETE_COMPOSER_FOOTER.test(lines[index].trimEnd())) {
|
|
140
|
+
return index;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return -1;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Classify Codex 0.154's non-blocking inline-question surface by current input
|
|
147
|
+
* ownership. The shared `Queued follow-up inputs` heading is not sufficient:
|
|
148
|
+
* ordinary queued messages use it too. An exact collapsed summary, after any
|
|
149
|
+
* ordinary queue preview is removed, proves Send-safe ownership even when the
|
|
150
|
+
* Composer is outside the viewport. Positive editor evidence without a
|
|
151
|
+
* complete shape remains fail-closed because paste plus Enter would otherwise
|
|
152
|
+
* submit the user's task as an answer.
|
|
153
|
+
*/
|
|
154
|
+
export function inspectCodexAsyncQuestionInputMode(styledScreen) {
|
|
155
|
+
const lines = stripTerminalEscapeSequences(styledScreen)
|
|
156
|
+
.replace(/\r\n?/gu, "\n")
|
|
157
|
+
.replace(/\u00a0/gu, " ")
|
|
158
|
+
.split("\n")
|
|
159
|
+
.slice(-80);
|
|
160
|
+
while (lines.length > 0 && lines.at(-1)?.trim().length === 0) {
|
|
161
|
+
lines.pop();
|
|
162
|
+
}
|
|
163
|
+
const normalized = lines.map((line) => line.trim().replace(/\s+/gu, " "));
|
|
164
|
+
let mainComposerIndex = -1;
|
|
165
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
166
|
+
// The main Composer marker is column zero. Async option and inline-text
|
|
167
|
+
// markers are inset by the menu surface, even after ANSI is removed.
|
|
168
|
+
if (CODEX_COMPOSER_MARKER.test(lines[index])) {
|
|
169
|
+
mainComposerIndex = index;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let headerIndex = -1;
|
|
174
|
+
let wrappedHeaderRows = 1;
|
|
175
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
176
|
+
if (/^•\s+Queued follow-up inputs\s*$/u.test(lines[index]) ||
|
|
177
|
+
/^•\s+Queued follow-up inpu…\s*$/u.test(lines[index]) ||
|
|
178
|
+
(/^•\s+Queued follow-up\s*$/u.test(lines[index]) &&
|
|
179
|
+
/^ {2}inputs\s*$/u.test(lines[index + 1] ?? ""))) {
|
|
180
|
+
headerIndex = index;
|
|
181
|
+
wrappedHeaderRows = /^•\s+Queued follow-up\s*$/u.test(lines[index])
|
|
182
|
+
? 2
|
|
183
|
+
: 1;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const headerBodyIndex = headerIndex < 0
|
|
188
|
+
? -1
|
|
189
|
+
: headerIndex + wrappedHeaderRows;
|
|
190
|
+
let questionEvidenceStart = headerBodyIndex;
|
|
191
|
+
if (headerIndex >= 0) {
|
|
192
|
+
let cursor = headerBodyIndex;
|
|
193
|
+
while (cursor < lines.length && lines[cursor].trim().length === 0) {
|
|
194
|
+
cursor += 1;
|
|
195
|
+
}
|
|
196
|
+
let sawQueuedPreview = false;
|
|
197
|
+
while (cursor < lines.length) {
|
|
198
|
+
if (/^ {2}↳\s/u.test(lines[cursor])) {
|
|
199
|
+
sawQueuedPreview = true;
|
|
200
|
+
cursor += 1;
|
|
201
|
+
while (cursor < lines.length) {
|
|
202
|
+
if (lines[cursor].trim().length === 0 ||
|
|
203
|
+
/^ {4,}\S/u.test(lines[cursor])) {
|
|
204
|
+
cursor += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
if (sawQueuedPreview) {
|
|
214
|
+
questionEvidenceStart = cursor;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const mainComposerFooterIndex = mainComposerIndex < 0
|
|
218
|
+
? -1
|
|
219
|
+
: codexMainComposerFooterIndex(lines, mainComposerIndex);
|
|
220
|
+
const summaryBeforeMainComposer = headerIndex < 0 || mainComposerIndex < 0
|
|
221
|
+
? -1
|
|
222
|
+
: normalized.findIndex((line, index) => index >= questionEvidenceStart && index < mainComposerIndex &&
|
|
223
|
+
/^\?\s+\d+\s+questions?(?:\s+·\s+\d+s)?$/iu.test(line));
|
|
224
|
+
if (mainComposerIndex >= 0 && mainComposerFooterIndex < 0) {
|
|
225
|
+
// A statusline-disabled Composer is the strongest available ownership
|
|
226
|
+
// anchor. Treat all following wrapped rows as its arbitrary draft instead
|
|
227
|
+
// of letting question-like user text veto a human-priority Send.
|
|
228
|
+
return summaryBeforeMainComposer >= 0 ? "collapsed" : "absent";
|
|
229
|
+
}
|
|
230
|
+
const evidenceFloor = Math.max(mainComposerFooterIndex, questionEvidenceStart - 1);
|
|
231
|
+
const structuredFooter = codexStructuredAsyncFooterEvidence(lines, evidenceFloor);
|
|
232
|
+
if (structuredFooter.labelCount >= 2) {
|
|
233
|
+
return "expanded";
|
|
234
|
+
}
|
|
235
|
+
const clippedFooterIndex = normalized.findIndex((line, index) => index > evidenceFloor && /^ente(?:r)?…$/iu.test(line) &&
|
|
236
|
+
/^ctrl…$/iu.test(normalized[index + 1] ?? ""));
|
|
237
|
+
const clippedChoiceIndex = normalized.findIndex((line, index) => index > evidenceFloor &&
|
|
238
|
+
line === "Expand terminal to read the entire option");
|
|
239
|
+
if (clippedFooterIndex >= 0 || clippedChoiceIndex >= 0) {
|
|
240
|
+
return "ambiguous";
|
|
241
|
+
}
|
|
242
|
+
if (headerIndex < 0) {
|
|
243
|
+
const evidence = new Set();
|
|
244
|
+
normalized.forEach((line, index) => {
|
|
245
|
+
if (index <= evidenceFloor) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (/^\d+\s+of(?:\s+\d+)?$/iu.test(line)) {
|
|
249
|
+
evidence.add("progress");
|
|
250
|
+
}
|
|
251
|
+
if (/^›\s+\S/u.test(line)) {
|
|
252
|
+
evidence.add("selector");
|
|
253
|
+
}
|
|
254
|
+
if (/^Type your answer(?:\s*\(optional\))?$/iu.test(line)) {
|
|
255
|
+
evidence.add("free_text");
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
return evidence.size >= 2 ||
|
|
259
|
+
(evidence.size >= 1 && structuredFooter.labelCount >= 1)
|
|
260
|
+
? "ambiguous"
|
|
261
|
+
: "absent";
|
|
262
|
+
}
|
|
263
|
+
const weakQuestionIndex = normalized.findIndex((line, index) => index > evidenceFloor && index >= questionEvidenceStart && (/^\d+\s+of(?:\s+\d+)?$/iu.test(line) ||
|
|
264
|
+
/^›\s+\S/u.test(line) ||
|
|
265
|
+
/^Type your answer(?:\s*\(optional\))?$/iu.test(line)));
|
|
266
|
+
if (mainComposerIndex > headerIndex && weakQuestionIndex > evidenceFloor) {
|
|
267
|
+
return "ambiguous";
|
|
268
|
+
}
|
|
269
|
+
const summaryIndex = normalized.findIndex((line, index) => index >= questionEvidenceStart &&
|
|
270
|
+
(mainComposerIndex < 0 || index < mainComposerIndex) &&
|
|
271
|
+
/^\?\s+\d+\s+questions?(?:\s+·\s+\d+s)?$/iu.test(line));
|
|
272
|
+
if (summaryIndex >= 0) {
|
|
273
|
+
return weakQuestionIndex >= 0 ? "ambiguous" : "collapsed";
|
|
274
|
+
}
|
|
275
|
+
if (mainComposerIndex > headerIndex) {
|
|
276
|
+
return "absent";
|
|
277
|
+
}
|
|
278
|
+
const afterHeader = normalized.slice(questionEvidenceStart)
|
|
279
|
+
.filter((line) => line.length > 0);
|
|
280
|
+
const positiveAfterHeader = afterHeader.some((line) => /^\d+\s+of(?:\s+\d+)?$/iu.test(line) ||
|
|
281
|
+
/^›\s+\d+\.(?:\s|$)/u.test(line) ||
|
|
282
|
+
/^Type your answer(?:\s*\(optional\))?$/iu.test(line));
|
|
283
|
+
if (positiveAfterHeader) {
|
|
284
|
+
return "ambiguous";
|
|
285
|
+
}
|
|
286
|
+
// A bare heading (optionally followed by ↳ queued-message previews) is not
|
|
287
|
+
// question authority. Preserve human-explicit Send in that ordinary state.
|
|
288
|
+
if (afterHeader.length === 0) {
|
|
289
|
+
return "absent";
|
|
290
|
+
}
|
|
291
|
+
// An expanded editor can temporarily replace its footer with a flash. If
|
|
292
|
+
// there is question-like content under the shared heading and no proven main
|
|
293
|
+
// Composer, do not guess which input surface will receive the next paste.
|
|
294
|
+
return "ambiguous";
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Classify only the bottom live Codex composer region. A matching transcript
|
|
298
|
+
* prompt elsewhere in scrollback is deliberately ignored.
|
|
299
|
+
*/
|
|
300
|
+
function currentCodexComposerCapture(styledScreen, expectedText, allowOpaqueLargePastePlaceholder = false, classifyOpaqueLargePasteAsDifferent = false, exactSlashPopupRows) {
|
|
301
|
+
const screen = stripTerminalEscapeSequences(styledScreen);
|
|
302
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
303
|
+
while (lines.length > 0 && lines.at(-1)?.trim().length === 0) {
|
|
304
|
+
lines.pop();
|
|
305
|
+
}
|
|
306
|
+
let composerIndex = -1;
|
|
307
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
308
|
+
if (CODEX_COMPOSER_MARKER.test(lines[index])) {
|
|
309
|
+
composerIndex = index;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (composerIndex < 0) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
const footerIndex = lines.findIndex((line, index) => index > composerIndex &&
|
|
317
|
+
CODEX_COMPLETE_COMPOSER_FOOTER.test(line.trim()));
|
|
318
|
+
if (footerIndex >= 0 &&
|
|
319
|
+
lines.slice(footerIndex + 1).some((line) => line.trim().length > 0)) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
const regionEnd = footerIndex < 0 ? lines.length : footerIndex;
|
|
323
|
+
const region = lines.slice(composerIndex, regionEnd);
|
|
324
|
+
while (region.length > 1 && region.at(-1)?.trim().length === 0) {
|
|
325
|
+
region.pop();
|
|
326
|
+
}
|
|
327
|
+
if (region.length === 0 ||
|
|
328
|
+
region.slice(1).some((line) => line.length > 0 && !line.startsWith(" "))) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
const bodyRows = [
|
|
332
|
+
region[0].replace(/^[›»]\s?/u, ""),
|
|
333
|
+
...region.slice(1).map((line) => line.startsWith(" ") ? line.slice(2) : line)
|
|
334
|
+
];
|
|
335
|
+
const digest = createHash("sha256").update(region.join("\n")).digest("hex");
|
|
336
|
+
const positivelyEmpty = footerIndex >= 0 &&
|
|
337
|
+
bodyRows.slice(1).every((row) => row.trim().length === 0) &&
|
|
338
|
+
exactCodexReadyStyledComposerCapture(styledScreen) !== undefined;
|
|
339
|
+
if (positivelyEmpty) {
|
|
340
|
+
return { state: "exact_empty", digest };
|
|
341
|
+
}
|
|
342
|
+
const expectedComparable = composerComparableText(expectedText);
|
|
343
|
+
const exactStyledProfiledCommand = exactSlashPopupRows !== undefined &&
|
|
344
|
+
exactCodexStyledCommandComposerCapture(styledScreen, expectedText) !== undefined;
|
|
345
|
+
const exactFooterlessProfiledCommand = footerIndex < 0 &&
|
|
346
|
+
exactStyledProfiledCommand;
|
|
347
|
+
// Codex 0.154 replaces the ordinary model/cwd footer with its slash
|
|
348
|
+
// completion surface. Real terminal renderers may retain one or more blank
|
|
349
|
+
// layout rows between the Composer and that surface. The exact styled
|
|
350
|
+
// command is sufficient only for reversible cleanup; Enter additionally
|
|
351
|
+
// requires the unique, ordered, version-profiled completion rows below.
|
|
352
|
+
const popupRows = region.slice(1).map((row) => row.trimEnd());
|
|
353
|
+
const exactPopupLayout = exactSlashPopupRows !== undefined && (JSON.stringify(popupRows) === JSON.stringify(exactSlashPopupRows) ||
|
|
354
|
+
popupRows.length === exactSlashPopupRows.length + 1 &&
|
|
355
|
+
popupRows[0] === "" &&
|
|
356
|
+
JSON.stringify(popupRows.slice(1)) ===
|
|
357
|
+
JSON.stringify(exactSlashPopupRows));
|
|
358
|
+
const exactProfiledSlashPopup = exactSlashPopupRows !== undefined &&
|
|
359
|
+
composerComparableText(bodyRows[0] ?? "").trimEnd() ===
|
|
360
|
+
expectedComparable &&
|
|
361
|
+
exactPopupLayout;
|
|
362
|
+
if (footerIndex < 0 && !exactFooterlessProfiledCommand) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
const expectedCharacterCount = Array.from(expectedText).length;
|
|
366
|
+
const comparable = composerComparableText(bodyRows.join("\n"));
|
|
367
|
+
const opaqueLargePastePlaceholder = /^\[Pasted Content \d+ chars\]$/u.test(comparable);
|
|
368
|
+
if (opaqueLargePastePlaceholder && !allowOpaqueLargePastePlaceholder) {
|
|
369
|
+
return classifyOpaqueLargePasteAsDifferent
|
|
370
|
+
? { state: "different_draft", digest }
|
|
371
|
+
: undefined;
|
|
372
|
+
}
|
|
373
|
+
// Herdr's ANSI visible buffer preserves Codex's fixed-width Composer paint:
|
|
374
|
+
// the typed command is followed by layout padding through the viewport edge.
|
|
375
|
+
// Accept that padding only when the closed 0.154 profile, full-row
|
|
376
|
+
// background paint, exact command text, and complete model/cwd footer all
|
|
377
|
+
// agree. Plain padded text remains untrusted.
|
|
378
|
+
const exactVisibleDraft = footerIndex >= 0 && (terminalComposerRowsMatchExpected(bodyRows, expectedComparable) ||
|
|
379
|
+
exactStyledProfiledCommand);
|
|
380
|
+
const exactLargePastePlaceholder = allowOpaqueLargePastePlaceholder &&
|
|
381
|
+
expectedCharacterCount > CODEX_LARGE_PASTE_CHAR_THRESHOLD &&
|
|
382
|
+
comparable === composerComparableText(`[Pasted Content ${expectedCharacterCount} chars]`);
|
|
383
|
+
if (exactVisibleDraft ||
|
|
384
|
+
exactLargePastePlaceholder ||
|
|
385
|
+
exactProfiledSlashPopup ||
|
|
386
|
+
exactFooterlessProfiledCommand) {
|
|
387
|
+
return {
|
|
388
|
+
state: "exact_draft",
|
|
389
|
+
digest,
|
|
390
|
+
...(exactProfiledSlashPopup ? { profiledSlashPopup: true } : {}),
|
|
391
|
+
...(exactVisibleDraft && footerIndex >= 0
|
|
392
|
+
? { bareCommand: true }
|
|
393
|
+
: {})
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return comparable.length > 0
|
|
397
|
+
? { state: "different_draft", digest }
|
|
398
|
+
: undefined;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Prove the live footerless Codex slash Composer without trusting transcript
|
|
402
|
+
* text alone. The 0.154 TUI paints this row across the current viewport while
|
|
403
|
+
* its completion popup replaces the ordinary model/cwd footer.
|
|
404
|
+
*/
|
|
405
|
+
function exactCodexStyledCommandComposerCapture(styledScreen, expectedText) {
|
|
406
|
+
const rows = styledScreen.replace(/\r\n?/gu, "\n").split("\n");
|
|
407
|
+
const viewportColumns = inferCodexVisibleViewportColumns(styledScreen);
|
|
408
|
+
if (!viewportColumns)
|
|
409
|
+
return undefined;
|
|
410
|
+
let composerLine;
|
|
411
|
+
for (let index = rows.length - 1; index >= 0; index -= 1) {
|
|
412
|
+
const plain = stripTerminalEscapeSequences(rows[index]);
|
|
413
|
+
if (CODEX_COMPOSER_MARKER.test(plain)) {
|
|
414
|
+
composerLine = rows[index];
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (!composerLine)
|
|
419
|
+
return undefined;
|
|
420
|
+
const plain = stripTerminalEscapeSequences(composerLine);
|
|
421
|
+
const exactCommand = plain.trimEnd().replace(/^[›»]\s?/u, "") ===
|
|
422
|
+
expectedText;
|
|
423
|
+
const spansViewport = Array.from(plain).length === viewportColumns &&
|
|
424
|
+
plain.endsWith(" ");
|
|
425
|
+
const hasBackground = [...composerLine.matchAll(/\x1b\[([0-9;]*)m/gu)]
|
|
426
|
+
.some((match) => match[1].split(";").map(Number).includes(48));
|
|
427
|
+
if (!exactCommand || !spansViewport || !hasBackground) {
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
digest: createHash("sha256").update(composerLine).digest("hex")
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function exactCodexComposerCapture(screen, expectedText) {
|
|
435
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
436
|
+
const expectedComparable = composerComparableText(expectedText);
|
|
437
|
+
const expectedCharacterCount = Array.from(expectedText).length;
|
|
438
|
+
const largePasteComparable = composerComparableText(`[Pasted Content ${expectedCharacterCount} chars]`);
|
|
439
|
+
const matches = [];
|
|
440
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
441
|
+
if (!CODEX_COMPOSER_MARKER.test(lines[index])) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const footerIndex = lines.findIndex((line, candidateIndex) => candidateIndex > index && CODEX_COMPOSER_FOOTER.test(line.trim()));
|
|
445
|
+
const region = lines.slice(index, footerIndex < 0 ? lines.length : footerIndex);
|
|
446
|
+
while (region.length > 1 && region.at(-1)?.trim() === "") {
|
|
447
|
+
region.pop();
|
|
448
|
+
}
|
|
449
|
+
const bodyRows = [
|
|
450
|
+
region[0].replace(/^[›»]\s?/u, ""),
|
|
451
|
+
...region.slice(1).map((line) => line.startsWith(" ") ? line.slice(2) : line)
|
|
452
|
+
];
|
|
453
|
+
const comparable = composerComparableText(bodyRows.join("\n"));
|
|
454
|
+
const exactVisibleDraft = terminalComposerRowsMatchExpected(bodyRows, expectedComparable);
|
|
455
|
+
const exactLargePastePlaceholder = expectedCharacterCount > CODEX_LARGE_PASTE_CHAR_THRESHOLD &&
|
|
456
|
+
comparable === largePasteComparable;
|
|
457
|
+
if (!exactVisibleDraft && !exactLargePastePlaceholder) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
matches.push({
|
|
461
|
+
digest: createHash("sha256")
|
|
462
|
+
.update(region.join("\n"))
|
|
463
|
+
.digest("hex")
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
if (matches.length > 1) {
|
|
467
|
+
throw new Error("multiple Codex composer regions matched the multiline terminal request");
|
|
468
|
+
}
|
|
469
|
+
return matches[0];
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Terminal UIs paint wrapped composer content as independent screen rows, so
|
|
473
|
+
* a provider capture cannot distinguish a visual wrap from an authored
|
|
474
|
+
* newline. Align the rows against the exact text AKK injected instead of
|
|
475
|
+
* joining every row with `\n`.
|
|
476
|
+
*
|
|
477
|
+
* Only row boundaries are ambiguous: they may consume an authored newline, an
|
|
478
|
+
* omitted run of ASCII spaces at a word wrap, or no character at a CJK/token
|
|
479
|
+
* wrap. Every visible character remains character-for-character exact, and an
|
|
480
|
+
* empty row can only advance through an authored newline (plus terminal-trimmed
|
|
481
|
+
* spaces before it), so blank-line structure is preserved.
|
|
482
|
+
*/
|
|
483
|
+
function terminalComposerRowsMatchExpected(rows, expectedText) {
|
|
484
|
+
const expected = composerComparableText(expectedText);
|
|
485
|
+
if (rows.length === 0) {
|
|
486
|
+
return expected.length === 0;
|
|
487
|
+
}
|
|
488
|
+
if (!expected.startsWith(rows[0])) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
let offsets = new Set([rows[0].length]);
|
|
492
|
+
for (let index = 1; index < rows.length && offsets.size > 0; index += 1) {
|
|
493
|
+
const row = rows[index];
|
|
494
|
+
const previousRow = rows[index - 1];
|
|
495
|
+
const nextOffsets = new Set();
|
|
496
|
+
for (const offset of offsets) {
|
|
497
|
+
const candidateStarts = new Set();
|
|
498
|
+
if (expected[offset] === "\n") {
|
|
499
|
+
candidateStarts.add(offset + 1);
|
|
500
|
+
}
|
|
501
|
+
let whitespaceEnd = offset;
|
|
502
|
+
while (expected[whitespaceEnd] === " ") {
|
|
503
|
+
whitespaceEnd += 1;
|
|
504
|
+
}
|
|
505
|
+
if (previousRow.length > 0 && row.length > 0) {
|
|
506
|
+
candidateStarts.add(offset);
|
|
507
|
+
if (whitespaceEnd > offset) {
|
|
508
|
+
candidateStarts.add(whitespaceEnd);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (whitespaceEnd > offset &&
|
|
512
|
+
expected[whitespaceEnd] === "\n") {
|
|
513
|
+
candidateStarts.add(whitespaceEnd + 1);
|
|
514
|
+
}
|
|
515
|
+
for (const candidateStart of candidateStarts) {
|
|
516
|
+
if (expected.startsWith(row, candidateStart)) {
|
|
517
|
+
nextOffsets.add(candidateStart + row.length);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
offsets = nextOffsets;
|
|
522
|
+
}
|
|
523
|
+
return offsets.has(expected.length);
|
|
524
|
+
}
|
|
525
|
+
function exactTerminalComposerCapture(agent, screen, expectedText) {
|
|
526
|
+
return agent === "codex"
|
|
527
|
+
? exactCodexComposerCapture(screen, expectedText)
|
|
528
|
+
: exactClaudeComposerCapture(screen, expectedText);
|
|
529
|
+
}
|
|
530
|
+
function exactClaudeComposerCapture(screen, expectedText) {
|
|
531
|
+
const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
|
|
532
|
+
const dividerIndexes = lines
|
|
533
|
+
.map((line, index) => /^\s*[─━]{8,}\s*$/u.test(line) ? index : -1)
|
|
534
|
+
.filter((index) => index >= 0);
|
|
535
|
+
if (dividerIndexes.length < 2) {
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
const closeIndex = dividerIndexes.at(-1);
|
|
539
|
+
const openIndex = dividerIndexes.at(-2);
|
|
540
|
+
const trailing = lines.slice(closeIndex + 1)
|
|
541
|
+
.filter((line) => line.trim().length > 0);
|
|
542
|
+
if (trailing.length > 2 ||
|
|
543
|
+
trailing.some((line) => !/^\s*(?:[⏵⏴⏸]{1,2}|\?)\s*.*(?:manual mode|shift\+tab|accept edits|bypass permissions|for shortcuts|← for agents)/iu
|
|
544
|
+
.test(line))) {
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
const region = lines.slice(openIndex + 1, closeIndex);
|
|
548
|
+
while (region.length > 1 && region.at(-1)?.trim() === "") {
|
|
549
|
+
region.pop();
|
|
550
|
+
}
|
|
551
|
+
if (region.length === 0 || !/^\s*❯(?:\s|$)/u.test(region[0])) {
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
const bodyRows = [
|
|
555
|
+
region[0].replace(/^\s*❯\s?/u, ""),
|
|
556
|
+
...region.slice(1).map((line) => line.startsWith(" ") ? line.slice(2) : line)
|
|
557
|
+
];
|
|
558
|
+
if (!terminalComposerRowsMatchExpected(bodyRows, expectedText)) {
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
digest: createHash("sha256")
|
|
563
|
+
.update(lines.slice(openIndex, closeIndex + 1).join("\n"))
|
|
564
|
+
.digest("hex")
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function exactClaudeInjectedPastePlaceholderCapture(screen, expectedText) {
|
|
568
|
+
const expectedNewlineCount = (composerComparableText(expectedText).match(/\n/gu) ?? []).length;
|
|
569
|
+
if (expectedNewlineCount === 0) {
|
|
570
|
+
return undefined;
|
|
571
|
+
}
|
|
572
|
+
const frame = exactClaudeComposerFrame(screen);
|
|
573
|
+
if (!frame ||
|
|
574
|
+
frame.composerRows.length !== 1 ||
|
|
575
|
+
!claudeInjectedPastePlaceholderTrailingMatches(frame.trailing)) {
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
const placeholder = /^\s*❯\s*\[Pasted text #([1-9]\d*) \+([1-9]\d*) lines\]\s*$/u
|
|
579
|
+
.exec(frame.composerRows[0]);
|
|
580
|
+
if (!placeholder) {
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
const pasteId = Number(placeholder[1]);
|
|
584
|
+
const newlineCount = Number(placeholder[2]);
|
|
585
|
+
if (!Number.isSafeInteger(pasteId) ||
|
|
586
|
+
!Number.isSafeInteger(newlineCount) ||
|
|
587
|
+
newlineCount !== expectedNewlineCount) {
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
state: "exact_injected_paste_placeholder",
|
|
592
|
+
digest: createHash("sha256")
|
|
593
|
+
.update(frame.lines
|
|
594
|
+
.slice(frame.openIndex, frame.closeIndex + 1)
|
|
595
|
+
.concat(frame.trailing)
|
|
596
|
+
.join("\n"))
|
|
597
|
+
.digest("hex"),
|
|
598
|
+
pasteId,
|
|
599
|
+
newlineCount,
|
|
600
|
+
frameProfile: CLAUDE_INJECTED_PASTE_FRAME_PROFILE
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function claudeInjectedPastePlaceholderTrailingMatches(lines) {
|
|
604
|
+
if (lines.length === 0) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
const hint = "paste again to expand";
|
|
608
|
+
const first = lines[0].trimStart();
|
|
609
|
+
if (!first.startsWith(hint)) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
const sameRowStatus = first.slice(hint.length);
|
|
613
|
+
if (sameRowStatus.length > 0 && !/^\s{2,}\S/u.test(sameRowStatus)) {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
const footerRows = [
|
|
617
|
+
...(sameRowStatus.trim().length > 0 ? [sameRowStatus.trim()] : []),
|
|
618
|
+
...lines.slice(1).map((line) => line.trim())
|
|
619
|
+
];
|
|
620
|
+
return footerRows.length === 0 ||
|
|
621
|
+
claudeNativeInspectionTrailingIsFooter(footerRows) ||
|
|
622
|
+
claudeInjectedPasteAuxiliaryFooterMatches(footerRows);
|
|
623
|
+
}
|
|
624
|
+
function claudeInjectedPasteAuxiliaryFooterMatches(lines) {
|
|
625
|
+
if (lines.length === 0 || lines.length > 2) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
const autoUpdate = lines.filter((line) => line === "✘ Auto-update failed · Run claude doctor").length;
|
|
629
|
+
const effort = lines.filter((line) => /^[●○◐◉] (?:low|medium|high|xhigh|max|ultracode) · \/effort$/u
|
|
630
|
+
.test(line)).length;
|
|
631
|
+
return autoUpdate <= 1 && effort <= 1 &&
|
|
632
|
+
autoUpdate + effort === lines.length;
|
|
633
|
+
}
|
|
634
|
+
function composerComparableText(value) {
|
|
635
|
+
return value.replace(/\r\n?/gu, "\n");
|
|
636
|
+
}
|
|
637
|
+
export { codexActiveWriterViewerVisible, codexBlockingModalVisible, currentCodexComposerCapture, exactClaudeComposerCapture, exactClaudeInjectedPastePlaceholderCapture, exactClaudeModelControlComposerCapture, exactTerminalComposerCapture, terminalComposerRowsMatchExpected };
|
|
638
|
+
//# sourceMappingURL=terminal-composer-classifier.js.map
|