@quanta-intellect/vessel-browser 0.1.145 → 0.1.146
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 +265 -61
- package/package.json +1 -1
package/out/main/index.js
CHANGED
|
@@ -682,15 +682,6 @@ function loadTrustedAppURL(wc, url) {
|
|
|
682
682
|
}
|
|
683
683
|
return wc.loadURL(parsed.toString());
|
|
684
684
|
}
|
|
685
|
-
const urlSafety = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
686
|
-
__proto__: null,
|
|
687
|
-
assertPermittedNavigationURL,
|
|
688
|
-
assertSafeURL,
|
|
689
|
-
isSafeNavigationURL,
|
|
690
|
-
loadInternalDataURL,
|
|
691
|
-
loadPermittedNavigationURL,
|
|
692
|
-
loadTrustedAppURL
|
|
693
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
694
685
|
const MAX_CUSTOM_HISTORY = 50;
|
|
695
686
|
const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
|
|
696
687
|
const logger$B = createLogger("Tab");
|
|
@@ -7806,6 +7797,96 @@ function isClickReadLoop(names) {
|
|
|
7806
7797
|
}
|
|
7807
7798
|
return clickReadPairs >= 2;
|
|
7808
7799
|
}
|
|
7800
|
+
const CLICK_READ_LOOP_SUPPRESS_THRESHOLD = 3;
|
|
7801
|
+
function classifyClickFailure(output) {
|
|
7802
|
+
if (/Error\[hidden\]/i.test(output)) return "hidden";
|
|
7803
|
+
if (/Error\[stale-index\]/i.test(output)) return "stale";
|
|
7804
|
+
if (/^\s*Error:/i.test(output)) return "other";
|
|
7805
|
+
return null;
|
|
7806
|
+
}
|
|
7807
|
+
function buildClickReadLoopIntervention(strikes, lastClickFailureKind) {
|
|
7808
|
+
if (strikes <= 0) return null;
|
|
7809
|
+
if (strikes >= CLICK_READ_LOOP_SUPPRESS_THRESHOLD) {
|
|
7810
|
+
const lines = [
|
|
7811
|
+
`Error: Suppressed repeated click — you have alternated click and read_page ${strikes} times without making progress and the clicks are not landing.`,
|
|
7812
|
+
`Stop calling click. Instead do one of: scroll (scroll or scroll_to_element) to load more of the page then read_page to refresh, inspect_element on a specific indexed result, or answer from the results already visible in the conversation.`
|
|
7813
|
+
];
|
|
7814
|
+
if (lastClickFailureKind === "hidden") {
|
|
7815
|
+
lines.push(
|
|
7816
|
+
`The last click target was hidden / not laid out — scrolling toward it first usually reveals it.`
|
|
7817
|
+
);
|
|
7818
|
+
} else if (lastClickFailureKind === "stale") {
|
|
7819
|
+
lines.push(
|
|
7820
|
+
`The last click target was stale — refresh page state with read_page and choose a currently listed target before clicking again.`
|
|
7821
|
+
);
|
|
7822
|
+
}
|
|
7823
|
+
return { kind: "suppress", message: lines.join("\n") };
|
|
7824
|
+
}
|
|
7825
|
+
if (strikes >= 2) {
|
|
7826
|
+
const lines = [
|
|
7827
|
+
`[System] You are alternating between click and read_page without advancing the task, and the last click did not complete.`,
|
|
7828
|
+
`The click result already includes a page snapshot, so do not read_page after every click.`
|
|
7829
|
+
];
|
|
7830
|
+
if (lastClickFailureKind === "hidden") {
|
|
7831
|
+
lines.push(
|
|
7832
|
+
`The click failed on a hidden element — call scroll (scroll or scroll_to_element) to reveal it, then read_page to refresh visible elements, before clicking again.`
|
|
7833
|
+
);
|
|
7834
|
+
} else if (lastClickFailureKind === "stale") {
|
|
7835
|
+
lines.push(
|
|
7836
|
+
`The click failed on a stale element index — call read_page to refresh current indexes before clicking again.`
|
|
7837
|
+
);
|
|
7838
|
+
} else {
|
|
7839
|
+
lines.push(
|
|
7840
|
+
`If you need detail on a specific element, use inspect_element. Otherwise continue the original task directly.`
|
|
7841
|
+
);
|
|
7842
|
+
}
|
|
7843
|
+
return { kind: "nudge", message: lines.join("\n") };
|
|
7844
|
+
}
|
|
7845
|
+
return {
|
|
7846
|
+
kind: "nudge",
|
|
7847
|
+
message: `[System] You are alternating between click and read_page without advancing the task. The click result already includes a page snapshot when it navigates, so do not read_page after every click. If you need detail on a specific element, use inspect_element. Otherwise continue the original task directly.`
|
|
7848
|
+
};
|
|
7849
|
+
}
|
|
7850
|
+
class ClickReadLoopGuard {
|
|
7851
|
+
recentToolNames = [];
|
|
7852
|
+
strikes = 0;
|
|
7853
|
+
lastClickFailureKind = null;
|
|
7854
|
+
beforeTool(toolName) {
|
|
7855
|
+
if (toolName === "click" && this.strikes >= CLICK_READ_LOOP_SUPPRESS_THRESHOLD && isClickReadLoop(this.recentToolNames)) {
|
|
7856
|
+
return buildClickReadLoopIntervention(
|
|
7857
|
+
this.strikes,
|
|
7858
|
+
this.lastClickFailureKind
|
|
7859
|
+
);
|
|
7860
|
+
}
|
|
7861
|
+
return null;
|
|
7862
|
+
}
|
|
7863
|
+
afterToolResult(toolName, output, succeeded) {
|
|
7864
|
+
if (toolName === "click") {
|
|
7865
|
+
this.lastClickFailureKind = succeeded ? null : classifyClickFailure(output);
|
|
7866
|
+
}
|
|
7867
|
+
this.recentToolNames.push(toolName);
|
|
7868
|
+
if (this.recentToolNames.length > 8) this.recentToolNames.shift();
|
|
7869
|
+
if (toolName === "click" && succeeded) {
|
|
7870
|
+
this.strikes = 0;
|
|
7871
|
+
return null;
|
|
7872
|
+
}
|
|
7873
|
+
if (toolName !== "click" && toolName !== "read_page") {
|
|
7874
|
+
this.strikes = 0;
|
|
7875
|
+
return null;
|
|
7876
|
+
}
|
|
7877
|
+
if (isClickReadLoop(this.recentToolNames) && this.lastClickFailureKind) {
|
|
7878
|
+
this.strikes += 1;
|
|
7879
|
+
if (this.strikes >= CLICK_READ_LOOP_SUPPRESS_THRESHOLD) {
|
|
7880
|
+
return null;
|
|
7881
|
+
}
|
|
7882
|
+
return buildClickReadLoopIntervention(
|
|
7883
|
+
this.strikes,
|
|
7884
|
+
this.lastClickFailureKind
|
|
7885
|
+
);
|
|
7886
|
+
}
|
|
7887
|
+
return null;
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7809
7890
|
const TERMINAL_TOOL_RESULT = "__VESSEL_TERMINAL_TOOL_RESULT__";
|
|
7810
7891
|
const logger$w = createLogger("PromptCache");
|
|
7811
7892
|
function shortHash(value) {
|
|
@@ -7966,8 +8047,7 @@ class AnthropicProvider {
|
|
|
7966
8047
|
try {
|
|
7967
8048
|
const maxIterations = getEffectiveMaxIterations();
|
|
7968
8049
|
let iterationsUsed = 0;
|
|
7969
|
-
const
|
|
7970
|
-
let clickReadLoopNudged = false;
|
|
8050
|
+
const clickReadLoopGuard = new ClickReadLoopGuard();
|
|
7971
8051
|
for (let i = 0; i < maxIterations; i++) {
|
|
7972
8052
|
iterationsUsed = i + 1;
|
|
7973
8053
|
const stream = this.client.messages.stream(
|
|
@@ -8074,6 +8154,7 @@ class AnthropicProvider {
|
|
|
8074
8154
|
break;
|
|
8075
8155
|
}
|
|
8076
8156
|
const toolResults = [];
|
|
8157
|
+
const loopNudges = [];
|
|
8077
8158
|
for (const tb of toolUseBlocks) {
|
|
8078
8159
|
if (tb._malformedArgs !== void 0) {
|
|
8079
8160
|
onChunk(`
|
|
@@ -8087,6 +8168,19 @@ class AnthropicProvider {
|
|
|
8087
8168
|
});
|
|
8088
8169
|
continue;
|
|
8089
8170
|
}
|
|
8171
|
+
const clickLoopPreflight = clickReadLoopGuard.beforeTool(tb.name);
|
|
8172
|
+
if (clickLoopPreflight?.kind === "suppress") {
|
|
8173
|
+
onChunk(`
|
|
8174
|
+
<<tool:click:↻ loop suppressed>>
|
|
8175
|
+
`);
|
|
8176
|
+
toolResults.push({
|
|
8177
|
+
type: "tool_result",
|
|
8178
|
+
tool_use_id: tb.id,
|
|
8179
|
+
content: clickLoopPreflight.message,
|
|
8180
|
+
is_error: true
|
|
8181
|
+
});
|
|
8182
|
+
continue;
|
|
8183
|
+
}
|
|
8090
8184
|
const argSummary = [tb.input.url, tb.input.query, tb.input.text, tb.input.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
|
|
8091
8185
|
onChunk(`
|
|
8092
8186
|
<<tool:${tb.name}${argSummary ? ":" + argSummary : ""}>>
|
|
@@ -8101,6 +8195,7 @@ class AnthropicProvider {
|
|
|
8101
8195
|
if (result === TERMINAL_TOOL_RESULT) {
|
|
8102
8196
|
return;
|
|
8103
8197
|
}
|
|
8198
|
+
const toolSucceeded = !/^Error:/i.test(result.trim());
|
|
8104
8199
|
let parsedRich = null;
|
|
8105
8200
|
try {
|
|
8106
8201
|
const parsed = JSON.parse(result);
|
|
@@ -8132,16 +8227,18 @@ class AnthropicProvider {
|
|
|
8132
8227
|
content: result
|
|
8133
8228
|
});
|
|
8134
8229
|
}
|
|
8135
|
-
|
|
8136
|
-
|
|
8230
|
+
const clickLoopIntervention = clickReadLoopGuard.afterToolResult(
|
|
8231
|
+
tb.name,
|
|
8232
|
+
result,
|
|
8233
|
+
toolSucceeded
|
|
8234
|
+
);
|
|
8235
|
+
if (clickLoopIntervention?.kind === "nudge") {
|
|
8236
|
+
loopNudges.push(clickLoopIntervention.message);
|
|
8237
|
+
}
|
|
8137
8238
|
}
|
|
8138
8239
|
messages.push({ role: "user", content: toolResults });
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
messages.push({
|
|
8142
|
-
role: "user",
|
|
8143
|
-
content: `You are alternating between click and read_page without advancing the task. The click result already includes a page snapshot when it navigates — you do not need read_page after every click. If you need detail on a specific element, use inspect_element instead. If you have enough context, proceed with the next action directly.`
|
|
8144
|
-
});
|
|
8240
|
+
for (const nudge of loopNudges) {
|
|
8241
|
+
messages.push({ role: "user", content: nudge });
|
|
8145
8242
|
}
|
|
8146
8243
|
}
|
|
8147
8244
|
if (iterationsUsed >= maxIterations) {
|
|
@@ -9432,10 +9529,9 @@ class OpenAICompatProvider {
|
|
|
9432
9529
|
let highlightCompletionRecoveryCount = 0;
|
|
9433
9530
|
let compactCorrectionCount = 0;
|
|
9434
9531
|
const recentCompactToolSignatures = [];
|
|
9435
|
-
const recentToolNames = [];
|
|
9436
9532
|
const successfulToolNames = [];
|
|
9437
9533
|
const searchLoopGuard = new SearchLoopGuard(isSearchContextResettingTool);
|
|
9438
|
-
|
|
9534
|
+
const clickReadLoopGuard = new ClickReadLoopGuard();
|
|
9439
9535
|
for (let i = 0; i < maxIterations; i++) {
|
|
9440
9536
|
iterationsUsed = i + 1;
|
|
9441
9537
|
let textAccum = "";
|
|
@@ -9725,6 +9821,19 @@ class OpenAICompatProvider {
|
|
|
9725
9821
|
}
|
|
9726
9822
|
continue;
|
|
9727
9823
|
}
|
|
9824
|
+
const clickLoopPreflight = clickReadLoopGuard.beforeTool(tc.name);
|
|
9825
|
+
if (clickLoopPreflight?.kind === "suppress") {
|
|
9826
|
+
onChunk(`
|
|
9827
|
+
<<tool:click:↻ loop suppressed>>
|
|
9828
|
+
`);
|
|
9829
|
+
messages.push({
|
|
9830
|
+
role: "tool",
|
|
9831
|
+
tool_call_id: tc.id,
|
|
9832
|
+
content: clickLoopPreflight.message
|
|
9833
|
+
});
|
|
9834
|
+
compactCorrectionCount += 1;
|
|
9835
|
+
continue;
|
|
9836
|
+
}
|
|
9728
9837
|
const argSummary = [args.url, args.query, args.text, args.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
|
|
9729
9838
|
onChunk(`
|
|
9730
9839
|
<<tool:${tc.name}${argSummary ? ":" + argSummary : ""}>>
|
|
@@ -9762,15 +9871,11 @@ class OpenAICompatProvider {
|
|
|
9762
9871
|
searchToolQuery,
|
|
9763
9872
|
toolSucceeded
|
|
9764
9873
|
);
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
role: "user",
|
|
9771
|
-
content: `[System] You are alternating between click and read_page without advancing the task. The click result already includes a page snapshot when it navigates — you do not need read_page after every click. If you need detail on a specific element, use inspect_element instead. If you have enough context, proceed with the next action directly.`
|
|
9772
|
-
});
|
|
9773
|
-
}
|
|
9874
|
+
const clickLoopIntervention = clickReadLoopGuard.afterToolResult(
|
|
9875
|
+
tc.name,
|
|
9876
|
+
toolContent,
|
|
9877
|
+
toolSucceeded
|
|
9878
|
+
);
|
|
9774
9879
|
compactCorrectionCount = 0;
|
|
9775
9880
|
iterationToolResultPreviews.push(toolContent);
|
|
9776
9881
|
messages.push({
|
|
@@ -9778,6 +9883,9 @@ class OpenAICompatProvider {
|
|
|
9778
9883
|
tool_call_id: tc.id,
|
|
9779
9884
|
content: toolContent
|
|
9780
9885
|
});
|
|
9886
|
+
if (clickLoopIntervention?.kind === "nudge") {
|
|
9887
|
+
messages.push({ role: "user", content: clickLoopIntervention.message });
|
|
9888
|
+
}
|
|
9781
9889
|
}
|
|
9782
9890
|
const followUpReminder = followUpReminderForProfile(
|
|
9783
9891
|
this.agentToolProfile,
|
|
@@ -10449,12 +10557,25 @@ function buildCodexFlightPriceEvidenceRecoveryInput(userMessage, assistantText,
|
|
|
10449
10557
|
]
|
|
10450
10558
|
};
|
|
10451
10559
|
}
|
|
10452
|
-
function buildCodexFailedClickRecoveryInput(attemptedTarget, latestToolResultPreview, failedClickCount = 1) {
|
|
10560
|
+
function buildCodexFailedClickRecoveryInput(attemptedTarget, latestToolResultPreview, failedClickCount = 1, errorOutput = "") {
|
|
10453
10561
|
const stateReminder = buildLatestStateReminder(latestToolResultPreview);
|
|
10562
|
+
const clickFailureKind = classifyClickFailure(errorOutput);
|
|
10454
10563
|
const lines = [
|
|
10455
|
-
`[System] The previous click did not complete${attemptedTarget ? ` for ${attemptedTarget}` : ""}
|
|
10456
|
-
`Take the next step yourself: try a different target, refresh the page state with read_page, call inspect_element on the intended element, or answer from the results already visible in the conversation. Do not ask the user to inspect or click the result for you.`
|
|
10564
|
+
`[System] The previous click did not complete${attemptedTarget ? ` for ${attemptedTarget}` : ""}.`
|
|
10457
10565
|
];
|
|
10566
|
+
if (clickFailureKind === "hidden") {
|
|
10567
|
+
lines.push(
|
|
10568
|
+
`The click target was hidden / not laid out (collapsed, lazy-loaded, or virtual-scroll). Call scroll or scroll_to_element toward it to reveal it, then read_page to refresh visible elements, before clicking again — or inspect_element on the intended index, or answer from the results already visible. Do not ask the user to inspect or click the result for you.`
|
|
10569
|
+
);
|
|
10570
|
+
} else if (clickFailureKind === "stale") {
|
|
10571
|
+
lines.push(
|
|
10572
|
+
`The click target was stale — the page changed since the last snapshot. Call read_page to refresh current indexes, then choose a currently listed target, inspect_element on the intended element, or answer from the results already visible. Do not ask the user to inspect or click the result for you.`
|
|
10573
|
+
);
|
|
10574
|
+
} else {
|
|
10575
|
+
lines.push(
|
|
10576
|
+
`Take the next step yourself: try a different target, refresh the page state with read_page, call inspect_element on the intended element, or answer from the results already visible in the conversation. Do not ask the user to inspect or click the result for you.`
|
|
10577
|
+
);
|
|
10578
|
+
}
|
|
10458
10579
|
if (failedClickCount >= 2) {
|
|
10459
10580
|
lines.push(
|
|
10460
10581
|
`You have already had multiple failed clicks without making page progress. Do not keep clicking similar search result titles. Use the latest read_page/search result text to answer, or inspect a specific indexed result/control only if essential.`
|
|
@@ -10665,8 +10786,7 @@ class CodexProvider {
|
|
|
10665
10786
|
let flightPriceEvidenceRecoveryCount = 0;
|
|
10666
10787
|
let correctionCount = 0;
|
|
10667
10788
|
const recentToolSignatures = [];
|
|
10668
|
-
const
|
|
10669
|
-
let clickReadLoopNudged = false;
|
|
10789
|
+
const clickReadLoopGuard = new ClickReadLoopGuard();
|
|
10670
10790
|
let latestToolResultPreview = null;
|
|
10671
10791
|
let failedClickCountSinceProgress = 0;
|
|
10672
10792
|
const searchLoopGuard = new SearchLoopGuard(isRealProgressTool);
|
|
@@ -10822,6 +10942,22 @@ ${latestToolResultPreview || ""}`
|
|
|
10822
10942
|
correctionCount += 1;
|
|
10823
10943
|
continue;
|
|
10824
10944
|
}
|
|
10945
|
+
const clickLoopPreflight = clickReadLoopGuard.beforeTool(
|
|
10946
|
+
prepared.prepared.name
|
|
10947
|
+
);
|
|
10948
|
+
if (clickLoopPreflight?.kind === "suppress") {
|
|
10949
|
+
onChunk(`
|
|
10950
|
+
<<tool:click:↻ loop suppressed>>
|
|
10951
|
+
`);
|
|
10952
|
+
const suppressed = createCodexToolOutput(
|
|
10953
|
+
prepared.prepared.callId,
|
|
10954
|
+
clickLoopPreflight.message
|
|
10955
|
+
);
|
|
10956
|
+
currentInput.push(suppressed);
|
|
10957
|
+
latestToolResultPreview = previewToolResult(suppressed.output);
|
|
10958
|
+
correctionCount += 1;
|
|
10959
|
+
continue;
|
|
10960
|
+
}
|
|
10825
10961
|
const output = await executePreparedCodexFunctionCall(
|
|
10826
10962
|
prepared.prepared,
|
|
10827
10963
|
onChunk,
|
|
@@ -10849,7 +10985,8 @@ ${latestToolResultPreview || ""}`
|
|
|
10849
10985
|
buildCodexFailedClickRecoveryInput(
|
|
10850
10986
|
summarizeToolArg(prepared.prepared.args),
|
|
10851
10987
|
latestToolResultPreview,
|
|
10852
|
-
failedClickCountSinceProgress
|
|
10988
|
+
failedClickCountSinceProgress,
|
|
10989
|
+
outputText
|
|
10853
10990
|
)
|
|
10854
10991
|
);
|
|
10855
10992
|
}
|
|
@@ -10857,17 +10994,16 @@ ${latestToolResultPreview || ""}`
|
|
|
10857
10994
|
if (recentToolSignatures.length > 4) {
|
|
10858
10995
|
recentToolSignatures.shift();
|
|
10859
10996
|
}
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10997
|
+
const clickLoopIntervention = clickReadLoopGuard.afterToolResult(
|
|
10998
|
+
prepared.prepared.name,
|
|
10999
|
+
outputText,
|
|
11000
|
+
toolSucceeded
|
|
11001
|
+
);
|
|
11002
|
+
if (clickLoopIntervention?.kind === "nudge") {
|
|
10864
11003
|
currentInput.push({
|
|
10865
11004
|
type: "message",
|
|
10866
11005
|
role: "user",
|
|
10867
|
-
content: [{
|
|
10868
|
-
type: "input_text",
|
|
10869
|
-
text: `[System] You are alternating between click and read_page without advancing the task. The click result already includes a page snapshot when it navigates, so do not read_page after every click. If you need detail on a specific element, use inspect_element. Otherwise continue the original task directly.`
|
|
10870
|
-
}]
|
|
11006
|
+
content: [{ type: "input_text", text: clickLoopIntervention.message }]
|
|
10871
11007
|
});
|
|
10872
11008
|
}
|
|
10873
11009
|
correctionCount = 0;
|
|
@@ -12091,8 +12227,7 @@ function pageBusyError(action) {
|
|
|
12091
12227
|
return `Error: Page is still busy; ${action} timed out waiting for page scripts. Retry in a moment.`;
|
|
12092
12228
|
}
|
|
12093
12229
|
async function loadPermittedUrl(wc, url) {
|
|
12094
|
-
|
|
12095
|
-
assertPermittedNavigationURL2(url);
|
|
12230
|
+
assertPermittedNavigationURL(url);
|
|
12096
12231
|
await wc.loadURL(url);
|
|
12097
12232
|
}
|
|
12098
12233
|
async function executePageScript(wc, script, options) {
|
|
@@ -16432,6 +16567,60 @@ async function clickElement(wc, selector) {
|
|
|
16432
16567
|
}));
|
|
16433
16568
|
}
|
|
16434
16569
|
|
|
16570
|
+
// Sum offsetTop up the offsetParent chain until reaching "container",
|
|
16571
|
+
// giving the element's vertical position within that scroll container.
|
|
16572
|
+
function offsetTopWithin(el, container) {
|
|
16573
|
+
let top = 0;
|
|
16574
|
+
let node = el;
|
|
16575
|
+
while (node && node !== container) {
|
|
16576
|
+
top += node.offsetTop || 0;
|
|
16577
|
+
node = node.offsetParent;
|
|
16578
|
+
}
|
|
16579
|
+
return top;
|
|
16580
|
+
}
|
|
16581
|
+
|
|
16582
|
+
function nearestScrollableAncestor(el) {
|
|
16583
|
+
let node = el.parentElement;
|
|
16584
|
+
while (node) {
|
|
16585
|
+
if (node instanceof HTMLElement) {
|
|
16586
|
+
const style = window.getComputedStyle(node);
|
|
16587
|
+
if (
|
|
16588
|
+
(style.overflowY === "auto" || style.overflowY === "scroll") &&
|
|
16589
|
+
node.scrollHeight > node.clientHeight
|
|
16590
|
+
) {
|
|
16591
|
+
return node;
|
|
16592
|
+
}
|
|
16593
|
+
}
|
|
16594
|
+
node = node.parentElement;
|
|
16595
|
+
}
|
|
16596
|
+
return null;
|
|
16597
|
+
}
|
|
16598
|
+
|
|
16599
|
+
// Wait for the element to gain a non-zero layout box, polling for up to
|
|
16600
|
+
// maxFrames animation frames. Lazy / virtual-scroll renderers
|
|
16601
|
+
// (content-visibility, intersection-triggered list items) often lay out
|
|
16602
|
+
// a frame or two after the scroller moves. Falls back to setTimeout when
|
|
16603
|
+
// the window is hidden (requestAnimationFrame does not fire then).
|
|
16604
|
+
function waitForBox(el, maxFrames) {
|
|
16605
|
+
return new Promise((resolve) => {
|
|
16606
|
+
let frames = 0;
|
|
16607
|
+
const rafAvailable =
|
|
16608
|
+
typeof requestAnimationFrame === "function" &&
|
|
16609
|
+
document.visibilityState === "visible";
|
|
16610
|
+
const schedule = rafAvailable
|
|
16611
|
+
? (cb) => requestAnimationFrame(cb)
|
|
16612
|
+
: (cb) => setTimeout(cb, 16);
|
|
16613
|
+
const check = () => {
|
|
16614
|
+
const r = el.getBoundingClientRect();
|
|
16615
|
+
if (r.width > 0 && r.height > 0) return resolve(true);
|
|
16616
|
+
if (frames >= maxFrames) return resolve(false);
|
|
16617
|
+
frames += 1;
|
|
16618
|
+
schedule(check);
|
|
16619
|
+
};
|
|
16620
|
+
check();
|
|
16621
|
+
});
|
|
16622
|
+
}
|
|
16623
|
+
|
|
16435
16624
|
const el = document.querySelector(${JSON.stringify(selector)});
|
|
16436
16625
|
if (!el) return { error: "Error[stale-index]: Element not found — the page may have changed. Call read_page to refresh." };
|
|
16437
16626
|
|
|
@@ -16439,21 +16628,26 @@ async function clickElement(wc, selector) {
|
|
|
16439
16628
|
el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
|
|
16440
16629
|
}
|
|
16441
16630
|
|
|
16442
|
-
|
|
16443
|
-
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16450
|
-
|
|
16451
|
-
|
|
16452
|
-
)
|
|
16453
|
-
|
|
16631
|
+
// Give the renderer a brief grace to lay the element out after the
|
|
16632
|
+
// initial scroll. Already-visible elements resolve on the first check.
|
|
16633
|
+
let revealed = await waitForBox(el, 4);
|
|
16634
|
+
|
|
16635
|
+
// scrollIntoView is a no-op on zero-rect elements (collapsed, lazy, or
|
|
16636
|
+
// virtual-scroll content). Force the nearest scrollable ancestor to bring
|
|
16637
|
+
// the element's offset position into view, then wait longer for the
|
|
16638
|
+
// renderer to produce a layout box. This recovers many hidden targets
|
|
16639
|
+
// without the model having to scroll manually.
|
|
16640
|
+
if (!revealed) {
|
|
16641
|
+
const scroller = nearestScrollableAncestor(el);
|
|
16642
|
+
if (scroller) {
|
|
16643
|
+
const targetTop = offsetTopWithin(el, scroller) - scroller.clientHeight / 2;
|
|
16644
|
+
scroller.scrollTop = Math.max(0, targetTop);
|
|
16454
16645
|
}
|
|
16455
|
-
|
|
16456
|
-
|
|
16646
|
+
if (el instanceof HTMLElement) {
|
|
16647
|
+
el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
|
|
16648
|
+
}
|
|
16649
|
+
revealed = await waitForBox(el, 24);
|
|
16650
|
+
}
|
|
16457
16651
|
|
|
16458
16652
|
const rect = el.getBoundingClientRect();
|
|
16459
16653
|
if (rect.width <= 0 || rect.height <= 0) {
|
|
@@ -18016,9 +18210,13 @@ Go back and select a different product.`;
|
|
|
18016
18210
|
const clickText = `Clicked: ${elInfo.text}${tagLabel}`;
|
|
18017
18211
|
const clickResult = await clickElement(wc, selector);
|
|
18018
18212
|
if (clickResult.startsWith("Error:")) return clickResult;
|
|
18019
|
-
|
|
18213
|
+
const initialNavigationWaitMs = /DOM activation/i.test(clickResult) && !elInfo.href ? 800 : void 0;
|
|
18214
|
+
await waitForPotentialNavigation$1(wc, beforeUrl, initialNavigationWaitMs);
|
|
18020
18215
|
const afterUrl = wc.getURL();
|
|
18021
18216
|
if (afterUrl !== beforeUrl) {
|
|
18217
|
+
if (/DOM activation/i.test(clickResult)) {
|
|
18218
|
+
return `${clickText} -> ${afterUrl} (recovered via DOM activation)`;
|
|
18219
|
+
}
|
|
18022
18220
|
return `${clickText} -> ${afterUrl}`;
|
|
18023
18221
|
}
|
|
18024
18222
|
const overlayHint = await detectPostClickOverlay(wc);
|
|
@@ -18045,6 +18243,9 @@ ${overlayHint}`;
|
|
|
18045
18243
|
}
|
|
18046
18244
|
return `${clickText} (${clickResult})${await buildCartSuccessSuffix(wc, beforeUrl)}`;
|
|
18047
18245
|
}
|
|
18246
|
+
if (/DOM activation/i.test(clickResult) && (!elInfo.href || elInfo.target === "_blank")) {
|
|
18247
|
+
return `${clickText} (${clickResult})`;
|
|
18248
|
+
}
|
|
18048
18249
|
const activationResult = await activateElement(wc, selector);
|
|
18049
18250
|
if (!activationResult.startsWith("Error:")) {
|
|
18050
18251
|
await waitForPotentialNavigation$1(wc, beforeUrl);
|
|
@@ -23847,6 +24048,9 @@ All open tabs: ${allTabs.map((t) => `${t.id === activeTabId ? "→ " : ""}${t.ti
|
|
|
23847
24048
|
let isError = false;
|
|
23848
24049
|
try {
|
|
23849
24050
|
output = await executeAction(name, args, actionCtx);
|
|
24051
|
+
if (/^\s*Error:/i.test(output)) {
|
|
24052
|
+
isError = true;
|
|
24053
|
+
}
|
|
23850
24054
|
if (provider.agentToolProfile === "compact") {
|
|
23851
24055
|
runtime2.updateTaskTracker(name, output);
|
|
23852
24056
|
const trackerCtx = runtime2.getTaskTrackerContext();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quanta-intellect/vessel-browser",
|
|
3
3
|
"mcpName": "io.github.unmodeled-tyler/vessel-browser",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.146",
|
|
5
5
|
"description": "AI-native web browser runtime for autonomous agents with human supervision",
|
|
6
6
|
"main": "./out/main/index.js",
|
|
7
7
|
"bin": {
|