From 589cd294931c01b3748779635d17a13dd240af19 Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Wed, 6 May 2026 00:06:49 +0530 Subject: release: v0.4.0 — accessibility snapshot, network capture, snapshot-first prompts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two new tools and pivots the agent's default workflow away from screenshots toward structured page data. browser_snapshot — CDP accessibility tree (roles, names, states, hierarchy) with click coordinates @(x,y) attached to every interactive element via DOM.getBoxModel (parallel, 1.5s aggregate budget). Pass directly to browser_click — no screenshot round-trip. Optional includeScreenshot:true for visual confirmation. format:"json" returns a slim structure with full box per node. browser_network_requests — replaces the deprecated browser_get_network_log placeholder. Filters by urlPattern (substring or /regex/), method, status, resource type, recency. Optional response-body capture under a 5s aggregate budget with 50KB per-body cap. Buffer is page-scoped (cleared on tab switch), 500 records/tab, with a bufferOverflowed flag on drain. CDP plumbing: in-process aggregator in src/cdp/network-buffer.ts wired into the existing single-consumer event loop. Accessibility domain enabled alongside Page/DOM/Runtime/Network on every attach. Ctrl+O expand/collapse on three tools' renderResult — browser_snapshot, browser_network_requests, browser_execute_js. Compact-by-default views with keyHint("app.tools.expand") that respects user keybinding remaps. Tool prompts pivoted snapshot-first: browser_screenshot is documented as LAST RESORT for visual verification only; browser_snapshot is the default for understanding pages; browser_execute_js is the default for surgical reads. browser_click and browser_open_urls guidance follows. SKILL.md collapsed from ~342 to ~53 lines — keeping only what isn't already in tool prompts: the decision tree, connection rules, browser_run_script daemon bindings. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/cdp/session.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/cdp/session.ts') diff --git a/src/cdp/session.ts b/src/cdp/session.ts index 42f6107..89bdfdd 100644 --- a/src/cdp/session.ts +++ b/src/cdp/session.ts @@ -3,6 +3,7 @@ import type { CdpError } from "./errors"; import type { DialogInfo } from "./types"; import type { OwnershipRegistry } from "./ownership"; import type { CdpTransport } from "./transport"; +import { createNetworkBuffer, type DrainResult, type NetworkFilter } from "./network-buffer"; export type CdpSession = { attachFirstPage(): Promise>; @@ -13,6 +14,7 @@ export type CdpSession = { callBrowser(method: string, params?: Record, opts?: { timeoutMs?: number }): Promise>; takeDialog(): DialogInfo | null; drainPageInfoInvalidations(): boolean; + drainNetworkBuffer(filter: NetworkFilter): DrainResult; }; export const createCdpSession = ( @@ -23,6 +25,7 @@ export const createCdpSession = ( let targetId: string | null = null; let dialog: DialogInfo | null = null; let pageInfoDirty = false; + const networkBuffer = createNetworkBuffer(); let activeConsumer: Promise = Promise.resolve(); @@ -48,6 +51,10 @@ export const createCdpSession = ( const params = ev.params as { targetId?: string } | undefined; if (params?.targetId) ownership.remove(params.targetId); } + if (ev.method === "Network.requestWillBeSent") networkBuffer.ingestRequestWillBeSent(ev.params); + else if (ev.method === "Network.responseReceived") networkBuffer.ingestResponseReceived(ev.params); + else if (ev.method === "Network.loadingFinished") networkBuffer.ingestLoadingFinished(ev.params); + else if (ev.method === "Network.loadingFailed") networkBuffer.ingestLoadingFailed(ev.params); } }; @@ -75,7 +82,7 @@ export const createCdpSession = ( // Switching to Promise.all over a single WS pipelines the round-trips and // saves ~3× on tab-switch latency. Defer until session.ts has tests. const enableDomains = async (sid: string): Promise => { - for (const d of ["Page", "DOM", "Runtime", "Network"]) { + for (const d of ["Page", "DOM", "Runtime", "Network", "Accessibility"]) { await transport.request(`${d}.enable`, {}, { sessionId: sid }); } }; @@ -143,6 +150,9 @@ export const createCdpSession = ( sessionId = a.sessionId; targetId = tid; pageInfoDirty = true; + // Buffer is page-scoped: on switch, the network history of the previous + // tab is no longer relevant to what the agent is now looking at. + networkBuffer.clear(); await enableDomains(a.sessionId); return ok(undefined); }, @@ -168,5 +178,8 @@ export const createCdpSession = ( pageInfoDirty = false; return dirty; }, + drainNetworkBuffer(filter) { + return networkBuffer.drain(filter); + }, }; }; -- cgit v1.2.3