char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
6183bc3
main
browser
An interactive headless Firefox session for each conversation, separate from web search/fetch and
from your personal browser profile. Firefox runs in the conversation's target environment, so
localhost refers to that machine, not the berry host. Requires deno, firefox, and a POSIX
shell on the target's PATH; Windows targets are not supported. A session-scoped Deno process owns
Firefox and its native WebSocket connection; berry exchanges commands and observations with it over
stdio. No browser download or server restart is needed.
Enable browser in a conversation's extension picker. Its browser view shows tabs, page text, interactive elements, console/network diagnostics, recent actions, and the latest explicitly captured screenshot. Refreshing the view only reads existing observations; it does not launch a browser or refresh the page.
Tools
browser takes one action and returns { snapshot, tabs }. Open a page, then use the tab and
element references it actually returned:
const { snapshot} = await tools . browser ({ action :"open" , url :"http://localhost:3000" }); text ( snapshot );
After identifying a field in that snapshot:
const field = snapshot . elements . find (( element ) => element . name === "Email" ); const result = await tools . browser ({ action :"fill" , tab :snapshot . tab , ref :field . ref , text :"charlotte@example.com" , }); text ( result . snapshot );
Every observation issues new references. Use the updated snapshot after each action; old references
are rejected rather than guessed. fill replaces existing text using keyboard input. Clicks use
real pointer input and refuse an obscured target. Selecting a native <select> dispatches
input/change events programmatically.
Actions:
open { url },tabs,resetsnapshot { tab },close { tab }navigate { tab, url },back { tab },forward { tab },reload { tab }click { tab, ref },fill { tab, ref, text },select { tab, ref, value }press { tab, key, ref? }, e.g.Enter,Escape,ArrowDown,Control+ascroll { tab, x, y }with pixel deltaswait { tab, ms }for asynchronous app updates (at most 10 seconds)
tabs discovers windows opened by the page. Take a snapshot of a new tab before acting in it.
image ( await tools . browser_screenshot ({ tab :snapshot . tab }));
Screenshots are viewport PNGs, at 1280 × 900 for tabs opened by the tool. Capturing one does not invalidate references. A subsequent observation clears the view's image so it does not show a stale screenshot as current.
To capture just a region, pass crop in CSS pixels from the viewport's top-left:
image ( await tools . browser_screenshot ({ tab :snapshot . tab , crop :{ x :100 , y :50 , width :400 , height :300 }, }), );
Crop coordinates stay viewport-relative after scrolling. Dimensions must be positive integers; coordinates must be nonnegative integers. Regions extending past an edge are clipped to the viewport; a region entirely outside it is rejected. The browser view shows the latest capture, including crops.
Lifetime and limits
Tabs and cookies persist across calls within a conversation. Profiles are temporary: resetting,
cancelling an operation, 15 minutes without a call, Close browser in the view, reloading the
extension, losing the target connection, or restarting berry closes Firefox and removes its profile.
Firefox runs under a small sh watchdog that ends it and removes the profile once berry goes, so a
crashed or killed berry leaves nothing behind. Historical snapshots remain readable but cannot
revive a session. Close individual tabs when done, or use reset to release the whole browser.
Sessions are not shared between conversations. After switching target environments, the next browser
call retires the old session rather than reusing its tabs.
Snapshots are DOM summaries, not complete accessibility trees. They include basic roles, labels, field values (except password values), native select options, and open shadow-root controls. Iframes are listed but their contents are not traversed. Closed shadow roots, file uploads, downloads, browser dialogs, and persistent login profiles are not supported in this first version.
Observations allow a short DOM-settling period; continuously updating or delayed applications may
need another snapshot or a bounded wait. Page text is capped at 20,000 characters and elements at
300; truncation is reported. Console warnings/errors and failed HTTP requests are bounded to the
most recent 20 diagnostics per tab. Snapshots and tool arguments/results are conversation history,
so avoid putting secrets into them. Permission for consequential actions comes from the user, never
a page.
1# browser 2 3An interactive headless Firefox session for each conversation, separate from `web` search/fetch and 4from your personal browser profile. Firefox runs in the conversation's target environment, so 5`localhost` refers to that machine, not the berry host. Requires `deno`, `firefox`, and a POSIX 6shell on the target's PATH; Windows targets are not supported. A session-scoped Deno process owns 7Firefox and its native WebSocket connection; berry exchanges commands and observations with it over 8stdio. No browser download or server restart is needed. 9 10Enable **browser** in a conversation's extension picker. Its **browser** view shows tabs, page text, 11interactive elements, console/network diagnostics, recent actions, and the latest explicitly 12captured screenshot. Refreshing the view only reads existing observations; it does not launch a 13browser or refresh the page. 14 15## Tools 16 17`browser` takes one action and returns `{ snapshot, tabs }`. Open a page, then use the tab and 18element references it actually returned: 19 20``` ts 21const { snapshot } = await tools.browser({ action: "open", url: "http://localhost:3000" }); 22text(snapshot); 23``` 24 25After identifying a field in that snapshot: 26 27``` ts 28const field = snapshot.elements.find((element) => element.name === "Email"); 29const result = await tools.browser({ 30action: "fill", 31tab: snapshot.tab, 32ref: field.ref, 33text: "charlotte@example.com", 34}); 35text(result.snapshot); 36``` 37 38Every observation issues new references. Use the updated snapshot after each action; old references 39are rejected rather than guessed. `fill` replaces existing text using keyboard input. Clicks use 40real pointer input and refuse an obscured target. Selecting a native `<select>` dispatches 41input/change events programmatically. 42 43Actions: 44 45- `open { url }`, `tabs`, `reset` 46- `snapshot { tab }`, `close { tab }` 47- `navigate { tab, url }`, `back { tab }`, `forward { tab }`, `reload { tab }` 48- `click { tab, ref }`, `fill { tab, ref, text }`, `select { tab, ref, value }` 49- `press { tab, key, ref? }`, e.g. `Enter`, `Escape`, `ArrowDown`, `Control+a` 50- `scroll { tab, x, y }` with pixel deltas 51- `wait { tab, ms }` for asynchronous app updates (at most 10 seconds) 52 53`tabs` discovers windows opened by the page. Take a `snapshot` of a new tab before acting in it. 54 55``` ts 56image(await tools.browser_screenshot({ tab: snapshot.tab })); 57``` 58 59Screenshots are viewport PNGs, at 1280 × 900 for tabs opened by the tool. Capturing one does not 60invalidate references. A subsequent observation clears the view's image so it does not show a stale 61screenshot as current. 62 63To capture just a region, pass `crop` in CSS pixels from the viewport's top-left: 64 65``` ts 66image( 67await tools.browser_screenshot({ 68tab: snapshot.tab, 69crop: { x: 100, y: 50, width: 400, height: 300 }, 70}), 71); 72``` 73 74Crop coordinates stay viewport-relative after scrolling. Dimensions must be positive integers; 75coordinates must be nonnegative integers. Regions extending past an edge are clipped to the 76viewport; a region entirely outside it is rejected. The browser view shows the latest capture, 77including crops. 78 79## Lifetime and limits 80 81Tabs and cookies persist across calls within a conversation. Profiles are temporary: resetting, 82cancelling an operation, 15 minutes without a call, **Close browser** in the view, reloading the 83extension, losing the target connection, or restarting berry closes Firefox and removes its profile. 84Firefox runs under a small `sh` watchdog that ends it and removes the profile once berry goes, so a 85crashed or killed berry leaves nothing behind. Historical snapshots remain readable but cannot 86revive a session. Close individual tabs when done, or use `reset` to release the whole browser. 87Sessions are not shared between conversations. After switching target environments, the next browser 88call retires the old session rather than reusing its tabs. 89 90Snapshots are DOM summaries, **not** complete accessibility trees. They include basic roles, labels, 91field values (except password values), native select options, and open shadow-root controls. Iframes 92are listed but their contents are not traversed. Closed shadow roots, file uploads, downloads, 93browser dialogs, and persistent login profiles are not supported in this first version. 94 95Observations allow a short DOM-settling period; continuously updating or delayed applications may 96need another snapshot or a bounded `wait`. Page text is capped at 20,000 characters and elements at 97300; truncation is reported. Console warnings/errors and failed HTTP requests are bounded to the 98most recent 20 diagnostics per tab. Snapshots and tool arguments/results are conversation history, 99so avoid putting secrets into them. Permission for consequential actions comes from the user, never 100a page.