@quanta-intellect/vessel-browser 0.1.13 → 0.1.14
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/out/main/index.js
CHANGED
|
@@ -710,7 +710,8 @@ async function highlightOnPage(wc, resolvedSelector, text, label, durationMs, co
|
|
|
710
710
|
if (text) {
|
|
711
711
|
return wc.executeJavaScript(`
|
|
712
712
|
(function() {
|
|
713
|
-
var searchText = ${JSON.stringify(text)};
|
|
713
|
+
var searchText = (${JSON.stringify(text)} || '').trim();
|
|
714
|
+
var foldedSearchText = searchText.toLowerCase();
|
|
714
715
|
var solidColor = ${JSON.stringify(c.solid)};
|
|
715
716
|
var bgColor = ${JSON.stringify(c.bg)};
|
|
716
717
|
var labelBg = ${JSON.stringify(c.label)};
|
|
@@ -748,7 +749,11 @@ async function highlightOnPage(wc, resolvedSelector, text, label, durationMs, co
|
|
|
748
749
|
});
|
|
749
750
|
var n;
|
|
750
751
|
while ((n = w.nextNode())) {
|
|
751
|
-
var
|
|
752
|
+
var haystack = n.textContent || '';
|
|
753
|
+
var idx = haystack.indexOf(searchText);
|
|
754
|
+
if (idx === -1 && foldedSearchText) {
|
|
755
|
+
idx = haystack.toLowerCase().indexOf(foldedSearchText);
|
|
756
|
+
}
|
|
752
757
|
if (idx !== -1) {
|
|
753
758
|
matches.push({ node: n, idx: idx });
|
|
754
759
|
if (matches.length >= limit) break;
|
|
@@ -4571,6 +4576,158 @@ function createProvider(config) {
|
|
|
4571
4576
|
}
|
|
4572
4577
|
return new OpenAICompatProvider(normalized);
|
|
4573
4578
|
}
|
|
4579
|
+
const CORRECT_HINT_RE = /\b(correct|right choice|this is correct|correct answer|pick this|select this|choose this|right answer)\b/i;
|
|
4580
|
+
const WRONG_HINT_RE = /\b(wrong|incorrect|not this|don't pick|do not pick|bad option|decoy)\b/i;
|
|
4581
|
+
function elementLabel(el) {
|
|
4582
|
+
return el.text?.trim() || el.label?.trim() || el.value?.trim() || el.placeholder?.trim() || void 0;
|
|
4583
|
+
}
|
|
4584
|
+
function isOverlayAction(el) {
|
|
4585
|
+
if (el.type === "button" || el.type === "link") return true;
|
|
4586
|
+
if (el.type !== "input") return false;
|
|
4587
|
+
return ["button", "submit", "radio", "checkbox"].includes(
|
|
4588
|
+
(el.inputType || "").toLowerCase()
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
function isRadioOption(el) {
|
|
4592
|
+
return el.role === "radio" || el.type === "input" && (el.inputType || "").toLowerCase() === "radio";
|
|
4593
|
+
}
|
|
4594
|
+
function normalizeAction(el) {
|
|
4595
|
+
return {
|
|
4596
|
+
index: el.index,
|
|
4597
|
+
label: elementLabel(el),
|
|
4598
|
+
selector: el.selector,
|
|
4599
|
+
role: el.role,
|
|
4600
|
+
labelSource: el.labelSource,
|
|
4601
|
+
looksCorrect: el.looksCorrect !== void 0 ? el.looksCorrect : looksLikeCorrectOption(elementLabel(el))
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4604
|
+
function normalizeStoredAction(action) {
|
|
4605
|
+
return {
|
|
4606
|
+
label: action.label,
|
|
4607
|
+
selector: action.selector,
|
|
4608
|
+
role: action.kind === "radio" ? "radio" : void 0
|
|
4609
|
+
};
|
|
4610
|
+
}
|
|
4611
|
+
function normalizeStoredRadioOption(option) {
|
|
4612
|
+
return {
|
|
4613
|
+
label: option.label,
|
|
4614
|
+
selector: option.selector,
|
|
4615
|
+
role: "radio",
|
|
4616
|
+
labelSource: option.labelSource,
|
|
4617
|
+
looksCorrect: option.looksCorrect !== void 0 ? option.looksCorrect : looksLikeCorrectOption(option.label)
|
|
4618
|
+
};
|
|
4619
|
+
}
|
|
4620
|
+
function dedupeCandidates(actions) {
|
|
4621
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4622
|
+
return actions.filter((action) => {
|
|
4623
|
+
const key = [
|
|
4624
|
+
action.selector || "",
|
|
4625
|
+
action.label || "",
|
|
4626
|
+
action.role || "",
|
|
4627
|
+
action.labelSource || ""
|
|
4628
|
+
].join("::");
|
|
4629
|
+
if (seen.has(key)) return false;
|
|
4630
|
+
seen.add(key);
|
|
4631
|
+
return true;
|
|
4632
|
+
});
|
|
4633
|
+
}
|
|
4634
|
+
function classifyOverlayKind(overlay, radioOptions) {
|
|
4635
|
+
if (overlay.kind) {
|
|
4636
|
+
return overlay.kind;
|
|
4637
|
+
}
|
|
4638
|
+
const haystack = [overlay.label, overlay.text, overlay.role].filter(Boolean).join(" ").toLowerCase();
|
|
4639
|
+
if (/cookie|consent|privacy|gdpr|ccpa|onetrust|trustarc|cookiebot/.test(
|
|
4640
|
+
haystack
|
|
4641
|
+
)) {
|
|
4642
|
+
return "cookie_consent";
|
|
4643
|
+
}
|
|
4644
|
+
if (radioOptions.length > 0) return "selection_modal";
|
|
4645
|
+
if (overlay.role === "alertdialog" || /\b(alert|warning|error)\b/.test(haystack)) {
|
|
4646
|
+
return "alert";
|
|
4647
|
+
}
|
|
4648
|
+
if (overlay.type === "dialog") return "dialog";
|
|
4649
|
+
if (overlay.type === "modal") return "modal";
|
|
4650
|
+
return "overlay";
|
|
4651
|
+
}
|
|
4652
|
+
function findAction(actions, matcher) {
|
|
4653
|
+
return actions.find(
|
|
4654
|
+
(action) => matcher.test((action.label || "").toLowerCase())
|
|
4655
|
+
);
|
|
4656
|
+
}
|
|
4657
|
+
function looksLikeCorrectOption(label) {
|
|
4658
|
+
const text = label?.trim();
|
|
4659
|
+
if (!text) return void 0;
|
|
4660
|
+
if (CORRECT_HINT_RE.test(text)) return true;
|
|
4661
|
+
if (WRONG_HINT_RE.test(text)) return false;
|
|
4662
|
+
return void 0;
|
|
4663
|
+
}
|
|
4664
|
+
function getBlockingOverlaySignature(overlays) {
|
|
4665
|
+
return overlays.filter((overlay) => overlay.blocksInteraction).map(
|
|
4666
|
+
(overlay) => [
|
|
4667
|
+
overlay.kind,
|
|
4668
|
+
overlay.selector || "",
|
|
4669
|
+
overlay.label || "",
|
|
4670
|
+
overlay.text || "",
|
|
4671
|
+
overlay.actions.map((action) => `${action.selector || ""}:${action.label || ""}`).join("|"),
|
|
4672
|
+
overlay.radioOptions.map((option) => `${option.selector || ""}:${option.label || ""}`).join("|")
|
|
4673
|
+
].join("::")
|
|
4674
|
+
).join("||");
|
|
4675
|
+
}
|
|
4676
|
+
function buildOverlayInventory(page) {
|
|
4677
|
+
if (page.overlays.length === 0) return [];
|
|
4678
|
+
return page.overlays.map((overlay) => {
|
|
4679
|
+
const controls = dedupeCandidates([
|
|
4680
|
+
...page.interactiveElements.filter((el) => {
|
|
4681
|
+
if (overlay.selector && el.parentOverlay === overlay.selector) {
|
|
4682
|
+
return true;
|
|
4683
|
+
}
|
|
4684
|
+
return page.overlays.length === 1 && el.context === "dialog";
|
|
4685
|
+
}).filter(isOverlayAction).map(normalizeAction),
|
|
4686
|
+
...(overlay.actions || []).map(normalizeStoredAction)
|
|
4687
|
+
]).filter((action) => action.label || action.selector);
|
|
4688
|
+
const radioOptions = dedupeCandidates([
|
|
4689
|
+
...page.interactiveElements.filter((el) => {
|
|
4690
|
+
if (!isRadioOption(el)) return false;
|
|
4691
|
+
if (overlay.selector && el.parentOverlay === overlay.selector) {
|
|
4692
|
+
return true;
|
|
4693
|
+
}
|
|
4694
|
+
return page.overlays.length === 1 && el.context === "dialog";
|
|
4695
|
+
}).map(normalizeAction),
|
|
4696
|
+
...(overlay.radioOptions || []).map(normalizeStoredRadioOption)
|
|
4697
|
+
]).filter((action) => action.label || action.selector);
|
|
4698
|
+
const kind = classifyOverlayKind(overlay, radioOptions);
|
|
4699
|
+
const dismissAction = findAction(
|
|
4700
|
+
controls,
|
|
4701
|
+
/\b(close|dismiss|skip|cancel|reject|decline|no thanks|not now|maybe later|continue without)\b/
|
|
4702
|
+
);
|
|
4703
|
+
const acceptAction = findAction(
|
|
4704
|
+
controls,
|
|
4705
|
+
/\b(accept|allow|agree|got it|ok|okay|consent)\b/
|
|
4706
|
+
);
|
|
4707
|
+
const submitAction = findAction(
|
|
4708
|
+
controls,
|
|
4709
|
+
/\b(submit|continue|confirm|done|next|save|apply|finish)\b/
|
|
4710
|
+
);
|
|
4711
|
+
const correctOption = radioOptions.find(
|
|
4712
|
+
(option) => option.looksCorrect === true
|
|
4713
|
+
);
|
|
4714
|
+
return {
|
|
4715
|
+
type: overlay.type,
|
|
4716
|
+
kind,
|
|
4717
|
+
role: overlay.role,
|
|
4718
|
+
label: overlay.label,
|
|
4719
|
+
selector: overlay.selector,
|
|
4720
|
+
text: overlay.text,
|
|
4721
|
+
blocksInteraction: overlay.blocksInteraction,
|
|
4722
|
+
actions: controls,
|
|
4723
|
+
radioOptions,
|
|
4724
|
+
dismissAction,
|
|
4725
|
+
acceptAction,
|
|
4726
|
+
submitAction,
|
|
4727
|
+
correctOption
|
|
4728
|
+
};
|
|
4729
|
+
});
|
|
4730
|
+
}
|
|
4574
4731
|
const MAX_CONTENT_LENGTH = 6e4;
|
|
4575
4732
|
const MAX_STRUCTURED_ITEMS = 100;
|
|
4576
4733
|
const LARGE_PAGE_HINT_THRESHOLD = 12e3;
|
|
@@ -4638,6 +4795,14 @@ function formatElementMeta(el) {
|
|
|
4638
4795
|
if (el.pattern) {
|
|
4639
4796
|
meta.push(`pattern="${el.pattern}"`);
|
|
4640
4797
|
}
|
|
4798
|
+
if (el.labelSource) {
|
|
4799
|
+
meta.push(`source=${el.labelSource}`);
|
|
4800
|
+
}
|
|
4801
|
+
if (el.looksCorrect === true) {
|
|
4802
|
+
meta.push("likely-correct");
|
|
4803
|
+
} else if (el.looksCorrect === false) {
|
|
4804
|
+
meta.push("likely-wrong");
|
|
4805
|
+
}
|
|
4641
4806
|
if (el.description) {
|
|
4642
4807
|
meta.push(`desc="${el.description.slice(0, 80)}"`);
|
|
4643
4808
|
}
|
|
@@ -4867,7 +5032,7 @@ function formatInteractiveElements(elements) {
|
|
|
4867
5032
|
const parts = [prefix];
|
|
4868
5033
|
if (el.type === "button") {
|
|
4869
5034
|
parts.push(`[${el.text || "Button"}]`);
|
|
4870
|
-
parts.push("button");
|
|
5035
|
+
parts.push(el.role === "radio" ? "radio" : "button");
|
|
4871
5036
|
} else if (el.type === "link") {
|
|
4872
5037
|
parts.push(`[${el.text || "Link"}]`);
|
|
4873
5038
|
parts.push("link");
|
|
@@ -4932,7 +5097,7 @@ function formatForms(forms) {
|
|
|
4932
5097
|
];
|
|
4933
5098
|
if (field.type === "button") {
|
|
4934
5099
|
fieldParts.push(`[${field.text || "Submit"}]`);
|
|
4935
|
-
fieldParts.push("button");
|
|
5100
|
+
fieldParts.push(field.role === "radio" ? "radio" : "button");
|
|
4936
5101
|
} else if (field.type === "input") {
|
|
4937
5102
|
fieldParts.push(`[${field.label || field.placeholder || "Input"}]`);
|
|
4938
5103
|
fieldParts.push(field.inputType || "text");
|
|
@@ -4979,18 +5144,51 @@ function formatLandmarks(landmarks) {
|
|
|
4979
5144
|
function formatViewport(page) {
|
|
4980
5145
|
return `${page.viewport.width}x${page.viewport.height} at scroll (${page.viewport.scrollX}, ${page.viewport.scrollY})`;
|
|
4981
5146
|
}
|
|
4982
|
-
function formatOverlays(
|
|
4983
|
-
if (overlays.length === 0) return "None detected";
|
|
4984
|
-
const items = limitItems(
|
|
5147
|
+
function formatOverlays(page) {
|
|
5148
|
+
if (page.overlays.length === 0) return "None detected";
|
|
5149
|
+
const items = limitItems(buildOverlayInventory(page), 10);
|
|
4985
5150
|
return items.map((overlay) => {
|
|
4986
|
-
const
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
5151
|
+
const lines = [
|
|
5152
|
+
[
|
|
5153
|
+
`- ${overlay.kind}`,
|
|
5154
|
+
overlay.role ? `role=${overlay.role}` : "",
|
|
5155
|
+
overlay.blocksInteraction ? "blocking" : "",
|
|
5156
|
+
overlay.label ? `label="${overlay.label.slice(0, 80)}"` : "",
|
|
5157
|
+
overlay.text ? `text="${overlay.text.slice(0, 100)}"` : ""
|
|
5158
|
+
].filter(Boolean).join(" ")
|
|
5159
|
+
];
|
|
5160
|
+
if (overlay.radioOptions.length > 0) {
|
|
5161
|
+
const options = overlay.radioOptions.slice(0, 4).map((option) => {
|
|
5162
|
+
const tags = [];
|
|
5163
|
+
if (option.labelSource) tags.push(`source=${option.labelSource}`);
|
|
5164
|
+
if (option.looksCorrect === true) tags.push("likely-correct");
|
|
5165
|
+
if (option.looksCorrect === false) tags.push("likely-wrong");
|
|
5166
|
+
const suffix = tags.length > 0 ? ` (${tags.join(", ")})` : "";
|
|
5167
|
+
return `${option.label || option.selector || "radio"}${suffix}`;
|
|
5168
|
+
}).join(" | ");
|
|
5169
|
+
lines.push(` options: ${options}`);
|
|
5170
|
+
}
|
|
5171
|
+
const actionLabels = [
|
|
5172
|
+
overlay.dismissAction?.label ? `dismiss="${overlay.dismissAction.label}"` : "",
|
|
5173
|
+
overlay.acceptAction?.label ? `accept="${overlay.acceptAction.label}"` : "",
|
|
5174
|
+
overlay.submitAction?.label ? `submit="${overlay.submitAction.label}"` : ""
|
|
5175
|
+
].filter(Boolean);
|
|
5176
|
+
if (actionLabels.length > 0) {
|
|
5177
|
+
lines.push(` actions: ${actionLabels.join(" ")}`);
|
|
5178
|
+
}
|
|
5179
|
+
return lines.join("\n");
|
|
4992
5180
|
}).join("\n");
|
|
4993
5181
|
}
|
|
5182
|
+
function getScrollHints(page) {
|
|
5183
|
+
const candidates = page.interactiveElements.filter(
|
|
5184
|
+
(el) => el.visible !== false && el.inViewport === false && el.context !== "nav" && el.context !== "footer" && el.context !== "sidebar" && el.blockedByOverlay !== true && (el.type === "input" || el.type === "textarea" || el.type === "select" || el.type === "button")
|
|
5185
|
+
);
|
|
5186
|
+
if (candidates.length === 0) return [];
|
|
5187
|
+
const labels = limitItems(candidates, 3).map((el) => el.text || el.label || el.placeholder || el.type).filter(Boolean);
|
|
5188
|
+
return [
|
|
5189
|
+
`Scroll to reveal offscreen controls: ${labels.join(", ")}${candidates.length > labels.length ? ", ..." : ""}`
|
|
5190
|
+
];
|
|
5191
|
+
}
|
|
4994
5192
|
function formatDormantOverlays(overlays) {
|
|
4995
5193
|
if (overlays.length === 0) return "None detected";
|
|
4996
5194
|
const items = limitItems(overlays, 10);
|
|
@@ -5327,6 +5525,10 @@ function buildScopedContext(page, mode) {
|
|
|
5327
5525
|
if (page.excerpt) sections.push(`**Summary:** ${page.excerpt}`);
|
|
5328
5526
|
const largePageHint = formatLargePageHint(page);
|
|
5329
5527
|
if (largePageHint) sections.push(`**Reading Hint:** ${largePageHint}`);
|
|
5528
|
+
const scrollHints = getScrollHints(page);
|
|
5529
|
+
if (scrollHints.length > 0) {
|
|
5530
|
+
sections.push(`**Scroll Hint:** ${scrollHints[0]}`);
|
|
5531
|
+
}
|
|
5330
5532
|
sections.push("");
|
|
5331
5533
|
const summaryIntent = analyzePageIntent(page);
|
|
5332
5534
|
if (summaryIntent) {
|
|
@@ -5395,6 +5597,10 @@ function buildScopedContext(page, mode) {
|
|
|
5395
5597
|
sections.push(`**URL:** ${page.url}`);
|
|
5396
5598
|
sections.push(`**Title:** ${page.title}`);
|
|
5397
5599
|
sections.push(`**Viewport:** ${formatViewport(page)}`);
|
|
5600
|
+
const interactivesScrollHints = getScrollHints(page);
|
|
5601
|
+
if (interactivesScrollHints.length > 0) {
|
|
5602
|
+
sections.push(`**Scroll Hint:** ${interactivesScrollHints[0]}`);
|
|
5603
|
+
}
|
|
5398
5604
|
sections.push("");
|
|
5399
5605
|
const interactivesIntent = analyzePageIntent(page);
|
|
5400
5606
|
if (interactivesIntent) {
|
|
@@ -5419,7 +5625,7 @@ function buildScopedContext(page, mode) {
|
|
|
5419
5625
|
}
|
|
5420
5626
|
if (page.overlays.length > 0) {
|
|
5421
5627
|
sections.push("### Active Overlays");
|
|
5422
|
-
sections.push(formatOverlays(page
|
|
5628
|
+
sections.push(formatOverlays(page));
|
|
5423
5629
|
sections.push("");
|
|
5424
5630
|
}
|
|
5425
5631
|
if (dialogFocus) {
|
|
@@ -5457,6 +5663,10 @@ function buildScopedContext(page, mode) {
|
|
|
5457
5663
|
sections.push(`**URL:** ${page.url}`);
|
|
5458
5664
|
sections.push(`**Title:** ${page.title}`);
|
|
5459
5665
|
sections.push(`**Viewport:** ${formatViewport(page)}`);
|
|
5666
|
+
const visibleScrollHints = getScrollHints(page);
|
|
5667
|
+
if (visibleScrollHints.length > 0) {
|
|
5668
|
+
sections.push(`**Scroll Hint:** ${visibleScrollHints[0]}`);
|
|
5669
|
+
}
|
|
5460
5670
|
sections.push("");
|
|
5461
5671
|
const formsHighlights = getHighlightsForPage(page.url);
|
|
5462
5672
|
if (formsHighlights.length > 0) {
|
|
@@ -5476,7 +5686,7 @@ function buildScopedContext(page, mode) {
|
|
|
5476
5686
|
}
|
|
5477
5687
|
if (page.overlays.length > 0) {
|
|
5478
5688
|
sections.push("### Active Overlays");
|
|
5479
|
-
sections.push(formatOverlays(page
|
|
5689
|
+
sections.push(formatOverlays(page));
|
|
5480
5690
|
sections.push("");
|
|
5481
5691
|
}
|
|
5482
5692
|
if (page.dormantOverlays.length > 0) {
|
|
@@ -5559,7 +5769,7 @@ function buildScopedContext(page, mode) {
|
|
|
5559
5769
|
}
|
|
5560
5770
|
if (page.overlays.length > 0) {
|
|
5561
5771
|
sections.push("### Active Overlays");
|
|
5562
|
-
sections.push(formatOverlays(page
|
|
5772
|
+
sections.push(formatOverlays(page));
|
|
5563
5773
|
sections.push("");
|
|
5564
5774
|
}
|
|
5565
5775
|
if (dialogFocus) {
|
|
@@ -5744,6 +5954,10 @@ function buildStructuredContext(page) {
|
|
|
5744
5954
|
sections.push(`**Viewport:** ${formatViewport(page)}`);
|
|
5745
5955
|
if (page.byline) sections.push(`**Author:** ${page.byline}`);
|
|
5746
5956
|
if (page.excerpt) sections.push(`**Summary:** ${page.excerpt}`);
|
|
5957
|
+
const structuredScrollHints = getScrollHints(page);
|
|
5958
|
+
if (structuredScrollHints.length > 0) {
|
|
5959
|
+
sections.push(`**Scroll Hint:** ${structuredScrollHints[0]}`);
|
|
5960
|
+
}
|
|
5747
5961
|
sections.push("");
|
|
5748
5962
|
const pageIntent = analyzePageIntent(page);
|
|
5749
5963
|
if (pageIntent) {
|
|
@@ -5782,7 +5996,7 @@ function buildStructuredContext(page) {
|
|
|
5782
5996
|
sections.push(formatLandmarks(page.landmarks));
|
|
5783
5997
|
sections.push("");
|
|
5784
5998
|
sections.push("### Active Overlays / Modals");
|
|
5785
|
-
sections.push(formatOverlays(page
|
|
5999
|
+
sections.push(formatOverlays(page));
|
|
5786
6000
|
sections.push("");
|
|
5787
6001
|
sections.push("### Dormant Consent / Modal UI");
|
|
5788
6002
|
sections.push(formatDormantOverlays(page.dormantOverlays));
|
|
@@ -5861,6 +6075,83 @@ function buildGeneralPrompt(query) {
|
|
|
5861
6075
|
user: query
|
|
5862
6076
|
};
|
|
5863
6077
|
}
|
|
6078
|
+
const WRAPPING_QUOTES = /* @__PURE__ */ new Set(['"', "'", "`"]);
|
|
6079
|
+
function stripWrappingQuotes(value) {
|
|
6080
|
+
const trimmed = value.trim();
|
|
6081
|
+
if (trimmed.length < 2) return trimmed;
|
|
6082
|
+
const first = trimmed[0];
|
|
6083
|
+
const last = trimmed[trimmed.length - 1];
|
|
6084
|
+
if (first === last && WRAPPING_QUOTES.has(first)) {
|
|
6085
|
+
return trimmed.slice(1, -1).trim();
|
|
6086
|
+
}
|
|
6087
|
+
return trimmed;
|
|
6088
|
+
}
|
|
6089
|
+
function normalizeArrayItem(value) {
|
|
6090
|
+
if (typeof value === "string") {
|
|
6091
|
+
return stripWrappingQuotes(value).trim();
|
|
6092
|
+
}
|
|
6093
|
+
return String(value).trim();
|
|
6094
|
+
}
|
|
6095
|
+
function normalizeLooseString(value) {
|
|
6096
|
+
if (typeof value !== "string") return void 0;
|
|
6097
|
+
const normalized = stripWrappingQuotes(value);
|
|
6098
|
+
return normalized ? normalized : void 0;
|
|
6099
|
+
}
|
|
6100
|
+
function coerceOptionalNumber(value) {
|
|
6101
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
6102
|
+
return value;
|
|
6103
|
+
}
|
|
6104
|
+
const normalized = normalizeLooseString(value);
|
|
6105
|
+
if (!normalized) return void 0;
|
|
6106
|
+
const parsed = Number(normalized);
|
|
6107
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
6108
|
+
}
|
|
6109
|
+
function coerceStringArray(value) {
|
|
6110
|
+
if (Array.isArray(value)) {
|
|
6111
|
+
return value.map(normalizeArrayItem).filter(Boolean);
|
|
6112
|
+
}
|
|
6113
|
+
const normalized = normalizeLooseString(value);
|
|
6114
|
+
if (!normalized) return [];
|
|
6115
|
+
try {
|
|
6116
|
+
const parsed = JSON.parse(normalized);
|
|
6117
|
+
if (Array.isArray(parsed)) {
|
|
6118
|
+
return parsed.map(normalizeArrayItem).filter(Boolean);
|
|
6119
|
+
}
|
|
6120
|
+
} catch {
|
|
6121
|
+
}
|
|
6122
|
+
const lines = normalized.split(/\r?\n/).map((line) => line.replace(/^\s*[-*]\s+/, "").trim()).filter(Boolean);
|
|
6123
|
+
if (lines.length > 1) {
|
|
6124
|
+
return lines;
|
|
6125
|
+
}
|
|
6126
|
+
return [normalized];
|
|
6127
|
+
}
|
|
6128
|
+
function optionalNumberLikeSchema() {
|
|
6129
|
+
return zod.z.preprocess(
|
|
6130
|
+
(value) => {
|
|
6131
|
+
if (value == null) return void 0;
|
|
6132
|
+
return coerceOptionalNumber(value) ?? value;
|
|
6133
|
+
},
|
|
6134
|
+
zod.z.number().finite().optional()
|
|
6135
|
+
);
|
|
6136
|
+
}
|
|
6137
|
+
function normalizedOptionalStringSchema() {
|
|
6138
|
+
return zod.z.preprocess(
|
|
6139
|
+
(value) => {
|
|
6140
|
+
if (value == null) return void 0;
|
|
6141
|
+
return normalizeLooseString(value) ?? value;
|
|
6142
|
+
},
|
|
6143
|
+
zod.z.string().optional()
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
function stringArrayLikeSchema() {
|
|
6147
|
+
return zod.z.preprocess(
|
|
6148
|
+
(value) => {
|
|
6149
|
+
if (value == null) return value;
|
|
6150
|
+
return coerceStringArray(value) ?? value;
|
|
6151
|
+
},
|
|
6152
|
+
zod.z.array(zod.z.string().min(1)).min(1)
|
|
6153
|
+
);
|
|
6154
|
+
}
|
|
5864
6155
|
const TOOL_DEFINITIONS = [
|
|
5865
6156
|
// --- Tab Management ---
|
|
5866
6157
|
{
|
|
@@ -5989,7 +6280,9 @@ const TOOL_DEFINITIONS = [
|
|
|
5989
6280
|
description: "Scroll the page up or down.",
|
|
5990
6281
|
inputSchema: {
|
|
5991
6282
|
direction: zod.z.enum(["up", "down"]).describe("Scroll direction"),
|
|
5992
|
-
amount:
|
|
6283
|
+
amount: optionalNumberLikeSchema().describe(
|
|
6284
|
+
"Pixels to scroll (default 500)"
|
|
6285
|
+
)
|
|
5993
6286
|
},
|
|
5994
6287
|
tier: 0,
|
|
5995
6288
|
relevance: ["ARTICLE", "SEARCH_RESULTS", "PAGINATED_LIST"]
|
|
@@ -6034,6 +6327,17 @@ const TOOL_DEFINITIONS = [
|
|
|
6034
6327
|
description: "Dismiss a modal, popup, newsletter gate, cookie banner, or overlay using common close/decline actions.",
|
|
6035
6328
|
tier: 1
|
|
6036
6329
|
},
|
|
6330
|
+
{
|
|
6331
|
+
name: "clear_overlays",
|
|
6332
|
+
title: "Clear Overlays",
|
|
6333
|
+
description: "Work through blocking overlays and modals until the page is unblocked, using overlay-specific heuristics for consent banners and radio-selection dialogs.",
|
|
6334
|
+
inputSchema: {
|
|
6335
|
+
strategy: zod.z.enum(["auto", "interactive"]).optional().describe(
|
|
6336
|
+
'How aggressively to clear overlays. "auto" uses heuristics; "interactive" stops earlier when human judgment may be needed.'
|
|
6337
|
+
)
|
|
6338
|
+
},
|
|
6339
|
+
tier: 1
|
|
6340
|
+
},
|
|
6037
6341
|
{
|
|
6038
6342
|
name: "inspect_element",
|
|
6039
6343
|
title: "Inspect Element",
|
|
@@ -6233,7 +6537,9 @@ const TOOL_DEFINITIONS = [
|
|
|
6233
6537
|
inputSchema: {
|
|
6234
6538
|
index: zod.z.number().optional().describe("Element index from page content to highlight"),
|
|
6235
6539
|
selector: zod.z.string().optional().describe("CSS selector of element to highlight"),
|
|
6236
|
-
text:
|
|
6540
|
+
text: normalizedOptionalStringSchema().describe(
|
|
6541
|
+
"Text to find and highlight on the page (all occurrences)"
|
|
6542
|
+
),
|
|
6237
6543
|
label: zod.z.string().optional().describe("Annotation label to display near the highlight"),
|
|
6238
6544
|
durationMs: zod.z.number().optional().describe(
|
|
6239
6545
|
"Auto-clear after this many milliseconds (omit for permanent)"
|
|
@@ -6258,7 +6564,7 @@ const TOOL_DEFINITIONS = [
|
|
|
6258
6564
|
goal: zod.z.string().describe(
|
|
6259
6565
|
"What this workflow accomplishes (e.g. 'Purchase item from Amazon')"
|
|
6260
6566
|
),
|
|
6261
|
-
steps:
|
|
6567
|
+
steps: stringArrayLikeSchema().describe(
|
|
6262
6568
|
"Ordered list of step labels (e.g. ['Log in', 'Search', 'Select item', 'Checkout'])"
|
|
6263
6569
|
)
|
|
6264
6570
|
},
|
|
@@ -6495,6 +6801,7 @@ const ALWAYS_FAST_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
6495
6801
|
"search",
|
|
6496
6802
|
"scroll",
|
|
6497
6803
|
"dismiss_popup",
|
|
6804
|
+
"clear_overlays",
|
|
6498
6805
|
"accept_cookies",
|
|
6499
6806
|
"wait_for",
|
|
6500
6807
|
"read_page",
|
|
@@ -6518,6 +6825,9 @@ function inferIntent(query) {
|
|
|
6518
6825
|
}
|
|
6519
6826
|
if (/\b(highlight|mark|annotate)\b/.test(lowered)) intents.add("highlight");
|
|
6520
6827
|
if (/\b(table|csv|rows|columns)\b/.test(lowered)) intents.add("table");
|
|
6828
|
+
if (/\b(overlay|modal|popup|consent|cookie|blocking ui)\b/.test(lowered)) {
|
|
6829
|
+
intents.add("debug");
|
|
6830
|
+
}
|
|
6521
6831
|
if (/\b(debug|diagnose|what should i do|stuck|inspect)\b/.test(lowered)) {
|
|
6522
6832
|
intents.add("debug");
|
|
6523
6833
|
}
|
|
@@ -8683,6 +8993,93 @@ async function dismissPopup$1(wc) {
|
|
|
8683
8993
|
}
|
|
8684
8994
|
return initialBlocking > 0 ? "Could not dismiss the blocking popup automatically" : initialDormant > 0 ? `No active blocking popup detected. Found ${initialDormant} dormant consent/modal surface(s) in the DOM, likely geo-gated or inactive in this session.` : "No blocking popup detected";
|
|
8685
8995
|
}
|
|
8996
|
+
function describeOverlayState(page) {
|
|
8997
|
+
const inventory = buildOverlayInventory(page);
|
|
8998
|
+
return {
|
|
8999
|
+
inventory,
|
|
9000
|
+
blocking: inventory.filter((overlay) => overlay.blocksInteraction).length,
|
|
9001
|
+
total: inventory.length,
|
|
9002
|
+
signature: getBlockingOverlaySignature(inventory)
|
|
9003
|
+
};
|
|
9004
|
+
}
|
|
9005
|
+
async function clickOverlayCandidate(wc, action) {
|
|
9006
|
+
if (!action?.selector) return null;
|
|
9007
|
+
const result = await clickResolvedSelector$1(wc, action.selector);
|
|
9008
|
+
return `${action.label || action.selector}: ${result}`;
|
|
9009
|
+
}
|
|
9010
|
+
async function clearOverlays(wc, strategy = "auto") {
|
|
9011
|
+
const steps = [];
|
|
9012
|
+
let cleared = 0;
|
|
9013
|
+
const maxIterations = 8;
|
|
9014
|
+
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
9015
|
+
const before = await extractContent(wc);
|
|
9016
|
+
const beforeState = describeOverlayState(before);
|
|
9017
|
+
const blockingOverlays = beforeState.inventory.filter(
|
|
9018
|
+
(overlay2) => overlay2.blocksInteraction
|
|
9019
|
+
);
|
|
9020
|
+
if (blockingOverlays.length === 0) {
|
|
9021
|
+
if (cleared === 0) return "No blocking overlays detected";
|
|
9022
|
+
steps.push(`Overlays remaining: ${beforeState.total}`);
|
|
9023
|
+
steps.push("Page still blocked: false");
|
|
9024
|
+
return steps.join("\n");
|
|
9025
|
+
}
|
|
9026
|
+
const overlay = blockingOverlays[0];
|
|
9027
|
+
let actionMessage = null;
|
|
9028
|
+
if (overlay.kind === "cookie_consent") {
|
|
9029
|
+
actionMessage = await clickOverlayCandidate(
|
|
9030
|
+
wc,
|
|
9031
|
+
overlay.acceptAction || overlay.dismissAction || overlay.actions[0]
|
|
9032
|
+
);
|
|
9033
|
+
} else if (overlay.kind === "selection_modal") {
|
|
9034
|
+
if (!overlay.correctOption?.selector) {
|
|
9035
|
+
if (strategy === "interactive") {
|
|
9036
|
+
steps.push(
|
|
9037
|
+
"Stopped: selection modal needs human judgment because no likely-correct option was detected."
|
|
9038
|
+
);
|
|
9039
|
+
steps.push(`Overlays remaining: ${beforeState.total}`);
|
|
9040
|
+
steps.push("Page still blocked: true");
|
|
9041
|
+
return steps.join("\n");
|
|
9042
|
+
}
|
|
9043
|
+
} else {
|
|
9044
|
+
const optionResult = await clickOverlayCandidate(
|
|
9045
|
+
wc,
|
|
9046
|
+
overlay.correctOption
|
|
9047
|
+
);
|
|
9048
|
+
if (optionResult) {
|
|
9049
|
+
actionMessage = `Selected likely-correct option: ${optionResult}`;
|
|
9050
|
+
await sleep$1(120);
|
|
9051
|
+
const submitResult = await clickOverlayCandidate(
|
|
9052
|
+
wc,
|
|
9053
|
+
overlay.submitAction || overlay.acceptAction
|
|
9054
|
+
);
|
|
9055
|
+
if (submitResult) {
|
|
9056
|
+
actionMessage += `
|
|
9057
|
+
Submitted modal: ${submitResult}`;
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
if (!actionMessage) {
|
|
9063
|
+
actionMessage = `Fallback popup handling: ${await dismissPopup$1(wc)}`;
|
|
9064
|
+
}
|
|
9065
|
+
steps.push(actionMessage);
|
|
9066
|
+
await sleep$1(250);
|
|
9067
|
+
const after = await extractContent(wc);
|
|
9068
|
+
const afterState = describeOverlayState(after);
|
|
9069
|
+
steps.push(`Overlays remaining: ${afterState.total}`);
|
|
9070
|
+
steps.push(`Page still blocked: ${afterState.blocking > 0}`);
|
|
9071
|
+
if (afterState.blocking === 0) {
|
|
9072
|
+
return steps.join("\n");
|
|
9073
|
+
}
|
|
9074
|
+
const progressMade = afterState.blocking < beforeState.blocking || afterState.total !== beforeState.total || afterState.signature !== beforeState.signature;
|
|
9075
|
+
if (progressMade) {
|
|
9076
|
+
cleared += 1;
|
|
9077
|
+
continue;
|
|
9078
|
+
}
|
|
9079
|
+
return steps.join("\n");
|
|
9080
|
+
}
|
|
9081
|
+
return steps.join("\n");
|
|
9082
|
+
}
|
|
8686
9083
|
async function resolveSelector$1(wc, index, selector) {
|
|
8687
9084
|
if (selector) return selector;
|
|
8688
9085
|
if (index == null) return null;
|
|
@@ -9589,7 +9986,8 @@ async function getPostActionState$1(ctx, name) {
|
|
|
9589
9986
|
"hover",
|
|
9590
9987
|
"focus",
|
|
9591
9988
|
"fill_form",
|
|
9592
|
-
"inspect_element"
|
|
9989
|
+
"inspect_element",
|
|
9990
|
+
"clear_overlays"
|
|
9593
9991
|
];
|
|
9594
9992
|
const tabActions = [
|
|
9595
9993
|
"create_tab",
|
|
@@ -9639,6 +10037,7 @@ const KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
|
9639
10037
|
"focus",
|
|
9640
10038
|
"set_ad_blocking",
|
|
9641
10039
|
"dismiss_popup",
|
|
10040
|
+
"clear_overlays",
|
|
9642
10041
|
"read_page",
|
|
9643
10042
|
"wait_for",
|
|
9644
10043
|
"create_checkpoint",
|
|
@@ -9866,7 +10265,7 @@ async function executeAction(name, args, ctx) {
|
|
|
9866
10265
|
}
|
|
9867
10266
|
case "scroll": {
|
|
9868
10267
|
if (!wc) return "Error: No active tab";
|
|
9869
|
-
const pixels = args.amount
|
|
10268
|
+
const pixels = coerceOptionalNumber(args.amount) ?? 500;
|
|
9870
10269
|
const dir = args.direction === "up" ? -pixels : pixels;
|
|
9871
10270
|
const result2 = await scrollPage$1(wc, dir);
|
|
9872
10271
|
return `Scrolled ${args.direction} by ${pixels}px (moved ${Math.abs(result2.movedY)}px, now at y=${Math.round(result2.afterY)})`;
|
|
@@ -9911,6 +10310,11 @@ async function executeAction(name, args, ctx) {
|
|
|
9911
10310
|
if (!wc) return "Error: No active tab";
|
|
9912
10311
|
return dismissPopup$1(wc);
|
|
9913
10312
|
}
|
|
10313
|
+
case "clear_overlays": {
|
|
10314
|
+
if (!wc) return "Error: No active tab";
|
|
10315
|
+
const strategy = args.strategy === "interactive" ? "interactive" : "auto";
|
|
10316
|
+
return clearOverlays(wc, strategy);
|
|
10317
|
+
}
|
|
9914
10318
|
case "read_page": {
|
|
9915
10319
|
if (!wc) return "Error: No active tab";
|
|
9916
10320
|
console.log("[Vessel read_page] starting extraction with 6s timeout");
|
|
@@ -10277,9 +10681,9 @@ ${truncated}`;
|
|
|
10277
10681
|
if (!wc) return "Error: No active tab";
|
|
10278
10682
|
const selector = await resolveSelector$1(wc, args.index, args.selector);
|
|
10279
10683
|
const highlightColor = args.color || "yellow";
|
|
10684
|
+
const highlightText = normalizeLooseString(args.text);
|
|
10280
10685
|
const url = wc.getURL();
|
|
10281
10686
|
if (url && url !== "about:blank") {
|
|
10282
|
-
const highlightText = typeof args.text === "string" ? args.text : void 0;
|
|
10283
10687
|
addHighlight(
|
|
10284
10688
|
url,
|
|
10285
10689
|
typeof selector === "string" ? selector : void 0,
|
|
@@ -10292,7 +10696,7 @@ ${truncated}`;
|
|
|
10292
10696
|
return highlightOnPage(
|
|
10293
10697
|
wc,
|
|
10294
10698
|
selector,
|
|
10295
|
-
|
|
10699
|
+
highlightText,
|
|
10296
10700
|
args.label,
|
|
10297
10701
|
args.durationMs,
|
|
10298
10702
|
highlightColor
|
|
@@ -10305,7 +10709,7 @@ ${truncated}`;
|
|
|
10305
10709
|
// --- Speedee System ---
|
|
10306
10710
|
case "flow_start": {
|
|
10307
10711
|
const goal = typeof args.goal === "string" ? args.goal : "";
|
|
10308
|
-
const steps =
|
|
10712
|
+
const steps = coerceStringArray(args.steps) ?? [];
|
|
10309
10713
|
if (!goal || steps.length === 0)
|
|
10310
10714
|
return "Error: goal and steps are required";
|
|
10311
10715
|
const flow = ctx.runtime.startFlow(goal, steps, wc?.getURL());
|
|
@@ -10365,9 +10769,8 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
10365
10769
|
const hasOverlays = page.overlays.some((o) => o.blocksInteraction);
|
|
10366
10770
|
if (hasOverlays) {
|
|
10367
10771
|
suggestions.push("BLOCKING OVERLAY detected — dismiss it first:");
|
|
10368
|
-
suggestions.push(
|
|
10369
|
-
|
|
10370
|
-
);
|
|
10772
|
+
suggestions.push(" → clear_overlays for stacked modals");
|
|
10773
|
+
suggestions.push(" → or dismiss_popup for a single popup");
|
|
10371
10774
|
suggestions.push("");
|
|
10372
10775
|
}
|
|
10373
10776
|
if (hasPasswordField) {
|
|
@@ -10931,6 +11334,8 @@ Instructions:
|
|
|
10931
11334
|
- When you only need detail on one product/result/card/form section, use inspect_element instead of reading the page.
|
|
10932
11335
|
- Escalate page reads progressively: read_page(mode="visible_only"), read_page(mode="results_only"), read_page(mode="forms_only"), read_page(mode="summary"), or read_page(mode="text_only") depending on what you need.
|
|
10933
11336
|
- Use read_page(mode="debug") only as a last resort when the narrower modes are insufficient.
|
|
11337
|
+
- VIEWPORT SYNC: Treat scrolling as a real, user-visible browser action. If you say you are going to scroll, call scroll or scroll_to_element so the human sees the page move too.
|
|
11338
|
+
- read_page inspects the page without moving the human-visible viewport. Do not describe read_page as scrolling. If you want more context without changing the user's view, say you're reading the page; if you want the user to follow along lower on the page, actually scroll first.
|
|
10934
11339
|
- After clicking or submitting a form, prefer wait_for on a specific result signal or a narrow read_page mode. Do not jump straight to read_page(mode="debug").
|
|
10935
11340
|
- If the user says they highlighted or selected text, use read_page before falling back to screenshots because it includes active selection and visible unsaved highlights.
|
|
10936
11341
|
- If a page behaves abnormally or key UI fails to load, consider disabling ad blocking for that tab and reloading before retrying.
|
|
@@ -13995,7 +14400,9 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
13995
14400
|
description: "Scroll the page up or down.",
|
|
13996
14401
|
inputSchema: {
|
|
13997
14402
|
direction: zod.z.enum(["up", "down"]).describe("Scroll direction"),
|
|
13998
|
-
amount:
|
|
14403
|
+
amount: optionalNumberLikeSchema().describe(
|
|
14404
|
+
"Pixels to scroll (default 500)"
|
|
14405
|
+
)
|
|
13999
14406
|
}
|
|
14000
14407
|
},
|
|
14001
14408
|
async ({ direction, amount }) => {
|
|
@@ -14007,7 +14414,7 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
14007
14414
|
"scroll",
|
|
14008
14415
|
{ direction, amount },
|
|
14009
14416
|
async () => {
|
|
14010
|
-
const pixels = amount
|
|
14417
|
+
const pixels = coerceOptionalNumber(amount) ?? 500;
|
|
14011
14418
|
const dir = direction === "up" ? -pixels : pixels;
|
|
14012
14419
|
const result = await scrollPage(tab.view.webContents, dir);
|
|
14013
14420
|
return `Scrolled ${direction} by ${pixels}px (moved ${Math.abs(result.movedY)}px, now at y=${Math.round(result.afterY)})`;
|
|
@@ -14033,6 +14440,32 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
14033
14440
|
);
|
|
14034
14441
|
}
|
|
14035
14442
|
);
|
|
14443
|
+
server.registerTool(
|
|
14444
|
+
"vessel_clear_overlays",
|
|
14445
|
+
{
|
|
14446
|
+
title: "Clear Overlays",
|
|
14447
|
+
description: "Work through blocking overlays and modals until the page is unblocked, using overlay-specific heuristics for consent banners and radio-selection dialogs.",
|
|
14448
|
+
inputSchema: {
|
|
14449
|
+
strategy: zod.z.enum(["auto", "interactive"]).optional().describe(
|
|
14450
|
+
'How aggressively to clear overlays. "auto" uses heuristics; "interactive" stops earlier when human judgment may be needed.'
|
|
14451
|
+
)
|
|
14452
|
+
}
|
|
14453
|
+
},
|
|
14454
|
+
async ({ strategy }) => {
|
|
14455
|
+
const tab = tabManager.getActiveTab();
|
|
14456
|
+
if (!tab) return asTextResponse("Error: No active tab");
|
|
14457
|
+
return withAction(
|
|
14458
|
+
runtime,
|
|
14459
|
+
tabManager,
|
|
14460
|
+
"clear_overlays",
|
|
14461
|
+
{ strategy: strategy || "auto" },
|
|
14462
|
+
async () => clearOverlays(
|
|
14463
|
+
tab.view.webContents,
|
|
14464
|
+
strategy === "interactive" ? "interactive" : "auto"
|
|
14465
|
+
)
|
|
14466
|
+
);
|
|
14467
|
+
}
|
|
14468
|
+
);
|
|
14036
14469
|
server.registerTool(
|
|
14037
14470
|
"vessel_wait_for",
|
|
14038
14471
|
{
|
|
@@ -14332,7 +14765,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
14332
14765
|
inputSchema: {
|
|
14333
14766
|
index: zod.z.number().optional().describe("Element index from extracted content to highlight"),
|
|
14334
14767
|
selector: zod.z.string().optional().describe("CSS selector of element to highlight"),
|
|
14335
|
-
text:
|
|
14768
|
+
text: normalizedOptionalStringSchema().describe(
|
|
14336
14769
|
"Text to find and highlight on the page (highlights all occurrences)"
|
|
14337
14770
|
),
|
|
14338
14771
|
label: zod.z.string().optional().describe("Optional annotation label to display near the highlight"),
|
|
@@ -14350,18 +14783,27 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
14350
14783
|
async ({ index, selector, text, label, durationMs, persist, color }) => {
|
|
14351
14784
|
const tab = tabManager.getActiveTab();
|
|
14352
14785
|
if (!tab) return asTextResponse("Error: No active tab");
|
|
14786
|
+
const normalizedText = normalizeLooseString(text);
|
|
14353
14787
|
return withAction(
|
|
14354
14788
|
runtime,
|
|
14355
14789
|
tabManager,
|
|
14356
14790
|
"highlight",
|
|
14357
|
-
{
|
|
14791
|
+
{
|
|
14792
|
+
index,
|
|
14793
|
+
selector,
|
|
14794
|
+
text: normalizedText,
|
|
14795
|
+
label,
|
|
14796
|
+
durationMs,
|
|
14797
|
+
persist,
|
|
14798
|
+
color
|
|
14799
|
+
},
|
|
14358
14800
|
async () => {
|
|
14359
14801
|
const wc = tab.view.webContents;
|
|
14360
14802
|
const resolvedSelector = await resolveSelector(wc, index, selector);
|
|
14361
14803
|
const result = await highlightOnPage(
|
|
14362
14804
|
wc,
|
|
14363
14805
|
resolvedSelector,
|
|
14364
|
-
|
|
14806
|
+
normalizedText,
|
|
14365
14807
|
label,
|
|
14366
14808
|
durationMs,
|
|
14367
14809
|
color
|
|
@@ -14371,7 +14813,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
14371
14813
|
addHighlight(
|
|
14372
14814
|
url,
|
|
14373
14815
|
resolvedSelector ?? void 0,
|
|
14374
|
-
|
|
14816
|
+
normalizedText,
|
|
14375
14817
|
label,
|
|
14376
14818
|
color,
|
|
14377
14819
|
"agent"
|
|
@@ -15243,16 +15685,17 @@ ${JSON.stringify(otherHighlights, null, 2)}`
|
|
|
15243
15685
|
goal: zod.z.string().describe(
|
|
15244
15686
|
"What this workflow accomplishes (e.g. 'Purchase item from Amazon')"
|
|
15245
15687
|
),
|
|
15246
|
-
steps:
|
|
15688
|
+
steps: stringArrayLikeSchema().describe(
|
|
15247
15689
|
"Ordered list of step labels (e.g. ['Log in', 'Search', 'Select item', 'Checkout'])"
|
|
15248
15690
|
)
|
|
15249
15691
|
}
|
|
15250
15692
|
},
|
|
15251
15693
|
async ({ goal, steps }) => {
|
|
15694
|
+
const normalizedSteps = coerceStringArray(steps) ?? [];
|
|
15252
15695
|
const tab = tabManager.getActiveTab();
|
|
15253
15696
|
const flow = runtime.startFlow(
|
|
15254
15697
|
goal,
|
|
15255
|
-
|
|
15698
|
+
normalizedSteps,
|
|
15256
15699
|
tab?.view.webContents.getURL()
|
|
15257
15700
|
);
|
|
15258
15701
|
return asTextResponse(
|
|
@@ -15348,9 +15791,8 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
15348
15791
|
const hasOverlays = page.overlays.some((o) => o.blocksInteraction);
|
|
15349
15792
|
if (hasOverlays) {
|
|
15350
15793
|
suggestions.push("⚠ BLOCKING OVERLAY detected — dismiss it first:");
|
|
15351
|
-
suggestions.push(
|
|
15352
|
-
|
|
15353
|
-
);
|
|
15794
|
+
suggestions.push(" → vessel_clear_overlays for stacked modals");
|
|
15795
|
+
suggestions.push(" → or vessel_dismiss_popup for a single popup");
|
|
15354
15796
|
suggestions.push("");
|
|
15355
15797
|
}
|
|
15356
15798
|
if (hasPasswordField) {
|