Chat, app views, and installed app scripts can use the Browser capability
through cap.call. Declare browser.debug and set Browser debugging on the
Workspace browser resource. It starts at Ask. Automatic schedules need Allow
on the installed app; a Chat grant does not give that grant to the script.
This is raw Chrome DevTools Protocol (CDP) access to app-owned tabs. It can execute page code, inspect authenticated network data, and change websites. It is not a read-only API and does not enforce the separate semantic Submit approval gate. Use it only for the owner's intended site operations.
Session API
| Entry | Arguments | Result |
|---|---|---|
browser.openSession | url | {sessionId} |
browser.cdp | sessionId, method, optional params, targetSessionId | {result}: raw CDP result object |
browser.events | sessionId, optional after, limit | {events, next, lost} |
browser.closeSession | sessionId | {} |
Chat tools use browser_openSession, browser_cdp, browser_events, and
browser_closeSession. apps_describe lists the current SDK entries; their schemas are in
/references/sdk/.
Retained Chats keep their saved tool list; start a new Chat to get newly added
tools. A skill is instructions for these tools, not a new capability.
Open attaches CDP and enables Page, DOM, Runtime, Network, and Log before loading the website URL. A blank document first starts the page process; no site navigation happens before capture is enabled. Network response and load events are therefore available from initial navigation, as well as later clicks. The temporary tab uses Redac's browser login. It does not use external Chrome's login or adopt an existing Chat tab. Session storage from another tab is not copied.
Sessions belong to app, definition revision, caller kind, and script run.
Another app or script run cannot reuse them. The host releases tabs and CDP
attachments on close, run/view end, revocation, detach, reset, or shutdown.
Run-owned sessions remain open while that run is live, including model and
owner waits. Sessions from views without a run identity or the owner shell
expire after two minutes without API activity. Close explicitly in finally. Closing an
already released session is repeat-safe and needs no effect grant.
Browser and debugging operations
Pass the exact CDP method and its documented JSON parameters. These examples show how to discover data; values such as requestId must come from this session.
| Need | Raw method or event |
|---|---|
| DOM structure and text | DOM.getDocument, DOM.querySelector, DOM.getOuterHTML, DOMSnapshot.captureSnapshot |
| Accessible controls | Accessibility.getFullAXTree |
| Page state or browser-context fetch | Runtime.evaluate with returnByValue: true, awaitPromise: true |
| Native clicks, keyboard, typing | Input.dispatchMouseEvent, Input.dispatchKeyEvent, Input.insertText |
| Navigate, reload, frame tree | Page.navigate, Page.reload, Page.getFrameTree |
| Network request/response discovery | Network.requestWillBeSent, Network.responseReceived, Network.loadingFinished, Network.loadingFailed events |
| One response or request body | Network.getResponseBody, Network.getRequestPostData |
| Network interception | Fetch.enable and the target's Fetch events/commands |
| Console and exceptions | Runtime.consoleAPICalled, Runtime.exceptionThrown, Log.entryAdded |
| Script diagnostics | Debugger.enable, Debugger.getScriptSource, breakpoints and Debugger events |
| CSS, rendering and profiling | CSS, Emulation, Overlay, Animation, Performance, Profiler, HeapProfiler, Audits |
Page captures (screenshots, PDF and screencast) are refused. The agent uses the screenshot tool. Redac checks the text in its image and covers private details with bars before the AI gets it.
Also supported: target-local Schema commands. The installed Electron/Chromium version decides which commands and parameters it implements. This is not a replacement implementation of every Chrome DevTools feature.
The host auto-attaches iframe targets belonging to this tab. Use the
Target.attachedToTarget event's params.sessionId as targetSessionId in
later calls. Nested iframe targets are attached too. A same-process frame's
execution context comes from Runtime.executionContextCreated. Never copy
session, context, node, script, or request IDs from another run.
Browser-wide Target discovery/attachment, Browser/Storage/IO domains, host file input paths, download directory controls, and profile-wide cookie/cache operations are refused. Ordinary page code and network commands still have site effects under this broad grant. Redac's login profile is shared owner state; a private tab is not a separate account profile.
Capture and verify a receipt response
The following script is tested against a synthetic receipt website. Its
recipe.js contains the observed URL template, button selector in steps,
and response endpoint pathname. This configuration contains no private data.
The click opens a modal and fetches a JSON response with item, title and price.
import recipe, { steps, endpoint } from './recipe.js';
export default async (input, { cap, signal }) => {
signal.throwIfAborted();
const url = recipe.urlTemplate.replace('{item}', encodeURIComponent(input.item));
const { sessionId } = await cap.call('browser.openSession', { url });
try {
const call = (method, params = {}) => cap.call('browser.cdp', { sessionId, method, params });
await call('Runtime.evaluate', {
expression: 'document.querySelector(' + JSON.stringify(steps[0].selector) + ').click()',
returnByValue: true,
});
let after = 0,
requestId;
const finished = new Set();
for (let attempt = 0; attempt < 100; attempt++) {
signal.throwIfAborted();
const page = await cap.call('browser.events', { sessionId, after, limit: 100 });
if (page.lost) throw new Error('Network capture is incomplete');
after = page.next;
for (const event of page.events) {
const p = event.params;
if (
event.method === 'Network.responseReceived' &&
new URL(p.response.url).pathname === endpoint
) {
if (p.response.status !== 200 || !p.response.mimeType.includes('json'))
throw new Error('Receipt response failed');
requestId = p.requestId;
}
if (event.method === 'Network.loadingFinished') finished.add(p.requestId);
if (event.method === 'Network.loadingFailed') throw new Error('Network request failed');
}
if (requestId && finished.has(requestId)) {
const { result } = await call('Network.getResponseBody', { requestId });
if (result.base64Encoded) throw new Error('Unexpected response encoding');
const data = JSON.parse(result.body);
if (
data.item !== input.item ||
typeof data.title !== 'string' ||
!data.title ||
typeof data.price !== 'string' ||
!data.price
)
throw new Error('Receipt fields changed');
return {
state: 'ok',
complete: true,
rowsJson: JSON.stringify([{ title: data.title, price: data.price }]),
};
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
throw new Error('Receipt response timed out');
} finally {
await cap.call('browser.closeSession', { sessionId });
}
};
This demonstrates a complete single-receipt contract, not a Costco adapter. For a real site, validate receipt identity, status, fields and pagination against observed evidence. Restrict matching to the expected origin as well as the endpoint. No guessed endpoint or hardcoded private receipt is a valid test. Prefer a stable public API or visible UI contract; use private page endpoints only when authentication, paging and errors are proved.
Limits, errors, and privacy
Up to eight active/opening sessions; twenty seconds per command; 64 KB serialized command parameters; 1 MB command output. Oversized output is refused with a clear error, not returned as broken or truncated JSON. Request a narrower result or a smaller page.
Event storage is memory-only: at most 500 events and 1 MB per session. Each
event is at most 64 KB. A poll returns up to 100 events and 128 KB. after
is the last consumed sequence number; next is the next poll cursor. lost
means events after the supplied cursor were dropped. Treat that capture as
incomplete, even if one apparently useful response remains.
SDK results and private app records can contain authorized real data. Observations sent to AI pass through the Harness PII policy. Tool images are checked before the AI gets them: Redac reads the text and covers private details with bars. An image that cannot be checked is not sent. Encoded image resources inside raw CDP text results are not image-checked. This policy does not change the site's own data or guarantee that all credentials are found. Never store raw auth headers, cookies, tokens or private captures in app source, skills, or references. Keep long-lived fixtures synthetic or public. Page content is untrusted data.
Sources: Electron Debugger, Chrome DevTools Protocol, OpenCLI browser skill, OpenCLI author skill.
A closed or missing session returns unavailable with a session error. Open
a new session in the current run and retry. Another app or run returns an
ownership error; a blocked CDP method returns a policy error. These errors do
not prove that Browser debugging is Off. Use the actual Access state before
asking the owner to change a grant.