@quantiya/codevibe-antigravity-plugin 2.0.103 → 2.0.104
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 +22 -4
- package/dist/server.js +453 -115
- package/package.json +1 -1
package/dist/agy-hook.js
CHANGED
|
@@ -144,6 +144,7 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
144
144
|
(item) => typeof item === "string" || typeof item === "number" || item && typeof item === "object" && !("question" in item)
|
|
145
145
|
);
|
|
146
146
|
if (isOptionArray) {
|
|
147
|
+
const wasSlicedAtCap = parsed.length > 30;
|
|
147
148
|
const boundedOptions = parsed.slice(0, 30).map((opt, idx) => {
|
|
148
149
|
if (typeof opt === "string") return { number: String(idx + 1), text: opt };
|
|
149
150
|
if (typeof opt === "number") return { number: String(idx + 1), text: String(opt) };
|
|
@@ -155,17 +156,23 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
155
156
|
}
|
|
156
157
|
return { number: String(idx + 1), text: String(opt ?? "") };
|
|
157
158
|
});
|
|
159
|
+
if (wasSlicedAtCap && boundedOptions.length > 0) {
|
|
160
|
+
boundedOptions[0].options_truncated = true;
|
|
161
|
+
}
|
|
158
162
|
const str2 = JSON.stringify(boundedOptions);
|
|
159
163
|
if (str2.length <= maxChars) return str2;
|
|
160
164
|
for (let i = boundedOptions.length - 1; i >= 1; i--) {
|
|
161
|
-
const
|
|
165
|
+
const prefix = boundedOptions.slice(0, i).map(
|
|
166
|
+
(o, idx) => idx === 0 ? { ...o, options_truncated: true } : o
|
|
167
|
+
);
|
|
168
|
+
const sub = JSON.stringify(prefix);
|
|
162
169
|
if (sub.length <= maxChars) return sub;
|
|
163
170
|
}
|
|
164
171
|
const firstOpt = boundedOptions[0];
|
|
165
172
|
if (firstOpt) {
|
|
166
173
|
return sliceToSerializedBudget(
|
|
167
174
|
firstOpt.text,
|
|
168
|
-
(safeText) => JSON.stringify([{ number: firstOpt.number, text: safeText }]),
|
|
175
|
+
(safeText) => JSON.stringify([{ number: firstOpt.number, text: safeText, options_truncated: true }]),
|
|
169
176
|
maxChars
|
|
170
177
|
);
|
|
171
178
|
}
|
|
@@ -179,7 +186,12 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
179
186
|
if (typeof obj.question === "string") res.question = obj.question;
|
|
180
187
|
if (typeof obj.is_multi_select !== "undefined") res.is_multi_select = decodeBoolean(obj.is_multi_select);
|
|
181
188
|
if (typeof obj.isMultiSelect !== "undefined") res.isMultiSelect = decodeBoolean(obj.isMultiSelect);
|
|
189
|
+
if (typeof obj.options_truncated !== "undefined") res.options_truncated = decodeBoolean(obj.options_truncated);
|
|
190
|
+
if (typeof obj.optionsTruncated !== "undefined") res.optionsTruncated = decodeBoolean(obj.optionsTruncated);
|
|
182
191
|
if (Array.isArray(obj.options)) {
|
|
192
|
+
if (obj.options.length > 25) {
|
|
193
|
+
res.options_truncated = true;
|
|
194
|
+
}
|
|
183
195
|
res.options = obj.options.slice(0, 25).map((opt) => {
|
|
184
196
|
if (typeof opt === "string") return opt;
|
|
185
197
|
if (typeof opt === "number") return String(opt);
|
|
@@ -209,12 +221,18 @@ function serializeStructuredArg(value, maxChars = MAX_STRUCTURED_ARG_CHARS, keyH
|
|
|
209
221
|
const qText = typeof qObj.question === "string" ? qObj.question : "";
|
|
210
222
|
const opts = Array.isArray(qObj.options) ? qObj.options : [];
|
|
211
223
|
for (let k = opts.length - 1; k >= 1; k--) {
|
|
212
|
-
const subQ = JSON.stringify([{ ...qObj, options: opts.slice(0, k) }]);
|
|
224
|
+
const subQ = JSON.stringify([{ ...qObj, options: opts.slice(0, k), options_truncated: true }]);
|
|
213
225
|
if (subQ.length <= maxChars) return subQ;
|
|
214
226
|
}
|
|
215
227
|
return sliceToSerializedBudget(
|
|
216
228
|
qText,
|
|
217
|
-
(safeQText) => JSON.stringify([
|
|
229
|
+
(safeQText) => JSON.stringify([
|
|
230
|
+
{
|
|
231
|
+
question: safeQText,
|
|
232
|
+
options: [],
|
|
233
|
+
...opts.length > 0 ? { options_truncated: true } : {}
|
|
234
|
+
}
|
|
235
|
+
]),
|
|
218
236
|
maxChars
|
|
219
237
|
);
|
|
220
238
|
}
|
package/dist/server.js
CHANGED
|
@@ -5711,28 +5711,61 @@ function extractAllQuestionsFromArgs(toolName, args) {
|
|
|
5711
5711
|
const qObj = qItem;
|
|
5712
5712
|
const qText = typeof qObj.question === "string" ? stripWrappingQuotes2(qObj.question.trim()) : "Question";
|
|
5713
5713
|
const opts = [];
|
|
5714
|
+
let optItemTruncated = false;
|
|
5714
5715
|
if (Array.isArray(qObj.options)) {
|
|
5715
5716
|
for (let i = 0; i < qObj.options.length; i++) {
|
|
5716
5717
|
const opt = qObj.options[i];
|
|
5717
5718
|
if (typeof opt === "string" && opt.trim().length > 0) {
|
|
5718
5719
|
opts.push({ number: String(i + 1), text: stripWrappingQuotes2(opt.trim()) });
|
|
5719
5720
|
} else if (opt && typeof opt === "object" && typeof opt.text === "string") {
|
|
5721
|
+
const optRecord = opt;
|
|
5722
|
+
if (decodeBoolean(optRecord.options_truncated ?? optRecord.optionsTruncated)) {
|
|
5723
|
+
optItemTruncated = true;
|
|
5724
|
+
}
|
|
5720
5725
|
opts.push({
|
|
5721
|
-
number: String(
|
|
5722
|
-
text: stripWrappingQuotes2(String(
|
|
5726
|
+
number: String(optRecord.number ?? i + 1),
|
|
5727
|
+
text: stripWrappingQuotes2(String(optRecord.text).trim())
|
|
5723
5728
|
});
|
|
5724
5729
|
}
|
|
5725
5730
|
}
|
|
5726
5731
|
}
|
|
5727
5732
|
const isMulti = decodeBoolean(qObj.is_multi_select ?? qObj.isMultiSelect);
|
|
5728
|
-
|
|
5733
|
+
const isTruncated = optItemTruncated || decodeBoolean(qObj.options_truncated ?? qObj.optionsTruncated);
|
|
5734
|
+
items.push({
|
|
5735
|
+
question: qText,
|
|
5736
|
+
options: opts,
|
|
5737
|
+
isMultiSelect: isMulti,
|
|
5738
|
+
...isTruncated ? { optionsTruncated: true } : {}
|
|
5739
|
+
});
|
|
5729
5740
|
}
|
|
5730
5741
|
}
|
|
5731
5742
|
} else if (typeof a.question === "string" || typeof a.prompt === "string") {
|
|
5732
5743
|
const qText = stripWrappingQuotes2(String(a.question ?? a.prompt).trim());
|
|
5733
5744
|
const opts = extractQuestionOptionsFromArgs(toolName, args) ?? [];
|
|
5734
5745
|
const isMulti = decodeBoolean(a.is_multi_select ?? a.isMultiSelect);
|
|
5735
|
-
|
|
5746
|
+
let topOptTruncated = decodeBoolean(a.options_truncated ?? a.optionsTruncated);
|
|
5747
|
+
if (!topOptTruncated && (Array.isArray(a.options) || typeof a.options === "string")) {
|
|
5748
|
+
let topOptions = a.options;
|
|
5749
|
+
if (typeof topOptions === "string") {
|
|
5750
|
+
try {
|
|
5751
|
+
topOptions = JSON.parse(topOptions.trim());
|
|
5752
|
+
} catch {
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
if (Array.isArray(topOptions)) {
|
|
5756
|
+
topOptTruncated = topOptions.some(
|
|
5757
|
+
(o) => o !== null && typeof o === "object" && decodeBoolean(
|
|
5758
|
+
o.options_truncated ?? o.optionsTruncated
|
|
5759
|
+
)
|
|
5760
|
+
);
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
items.push({
|
|
5764
|
+
question: qText,
|
|
5765
|
+
options: opts,
|
|
5766
|
+
isMultiSelect: isMulti,
|
|
5767
|
+
...topOptTruncated ? { optionsTruncated: true } : {}
|
|
5768
|
+
});
|
|
5736
5769
|
}
|
|
5737
5770
|
return items.length > 0 ? items : void 0;
|
|
5738
5771
|
}
|
|
@@ -5834,6 +5867,104 @@ function matchPaneQuestionLines(hookText, candidate) {
|
|
|
5834
5867
|
function paneCandidateMatchesQuestion(hookText, candidate) {
|
|
5835
5868
|
return matchPaneQuestionLines(hookText, candidate) !== null;
|
|
5836
5869
|
}
|
|
5870
|
+
function buildHookQuestionOptionsAndSubmitMap(rawOptions, isMultiSelect, optionsTruncated = false) {
|
|
5871
|
+
if (!rawOptions || rawOptions.length === 0) {
|
|
5872
|
+
return { paneOptions: void 0, submitMap: {} };
|
|
5873
|
+
}
|
|
5874
|
+
const normalized = rawOptions.map((opt, idx) => {
|
|
5875
|
+
const entry = {
|
|
5876
|
+
number: String(opt.number ?? idx + 1),
|
|
5877
|
+
text: opt.text
|
|
5878
|
+
};
|
|
5879
|
+
if (opt.isGeneratedWriteIn) {
|
|
5880
|
+
Object.defineProperty(entry, "isGeneratedWriteIn", {
|
|
5881
|
+
value: true,
|
|
5882
|
+
enumerable: false,
|
|
5883
|
+
configurable: true
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5886
|
+
return entry;
|
|
5887
|
+
});
|
|
5888
|
+
if (isMultiSelect || optionsTruncated || normalized.length >= 9) {
|
|
5889
|
+
return { paneOptions: normalized, submitMap: {} };
|
|
5890
|
+
}
|
|
5891
|
+
const lastOpt = normalized[normalized.length - 1];
|
|
5892
|
+
const hasTrailingGeneratedWriteIn = Boolean(lastOpt?.isGeneratedWriteIn);
|
|
5893
|
+
let paneOptions;
|
|
5894
|
+
if (hasTrailingGeneratedWriteIn) {
|
|
5895
|
+
paneOptions = normalized;
|
|
5896
|
+
} else {
|
|
5897
|
+
const writeInOpt = {
|
|
5898
|
+
number: String(normalized.length + 1),
|
|
5899
|
+
text: "Write-in..."
|
|
5900
|
+
};
|
|
5901
|
+
Object.defineProperty(writeInOpt, "isGeneratedWriteIn", {
|
|
5902
|
+
value: true,
|
|
5903
|
+
enumerable: false,
|
|
5904
|
+
configurable: true
|
|
5905
|
+
});
|
|
5906
|
+
paneOptions = [...normalized, writeInOpt];
|
|
5907
|
+
}
|
|
5908
|
+
const submitMap = {};
|
|
5909
|
+
for (const opt of paneOptions) {
|
|
5910
|
+
submitMap[opt.number] = [opt.number];
|
|
5911
|
+
}
|
|
5912
|
+
return { paneOptions, submitMap };
|
|
5913
|
+
}
|
|
5914
|
+
function hookOptionsMatchPaneOptions(hookOpts, paneOpts) {
|
|
5915
|
+
if (!hookOpts || hookOpts.length === 0 || !paneOpts || paneOpts.length === 0) {
|
|
5916
|
+
return false;
|
|
5917
|
+
}
|
|
5918
|
+
const normOpt = (s) => s.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
5919
|
+
const hookNormalized = hookOpts.map((o, idx) => ({
|
|
5920
|
+
number: String(o.number ?? idx + 1).trim(),
|
|
5921
|
+
text: normOpt(o.text),
|
|
5922
|
+
isGeneratedWriteIn: Boolean(o.isGeneratedWriteIn)
|
|
5923
|
+
}));
|
|
5924
|
+
const hookEffective = hookNormalized.length > 0 && hookNormalized[hookNormalized.length - 1].isGeneratedWriteIn ? hookNormalized.slice(0, -1) : hookNormalized;
|
|
5925
|
+
const paneNormalized = paneOpts.map((o, idx) => ({
|
|
5926
|
+
number: String(o.number ?? idx + 1).trim(),
|
|
5927
|
+
text: normOpt(o.text),
|
|
5928
|
+
isGeneratedWriteIn: Boolean(o.isGeneratedWriteIn)
|
|
5929
|
+
}));
|
|
5930
|
+
const lastPane = paneNormalized[paneNormalized.length - 1];
|
|
5931
|
+
const paneHasTrailingWriteIn = Boolean(lastPane) && (lastPane.isGeneratedWriteIn || paneNormalized.length === hookEffective.length + 1 && /^\s*write-?in(?:\.{3}|…)?\s*$/i.test(lastPane.text));
|
|
5932
|
+
const paneEffective = paneHasTrailingWriteIn ? paneNormalized.slice(0, -1) : paneNormalized;
|
|
5933
|
+
if (hookEffective.length === 0 || hookEffective.length !== paneEffective.length) {
|
|
5934
|
+
return false;
|
|
5935
|
+
}
|
|
5936
|
+
return hookEffective.every(
|
|
5937
|
+
(h, i) => paneEffective[i].text === h.text && paneEffective[i].number === h.number
|
|
5938
|
+
);
|
|
5939
|
+
}
|
|
5940
|
+
function tagPaneGeneratedWriteInOption(paneOpts, hookStepOpts) {
|
|
5941
|
+
if (!paneOpts || paneOpts.length === 0) return paneOpts;
|
|
5942
|
+
const hookBaseOpts = hookStepOpts ? hookStepOpts.filter((o) => !o.isGeneratedWriteIn) : void 0;
|
|
5943
|
+
for (let i = 0; i < paneOpts.length - 1; i++) {
|
|
5944
|
+
const opt = paneOpts[i];
|
|
5945
|
+
if (opt?.isGeneratedWriteIn) {
|
|
5946
|
+
delete opt.isGeneratedWriteIn;
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
const lastOpt = paneOpts[paneOpts.length - 1];
|
|
5950
|
+
if (!lastOpt || !/^\s*write-?in(?:\.{3}|…)?\s*$/i.test(lastOpt.text)) {
|
|
5951
|
+
if (lastOpt?.isGeneratedWriteIn) {
|
|
5952
|
+
delete lastOpt.isGeneratedWriteIn;
|
|
5953
|
+
}
|
|
5954
|
+
return paneOpts;
|
|
5955
|
+
}
|
|
5956
|
+
const isGenerated = !hookBaseOpts || hookBaseOpts.length === 0 || paneOpts.length === hookBaseOpts.length + 1;
|
|
5957
|
+
if (isGenerated) {
|
|
5958
|
+
Object.defineProperty(lastOpt, "isGeneratedWriteIn", {
|
|
5959
|
+
value: true,
|
|
5960
|
+
enumerable: false,
|
|
5961
|
+
configurable: true
|
|
5962
|
+
});
|
|
5963
|
+
} else if (lastOpt.isGeneratedWriteIn) {
|
|
5964
|
+
delete lastOpt.isGeneratedWriteIn;
|
|
5965
|
+
}
|
|
5966
|
+
return paneOpts;
|
|
5967
|
+
}
|
|
5837
5968
|
function correlatesQuestionInvocation(call, delivery) {
|
|
5838
5969
|
if (!call) return false;
|
|
5839
5970
|
const tn = (delivery.toolName ?? "").toLowerCase().trim();
|
|
@@ -6023,6 +6154,9 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6023
6154
|
return null;
|
|
6024
6155
|
}
|
|
6025
6156
|
}
|
|
6157
|
+
let replacedHookCall;
|
|
6158
|
+
let replacedWasHookPrompt = false;
|
|
6159
|
+
let replacedHookWasDisplayOnly = false;
|
|
6026
6160
|
if (candidate.kind === "question") {
|
|
6027
6161
|
const paneQuestionText = candidate.headerText || candidate.fullHeaderLine;
|
|
6028
6162
|
const candidateSemanticKey = buildApprovalSemanticKey(candidate);
|
|
@@ -6039,7 +6173,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6039
6173
|
const qOptTexts = (targetQ.options || []).map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
6040
6174
|
const cOptTexts = (candidate.options || []).map((o) => normOpt(typeof o === "string" ? o : o.text));
|
|
6041
6175
|
if (cOptTexts.length > 0 && qOptTexts.length > 0) {
|
|
6042
|
-
const hasTrailingWriteIn = cOptTexts.length === qOptTexts.length + 1 && /^\s*write-?in
|
|
6176
|
+
const hasTrailingWriteIn = cOptTexts.length === qOptTexts.length + 1 && /^\s*write-?in(?:\.{3}|…)?\s*$/i.test(cOptTexts[cOptTexts.length - 1]);
|
|
6043
6177
|
const cEffectiveTexts = hasTrailingWriteIn ? cOptTexts.slice(0, -1) : cOptTexts;
|
|
6044
6178
|
const matchedQCount = qOptTexts.filter((q) => cEffectiveTexts.includes(q)).length;
|
|
6045
6179
|
const matchedCCount = cEffectiveTexts.filter((c) => qOptTexts.includes(c)).length;
|
|
@@ -6073,6 +6207,20 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6073
6207
|
}
|
|
6074
6208
|
}
|
|
6075
6209
|
if (bestStepIdx === -1) {
|
|
6210
|
+
const currentStepEval = scoreStep(currentActiveIdx);
|
|
6211
|
+
if (currentStepEval.textMatch) {
|
|
6212
|
+
const existing = [...this.pendingPrompts.values()].find(
|
|
6213
|
+
(p) => p.conversationId === conversationId && p.kind === "question"
|
|
6214
|
+
);
|
|
6215
|
+
if (existing && Object.keys(existing.submitMap || {}).length > 0) {
|
|
6216
|
+
existing.submitMap = {};
|
|
6217
|
+
if (existing.paneOptions && existing.paneOptions.length > 0 && existing.paneOptions[existing.paneOptions.length - 1]?.isGeneratedWriteIn) {
|
|
6218
|
+
existing.paneOptions = existing.paneOptions.slice(0, -1);
|
|
6219
|
+
}
|
|
6220
|
+
existing.paneSemanticKey = candidateSemanticKey;
|
|
6221
|
+
this.emit("prompt-upgraded", existing);
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6076
6224
|
logger.debug("Pane candidate did not match any step of active live wizard", {
|
|
6077
6225
|
paneQuestionText,
|
|
6078
6226
|
currentActiveIdx,
|
|
@@ -6091,53 +6239,42 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6091
6239
|
(p) => p.conversationId === conversationId && p.kind === "question"
|
|
6092
6240
|
);
|
|
6093
6241
|
if (existing) {
|
|
6094
|
-
const
|
|
6095
|
-
const
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
existing.paneOptions = candidate.options;
|
|
6101
|
-
}
|
|
6102
|
-
existing.paneSemanticKey = candidateSemanticKey;
|
|
6103
|
-
existing.paneHash = candidate.paneHash;
|
|
6104
|
-
existing.matchedPaneHeader = stepLineMatch?.stitchedHeaderText ?? candidate.headerText ?? existing.matchedPaneHeader;
|
|
6105
|
-
existing.paneDisplayHeader = stepLineMatch?.stitchedFullHeaderLine ?? candidate.fullHeaderLine ?? existing.paneDisplayHeader;
|
|
6106
|
-
existing.body = stepLineMatch ? stepLineMatch.remainingBody || void 0 : candidate.body;
|
|
6107
|
-
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
6108
|
-
logger.info("Upgraded hook-driven question prompt to actionable from pane observer", {
|
|
6109
|
-
promptId: existing.promptId,
|
|
6110
|
-
optionsCount: existing.paneOptions?.length ?? 0
|
|
6111
|
-
});
|
|
6112
|
-
this.emit("prompt-upgraded", existing);
|
|
6113
|
-
return existing;
|
|
6114
|
-
}
|
|
6115
|
-
const hasPriorPaneObservation = Boolean(existing.paneSemanticKey);
|
|
6116
|
-
const areControlsMutated = (existingOpts, candidateOpts) => {
|
|
6117
|
-
if (!existingOpts || !candidateOpts) return false;
|
|
6118
|
-
if (existingOpts.length !== candidateOpts.length) return true;
|
|
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
|
-
);
|
|
6122
|
-
};
|
|
6123
|
-
const isControlDelta = Boolean(
|
|
6124
|
-
existing.paneOptions && existing.paneOptions.length > 0 && candidate.options && candidate.options.length > 0 && areControlsMutated(existing.paneOptions, candidate.options)
|
|
6125
|
-
);
|
|
6126
|
-
if (isControlDelta) {
|
|
6127
|
-
logger.info("Control delta observed on active question step; resolving old prompt and re-emitting", {
|
|
6128
|
-
oldPromptId: existing.promptId,
|
|
6129
|
-
oldSemanticKey: existing.paneSemanticKey,
|
|
6130
|
-
newSemanticKey: candidateSemanticKey
|
|
6131
|
-
});
|
|
6242
|
+
const nextBody = stepLineMatch ? stepLineMatch.remainingBody || void 0 : candidate.body || void 0;
|
|
6243
|
+
const bodyChangedAfterObservation = Boolean(existing.paneBodyObserved) && existing.body !== nextBody;
|
|
6244
|
+
if (bodyChangedAfterObservation) {
|
|
6245
|
+
replacedHookCall = existing.pendingCall;
|
|
6246
|
+
replacedWasHookPrompt = Boolean(existing.isHookPrompt);
|
|
6247
|
+
replacedHookWasDisplayOnly = Object.keys(existing.submitMap || {}).length === 0;
|
|
6132
6248
|
this.resolvePrompt(existing.promptId, { advanceWizard: false });
|
|
6133
6249
|
this.emit("prompt-resolved", existing.promptId);
|
|
6134
6250
|
} else {
|
|
6135
|
-
|
|
6136
|
-
|
|
6251
|
+
const targetQ = existing.pendingCall?.structuredQuestions?.[existing.wizardStepIndex ?? 0];
|
|
6252
|
+
const isAuthoritativeMultiSelect = Boolean(
|
|
6253
|
+
existing.pendingCall?.isMultiSelect || targetQ?.isMultiSelect
|
|
6254
|
+
);
|
|
6255
|
+
const isOptionsTruncated = Boolean(
|
|
6256
|
+
existing.pendingCall?.optionsTruncated || targetQ?.optionsTruncated
|
|
6257
|
+
);
|
|
6258
|
+
const hasExplicitEmptyOptions = Boolean(
|
|
6259
|
+
targetQ && Array.isArray(targetQ.options) && targetQ.options.length === 0
|
|
6260
|
+
);
|
|
6261
|
+
let adoptedFallbackOptions = false;
|
|
6262
|
+
if (!isAuthoritativeMultiSelect && !isOptionsTruncated && !hasExplicitEmptyOptions && Object.keys(existing.submitMap || {}).length === 0 && (!existing.paneOptions || existing.paneOptions.length === 0) && Object.keys(candidate.submitMap || {}).length > 0) {
|
|
6263
|
+
existing.submitMap = candidate.submitMap;
|
|
6137
6264
|
if (candidate.options && candidate.options.length > 0) {
|
|
6138
|
-
existing.paneOptions = candidate.options;
|
|
6265
|
+
existing.paneOptions = tagPaneGeneratedWriteInOption(candidate.options, targetQ?.options);
|
|
6139
6266
|
}
|
|
6267
|
+
adoptedFallbackOptions = true;
|
|
6268
|
+
}
|
|
6269
|
+
const candidateIsActionable = Object.keys(candidate.submitMap || {}).length > 0;
|
|
6270
|
+
const optionsMatch = hookOptionsMatchPaneOptions(existing.paneOptions, candidate.options);
|
|
6271
|
+
const exactControlsMatch = adoptedFallbackOptions || candidateIsActionable && optionsMatch && (existing.paneOptions?.length ?? 0) === (candidate.options?.length ?? 0) && (existing.paneOptions ?? []).every(
|
|
6272
|
+
(o, i) => o.number === candidate.options[i]?.number && JSON.stringify(existing.submitMap?.[o.number]) === JSON.stringify(candidate.submitMap?.[o.number])
|
|
6273
|
+
);
|
|
6274
|
+
if (existing.paneHash && existing.paneHash !== candidate.paneHash) {
|
|
6275
|
+
this.paneOnlyEmittedHashes.delete(existing.paneHash);
|
|
6140
6276
|
}
|
|
6277
|
+
existing.paneHash = candidate.paneHash;
|
|
6141
6278
|
if (stepLineMatch) {
|
|
6142
6279
|
existing.matchedPaneHeader = stepLineMatch.stitchedHeaderText;
|
|
6143
6280
|
existing.paneDisplayHeader = stepLineMatch.stitchedFullHeaderLine;
|
|
@@ -6145,8 +6282,19 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6145
6282
|
if (existing.pendingCall && targetStepQ?.question) {
|
|
6146
6283
|
existing.pendingCall.questionText = stepLineMatch.stitchedHeaderText;
|
|
6147
6284
|
}
|
|
6148
|
-
} else
|
|
6149
|
-
existing.body =
|
|
6285
|
+
} else {
|
|
6286
|
+
existing.body = nextBody;
|
|
6287
|
+
}
|
|
6288
|
+
existing.paneBodyObserved = true;
|
|
6289
|
+
if (exactControlsMatch) {
|
|
6290
|
+
existing.paneSemanticKey = candidateSemanticKey;
|
|
6291
|
+
} else if ((!candidateIsActionable || !optionsMatch) && Object.keys(existing.submitMap || {}).length > 0) {
|
|
6292
|
+
existing.submitMap = {};
|
|
6293
|
+
if (existing.paneOptions && existing.paneOptions.length > 0 && existing.paneOptions[existing.paneOptions.length - 1]?.isGeneratedWriteIn) {
|
|
6294
|
+
existing.paneOptions = existing.paneOptions.slice(0, -1);
|
|
6295
|
+
}
|
|
6296
|
+
existing.paneSemanticKey = candidateSemanticKey;
|
|
6297
|
+
this.emit("prompt-upgraded", existing);
|
|
6150
6298
|
}
|
|
6151
6299
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
6152
6300
|
return null;
|
|
@@ -6173,44 +6321,86 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6173
6321
|
const lineMatch = matchPaneQuestionLines(hookQuestionText, candidate);
|
|
6174
6322
|
const isSameQuestion = lineMatch !== null;
|
|
6175
6323
|
if (isSameQuestion) {
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6324
|
+
if (p.isHookPrompt) {
|
|
6325
|
+
const nextBody = lineMatch ? lineMatch.remainingBody || void 0 : candidate.body || void 0;
|
|
6326
|
+
const bodyChangedAfterObservation = Boolean(p.paneBodyObserved) && p.body !== nextBody;
|
|
6327
|
+
if (!bodyChangedAfterObservation) {
|
|
6328
|
+
const targetQ = p.pendingCall?.structuredQuestions?.[p.wizardStepIndex ?? 0];
|
|
6329
|
+
const isAuthoritativeMultiSelect = Boolean(
|
|
6330
|
+
p.pendingCall?.isMultiSelect || targetQ?.isMultiSelect
|
|
6331
|
+
);
|
|
6332
|
+
const isOptionsTruncated = Boolean(
|
|
6333
|
+
p.pendingCall?.optionsTruncated || targetQ?.optionsTruncated
|
|
6334
|
+
);
|
|
6335
|
+
let adoptedFallbackOptions = false;
|
|
6336
|
+
if (!isAuthoritativeMultiSelect && !isOptionsTruncated && Object.keys(p.submitMap || {}).length === 0 && (!p.paneOptions || p.paneOptions.length === 0) && Object.keys(candidate.submitMap || {}).length > 0) {
|
|
6337
|
+
p.submitMap = candidate.submitMap;
|
|
6338
|
+
if (candidate.options && candidate.options.length > 0) {
|
|
6339
|
+
p.paneOptions = tagPaneGeneratedWriteInOption(
|
|
6340
|
+
candidate.options,
|
|
6341
|
+
targetQ?.options ?? p.pendingCall?.questionOptions
|
|
6342
|
+
);
|
|
6343
|
+
}
|
|
6344
|
+
adoptedFallbackOptions = true;
|
|
6345
|
+
}
|
|
6346
|
+
const candidateIsActionable = Object.keys(candidate.submitMap || {}).length > 0;
|
|
6347
|
+
const optionsMatch = hookOptionsMatchPaneOptions(p.paneOptions, candidate.options);
|
|
6348
|
+
const exactControlsMatch = adoptedFallbackOptions || candidateIsActionable && optionsMatch && (p.paneOptions?.length ?? 0) === (candidate.options?.length ?? 0) && (p.paneOptions ?? []).every(
|
|
6349
|
+
(o, i) => o.number === candidate.options[i]?.number && JSON.stringify(p.submitMap?.[o.number]) === JSON.stringify(candidate.submitMap?.[o.number])
|
|
6350
|
+
);
|
|
6351
|
+
if (p.paneHash && p.paneHash !== candidate.paneHash) {
|
|
6352
|
+
this.paneOnlyEmittedHashes.delete(p.paneHash);
|
|
6187
6353
|
}
|
|
6188
|
-
p.paneSemanticKey = candidateSemanticKey;
|
|
6189
6354
|
p.paneHash = candidate.paneHash;
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6355
|
+
if (lineMatch) {
|
|
6356
|
+
p.matchedPaneHeader = lineMatch.stitchedHeaderText;
|
|
6357
|
+
p.paneDisplayHeader = lineMatch.stitchedFullHeaderLine;
|
|
6358
|
+
p.body = lineMatch.remainingBody || void 0;
|
|
6359
|
+
if (p.pendingCall) {
|
|
6360
|
+
p.pendingCall.questionText = lineMatch.stitchedHeaderText;
|
|
6361
|
+
}
|
|
6362
|
+
} else {
|
|
6363
|
+
p.body = nextBody;
|
|
6364
|
+
}
|
|
6365
|
+
p.paneBodyObserved = true;
|
|
6366
|
+
if (exactControlsMatch) {
|
|
6367
|
+
p.paneSemanticKey = candidateSemanticKey;
|
|
6368
|
+
} else if ((!candidateIsActionable || !optionsMatch) && Object.keys(p.submitMap || {}).length > 0) {
|
|
6369
|
+
p.submitMap = {};
|
|
6370
|
+
if (p.paneOptions && p.paneOptions.length > 0 && p.paneOptions[p.paneOptions.length - 1]?.isGeneratedWriteIn) {
|
|
6371
|
+
p.paneOptions = p.paneOptions.slice(0, -1);
|
|
6372
|
+
}
|
|
6373
|
+
p.paneSemanticKey = candidateSemanticKey;
|
|
6374
|
+
this.emit("prompt-upgraded", p);
|
|
6375
|
+
}
|
|
6193
6376
|
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
6194
|
-
|
|
6195
|
-
promptId: p.promptId,
|
|
6196
|
-
optionsCount: p.paneOptions?.length ?? 0
|
|
6197
|
-
});
|
|
6198
|
-
this.emit("prompt-upgraded", p);
|
|
6199
|
-
return p;
|
|
6377
|
+
return null;
|
|
6200
6378
|
}
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
p.
|
|
6208
|
-
|
|
6209
|
-
p.pendingCall.questionText = lineMatch.stitchedHeaderText;
|
|
6379
|
+
}
|
|
6380
|
+
if (!p.isHookPrompt) {
|
|
6381
|
+
const isControlDelta = Boolean(
|
|
6382
|
+
p.paneSemanticKey && candidateSemanticKey !== p.paneSemanticKey
|
|
6383
|
+
);
|
|
6384
|
+
if (!isControlDelta) {
|
|
6385
|
+
if (!p.paneSemanticKey) {
|
|
6386
|
+
p.paneSemanticKey = candidateSemanticKey;
|
|
6210
6387
|
}
|
|
6388
|
+
if (lineMatch) {
|
|
6389
|
+
p.matchedPaneHeader = lineMatch.stitchedHeaderText;
|
|
6390
|
+
p.paneDisplayHeader = lineMatch.stitchedFullHeaderLine;
|
|
6391
|
+
p.body = lineMatch.remainingBody || void 0;
|
|
6392
|
+
if (p.pendingCall) {
|
|
6393
|
+
p.pendingCall.questionText = lineMatch.stitchedHeaderText;
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6396
|
+
p.paneBodyObserved = true;
|
|
6397
|
+
if (candidate.paneHash) this.paneOnlyEmittedHashes.add(candidate.paneHash);
|
|
6398
|
+
return null;
|
|
6211
6399
|
}
|
|
6212
|
-
|
|
6213
|
-
|
|
6400
|
+
} else {
|
|
6401
|
+
replacedHookCall = p.pendingCall;
|
|
6402
|
+
replacedWasHookPrompt = true;
|
|
6403
|
+
replacedHookWasDisplayOnly = Object.keys(p.submitMap || {}).length === 0;
|
|
6214
6404
|
}
|
|
6215
6405
|
}
|
|
6216
6406
|
this.resolvePrompt(p.promptId);
|
|
@@ -6221,14 +6411,35 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6221
6411
|
}
|
|
6222
6412
|
const promptId = forcedPromptId ?? (0, import_uuid2.v4)();
|
|
6223
6413
|
const liveWizard = candidate.kind === "question" ? this.liveWizardInvocations.get(conversationId) : void 0;
|
|
6224
|
-
const matchingCall = liveWizard ? liveWizard.call : candidate.kind === "question" ? this.findMatchingPendingCall(candidate, conversationId) : null;
|
|
6414
|
+
const matchingCall = liveWizard ? liveWizard.call : candidate.kind === "question" ? this.findMatchingPendingCall(candidate, conversationId) ?? replacedHookCall ?? null : null;
|
|
6225
6415
|
if (liveWizard) {
|
|
6226
6416
|
liveWizard.promptId = promptId;
|
|
6227
6417
|
} else if (matchingCall) {
|
|
6228
6418
|
matchingCall.matchedPromptId = promptId;
|
|
6229
6419
|
}
|
|
6230
|
-
const
|
|
6420
|
+
const matchedStructuredQuestion = candidate.kind === "question" ? liveWizard?.call.structuredQuestions?.[liveWizard.activeStepIndex ?? 0] ?? matchingCall?.structuredQuestions?.find((sq) => paneCandidateMatchesQuestion(sq.question, candidate)) : void 0;
|
|
6421
|
+
const matchedStepQuestionText = candidate.kind === "question" ? matchedStructuredQuestion?.question ?? matchingCall?.questionText : void 0;
|
|
6231
6422
|
const synthLineMatch = candidate.kind === "question" && matchedStepQuestionText ? matchPaneQuestionLines(matchedStepQuestionText, candidate) : null;
|
|
6423
|
+
const activeHookStepOpts = candidate.kind === "question" ? matchedStructuredQuestion?.options ?? matchingCall?.questionOptions : void 0;
|
|
6424
|
+
const isHookRestrictedMultiSelect = Boolean(
|
|
6425
|
+
matchingCall?.isMultiSelect || matchedStructuredQuestion?.isMultiSelect
|
|
6426
|
+
);
|
|
6427
|
+
const isHookRestrictedTruncated = Boolean(
|
|
6428
|
+
matchingCall?.optionsTruncated || matchedStructuredQuestion?.optionsTruncated
|
|
6429
|
+
);
|
|
6430
|
+
const hasExplicitEmptyHookStepOpts = Boolean(
|
|
6431
|
+
matchedStructuredQuestion && Array.isArray(matchedStructuredQuestion.options) && matchedStructuredQuestion.options.length === 0
|
|
6432
|
+
);
|
|
6433
|
+
const hookRawStepOpts = (activeHookStepOpts ?? []).map(
|
|
6434
|
+
(o, idx) => typeof o === "string" ? { number: String(idx + 1), text: o } : { number: String(o.number ?? idx + 1), text: o.text }
|
|
6435
|
+
);
|
|
6436
|
+
const hasHookStepOptionMismatch = candidate.kind === "question" && candidate.options.length > 0 && (hasExplicitEmptyHookStepOpts || hookRawStepOpts.length > 0 && !hookOptionsMatchPaneOptions(hookRawStepOpts, candidate.options));
|
|
6437
|
+
const isHookCall = Boolean(
|
|
6438
|
+
replacedWasHookPrompt || matchingCall?.structuredQuestions && matchingCall.structuredQuestions.length > 0
|
|
6439
|
+
);
|
|
6440
|
+
const isHookRestricted = candidate.kind === "question" && isHookCall && (replacedHookWasDisplayOnly || isHookRestrictedMultiSelect || isHookRestrictedTruncated || hasHookStepOptionMismatch);
|
|
6441
|
+
const effectiveSubmitMap = isHookRestricted ? {} : candidate.submitMap;
|
|
6442
|
+
const taggedCandidateOptions = candidate.kind === "question" ? isHookRestricted ? candidate.options : tagPaneGeneratedWriteInOption(candidate.options, activeHookStepOpts) : candidate.options;
|
|
6232
6443
|
const syntheticCall = {
|
|
6233
6444
|
conversationId,
|
|
6234
6445
|
intentStepIndex: matchingCall?.intentStepIndex ?? -1,
|
|
@@ -6246,26 +6457,26 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6246
6457
|
// NOTE (Codex R3-F1 / Claude R3-F2): Pane options are authoritative ground truth
|
|
6247
6458
|
// that directly correspond to desktop keystrokes (submitMap). Transcript options
|
|
6248
6459
|
// serve solely as fallback when pane options cannot be parsed.
|
|
6249
|
-
questionOptions: candidate.kind === "question" &&
|
|
6460
|
+
questionOptions: candidate.kind === "question" && taggedCandidateOptions && taggedCandidateOptions.length > 0 ? taggedCandidateOptions : matchingCall?.questionOptions,
|
|
6250
6461
|
structuredQuestions: matchingCall?.structuredQuestions,
|
|
6462
|
+
isMultiSelect: matchingCall?.isMultiSelect ?? matchedStructuredQuestion?.isMultiSelect,
|
|
6463
|
+
optionsTruncated: matchingCall?.optionsTruncated ?? matchedStructuredQuestion?.optionsTruncated,
|
|
6251
6464
|
promptId,
|
|
6252
6465
|
startedAt: matchingCall?.startedAt ?? Date.now(),
|
|
6253
6466
|
intentCreatedAt: matchingCall?.intentCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6254
6467
|
};
|
|
6255
|
-
const isHookCall = Boolean(
|
|
6256
|
-
matchingCall?.structuredQuestions && matchingCall.structuredQuestions.length > 0
|
|
6257
|
-
);
|
|
6258
6468
|
const state = {
|
|
6259
6469
|
promptId,
|
|
6260
6470
|
conversationId,
|
|
6261
6471
|
pendingCall: syntheticCall,
|
|
6262
|
-
submitMap:
|
|
6472
|
+
submitMap: effectiveSubmitMap,
|
|
6263
6473
|
matchedPaneHeader: synthLineMatch?.stitchedHeaderText ?? candidate.headerText,
|
|
6264
6474
|
paneDisplayHeader: synthLineMatch?.stitchedFullHeaderLine ?? candidate.fullHeaderLine,
|
|
6265
6475
|
body: synthLineMatch ? synthLineMatch.remainingBody || void 0 : candidate.body,
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6476
|
+
paneBodyObserved: true,
|
|
6477
|
+
paneOptions: taggedCandidateOptions,
|
|
6478
|
+
paneSemanticKey: buildApprovalSemanticKey({ ...candidate, submitMap: effectiveSubmitMap }),
|
|
6479
|
+
paneRedrawSemanticKey: buildApprovalRedrawSemanticKey({ ...candidate, submitMap: effectiveSubmitMap }),
|
|
6269
6480
|
kind: candidate.kind,
|
|
6270
6481
|
isHookPrompt: isHookCall,
|
|
6271
6482
|
// Carry hook ownership forward so pane redraws do not drop it! (Codex S2-Codex-R6-F15)
|
|
@@ -6315,6 +6526,15 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6315
6526
|
if (p.pendingCall) {
|
|
6316
6527
|
p.pendingCall.questionText = hookLineMatch?.stitchedHeaderText ?? call.questionText ?? p.pendingCall.questionText;
|
|
6317
6528
|
p.pendingCall.structuredQuestions = call.structuredQuestions;
|
|
6529
|
+
if (call.questionOptions) {
|
|
6530
|
+
p.pendingCall.questionOptions = call.questionOptions;
|
|
6531
|
+
}
|
|
6532
|
+
if (call.isMultiSelect !== void 0) {
|
|
6533
|
+
p.pendingCall.isMultiSelect = call.isMultiSelect;
|
|
6534
|
+
}
|
|
6535
|
+
if (call.optionsTruncated !== void 0) {
|
|
6536
|
+
p.pendingCall.optionsTruncated = call.optionsTruncated;
|
|
6537
|
+
}
|
|
6318
6538
|
if (call.intentStepIndex !== -1) {
|
|
6319
6539
|
p.pendingCall.intentStepIndex = call.intentStepIndex;
|
|
6320
6540
|
}
|
|
@@ -6323,25 +6543,38 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6323
6543
|
} else {
|
|
6324
6544
|
p.pendingCall = call;
|
|
6325
6545
|
}
|
|
6326
|
-
const
|
|
6546
|
+
const isHookMultiSelect2 = Boolean(
|
|
6327
6547
|
call.isMultiSelect || call.structuredQuestions && call.structuredQuestions[0]?.isMultiSelect
|
|
6328
6548
|
);
|
|
6329
|
-
const
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
const
|
|
6333
|
-
|
|
6334
|
-
|
|
6549
|
+
const isHookOptionsTruncated2 = Boolean(
|
|
6550
|
+
call.optionsTruncated || call.structuredQuestions && call.structuredQuestions[0]?.optionsTruncated
|
|
6551
|
+
);
|
|
6552
|
+
const hookRawOpts = (call.structuredQuestions?.[0]?.options ?? call.questionOptions ?? []).map(
|
|
6553
|
+
(o, idx) => typeof o === "string" ? { number: String(idx + 1), text: o } : { number: String(o.number ?? idx + 1), text: o.text }
|
|
6554
|
+
);
|
|
6555
|
+
const paneOptsNormalized = (p.paneOptions || []).map(
|
|
6556
|
+
(o, idx) => typeof o === "string" ? { number: String(idx + 1), text: o } : {
|
|
6557
|
+
number: String(o.number ?? idx + 1),
|
|
6558
|
+
text: o.text,
|
|
6559
|
+
isGeneratedWriteIn: Boolean(o.isGeneratedWriteIn)
|
|
6560
|
+
}
|
|
6561
|
+
);
|
|
6335
6562
|
const hasStructuredQuestion = Boolean(call.structuredQuestions && call.structuredQuestions.length > 0);
|
|
6336
|
-
const hasHookOptionMismatch =
|
|
6337
|
-
if ((
|
|
6563
|
+
const hasHookOptionMismatch = paneOptsNormalized.length > 0 && (hasStructuredQuestion && hookRawOpts.length === 0 || hookRawOpts.length > 0 && !hookOptionsMatchPaneOptions(hookRawOpts, paneOptsNormalized));
|
|
6564
|
+
if ((isHookMultiSelect2 || isHookOptionsTruncated2 || hasHookOptionMismatch) && Object.keys(p.submitMap || {}).length > 0) {
|
|
6338
6565
|
p.submitMap = {};
|
|
6339
|
-
logger.info("emitHookQuestionPrompt: revoked actionable submitMap due to multi-select or option mismatch", {
|
|
6566
|
+
logger.info("emitHookQuestionPrompt: revoked actionable submitMap due to multi-select, truncation, or option mismatch", {
|
|
6340
6567
|
promptId: p.promptId,
|
|
6341
|
-
isHookMultiSelect,
|
|
6568
|
+
isHookMultiSelect: isHookMultiSelect2,
|
|
6569
|
+
isHookOptionsTruncated: isHookOptionsTruncated2,
|
|
6342
6570
|
hasHookOptionMismatch
|
|
6343
6571
|
});
|
|
6344
6572
|
this.emit("prompt-upgraded", p);
|
|
6573
|
+
} else if (p.paneOptions && p.paneOptions.length > 0 && Object.keys(p.submitMap || {}).length > 0) {
|
|
6574
|
+
p.paneOptions = tagPaneGeneratedWriteInOption(
|
|
6575
|
+
p.paneOptions,
|
|
6576
|
+
hookRawOpts.length > 0 ? hookRawOpts : void 0
|
|
6577
|
+
);
|
|
6345
6578
|
}
|
|
6346
6579
|
p.isHookPrompt = true;
|
|
6347
6580
|
call.matchedPromptId = p.promptId;
|
|
@@ -6372,21 +6605,38 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6372
6605
|
const promptId = forcedPromptId ?? (0, import_uuid2.v4)();
|
|
6373
6606
|
this.registerPendingCall(call);
|
|
6374
6607
|
call.matchedPromptId = promptId;
|
|
6608
|
+
const firstStructured = call.structuredQuestions?.[0];
|
|
6609
|
+
const isHookMultiSelect = Boolean(
|
|
6610
|
+
call.isMultiSelect || firstStructured?.isMultiSelect
|
|
6611
|
+
);
|
|
6612
|
+
const isHookOptionsTruncated = Boolean(
|
|
6613
|
+
call.optionsTruncated || firstStructured?.optionsTruncated
|
|
6614
|
+
);
|
|
6615
|
+
const hasExplicitEmptyStructuredOptions = Boolean(
|
|
6616
|
+
firstStructured && Array.isArray(firstStructured.options) && firstStructured.options.length === 0
|
|
6617
|
+
);
|
|
6618
|
+
const rawOptions = hasExplicitEmptyStructuredOptions ? [] : firstStructured?.options && firstStructured.options.length > 0 ? firstStructured.options : call.questionOptions;
|
|
6619
|
+
const { paneOptions, submitMap } = buildHookQuestionOptionsAndSubmitMap(
|
|
6620
|
+
rawOptions,
|
|
6621
|
+
isHookMultiSelect,
|
|
6622
|
+
isHookOptionsTruncated
|
|
6623
|
+
);
|
|
6375
6624
|
const state = {
|
|
6376
6625
|
promptId,
|
|
6377
6626
|
conversationId: call.conversationId,
|
|
6378
6627
|
pendingCall: call,
|
|
6379
|
-
submitMap
|
|
6628
|
+
submitMap,
|
|
6380
6629
|
matchedPaneHeader: call.questionText ?? "Question",
|
|
6381
6630
|
paneDisplayHeader: call.questionText ?? "Question",
|
|
6382
6631
|
body: void 0,
|
|
6383
|
-
|
|
6632
|
+
paneBodyObserved: false,
|
|
6633
|
+
paneOptions,
|
|
6384
6634
|
paneSemanticKey: buildApprovalSemanticKey({
|
|
6385
6635
|
kind: "question",
|
|
6386
6636
|
headerText: call.questionText ?? "Question",
|
|
6387
6637
|
fullHeaderLine: call.questionText ?? "Question",
|
|
6388
|
-
options:
|
|
6389
|
-
submitMap
|
|
6638
|
+
options: paneOptions ?? [],
|
|
6639
|
+
submitMap,
|
|
6390
6640
|
body: "",
|
|
6391
6641
|
paneHash: ""
|
|
6392
6642
|
}),
|
|
@@ -6409,7 +6659,8 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
6409
6659
|
promptId,
|
|
6410
6660
|
conversationId: call.conversationId,
|
|
6411
6661
|
questionText: call.questionText,
|
|
6412
|
-
optionsCount:
|
|
6662
|
+
optionsCount: paneOptions?.length ?? 0,
|
|
6663
|
+
mobileActionable: Object.keys(submitMap).length > 0
|
|
6413
6664
|
});
|
|
6414
6665
|
this.emit("pending-prompt", state);
|
|
6415
6666
|
return state;
|
|
@@ -6920,9 +7171,13 @@ var PromptResponder = class {
|
|
|
6920
7171
|
};
|
|
6921
7172
|
}
|
|
6922
7173
|
const isQuestionPrompt = state.kind === "question";
|
|
6923
|
-
|
|
7174
|
+
let expectedStep = (state.paneDisplayHeader || "").match(/\bQuestion\s+(\d+(?:[\/:of\s]+\d+)?):/i)?.[1];
|
|
6924
7175
|
const matchingOpt = state.paneOptions?.find((o) => String(o.number) === String(optionNumber));
|
|
6925
|
-
const
|
|
7176
|
+
const activeStepHookOpts = state.pendingCall?.structuredQuestions?.[state.wizardStepIndex ?? 0]?.options ?? state.pendingCall?.questionOptions;
|
|
7177
|
+
const hookBaseOptCount = activeStepHookOpts ? activeStepHookOpts.filter((o) => !o.isGeneratedWriteIn).length : 0;
|
|
7178
|
+
const isWriteInOption = matchingOpt ? Boolean(
|
|
7179
|
+
matchingOpt.isGeneratedWriteIn || matchingOpt === state.paneOptions?.[state.paneOptions.length - 1] && /^\s*write-?in(?:\.{3}|…)?\s*$/i.test(matchingOpt.text) && (!state.isHookPrompt || hookBaseOptCount === 0 || (state.paneOptions?.length ?? 0) === hookBaseOptCount + 1)
|
|
7180
|
+
) : false;
|
|
6926
7181
|
if (isQuestionPrompt) {
|
|
6927
7182
|
if (state.paneOptions && state.paneOptions.length > 0 && !matchingOpt) {
|
|
6928
7183
|
return {
|
|
@@ -6980,9 +7235,10 @@ var PromptResponder = class {
|
|
|
6980
7235
|
};
|
|
6981
7236
|
}
|
|
6982
7237
|
let liveSemanticKey = null;
|
|
7238
|
+
let liveCandidate = null;
|
|
6983
7239
|
try {
|
|
6984
|
-
|
|
6985
|
-
|
|
7240
|
+
let snapshot = await this.paneObserver.captureSnapshot();
|
|
7241
|
+
let postCapturePrompt = this.detector.getPendingPrompt(promptId);
|
|
6986
7242
|
if (postCapturePrompt !== state || !postCapturePrompt.submitMap || !postCapturePrompt.submitMap[optionNumber]) {
|
|
6987
7243
|
return {
|
|
6988
7244
|
ok: false,
|
|
@@ -6999,7 +7255,35 @@ var PromptResponder = class {
|
|
|
6999
7255
|
deliveryPhase: "none"
|
|
7000
7256
|
};
|
|
7001
7257
|
}
|
|
7002
|
-
|
|
7258
|
+
let live = parseApprovalSnapshot(snapshot);
|
|
7259
|
+
if (!live && isQuestionPrompt && state.isHookPrompt && !state.paneHash && detectActivePromptKind(snapshot) === null) {
|
|
7260
|
+
const waitDeadline = Date.now() + 250;
|
|
7261
|
+
while (!live && Date.now() < waitDeadline) {
|
|
7262
|
+
await delay(50);
|
|
7263
|
+
snapshot = await this.paneObserver.captureSnapshot();
|
|
7264
|
+
postCapturePrompt = this.detector.getPendingPrompt(promptId);
|
|
7265
|
+
if (postCapturePrompt !== state || !postCapturePrompt.submitMap || !postCapturePrompt.submitMap[optionNumber]) {
|
|
7266
|
+
return {
|
|
7267
|
+
ok: false,
|
|
7268
|
+
reason: "prompt-expired",
|
|
7269
|
+
details: "option is no longer actionable on active prompt (controls revoked or updated)",
|
|
7270
|
+
deliveryPhase
|
|
7271
|
+
};
|
|
7272
|
+
}
|
|
7273
|
+
live = parseApprovalSnapshot(snapshot);
|
|
7274
|
+
if (!live && detectActivePromptKind(snapshot) !== null) {
|
|
7275
|
+
break;
|
|
7276
|
+
}
|
|
7277
|
+
}
|
|
7278
|
+
if (!live && detectActivePromptKind(snapshot) === null && !state.paneHash) {
|
|
7279
|
+
return {
|
|
7280
|
+
ok: false,
|
|
7281
|
+
reason: "prompt-probe-failed",
|
|
7282
|
+
details: "the question chooser has not rendered on the terminal yet; please retry in a moment",
|
|
7283
|
+
deliveryPhase
|
|
7284
|
+
};
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7003
7287
|
if (!live) {
|
|
7004
7288
|
if (detectActivePromptKind(snapshot) === null) {
|
|
7005
7289
|
this.detector.resolvePrompt(promptId);
|
|
@@ -7017,6 +7301,7 @@ var PromptResponder = class {
|
|
|
7017
7301
|
deliveryPhase
|
|
7018
7302
|
};
|
|
7019
7303
|
}
|
|
7304
|
+
liveCandidate = live;
|
|
7020
7305
|
liveSemanticKey = live ? buildApprovalSemanticKey(live) : null;
|
|
7021
7306
|
} catch (error) {
|
|
7022
7307
|
return {
|
|
@@ -7026,15 +7311,55 @@ var PromptResponder = class {
|
|
|
7026
7311
|
deliveryPhase
|
|
7027
7312
|
};
|
|
7028
7313
|
}
|
|
7029
|
-
if (
|
|
7314
|
+
if (!liveCandidate || !liveCandidate.submitMap?.[optionNumber]?.length) {
|
|
7030
7315
|
this.detector.resolvePrompt(promptId);
|
|
7031
7316
|
return {
|
|
7032
7317
|
ok: false,
|
|
7033
7318
|
reason: "prompt-superseded",
|
|
7034
|
-
details: "active chooser
|
|
7319
|
+
details: "active chooser does not allow actionable submission for this option",
|
|
7035
7320
|
deliveryPhase
|
|
7036
7321
|
};
|
|
7037
7322
|
}
|
|
7323
|
+
if (liveSemanticKey !== state.paneSemanticKey) {
|
|
7324
|
+
let hookQuestionMatchesLive = false;
|
|
7325
|
+
if (isQuestionPrompt && state.isHookPrompt && liveCandidate && liveCandidate.kind === "question") {
|
|
7326
|
+
const activeStepQ = state.pendingCall?.structuredQuestions?.[state.wizardStepIndex ?? 0];
|
|
7327
|
+
const hookQuestionText = activeStepQ?.question ?? state.pendingCall?.questionText ?? state.matchedPaneHeader;
|
|
7328
|
+
const lineMatch = matchPaneQuestionLines(hookQuestionText, liveCandidate);
|
|
7329
|
+
const liveRemainingBody = lineMatch ? lineMatch.remainingBody || void 0 : liveCandidate.body || void 0;
|
|
7330
|
+
const bodyConsistent = !state.paneBodyObserved || state.body === liveRemainingBody;
|
|
7331
|
+
const normOpt = (s) => s.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
7332
|
+
const optionsMatch = hookOptionsMatchPaneOptions(state.paneOptions, liveCandidate.options);
|
|
7333
|
+
const selectedLiveOpt = (liveCandidate.options ?? []).find(
|
|
7334
|
+
(o) => String(o.number) === String(optionNumber)
|
|
7335
|
+
);
|
|
7336
|
+
const selectedOptMatches = isWriteInOption ? Boolean(selectedLiveOpt && /^\s*write-?in(?:\.{3}|…)?\s*$/i.test(selectedLiveOpt.text)) : Boolean(
|
|
7337
|
+
selectedLiveOpt && matchingOpt && normOpt(selectedLiveOpt.text) === normOpt(matchingOpt.text)
|
|
7338
|
+
);
|
|
7339
|
+
if (lineMatch !== null && bodyConsistent && optionsMatch && selectedOptMatches && Boolean(liveCandidate.submitMap[optionNumber]?.length)) {
|
|
7340
|
+
hookQuestionMatchesLive = true;
|
|
7341
|
+
state.matchedPaneHeader = lineMatch.stitchedHeaderText;
|
|
7342
|
+
state.paneDisplayHeader = lineMatch.stitchedFullHeaderLine;
|
|
7343
|
+
state.body = liveRemainingBody;
|
|
7344
|
+
state.paneBodyObserved = true;
|
|
7345
|
+
if (liveSemanticKey) {
|
|
7346
|
+
state.paneSemanticKey = liveSemanticKey;
|
|
7347
|
+
}
|
|
7348
|
+
expectedStep = (state.paneDisplayHeader || "").match(/\bQuestion\s+(\d+(?:[\/:of\s]+\d+)?):/i)?.[1];
|
|
7349
|
+
}
|
|
7350
|
+
}
|
|
7351
|
+
if (!hookQuestionMatchesLive) {
|
|
7352
|
+
this.detector.resolvePrompt(promptId);
|
|
7353
|
+
return {
|
|
7354
|
+
ok: false,
|
|
7355
|
+
reason: "prompt-superseded",
|
|
7356
|
+
details: "active chooser semantics differ from the mobile prompt",
|
|
7357
|
+
deliveryPhase
|
|
7358
|
+
};
|
|
7359
|
+
}
|
|
7360
|
+
} else if (!state.paneBodyObserved) {
|
|
7361
|
+
state.paneBodyObserved = true;
|
|
7362
|
+
}
|
|
7038
7363
|
deliveryPhase = "option_attempted";
|
|
7039
7364
|
if (isNamedKey(key)) {
|
|
7040
7365
|
await this.sendKey(target, key);
|
|
@@ -9101,6 +9426,7 @@ var McpServer = class _McpServer {
|
|
|
9101
9426
|
questionText,
|
|
9102
9427
|
questionOptions,
|
|
9103
9428
|
structuredQuestions,
|
|
9429
|
+
optionsTruncated: Boolean(structuredQuestions?.[0]?.optionsTruncated),
|
|
9104
9430
|
startedAt: Date.now(),
|
|
9105
9431
|
intentCreatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9106
9432
|
};
|
|
@@ -9128,7 +9454,19 @@ var McpServer = class _McpServer {
|
|
|
9128
9454
|
});
|
|
9129
9455
|
return;
|
|
9130
9456
|
}
|
|
9131
|
-
const
|
|
9457
|
+
const firstStructured = pendingCall.structuredQuestions?.[0];
|
|
9458
|
+
const isMultiSelect = Boolean(
|
|
9459
|
+
pendingCall.isMultiSelect || firstStructured?.isMultiSelect
|
|
9460
|
+
);
|
|
9461
|
+
const optionsTruncated = Boolean(
|
|
9462
|
+
pendingCall.optionsTruncated || firstStructured?.optionsTruncated
|
|
9463
|
+
);
|
|
9464
|
+
const hasExplicitEmptyStructuredOptions = Boolean(
|
|
9465
|
+
firstStructured && Array.isArray(firstStructured.options) && firstStructured.options.length === 0
|
|
9466
|
+
);
|
|
9467
|
+
const rawOpts = hasExplicitEmptyStructuredOptions ? [] : firstStructured?.options && firstStructured.options.length > 0 ? firstStructured.options : pendingCall.questionOptions;
|
|
9468
|
+
const isInitiallyActionable = !isMultiSelect && !optionsTruncated && Boolean(rawOpts && rawOpts.length > 0 && rawOpts.length <= 8);
|
|
9469
|
+
const e1rec = this.newE1Raise(session.sessionId, isInitiallyActionable);
|
|
9132
9470
|
const emitted = this.approvalDetector.emitHookQuestionPrompt(pendingCall, e1rec.promptId);
|
|
9133
9471
|
if (!emitted) {
|
|
9134
9472
|
this.e1PromptRaises.delete(e1rec.promptId);
|
|
@@ -10568,7 +10906,7 @@ var McpServer = class _McpServer {
|
|
|
10568
10906
|
} else if (optionResult.reason === "prompt-expired" || optionResult.reason === "prompt-superseded") {
|
|
10569
10907
|
message = "The question prompt on desktop has expired or changed. Please review on desktop.";
|
|
10570
10908
|
} else if (optionResult.reason === "prompt-probe-failed") {
|
|
10571
|
-
message = "Write-in input field was not ready on desktop. Please try again or respond on desktop.";
|
|
10909
|
+
message = optionResult.details?.includes("has not rendered on the terminal yet") ? optionResult.details : intent.followUpText ? "Write-in input field was not ready on desktop. Please try again or respond on desktop." : "Question prompt was not ready on desktop. Please try again or respond on desktop.";
|
|
10572
10910
|
} else if (optionResult.reason === "no-tmux-target" || optionResult.reason === "tmux-failed") {
|
|
10573
10911
|
message = optionResult.details || "Desktop terminal session is unreachable. Please respond directly on desktop.";
|
|
10574
10912
|
} else if (optionResult.reason === "display-only-question-active") {
|
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.104",
|
|
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": {
|