@quanta-intellect/vessel-browser 0.1.15 → 0.1.16
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
|
@@ -4406,6 +4406,19 @@ function setMcpHealth(update) {
|
|
|
4406
4406
|
}
|
|
4407
4407
|
}
|
|
4408
4408
|
}
|
|
4409
|
+
function isRichToolResult(value) {
|
|
4410
|
+
return typeof value === "object" && value !== null && value.__richResult === true;
|
|
4411
|
+
}
|
|
4412
|
+
function makeImageResult(base64, description, mediaType = "image/png") {
|
|
4413
|
+
const result = {
|
|
4414
|
+
__richResult: true,
|
|
4415
|
+
content: [
|
|
4416
|
+
{ type: "text", text: description },
|
|
4417
|
+
{ type: "image", mediaType, base64 }
|
|
4418
|
+
]
|
|
4419
|
+
};
|
|
4420
|
+
return JSON.stringify(result);
|
|
4421
|
+
}
|
|
4409
4422
|
const DEFAULT_MAX_ITERATIONS$1 = 200;
|
|
4410
4423
|
class AnthropicProvider {
|
|
4411
4424
|
client;
|
|
@@ -4437,7 +4450,7 @@ class AnthropicProvider {
|
|
|
4437
4450
|
}
|
|
4438
4451
|
}
|
|
4439
4452
|
} catch (err) {
|
|
4440
|
-
if (err.name !== "AbortError") {
|
|
4453
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
4441
4454
|
onChunk(`
|
|
4442
4455
|
|
|
4443
4456
|
[Error: ${err.message}]`);
|
|
@@ -4460,7 +4473,6 @@ class AnthropicProvider {
|
|
|
4460
4473
|
iterationsUsed = i + 1;
|
|
4461
4474
|
const msgTokenEstimate = JSON.stringify(messages).length;
|
|
4462
4475
|
const sysTokenEstimate = systemPrompt.length;
|
|
4463
|
-
console.log(`[Vessel Agent] iteration=${i} messages=${messages.length} msgChars=${msgTokenEstimate} sysChars=${sysTokenEstimate} tools=${tools.length}`);
|
|
4464
4476
|
const streamStartTime = Date.now();
|
|
4465
4477
|
const stream = this.client.messages.stream(
|
|
4466
4478
|
{
|
|
@@ -4522,9 +4534,7 @@ class AnthropicProvider {
|
|
|
4522
4534
|
} finally {
|
|
4523
4535
|
if (idleTimer) clearTimeout(idleTimer);
|
|
4524
4536
|
}
|
|
4525
|
-
console.log(`[Vessel Agent] stream complete in ${Date.now() - streamStartTime}ms, toolCalls=${toolUseBlocks.length} textLen=${textContent.length}`);
|
|
4526
4537
|
const finalMessage = await stream.finalMessage();
|
|
4527
|
-
console.log(`[Vessel Agent] finalMessage received, stop_reason=${finalMessage.stop_reason}`);
|
|
4528
4538
|
const assistantContent = [];
|
|
4529
4539
|
if (textContent) {
|
|
4530
4540
|
assistantContent.push({ type: "text", text: textContent });
|
|
@@ -4548,19 +4558,43 @@ class AnthropicProvider {
|
|
|
4548
4558
|
<<tool:${tb.name}${argSummary ? ":" + argSummary : ""}>>
|
|
4549
4559
|
`);
|
|
4550
4560
|
let result;
|
|
4551
|
-
const toolStartTime = Date.now();
|
|
4552
|
-
console.log(`[Vessel Agent] executing tool: ${tb.name}`);
|
|
4553
4561
|
try {
|
|
4554
4562
|
result = await onToolCall(tb.name, tb.input);
|
|
4555
4563
|
} catch (toolErr) {
|
|
4556
|
-
|
|
4564
|
+
const msg = toolErr instanceof Error ? toolErr.message : String(toolErr);
|
|
4565
|
+
result = `Error: Tool execution failed — ${msg}. Try a different approach or call read_page to refresh context.`;
|
|
4566
|
+
}
|
|
4567
|
+
let parsedRich = null;
|
|
4568
|
+
try {
|
|
4569
|
+
const parsed = JSON.parse(result);
|
|
4570
|
+
if (isRichToolResult(parsed)) parsedRich = parsed;
|
|
4571
|
+
} catch {
|
|
4572
|
+
}
|
|
4573
|
+
if (parsedRich) {
|
|
4574
|
+
toolResults.push({
|
|
4575
|
+
type: "tool_result",
|
|
4576
|
+
tool_use_id: tb.id,
|
|
4577
|
+
content: parsedRich.content.map((block) => {
|
|
4578
|
+
if (block.type === "image") {
|
|
4579
|
+
return {
|
|
4580
|
+
type: "image",
|
|
4581
|
+
source: {
|
|
4582
|
+
type: "base64",
|
|
4583
|
+
media_type: block.mediaType,
|
|
4584
|
+
data: block.base64
|
|
4585
|
+
}
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4588
|
+
return { type: "text", text: block.text };
|
|
4589
|
+
})
|
|
4590
|
+
});
|
|
4591
|
+
} else {
|
|
4592
|
+
toolResults.push({
|
|
4593
|
+
type: "tool_result",
|
|
4594
|
+
tool_use_id: tb.id,
|
|
4595
|
+
content: result
|
|
4596
|
+
});
|
|
4557
4597
|
}
|
|
4558
|
-
console.log(`[Vessel Agent] tool ${tb.name} completed in ${Date.now() - toolStartTime}ms, resultLen=${result.length}`);
|
|
4559
|
-
toolResults.push({
|
|
4560
|
-
type: "tool_result",
|
|
4561
|
-
tool_use_id: tb.id,
|
|
4562
|
-
content: result
|
|
4563
|
-
});
|
|
4564
4598
|
}
|
|
4565
4599
|
messages.push({ role: "user", content: toolResults });
|
|
4566
4600
|
}
|
|
@@ -4570,7 +4604,7 @@ class AnthropicProvider {
|
|
|
4570
4604
|
[Reached maximum tool call limit (${maxIterations} steps). You can adjust this in Settings → Max Tool Iterations, or continue by sending another message.]`);
|
|
4571
4605
|
}
|
|
4572
4606
|
} catch (err) {
|
|
4573
|
-
if (err.name !== "AbortError") {
|
|
4607
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
4574
4608
|
onChunk(`
|
|
4575
4609
|
|
|
4576
4610
|
[Error: ${err.message}]`);
|
|
@@ -4721,7 +4755,7 @@ class OpenAICompatProvider {
|
|
|
4721
4755
|
}
|
|
4722
4756
|
}
|
|
4723
4757
|
} catch (err) {
|
|
4724
|
-
if (err.name !== "AbortError") {
|
|
4758
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
4725
4759
|
onChunk(`
|
|
4726
4760
|
|
|
4727
4761
|
[Error: ${err.message}]`);
|
|
@@ -4745,7 +4779,6 @@ class OpenAICompatProvider {
|
|
|
4745
4779
|
for (let i = 0; i < maxIterations; i++) {
|
|
4746
4780
|
iterationsUsed = i + 1;
|
|
4747
4781
|
const msgTokenEstimate = JSON.stringify(messages).length;
|
|
4748
|
-
console.log(`[Vessel Agent OpenAI] iteration=${i} messages=${messages.length} msgChars=${msgTokenEstimate} tools=${openAITools.length}`);
|
|
4749
4782
|
const streamStartTime = Date.now();
|
|
4750
4783
|
let textAccum = "";
|
|
4751
4784
|
const toolCallAccums = {};
|
|
@@ -4781,7 +4814,6 @@ class OpenAICompatProvider {
|
|
|
4781
4814
|
}
|
|
4782
4815
|
}
|
|
4783
4816
|
}
|
|
4784
|
-
console.log(`[Vessel Agent OpenAI] stream complete in ${Date.now() - streamStartTime}ms, toolCalls=${Object.keys(toolCallAccums).length} textLen=${textAccum.length} finishReason=${finishReason}`);
|
|
4785
4817
|
const toolCalls = Object.values(toolCallAccums);
|
|
4786
4818
|
for (const tc of Object.values(toolCallAccums)) {
|
|
4787
4819
|
if (!tc.id) tc.id = `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4826,18 +4858,24 @@ class OpenAICompatProvider {
|
|
|
4826
4858
|
<<tool:${tc.name}${argSummary ? ":" + argSummary : ""}>>
|
|
4827
4859
|
`);
|
|
4828
4860
|
let result;
|
|
4829
|
-
const toolStartTime = Date.now();
|
|
4830
|
-
console.log(`[Vessel Agent OpenAI] executing tool: ${tc.name}`);
|
|
4831
4861
|
try {
|
|
4832
4862
|
result = await onToolCall(tc.name, args);
|
|
4833
4863
|
} catch (toolErr) {
|
|
4834
|
-
|
|
4864
|
+
const msg = toolErr instanceof Error ? toolErr.message : String(toolErr);
|
|
4865
|
+
result = `Error: Tool execution failed — ${msg}. Try a different approach or call read_page to refresh context.`;
|
|
4866
|
+
}
|
|
4867
|
+
let toolContent = result;
|
|
4868
|
+
try {
|
|
4869
|
+
const parsed = JSON.parse(result);
|
|
4870
|
+
if (isRichToolResult(parsed)) {
|
|
4871
|
+
toolContent = parsed.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
4872
|
+
}
|
|
4873
|
+
} catch {
|
|
4835
4874
|
}
|
|
4836
|
-
console.log(`[Vessel Agent OpenAI] tool ${tc.name} completed in ${Date.now() - toolStartTime}ms, resultLen=${result.length}`);
|
|
4837
4875
|
messages.push({
|
|
4838
4876
|
role: "tool",
|
|
4839
4877
|
tool_call_id: tc.id,
|
|
4840
|
-
content:
|
|
4878
|
+
content: toolContent
|
|
4841
4879
|
});
|
|
4842
4880
|
}
|
|
4843
4881
|
}
|
|
@@ -4847,7 +4885,7 @@ class OpenAICompatProvider {
|
|
|
4847
4885
|
[Reached maximum tool call limit (${maxIterations} steps). You can adjust this in Settings → Max Tool Iterations, or continue by sending another message.]`);
|
|
4848
4886
|
}
|
|
4849
4887
|
} catch (err) {
|
|
4850
|
-
if (err.name !== "AbortError") {
|
|
4888
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
4851
4889
|
onChunk(`
|
|
4852
4890
|
|
|
4853
4891
|
[Error: ${err.message}]`);
|
|
@@ -6715,6 +6753,13 @@ const TOOL_DEFINITIONS = [
|
|
|
6715
6753
|
},
|
|
6716
6754
|
tier: 0
|
|
6717
6755
|
},
|
|
6756
|
+
{
|
|
6757
|
+
name: "screenshot",
|
|
6758
|
+
title: "Screenshot",
|
|
6759
|
+
description: "Take a screenshot of the current page — see exactly what the user sees. Returns the image for visual analysis. Use when you need to verify visual layout, check what's actually rendered on screen, or when text extraction fails on heavy pages.",
|
|
6760
|
+
inputSchema: {},
|
|
6761
|
+
tier: 1
|
|
6762
|
+
},
|
|
6718
6763
|
{
|
|
6719
6764
|
name: "wait_for",
|
|
6720
6765
|
title: "Wait For",
|
|
@@ -7150,6 +7195,7 @@ const ALWAYS_FAST_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
7150
7195
|
"accept_cookies",
|
|
7151
7196
|
"wait_for",
|
|
7152
7197
|
"read_page",
|
|
7198
|
+
"screenshot",
|
|
7153
7199
|
"inspect_element"
|
|
7154
7200
|
]);
|
|
7155
7201
|
function inferIntent(query) {
|
|
@@ -7861,6 +7907,23 @@ function assertSafeURL(url) {
|
|
|
7861
7907
|
);
|
|
7862
7908
|
}
|
|
7863
7909
|
}
|
|
7910
|
+
async function captureScreenshot(wc) {
|
|
7911
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
7912
|
+
await new Promise((resolve) => setTimeout(resolve, 120 * (attempt + 1)));
|
|
7913
|
+
try {
|
|
7914
|
+
const image = await wc.capturePage();
|
|
7915
|
+
if (!image.isEmpty()) {
|
|
7916
|
+
const size = image.getSize();
|
|
7917
|
+
const base64 = image.toPNG().toString("base64");
|
|
7918
|
+
if (base64) {
|
|
7919
|
+
return { ok: true, base64, width: size.width, height: size.height };
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
} catch {
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
return { ok: false, error: "Page image was empty after 3 attempts" };
|
|
7926
|
+
}
|
|
7864
7927
|
const SESSION_VERSION = 1;
|
|
7865
7928
|
function getSessionsDir() {
|
|
7866
7929
|
return path$1.join(electron.app.getPath("userData"), "named-sessions");
|
|
@@ -8308,9 +8371,6 @@ async function executePageScript(wc, script, options) {
|
|
|
8308
8371
|
})
|
|
8309
8372
|
]);
|
|
8310
8373
|
if (result === PAGE_SCRIPT_TIMEOUT) {
|
|
8311
|
-
console.log(
|
|
8312
|
-
`[Vessel pageScript] timed out after ${timeoutMs}ms (${options?.label || "page-script"})`
|
|
8313
|
-
);
|
|
8314
8374
|
return PAGE_SCRIPT_TIMEOUT;
|
|
8315
8375
|
}
|
|
8316
8376
|
return result;
|
|
@@ -8325,9 +8385,6 @@ async function executePageScript(wc, script, options) {
|
|
|
8325
8385
|
function waitForLoad$1(wc, timeout = 5e3) {
|
|
8326
8386
|
return new Promise((resolve) => {
|
|
8327
8387
|
let finished = false;
|
|
8328
|
-
console.log(
|
|
8329
|
-
`[Vessel waitForLoad] started, isLoading=${wc.isLoading()}, timeout=${timeout}`
|
|
8330
|
-
);
|
|
8331
8388
|
const cleanup = () => {
|
|
8332
8389
|
wc.removeListener("did-finish-load", onLoadEvent);
|
|
8333
8390
|
wc.removeListener("did-stop-loading", onLoadEvent);
|
|
@@ -8336,23 +8393,19 @@ function waitForLoad$1(wc, timeout = 5e3) {
|
|
|
8336
8393
|
const finish = (reason) => {
|
|
8337
8394
|
if (finished) return;
|
|
8338
8395
|
finished = true;
|
|
8339
|
-
console.log(`[Vessel waitForLoad] finished: ${reason}`);
|
|
8340
8396
|
clearTimeout(timer);
|
|
8341
8397
|
cleanup();
|
|
8342
8398
|
resolve();
|
|
8343
8399
|
};
|
|
8344
8400
|
const onLoadEvent = () => {
|
|
8345
8401
|
const loading = wc.isLoading();
|
|
8346
|
-
console.log(
|
|
8347
|
-
`[Vessel waitForLoad] load event fired, isLoading=${loading}`
|
|
8348
|
-
);
|
|
8349
8402
|
if (!loading) {
|
|
8350
|
-
finish(
|
|
8403
|
+
finish();
|
|
8351
8404
|
}
|
|
8352
8405
|
};
|
|
8353
|
-
const timer = setTimeout(() => finish(
|
|
8406
|
+
const timer = setTimeout(() => finish(), timeout);
|
|
8354
8407
|
if (!wc.isLoading()) {
|
|
8355
|
-
finish(
|
|
8408
|
+
finish();
|
|
8356
8409
|
return;
|
|
8357
8410
|
}
|
|
8358
8411
|
wc.on("did-finish-load", onLoadEvent);
|
|
@@ -9041,19 +9094,12 @@ ${shadowOverlay}` : result;
|
|
|
9041
9094
|
const elInfo = await describeElementForClick$1(wc, selector);
|
|
9042
9095
|
if ("error" in elInfo) return `Error: ${elInfo.error}`;
|
|
9043
9096
|
const cartMatch = isAddToCartText(elInfo.text);
|
|
9044
|
-
console.log(
|
|
9045
|
-
`[Vessel cart-guard] text="${elInfo.text}" cartMatch=${cartMatch} url=${beforeUrl} hasPrior=${recentCartClicks.has(beforeUrl)}`
|
|
9046
|
-
);
|
|
9047
9097
|
if (cartMatch && isDuplicateCartClick(beforeUrl, elInfo.text)) {
|
|
9048
|
-
console.log(`[Vessel cart-guard] BLOCKED duplicate add-to-cart click`);
|
|
9049
9098
|
return `Blocked: "${elInfo.text}" was already clicked on this page. The item is in your cart. Call read_page to see available actions (e.g. View Cart, Continue Shopping).`;
|
|
9050
9099
|
}
|
|
9051
9100
|
if (!cartMatch && recentCartClicks.has(beforeUrl)) {
|
|
9052
9101
|
const dialogActions = await getCartDialogActions$1(wc);
|
|
9053
9102
|
if (dialogActions) {
|
|
9054
|
-
console.log(
|
|
9055
|
-
`[Vessel cart-guard] BLOCKED background click while cart dialog is open`
|
|
9056
|
-
);
|
|
9057
9103
|
return `Blocked: a cart confirmation dialog is open. Do not click background elements.
|
|
9058
9104
|
${dialogActions}
|
|
9059
9105
|
Click one of these dialog actions instead.`;
|
|
@@ -9066,7 +9112,6 @@ Click one of these dialog actions instead.`;
|
|
|
9066
9112
|
}
|
|
9067
9113
|
}
|
|
9068
9114
|
if (cartMatch) {
|
|
9069
|
-
console.log(`[Vessel cart-guard] RECORDED cart click for url=${beforeUrl}`);
|
|
9070
9115
|
recordCartClick(beforeUrl, elInfo.text);
|
|
9071
9116
|
}
|
|
9072
9117
|
const clickText = `Clicked: ${elInfo.text}`;
|
|
@@ -9306,9 +9351,6 @@ async function dismissPopup$1(wc) {
|
|
|
9306
9351
|
{ timeoutMs: 1500, label: "cart dialog continue shopping" }
|
|
9307
9352
|
);
|
|
9308
9353
|
if (continueResult && continueResult !== PAGE_SCRIPT_TIMEOUT && typeof continueResult === "string" && !continueResult.startsWith("Error")) {
|
|
9309
|
-
console.log(
|
|
9310
|
-
`[Vessel cart-guard] dismiss_popup auto-clicked dialog action: ${continueResult}`
|
|
9311
|
-
);
|
|
9312
9354
|
return `Cart confirmation handled: ${continueResult}. Item was already added to your cart.`;
|
|
9313
9355
|
}
|
|
9314
9356
|
const dialogActions = await getCartDialogActions$1(wc);
|
|
@@ -10669,6 +10711,7 @@ const KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
|
10669
10711
|
"dismiss_popup",
|
|
10670
10712
|
"clear_overlays",
|
|
10671
10713
|
"read_page",
|
|
10714
|
+
"screenshot",
|
|
10672
10715
|
"wait_for",
|
|
10673
10716
|
"create_checkpoint",
|
|
10674
10717
|
"restore_checkpoint",
|
|
@@ -10748,6 +10791,19 @@ async function executeAction(name, args, ctx) {
|
|
|
10748
10791
|
dangerous: isDangerousAction$1(name),
|
|
10749
10792
|
executor: async () => {
|
|
10750
10793
|
switch (name) {
|
|
10794
|
+
case "screenshot": {
|
|
10795
|
+
if (!wc) return "Error: No active tab";
|
|
10796
|
+
const screenshotStart = Date.now();
|
|
10797
|
+
const shot = await captureScreenshot(wc);
|
|
10798
|
+
if (!shot.ok) return `Error: ${shot.error}`;
|
|
10799
|
+
const screenshotMs = Date.now() - screenshotStart;
|
|
10800
|
+
const title = wc.getTitle() || "(untitled)";
|
|
10801
|
+
const url = wc.getURL();
|
|
10802
|
+
return makeImageResult(
|
|
10803
|
+
shot.base64,
|
|
10804
|
+
`Screenshot of "${title}" (${url}) — ${shot.width}x${shot.height}, captured in ${screenshotMs}ms. Analyze the image to understand the current visual state of the page.`
|
|
10805
|
+
);
|
|
10806
|
+
}
|
|
10751
10807
|
case "current_tab": {
|
|
10752
10808
|
const active = ctx.tabManager.getActiveTab();
|
|
10753
10809
|
const activeId = ctx.tabManager.getActiveTabId();
|
|
@@ -10951,14 +11007,12 @@ async function executeAction(name, args, ctx) {
|
|
|
10951
11007
|
if (requestedGlance) {
|
|
10952
11008
|
return glanceExtract(wc);
|
|
10953
11009
|
}
|
|
10954
|
-
console.log("[Vessel read_page] starting extraction with 6s timeout");
|
|
10955
11010
|
let content = null;
|
|
10956
11011
|
try {
|
|
10957
11012
|
content = await Promise.race([
|
|
10958
11013
|
extractContent(wc),
|
|
10959
11014
|
new Promise(
|
|
10960
11015
|
(resolve) => setTimeout(() => {
|
|
10961
|
-
console.log("[Vessel read_page] timeout fired, falling back");
|
|
10962
11016
|
resolve(null);
|
|
10963
11017
|
}, 6e3)
|
|
10964
11018
|
)
|
|
@@ -10966,18 +11020,13 @@ async function executeAction(name, args, ctx) {
|
|
|
10966
11020
|
} catch {
|
|
10967
11021
|
content = null;
|
|
10968
11022
|
}
|
|
10969
|
-
console.log(
|
|
10970
|
-
`[Vessel read_page] extraction result: ${content ? `content=${content.content.length}` : "null (timeout)"}`
|
|
10971
|
-
);
|
|
10972
11023
|
if (!content || content.content.length === 0) {
|
|
10973
|
-
console.log("[Vessel read_page] content empty/null, trying quick iframe dismiss");
|
|
10974
11024
|
try {
|
|
10975
11025
|
const iframeResult = await Promise.race([
|
|
10976
11026
|
tryDismissConsentIframe(wc),
|
|
10977
11027
|
new Promise((resolve) => setTimeout(() => resolve(null), 2e3))
|
|
10978
11028
|
]);
|
|
10979
11029
|
if (iframeResult) {
|
|
10980
|
-
console.log(`[Vessel read_page] iframe dismiss: ${iframeResult}`);
|
|
10981
11030
|
await sleep$1(500);
|
|
10982
11031
|
try {
|
|
10983
11032
|
content = await Promise.race([
|
|
@@ -11023,7 +11072,6 @@ ${truncated}`;
|
|
|
11023
11072
|
`Need more detail? Escalate with read_page(mode="debug") only if the narrow modes are insufficient.`
|
|
11024
11073
|
].filter(Boolean).join("\n\n");
|
|
11025
11074
|
}
|
|
11026
|
-
console.log("[Vessel read_page] falling back to glance mode");
|
|
11027
11075
|
return glanceExtract(wc);
|
|
11028
11076
|
}
|
|
11029
11077
|
case "wait_for": {
|
|
@@ -11941,11 +11989,7 @@ async function handleAIQuery(query, provider, activeWebContents, onChunk, onEnd,
|
|
|
11941
11989
|
const isSummarize = lowerQuery.startsWith("summarize") || lowerQuery.startsWith("tldr") || lowerQuery === "summary";
|
|
11942
11990
|
if (provider.streamAgentQuery && tabManager && activeWebContents && runtime2) {
|
|
11943
11991
|
try {
|
|
11944
|
-
const extractStart = Date.now();
|
|
11945
11992
|
const pageContent = await extractContent(activeWebContents);
|
|
11946
|
-
console.log(
|
|
11947
|
-
`[Vessel Agent] initial extractContent completed in ${Date.now() - extractStart}ms, contentLen=${pageContent.content.length}`
|
|
11948
|
-
);
|
|
11949
11993
|
const pageType = detectPageType(pageContent);
|
|
11950
11994
|
const defaultReadMode = chooseAgentReadMode(pageContent);
|
|
11951
11995
|
const structuredContext = buildScopedContext(
|
|
@@ -11998,7 +12042,8 @@ Instructions:
|
|
|
11998
12042
|
- Escalate page reads progressively: read_page(mode="glance") for a fast viewport snapshot on heavy/slow pages, then 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.
|
|
11999
12043
|
- Use read_page(mode="glance") when a page is slow to load or extraction times out — it shows what's on screen (headings, links, buttons, inputs) without waiting for heavy JS. It's what a human would see by just looking at the page.
|
|
12000
12044
|
- Use read_page(mode="debug") only as a last resort when the narrower modes are insufficient.
|
|
12001
|
-
- If read_page returns empty or times out, do NOT retry with the same mode. Switch to read_page(mode="glance") or
|
|
12045
|
+
- If read_page returns empty or times out, do NOT retry with the same mode. Switch to read_page(mode="glance") or use screenshot to see the page visually.
|
|
12046
|
+
- Use screenshot when you need to see exactly what the user sees — visual layout, rendered content, images, or when text extraction is failing. The screenshot returns the actual rendered page image for visual analysis. It works even when the JS thread is completely blocked.
|
|
12002
12047
|
- 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.
|
|
12003
12048
|
- 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.
|
|
12004
12049
|
- 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").
|
|
@@ -14215,25 +14260,6 @@ async function waitForCondition(wc, text, selector, timeoutMs) {
|
|
|
14215
14260
|
...diagnostic ? { diagnostic } : {}
|
|
14216
14261
|
});
|
|
14217
14262
|
}
|
|
14218
|
-
async function captureScreenshotPayload(wc) {
|
|
14219
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
14220
|
-
await new Promise((resolve) => setTimeout(resolve, 120 * (attempt + 1)));
|
|
14221
|
-
const image = await wc.capturePage();
|
|
14222
|
-
if (!image.isEmpty()) {
|
|
14223
|
-
const size = image.getSize();
|
|
14224
|
-
const base64 = image.toPNG().toString("base64");
|
|
14225
|
-
if (base64) {
|
|
14226
|
-
return {
|
|
14227
|
-
ok: true,
|
|
14228
|
-
base64,
|
|
14229
|
-
width: size.width,
|
|
14230
|
-
height: size.height
|
|
14231
|
-
};
|
|
14232
|
-
}
|
|
14233
|
-
}
|
|
14234
|
-
}
|
|
14235
|
-
return { ok: false, error: "page image was empty after 3 attempts" };
|
|
14236
|
-
}
|
|
14237
14263
|
function registerTools(server, tabManager, runtime2) {
|
|
14238
14264
|
server.registerPrompt(
|
|
14239
14265
|
"vessel-supervisor-brief",
|
|
@@ -15388,7 +15414,7 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
15388
15414
|
"Error capturing screenshot: active tab has zero-sized bounds"
|
|
15389
15415
|
);
|
|
15390
15416
|
}
|
|
15391
|
-
const screenshot = await
|
|
15417
|
+
const screenshot = await captureScreenshot(tab.view.webContents);
|
|
15392
15418
|
if (!screenshot.ok) {
|
|
15393
15419
|
return asTextResponse(
|
|
15394
15420
|
`Error capturing screenshot: ${screenshot.error}`
|
|
@@ -17932,7 +17958,6 @@ ${progress}
|
|
|
17932
17958
|
mode: "replace"
|
|
17933
17959
|
});
|
|
17934
17960
|
const approvalReason = this.getApprovalReason(dangerous);
|
|
17935
|
-
console.log(`[Vessel Runtime] action=${name} dangerous=${dangerous} approvalReason=${approvalReason} mode=${this.state.supervisor.approvalMode}`);
|
|
17936
17961
|
if (approvalReason) {
|
|
17937
17962
|
this.publishTranscript({
|
|
17938
17963
|
source,
|
|
@@ -17942,9 +17967,7 @@ ${progress}
|
|
|
17942
17967
|
streamId: transcriptStreamId,
|
|
17943
17968
|
mode: "replace"
|
|
17944
17969
|
});
|
|
17945
|
-
console.log(`[Vessel Runtime] awaiting approval for ${name}...`);
|
|
17946
17970
|
const approved = await this.awaitApproval(action, approvalReason);
|
|
17947
|
-
console.log(`[Vessel Runtime] approval result for ${name}: ${approved}`);
|
|
17948
17971
|
if (!approved) {
|
|
17949
17972
|
this.publishTranscript({
|
|
17950
17973
|
source,
|
|
@@ -18125,7 +18148,7 @@ ${progress}
|
|
|
18125
18148
|
if (!toolBreakdown[name]) toolBreakdown[name] = { count: 0, totalMs: 0, avgMs: 0, errors: 0 };
|
|
18126
18149
|
toolBreakdown[name].count++;
|
|
18127
18150
|
if (action.durationMs != null) toolBreakdown[name].totalMs += action.durationMs;
|
|
18128
|
-
if (action.status === "
|
|
18151
|
+
if (action.status === "failed") toolBreakdown[name].errors++;
|
|
18129
18152
|
}
|
|
18130
18153
|
for (const entry of Object.values(toolBreakdown)) {
|
|
18131
18154
|
entry.avgMs = entry.count > 0 ? Math.round(entry.totalMs / entry.count) : 0;
|
|
@@ -1972,7 +1972,7 @@ const _createHooksMap = function _createHooksMap2() {
|
|
|
1972
1972
|
function createDOMPurify() {
|
|
1973
1973
|
let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
1974
1974
|
const DOMPurify = (root2) => createDOMPurify(root2);
|
|
1975
|
-
DOMPurify.version = "3.3.
|
|
1975
|
+
DOMPurify.version = "3.3.3";
|
|
1976
1976
|
DOMPurify.removed = [];
|
|
1977
1977
|
if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
|
|
1978
1978
|
DOMPurify.isSupported = false;
|
package/out/renderer/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:;" />
|
|
7
7
|
<title>Vessel</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-FJxHleYP.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="./assets/index-DMd-y6tm.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
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.16",
|
|
5
5
|
"description": "AI-native web browser for Linux — persistent browser runtime for autonomous agents with human supervision",
|
|
6
6
|
"main": "./out/main/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -52,22 +52,22 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/dompurify": "^3.0.5",
|
|
55
|
-
"@types/node": "^25.
|
|
56
|
-
"electron": "^40.8.
|
|
55
|
+
"@types/node": "^25.5.0",
|
|
56
|
+
"electron": "^40.8.3",
|
|
57
57
|
"electron-builder": "^26.8.1",
|
|
58
58
|
"electron-vite": "^5.0.0",
|
|
59
59
|
"solid-js": "^1.9.11",
|
|
60
60
|
"tsx": "^4.21.0",
|
|
61
61
|
"typescript": "^5.9.3",
|
|
62
|
-
"vite-plugin-solid": "^2.11.
|
|
62
|
+
"vite-plugin-solid": "^2.11.11"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@anthropic-ai/sdk": "^0.
|
|
65
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
66
66
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
67
67
|
"@mozilla/readability": "^0.6.0",
|
|
68
|
-
"dompurify": "^3.3.
|
|
68
|
+
"dompurify": "^3.3.3",
|
|
69
69
|
"linkedom": "^0.18.12",
|
|
70
|
-
"openai": "^6.
|
|
70
|
+
"openai": "^6.32.0",
|
|
71
71
|
"zod": "^4.3.6"
|
|
72
72
|
}
|
|
73
73
|
}
|