browsentic 0.4.6 → 0.4.8
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/cli.js +100 -19
- package/dist/daemon-main.js +114 -21
- package/extension/chrome-mv3/assets/globals-DF66MUxu.css +2 -0
- package/extension/chrome-mv3/background.js +12 -12
- package/extension/chrome-mv3/chunks/globals-DAVXgO1U.js +177 -0
- package/extension/chrome-mv3/chunks/popup-BBVQYKJE.js +1 -0
- package/extension/chrome-mv3/chunks/sidepanel-BTSXdzrt.js +11 -0
- package/extension/chrome-mv3/content-scripts/content.js +22 -22
- package/extension/chrome-mv3/manifest.json +1 -1
- package/extension/chrome-mv3/popup.html +3 -3
- package/extension/chrome-mv3/sidepanel.html +3 -3
- package/package.json +1 -1
- package/skills/a-eye.md +3 -3
- package/extension/chrome-mv3/assets/globals-BOLwDXwQ.css +0 -2
- package/extension/chrome-mv3/chunks/globals-DIBegMWk.js +0 -137
- package/extension/chrome-mv3/chunks/popup-yL0itqnF.js +0 -1
- package/extension/chrome-mv3/chunks/sidepanel-WyGwqwXh.js +0 -11
package/dist/cli.js
CHANGED
|
@@ -28636,12 +28636,9 @@ var CURSOR_PATHS = [
|
|
|
28636
28636
|
];
|
|
28637
28637
|
var CURSOR2 = `url("data:image/svg+xml,${encodeURIComponent(cursorSvg())}") 14 14, crosshair`;
|
|
28638
28638
|
var DEFAULT_HINT = "Click the element you mean";
|
|
28639
|
-
var
|
|
28640
|
-
function lensIsUp() {
|
|
28641
|
-
return picking;
|
|
28642
|
-
}
|
|
28639
|
+
var dismissCurrent = null;
|
|
28643
28640
|
function pickWithLens({ hint, timeoutMs }) {
|
|
28644
|
-
|
|
28641
|
+
dismissCurrent?.();
|
|
28645
28642
|
const host = document.createElement("div");
|
|
28646
28643
|
host.id = HOST_ID;
|
|
28647
28644
|
host.style.cssText = "all: initial; position: static;";
|
|
@@ -28656,6 +28653,8 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28656
28653
|
const chip = root.querySelector(".chip");
|
|
28657
28654
|
let hovered = null;
|
|
28658
28655
|
return new Promise((resolve) => {
|
|
28656
|
+
const dismiss = () => settle2({ cancelled: true });
|
|
28657
|
+
dismissCurrent = dismiss;
|
|
28659
28658
|
const timer = setTimeout(() => settle2({ timedOut: true }), timeoutMs);
|
|
28660
28659
|
const mute = (event) => {
|
|
28661
28660
|
event.stopPropagation();
|
|
@@ -28720,7 +28719,7 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28720
28719
|
}
|
|
28721
28720
|
host.remove();
|
|
28722
28721
|
cursor.remove();
|
|
28723
|
-
|
|
28722
|
+
if (dismissCurrent === dismiss) dismissCurrent = null;
|
|
28724
28723
|
resolve(outcome);
|
|
28725
28724
|
}
|
|
28726
28725
|
});
|
|
@@ -28799,18 +28798,16 @@ function styles() {
|
|
|
28799
28798
|
|
|
28800
28799
|
// ../lib/actions/page/pick-element.ts
|
|
28801
28800
|
var MAX_CONTENT = 2e4;
|
|
28801
|
+
var PICK_DEFAULT_TIMEOUT_MS = 6e4;
|
|
28802
28802
|
var pickElement = defineAction({
|
|
28803
28803
|
name: "page.pickElement",
|
|
28804
|
-
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role
|
|
28804
|
+
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role, its rendered text and a screenshot of it exactly as they saw it. Use it when a target is genuinely ambiguous \u2014 several things share a label, or the user said \u201Cthis one\u201D about something you cannot see \u2014 and pointing is faster than describing. It takes over the page and waits for a person, so never call it to explore; a new call dismisses a pick already waiting.",
|
|
28805
28805
|
input: external_exports.object({
|
|
28806
28806
|
hint: external_exports.string().max(120).optional().describe("One line shown over the page saying what to point at, e.g. \u201CPoint at the price you mean\u201D"),
|
|
28807
28807
|
maxContentLength: external_exports.number().int().positive().max(MAX_CONTENT).default(2e3).describe('Characters of the element\u2019s rendered text to return; past that it is cut and "truncated" comes back true'),
|
|
28808
|
-
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(
|
|
28808
|
+
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(PICK_DEFAULT_TIMEOUT_MS).describe("How long to wait for the user to click before giving up")
|
|
28809
28809
|
}),
|
|
28810
28810
|
async execute({ hint, maxContentLength, timeoutMs }) {
|
|
28811
|
-
if (lensIsUp()) {
|
|
28812
|
-
throw new ActionError("A-Eye is already waiting for the user to point at something", "ACTION_FAILED");
|
|
28813
|
-
}
|
|
28814
28811
|
const outcome = await pickWithLens({ hint, timeoutMs });
|
|
28815
28812
|
if ("timedOut" in outcome) {
|
|
28816
28813
|
throw new ActionError(
|
|
@@ -28824,12 +28821,18 @@ var pickElement = defineAction({
|
|
|
28824
28821
|
const element = outcome.picked;
|
|
28825
28822
|
const rendered2 = element instanceof HTMLElement ? element.innerText : element.textContent ?? "";
|
|
28826
28823
|
const content = rendered2.replace(/\n{3,}/g, "\n\n").trim() || accessibleText(element);
|
|
28824
|
+
const rect = element.getBoundingClientRect();
|
|
28827
28825
|
return {
|
|
28828
28826
|
element: describeElement(element),
|
|
28829
28827
|
content: content.slice(0, maxContentLength),
|
|
28830
28828
|
truncated: content.length > maxContentLength,
|
|
28831
28829
|
url: location.href,
|
|
28832
|
-
title: document.title
|
|
28830
|
+
title: document.title,
|
|
28831
|
+
capture: {
|
|
28832
|
+
region: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
|
|
28833
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
28834
|
+
dpr: window.devicePixelRatio || 1
|
|
28835
|
+
}
|
|
28833
28836
|
};
|
|
28834
28837
|
}
|
|
28835
28838
|
});
|
|
@@ -29895,7 +29898,8 @@ var AGENTS = {
|
|
|
29895
29898
|
vendor: "Anthropic",
|
|
29896
29899
|
bin: "claude",
|
|
29897
29900
|
install: "npm i -g @anthropic-ai/claude-code",
|
|
29898
|
-
docs: "https://claude.com/claude-code"
|
|
29901
|
+
docs: "https://claude.com/claude-code",
|
|
29902
|
+
models: ["claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"]
|
|
29899
29903
|
},
|
|
29900
29904
|
codex: {
|
|
29901
29905
|
kind: "codex",
|
|
@@ -29903,7 +29907,8 @@ var AGENTS = {
|
|
|
29903
29907
|
vendor: "OpenAI",
|
|
29904
29908
|
bin: "codex",
|
|
29905
29909
|
install: "npm i -g @openai/codex",
|
|
29906
|
-
docs: "https://developers.openai.com/codex/cli"
|
|
29910
|
+
docs: "https://developers.openai.com/codex/cli",
|
|
29911
|
+
models: ["gpt-5.6-terra", "gpt-5.1-codex-max", "gpt-5.1-codex", "gpt-5.1-codex-mini"]
|
|
29907
29912
|
},
|
|
29908
29913
|
antigravity: {
|
|
29909
29914
|
kind: "antigravity",
|
|
@@ -29911,7 +29916,8 @@ var AGENTS = {
|
|
|
29911
29916
|
vendor: "Google",
|
|
29912
29917
|
bin: "agy",
|
|
29913
29918
|
install: "https://antigravity.google/docs/cli/install",
|
|
29914
|
-
docs: "https://antigravity.google/docs/cli"
|
|
29919
|
+
docs: "https://antigravity.google/docs/cli",
|
|
29920
|
+
models: ["gemini-3-pro", "gemini-3-flash"]
|
|
29915
29921
|
}
|
|
29916
29922
|
};
|
|
29917
29923
|
var AGENT_LIST = AGENT_KINDS.map((kind) => AGENTS[kind]);
|
|
@@ -29925,11 +29931,13 @@ var SAVE_SITE_MAP_ACTION = `${RESERVED_PREFIX}saveSiteMap`;
|
|
|
29925
29931
|
var START_RECORDING_ACTION = `${RESERVED_PREFIX}startRecording`;
|
|
29926
29932
|
var STOP_RECORDING_ACTION = `${RESERVED_PREFIX}stopRecording`;
|
|
29927
29933
|
var READ_SITEMAP_ACTION = `${RESERVED_PREFIX}readSitemap`;
|
|
29934
|
+
var FOCUS_SHOT_ACTION = `${RESERVED_PREFIX}focusShot`;
|
|
29928
29935
|
var RESERVED_ACTIONS = [
|
|
29929
29936
|
SAVE_SITE_MAP_ACTION,
|
|
29930
29937
|
START_RECORDING_ACTION,
|
|
29931
29938
|
STOP_RECORDING_ACTION,
|
|
29932
|
-
READ_SITEMAP_ACTION
|
|
29939
|
+
READ_SITEMAP_ACTION,
|
|
29940
|
+
FOCUS_SHOT_ACTION
|
|
29933
29941
|
];
|
|
29934
29942
|
|
|
29935
29943
|
// ../lib/actions/tool-names.ts
|
|
@@ -30243,6 +30251,15 @@ var claudeRunner = {
|
|
|
30243
30251
|
};
|
|
30244
30252
|
},
|
|
30245
30253
|
reader() {
|
|
30254
|
+
let generated = 0;
|
|
30255
|
+
const report = (usage, sink) => {
|
|
30256
|
+
if (!usage) return;
|
|
30257
|
+
generated += usage.output_tokens ?? 0;
|
|
30258
|
+
sink.usage({
|
|
30259
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
30260
|
+
outputTokens: generated
|
|
30261
|
+
});
|
|
30262
|
+
};
|
|
30246
30263
|
return (line, sink) => {
|
|
30247
30264
|
const message = parseJsonLine(line);
|
|
30248
30265
|
if (!message) return;
|
|
@@ -30266,10 +30283,14 @@ var claudeRunner = {
|
|
|
30266
30283
|
}
|
|
30267
30284
|
return;
|
|
30268
30285
|
}
|
|
30286
|
+
case "assistant":
|
|
30287
|
+
if (!message.parent_tool_use_id) report(message.message?.usage, sink);
|
|
30288
|
+
return;
|
|
30269
30289
|
case "result":
|
|
30270
30290
|
if (message.is_error) {
|
|
30271
30291
|
return sink.fail("AGENT_FAILED", message.result || message.subtype || "Claude Code reported an error");
|
|
30272
30292
|
}
|
|
30293
|
+
if (!generated) report(message.usage, sink);
|
|
30273
30294
|
return sink.done(message.stop_reason || "end_turn");
|
|
30274
30295
|
}
|
|
30275
30296
|
};
|
|
@@ -30580,6 +30601,17 @@ var codexRunner = {
|
|
|
30580
30601
|
return finish(msg.message, sink);
|
|
30581
30602
|
case "web_search_begin":
|
|
30582
30603
|
return sink.tool(randomUUID3(), WEB_TOOL);
|
|
30604
|
+
case "token_count": {
|
|
30605
|
+
const last = msg.info?.last_token_usage ?? msg.info?.total_token_usage;
|
|
30606
|
+
const total = msg.info?.total_token_usage ?? last;
|
|
30607
|
+
if (last) {
|
|
30608
|
+
sink.usage({
|
|
30609
|
+
contextTokens: (last.input_tokens ?? 0) + (last.output_tokens ?? 0),
|
|
30610
|
+
outputTokens: total?.output_tokens ?? 0
|
|
30611
|
+
});
|
|
30612
|
+
}
|
|
30613
|
+
return;
|
|
30614
|
+
}
|
|
30583
30615
|
case "task_complete":
|
|
30584
30616
|
return sink.done("end_turn");
|
|
30585
30617
|
case "error":
|
|
@@ -30603,8 +30635,16 @@ var codexRunner = {
|
|
|
30603
30635
|
if (kind === "web_search") return sink.tool(item?.id ?? randomUUID3(), WEB_TOOL);
|
|
30604
30636
|
return;
|
|
30605
30637
|
}
|
|
30606
|
-
case "turn.completed":
|
|
30638
|
+
case "turn.completed": {
|
|
30639
|
+
const usage = frame.usage;
|
|
30640
|
+
if (usage) {
|
|
30641
|
+
sink.usage({
|
|
30642
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
30643
|
+
outputTokens: usage.output_tokens ?? 0
|
|
30644
|
+
});
|
|
30645
|
+
}
|
|
30607
30646
|
return sink.done("end_turn");
|
|
30647
|
+
}
|
|
30608
30648
|
case "turn.failed":
|
|
30609
30649
|
return sink.fail("AGENT_FAILED", frame.error?.message || "Codex could not finish the turn");
|
|
30610
30650
|
case "error":
|
|
@@ -31707,6 +31747,7 @@ function invokeTimeoutFor(action, input2) {
|
|
|
31707
31747
|
const declared = input2?.timeoutMs;
|
|
31708
31748
|
if (typeof declared === "number" && declared > 0) return declared + 1e4;
|
|
31709
31749
|
if (action === awaitMonitor.name) return AWAIT_DEFAULT_TIMEOUT_MS + 1e4;
|
|
31750
|
+
if (action === pickElement.name) return PICK_DEFAULT_TIMEOUT_MS + 1e4;
|
|
31710
31751
|
return REQUEST_TIMEOUT_MS;
|
|
31711
31752
|
}
|
|
31712
31753
|
|
|
@@ -33455,6 +33496,12 @@ var Server = class extends Protocol {
|
|
|
33455
33496
|
// server.ts
|
|
33456
33497
|
var STATUS_TOOL = toolNameFor(`${RESERVED_PREFIX}status`);
|
|
33457
33498
|
var SCREENSHOT_TOOL = "page_screenshot";
|
|
33499
|
+
var PICK_TOOL = "page_pickElement";
|
|
33500
|
+
var FOCUS_SHOT_TOOL = {
|
|
33501
|
+
name: toolNameFor(FOCUS_SHOT_ACTION),
|
|
33502
|
+
description: "Show the screenshot of the element the user pointed at with A-Eye, taken at the instant they picked it. Only answers when the current instruction arrived with a pick attached.",
|
|
33503
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
33504
|
+
};
|
|
33458
33505
|
var RESOURCES = [
|
|
33459
33506
|
{
|
|
33460
33507
|
uri: "browsentic://page/current",
|
|
@@ -33568,7 +33615,7 @@ function createMcpServer(bridge, version2, opts = {}) {
|
|
|
33568
33615
|
description: "Report whether the Browsentic browser extension is connected, its version, and the active tab. Use this first if a page tool fails.",
|
|
33569
33616
|
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
33570
33617
|
},
|
|
33571
|
-
...opts.agentRun ? [SAVE_SITE_MAP_TOOL] : []
|
|
33618
|
+
...opts.agentRun ? [SAVE_SITE_MAP_TOOL, FOCUS_SHOT_TOOL] : []
|
|
33572
33619
|
]
|
|
33573
33620
|
};
|
|
33574
33621
|
});
|
|
@@ -33577,6 +33624,8 @@ function createMcpServer(bridge, version2, opts = {}) {
|
|
|
33577
33624
|
const action = actionNameFor(params.name);
|
|
33578
33625
|
const result = await bridge.invoke(action, params.arguments ?? {});
|
|
33579
33626
|
if (params.name === SCREENSHOT_TOOL) return renderScreenshot(result);
|
|
33627
|
+
if (params.name === FOCUS_SHOT_TOOL.name) return renderFocusShot(result);
|
|
33628
|
+
if (params.name === PICK_TOOL) return renderPick(result, shouldFence(action, policy) ? tag2 : void 0);
|
|
33580
33629
|
return render(result, shouldFence(action, policy) ? tag2 : void 0);
|
|
33581
33630
|
});
|
|
33582
33631
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [...RESOURCES] }));
|
|
@@ -33654,6 +33703,38 @@ function renderScreenshot(result) {
|
|
|
33654
33703
|
]
|
|
33655
33704
|
};
|
|
33656
33705
|
}
|
|
33706
|
+
function renderPick(result, fenceWith) {
|
|
33707
|
+
if (!result.ok) return render(result, fenceWith);
|
|
33708
|
+
const { shot, ...rest } = result.data;
|
|
33709
|
+
if (typeof shot?.dataUrl !== "string") return render(result, fenceWith);
|
|
33710
|
+
const [mimeType, base643] = splitDataUrl(shot.dataUrl);
|
|
33711
|
+
const rendered2 = render({ ok: true, data: rest }, fenceWith);
|
|
33712
|
+
return {
|
|
33713
|
+
content: [
|
|
33714
|
+
...rendered2.content,
|
|
33715
|
+
{ type: "image", data: base643, mimeType },
|
|
33716
|
+
{
|
|
33717
|
+
type: "text",
|
|
33718
|
+
text: `${IMAGE_NOTE} This is the picked element photographed at the instant the user clicked it.`
|
|
33719
|
+
}
|
|
33720
|
+
]
|
|
33721
|
+
};
|
|
33722
|
+
}
|
|
33723
|
+
function renderFocusShot(result) {
|
|
33724
|
+
if (!result.ok) return render(result);
|
|
33725
|
+
const dataUrl = result.data?.dataUrl;
|
|
33726
|
+
if (typeof dataUrl !== "string") return render(result);
|
|
33727
|
+
const [mimeType, base643] = splitDataUrl(dataUrl);
|
|
33728
|
+
return {
|
|
33729
|
+
content: [
|
|
33730
|
+
{ type: "image", data: base643, mimeType },
|
|
33731
|
+
{
|
|
33732
|
+
type: "text",
|
|
33733
|
+
text: `${IMAGE_NOTE} This is the element the user pointed at with A-Eye, as it stood when they picked it.`
|
|
33734
|
+
}
|
|
33735
|
+
]
|
|
33736
|
+
};
|
|
33737
|
+
}
|
|
33657
33738
|
function splitDataUrl(dataUrl) {
|
|
33658
33739
|
const match = /^data:([^;,]+);base64,(.*)$/s.exec(dataUrl);
|
|
33659
33740
|
return match ? [match[1], match[2]] : ["image/png", ""];
|
|
@@ -33679,7 +33760,7 @@ function text2(uri, mimeType, body) {
|
|
|
33679
33760
|
// package.json
|
|
33680
33761
|
var package_default = {
|
|
33681
33762
|
name: "browsentic",
|
|
33682
|
-
version: "0.4.
|
|
33763
|
+
version: "0.4.8",
|
|
33683
33764
|
description: "Hand your real, logged-in browser to the AI agent you already run. Installs the browser extension, runs the local daemon, and speaks MCP.",
|
|
33684
33765
|
type: "module",
|
|
33685
33766
|
license: "MIT",
|
package/dist/daemon-main.js
CHANGED
|
@@ -3722,7 +3722,7 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
3722
3722
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
3723
3723
|
|
|
3724
3724
|
// ../lib/actions/protocol.ts
|
|
3725
|
-
var SOCKET_PROTOCOL_VERSION =
|
|
3725
|
+
var SOCKET_PROTOCOL_VERSION = 16;
|
|
3726
3726
|
var EXTERNAL_RUN_ID = "external";
|
|
3727
3727
|
var DAEMON_PORTS = [8765, 8766, 8767];
|
|
3728
3728
|
var EXTENSION_REQUEST_FRAMES = [
|
|
@@ -3740,6 +3740,7 @@ var EXTENSION_REQUEST_FRAMES = [
|
|
|
3740
3740
|
"analyzeRecording",
|
|
3741
3741
|
"agentState",
|
|
3742
3742
|
"setAgent",
|
|
3743
|
+
"setAgentModel",
|
|
3743
3744
|
"grantAgent",
|
|
3744
3745
|
"listSkills",
|
|
3745
3746
|
"guardrails",
|
|
@@ -20228,12 +20229,9 @@ var CURSOR_PATHS = [
|
|
|
20228
20229
|
];
|
|
20229
20230
|
var CURSOR2 = `url("data:image/svg+xml,${encodeURIComponent(cursorSvg())}") 14 14, crosshair`;
|
|
20230
20231
|
var DEFAULT_HINT = "Click the element you mean";
|
|
20231
|
-
var
|
|
20232
|
-
function lensIsUp() {
|
|
20233
|
-
return picking;
|
|
20234
|
-
}
|
|
20232
|
+
var dismissCurrent = null;
|
|
20235
20233
|
function pickWithLens({ hint, timeoutMs }) {
|
|
20236
|
-
|
|
20234
|
+
dismissCurrent?.();
|
|
20237
20235
|
const host = document.createElement("div");
|
|
20238
20236
|
host.id = HOST_ID;
|
|
20239
20237
|
host.style.cssText = "all: initial; position: static;";
|
|
@@ -20248,6 +20246,8 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
20248
20246
|
const chip = root.querySelector(".chip");
|
|
20249
20247
|
let hovered = null;
|
|
20250
20248
|
return new Promise((resolve4) => {
|
|
20249
|
+
const dismiss = () => settle2({ cancelled: true });
|
|
20250
|
+
dismissCurrent = dismiss;
|
|
20251
20251
|
const timer = setTimeout(() => settle2({ timedOut: true }), timeoutMs);
|
|
20252
20252
|
const mute = (event) => {
|
|
20253
20253
|
event.stopPropagation();
|
|
@@ -20312,7 +20312,7 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
20312
20312
|
}
|
|
20313
20313
|
host.remove();
|
|
20314
20314
|
cursor.remove();
|
|
20315
|
-
|
|
20315
|
+
if (dismissCurrent === dismiss) dismissCurrent = null;
|
|
20316
20316
|
resolve4(outcome);
|
|
20317
20317
|
}
|
|
20318
20318
|
});
|
|
@@ -20391,18 +20391,16 @@ function styles() {
|
|
|
20391
20391
|
|
|
20392
20392
|
// ../lib/actions/page/pick-element.ts
|
|
20393
20393
|
var MAX_CONTENT = 2e4;
|
|
20394
|
+
var PICK_DEFAULT_TIMEOUT_MS = 6e4;
|
|
20394
20395
|
var pickElement = defineAction({
|
|
20395
20396
|
name: "page.pickElement",
|
|
20396
|
-
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role
|
|
20397
|
+
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role, its rendered text and a screenshot of it exactly as they saw it. Use it when a target is genuinely ambiguous \u2014 several things share a label, or the user said \u201Cthis one\u201D about something you cannot see \u2014 and pointing is faster than describing. It takes over the page and waits for a person, so never call it to explore; a new call dismisses a pick already waiting.",
|
|
20397
20398
|
input: external_exports.object({
|
|
20398
20399
|
hint: external_exports.string().max(120).optional().describe("One line shown over the page saying what to point at, e.g. \u201CPoint at the price you mean\u201D"),
|
|
20399
20400
|
maxContentLength: external_exports.number().int().positive().max(MAX_CONTENT).default(2e3).describe('Characters of the element\u2019s rendered text to return; past that it is cut and "truncated" comes back true'),
|
|
20400
|
-
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(
|
|
20401
|
+
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(PICK_DEFAULT_TIMEOUT_MS).describe("How long to wait for the user to click before giving up")
|
|
20401
20402
|
}),
|
|
20402
20403
|
async execute({ hint, maxContentLength, timeoutMs }) {
|
|
20403
|
-
if (lensIsUp()) {
|
|
20404
|
-
throw new ActionError("A-Eye is already waiting for the user to point at something", "ACTION_FAILED");
|
|
20405
|
-
}
|
|
20406
20404
|
const outcome = await pickWithLens({ hint, timeoutMs });
|
|
20407
20405
|
if ("timedOut" in outcome) {
|
|
20408
20406
|
throw new ActionError(
|
|
@@ -20416,12 +20414,18 @@ var pickElement = defineAction({
|
|
|
20416
20414
|
const element = outcome.picked;
|
|
20417
20415
|
const rendered2 = element instanceof HTMLElement ? element.innerText : element.textContent ?? "";
|
|
20418
20416
|
const content = rendered2.replace(/\n{3,}/g, "\n\n").trim() || accessibleText(element);
|
|
20417
|
+
const rect = element.getBoundingClientRect();
|
|
20419
20418
|
return {
|
|
20420
20419
|
element: describeElement(element),
|
|
20421
20420
|
content: content.slice(0, maxContentLength),
|
|
20422
20421
|
truncated: content.length > maxContentLength,
|
|
20423
20422
|
url: location.href,
|
|
20424
|
-
title: document.title
|
|
20423
|
+
title: document.title,
|
|
20424
|
+
capture: {
|
|
20425
|
+
region: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
|
|
20426
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
20427
|
+
dpr: window.devicePixelRatio || 1
|
|
20428
|
+
}
|
|
20425
20429
|
};
|
|
20426
20430
|
}
|
|
20427
20431
|
});
|
|
@@ -21489,6 +21493,7 @@ var SAVE_SITE_MAP_ACTION = `${RESERVED_PREFIX}saveSiteMap`;
|
|
|
21489
21493
|
var START_RECORDING_ACTION = `${RESERVED_PREFIX}startRecording`;
|
|
21490
21494
|
var STOP_RECORDING_ACTION = `${RESERVED_PREFIX}stopRecording`;
|
|
21491
21495
|
var READ_SITEMAP_ACTION = `${RESERVED_PREFIX}readSitemap`;
|
|
21496
|
+
var FOCUS_SHOT_ACTION = `${RESERVED_PREFIX}focusShot`;
|
|
21492
21497
|
|
|
21493
21498
|
// ../lib/agents/catalog.ts
|
|
21494
21499
|
var AGENT_KINDS = ["claude", "codex", "antigravity"];
|
|
@@ -21500,7 +21505,8 @@ var AGENTS = {
|
|
|
21500
21505
|
vendor: "Anthropic",
|
|
21501
21506
|
bin: "claude",
|
|
21502
21507
|
install: "npm i -g @anthropic-ai/claude-code",
|
|
21503
|
-
docs: "https://claude.com/claude-code"
|
|
21508
|
+
docs: "https://claude.com/claude-code",
|
|
21509
|
+
models: ["claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"]
|
|
21504
21510
|
},
|
|
21505
21511
|
codex: {
|
|
21506
21512
|
kind: "codex",
|
|
@@ -21508,7 +21514,8 @@ var AGENTS = {
|
|
|
21508
21514
|
vendor: "OpenAI",
|
|
21509
21515
|
bin: "codex",
|
|
21510
21516
|
install: "npm i -g @openai/codex",
|
|
21511
|
-
docs: "https://developers.openai.com/codex/cli"
|
|
21517
|
+
docs: "https://developers.openai.com/codex/cli",
|
|
21518
|
+
models: ["gpt-5.6-terra", "gpt-5.1-codex-max", "gpt-5.1-codex", "gpt-5.1-codex-mini"]
|
|
21512
21519
|
},
|
|
21513
21520
|
antigravity: {
|
|
21514
21521
|
kind: "antigravity",
|
|
@@ -21516,7 +21523,8 @@ var AGENTS = {
|
|
|
21516
21523
|
vendor: "Google",
|
|
21517
21524
|
bin: "agy",
|
|
21518
21525
|
install: "https://antigravity.google/docs/cli/install",
|
|
21519
|
-
docs: "https://antigravity.google/docs/cli"
|
|
21526
|
+
docs: "https://antigravity.google/docs/cli",
|
|
21527
|
+
models: ["gemini-3-pro", "gemini-3-flash"]
|
|
21520
21528
|
}
|
|
21521
21529
|
};
|
|
21522
21530
|
var AGENT_LIST = AGENT_KINDS.map((kind) => AGENTS[kind]);
|
|
@@ -21642,6 +21650,21 @@ function writeActiveAgent(kind) {
|
|
|
21642
21650
|
const stored = readStored();
|
|
21643
21651
|
write({ ...stored, agent: kind });
|
|
21644
21652
|
}
|
|
21653
|
+
function writeAgentModel(kind, model) {
|
|
21654
|
+
const stored = readStored();
|
|
21655
|
+
const agents = { ...stored.agents ?? {} };
|
|
21656
|
+
const scoped = { ...agents[kind] ?? {} };
|
|
21657
|
+
const value = text(model);
|
|
21658
|
+
if (value) scoped.model = value;
|
|
21659
|
+
else delete scoped.model;
|
|
21660
|
+
if (Object.keys(scoped).length) agents[kind] = scoped;
|
|
21661
|
+
else delete agents[kind];
|
|
21662
|
+
const next = { ...stored };
|
|
21663
|
+
if (Object.keys(agents).length) next.agents = agents;
|
|
21664
|
+
else delete next.agents;
|
|
21665
|
+
if (kind === "claude") delete next.model;
|
|
21666
|
+
write(next);
|
|
21667
|
+
}
|
|
21645
21668
|
function writeGuardrailSetting(setting, value) {
|
|
21646
21669
|
const stored = readStored();
|
|
21647
21670
|
const guardrails = { ...stored.guardrails ?? {} };
|
|
@@ -23346,6 +23369,11 @@ function revokeSessions(predicate) {
|
|
|
23346
23369
|
// agent/service.ts
|
|
23347
23370
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
23348
23371
|
|
|
23372
|
+
// ../lib/actions/tool-names.ts
|
|
23373
|
+
function toolNameFor(actionName) {
|
|
23374
|
+
return actionName.replaceAll(".", "_");
|
|
23375
|
+
}
|
|
23376
|
+
|
|
23349
23377
|
// ../lib/skills/scrub.ts
|
|
23350
23378
|
var CONTROL_CHARS = new RegExp(
|
|
23351
23379
|
"[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2066-\\u2069\\ufeff]",
|
|
@@ -23615,6 +23643,15 @@ var claudeRunner = {
|
|
|
23615
23643
|
};
|
|
23616
23644
|
},
|
|
23617
23645
|
reader() {
|
|
23646
|
+
let generated = 0;
|
|
23647
|
+
const report = (usage, sink) => {
|
|
23648
|
+
if (!usage) return;
|
|
23649
|
+
generated += usage.output_tokens ?? 0;
|
|
23650
|
+
sink.usage({
|
|
23651
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
23652
|
+
outputTokens: generated
|
|
23653
|
+
});
|
|
23654
|
+
};
|
|
23618
23655
|
return (line, sink) => {
|
|
23619
23656
|
const message = parseJsonLine(line);
|
|
23620
23657
|
if (!message) return;
|
|
@@ -23638,10 +23675,14 @@ var claudeRunner = {
|
|
|
23638
23675
|
}
|
|
23639
23676
|
return;
|
|
23640
23677
|
}
|
|
23678
|
+
case "assistant":
|
|
23679
|
+
if (!message.parent_tool_use_id) report(message.message?.usage, sink);
|
|
23680
|
+
return;
|
|
23641
23681
|
case "result":
|
|
23642
23682
|
if (message.is_error) {
|
|
23643
23683
|
return sink.fail("AGENT_FAILED", message.result || message.subtype || "Claude Code reported an error");
|
|
23644
23684
|
}
|
|
23685
|
+
if (!generated) report(message.usage, sink);
|
|
23645
23686
|
return sink.done(message.stop_reason || "end_turn");
|
|
23646
23687
|
}
|
|
23647
23688
|
};
|
|
@@ -23952,6 +23993,17 @@ var codexRunner = {
|
|
|
23952
23993
|
return finish(msg.message, sink);
|
|
23953
23994
|
case "web_search_begin":
|
|
23954
23995
|
return sink.tool(randomUUID4(), WEB_TOOL);
|
|
23996
|
+
case "token_count": {
|
|
23997
|
+
const last = msg.info?.last_token_usage ?? msg.info?.total_token_usage;
|
|
23998
|
+
const total = msg.info?.total_token_usage ?? last;
|
|
23999
|
+
if (last) {
|
|
24000
|
+
sink.usage({
|
|
24001
|
+
contextTokens: (last.input_tokens ?? 0) + (last.output_tokens ?? 0),
|
|
24002
|
+
outputTokens: total?.output_tokens ?? 0
|
|
24003
|
+
});
|
|
24004
|
+
}
|
|
24005
|
+
return;
|
|
24006
|
+
}
|
|
23955
24007
|
case "task_complete":
|
|
23956
24008
|
return sink.done("end_turn");
|
|
23957
24009
|
case "error":
|
|
@@ -23975,8 +24027,16 @@ var codexRunner = {
|
|
|
23975
24027
|
if (kind === "web_search") return sink.tool(item?.id ?? randomUUID4(), WEB_TOOL);
|
|
23976
24028
|
return;
|
|
23977
24029
|
}
|
|
23978
|
-
case "turn.completed":
|
|
24030
|
+
case "turn.completed": {
|
|
24031
|
+
const usage = frame.usage;
|
|
24032
|
+
if (usage) {
|
|
24033
|
+
sink.usage({
|
|
24034
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
24035
|
+
outputTokens: usage.output_tokens ?? 0
|
|
24036
|
+
});
|
|
24037
|
+
}
|
|
23979
24038
|
return sink.done("end_turn");
|
|
24039
|
+
}
|
|
23980
24040
|
case "turn.failed":
|
|
23981
24041
|
return sink.fail("AGENT_FAILED", frame.error?.message || "Codex could not finish the turn");
|
|
23982
24042
|
case "error":
|
|
@@ -24087,6 +24147,7 @@ async function probe2(runner, settings) {
|
|
|
24087
24147
|
return {
|
|
24088
24148
|
kind: runner.kind,
|
|
24089
24149
|
bin: settings.bin,
|
|
24150
|
+
model: settings.model,
|
|
24090
24151
|
ready: false,
|
|
24091
24152
|
problem: {
|
|
24092
24153
|
code: "AGENT_MISSING",
|
|
@@ -24099,6 +24160,7 @@ async function probe2(runner, settings) {
|
|
|
24099
24160
|
return {
|
|
24100
24161
|
kind: runner.kind,
|
|
24101
24162
|
bin: settings.bin,
|
|
24163
|
+
model: settings.model,
|
|
24102
24164
|
ready: false,
|
|
24103
24165
|
problem: {
|
|
24104
24166
|
code: "AGENT_UNUSABLE",
|
|
@@ -24108,7 +24170,14 @@ async function probe2(runner, settings) {
|
|
|
24108
24170
|
};
|
|
24109
24171
|
}
|
|
24110
24172
|
const problem = await runner.check?.(settings) ?? null;
|
|
24111
|
-
return {
|
|
24173
|
+
return {
|
|
24174
|
+
kind: runner.kind,
|
|
24175
|
+
bin: settings.bin,
|
|
24176
|
+
model: settings.model,
|
|
24177
|
+
ready: !problem,
|
|
24178
|
+
version: found.detail,
|
|
24179
|
+
problem: problem ?? void 0
|
|
24180
|
+
};
|
|
24112
24181
|
}
|
|
24113
24182
|
function version2(bin, args) {
|
|
24114
24183
|
return new Promise((resolve4) => {
|
|
@@ -24614,6 +24683,7 @@ function runStream(runner, context, signal, emit) {
|
|
|
24614
24683
|
session: (id) => {
|
|
24615
24684
|
if (id) sessionId = id;
|
|
24616
24685
|
},
|
|
24686
|
+
usage: (usage) => emit({ kind: "usage", usage }),
|
|
24617
24687
|
done: (stopReason) => settle2(() => {
|
|
24618
24688
|
flush();
|
|
24619
24689
|
resolve4({ stopReason, sessionId });
|
|
@@ -25287,6 +25357,7 @@ function isPrivateAddress(address) {
|
|
|
25287
25357
|
}
|
|
25288
25358
|
|
|
25289
25359
|
// agent/service.ts
|
|
25360
|
+
var FOCUS_SHOT_TOOL_NAME = toolNameFor(FOCUS_SHOT_ACTION);
|
|
25290
25361
|
var AgentSession = class {
|
|
25291
25362
|
constructor(deps) {
|
|
25292
25363
|
this.deps = deps;
|
|
@@ -25333,6 +25404,16 @@ var AgentSession = class {
|
|
|
25333
25404
|
emit({ kind: "toolResult", toolId, ok: result2.ok, summary: summarize(input2, result2) });
|
|
25334
25405
|
return result2;
|
|
25335
25406
|
}
|
|
25407
|
+
if (action === FOCUS_SHOT_ACTION) {
|
|
25408
|
+
const result2 = run.focusShot ? success({ dataUrl: run.focusShot }) : failure("NO_FOCUS_SHOT", "No A-Eye pick came with this instruction, so nothing was photographed.");
|
|
25409
|
+
emit({
|
|
25410
|
+
kind: "toolResult",
|
|
25411
|
+
toolId,
|
|
25412
|
+
ok: result2.ok,
|
|
25413
|
+
summary: result2.ok ? "the picked element, as photographed" : "no pick attached"
|
|
25414
|
+
});
|
|
25415
|
+
return result2;
|
|
25416
|
+
}
|
|
25336
25417
|
if (run.map) {
|
|
25337
25418
|
const gate = gateMappingInvoke(run.map, action, input2);
|
|
25338
25419
|
if (!gate.allow) {
|
|
@@ -25442,7 +25523,8 @@ var AgentSession = class {
|
|
|
25442
25523
|
site: siteOf(context?.url),
|
|
25443
25524
|
abort: new AbortController(),
|
|
25444
25525
|
pending: /* @__PURE__ */ new Map(),
|
|
25445
|
-
ownedTabIds: []
|
|
25526
|
+
ownedTabIds: [],
|
|
25527
|
+
focusShot: context?.focus?.shot
|
|
25446
25528
|
};
|
|
25447
25529
|
this.runs.set(runId, run);
|
|
25448
25530
|
const runner = activeRunner(await agentState(config2));
|
|
@@ -25648,6 +25730,11 @@ function focusBlock(focus) {
|
|
|
25648
25730
|
];
|
|
25649
25731
|
if (focus.label) lines.push(`- Label: ${flatten2(focus.label)}`);
|
|
25650
25732
|
lines.push(`- On: ${flatten2(focus.title)} \u2014 ${flatten2(focus.url)}`);
|
|
25733
|
+
if (focus.shot) {
|
|
25734
|
+
lines.push(
|
|
25735
|
+
`- A screenshot of it, taken at the instant they picked it, is attached \u2014 call \`${FOCUS_SHOT_TOOL_NAME}\` once to see it.`
|
|
25736
|
+
);
|
|
25737
|
+
}
|
|
25651
25738
|
const content = focus.content.slice(0, MAX_FOCUS_CONTENT);
|
|
25652
25739
|
const cut = focus.truncated || content.length < focus.content.length;
|
|
25653
25740
|
lines.push(
|
|
@@ -26297,6 +26384,7 @@ function timeoutFor(action, input2) {
|
|
|
26297
26384
|
if (action === typeText.name) return typingDurationMs(input2) + DEFAULT_TIMEOUT_MS2;
|
|
26298
26385
|
if (action === startMonitor.name) return DEFAULT_TIMEOUT_MS2;
|
|
26299
26386
|
if (action === awaitMonitor.name) return (declaredTimeout(input2) ?? AWAIT_DEFAULT_TIMEOUT_MS) + 5e3;
|
|
26387
|
+
if (action === pickElement.name) return (declaredTimeout(input2) ?? PICK_DEFAULT_TIMEOUT_MS) + 5e3;
|
|
26300
26388
|
const declared = declaredTimeout(input2);
|
|
26301
26389
|
return declared != null ? declared + 5e3 : DEFAULT_TIMEOUT_MS2;
|
|
26302
26390
|
}
|
|
@@ -26525,7 +26613,7 @@ async function startDaemon({ version: version3, idleExit = true }) {
|
|
|
26525
26613
|
);
|
|
26526
26614
|
return;
|
|
26527
26615
|
}
|
|
26528
|
-
if (request.t === "agentState" || request.t === "setAgent" || request.t === "grantAgent") {
|
|
26616
|
+
if (request.t === "agentState" || request.t === "setAgent" || request.t === "setAgentModel" || request.t === "grantAgent") {
|
|
26529
26617
|
void settleAgent(request).then((state) => {
|
|
26530
26618
|
source.send({ t: "agentInfo", id: request.id, result: state });
|
|
26531
26619
|
if (request.t === "setAgent") pushSkillCatalog(source);
|
|
@@ -26584,6 +26672,11 @@ async function startDaemon({ version: version3, idleExit = true }) {
|
|
|
26584
26672
|
agent?.handle({ t: "reset" });
|
|
26585
26673
|
log(`agent set to ${AGENTS[request.agent].label}`);
|
|
26586
26674
|
}
|
|
26675
|
+
if (request.t === "setAgentModel") {
|
|
26676
|
+
const model = typeof request.model === "string" ? request.model : null;
|
|
26677
|
+
writeAgentModel(request.agent, model);
|
|
26678
|
+
log(`${AGENTS[request.agent].label} model set to ${model?.trim() || "the default"}`);
|
|
26679
|
+
}
|
|
26587
26680
|
if (request.t === "grantAgent") await grantRunner(request.agent);
|
|
26588
26681
|
const refresh = request.t !== "agentState" || request.refresh === true;
|
|
26589
26682
|
return success(await agentState(readAgentConfig(), { refresh }));
|
|
@@ -26874,7 +26967,7 @@ X-Browsentic-Reason: ${reason}\r
|
|
|
26874
26967
|
// package.json
|
|
26875
26968
|
var package_default = {
|
|
26876
26969
|
name: "browsentic",
|
|
26877
|
-
version: "0.4.
|
|
26970
|
+
version: "0.4.8",
|
|
26878
26971
|
description: "Hand your real, logged-in browser to the AI agent you already run. Installs the browser extension, runs the local daemon, and speaks MCP.",
|
|
26879
26972
|
type: "module",
|
|
26880
26973
|
license: "MIT",
|