@quantiya/codevibe-antigravity-plugin 2.0.79 → 2.0.81
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/dist/server.js +802 -64
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -34,16 +34,19 @@ __export(server_exports, {
|
|
|
34
34
|
SessionNotFoundError: () => SessionNotFoundError,
|
|
35
35
|
StoredAuthFailureError: () => StoredAuthFailureError,
|
|
36
36
|
TERMINAL_SHUTDOWN_SIGNALS: () => TERMINAL_SHUTDOWN_SIGNALS,
|
|
37
|
+
TmuxPaneObserver: () => TmuxPaneObserver,
|
|
37
38
|
__testing: () => __testing,
|
|
38
39
|
classifyEntrypoint: () => classifyEntrypoint,
|
|
39
40
|
classifyTmuxHasSessionError: () => classifyTmuxHasSessionError,
|
|
40
41
|
commandDigest: () => commandDigest,
|
|
41
42
|
detectActivePromptKind: () => detectActivePromptKind,
|
|
43
|
+
extractFileAccessPrompt: () => extractFileAccessPrompt,
|
|
42
44
|
extractSystemErrorPrompt: () => extractSystemErrorPrompt,
|
|
43
45
|
generateLaunchSessionId: () => generateLaunchSessionId,
|
|
44
46
|
getActiveConversationFromCliLog: () => getActiveConversationFromCliLog,
|
|
45
47
|
isAgyStatusFooterLine: () => isAgyStatusFooterLine,
|
|
46
48
|
isComposedStatusFooter: () => isComposedStatusFooter,
|
|
49
|
+
isPaneInputSafetyBlocked: () => isPaneInputSafetyBlocked,
|
|
47
50
|
isSystemErrorActiveFromIndex: () => isSystemErrorActiveFromIndex,
|
|
48
51
|
looksLikePromptSnapshot: () => looksLikePromptSnapshot,
|
|
49
52
|
parseApprovalSnapshot: () => parseApprovalSnapshot,
|
|
@@ -2343,7 +2346,9 @@ function promptMatchesCompletedTool(state, toolName, toolArgs) {
|
|
|
2343
2346
|
const promptPaths = /* @__PURE__ */ new Set();
|
|
2344
2347
|
const declared = normalizePath(state.pendingCall?.filePath);
|
|
2345
2348
|
if (declared) promptPaths.add(declared);
|
|
2346
|
-
|
|
2349
|
+
if (!isFileAccessPrompt(state) || !declared) {
|
|
2350
|
+
for (const link of fileLinks(state.paneDisplayHeader)) promptPaths.add(link);
|
|
2351
|
+
}
|
|
2347
2352
|
if (promptPaths.size === 0) return false;
|
|
2348
2353
|
for (const key of TOOL_PATH_KEYS) {
|
|
2349
2354
|
const toolPath = normalizePath(toolArgs[key]);
|
|
@@ -2603,8 +2608,24 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2603
2608
|
headerText = extractHeader(snapshot);
|
|
2604
2609
|
}
|
|
2605
2610
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2611
|
+
let isChooser = hasPrompt && kind !== "system_error";
|
|
2612
|
+
let reportedKind = kind;
|
|
2613
|
+
let isAmbiguous = false;
|
|
2614
|
+
if (!isChooser && kind !== "system_error") {
|
|
2615
|
+
const safety = isPaneInputSafetyBlocked(snapshot);
|
|
2616
|
+
if (safety.blocked) {
|
|
2617
|
+
isChooser = true;
|
|
2618
|
+
reportedKind = safety.kind;
|
|
2619
|
+
isAmbiguous = true;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
return {
|
|
2623
|
+
active: isChooser,
|
|
2624
|
+
headerText,
|
|
2625
|
+
probeSucceeded: true,
|
|
2626
|
+
kind: reportedKind,
|
|
2627
|
+
...isAmbiguous ? { ambiguous: true } : {}
|
|
2628
|
+
};
|
|
2608
2629
|
} catch {
|
|
2609
2630
|
return { active: false, headerText: null, probeSucceeded: false, kind: null };
|
|
2610
2631
|
}
|
|
@@ -2864,10 +2885,203 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2864
2885
|
}
|
|
2865
2886
|
}
|
|
2866
2887
|
};
|
|
2888
|
+
function isComposerBoxAt(lines, idx) {
|
|
2889
|
+
if (idx < 0 || idx >= lines.length) return false;
|
|
2890
|
+
const line0 = lines[idx].trim();
|
|
2891
|
+
if (!/^[─━│╭╰╮╯\s_-]{3,}$/.test(line0)) return false;
|
|
2892
|
+
let cursorIdx = -1;
|
|
2893
|
+
for (let k = idx + 1; k < Math.min(lines.length, idx + 4); k++) {
|
|
2894
|
+
const t = lines[k].trim();
|
|
2895
|
+
if (!t) continue;
|
|
2896
|
+
if (/^[│\s]*[❯>]\s*$/.test(t)) {
|
|
2897
|
+
cursorIdx = k;
|
|
2898
|
+
break;
|
|
2899
|
+
}
|
|
2900
|
+
break;
|
|
2901
|
+
}
|
|
2902
|
+
if (cursorIdx < 0) return false;
|
|
2903
|
+
for (let k = cursorIdx + 1; k < Math.min(lines.length, cursorIdx + 4); k++) {
|
|
2904
|
+
const t = lines[k].trim();
|
|
2905
|
+
if (!t) continue;
|
|
2906
|
+
if (/^[─━│╭╰╮╯\s_-]{3,}$/.test(t)) {
|
|
2907
|
+
return true;
|
|
2908
|
+
}
|
|
2909
|
+
break;
|
|
2910
|
+
}
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2913
|
+
function isPaneInputSafetyBlocked(input) {
|
|
2914
|
+
const rawText = Array.isArray(input) ? input.join("\n") : input;
|
|
2915
|
+
if (!rawText) return { blocked: false, kind: null };
|
|
2916
|
+
const plainText = rawText.replace(ANSI_ESCAPE_REGEX, "").trimEnd();
|
|
2917
|
+
if (!plainText) return { blocked: false, kind: null };
|
|
2918
|
+
const lines = plainText.split(/\r?\n/);
|
|
2919
|
+
if (lines.length === 0) return { blocked: false, kind: null };
|
|
2920
|
+
const startIdx = Math.max(0, lines.length - 40);
|
|
2921
|
+
let footerIdx = -1;
|
|
2922
|
+
for (let i = lines.length - 1; i >= startIdx; i--) {
|
|
2923
|
+
const trimmed = lines[i].trim();
|
|
2924
|
+
if (!trimmed) continue;
|
|
2925
|
+
if (/^(`{3,}|~{3,})\s*$/.test(lines[i])) {
|
|
2926
|
+
break;
|
|
2927
|
+
}
|
|
2928
|
+
if (isQuestionFooterLine(trimmed)) {
|
|
2929
|
+
footerIdx = i;
|
|
2930
|
+
break;
|
|
2931
|
+
} else if (/\besc to cancel\b/i.test(trimmed) && !isAgyStatusFooterLine(trimmed)) {
|
|
2932
|
+
footerIdx = i;
|
|
2933
|
+
break;
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
if (footerIdx < 0) {
|
|
2937
|
+
const candidateHeaderIndices = [];
|
|
2938
|
+
for (let i = lines.length - 1; i >= startIdx; i--) {
|
|
2939
|
+
const trimmed = lines[i].trim();
|
|
2940
|
+
if (!trimmed) continue;
|
|
2941
|
+
if (isQuestionHeaderLine(trimmed) && /\((?:Use\s+arrow\s+keys|Use\s+\S+\s+keys)[^)]*\)/i.test(trimmed)) {
|
|
2942
|
+
candidateHeaderIndices.push(i);
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
for (const headerIdx of candidateHeaderIndices) {
|
|
2946
|
+
let optCount = 0;
|
|
2947
|
+
let cursorCount = 0;
|
|
2948
|
+
let lastOptionIdx = -1;
|
|
2949
|
+
let inInnerFence = false;
|
|
2950
|
+
let innerFenceChar = "";
|
|
2951
|
+
let innerFenceLen = 0;
|
|
2952
|
+
let composerEncountered = false;
|
|
2953
|
+
for (let j = headerIdx + 1; j < lines.length; j++) {
|
|
2954
|
+
const lineJ = lines[j];
|
|
2955
|
+
const trimmed = lineJ.trim();
|
|
2956
|
+
if (!trimmed) continue;
|
|
2957
|
+
const fenceMatch = lineJ.match(/^(`{3,}|~{3,})(.*)$/);
|
|
2958
|
+
if (fenceMatch) {
|
|
2959
|
+
const matchedFence = fenceMatch[1];
|
|
2960
|
+
const matchedChar = matchedFence[0];
|
|
2961
|
+
const matchedLen = matchedFence.length;
|
|
2962
|
+
const fenceInfo = fenceMatch[2].trim();
|
|
2963
|
+
if (inInnerFence) {
|
|
2964
|
+
if (matchedChar === innerFenceChar && matchedLen >= innerFenceLen && fenceInfo === "") {
|
|
2965
|
+
inInnerFence = false;
|
|
2966
|
+
innerFenceChar = "";
|
|
2967
|
+
innerFenceLen = 0;
|
|
2968
|
+
optCount = 0;
|
|
2969
|
+
cursorCount = 0;
|
|
2970
|
+
lastOptionIdx = -1;
|
|
2971
|
+
}
|
|
2972
|
+
continue;
|
|
2973
|
+
}
|
|
2974
|
+
inInnerFence = true;
|
|
2975
|
+
innerFenceChar = matchedChar;
|
|
2976
|
+
innerFenceLen = matchedLen;
|
|
2977
|
+
optCount = 0;
|
|
2978
|
+
cursorCount = 0;
|
|
2979
|
+
lastOptionIdx = -1;
|
|
2980
|
+
continue;
|
|
2981
|
+
}
|
|
2982
|
+
if (inInnerFence) {
|
|
2983
|
+
continue;
|
|
2984
|
+
}
|
|
2985
|
+
if (isAgyStatusFooterLine(trimmed) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(trimmed) || isComposerBoxAt(lines, j)) {
|
|
2986
|
+
composerEncountered = true;
|
|
2987
|
+
break;
|
|
2988
|
+
}
|
|
2989
|
+
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) {
|
|
2990
|
+
if (/^[>\s❯]*1[.)]/.test(trimmed) && optCount > 0) {
|
|
2991
|
+
optCount = 0;
|
|
2992
|
+
cursorCount = 0;
|
|
2993
|
+
}
|
|
2994
|
+
optCount++;
|
|
2995
|
+
if (/^[>❯]/.test(trimmed)) {
|
|
2996
|
+
cursorCount++;
|
|
2997
|
+
}
|
|
2998
|
+
lastOptionIdx = j;
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
const isValidOptionGroup = optCount >= 2 && cursorCount <= 1;
|
|
3003
|
+
if (!inInnerFence && !composerEncountered && isValidOptionGroup && lastOptionIdx >= 0 && !hasTrailingAgyComposer(lines, lastOptionIdx)) {
|
|
3004
|
+
return { blocked: true, kind: "question" };
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return { blocked: false, kind: null };
|
|
3008
|
+
}
|
|
3009
|
+
const candidateHeaders = [];
|
|
3010
|
+
for (let i = footerIdx - 1; i >= startIdx; i--) {
|
|
3011
|
+
const trimmed = lines[i].trim();
|
|
3012
|
+
if (!trimmed) continue;
|
|
3013
|
+
if (isQuestionHeaderLine(trimmed)) {
|
|
3014
|
+
candidateHeaders.push({ lineIdx: i, kind: "question" });
|
|
3015
|
+
} else if (/Allow access to this file\?/i.test(trimmed)) {
|
|
3016
|
+
candidateHeaders.push({ lineIdx: i, kind: "file_access" });
|
|
3017
|
+
} else if (/Requesting permission for:/i.test(trimmed) || /Do you want to (?:proceed|run|execute)/i.test(trimmed)) {
|
|
3018
|
+
candidateHeaders.push({ lineIdx: i, kind: "approval" });
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
for (const candidate of candidateHeaders) {
|
|
3022
|
+
let optCount = 0;
|
|
3023
|
+
let cursorCount = 0;
|
|
3024
|
+
let inInnerFence = false;
|
|
3025
|
+
let innerFenceChar = "";
|
|
3026
|
+
let innerFenceLen = 0;
|
|
3027
|
+
let composerEncountered = false;
|
|
3028
|
+
for (let j = candidate.lineIdx + 1; j < footerIdx; j++) {
|
|
3029
|
+
const lineJ = lines[j];
|
|
3030
|
+
const trimmed = lineJ.trim();
|
|
3031
|
+
if (!trimmed) continue;
|
|
3032
|
+
const fenceMatch = lineJ.match(/^(`{3,}|~{3,})(.*)$/);
|
|
3033
|
+
if (fenceMatch) {
|
|
3034
|
+
const marker = fenceMatch[1];
|
|
3035
|
+
const rest = fenceMatch[2].trim();
|
|
3036
|
+
const fChar = marker[0];
|
|
3037
|
+
const fLen = marker.length;
|
|
3038
|
+
if (inInnerFence) {
|
|
3039
|
+
if (fChar === innerFenceChar && fLen >= innerFenceLen && rest === "") {
|
|
3040
|
+
inInnerFence = false;
|
|
3041
|
+
innerFenceChar = "";
|
|
3042
|
+
innerFenceLen = 0;
|
|
3043
|
+
optCount = 0;
|
|
3044
|
+
cursorCount = 0;
|
|
3045
|
+
}
|
|
3046
|
+
continue;
|
|
3047
|
+
}
|
|
3048
|
+
inInnerFence = true;
|
|
3049
|
+
innerFenceChar = fChar;
|
|
3050
|
+
innerFenceLen = fLen;
|
|
3051
|
+
optCount = 0;
|
|
3052
|
+
cursorCount = 0;
|
|
3053
|
+
continue;
|
|
3054
|
+
}
|
|
3055
|
+
if (inInnerFence) {
|
|
3056
|
+
continue;
|
|
3057
|
+
}
|
|
3058
|
+
if (isAgyStatusFooterLine(trimmed) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(trimmed) || isComposerBoxAt(lines, j)) {
|
|
3059
|
+
composerEncountered = true;
|
|
3060
|
+
break;
|
|
3061
|
+
}
|
|
3062
|
+
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) {
|
|
3063
|
+
if (/^[>\s❯]*1[.)]/.test(trimmed) && optCount > 0) {
|
|
3064
|
+
optCount = 0;
|
|
3065
|
+
cursorCount = 0;
|
|
3066
|
+
}
|
|
3067
|
+
optCount++;
|
|
3068
|
+
if (/^[>❯]/.test(trimmed)) {
|
|
3069
|
+
cursorCount++;
|
|
3070
|
+
}
|
|
3071
|
+
continue;
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
const isValidOptionGroup = optCount >= 2 && cursorCount <= 1;
|
|
3075
|
+
if (!inInnerFence && !composerEncountered && isValidOptionGroup && !hasTrailingAgyComposer(lines, footerIdx)) {
|
|
3076
|
+
return { blocked: true, kind: candidate.kind };
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
return { blocked: false, kind: null };
|
|
3080
|
+
}
|
|
2867
3081
|
function looksLikePromptDelta(chunk) {
|
|
2868
3082
|
return (
|
|
2869
3083
|
// Approval UI signatures
|
|
2870
|
-
/Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(chunk) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(chunk) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /Question
|
|
3084
|
+
/Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(chunk) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(chunk) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /^\s*(?:[❯>]\s*)?\?\s+(?!for\s+(?:shortcuts|help|commands))\S/im.test(chunk) || /Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:/i.test(chunk) || /\benter\s+(?:to\s+)?select\b/i.test(chunk) || /\b(?:navigate|use\s+arrow\s+keys)\b/i.test(chunk) || /^File access\s*$/im.test(chunk) || /^\s*Allow access to this file\?\s*$/im.test(chunk) || /(?:Yes, allow access|No, deny access)/i.test(chunk) || /(?:↑\/↓\s+Navigate|ctrl\+r\s+Review)/i.test(chunk) || /Our servers are experiencing/i.test(chunk) || /Error ID:\s*[0-9a-fA-F-]*/i.test(chunk) || /(?:⚠️|⚠\uFE0F?)\s+.*(?:high[- ]traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error|quota\b|upgrade your subscription|increase your limits|resets in\s+\d|credits balance is too low|out of credits|rate[- ]limit|capacity exhausted|exhausted your quota|internal error|currently unreachable|network issue connecting|authentication expired|authentication issue|terminated due to error|invalid output|invalid tool call)/i.test(chunk)
|
|
2871
3085
|
);
|
|
2872
3086
|
}
|
|
2873
3087
|
function isNonSystemErrorWarningLine(trimmed) {
|
|
@@ -2936,7 +3150,13 @@ function isComposedStatusFooter(trimmed) {
|
|
|
2936
3150
|
}
|
|
2937
3151
|
function isAgyStatusFooterLine(trimmed) {
|
|
2938
3152
|
if (!trimmed) return false;
|
|
2939
|
-
if (
|
|
3153
|
+
if (/(?:\benter|<enter>)\s+(?:to\s+)?select\b/i.test(trimmed) || /\bNavigate\b/i.test(trimmed)) {
|
|
3154
|
+
return false;
|
|
3155
|
+
}
|
|
3156
|
+
if (/^\s*(?:[❯>]\s*)?\?\s+for shortcuts\b/i.test(trimmed)) {
|
|
3157
|
+
return true;
|
|
3158
|
+
}
|
|
3159
|
+
if (/^\s*esc to cancel\b/i.test(trimmed)) {
|
|
2940
3160
|
return true;
|
|
2941
3161
|
}
|
|
2942
3162
|
if (/^(?:\(initializing\.\.\.\)|\d+\s+task\(s\)\s+·\s+\/tasks|\d+\s+subagent\s*\(s\)|AI:\s+(?:\d+\s+\(\/credits to add more\)|Out of credits)|AI Credits:\s+\d+|Quota:\s*\d+(?:\.\d+)?%)$/i.test(trimmed)) {
|
|
@@ -2947,6 +3167,68 @@ function isAgyStatusFooterLine(trimmed) {
|
|
|
2947
3167
|
}
|
|
2948
3168
|
return false;
|
|
2949
3169
|
}
|
|
3170
|
+
function computeFenceMask(lines) {
|
|
3171
|
+
const inFence = new Array(lines.length).fill(false);
|
|
3172
|
+
let inside = false;
|
|
3173
|
+
let activeFenceChar = "";
|
|
3174
|
+
let activeFenceLen = 0;
|
|
3175
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3176
|
+
const line = lines[i];
|
|
3177
|
+
const match = line.match(/^(`{3,}|~{3,})(.*)$/);
|
|
3178
|
+
if (match) {
|
|
3179
|
+
const marker = match[1];
|
|
3180
|
+
const rest = match[2];
|
|
3181
|
+
const fenceChar = marker[0];
|
|
3182
|
+
const fenceLen = marker.length;
|
|
3183
|
+
if (!inside) {
|
|
3184
|
+
inside = true;
|
|
3185
|
+
activeFenceChar = fenceChar;
|
|
3186
|
+
activeFenceLen = fenceLen;
|
|
3187
|
+
inFence[i] = true;
|
|
3188
|
+
continue;
|
|
3189
|
+
} else {
|
|
3190
|
+
if (fenceChar === activeFenceChar && fenceLen >= activeFenceLen && rest.trim() === "") {
|
|
3191
|
+
inside = false;
|
|
3192
|
+
activeFenceChar = "";
|
|
3193
|
+
activeFenceLen = 0;
|
|
3194
|
+
inFence[i] = true;
|
|
3195
|
+
continue;
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
inFence[i] = inside;
|
|
3200
|
+
}
|
|
3201
|
+
return inFence;
|
|
3202
|
+
}
|
|
3203
|
+
function hasTrailingAgyComposer(lines, afterLineIdx, fenceMask) {
|
|
3204
|
+
if (afterLineIdx < -1 || afterLineIdx >= lines.length - 1) return false;
|
|
3205
|
+
const trailingLines = [];
|
|
3206
|
+
for (let i = afterLineIdx + 1; i < lines.length; i++) {
|
|
3207
|
+
if (fenceMask && fenceMask[i]) continue;
|
|
3208
|
+
const trimmed = lines[i].trim();
|
|
3209
|
+
if (trimmed.length > 0) {
|
|
3210
|
+
trailingLines.push(trimmed);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
if (trailingLines.length === 0) return false;
|
|
3214
|
+
if (trailingLines.some((l) => /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(l))) {
|
|
3215
|
+
return true;
|
|
3216
|
+
}
|
|
3217
|
+
for (let i = 0; i < trailingLines.length; i++) {
|
|
3218
|
+
const l = trailingLines[i];
|
|
3219
|
+
if (/^[─━│╭╰╮╯\s_-]{3,}$/.test(l)) {
|
|
3220
|
+
if (i + 1 < trailingLines.length && /^[│\s]*[❯>]\s*$/.test(trailingLines[i + 1])) {
|
|
3221
|
+
if (i + 2 < trailingLines.length && /^[─━│╭╰╮╯\s_-]{3,}$/.test(trailingLines[i + 2])) {
|
|
3222
|
+
return true;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
if (trailingLines.some((l) => isAgyStatusFooterLine(l) && /(?:Gemini|Flash|Pro|Claude|Sonnet|Opus|GPT|auto|high|low|medium)/i.test(l))) {
|
|
3228
|
+
return true;
|
|
3229
|
+
}
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
2950
3232
|
function reconstructFooter(rawLines) {
|
|
2951
3233
|
const cleaned = rawLines.map((l) => l.trim()).filter(Boolean);
|
|
2952
3234
|
if (cleaned.length === 0) return "";
|
|
@@ -3098,17 +3380,62 @@ function looksLikePromptSnapshot(snapshot) {
|
|
|
3098
3380
|
return hasApprovalHeader && hasApprovalOptions && hasApprovalFooter;
|
|
3099
3381
|
}
|
|
3100
3382
|
if (activeKind === "question") {
|
|
3101
|
-
const
|
|
3102
|
-
const
|
|
3103
|
-
const
|
|
3104
|
-
|
|
3383
|
+
const allLines = stripped.trimEnd().split("\n");
|
|
3384
|
+
const fenceMask = computeFenceMask(allLines);
|
|
3385
|
+
const windowStart = Math.max(0, allLines.length - 30);
|
|
3386
|
+
let headerIdx = -1;
|
|
3387
|
+
for (let i = allLines.length - 1; i >= windowStart; i--) {
|
|
3388
|
+
if (!fenceMask[i] && isQuestionHeaderLine(allLines[i])) {
|
|
3389
|
+
headerIdx = i;
|
|
3390
|
+
break;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
if (headerIdx < 0) return false;
|
|
3394
|
+
const headerLine = allLines[headerIdx].trim();
|
|
3395
|
+
const optionIndices = [];
|
|
3396
|
+
for (let i = headerIdx + 1; i < allLines.length; i++) {
|
|
3397
|
+
if (!fenceMask[i] && /^[>\s❯]*[1-9][.)]\s+\S/.test(allLines[i].trim())) {
|
|
3398
|
+
optionIndices.push(i);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
if (optionIndices.length < 2) return false;
|
|
3402
|
+
const lastOptionIdx = optionIndices[optionIndices.length - 1];
|
|
3403
|
+
const hasHeaderHint = /\((?:Use\s+arrow\s+keys|Use\s+\S+\s+keys)[^)]*\)/i.test(headerLine);
|
|
3404
|
+
let hasQuestionFooter = hasHeaderHint;
|
|
3405
|
+
let footerIdx = lastOptionIdx;
|
|
3406
|
+
if (!hasQuestionFooter) {
|
|
3407
|
+
for (let i = lastOptionIdx + 1; i < allLines.length; i++) {
|
|
3408
|
+
if (!fenceMask[i] && isQuestionFooterLine(allLines[i].trim())) {
|
|
3409
|
+
hasQuestionFooter = true;
|
|
3410
|
+
footerIdx = i;
|
|
3411
|
+
break;
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
if (!hasQuestionFooter) return false;
|
|
3416
|
+
if (hasTrailingAgyComposer(allLines, footerIdx, fenceMask)) {
|
|
3417
|
+
return false;
|
|
3418
|
+
}
|
|
3419
|
+
return true;
|
|
3105
3420
|
}
|
|
3106
3421
|
if (activeKind === "file_access") {
|
|
3107
|
-
const hasFileAccessHeader = /^File access\s*$/im.test(recent);
|
|
3108
|
-
const
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3422
|
+
const hasFileAccessHeader = /^File access\s*$/im.test(recent) || /^\s*(?:Read|Write|Edit|Access):\s*\S/im.test(recent);
|
|
3423
|
+
const questionMatches = Array.from(recent.matchAll(/^\s*Allow access to this file\?\s*$/gim));
|
|
3424
|
+
if (!hasFileAccessHeader || questionMatches.length === 0) {
|
|
3425
|
+
return false;
|
|
3426
|
+
}
|
|
3427
|
+
const lastQuestionMatch = questionMatches[questionMatches.length - 1];
|
|
3428
|
+
if (lastQuestionMatch.index === void 0) {
|
|
3429
|
+
return false;
|
|
3430
|
+
}
|
|
3431
|
+
const belowQuestion = recent.slice(lastQuestionMatch.index);
|
|
3432
|
+
if (/\?\s+for shortcuts/i.test(belowQuestion)) {
|
|
3433
|
+
return false;
|
|
3434
|
+
}
|
|
3435
|
+
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(belowQuestion);
|
|
3436
|
+
const hasApprovalFooter = /^\s*(?:esc to cancel\b|[↑↓/\s]*Navigate\b|ctrl\+r\s+Review\b)/im.test(belowQuestion);
|
|
3437
|
+
const hasQuestionFooter = /\benter\s+Select\b/i.test(belowQuestion);
|
|
3438
|
+
return hasApprovalOptions && hasApprovalFooter && !hasQuestionFooter;
|
|
3112
3439
|
}
|
|
3113
3440
|
return true;
|
|
3114
3441
|
}
|
|
@@ -3119,21 +3446,62 @@ function looksLikePromptSnapshot(snapshot) {
|
|
|
3119
3446
|
function extractFileAccessPrompt(snapshot) {
|
|
3120
3447
|
const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
|
|
3121
3448
|
const lines = stripped.split("\n");
|
|
3122
|
-
let
|
|
3449
|
+
let questionIndex = -1;
|
|
3123
3450
|
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
3124
|
-
if (/^\s*
|
|
3125
|
-
|
|
3451
|
+
if (/^\s*Allow access to this file\?\s*$/i.test(lines[index])) {
|
|
3452
|
+
questionIndex = index;
|
|
3126
3453
|
break;
|
|
3127
3454
|
}
|
|
3128
3455
|
}
|
|
3129
|
-
if (headingIndex < 0) return null;
|
|
3130
|
-
const block = lines.slice(headingIndex, headingIndex + 20);
|
|
3131
|
-
const questionIndex = block.findIndex((line) => /Allow access to this file\?/i.test(line));
|
|
3132
3456
|
if (questionIndex < 0) return null;
|
|
3133
|
-
const
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3457
|
+
const scanStart = Math.max(0, questionIndex - 15);
|
|
3458
|
+
let headingIndex = -1;
|
|
3459
|
+
let actionIndex = -1;
|
|
3460
|
+
let actionLine = null;
|
|
3461
|
+
let targetPath = null;
|
|
3462
|
+
for (let index = questionIndex - 1; index >= scanStart; index -= 1) {
|
|
3463
|
+
const line = lines[index].trim();
|
|
3464
|
+
if (!line) continue;
|
|
3465
|
+
const actionMatch = line.match(/^\s*((?:Read|Write|Edit|Access):\s*(\S.*))$/i);
|
|
3466
|
+
if (actionMatch && actionIndex < 0) {
|
|
3467
|
+
actionIndex = index;
|
|
3468
|
+
actionLine = actionMatch[1].trim();
|
|
3469
|
+
targetPath = actionMatch[2].trim();
|
|
3470
|
+
for (let prevIdx = index - 1; prevIdx >= scanStart; prevIdx -= 1) {
|
|
3471
|
+
const prevLine = lines[prevIdx].trim();
|
|
3472
|
+
if (!prevLine) continue;
|
|
3473
|
+
if (/^\s*File access\s*$/i.test(prevLine)) {
|
|
3474
|
+
headingIndex = prevIdx;
|
|
3475
|
+
}
|
|
3476
|
+
break;
|
|
3477
|
+
}
|
|
3478
|
+
break;
|
|
3479
|
+
}
|
|
3480
|
+
if (/^\s*Reason:\s*\S.*/i.test(line) && actionIndex < 0) {
|
|
3481
|
+
continue;
|
|
3482
|
+
}
|
|
3483
|
+
if (/^[⚠⚠️]\s*/.test(line) && actionIndex < 0) {
|
|
3484
|
+
continue;
|
|
3485
|
+
}
|
|
3486
|
+
if (/^\s*File access\s*$/i.test(line)) {
|
|
3487
|
+
headingIndex = index;
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
if (/^[─━│╭╰╮╯\s_-]*$/.test(line) || /^[│\s]*[❯>]\s+\S/.test(line) || /^\s*(?:Requesting permission for:|Question \d+\/\d+:|Allow access to this file\?|Do you want to proceed\?)/i.test(line) || /^[▶▸\s]*Thought|^●\s*/i.test(line) || /^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(line) || /^\s*(?:[↑↓/\s]*Navigate\b|ctrl\+r\s+Review\b)/i.test(line)) {
|
|
3491
|
+
break;
|
|
3492
|
+
}
|
|
3493
|
+
break;
|
|
3494
|
+
}
|
|
3495
|
+
const identity = targetPath || "non-workspace file access";
|
|
3496
|
+
const bodyStart = headingIndex >= 0 ? headingIndex + 1 : actionIndex >= 0 ? actionIndex : questionIndex;
|
|
3497
|
+
const contextLines = lines.slice(bodyStart, questionIndex).map((line) => line.trim()).filter((line) => {
|
|
3498
|
+
if (!line) return false;
|
|
3499
|
+
if (/^\s*File access\s*$/i.test(line)) return false;
|
|
3500
|
+
if (actionIndex < 0 && /^\s*Reason:\s*/i.test(line)) return false;
|
|
3501
|
+
return true;
|
|
3502
|
+
});
|
|
3503
|
+
const body = contextLines.join("\n").trim();
|
|
3504
|
+
const fullLine = actionLine ? `Allow access to this file? (${actionLine})` : lines[questionIndex].trim();
|
|
3137
3505
|
return { identity, fullLine, body };
|
|
3138
3506
|
}
|
|
3139
3507
|
function extractHeader(snapshot) {
|
|
@@ -3148,13 +3516,177 @@ function extractFullHeaderLine(snapshot) {
|
|
|
3148
3516
|
if (matches.length === 0) return null;
|
|
3149
3517
|
return matches[matches.length - 1][0].trim();
|
|
3150
3518
|
}
|
|
3519
|
+
function isOpenEnclosingContext(lines, checkTop, topAnchor, boundaryIdx) {
|
|
3520
|
+
let activeFence = null;
|
|
3521
|
+
for (let k = checkTop; k < topAnchor; k++) {
|
|
3522
|
+
const raw = lines[k];
|
|
3523
|
+
if (!activeFence) {
|
|
3524
|
+
const openMatch = raw.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
3525
|
+
if (openMatch) {
|
|
3526
|
+
const fenceStr = openMatch[1];
|
|
3527
|
+
const rest = openMatch[2];
|
|
3528
|
+
const char = fenceStr[0];
|
|
3529
|
+
if (char === "`" && rest.includes("`")) {
|
|
3530
|
+
} else {
|
|
3531
|
+
activeFence = { char, length: fenceStr.length };
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
} else {
|
|
3535
|
+
const closeMatch = raw.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
|
|
3536
|
+
if (closeMatch) {
|
|
3537
|
+
const fenceStr = closeMatch[1];
|
|
3538
|
+
if (fenceStr[0] === activeFence.char && fenceStr.length >= activeFence.length) {
|
|
3539
|
+
activeFence = null;
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
if (activeFence !== null) {
|
|
3545
|
+
return true;
|
|
3546
|
+
}
|
|
3547
|
+
for (let k = boundaryIdx - 1; k >= checkTop; k--) {
|
|
3548
|
+
const l = lines[k].trim();
|
|
3549
|
+
if (!l) continue;
|
|
3550
|
+
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
3551
|
+
const sliceBetween = lines.slice(k + 1, boundaryIdx);
|
|
3552
|
+
let optCount = 0;
|
|
3553
|
+
let hasFooter = false;
|
|
3554
|
+
let inDiff = false;
|
|
3555
|
+
for (const raw of sliceBetween) {
|
|
3556
|
+
const s = raw.trim();
|
|
3557
|
+
if (!s) continue;
|
|
3558
|
+
if (inDiff) {
|
|
3559
|
+
if (/^[ +\-\\]/.test(raw)) {
|
|
3560
|
+
continue;
|
|
3561
|
+
}
|
|
3562
|
+
if (/^(?:diff\s+--git\b|@@\s+-\d+|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|index\s+[0-9a-f]{7,})/i.test(s)) {
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
if (/^●\s+(?:Edit completed|[A-Z])/i.test(raw)) {
|
|
3566
|
+
inDiff = false;
|
|
3567
|
+
continue;
|
|
3568
|
+
}
|
|
3569
|
+
if (/^Do you want to proceed\?/i.test(raw)) {
|
|
3570
|
+
inDiff = false;
|
|
3571
|
+
continue;
|
|
3572
|
+
}
|
|
3573
|
+
inDiff = false;
|
|
3574
|
+
}
|
|
3575
|
+
if (/^(?:diff\s+--git\b|@@\s+-\d+|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|index\s+[0-9a-f]{7,})/i.test(s)) {
|
|
3576
|
+
inDiff = true;
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
if (/^●\s+(?:Edit completed|[A-Z])/i.test(raw)) {
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
if (/^[>\s]*[1-9]\.\s+\S/.test(s)) optCount++;
|
|
3583
|
+
if (/esc to cancel\b|enter Select\b|[↑↓/\s]*Navigate\b/i.test(s)) hasFooter = true;
|
|
3584
|
+
}
|
|
3585
|
+
if (optCount < 2 || !hasFooter) {
|
|
3586
|
+
return true;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
return false;
|
|
3591
|
+
}
|
|
3592
|
+
function isStandaloneFileAccessContext(lines, questionIdx) {
|
|
3593
|
+
const windowEnd = Math.min(lines.length, questionIdx + 15);
|
|
3594
|
+
let hasFileAccessOption = false;
|
|
3595
|
+
for (let j = questionIdx + 1; j < windowEnd; j++) {
|
|
3596
|
+
const l = lines[j].trim();
|
|
3597
|
+
if (!l) continue;
|
|
3598
|
+
if (/\benter\s+Select\b/i.test(l) || /\benter\s+to\s+(?:select|submit)\b/i.test(l)) {
|
|
3599
|
+
return false;
|
|
3600
|
+
}
|
|
3601
|
+
if (/Do you want to proceed\?/i.test(l) || /Requesting permission for:/i.test(l)) {
|
|
3602
|
+
return false;
|
|
3603
|
+
}
|
|
3604
|
+
if (/\b(?:allow|deny)\s+access\b/i.test(l)) {
|
|
3605
|
+
hasFileAccessOption = true;
|
|
3606
|
+
}
|
|
3607
|
+
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
3608
|
+
break;
|
|
3609
|
+
}
|
|
3610
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l)) {
|
|
3611
|
+
break;
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
const start = Math.max(0, questionIdx - 15);
|
|
3615
|
+
let actionIdx = -1;
|
|
3616
|
+
let headingIdx = -1;
|
|
3617
|
+
for (let j = questionIdx - 1; j >= start; j--) {
|
|
3618
|
+
const l = lines[j].trim();
|
|
3619
|
+
if (!l) continue;
|
|
3620
|
+
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l) || /Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l) || /Question \d+\/\d+:/i.test(l)) {
|
|
3621
|
+
return false;
|
|
3622
|
+
}
|
|
3623
|
+
if (/^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
3624
|
+
break;
|
|
3625
|
+
}
|
|
3626
|
+
if (/^[│\s]*[❯>]\s*$/.test(l)) {
|
|
3627
|
+
break;
|
|
3628
|
+
}
|
|
3629
|
+
if (/^\s*(?:Read|Write|Edit|Access):\s*\S/i.test(l)) {
|
|
3630
|
+
actionIdx = j;
|
|
3631
|
+
for (let k = j - 1; k >= start; k--) {
|
|
3632
|
+
const prev = lines[k].trim();
|
|
3633
|
+
if (!prev) continue;
|
|
3634
|
+
if (/^\s*File access\s*$/i.test(prev)) {
|
|
3635
|
+
headingIdx = k;
|
|
3636
|
+
}
|
|
3637
|
+
break;
|
|
3638
|
+
}
|
|
3639
|
+
break;
|
|
3640
|
+
}
|
|
3641
|
+
if (/^\s*Reason:\s*\S/i.test(l)) {
|
|
3642
|
+
continue;
|
|
3643
|
+
}
|
|
3644
|
+
if (/^[⚠⚠️]\s*/.test(l)) {
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
if (/^\s*File access\s*$/i.test(l)) {
|
|
3648
|
+
headingIdx = j;
|
|
3649
|
+
break;
|
|
3650
|
+
}
|
|
3651
|
+
return false;
|
|
3652
|
+
}
|
|
3653
|
+
if (actionIdx < 0 && headingIdx < 0) {
|
|
3654
|
+
return false;
|
|
3655
|
+
}
|
|
3656
|
+
if (headingIdx < 0 && !hasFileAccessOption) {
|
|
3657
|
+
return false;
|
|
3658
|
+
}
|
|
3659
|
+
const topAnchor = headingIdx >= 0 ? headingIdx : actionIdx;
|
|
3660
|
+
const checkTop = Math.max(0, topAnchor - 25);
|
|
3661
|
+
for (let j = topAnchor - 1; j >= checkTop; j--) {
|
|
3662
|
+
const l = lines[j].trim();
|
|
3663
|
+
if (!l) continue;
|
|
3664
|
+
if (/Question \d+\/\d+:/i.test(l)) {
|
|
3665
|
+
return false;
|
|
3666
|
+
}
|
|
3667
|
+
if (/Requesting permission for:/i.test(l) || /Do you want to proceed\?/i.test(l)) {
|
|
3668
|
+
return false;
|
|
3669
|
+
}
|
|
3670
|
+
if (/^(?:diff\s+--git|index\s+[0-9a-f]{7,}|---\s+(?:[ab]\/|\/dev\/null)|\+\+\+\s+(?:[ab]\/|\/dev\/null)|@@\s+-\d+)/i.test(l) || /^ {0,3}(`{3,}|~{3,})(?:[a-z0-9_+-]+)?\s*$/i.test(l)) {
|
|
3671
|
+
return false;
|
|
3672
|
+
}
|
|
3673
|
+
if (/^[▶▸\s]*Thought|^●\s*/i.test(l) || /^[│\s]*[❯>]\s*$/.test(l) || /^[─━│╭╰╮╯\s]*$/.test(l) && l.length > 5) {
|
|
3674
|
+
if (isOpenEnclosingContext(lines, checkTop, topAnchor, j)) {
|
|
3675
|
+
return false;
|
|
3676
|
+
}
|
|
3677
|
+
break;
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
return true;
|
|
3681
|
+
}
|
|
3151
3682
|
function detectActivePromptKind(snapshot) {
|
|
3152
3683
|
const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
|
|
3153
3684
|
const lines = stripped.trimEnd().split("\n");
|
|
3154
3685
|
const windowStart = Math.max(0, lines.length - 30);
|
|
3155
3686
|
const approvalRe = /Requesting permission for:/i;
|
|
3156
|
-
const questionRe = /Question \d+\/\d+:/i;
|
|
3157
3687
|
const fileAccessRe = /^\s*File access\s*$/i;
|
|
3688
|
+
const fileAccessQuestionRe = /^\s*Allow access to this file\?\s*$/i;
|
|
3689
|
+
const lineInFence = computeFenceMask(lines);
|
|
3158
3690
|
let lastApprovalLineIdx = -1;
|
|
3159
3691
|
let lastQuestionLineIdx = -1;
|
|
3160
3692
|
let lastFileAccessLineIdx = -1;
|
|
@@ -3162,14 +3694,22 @@ function detectActivePromptKind(snapshot) {
|
|
|
3162
3694
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3163
3695
|
const line = lines[i];
|
|
3164
3696
|
if (i >= windowStart) {
|
|
3165
|
-
if (
|
|
3166
|
-
lastApprovalLineIdx
|
|
3697
|
+
if (approvalRe.test(line)) {
|
|
3698
|
+
if (!lineInFence[i] && lastApprovalLineIdx < 0) {
|
|
3699
|
+
lastApprovalLineIdx = i;
|
|
3700
|
+
}
|
|
3167
3701
|
}
|
|
3168
|
-
if (
|
|
3169
|
-
lastQuestionLineIdx
|
|
3702
|
+
if (isQuestionHeaderLine(line)) {
|
|
3703
|
+
if (!lineInFence[i] && lastQuestionLineIdx < 0) {
|
|
3704
|
+
lastQuestionLineIdx = i;
|
|
3705
|
+
}
|
|
3170
3706
|
}
|
|
3171
|
-
if (lastFileAccessLineIdx < 0 &&
|
|
3172
|
-
|
|
3707
|
+
if (lastFileAccessLineIdx < 0 && !lineInFence[i]) {
|
|
3708
|
+
if (fileAccessRe.test(line)) {
|
|
3709
|
+
lastFileAccessLineIdx = i;
|
|
3710
|
+
} else if (fileAccessQuestionRe.test(line) && isStandaloneFileAccessContext(lines, i)) {
|
|
3711
|
+
lastFileAccessLineIdx = i;
|
|
3712
|
+
}
|
|
3173
3713
|
}
|
|
3174
3714
|
}
|
|
3175
3715
|
if (lastSystemErrorLineIdx < 0 && isSystemErrorBannerLineInContext(lines, i)) {
|
|
@@ -3180,12 +3720,14 @@ function detectActivePromptKind(snapshot) {
|
|
|
3180
3720
|
if (lastApprovalLineIdx < 0 && lastQuestionLineIdx < 0) {
|
|
3181
3721
|
for (let i = lines.length - 1; i >= windowStart; i--) {
|
|
3182
3722
|
if (/Do you want to proceed\?/i.test(lines[i])) {
|
|
3183
|
-
|
|
3184
|
-
|
|
3723
|
+
if (!lineInFence[i]) {
|
|
3724
|
+
lastApprovalLineIdx = i;
|
|
3725
|
+
break;
|
|
3726
|
+
}
|
|
3185
3727
|
}
|
|
3186
3728
|
}
|
|
3187
3729
|
}
|
|
3188
|
-
|
|
3730
|
+
let latestChooserIdx = Math.max(
|
|
3189
3731
|
lastApprovalLineIdx,
|
|
3190
3732
|
lastQuestionLineIdx,
|
|
3191
3733
|
lastFileAccessLineIdx
|
|
@@ -3194,15 +3736,32 @@ function detectActivePromptKind(snapshot) {
|
|
|
3194
3736
|
if (latestChooserIdx >= 0) {
|
|
3195
3737
|
const remainingLines = lines.slice(lastSystemErrorLineIdx + 1).join("\n");
|
|
3196
3738
|
if (latestChooserIdx === lastQuestionLineIdx) {
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3739
|
+
let hasQuestionOptions = false;
|
|
3740
|
+
let hasQuestionFooter = false;
|
|
3741
|
+
let promptEndIdx = lastSystemErrorLineIdx;
|
|
3742
|
+
for (let j = lastSystemErrorLineIdx + 1; j < lines.length; j++) {
|
|
3743
|
+
if (!lineInFence[j]) {
|
|
3744
|
+
const trimmed = lines[j].trim();
|
|
3745
|
+
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) {
|
|
3746
|
+
hasQuestionOptions = true;
|
|
3747
|
+
promptEndIdx = j;
|
|
3748
|
+
} else if (isQuestionFooterLine(lines[j])) {
|
|
3749
|
+
hasQuestionFooter = true;
|
|
3750
|
+
promptEndIdx = j;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
const headerLine = lines[lastQuestionLineIdx].trim();
|
|
3755
|
+
const hasHeaderHint = /\((?:Use\s+arrow\s+keys|Use\s+\S+\s+keys)[^)]*\)/i.test(headerLine);
|
|
3756
|
+
const footerSatisfied = hasQuestionFooter || hasHeaderHint;
|
|
3757
|
+
const hasComposer = hasTrailingAgyComposer(lines, promptEndIdx, lineInFence);
|
|
3758
|
+
if (hasQuestionOptions && footerSatisfied && !hasComposer) {
|
|
3200
3759
|
return "question";
|
|
3201
3760
|
}
|
|
3202
3761
|
} else if (latestChooserIdx === lastFileAccessLineIdx) {
|
|
3203
|
-
const hasFileAccessQuestion =
|
|
3762
|
+
const hasFileAccessQuestion = /^\s*Allow access to this file\?\s*$/im.test(remainingLines);
|
|
3204
3763
|
const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(remainingLines);
|
|
3205
|
-
const hasApprovalFooter =
|
|
3764
|
+
const hasApprovalFooter = /^\s*(?:esc to cancel\b|[↑↓/\s]*Navigate\b|ctrl\+r\s+Review\b)/im.test(remainingLines);
|
|
3206
3765
|
if (hasFileAccessQuestion && hasApprovalOptions && hasApprovalFooter) {
|
|
3207
3766
|
return "file_access";
|
|
3208
3767
|
}
|
|
@@ -3224,6 +3783,20 @@ function detectActivePromptKind(snapshot) {
|
|
|
3224
3783
|
return "file_access";
|
|
3225
3784
|
}
|
|
3226
3785
|
if (latestChooserIdx === lastQuestionLineIdx) {
|
|
3786
|
+
let promptEndIdx = lastQuestionLineIdx;
|
|
3787
|
+
for (let j = lastQuestionLineIdx + 1; j < lines.length; j++) {
|
|
3788
|
+
if (!lineInFence[j]) {
|
|
3789
|
+
if (isAgyStatusFooterLine(lines[j].trim()) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(lines[j].trim()) || isComposerBoxAt(lines, j)) {
|
|
3790
|
+
break;
|
|
3791
|
+
}
|
|
3792
|
+
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(lines[j].trim()) || isQuestionFooterLine(lines[j])) {
|
|
3793
|
+
promptEndIdx = j;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
if (hasTrailingAgyComposer(lines, promptEndIdx, lineInFence)) {
|
|
3798
|
+
return null;
|
|
3799
|
+
}
|
|
3227
3800
|
return "question";
|
|
3228
3801
|
}
|
|
3229
3802
|
return "approval";
|
|
@@ -3574,12 +4147,45 @@ function extractSystemErrorPrompt(snapshot) {
|
|
|
3574
4147
|
const fullLine = errorId ? `\u26A0\uFE0F Antigravity error: ${message} (Error ID: ${errorId})` : `\u26A0\uFE0F Antigravity error: ${message}`;
|
|
3575
4148
|
return { message, errorId, fullLine };
|
|
3576
4149
|
}
|
|
4150
|
+
function isQuestionHeaderLine(line) {
|
|
4151
|
+
if (!line) return false;
|
|
4152
|
+
const trimmed = line.trim();
|
|
4153
|
+
if (isAgyStatusFooterLine(trimmed)) return false;
|
|
4154
|
+
if (/^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b/i.test(trimmed)) {
|
|
4155
|
+
return false;
|
|
4156
|
+
}
|
|
4157
|
+
return /^\s*(?:[❯>]\s*)?\?\s+(?!for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b)\S/i.test(trimmed) || /^\s*(?:[❯>]\s*)?(?:\?\s*)?Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*\S/i.test(trimmed);
|
|
4158
|
+
}
|
|
4159
|
+
function isQuestionFooterLine(line) {
|
|
4160
|
+
if (!line) return false;
|
|
4161
|
+
const trimmed = line.trim();
|
|
4162
|
+
if (/^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b/i.test(trimmed)) {
|
|
4163
|
+
return false;
|
|
4164
|
+
}
|
|
4165
|
+
if (isComposedStatusFooter(trimmed)) return false;
|
|
4166
|
+
if (/^\s*esc to cancel\b/i.test(trimmed) && /(?:Gemini|Flash|Pro|Claude|Sonnet|Opus|GPT|auto|high|low|medium|credits|tasks|subagents?|quota)/i.test(trimmed)) {
|
|
4167
|
+
return false;
|
|
4168
|
+
}
|
|
4169
|
+
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) return false;
|
|
4170
|
+
if (isQuestionHeaderLine(trimmed)) return false;
|
|
4171
|
+
return /^(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip)(?:\s*[·│,]\s*(?:enter|<enter>)\s+(?:to\s+)?select)?$/i.test(trimmed) || /^(?:enter|<enter>)\s+(?:to\s+)?select(?:\s*[·│,]\s*(?:esc|<esc>)\s+(?:to\s+)?(?:cancel|skip))?$/i.test(trimmed) || /^(?:↑\/↓|↑|↓|\^|v)\s*(?:\/|\s+)?Navigate(?:\s*[·│,]\s*(?:enter|<enter>)?\s*(?:Select|to select))?(?:\s*[·│,]\s*(?:esc|<esc>)?\s*(?:Skip|Cancel))?$/i.test(trimmed) || /^press\s+<?(?:enter|return|space)>?\s+to\s+select$/i.test(trimmed) || /^space\s+to\s+select$/i.test(trimmed) || /^(?:\(?use\s+arrow\s+keys(?:\s+to\s+navigate)?\)?)$/i.test(trimmed) || /^waiting(?:\.\.\.)?$/i.test(trimmed);
|
|
4172
|
+
}
|
|
3577
4173
|
function extractQuestionHeader(snapshot) {
|
|
3578
4174
|
const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
|
|
3579
|
-
const
|
|
3580
|
-
|
|
3581
|
-
const
|
|
3582
|
-
|
|
4175
|
+
const lines = stripped.split("\n");
|
|
4176
|
+
const lineInFence = computeFenceMask(lines);
|
|
4177
|
+
const prefixRe = /^\s*(?:[❯>]\s*)?(?:\?\s*(?:Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)?|Question\b(?:\s+\d+(?:[\/:of\s]+\d+)?)?:\s*)/i;
|
|
4178
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
4179
|
+
if (lineInFence[i]) continue;
|
|
4180
|
+
const line = lines[i];
|
|
4181
|
+
if (isQuestionHeaderLine(line)) {
|
|
4182
|
+
const fullLine = line.trim();
|
|
4183
|
+
let body = fullLine.replace(prefixRe, "").trim();
|
|
4184
|
+
body = body.replace(/\s*\((?:Use\s+arrow\s+keys|Use\s+\S+\s+keys)[^)]*\)\s*$/i, "").trim();
|
|
4185
|
+
return { fullLine, body };
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
return null;
|
|
3583
4189
|
}
|
|
3584
4190
|
function hashPromptSnapshot(snapshot) {
|
|
3585
4191
|
let normalized = snapshot.replace(ANSI_ESCAPE_REGEX, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
@@ -3599,8 +4205,7 @@ function sleep(ms) {
|
|
|
3599
4205
|
});
|
|
3600
4206
|
}
|
|
3601
4207
|
function isQuestionUISnapshot(snapshot) {
|
|
3602
|
-
|
|
3603
|
-
return /Question \d+\/\d+:/i.test(stripped);
|
|
4208
|
+
return extractQuestionHeader(snapshot) !== null;
|
|
3604
4209
|
}
|
|
3605
4210
|
|
|
3606
4211
|
// src/approval-detector.ts
|
|
@@ -3656,7 +4261,7 @@ function parseFileAccessSnapshot(stripped, originalSnapshot, prompt) {
|
|
|
3656
4261
|
const lines = stripped.split("\n");
|
|
3657
4262
|
let questionIndex = -1;
|
|
3658
4263
|
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
3659
|
-
if (
|
|
4264
|
+
if (/^\s*Allow access to this file\?\s*$/i.test(lines[index])) {
|
|
3660
4265
|
questionIndex = index;
|
|
3661
4266
|
break;
|
|
3662
4267
|
}
|
|
@@ -3721,9 +4326,11 @@ function parseApprovalUISnapshot(stripped, originalSnapshot) {
|
|
|
3721
4326
|
}
|
|
3722
4327
|
function parseQuestionSnapshot(stripped, originalSnapshot, header) {
|
|
3723
4328
|
const lines = stripped.split("\n");
|
|
4329
|
+
const lineInFence = computeFenceMask(lines);
|
|
3724
4330
|
let headerIdx = -1;
|
|
3725
4331
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3726
|
-
if (
|
|
4332
|
+
if (lineInFence[i]) continue;
|
|
4333
|
+
if (isQuestionHeaderLine(lines[i])) {
|
|
3727
4334
|
headerIdx = i;
|
|
3728
4335
|
break;
|
|
3729
4336
|
}
|
|
@@ -3731,7 +4338,10 @@ function parseQuestionSnapshot(stripped, originalSnapshot, header) {
|
|
|
3731
4338
|
const belowHeader = headerIdx >= 0 ? lines.slice(headerIdx + 1).join("\n") : stripped;
|
|
3732
4339
|
const options = parseOptions(belowHeader);
|
|
3733
4340
|
if (options.length < 2) return null;
|
|
3734
|
-
const
|
|
4341
|
+
const normalizedHeader = header.fullLine.replace(/^[❯>\s*•-]+\s*/, "").trim();
|
|
4342
|
+
const hasArrowKeyHint = /\((?:Use\s+arrow\s+keys|Use\s+\S+\s+keys)[^)]*\)/i.test(normalizedHeader);
|
|
4343
|
+
const isBaseNumberedWizard = /^Question\s+\d+\/\d+:/i.test(normalizedHeader) && !hasArrowKeyHint;
|
|
4344
|
+
const submitMap = isBaseNumberedWizard ? buildQuestionSubmitMap(options) : {};
|
|
3735
4345
|
const body = extractBodyBetweenHeaderAndFirstOption(belowHeader);
|
|
3736
4346
|
return {
|
|
3737
4347
|
kind: "question",
|
|
@@ -3747,16 +4357,11 @@ function parseQuestionSnapshot(stripped, originalSnapshot, header) {
|
|
|
3747
4357
|
}
|
|
3748
4358
|
function extractBodyBetweenHeaderAndFirstOption(belowHeader) {
|
|
3749
4359
|
const lines = belowHeader.split("\n");
|
|
3750
|
-
const optionRegex = /^[>\s]*([1-9])
|
|
3751
|
-
const
|
|
4360
|
+
const optionRegex = /^[>\s❯]*([1-9])[.)]\s+(.+?)\s*$/;
|
|
4361
|
+
const lineInFence = computeFenceMask(lines);
|
|
3752
4362
|
let firstOptionIdx = -1;
|
|
3753
|
-
let inFence = false;
|
|
3754
4363
|
for (let i = 0; i < lines.length; i++) {
|
|
3755
|
-
if (
|
|
3756
|
-
inFence = !inFence;
|
|
3757
|
-
continue;
|
|
3758
|
-
}
|
|
3759
|
-
if (inFence) continue;
|
|
4364
|
+
if (lineInFence[i]) continue;
|
|
3760
4365
|
if (optionRegex.test(lines[i])) {
|
|
3761
4366
|
firstOptionIdx = i;
|
|
3762
4367
|
break;
|
|
@@ -3766,8 +4371,19 @@ function extractBodyBetweenHeaderAndFirstOption(belowHeader) {
|
|
|
3766
4371
|
return lines.slice(0, firstOptionIdx).join("\n").trim();
|
|
3767
4372
|
}
|
|
3768
4373
|
function parseOptions(snapshot) {
|
|
3769
|
-
const
|
|
3770
|
-
const
|
|
4374
|
+
const lines = snapshot.split("\n");
|
|
4375
|
+
const lineInFence = computeFenceMask(lines);
|
|
4376
|
+
const filteredLines = [];
|
|
4377
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4378
|
+
if (!lineInFence[i]) {
|
|
4379
|
+
filteredLines.push(lines[i]);
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
while (filteredLines.length > 0 && filteredLines[filteredLines.length - 1].trim() === "") {
|
|
4383
|
+
filteredLines.pop();
|
|
4384
|
+
}
|
|
4385
|
+
const recent = filteredLines.slice(-30);
|
|
4386
|
+
const optionRegex = /^[>\s❯]*([1-9])[.)]\s+(.+?)\s*$/;
|
|
3771
4387
|
const found = /* @__PURE__ */ new Map();
|
|
3772
4388
|
for (const line of recent) {
|
|
3773
4389
|
const m = line.match(optionRegex);
|
|
@@ -3871,6 +4487,7 @@ function extractIdentity(headerText) {
|
|
|
3871
4487
|
return { command: trimmed };
|
|
3872
4488
|
}
|
|
3873
4489
|
function guessToolTypeFromIdentity(c) {
|
|
4490
|
+
if (c.kind === "question") return "ASK_QUESTION";
|
|
3874
4491
|
if (c.command) return "RUN_COMMAND";
|
|
3875
4492
|
if (c.filePath) return "CODE_ACTION";
|
|
3876
4493
|
return "GENERIC";
|
|
@@ -3977,11 +4594,12 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
3977
4594
|
intentByteOffset: 0,
|
|
3978
4595
|
// Best-effort tool-type inference from the candidate. For shell
|
|
3979
4596
|
// commands → RUN_COMMAND. For edit-style headers with a file
|
|
3980
|
-
// path → CODE_ACTION. Default: GENERIC.
|
|
4597
|
+
// path → CODE_ACTION. For questions → ASK_QUESTION. Default: GENERIC.
|
|
3981
4598
|
toolType: guessToolTypeFromIdentity(candidate),
|
|
3982
4599
|
command: candidate.command,
|
|
3983
4600
|
filePath: candidate.filePath,
|
|
3984
|
-
rawToolName: candidate.command ?? candidate.filePath ?? "unknown",
|
|
4601
|
+
rawToolName: candidate.kind === "question" ? "ask_question" : candidate.command ?? candidate.filePath ?? "unknown",
|
|
4602
|
+
questionText: candidate.kind === "question" ? candidate.headerText : void 0,
|
|
3985
4603
|
promptId,
|
|
3986
4604
|
startedAt: Date.now(),
|
|
3987
4605
|
intentCreatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4303,6 +4921,21 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
4303
4921
|
return `${call.conversationId}|${call.intentStepIndex}|${call.toolCallIndex}|${call.intentByteOffset}`;
|
|
4304
4922
|
}
|
|
4305
4923
|
findMatchingPendingCall(candidate, conversationId) {
|
|
4924
|
+
if (candidate.kind === "question") {
|
|
4925
|
+
const questionCalls = Array.from(this.pendingCalls.values()).filter((c) => c.conversationId === conversationId).filter(
|
|
4926
|
+
(c) => c.toolType === "ASK_QUESTION" || c.rawToolName?.toLowerCase() === "ask_question"
|
|
4927
|
+
).sort((a, b) => a.intentStepIndex - b.intentStepIndex);
|
|
4928
|
+
if (questionCalls.length > 0 && candidate.headerText) {
|
|
4929
|
+
const normHeader = candidate.headerText.toLowerCase().trim();
|
|
4930
|
+
const textMatch = questionCalls.find((c) => {
|
|
4931
|
+
if (!c.questionText) return false;
|
|
4932
|
+
const normQ = c.questionText.toLowerCase().trim();
|
|
4933
|
+
return normHeader === normQ || normHeader.includes(normQ) || normQ.includes(normHeader);
|
|
4934
|
+
});
|
|
4935
|
+
if (textMatch) return textMatch;
|
|
4936
|
+
}
|
|
4937
|
+
return null;
|
|
4938
|
+
}
|
|
4306
4939
|
if (candidate.command) {
|
|
4307
4940
|
const matches = Array.from(this.pendingCalls.values()).filter((c) => c.conversationId === conversationId).filter((c) => c.command && c.command === candidate.command).sort((a, b) => a.intentStepIndex - b.intentStepIndex);
|
|
4308
4941
|
if (matches.length > 0) return matches[0];
|
|
@@ -4605,6 +5238,33 @@ var PromptResponder = class {
|
|
|
4605
5238
|
const before = await this.paneObserver.probeApprovalUIActive();
|
|
4606
5239
|
if (!before.probeSucceeded) return { ok: false, reason: "prompt-probe-failed" };
|
|
4607
5240
|
if (!before.active) return { ok: true };
|
|
5241
|
+
if (before.ambiguous) {
|
|
5242
|
+
return {
|
|
5243
|
+
ok: false,
|
|
5244
|
+
reason: "ambiguous-prompt-active",
|
|
5245
|
+
details: "masked/ambiguous prompt cannot be dismissed via Escape from mobile"
|
|
5246
|
+
};
|
|
5247
|
+
}
|
|
5248
|
+
if (before.kind === "question") {
|
|
5249
|
+
let isActionableWizard = false;
|
|
5250
|
+
try {
|
|
5251
|
+
const snapshot = await this.paneObserver.captureSnapshot();
|
|
5252
|
+
if (snapshot) {
|
|
5253
|
+
const parsed = parseApprovalSnapshot(snapshot);
|
|
5254
|
+
if (parsed && parsed.kind === "question" && Object.keys(parsed.submitMap || {}).length > 0) {
|
|
5255
|
+
isActionableWizard = true;
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
} catch {
|
|
5259
|
+
}
|
|
5260
|
+
if (!isActionableWizard) {
|
|
5261
|
+
return {
|
|
5262
|
+
ok: false,
|
|
5263
|
+
reason: "display-only-question-active",
|
|
5264
|
+
details: "active question prompt cannot be dismissed via Escape from mobile"
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
4608
5268
|
await this.sendKey(target, "Escape");
|
|
4609
5269
|
await delay(250);
|
|
4610
5270
|
const after = await this.paneObserver.probeApprovalUIActive();
|
|
@@ -4981,6 +5641,7 @@ function mapTranscriptEvent(emit, sessionId, notBeforeMs) {
|
|
|
4981
5641
|
conversationId: emit.conversationId,
|
|
4982
5642
|
command: extractCommandFromArgs(name, args),
|
|
4983
5643
|
filePath: extractFilePathFromArgs(name, args),
|
|
5644
|
+
questionText: extractQuestionFromArgs(name, args),
|
|
4984
5645
|
cwd: extractCwdFromArgs(args),
|
|
4985
5646
|
rawToolName: name,
|
|
4986
5647
|
startedAt: Date.now(),
|
|
@@ -5082,8 +5743,9 @@ function hookToolNameToTranscriptType(hookName) {
|
|
|
5082
5743
|
case "MANAGE_TASK":
|
|
5083
5744
|
case "SCHEDULE":
|
|
5084
5745
|
case "ASK_PERMISSION":
|
|
5085
|
-
case "ASK_QUESTION":
|
|
5086
5746
|
return "GENERIC";
|
|
5747
|
+
case "ASK_QUESTION":
|
|
5748
|
+
return "ASK_QUESTION";
|
|
5087
5749
|
default:
|
|
5088
5750
|
return upper;
|
|
5089
5751
|
}
|
|
@@ -5119,6 +5781,32 @@ function extractFilePathFromArgs(toolName, args) {
|
|
|
5119
5781
|
}
|
|
5120
5782
|
return void 0;
|
|
5121
5783
|
}
|
|
5784
|
+
function extractQuestionFromArgs(toolName, args) {
|
|
5785
|
+
if (!args || typeof args !== "object") return void 0;
|
|
5786
|
+
const tn = (toolName || "").toLowerCase().trim();
|
|
5787
|
+
const isQuestionTool = tn === "ask_question" || tn === "question" || tn === "ask";
|
|
5788
|
+
if (!isQuestionTool) return void 0;
|
|
5789
|
+
const a = args;
|
|
5790
|
+
if (typeof a.question === "string" && a.question.trim().length > 0) {
|
|
5791
|
+
return stripWrappingQuotes2(a.question.trim());
|
|
5792
|
+
}
|
|
5793
|
+
if (typeof a.prompt === "string" && a.prompt.trim().length > 0) {
|
|
5794
|
+
return stripWrappingQuotes2(a.prompt.trim());
|
|
5795
|
+
}
|
|
5796
|
+
if (Array.isArray(a.questions) && a.questions.length > 0) {
|
|
5797
|
+
const first = a.questions[0];
|
|
5798
|
+
if (typeof first === "string" && first.trim().length > 0) {
|
|
5799
|
+
return stripWrappingQuotes2(first.trim());
|
|
5800
|
+
}
|
|
5801
|
+
if (first && typeof first === "object") {
|
|
5802
|
+
const q = first.question;
|
|
5803
|
+
if (typeof q === "string" && q.trim().length > 0) {
|
|
5804
|
+
return stripWrappingQuotes2(q.trim());
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
return void 0;
|
|
5809
|
+
}
|
|
5122
5810
|
function extractCwdFromArgs(args) {
|
|
5123
5811
|
if (!args || typeof args !== "object") return void 0;
|
|
5124
5812
|
const a = args;
|
|
@@ -5803,6 +6491,8 @@ var McpServer = class _McpServer {
|
|
|
5803
6491
|
() => reject(new Error("status read timed out")),
|
|
5804
6492
|
{ once: true }
|
|
5805
6493
|
));
|
|
6494
|
+
timeout.catch(() => {
|
|
6495
|
+
});
|
|
5806
6496
|
const subscription = await Promise.race([this.appSyncClient.getSubscriptionStatus(signal), timeout]);
|
|
5807
6497
|
if (this.appSyncClient.getCurrentUserId() !== statusUserId) throw new Error("account changed during read");
|
|
5808
6498
|
const sync = await Promise.race([this.appSyncClient.getCompanionSyncState(signal), timeout]);
|
|
@@ -7409,7 +8099,8 @@ var McpServer = class _McpServer {
|
|
|
7409
8099
|
this.raiseSuppressedBadge(session, cand.snapshotHash);
|
|
7410
8100
|
return;
|
|
7411
8101
|
}
|
|
7412
|
-
const
|
|
8102
|
+
const isActionable = Object.keys(parsed.submitMap || {}).length > 0;
|
|
8103
|
+
const e1rec = this.newE1Raise(session.sessionId, isActionable);
|
|
7413
8104
|
const emitted = this.approvalDetector.emitPaneOnlyPrompt(parsed, activeConvId, e1rec.promptId);
|
|
7414
8105
|
if (emitted) {
|
|
7415
8106
|
if (cand.snapshotHash) {
|
|
@@ -7458,10 +8149,11 @@ var McpServer = class _McpServer {
|
|
|
7458
8149
|
});
|
|
7459
8150
|
return;
|
|
7460
8151
|
}
|
|
8152
|
+
const isDisplayOnly = Object.keys(state.submitMap || {}).length === 0;
|
|
7461
8153
|
const e1existing = this.e1PromptRaises.get(state.promptId);
|
|
7462
|
-
const e1rec = e1existing ? e1existing.record : this.newE1Raise(session.sessionId,
|
|
8154
|
+
const e1rec = e1existing ? e1existing.record : this.newE1Raise(session.sessionId, !isDisplayOnly);
|
|
7463
8155
|
if (!e1existing) state.promptId = e1rec.promptId;
|
|
7464
|
-
const optionsArr = state.paneOptions ? state.paneOptions.map((o) => ({ number: o.number, text: o.text })) : Object.keys(state.submitMap).map((n) => ({ number: n }));
|
|
8156
|
+
const optionsArr = isDisplayOnly ? [] : state.paneOptions ? state.paneOptions.map((o) => ({ number: o.number, text: o.text })) : Object.keys(state.submitMap).map((n) => ({ number: n }));
|
|
7465
8157
|
const diffParsed = parseFencedDiffFromBody(
|
|
7466
8158
|
state.body,
|
|
7467
8159
|
state.paneDisplayHeader
|
|
@@ -7729,6 +8421,49 @@ var McpServer = class _McpServer {
|
|
|
7729
8421
|
promptContent = intent.followUpText;
|
|
7730
8422
|
await new Promise((resolve2) => setTimeout(resolve2, 250));
|
|
7731
8423
|
} else {
|
|
8424
|
+
const hasDisplayOnlyPrompt = pendingPrompts.some(
|
|
8425
|
+
(p) => Object.keys(p.submitMap || {}).length === 0
|
|
8426
|
+
);
|
|
8427
|
+
let livePaneDisplayOnly = false;
|
|
8428
|
+
let livePaneBlockedReason = null;
|
|
8429
|
+
if (!hasDisplayOnlyPrompt && this.paneObserver) {
|
|
8430
|
+
try {
|
|
8431
|
+
const probe = await this.paneObserver.probeApprovalUIActive();
|
|
8432
|
+
if (!probe.probeSucceeded) {
|
|
8433
|
+
livePaneBlockedReason = "Live terminal state could not be verified safely. Please interact directly in your terminal.";
|
|
8434
|
+
} else if (probe.ambiguous) {
|
|
8435
|
+
livePaneBlockedReason = "An ambiguous prompt is active in your terminal. Please interact directly in your terminal.";
|
|
8436
|
+
} else if (probe.active && probe.kind === "question") {
|
|
8437
|
+
let isActionableWizard = false;
|
|
8438
|
+
try {
|
|
8439
|
+
const liveSnapshot = await this.paneObserver.captureSnapshot();
|
|
8440
|
+
if (liveSnapshot) {
|
|
8441
|
+
const parsed = parseApprovalSnapshot(liveSnapshot);
|
|
8442
|
+
if (parsed && parsed.kind === "question" && Object.keys(parsed.submitMap || {}).length > 0) {
|
|
8443
|
+
isActionableWizard = true;
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
8446
|
+
} catch {
|
|
8447
|
+
}
|
|
8448
|
+
if (!isActionableWizard) {
|
|
8449
|
+
livePaneDisplayOnly = true;
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
} catch {
|
|
8453
|
+
livePaneBlockedReason = "Live terminal state could not be verified safely. Please interact directly in your terminal.";
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
if (livePaneBlockedReason) {
|
|
8457
|
+
await this.emitPromptSafetyNotification(session, livePaneBlockedReason);
|
|
8458
|
+
return;
|
|
8459
|
+
}
|
|
8460
|
+
if (hasDisplayOnlyPrompt || livePaneDisplayOnly) {
|
|
8461
|
+
await this.emitPromptSafetyNotification(
|
|
8462
|
+
session,
|
|
8463
|
+
"The active terminal question prompt is display-only on mobile. Please select an option directly in your terminal."
|
|
8464
|
+
);
|
|
8465
|
+
return;
|
|
8466
|
+
}
|
|
7732
8467
|
const dismissal = await this.promptResponder.dismissActivePrompt();
|
|
7733
8468
|
if (!dismissal.ok) {
|
|
7734
8469
|
await this.emitPromptSafetyNotification(
|
|
@@ -8357,16 +9092,19 @@ var __testing = {
|
|
|
8357
9092
|
SessionNotFoundError,
|
|
8358
9093
|
StoredAuthFailureError,
|
|
8359
9094
|
TERMINAL_SHUTDOWN_SIGNALS,
|
|
9095
|
+
TmuxPaneObserver,
|
|
8360
9096
|
__testing,
|
|
8361
9097
|
classifyEntrypoint,
|
|
8362
9098
|
classifyTmuxHasSessionError,
|
|
8363
9099
|
commandDigest,
|
|
8364
9100
|
detectActivePromptKind,
|
|
9101
|
+
extractFileAccessPrompt,
|
|
8365
9102
|
extractSystemErrorPrompt,
|
|
8366
9103
|
generateLaunchSessionId,
|
|
8367
9104
|
getActiveConversationFromCliLog,
|
|
8368
9105
|
isAgyStatusFooterLine,
|
|
8369
9106
|
isComposedStatusFooter,
|
|
9107
|
+
isPaneInputSafetyBlocked,
|
|
8370
9108
|
isSystemErrorActiveFromIndex,
|
|
8371
9109
|
looksLikePromptSnapshot,
|
|
8372
9110
|
parseApprovalSnapshot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.81",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"codevibe": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"node": ">=22.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@quantiya/codevibe-core": "^2.0.
|
|
49
|
+
"@quantiya/codevibe-core": "^2.0.74",
|
|
50
50
|
"chokidar": "^5.0.0",
|
|
51
51
|
"dotenv": "^16.6.1",
|
|
52
52
|
"express": "^5.1.0",
|