@scotthuang/agent-knock-knock 0.13.0 → 0.13.2

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/src/claude-lifecycle-compatibility.js +49 -2
  3. package/dist/src/claude-lifecycle-compatibility.js.map +1 -1
  4. package/dist/src/claude-terminal-agent-adapter.d.ts +1 -1
  5. package/dist/src/claude-terminal-agent-adapter.js +2 -2
  6. package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
  7. package/dist/src/codex-lifecycle-compatibility.js +2 -1
  8. package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
  9. package/dist/src/native-thread-transition-verification-adapter.js +1 -0
  10. package/dist/src/native-thread-transition-verification-adapter.js.map +1 -1
  11. package/dist/src/openclaw-plugin-command-adapter.js +3 -3
  12. package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
  13. package/dist/src/openclaw-plugin-schemas.js +1 -1
  14. package/dist/src/openclaw-plugin-schemas.js.map +1 -1
  15. package/dist/src/terminal-agent-bridge.d.ts +11 -0
  16. package/dist/src/terminal-agent-bridge.js +248 -2
  17. package/dist/src/terminal-agent-bridge.js.map +1 -1
  18. package/dist/src/terminal-dispatch-recovery-cli-adapter.js +63 -1
  19. package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
  20. package/dist/src/terminal-dispatch-recovery-service.d.ts +1 -0
  21. package/dist/src/terminal-dispatch-recovery-service.js +7 -0
  22. package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
  23. package/dist/src/terminal-monitor-state-reconciliation-service.js +15 -1
  24. package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
  25. package/dist/src/terminal-questionnaire-adapter.d.ts +6 -2
  26. package/dist/src/terminal-questionnaire-adapter.js +61 -40
  27. package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
  28. package/dist/src/terminal-watch-store.js +135 -9
  29. package/dist/src/terminal-watch-store.js.map +1 -1
  30. package/docs/quickstart-tmux.md +2 -2
  31. package/package.json +1 -1
  32. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +4 -4
