@quantiya/codevibe-antigravity-plugin 2.0.101 → 2.0.103
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/agy-hook.js +56 -18
- package/dist/server.js +295 -27
- package/package.json +1 -1
package/dist/agy-hook.js
CHANGED
|
@@ -100,8 +100,32 @@ function decodeBoolean(val) {
|
|
|
100
100
|
if (typeof val === "number") return val !== 0;
|
|
101
101
|
return false;
|
|
102
102
|
}
|
|
103
|
+
function sliceToSerializedBudget(rawText, buildCandidate, maxChars) {
|
|
104
|
+
if (maxChars <= 0) return null;
|
|
105
|
+
const emptyCandidate = buildCandidate("");
|
|
106
|
+
if (emptyCandidate.length > maxChars) {
|
|
107
|
+
return "[]".length <= maxChars ? "[]" : null;
|
|
108
|
+
}
|
|
109
|
+
const fullCandidate = buildCandidate(rawText);
|
|
110
|
+
if (fullCandidate.length <= maxChars) return fullCandidate;
|
|
111
|
+
let low = 0;
|
|
112
|
+
let high = rawText.length;
|
|
113
|
+
let best = emptyCandidate;
|
|
114
|
+
while (low <= high) {
|
|
115
|
+
const mid = low + high >>> 1;
|
|
116
|
+
const candidate = buildCandidate(rawText.slice(0, mid));
|
|
117
|
+
if (candidate.length <= maxChars) {
|
|
118
|
+
best = candidate;
|
|
119
|
+
low = mid + 1;
|
|
120
|
+
} else {
|
|
121
|
+
high = mid - 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return best;
|
|
125
|
+
}
|
|
103
126
|
function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyHint) {
|
|
104
127
|
try {
|
|
128
|
+
if (maxChars <= 0) return null;
|
|
105
129
|
let parsed = value;
|
|
106
130
|
if (typeof value === "string") {
|
|
107
131
|
const trimmed = value.trim();
|
|
@@ -121,15 +145,15 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
121
145
|
);
|
|
122
146
|
if (isOptionArray) {
|
|
123
147
|
const boundedOptions = parsed.slice(0, 30).map((opt, idx) => {
|
|
124
|
-
if (typeof opt === "string") return { number: String(idx + 1), text: opt
|
|
148
|
+
if (typeof opt === "string") return { number: String(idx + 1), text: opt };
|
|
125
149
|
if (typeof opt === "number") return { number: String(idx + 1), text: String(opt) };
|
|
126
150
|
if (opt && typeof opt === "object") {
|
|
127
151
|
const optObj = opt;
|
|
128
152
|
const num = typeof optObj.number !== "undefined" ? String(optObj.number).slice(0, 16) : String(idx + 1);
|
|
129
|
-
const txt = typeof optObj.text === "string" ? optObj.text
|
|
153
|
+
const txt = typeof optObj.text === "string" ? optObj.text : String(optObj.text ?? optObj.label ?? optObj.value ?? "");
|
|
130
154
|
return { number: num, text: txt };
|
|
131
155
|
}
|
|
132
|
-
return { number: String(idx + 1), text: String(opt ?? "")
|
|
156
|
+
return { number: String(idx + 1), text: String(opt ?? "") };
|
|
133
157
|
});
|
|
134
158
|
const str2 = JSON.stringify(boundedOptions);
|
|
135
159
|
if (str2.length <= maxChars) return str2;
|
|
@@ -139,38 +163,41 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
139
163
|
}
|
|
140
164
|
const firstOpt = boundedOptions[0];
|
|
141
165
|
if (firstOpt) {
|
|
142
|
-
|
|
143
|
-
|
|
166
|
+
return sliceToSerializedBudget(
|
|
167
|
+
firstOpt.text,
|
|
168
|
+
(safeText) => JSON.stringify([{ number: firstOpt.number, text: safeText }]),
|
|
169
|
+
maxChars
|
|
170
|
+
);
|
|
144
171
|
}
|
|
145
|
-
return
|
|
172
|
+
return "[]".length <= maxChars ? "[]" : null;
|
|
146
173
|
}
|
|
147
174
|
const bounded = parsed.slice(0, 20).map((item) => {
|
|
148
|
-
if (typeof item === "string") return { question: item
|
|
175
|
+
if (typeof item === "string") return { question: item, options: [] };
|
|
149
176
|
if (item && typeof item === "object") {
|
|
150
177
|
const res = {};
|
|
151
178
|
const obj = item;
|
|
152
|
-
if (typeof obj.question === "string") res.question = obj.question
|
|
179
|
+
if (typeof obj.question === "string") res.question = obj.question;
|
|
153
180
|
if (typeof obj.is_multi_select !== "undefined") res.is_multi_select = decodeBoolean(obj.is_multi_select);
|
|
154
181
|
if (typeof obj.isMultiSelect !== "undefined") res.isMultiSelect = decodeBoolean(obj.isMultiSelect);
|
|
155
182
|
if (Array.isArray(obj.options)) {
|
|
156
|
-
res.options = obj.options.slice(0, 25).map((opt
|
|
157
|
-
if (typeof opt === "string") return opt
|
|
183
|
+
res.options = obj.options.slice(0, 25).map((opt) => {
|
|
184
|
+
if (typeof opt === "string") return opt;
|
|
158
185
|
if (typeof opt === "number") return String(opt);
|
|
159
186
|
if (opt && typeof opt === "object") {
|
|
160
187
|
const optObj = opt;
|
|
161
188
|
return {
|
|
162
189
|
...typeof optObj.number !== "undefined" ? { number: String(optObj.number).slice(0, 16) } : {},
|
|
163
|
-
text: typeof optObj.text === "string" ? optObj.text
|
|
190
|
+
text: typeof optObj.text === "string" ? optObj.text : String(optObj.text ?? optObj.label ?? optObj.value ?? "")
|
|
164
191
|
};
|
|
165
192
|
}
|
|
166
|
-
return String(opt ?? "")
|
|
193
|
+
return String(opt ?? "");
|
|
167
194
|
});
|
|
168
195
|
}
|
|
169
196
|
return res;
|
|
170
197
|
}
|
|
171
|
-
return { question: String(item)
|
|
198
|
+
return { question: String(item), options: [] };
|
|
172
199
|
});
|
|
173
|
-
|
|
200
|
+
const str = JSON.stringify(bounded);
|
|
174
201
|
if (str.length <= maxChars) return str;
|
|
175
202
|
for (let i = bounded.length - 1; i >= 1; i--) {
|
|
176
203
|
const sub = JSON.stringify(bounded.slice(0, i));
|
|
@@ -178,11 +205,20 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
178
205
|
}
|
|
179
206
|
const firstQ = bounded[0];
|
|
180
207
|
if (firstQ && typeof firstQ === "object") {
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
208
|
+
const qObj = firstQ;
|
|
209
|
+
const qText = typeof qObj.question === "string" ? qObj.question : "";
|
|
210
|
+
const opts = Array.isArray(qObj.options) ? qObj.options : [];
|
|
211
|
+
for (let k = opts.length - 1; k >= 1; k--) {
|
|
212
|
+
const subQ = JSON.stringify([{ ...qObj, options: opts.slice(0, k) }]);
|
|
213
|
+
if (subQ.length <= maxChars) return subQ;
|
|
214
|
+
}
|
|
215
|
+
return sliceToSerializedBudget(
|
|
216
|
+
qText,
|
|
217
|
+
(safeQText) => JSON.stringify([{ question: safeQText, options: [] }]),
|
|
218
|
+
maxChars
|
|
219
|
+
);
|
|
184
220
|
}
|
|
185
|
-
return
|
|
221
|
+
return "[]".length <= maxChars ? "[]" : null;
|
|
186
222
|
}
|
|
187
223
|
if (parsed && typeof parsed === "object") {
|
|
188
224
|
const raw = JSON.stringify(parsed);
|
|
@@ -216,6 +252,8 @@ function buildBody(raw) {
|
|
|
216
252
|
if (key === "questions" || key === "options") {
|
|
217
253
|
const serialized = serializeStructuredArg(value, MAX_STRUCTURED_ARG_CHARS, key);
|
|
218
254
|
if (serialized !== null) kept[key] = serialized;
|
|
255
|
+
} else if (key === "question" || key === "prompt") {
|
|
256
|
+
kept[key] = value.slice(0, MAX_STRUCTURED_ARG_CHARS);
|
|
219
257
|
} else {
|
|
220
258
|
kept[key] = value.slice(0, MAX_TOOL_ARG_CHARS);
|
|
221
259
|
}
|
package/dist/server.js
CHANGED
|
@@ -2448,6 +2448,10 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2448
2448
|
* start()/stop() lifecycle and doesn't distinguish debounce
|
|
2449
2449
|
* cycles. (Stage 2 iter-4 R1 HIGH finding 2026-05-21.) */
|
|
2450
2450
|
this.debounceCycleId = 0;
|
|
2451
|
+
this.clearDebounceTimer = null;
|
|
2452
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2453
|
+
this.clearDebounceCycleId = 0;
|
|
2454
|
+
this.clearFlushInFlight = false;
|
|
2451
2455
|
this.pipeFilePath = options.pipeFilePath ?? path6.join(os5.tmpdir(), `codevibe-agy-pane-${process.pid}.log`);
|
|
2452
2456
|
this.skipTmuxCommands = options.skipTmuxCommands ?? false;
|
|
2453
2457
|
this.captureFn = options.captureFn ?? null;
|
|
@@ -2482,6 +2486,9 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2482
2486
|
* and they paint atomically. */
|
|
2483
2487
|
this.QUESTION_SETTLE_DELAY_MS = 100;
|
|
2484
2488
|
}
|
|
2489
|
+
static {
|
|
2490
|
+
this.CLEAR_CONFIRM_DELAY_MS = 50;
|
|
2491
|
+
}
|
|
2485
2492
|
/**
|
|
2486
2493
|
* Start observing the tmux pane targeted by `sessionName`. If sessionName
|
|
2487
2494
|
* is omitted, read $CODEVIBE_AGY_TMUX_TARGET from env (set by the
|
|
@@ -2707,6 +2714,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2707
2714
|
* goes idle (e.g. an animated spinner inside the approval UI).
|
|
2708
2715
|
*/
|
|
2709
2716
|
scheduleDebouncedEmit(candidate) {
|
|
2717
|
+
this.cancelClearDebounce();
|
|
2710
2718
|
const now = Date.now();
|
|
2711
2719
|
this.pendingEmitCandidate = candidate;
|
|
2712
2720
|
if (this.debounceWindowOpenedAt === null) {
|
|
@@ -2754,7 +2762,10 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2754
2762
|
let candidate = staged;
|
|
2755
2763
|
try {
|
|
2756
2764
|
const freshSnapshot = await this.captureSnapshot();
|
|
2765
|
+
if (this.generation !== scheduledGeneration || !this.started) return;
|
|
2766
|
+
if (this.debounceCycleId !== scheduledCycleId) return;
|
|
2757
2767
|
if (freshSnapshot && looksLikePromptSnapshot(freshSnapshot)) {
|
|
2768
|
+
this.cancelClearDebounce();
|
|
2758
2769
|
candidate = {
|
|
2759
2770
|
rawDelta: staged.rawDelta,
|
|
2760
2771
|
snapshot: freshSnapshot,
|
|
@@ -2774,6 +2785,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2774
2785
|
const settledHeader = settled ? extractQuestionHeader(settled) : null;
|
|
2775
2786
|
const sameBody = !!firstHeader && !!settledHeader && firstHeader.body === settledHeader.body;
|
|
2776
2787
|
if (settled && looksLikePromptSnapshot(settled) && detectActivePromptKind(settled) === "question" && sameBody) {
|
|
2788
|
+
this.cancelClearDebounce();
|
|
2777
2789
|
candidate = {
|
|
2778
2790
|
rawDelta: staged.rawDelta,
|
|
2779
2791
|
snapshot: settled,
|
|
@@ -2825,6 +2837,64 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2825
2837
|
this.pendingEmitCandidate = null;
|
|
2826
2838
|
this.debounceWindowOpenedAt = null;
|
|
2827
2839
|
this.debounceCycleId += 1;
|
|
2840
|
+
this.cancelClearDebounce();
|
|
2841
|
+
}
|
|
2842
|
+
cancelClearDebounce() {
|
|
2843
|
+
if (this.clearDebounceTimer) {
|
|
2844
|
+
clearTimeout(this.clearDebounceTimer);
|
|
2845
|
+
this.clearDebounceTimer = null;
|
|
2846
|
+
}
|
|
2847
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2848
|
+
this.clearFlushInFlight = false;
|
|
2849
|
+
this.clearDebounceCycleId += 1;
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Schedule a debounced check for whether the active prompt UI has genuinely
|
|
2853
|
+
* cleared from the pane. Ink full-viewport redraws write upper scrollback
|
|
2854
|
+
* chunks (`!looksLikePromptDelta(chunk)`) before repainting the bottom
|
|
2855
|
+
* chooser; capturing immediately mid-redraw would false-fire
|
|
2856
|
+
* `prompt-cleared` and cause the repainted chooser to re-emit a duplicate card.
|
|
2857
|
+
* Bounded by DEBOUNCE_MAX_WAIT_MS (600ms) so continuous post-approval output
|
|
2858
|
+
* never starves `prompt-cleared` (Claude R1-F2, Codex R1-F3).
|
|
2859
|
+
*/
|
|
2860
|
+
scheduleDebouncedClear() {
|
|
2861
|
+
if (!this.approvalUIActive || this.clearFlushInFlight) return;
|
|
2862
|
+
const now = Date.now();
|
|
2863
|
+
if (this.clearDebounceWindowOpenedAt === null) {
|
|
2864
|
+
this.clearDebounceWindowOpenedAt = now;
|
|
2865
|
+
}
|
|
2866
|
+
const elapsed = now - this.clearDebounceWindowOpenedAt;
|
|
2867
|
+
const cappedRemaining = Math.max(0, _TmuxPaneObserver.DEBOUNCE_MAX_WAIT_MS - elapsed);
|
|
2868
|
+
if (cappedRemaining === 0 && this.clearDebounceTimer !== null) {
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const waitMs = Math.min(_TmuxPaneObserver.DEBOUNCE_QUIET_MS, cappedRemaining);
|
|
2872
|
+
if (this.clearDebounceTimer) clearTimeout(this.clearDebounceTimer);
|
|
2873
|
+
this.clearDebounceCycleId += 1;
|
|
2874
|
+
const myGeneration = this.generation;
|
|
2875
|
+
const myCycleId = this.clearDebounceCycleId;
|
|
2876
|
+
this.clearDebounceTimer = setTimeout(() => {
|
|
2877
|
+
void this.flushDebouncedClear(myGeneration, myCycleId);
|
|
2878
|
+
}, waitMs);
|
|
2879
|
+
if (this.clearDebounceTimer.unref) this.clearDebounceTimer.unref();
|
|
2880
|
+
}
|
|
2881
|
+
async flushDebouncedClear(scheduledGeneration, scheduledCycleId) {
|
|
2882
|
+
if (this.generation !== scheduledGeneration || !this.started) return;
|
|
2883
|
+
if (this.clearDebounceCycleId !== scheduledCycleId) return;
|
|
2884
|
+
this.clearDebounceTimer = null;
|
|
2885
|
+
if (!this.approvalUIActive) {
|
|
2886
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
this.clearFlushInFlight = true;
|
|
2890
|
+
try {
|
|
2891
|
+
await this.checkPromptCleared(scheduledGeneration, scheduledCycleId);
|
|
2892
|
+
} finally {
|
|
2893
|
+
if (this.clearDebounceCycleId === scheduledCycleId) {
|
|
2894
|
+
this.clearFlushInFlight = false;
|
|
2895
|
+
this.clearDebounceWindowOpenedAt = null;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2828
2898
|
}
|
|
2829
2899
|
/**
|
|
2830
2900
|
* Public hook for tests / drivers: trigger a read+process cycle as if
|
|
@@ -2846,6 +2916,11 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2846
2916
|
this.debounceTimer = null;
|
|
2847
2917
|
await this.flushDebouncedEmit(this.generation, this.debounceCycleId);
|
|
2848
2918
|
}
|
|
2919
|
+
if (this.clearDebounceTimer) {
|
|
2920
|
+
clearTimeout(this.clearDebounceTimer);
|
|
2921
|
+
this.clearDebounceTimer = null;
|
|
2922
|
+
await this.flushDebouncedClear(this.generation, this.clearDebounceCycleId);
|
|
2923
|
+
}
|
|
2849
2924
|
}
|
|
2850
2925
|
async processFileChanges() {
|
|
2851
2926
|
if (this.processing) {
|
|
@@ -2863,7 +2938,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2863
2938
|
if (!chunk) continue;
|
|
2864
2939
|
if (!looksLikePromptDelta(chunk)) {
|
|
2865
2940
|
if (this.approvalUIActive) {
|
|
2866
|
-
|
|
2941
|
+
this.scheduleDebouncedClear();
|
|
2867
2942
|
}
|
|
2868
2943
|
continue;
|
|
2869
2944
|
}
|
|
@@ -2871,10 +2946,11 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2871
2946
|
if (this.generation !== myGeneration || !this.started) return;
|
|
2872
2947
|
if (!snapshot || !looksLikePromptSnapshot(snapshot)) {
|
|
2873
2948
|
if (this.approvalUIActive) {
|
|
2874
|
-
|
|
2949
|
+
this.scheduleDebouncedClear();
|
|
2875
2950
|
}
|
|
2876
2951
|
continue;
|
|
2877
2952
|
}
|
|
2953
|
+
this.cancelClearDebounce();
|
|
2878
2954
|
const snapshotHash = hashPromptSnapshot(snapshot);
|
|
2879
2955
|
if (snapshotHash === this.lastPromptHash) {
|
|
2880
2956
|
continue;
|
|
@@ -2898,13 +2974,31 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
|
|
|
2898
2974
|
}
|
|
2899
2975
|
}
|
|
2900
2976
|
/** Called when we suspect the approval UI may have vanished. Does a
|
|
2901
|
-
* fresh capture; if the pane no longer shows approval UI
|
|
2902
|
-
* 'prompt-cleared'. Generation-checked
|
|
2903
|
-
|
|
2977
|
+
* fresh capture; if the pane no longer shows approval UI across two
|
|
2978
|
+
* consecutive captures, fires 'prompt-cleared'. Generation-checked
|
|
2979
|
+
* and cycle-checked to suppress emit after stop() or concurrent prompt
|
|
2980
|
+
* re-assertion (Claude R1-F3, Codex R1-F4). */
|
|
2981
|
+
async checkPromptCleared(myGeneration, expectedClearCycleId) {
|
|
2904
2982
|
try {
|
|
2905
2983
|
const snapshot = await this.captureSnapshot();
|
|
2906
2984
|
if (this.generation !== myGeneration || !this.started) return;
|
|
2907
|
-
if (
|
|
2985
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
if (looksLikePromptSnapshot(snapshot)) {
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
await sleep(_TmuxPaneObserver.CLEAR_CONFIRM_DELAY_MS);
|
|
2992
|
+
if (this.generation !== myGeneration || !this.started) return;
|
|
2993
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
const confirmSnapshot = await this.captureSnapshot();
|
|
2997
|
+
if (this.generation !== myGeneration || !this.started) return;
|
|
2998
|
+
if (expectedClearCycleId !== void 0 && this.clearDebounceCycleId !== expectedClearCycleId) {
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
if (!looksLikePromptSnapshot(confirmSnapshot)) {
|
|
2908
3002
|
this.approvalUIActive = false;
|
|
2909
3003
|
this.lastPromptHash = null;
|
|
2910
3004
|
this.currentApprovalHeader = null;
|
|
@@ -3462,8 +3556,9 @@ function looksLikePromptSnapshot(snapshot) {
|
|
|
3462
3556
|
const allLines = stripped.trimEnd().split("\n");
|
|
3463
3557
|
const fenceMask = computeFenceMask(allLines);
|
|
3464
3558
|
const windowStart = Math.max(0, allLines.length - 30);
|
|
3559
|
+
const questionWindowStart = Math.max(0, allLines.length - 80);
|
|
3465
3560
|
let headerIdx = -1;
|
|
3466
|
-
for (let i = allLines.length - 1; i >=
|
|
3561
|
+
for (let i = allLines.length - 1; i >= questionWindowStart; i--) {
|
|
3467
3562
|
if (!fenceMask[i] && isQuestionHeaderLine(allLines[i])) {
|
|
3468
3563
|
headerIdx = i;
|
|
3469
3564
|
break;
|
|
@@ -3471,9 +3566,16 @@ function looksLikePromptSnapshot(snapshot) {
|
|
|
3471
3566
|
}
|
|
3472
3567
|
if (headerIdx < 0) return false;
|
|
3473
3568
|
const headerLine = allLines[headerIdx].trim();
|
|
3569
|
+
const isTallHeader = headerIdx < windowStart;
|
|
3474
3570
|
const optionIndices = [];
|
|
3475
3571
|
for (let i = headerIdx + 1; i < allLines.length; i++) {
|
|
3476
|
-
if (
|
|
3572
|
+
if (fenceMask[i]) continue;
|
|
3573
|
+
const trimmed = allLines[i].trim();
|
|
3574
|
+
if (!trimmed) continue;
|
|
3575
|
+
if ((isTallHeader || optionIndices.length > 0) && isResumedTurnSignal(trimmed) || isComposerBoxAt(allLines, i) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b/i.test(trimmed)) {
|
|
3576
|
+
return false;
|
|
3577
|
+
}
|
|
3578
|
+
if (/^(?:[>\s❯]{0,4})[1-9][.)]\s+\S/.test(allLines[i])) {
|
|
3477
3579
|
optionIndices.push(i);
|
|
3478
3580
|
}
|
|
3479
3581
|
}
|
|
@@ -3497,7 +3599,7 @@ function looksLikePromptSnapshot(snapshot) {
|
|
|
3497
3599
|
}
|
|
3498
3600
|
}
|
|
3499
3601
|
}
|
|
3500
|
-
if (!hasQuestionFooter) return false;
|
|
3602
|
+
if (!hasQuestionFooter || footerIdx < windowStart) return false;
|
|
3501
3603
|
if (hasTrailingAgyComposer(allLines, footerIdx, fenceMask)) {
|
|
3502
3604
|
return false;
|
|
3503
3605
|
}
|
|
@@ -4216,6 +4318,38 @@ function detectActivePromptKind(snapshot) {
|
|
|
4216
4318
|
}
|
|
4217
4319
|
if (lastApprovalLineIdx >= 0 && lastQuestionLineIdx >= 0 && lastFileAccessLineIdx >= 0 && lastSystemErrorLineIdx >= 0) break;
|
|
4218
4320
|
}
|
|
4321
|
+
if (lastQuestionLineIdx < 0 && lastApprovalLineIdx < 0 && lastFileAccessLineIdx < 0) {
|
|
4322
|
+
const tallQuestionStart = Math.max(0, lines.length - 80);
|
|
4323
|
+
for (let i = windowStart - 1; i >= tallQuestionStart; i--) {
|
|
4324
|
+
if (lineInFence[i]) continue;
|
|
4325
|
+
if (isQuestionHeaderLine(lines[i])) {
|
|
4326
|
+
let optCount = 0;
|
|
4327
|
+
let footerInBottomWindow = false;
|
|
4328
|
+
let footerIdx = i;
|
|
4329
|
+
let interrupted = false;
|
|
4330
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
4331
|
+
if (lineInFence[j]) continue;
|
|
4332
|
+
const trimmed = lines[j].trim();
|
|
4333
|
+
if (!trimmed) continue;
|
|
4334
|
+
if (approvalRe.test(trimmed) || fileEditQuestionRe.test(trimmed) || fileAccessRe.test(trimmed) || fileAccessQuestionRe.test(trimmed) || /Do you want to proceed\?/i.test(trimmed) || isResumedTurnSignal(trimmed) || isComposerBoxAt(lines, j) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b/i.test(trimmed)) {
|
|
4335
|
+
interrupted = true;
|
|
4336
|
+
break;
|
|
4337
|
+
}
|
|
4338
|
+
if (/^(?:[>\s❯]{0,4})[1-9][.)]\s+\S/.test(lines[j])) {
|
|
4339
|
+
optCount += 1;
|
|
4340
|
+
} else if (isQuestionFooterLine(trimmed) && j >= windowStart) {
|
|
4341
|
+
footerInBottomWindow = true;
|
|
4342
|
+
footerIdx = j;
|
|
4343
|
+
break;
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
if (!interrupted && optCount >= 2 && footerInBottomWindow && !hasTrailingAgyComposer(lines, footerIdx, lineInFence)) {
|
|
4347
|
+
lastQuestionLineIdx = i;
|
|
4348
|
+
}
|
|
4349
|
+
break;
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4219
4353
|
if (lastApprovalLineIdx < 0 && lastQuestionLineIdx < 0) {
|
|
4220
4354
|
for (let i = lines.length - 1; i >= windowStart; i--) {
|
|
4221
4355
|
if (/Do you want to proceed\?/i.test(lines[i])) {
|
|
@@ -4241,7 +4375,7 @@ function detectActivePromptKind(snapshot) {
|
|
|
4241
4375
|
for (let j = lastSystemErrorLineIdx + 1; j < lines.length; j++) {
|
|
4242
4376
|
if (!lineInFence[j]) {
|
|
4243
4377
|
const trimmed = lines[j].trim();
|
|
4244
|
-
if (hasQuestionOptions && isResumedTurnSignal(trimmed)) {
|
|
4378
|
+
if ((hasQuestionOptions || lastQuestionLineIdx < windowStart) && isResumedTurnSignal(trimmed)) {
|
|
4245
4379
|
break;
|
|
4246
4380
|
}
|
|
4247
4381
|
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) {
|
|
@@ -4295,7 +4429,7 @@ function detectActivePromptKind(snapshot) {
|
|
|
4295
4429
|
if (isAgyStatusFooterLine(trimmed) || /^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help)\b/i.test(trimmed) || isComposerBoxAt(lines, j)) {
|
|
4296
4430
|
break;
|
|
4297
4431
|
}
|
|
4298
|
-
if (hasFoundOptions && isResumedTurnSignal(trimmed)) {
|
|
4432
|
+
if ((hasFoundOptions || lastQuestionLineIdx < windowStart) && isResumedTurnSignal(trimmed)) {
|
|
4299
4433
|
break;
|
|
4300
4434
|
}
|
|
4301
4435
|
if (/^[>\s❯]*[1-9][.)]\s+\S/.test(trimmed)) {
|
|
@@ -4701,8 +4835,16 @@ function extractQuestionHeader(snapshot) {
|
|
|
4701
4835
|
}
|
|
4702
4836
|
function hashPromptSnapshot(snapshot) {
|
|
4703
4837
|
let normalized = stripAnsiAndExpandRep(snapshot).replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
4704
|
-
|
|
4838
|
+
const activeKind = detectActivePromptKind(normalized);
|
|
4839
|
+
if (activeKind === "system_error") {
|
|
4705
4840
|
normalized = normalized.replace(/\bResets\s+in\s+\d+[dhms](?:\s*\d+[dhms])*\.?/gi, "Resets in <duration>");
|
|
4841
|
+
} else if (activeKind === "approval") {
|
|
4842
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
4843
|
+
if (fileEdit?.body) {
|
|
4844
|
+
normalized = `${normalized}
|
|
4845
|
+
@@file-edit-body@@
|
|
4846
|
+
${fileEdit.body}`;
|
|
4847
|
+
}
|
|
4706
4848
|
}
|
|
4707
4849
|
return (0, import_crypto.createHash)("sha256").update(normalized).digest("hex");
|
|
4708
4850
|
}
|
|
@@ -4762,7 +4904,7 @@ function parseApprovalSnapshot(snapshot) {
|
|
|
4762
4904
|
}
|
|
4763
4905
|
}
|
|
4764
4906
|
if (lastFileEditIdx > lastCmdApprovalIdx) {
|
|
4765
|
-
const fileEdit = extractFileEditPrompt(
|
|
4907
|
+
const fileEdit = extractFileEditPrompt(snapshot);
|
|
4766
4908
|
if (fileEdit) {
|
|
4767
4909
|
return parseFileEditSnapshot(stripped, snapshot, fileEdit);
|
|
4768
4910
|
}
|
|
@@ -4780,7 +4922,8 @@ function buildApprovalSemanticKey(candidate) {
|
|
|
4780
4922
|
body: isDiffBody ? rawBody.trim() : normalize(rawBody),
|
|
4781
4923
|
options: (candidate.options ?? []).map((option) => ({
|
|
4782
4924
|
number: option.number,
|
|
4783
|
-
text: normalize(option.text)
|
|
4925
|
+
text: normalize(option.text),
|
|
4926
|
+
...option.rawLines && option.rawLines.length > 1 ? { rawLines: option.rawLines.map((line) => normalize(line)) } : {}
|
|
4784
4927
|
})),
|
|
4785
4928
|
submitMap: Object.fromEntries(
|
|
4786
4929
|
Object.entries(candidate.submitMap ?? {}).sort(([left], [right]) => Number(left) - Number(right)).map(([number, keys]) => [number, [...keys]])
|
|
@@ -4788,6 +4931,13 @@ function buildApprovalSemanticKey(candidate) {
|
|
|
4788
4931
|
};
|
|
4789
4932
|
return (0, import_crypto2.createHash)("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
4790
4933
|
}
|
|
4934
|
+
function buildApprovalRedrawSemanticKey(candidate) {
|
|
4935
|
+
const rightTrimmedBody = (candidate.body ?? "").replace(/\r\n/g, "\n").split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n");
|
|
4936
|
+
return buildApprovalSemanticKey({
|
|
4937
|
+
...candidate,
|
|
4938
|
+
body: rightTrimmedBody
|
|
4939
|
+
});
|
|
4940
|
+
}
|
|
4791
4941
|
function hasNegativeApprovalOption(options) {
|
|
4792
4942
|
return options.some((option) => /^(?:no|deny|reject|cancel)\b/i.test(option.text.trim()));
|
|
4793
4943
|
}
|
|
@@ -4894,7 +5044,7 @@ function parseQuestionSnapshot(stripped, originalSnapshot, header) {
|
|
|
4894
5044
|
}
|
|
4895
5045
|
}
|
|
4896
5046
|
const belowHeader = headerIdx >= 0 ? lines.slice(headerIdx + 1).join("\n") : stripped;
|
|
4897
|
-
const options = parseOptions(belowHeader);
|
|
5047
|
+
const options = parseOptions(belowHeader, 80);
|
|
4898
5048
|
if (options.length < 2) return null;
|
|
4899
5049
|
const normalizedHeader = header.fullLine.replace(/^[❯>\s*•-]+\s*/, "").trim();
|
|
4900
5050
|
const isMultiSelect = /\bselect\s+(?:all|multiple)\b/i.test(normalizedHeader) || /\bselect\s+(?:all|multiple)\b/i.test(belowHeader) || /(?:<space>|\bspace)\s+(?:to\s+)?(?:select|toggle)\b/i.test(belowHeader) || /\bctrl\+a\s+select\s+all\b/i.test(belowHeader);
|
|
@@ -4915,7 +5065,7 @@ function parseQuestionSnapshot(stripped, originalSnapshot, header) {
|
|
|
4915
5065
|
}
|
|
4916
5066
|
function extractBodyBetweenHeaderAndFirstOption(belowHeader) {
|
|
4917
5067
|
const lines = belowHeader.split("\n");
|
|
4918
|
-
const optionRegex = /^[>\s❯]
|
|
5068
|
+
const optionRegex = /^(?:[>\s❯]{0,4})([1-9])[.)]\s+(.+?)\s*$/;
|
|
4919
5069
|
const lineInFence = computeFenceMask(lines);
|
|
4920
5070
|
let firstOptionIdx = -1;
|
|
4921
5071
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -4928,7 +5078,34 @@ function extractBodyBetweenHeaderAndFirstOption(belowHeader) {
|
|
|
4928
5078
|
if (firstOptionIdx <= 0) return "";
|
|
4929
5079
|
return lines.slice(0, firstOptionIdx).join("\n").trim();
|
|
4930
5080
|
}
|
|
4931
|
-
function
|
|
5081
|
+
function isOptionContinuationBoundary(trimmed, rawLine, nextTrimmedLine) {
|
|
5082
|
+
if (!trimmed) return true;
|
|
5083
|
+
if (/^[•‣◦▪▫]\s/.test(trimmed)) return true;
|
|
5084
|
+
if (/^\s{2,4}[-*]\s/.test(rawLine)) return true;
|
|
5085
|
+
if (isQuestionFooterLine(trimmed)) return true;
|
|
5086
|
+
if (isQuestionHeaderLine(trimmed)) return true;
|
|
5087
|
+
if (isResumedTurnSignal(trimmed)) return true;
|
|
5088
|
+
if (/^[─━═-]{4,}$/.test(trimmed)) return true;
|
|
5089
|
+
if (/^\s*esc to cancel\b/i.test(trimmed)) return true;
|
|
5090
|
+
if (/^(?:↑\/↓|↑|↓|\^|v)\s*(?:\/|\s+)?Navigate\b/i.test(trimmed)) return true;
|
|
5091
|
+
if (/^ctrl\+[a-z0-9]+\s+[a-zA-Z]+$/i.test(trimmed)) return true;
|
|
5092
|
+
if (/^\(?use\s+(?:arrow|\S+)\s+keys[^)]*\)?$/i.test(trimmed)) return true;
|
|
5093
|
+
if (!/^ {5,}/.test(rawLine) && /^Your answer:/i.test(trimmed) && (/^Your answer:\s*$/i.test(trimmed) || Boolean(
|
|
5094
|
+
nextTrimmedLine && (isQuestionFooterLine(nextTrimmedLine) || /^\(?Enter to submit(?:\s*,\s*esc to cancel)?\)?\s*$/i.test(nextTrimmedLine) || /^\s*esc to cancel\b/i.test(nextTrimmedLine))
|
|
5095
|
+
))) {
|
|
5096
|
+
return true;
|
|
5097
|
+
}
|
|
5098
|
+
if (!/^ {5,}/.test(rawLine) && /^\(?Enter to submit(?:\s*,\s*esc to cancel)?\)?\s*$/i.test(trimmed)) {
|
|
5099
|
+
return true;
|
|
5100
|
+
}
|
|
5101
|
+
if (/^\s*(?:[❯>]\s*)?\?\s+for\s+(?:shortcuts|help|commands|options|keybindings|menu)\b/i.test(trimmed)) return true;
|
|
5102
|
+
if (/Requesting permission for:/i.test(trimmed)) return true;
|
|
5103
|
+
if (/Do you want to proceed\?/i.test(trimmed)) return true;
|
|
5104
|
+
if (/^\s*Allow access to this file\?\s*$/i.test(trimmed)) return true;
|
|
5105
|
+
if (/^\s*(?:Accept this file edit\?|Allow creation of this file\?)\s*$/i.test(trimmed)) return true;
|
|
5106
|
+
return false;
|
|
5107
|
+
}
|
|
5108
|
+
function parseOptions(snapshot, maxRecentLines = 30) {
|
|
4932
5109
|
const lines = snapshot.split("\n");
|
|
4933
5110
|
const lineInFence = computeFenceMask(lines);
|
|
4934
5111
|
const filteredLines = [];
|
|
@@ -4940,16 +5117,72 @@ function parseOptions(snapshot) {
|
|
|
4940
5117
|
while (filteredLines.length > 0 && filteredLines[filteredLines.length - 1].trim() === "") {
|
|
4941
5118
|
filteredLines.pop();
|
|
4942
5119
|
}
|
|
4943
|
-
const recent = filteredLines.slice(-30);
|
|
4944
|
-
const optionRegex = /^[>\s❯]
|
|
5120
|
+
const recent = filteredLines.slice(-Math.max(30, maxRecentLines));
|
|
5121
|
+
const optionRegex = /^(?:[>\s❯]{0,4})([1-9])[.)]\s+(.+?)\s*$/;
|
|
4945
5122
|
const found = /* @__PURE__ */ new Map();
|
|
4946
|
-
|
|
5123
|
+
let currentOptionNum = null;
|
|
5124
|
+
let currentOptionTextCol = 5;
|
|
5125
|
+
let prevOptionRawLen = 0;
|
|
5126
|
+
for (let idx = 0; idx < recent.length; idx++) {
|
|
5127
|
+
const line = recent[idx];
|
|
4947
5128
|
const m = line.match(optionRegex);
|
|
4948
|
-
if (
|
|
4949
|
-
|
|
4950
|
-
|
|
5129
|
+
if (m) {
|
|
5130
|
+
const num = m[1];
|
|
5131
|
+
const text = m[2].trim();
|
|
5132
|
+
const prefixMatch = line.match(/^(?:[>\s❯]{0,4})[1-9][.)]\s+/);
|
|
5133
|
+
currentOptionTextCol = prefixMatch ? prefixMatch[0].length : 5;
|
|
5134
|
+
prevOptionRawLen = line.trimEnd().length;
|
|
5135
|
+
found.set(num, {
|
|
5136
|
+
text,
|
|
5137
|
+
rawLines: [text],
|
|
5138
|
+
rawLineLengths: [prevOptionRawLen]
|
|
5139
|
+
});
|
|
5140
|
+
currentOptionNum = /^\s*write-?in(?:\s*(?:\.{3}|…))?\s*$/i.test(text) ? null : num;
|
|
5141
|
+
continue;
|
|
5142
|
+
}
|
|
5143
|
+
const trimmed = line.trim();
|
|
5144
|
+
if (currentOptionNum !== null) {
|
|
5145
|
+
let nextTrimmedLine;
|
|
5146
|
+
for (let k = idx + 1; k < recent.length; k++) {
|
|
5147
|
+
const nt = recent[k].trim();
|
|
5148
|
+
if (nt) {
|
|
5149
|
+
nextTrimmedLine = nt;
|
|
5150
|
+
break;
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
const leadingSpaces = (line.match(/^\s*/)?.[0] ?? "").length;
|
|
5154
|
+
const isIndentedContinuation = leadingSpaces >= Math.max(4, currentOptionTextCol);
|
|
5155
|
+
const isColumnZeroWrapContinuation = leadingSpaces === 0;
|
|
5156
|
+
if (isOptionContinuationBoundary(trimmed, line, nextTrimmedLine) || !isIndentedContinuation && !isColumnZeroWrapContinuation) {
|
|
5157
|
+
currentOptionNum = null;
|
|
5158
|
+
} else {
|
|
5159
|
+
const entry = found.get(currentOptionNum);
|
|
5160
|
+
if (entry) {
|
|
5161
|
+
const existing = entry.text;
|
|
5162
|
+
entry.text = existing ? `${existing} ${trimmed}` : trimmed;
|
|
5163
|
+
entry.rawLines.push(trimmed);
|
|
5164
|
+
prevOptionRawLen = line.trimEnd().length;
|
|
5165
|
+
entry.rawLineLengths.push(prevOptionRawLen);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
4951
5169
|
}
|
|
4952
|
-
return Array.from(found.entries()).sort(([a], [b]) => Number(a) - Number(b)).map(([number,
|
|
5170
|
+
return Array.from(found.entries()).sort(([a], [b]) => Number(a) - Number(b)).map(([number, entry]) => {
|
|
5171
|
+
const opt = { number, text: entry.text };
|
|
5172
|
+
if (entry.rawLines.length > 1) {
|
|
5173
|
+
Object.defineProperty(opt, "rawLines", {
|
|
5174
|
+
value: entry.rawLines,
|
|
5175
|
+
enumerable: false,
|
|
5176
|
+
configurable: true
|
|
5177
|
+
});
|
|
5178
|
+
Object.defineProperty(opt, "rawLineLengths", {
|
|
5179
|
+
value: entry.rawLineLengths,
|
|
5180
|
+
enumerable: false,
|
|
5181
|
+
configurable: true
|
|
5182
|
+
});
|
|
5183
|
+
}
|
|
5184
|
+
return opt;
|
|
5185
|
+
});
|
|
4953
5186
|
}
|
|
4954
5187
|
function buildApprovalSubmitMap(options) {
|
|
4955
5188
|
const map = {};
|
|
@@ -4970,10 +5203,11 @@ function isWriteInInputActive(paneText, expectedHeader, expectedStep, expectedBo
|
|
|
4970
5203
|
if (detectActivePromptKind(paneText) !== "question") return false;
|
|
4971
5204
|
const stripped = stripAnsiAndExpandRep(paneText);
|
|
4972
5205
|
const lines = stripped.trimEnd().split("\n");
|
|
4973
|
-
const
|
|
5206
|
+
const fenceMask = computeFenceMask(lines);
|
|
5207
|
+
const windowStart = Math.max(0, lines.length - 80);
|
|
4974
5208
|
let headerIdx = -1;
|
|
4975
5209
|
for (let i = lines.length - 1; i >= windowStart; i--) {
|
|
4976
|
-
if (isQuestionHeaderLine(lines[i])) {
|
|
5210
|
+
if (!fenceMask[i] && isQuestionHeaderLine(lines[i])) {
|
|
4977
5211
|
headerIdx = i;
|
|
4978
5212
|
break;
|
|
4979
5213
|
}
|
|
@@ -5763,6 +5997,32 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5763
5997
|
});
|
|
5764
5998
|
return null;
|
|
5765
5999
|
}
|
|
6000
|
+
if (candidate.kind === "approval") {
|
|
6001
|
+
const candidateSemanticKey = buildApprovalSemanticKey(candidate);
|
|
6002
|
+
const candidateRedrawSemanticKey = buildApprovalRedrawSemanticKey(candidate);
|
|
6003
|
+
const existing = [...this.pendingPrompts.values()].find(
|
|
6004
|
+
(p) => p.conversationId === conversationId && p.kind === candidate.kind && (p.paneSemanticKey === candidateSemanticKey || p.paneRedrawSemanticKey !== void 0 && p.paneRedrawSemanticKey === candidateRedrawSemanticKey)
|
|
6005
|
+
);
|
|
6006
|
+
if (existing) {
|
|
6007
|
+
if (existing.paneHash && existing.paneHash !== candidate.paneHash) {
|
|
6008
|
+
this.paneOnlyEmittedHashes.delete(existing.paneHash);
|
|
6009
|
+
}
|
|
6010
|
+
existing.paneHash = candidate.paneHash;
|
|
6011
|
+
if (candidate.paneHash) {
|
|
6012
|
+
this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
6013
|
+
}
|
|
6014
|
+
existing.paneSemanticKey = candidateSemanticKey;
|
|
6015
|
+
existing.paneRedrawSemanticKey = candidateRedrawSemanticKey;
|
|
6016
|
+
existing.body = candidate.body;
|
|
6017
|
+
logger.debug("Skipping pane-only re-emit for active approval with matching semantic key", {
|
|
6018
|
+
promptId: existing.promptId,
|
|
6019
|
+
paneSemanticKey: candidateSemanticKey.substring(0, 16),
|
|
6020
|
+
paneRedrawSemanticKey: candidateRedrawSemanticKey.substring(0, 16),
|
|
6021
|
+
paneHash: candidate.paneHash.substring(0, 16)
|
|
6022
|
+
});
|
|
6023
|
+
return null;
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
5766
6026
|
if (candidate.kind === "question") {
|
|
5767
6027
|
const paneQuestionText = candidate.headerText || candidate.fullHeaderLine;
|
|
5768
6028
|
const candidateSemanticKey = buildApprovalSemanticKey(candidate);
|
|
@@ -5852,10 +6112,13 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5852
6112
|
this.emit("prompt-upgraded", existing);
|
|
5853
6113
|
return existing;
|
|
5854
6114
|
}
|
|
6115
|
+
const hasPriorPaneObservation = Boolean(existing.paneSemanticKey);
|
|
5855
6116
|
const areControlsMutated = (existingOpts, candidateOpts) => {
|
|
5856
6117
|
if (!existingOpts || !candidateOpts) return false;
|
|
5857
6118
|
if (existingOpts.length !== candidateOpts.length) return true;
|
|
5858
|
-
return !existingOpts.every(
|
|
6119
|
+
return !existingOpts.every(
|
|
6120
|
+
(o, idx) => o.number === candidateOpts[idx].number && (o.text || "").trim() === (candidateOpts[idx].text || "").trim() && (!hasPriorPaneObservation || JSON.stringify(o.rawLines ?? []) === JSON.stringify(candidateOpts[idx].rawLines ?? []))
|
|
6121
|
+
);
|
|
5859
6122
|
};
|
|
5860
6123
|
const isControlDelta = Boolean(
|
|
5861
6124
|
existing.paneOptions && existing.paneOptions.length > 0 && candidate.options && candidate.options.length > 0 && areControlsMutated(existing.paneOptions, candidate.options)
|
|
@@ -5871,6 +6134,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5871
6134
|
} else {
|
|
5872
6135
|
if (!existing.paneSemanticKey) {
|
|
5873
6136
|
existing.paneSemanticKey = candidateSemanticKey;
|
|
6137
|
+
if (candidate.options && candidate.options.length > 0) {
|
|
6138
|
+
existing.paneOptions = candidate.options;
|
|
6139
|
+
}
|
|
5874
6140
|
}
|
|
5875
6141
|
if (stepLineMatch) {
|
|
5876
6142
|
existing.matchedPaneHeader = stepLineMatch.stitchedHeaderText;
|
|
@@ -5999,6 +6265,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
5999
6265
|
body: synthLineMatch ? synthLineMatch.remainingBody || void 0 : candidate.body,
|
|
6000
6266
|
paneOptions: candidate.options,
|
|
6001
6267
|
paneSemanticKey: buildApprovalSemanticKey(candidate),
|
|
6268
|
+
paneRedrawSemanticKey: buildApprovalRedrawSemanticKey(candidate),
|
|
6002
6269
|
kind: candidate.kind,
|
|
6003
6270
|
isHookPrompt: isHookCall,
|
|
6004
6271
|
// Carry hook ownership forward so pane redraws do not drop it! (Codex S2-Codex-R6-F15)
|
|
@@ -6319,6 +6586,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6319
6586
|
body: candidate.body,
|
|
6320
6587
|
paneOptions: candidate.options,
|
|
6321
6588
|
paneSemanticKey: buildApprovalSemanticKey(candidate),
|
|
6589
|
+
paneRedrawSemanticKey: buildApprovalRedrawSemanticKey(candidate),
|
|
6322
6590
|
kind: candidate.kind,
|
|
6323
6591
|
emittedAt: Date.now(),
|
|
6324
6592
|
ttlMs: this.promptTtlMs
|
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.103",
|
|
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": {
|