@@ -5,7 +5,7 @@ import { hasCanonicalTerminalEndpoint, sameTerminalControlIncarnation, terminalC
5
5
  import { CLAUDE_INJECTED_PASTE_FRAME_PROFILE, claudeInjectedPasteProofEligible, revalidateClaudeComposerProof, settleClaudeInjectedPasteComposer } from "./claude-injected-paste-proof.js";
6
6
  import { TERMINAL_INTERACTION_SCHEMA, TERMINAL_INTERACTION_VERSION, validateAnyTerminalInteractionResponse, validateTerminalInteractionProjection } from "./terminal-interaction-protocol.js";
7
7
  import { captureTerminalInteraction } from "./terminal-interaction-core.js";
8
- // Verified Codex profiles through 0.153.4 keep Enter in paste/newline mode for
8
+ // Verified Codex profiles through 0.154.0 keep Enter in paste/newline mode for
9
9
  // 120ms after burst input. Cross that boundary rather than landing on it, and
10
10
  // also require observable composer stability instead of treating this delay
11
11
  // alone as acceptance.
@@ -60,6 +60,10 @@ const CODEX_NATIVE_STATUS_POPUP_BY_PROFILE = {
60
60
  " /status show current session configuration and token usage",
61
61
  " /statusline configure which items appear in the status line"
62
62
  ],
63
+ "codex-tui-0.154.0": [
64
+ " /status show current session configuration and token usage",
65
+ " /statusline configure which items appear in the status line"
66
+ ],
63
67
  "codex-tui-generic-v1": [
64
68
  " /status show current session configuration and token usage",
65
69
  " /statusline configure which items appear in the status line"
@@ -80,6 +84,7 @@ const CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE = {
80
84
  "codex-tui-0.151.0": 80,
81
85
  "codex-tui-0.153.0": 80,
82
86
  "codex-tui-0.153.4": 80,
87
+ "codex-tui-0.154.0": 80,
83
88
  "codex-tui-generic-v1": 80
84
89
  };
85
90
  const CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE = {
@@ -119,6 +124,18 @@ const CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE = {
119
124
  "/ide Manage IDE integrations and show status",
120
125
  "/usage Show session cost, plan usage, and activity stats"
121
126
  ],
127
+ "claude-code-2.1.266-native-status": [
128
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
129
+ "/statusline Set up Claude Code's status line UI",
130
+ "/ide Manage IDE integrations and show status",
131
+ "/usage Show session cost, plan usage, and activity stats"
132
+ ],
133
+ "claude-code-2.1.267-native-status": [
134
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
135
+ "/statusline Set up Claude Code's status line UI",
136
+ "/ide Manage IDE integrations and show status",
137
+ "/usage Show session cost, plan usage, and activity stats"
138
+ ],
122
139
  "claude-code-unverified-native-status-v1": [
123
140
  "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
124
141
  "/statusline Set up Claude Code's status line UI",
@@ -151,6 +168,14 @@ const CLAUDE_NATIVE_STATUS_SETTLE_BY_PROFILE = {
151
168
  minimumStableMs: 80,
152
169
  maximumSettleMs: 5_000
153
170
  },
171
+ "claude-code-2.1.266-native-status": {
172
+ minimumStableMs: 80,
173
+ maximumSettleMs: 5_000
174
+ },
175
+ "claude-code-2.1.267-native-status": {
176
+ minimumStableMs: 80,
177
+ maximumSettleMs: 5_000
178
+ },
154
179
  "claude-code-unverified-native-status-v1": {
155
180
  minimumStableMs: 80,
156
181
  maximumSettleMs: 5_000
@@ -935,6 +960,13 @@ export class TerminalAgentBridge {
935
960
  // not let historical Esc instructions in scrollback veto Send.
936
961
  scrollbackLines: 0
937
962
  });
963
+ const asyncQuestionInputMode = inspectCodexAsyncQuestionInputMode(captured.screen);
964
+ if (asyncQuestionInputMode === "expanded" ||
965
+ asyncQuestionInputMode === "ambiguous") {
966
+ throw new Error(asyncQuestionInputMode === "expanded"
967
+ ? "the Codex async question editor currently owns terminal input; answer, skip, or return to the main prompt before retrying the explicit user Send"
968
+ : "Codex shows an async-question surface but AKK cannot prove that the main prompt owns terminal input; return to a complete main prompt before retrying the explicit user Send");
969
+ }
938
970
  if (captured.inspection.approval.blocked ||
939
971
  captured.inspection.activity.state === "awaiting_approval" ||
940
972
  codexBlockingModalVisible(captured.screen)) {
@@ -2891,7 +2923,221 @@ function codexBlockingModalVisible(screen) {
2891
2923
  .join("\n");
2892
2924
  return /\b(?:press|use)\s+(?:esc|escape)\s+to\s+(?:cancel|close|dismiss)\b/iu
2893
2925
  .test(tail) ||
2894
- /\besc\s+to\s+cancel\b/iu.test(tail);
2926
+ /\besc\s+to\s+cancel\b/iu.test(tail) ||
2927
+ codexActiveWriterViewerVisible(tail) ||
2928
+ ["expanded", "ambiguous"].includes(inspectCodexAsyncQuestionInputMode(tail));
2929
+ }
2930
+ function codexActiveWriterViewerVisible(styledScreen) {
2931
+ const lines = stripTerminalEscapeSequences(styledScreen)
2932
+ .replace(/\r\n?/gu, "\n")
2933
+ .split("\n")
2934
+ .slice(-80);
2935
+ let titleIndex = -1;
2936
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
2937
+ if (/^ {2}🔒(?:\s|$)/u.test(lines[index])) {
2938
+ titleIndex = index;
2939
+ break;
2940
+ }
2941
+ }
2942
+ if (titleIndex < 0) {
2943
+ return false;
2944
+ }
2945
+ const laterMainComposer = lines.findIndex((line, index) => index > titleIndex && CODEX_COMPOSER_MARKER.test(line));
2946
+ if (laterMainComposer >= 0) {
2947
+ return false;
2948
+ }
2949
+ const frame = lines.slice(titleIndex, titleIndex + 12)
2950
+ .map((line) => line.trim().replace(/\s+/gu, " "))
2951
+ .filter((line) => line.length > 0)
2952
+ .join(" ");
2953
+ return /🔒\s+This conversation is open in another app\s+\S+ to Retry/iu
2954
+ .test(frame) &&
2955
+ /Close it there and press\s+\S+\s+to continue here\./iu.test(frame) &&
2956
+ /\S+ retry\s+\S+ exit(?:\s+\S+ transcript)?/iu.test(frame);
2957
+ }
2958
+ const CODEX_ASYNC_QUESTION_FOOTER_LABEL = /\s(submit|skip|main prompt|prev question|next question|queued messages)(?=\s{2,}|\s*$)/giu;
2959
+ function codexStructuredAsyncFooterEvidence(lines, evidenceFloor) {
2960
+ let strongest = { index: -1, labelCount: 0 };
2961
+ for (let start = evidenceFloor + 1; start < lines.length; start += 1) {
2962
+ const labels = new Set();
2963
+ for (let end = start; end < Math.min(lines.length, start + 5); end += 1) {
2964
+ const line = lines[end];
2965
+ if (!/^ {2}(?![ ↳?])\S/u.test(line)) {
2966
+ break;
2967
+ }
2968
+ for (const match of line.matchAll(CODEX_ASYNC_QUESTION_FOOTER_LABEL)) {
2969
+ labels.add(match[1].toLowerCase());
2970
+ }
2971
+ if (labels.size > strongest.labelCount) {
2972
+ strongest = { index: end, labelCount: labels.size };
2973
+ }
2974
+ if (labels.size >= 2) {
2975
+ return strongest;
2976
+ }
2977
+ }
2978
+ }
2979
+ return strongest;
2980
+ }
2981
+ function codexMainComposerFooterIndex(lines, composerIndex) {
2982
+ for (let index = lines.length - 1; index > composerIndex; index -= 1) {
2983
+ // A real statusline starts at column zero. Draft continuations are inset,
2984
+ // even when their text happens to look exactly like a model statusline.
2985
+ if (CODEX_COMPLETE_COMPOSER_FOOTER.test(lines[index].trimEnd())) {
2986
+ return index;
2987
+ }
2988
+ }
2989
+ return -1;
2990
+ }
2991
+ /**
2992
+ * Classify Codex 0.154's non-blocking inline-question surface by current input
2993
+ * ownership. The shared `Queued follow-up inputs` heading is not sufficient:
2994
+ * ordinary queued messages use it too. An exact collapsed summary, after any
2995
+ * ordinary queue preview is removed, proves Send-safe ownership even when the
2996
+ * Composer is outside the viewport. Positive editor evidence without a
2997
+ * complete shape remains fail-closed because paste plus Enter would otherwise
2998
+ * submit the user's task as an answer.
2999
+ */
3000
+ export function inspectCodexAsyncQuestionInputMode(styledScreen) {
3001
+ const lines = stripTerminalEscapeSequences(styledScreen)
3002
+ .replace(/\r\n?/gu, "\n")
3003
+ .replace(/\u00a0/gu, " ")
3004
+ .split("\n")
3005
+ .slice(-80);
3006
+ while (lines.length > 0 && lines.at(-1)?.trim().length === 0) {
3007
+ lines.pop();
3008
+ }
3009
+ const normalized = lines.map((line) => line.trim().replace(/\s+/gu, " "));
3010
+ let mainComposerIndex = -1;
3011
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
3012
+ // The main Composer marker is column zero. Async option and inline-text
3013
+ // markers are inset by the menu surface, even after ANSI is removed.
3014
+ if (CODEX_COMPOSER_MARKER.test(lines[index])) {
3015
+ mainComposerIndex = index;
3016
+ break;
3017
+ }
3018
+ }
3019
+ let headerIndex = -1;
3020
+ let wrappedHeaderRows = 1;
3021
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
3022
+ if (/^•\s+Queued follow-up inputs\s*$/u.test(lines[index]) ||
3023
+ /^•\s+Queued follow-up inpu…\s*$/u.test(lines[index]) ||
3024
+ (/^•\s+Queued follow-up\s*$/u.test(lines[index]) &&
3025
+ /^ {2}inputs\s*$/u.test(lines[index + 1] ?? ""))) {
3026
+ headerIndex = index;
3027
+ wrappedHeaderRows = /^•\s+Queued follow-up\s*$/u.test(lines[index])
3028
+ ? 2
3029
+ : 1;
3030
+ break;
3031
+ }
3032
+ }
3033
+ const headerBodyIndex = headerIndex < 0
3034
+ ? -1
3035
+ : headerIndex + wrappedHeaderRows;
3036
+ let questionEvidenceStart = headerBodyIndex;
3037
+ if (headerIndex >= 0) {
3038
+ let cursor = headerBodyIndex;
3039
+ while (cursor < lines.length && lines[cursor].trim().length === 0) {
3040
+ cursor += 1;
3041
+ }
3042
+ let sawQueuedPreview = false;
3043
+ while (cursor < lines.length) {
3044
+ if (/^ {2}↳\s/u.test(lines[cursor])) {
3045
+ sawQueuedPreview = true;
3046
+ cursor += 1;
3047
+ while (cursor < lines.length) {
3048
+ if (lines[cursor].trim().length === 0 ||
3049
+ /^ {4,}\S/u.test(lines[cursor])) {
3050
+ cursor += 1;
3051
+ continue;
3052
+ }
3053
+ break;
3054
+ }
3055
+ continue;
3056
+ }
3057
+ break;
3058
+ }
3059
+ if (sawQueuedPreview) {
3060
+ questionEvidenceStart = cursor;
3061
+ }
3062
+ }
3063
+ const mainComposerFooterIndex = mainComposerIndex < 0
3064
+ ? -1
3065
+ : codexMainComposerFooterIndex(lines, mainComposerIndex);
3066
+ const summaryBeforeMainComposer = headerIndex < 0 || mainComposerIndex < 0
3067
+ ? -1
3068
+ : normalized.findIndex((line, index) => index >= questionEvidenceStart && index < mainComposerIndex &&
3069
+ /^\?\s+\d+\s+questions?(?:\s+·\s+\d+s)?$/iu.test(line));
3070
+ if (mainComposerIndex >= 0 && mainComposerFooterIndex < 0) {
3071
+ // A statusline-disabled Composer is the strongest available ownership
3072
+ // anchor. Treat all following wrapped rows as its arbitrary draft instead
3073
+ // of letting question-like user text veto a human-priority Send.
3074
+ return summaryBeforeMainComposer >= 0 ? "collapsed" : "absent";
3075
+ }
3076
+ const evidenceFloor = Math.max(mainComposerFooterIndex, questionEvidenceStart - 1);
3077
+ const structuredFooter = codexStructuredAsyncFooterEvidence(lines, evidenceFloor);
3078
+ if (structuredFooter.labelCount >= 2) {
3079
+ return "expanded";
3080
+ }
3081
+ const clippedFooterIndex = normalized.findIndex((line, index) => index > evidenceFloor && /^ente(?:r)?…$/iu.test(line) &&
3082
+ /^ctrl…$/iu.test(normalized[index + 1] ?? ""));
3083
+ const clippedChoiceIndex = normalized.findIndex((line, index) => index > evidenceFloor &&
3084
+ line === "Expand terminal to read the entire option");
3085
+ if (clippedFooterIndex >= 0 || clippedChoiceIndex >= 0) {
3086
+ return "ambiguous";
3087
+ }
3088
+ if (headerIndex < 0) {
3089
+ const evidence = new Set();
3090
+ normalized.forEach((line, index) => {
3091
+ if (index <= evidenceFloor) {
3092
+ return;
3093
+ }
3094
+ if (/^\d+\s+of(?:\s+\d+)?$/iu.test(line)) {
3095
+ evidence.add("progress");
3096
+ }
3097
+ if (/^›\s+\S/u.test(line)) {
3098
+ evidence.add("selector");
3099
+ }
3100
+ if (/^Type your answer(?:\s*\(optional\))?$/iu.test(line)) {
3101
+ evidence.add("free_text");
3102
+ }
3103
+ });
3104
+ return evidence.size >= 2 ||
3105
+ (evidence.size >= 1 && structuredFooter.labelCount >= 1)
3106
+ ? "ambiguous"
3107
+ : "absent";
3108
+ }
3109
+ const weakQuestionIndex = normalized.findIndex((line, index) => index > evidenceFloor && index >= questionEvidenceStart && (/^\d+\s+of(?:\s+\d+)?$/iu.test(line) ||
3110
+ /^›\s+\S/u.test(line) ||
3111
+ /^Type your answer(?:\s*\(optional\))?$/iu.test(line)));
3112
+ if (mainComposerIndex > headerIndex && weakQuestionIndex > evidenceFloor) {
3113
+ return "ambiguous";
3114
+ }
3115
+ const summaryIndex = normalized.findIndex((line, index) => index >= questionEvidenceStart &&
3116
+ (mainComposerIndex < 0 || index < mainComposerIndex) &&
3117
+ /^\?\s+\d+\s+questions?(?:\s+·\s+\d+s)?$/iu.test(line));
3118
+ if (summaryIndex >= 0) {
3119
+ return weakQuestionIndex >= 0 ? "ambiguous" : "collapsed";
3120
+ }
3121
+ if (mainComposerIndex > headerIndex) {
3122
+ return "absent";
3123
+ }
3124
+ const afterHeader = normalized.slice(questionEvidenceStart)
3125
+ .filter((line) => line.length > 0);
3126
+ const positiveAfterHeader = afterHeader.some((line) => /^\d+\s+of(?:\s+\d+)?$/iu.test(line) ||
3127
+ /^›\s+\d+\.(?:\s|$)/u.test(line) ||
3128
+ /^Type your answer(?:\s*\(optional\))?$/iu.test(line));
3129
+ if (positiveAfterHeader) {
3130
+ return "ambiguous";
3131
+ }
3132
+ // A bare heading (optionally followed by ↳ queued-message previews) is not
3133
+ // question authority. Preserve human-explicit Send in that ordinary state.
3134
+ if (afterHeader.length === 0) {
3135
+ return "absent";
3136
+ }
3137
+ // An expanded editor can temporarily replace its footer with a flash. If
3138
+ // there is question-like content under the shared heading and no proven main
3139
+ // Composer, do not guess which input surface will receive the next paste.
3140
+ return "ambiguous";
2895
3141
  }
2896
3142
  /**
2897
3143
  * Classify only the bottom live Codex composer region. A matching transcript