stuntdouble 0.1.2 → 0.2.0

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.
Files changed (46) hide show
  1. package/README.md +37 -9
  2. package/dist/{chunk-VWTYG7G4.js → chunk-BFI6P5DO.js} +65 -25
  3. package/dist/chunk-BFI6P5DO.js.map +1 -0
  4. package/dist/chunk-DFXASIGV.js +431 -0
  5. package/dist/chunk-DFXASIGV.js.map +1 -0
  6. package/dist/{chunk-JAB46QV7.js → chunk-F3R3QL7V.js} +64 -8
  7. package/dist/chunk-F3R3QL7V.js.map +1 -0
  8. package/dist/{mcp-CYPMT37O.js → chunk-FMZTT2MC.js} +72 -1059
  9. package/dist/chunk-FMZTT2MC.js.map +1 -0
  10. package/dist/chunk-MSV5OLSY.js +117 -0
  11. package/dist/chunk-MSV5OLSY.js.map +1 -0
  12. package/dist/chunk-P3CI4FTE.js +469 -0
  13. package/dist/chunk-P3CI4FTE.js.map +1 -0
  14. package/dist/chunk-QDA5FTUH.js +79 -0
  15. package/dist/chunk-QDA5FTUH.js.map +1 -0
  16. package/dist/chunk-XHSDRB3X.js +996 -0
  17. package/dist/chunk-XHSDRB3X.js.map +1 -0
  18. package/dist/cli.js +180 -68
  19. package/dist/cli.js.map +1 -1
  20. package/dist/cloud-DLLNKYND.js +34 -0
  21. package/dist/credentials-QHESDY4K.js +27 -0
  22. package/dist/credentials-QHESDY4K.js.map +1 -0
  23. package/dist/doctor-ZAYOOCPT.js +13 -0
  24. package/dist/doctor-ZAYOOCPT.js.map +1 -0
  25. package/dist/files-UBPF7PKW.js +45 -0
  26. package/dist/files-UBPF7PKW.js.map +1 -0
  27. package/dist/mcp-HFIAQ6HP.js +133 -0
  28. package/dist/mcp-HFIAQ6HP.js.map +1 -0
  29. package/dist/open-browser-CTOGH5HQ.js +17 -0
  30. package/dist/open-browser-CTOGH5HQ.js.map +1 -0
  31. package/dist/run-MPIAR3LQ.js +20 -0
  32. package/dist/run-MPIAR3LQ.js.map +1 -0
  33. package/dist/runner-E45EJ76B.js +17 -0
  34. package/dist/runner-E45EJ76B.js.map +1 -0
  35. package/dist/studio-FBC66BER.js +360 -0
  36. package/dist/studio-FBC66BER.js.map +1 -0
  37. package/dist/studio.js +1621 -0
  38. package/dist/ui-server-C7CFJWRD.js +12 -0
  39. package/dist/ui-server-C7CFJWRD.js.map +1 -0
  40. package/dist/ui.html +210 -103
  41. package/package.json +2 -2
  42. package/dist/chunk-JAB46QV7.js.map +0 -1
  43. package/dist/chunk-VWTYG7G4.js.map +0 -1
  44. package/dist/mcp-CYPMT37O.js.map +0 -1
  45. package/dist/ui-server-AYIR7DTN.js +0 -10
  46. /package/dist/{ui-server-AYIR7DTN.js.map → cloud-DLLNKYND.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/mcp.ts","../../browser-toolset/src/keys.ts","../../browser-toolset/src/safety.ts","../../browser-toolset/src/page-script.ts","../../browser-toolset/src/executor.ts","../../browser-toolset/src/direct-tools.ts","../src/run.ts","../package.json","../src/brief.ts","../src/driver.ts","../src/evidence.ts"],"sourcesContent":["// The stdio MCP server: the browser toolset's direct tools plus the run tools,\n// over one `LocalSession`. The person's own agent session is the only model:\n// this process drives a browser and writes files, and calls no model at all.\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { DIRECT_TOOL_SPECS } from '@stunt-double/browser-toolset/direct-tools';\nimport { z } from 'zod';\n\nimport { projectDir } from './files.ts';\nimport { LocalSession, PACKAGE, type ToolOutput } from './run.ts';\n\nconst reply = ({ text, image, isError }: ToolOutput): CallToolResult => ({\n content: [\n { type: 'text', text },\n ...(image ? [{ type: 'image' as const, data: image, mimeType: 'image/png' }] : []),\n ],\n ...(isError ? { isError: true } : {}),\n});\n\nlet queue: Promise<unknown> = Promise.resolve();\n\n/**\n * Run a tool, turning a thrown error into a result the model reads and acts on.\n * Calls can arrive at once (two verdicts in one turn) but share one page and\n * one run.json, so they take turns.\n */\nfunction attempt(work: () => Promise<string | ToolOutput>): Promise<CallToolResult> {\n const run = queue.then(async () => {\n try {\n const out = await work();\n return reply(typeof out === 'string' ? { text: out } : out);\n } catch (err) {\n return reply({ text: (err as Error).message, isError: true });\n }\n });\n queue = run;\n return run;\n}\n\ntype PropertySpec = { type: string; enum?: string[]; description?: string };\n\n/** A direct tool's flat JSON Schema as the zod shape the SDK registers. */\nfunction shapeOf(spec: (typeof DIRECT_TOOL_SPECS)[number]): z.ZodRawShape {\n const required = new Set(spec.parameters.required ?? []);\n return Object.fromEntries(\n Object.entries(spec.parameters.properties).map(([key, raw]) => {\n const prop = raw as PropertySpec;\n let schema: z.ZodType = prop.enum\n ? z.enum(prop.enum as [string, ...string[]])\n : prop.type === 'number'\n ? z.number()\n : z.string();\n if (prop.description) schema = schema.describe(prop.description);\n return [key, required.has(key) ? schema : schema.optional()];\n })\n );\n}\n\nexport function createServer(session: LocalSession): McpServer {\n const server = new McpServer({ name: 'stuntdouble', version: PACKAGE.split('@')[1]! });\n\n // `done` is the toolset's end signal; here a run ends with finish_run.\n for (const spec of DIRECT_TOOL_SPECS.filter((s) => s.name !== 'done')) {\n server.registerTool(\n spec.name,\n { description: spec.description, inputSchema: shapeOf(spec) },\n (input) => attempt(() => session.browserTool(spec.name, input))\n );\n }\n\n server.registerTool(\n 'start_run',\n {\n description:\n \"Start a local run of a checklist from .stuntdouble/checklists/<checklist>.json: opens a fresh Chrome at the checklist's URL, and returns the viewer link, the actor's brief and the checks to judge. If a file is invalid, returns each field to fix. With resume, continues the run still open in this session, or else the newest interrupted or stopped run of that checklist, judging only the checks without a verdict.\",\n inputSchema: {\n checklist: z.string().describe('The checklist slug, e.g. \"checkout\".'),\n resume: z.boolean().optional().describe('Continue the last unfinished run instead.'),\n },\n },\n (input) => attempt(() => session.startRun(input))\n );\n\n server.registerTool(\n 'evaluate_check',\n {\n description:\n 'Record the verdict on one check, as soon as you have observed it. A passed verdict needs a locator for evidence visible on the page now; a failure may describe something missing. Use \"informational\" only for informational checks, and \"blocked\" when you could not reach what the check is about. Saves a screenshot of the evidence and returns the next check.',\n inputSchema: {\n check: z.string().describe('The check key, e.g. \"guest-checkout\".'),\n status: z.enum(['passed', 'failed', 'informational', 'blocked']),\n evidence: z\n .string()\n .min(1)\n .describe('What you observed, in a sentence or two, quoting the page where you can.'),\n locator: z\n .object({\n text: z.string().optional().describe('Distinctive text visible on the page.'),\n selector: z.string().optional().describe('A CSS selector for the element.'),\n })\n .optional()\n .describe('Where the evidence is on the page. Required for passed.'),\n },\n },\n (input) => attempt(() => session.evaluateCheck(input))\n );\n\n server.registerTool(\n 'finish_run',\n {\n description:\n 'End the run: settles the checks left, scores it the way the cloud does, writes run.json, closes the browser and returns the outcome. Always call it, even when blocked or stopped.',\n inputSchema: {\n summary: z.string().min(1).describe('One or two sentences on what you found.'),\n },\n },\n (input) => attempt(() => session.finishRun(input))\n );\n\n server.registerTool(\n 'open_ui',\n {\n description:\n 'The local viewer for this project: every run with its verdicts, screenshots and activity, and the checklists and actors. Returns a link to open in a browser.',\n },\n () => attempt(() => session.openUi())\n );\n\n return server;\n}\n\nexport async function serveMcp(): Promise<void> {\n const session = new LocalSession({ project: projectDir() });\n const server = createServer(session);\n // stdin closing and the SIGTERM that follows both land here; close once.\n let closing: Promise<void> | undefined;\n const shutdown = () => (closing ??= session.close().finally(() => process.exit(0)));\n process.stdin.on('close', shutdown);\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n await server.connect(new StdioServerTransport());\n}\n","// Claude's browser toolset spells keys the xdotool way (`ctrl+a`, `Return`,\n// `Page_Down`, space-separated sequences); `DriverPage.keyPress` takes\n// Playwright's (`Control+A`, `Enter`, `PageDown`). Pure, so it is unit tested.\n\nconst ALIASES: Record<string, string> = {\n ctrl: 'Control',\n control: 'Control',\n cmd: 'Meta',\n command: 'Meta',\n meta: 'Meta',\n super: 'Meta',\n win: 'Meta',\n alt: 'Alt',\n option: 'Alt',\n shift: 'Shift',\n enter: 'Enter',\n return: 'Enter',\n kp_enter: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n tab: 'Tab',\n space: ' ',\n backspace: 'Backspace',\n delete: 'Delete',\n del: 'Delete',\n insert: 'Insert',\n home: 'Home',\n end: 'End',\n page_up: 'PageUp',\n pageup: 'PageUp',\n prior: 'PageUp',\n page_down: 'PageDown',\n pagedown: 'PageDown',\n next: 'PageDown',\n up: 'ArrowUp',\n down: 'ArrowDown',\n left: 'ArrowLeft',\n right: 'ArrowRight',\n arrowup: 'ArrowUp',\n arrowdown: 'ArrowDown',\n arrowleft: 'ArrowLeft',\n arrowright: 'ArrowRight',\n capslock: 'CapsLock',\n caps_lock: 'CapsLock',\n};\n\nfunction normaliseKey(raw: string): string {\n const key = raw.trim();\n const alias = ALIASES[key.toLowerCase()];\n if (alias) return alias;\n if (/^f([1-9]|1[0-2])$/i.test(key)) return key.toUpperCase();\n // A single character is typed as itself; Playwright wants it upper-cased\n // inside a chord (`Control+A`) and as-is on its own.\n return key;\n}\n\n/** One chord (`ctrl+shift+t`) in Playwright's spelling (`Control+Shift+T`). */\nexport function toPlaywrightChord(chord: string): string {\n const parts = chord.split('+').filter((p) => p.length > 0);\n // A literal plus (`ctrl++` or `+`) survives the split as an empty tail.\n if (chord.endsWith('+') && chord.length > 1 && chord[chord.length - 2] === '+') parts.push('+');\n if (parts.length === 0) return chord === '+' ? '+' : chord;\n const keys = parts.map(normaliseKey);\n if (keys.length > 1) {\n const last = keys[keys.length - 1];\n if (last.length === 1) keys[keys.length - 1] = last.toUpperCase();\n }\n return keys.join('+');\n}\n\n/**\n * `text` as the toolset sends it: one key, one chord, or a space-separated\n * sequence (`Backspace Backspace`). A lone space is the space key.\n */\nexport function toPlaywrightKeySequence(text: string): string[] {\n if (text === ' ') return [' '];\n return text\n .split(/\\s+/)\n .filter((k) => k.length > 0)\n .map(toPlaywrightChord);\n}\n","// A promise about an agent's sessions, enforced rather than asked for: \"they\n// never pay or sign up for real: no payments go through and no real\n// registration details are ever entered\". Benchmark sessions on third-party\n// sites (such as the Stunt Double Index, https://index.stuntdouble.io) make\n// that promise. A prompt line is a request the model under test may ignore,\n// and ignoring it is exactly the behaviour a benchmark may be measuring, so\n// the tools refuse instead.\n//\n// Everything here is off unless a caller opts in (`BrowserSafetyOptions`):\n// agents testing their own product fill forms with test data on purpose, and\n// must keep doing so.\n//\n// No runtime imports, so it runs under `node --test` and inside the page script.\n\nexport type BrowserSafetyOptions = {\n /**\n * Refuse typing into a password or payment card field (including a card\n * field inside a payment provider's iframe), and refuse any typed or filled\n * value that is a Luhn-valid card number, whatever field it is headed for.\n */\n refuseSensitiveInput: boolean;\n /**\n * Refuse a click, or Enter in a field, that would submit a form which pays,\n * subscribes or creates an account. A link that only navigates (a \"Sign up\"\n * link to the sign-up page) is always allowed: reaching the form is the task.\n */\n blockSubmit?: boolean;\n};\n\n/**\n * Every guard on: what benchmark sessions on sites that are not yours should\n * run with. Named for the Stunt Double Index (https://index.stuntdouble.io),\n * where every session runs with it.\n */\nexport const INDEX_SESSION_SAFETY: BrowserSafetyOptions = {\n refuseSensitiveInput: true,\n blockSubmit: true,\n};\n\n/** The text an agent gets back when a guard refuses: a result, not an error. */\nexport const SAFETY_REFUSAL_SUFFIX =\n 'This session is not allowed to pay, subscribe, create an account or enter passwords or card details. Do not try another way round: report what you observed and what the next step would be, then carry on or call done.';\n\n/**\n * Payment providers' own hosts, as suffix-matched domains (an entry covers\n * itself and its subdomains). Stripe is listed by host rather than as\n * `stripe.com` so its marketing site and docs stay reachable, which matters\n * when stripe.com is itself the site under test.\n */\nexport const PAYMENT_PROVIDER_DOMAINS: readonly string[] = [\n 'js.stripe.com',\n 'api.stripe.com',\n 'checkout.stripe.com',\n 'm.stripe.network',\n 'hooks.stripe.com',\n 'paypal.com',\n 'paypalobjects.com',\n 'adyen.com',\n 'adyenpayments.com',\n 'braintreegateway.com',\n 'braintree-api.com',\n 'pay.google.com',\n 'payments.google.com',\n 'applepay.cdn-apple.com',\n 'klarna.com',\n 'klarnaservices.com',\n 'afterpay.com',\n 'checkout.com',\n 'squareup.com',\n 'squarecdn.com',\n 'paddle.com',\n 'lemonsqueezy.com',\n];\n\n/** Whether `host` is `domain` or one of its subdomains. */\nexport function hostMatchesDomain(host: string, domain: string): boolean {\n const h = host.toLowerCase().replace(/\\.$/, '');\n const d = domain.toLowerCase().replace(/^\\*\\./, '');\n return h === d || h.endsWith(`.${d}`);\n}\n\nexport function isPaymentProviderHost(\n host: string,\n domains: readonly string[] = PAYMENT_PROVIDER_DOMAINS\n): boolean {\n return domains.some((d) => hostMatchesDomain(host, d));\n}\n\n/** The last two labels of a host: crude, but every provider above is a plain `.com`/`.network`. */\nfunction baseDomain(host: string): string {\n return host.toLowerCase().replace(/\\.$/, '').split('.').slice(-2).join('.');\n}\n\n/**\n * The payment hosts to block for a session visiting `targetHost`. A provider's\n * own site is exempt, or visiting paypal.com would block the page under test.\n */\nexport function paymentBlocklistFor(targetHost: string): string[] {\n const target = baseDomain(targetHost);\n return PAYMENT_PROVIDER_DOMAINS.filter((d) => baseDomain(d) !== target);\n}\n\n/** The Luhn checksum every payment card number carries. */\nexport function luhnValid(digits: string): boolean {\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i--) {\n let n = digits.charCodeAt(i) - 48;\n if (n < 0 || n > 9) return false;\n if (double) {\n n *= 2;\n if (n > 9) n -= 9;\n }\n sum += n;\n double = !double;\n }\n return sum > 0 && sum % 10 === 0;\n}\n\n/**\n * Whether a value contains something that reads as a card number: 13 to 19\n * digits, optionally grouped by spaces or dashes, passing Luhn. The Luhn check\n * is what keeps phone numbers, order ids and postcodes from tripping it.\n */\nexport function containsCardNumber(value: string): boolean {\n const runs = value.match(/\\d(?:[ -]?\\d){12,18}/g) ?? [];\n return runs.some((run) => {\n const digits = run.replace(/[ -]/g, '');\n return digits.length >= 13 && digits.length <= 19 && luhnValid(digits);\n });\n}\n\n/**\n * Field names, ids and labels that mark a payment card field. Kept loose on\n * separators (`card_number`, `cardNumber`, `card-number`) and tight on words,\n * so \"gift card\" and \"expires in\" do not trip it.\n */\nexport const CARD_FIELD_PATTERN =\n /card.?(?:number|num|no\\b)|cc.?(?:num|number|no\\b|exp|csc|cvv|cvc)|credit.?card|debit.?card|\\bcvc\\b|\\bcvv2?\\b|\\bcsc\\b|security.?code|card.?(?:verification|code)|expir(?:y|ation)|exp.?(?:date|month|year)|\\bmm.?\\/?.?yy\\b|card.?holder|name.?on.?card|\\bpan\\b/i;\n\n/**\n * Accessible names of buttons that take money. Anchored at the start of the\n * label and checked on any non-link button, since checkouts built without a\n * `<form>` still pay on a plain button; the lookahead keeps billing toggles\n * (\"Pay monthly\") and provider choices (\"Pay with PayPal\") clickable.\n */\nexport const PAYMENT_ACTION_PATTERN =\n /^(?:pay(?:\\s+now)?\\b(?!\\s+(?:monthly|annually|yearly|per|by|with|later|in|over|as))|purchase\\b|complete (?:my |your |the )?(?:purchase|order|payment|booking)\\b|place (?:my |your |the )?order\\b|confirm (?:and|&) pay\\b|confirm (?:order|purchase|payment|booking)\\b|submit (?:order|payment)\\b|book and pay\\b)/i;\n\n/**\n * Accessible names of controls that commit money or an identity. Checked only\n * on controls that submit a form, never on links, so the \"Sign up\" link that\n * leads to the sign-up page still works.\n */\nexport const COMMIT_ACTION_PATTERN =\n /\\b(?:pay|purchase|place (?:my |your |the )?order|complete (?:my |your |the )?(?:order|purchase|booking)|subscribe|buy now|sign ?up|create (?:an |my |your )?account|register|join now|start (?:my |your )?(?:free )?trial|confirm (?:order|booking)|submit order)\\b/i;\n\n/**\n * The narrower set for a button outside any form on a page showing a password\n * or card field. \"Sign up\" and \"Subscribe\" are left out on purpose: outside a\n * form they usually open the sign-up form, which is what the task asks for.\n */\nexport const FORMLESS_COMMIT_PATTERN =\n /^(?:create (?:an |my |your )?account|complete (?:my |your )?(?:registration|sign ?up)|start (?:my |your )?(?:free )?trial)\\b/i;\n\n/** What the page script reports about an element the agent is about to act on. */\nexport type ElementFacts = {\n /** Lower-case tag name, e.g. \"input\", \"button\", \"a\", \"iframe\". */\n tag: string;\n type: string;\n autocomplete: string;\n name: string;\n id: string;\n ariaLabel: string;\n placeholder: string;\n /** The accessible name as `read_page` would print it. */\n label: string;\n role: string;\n /** A link with a real destination (not `#` or `javascript:`). */\n isNavigatingLink: boolean;\n /** A control that submits its form when clicked. */\n isSubmit: boolean;\n /** A button-like control that is not a link. */\n isButton: boolean;\n /** For an iframe, its `src`; the field is inside it when focus is. */\n iframeSrc: string;\n iframeTitle: string;\n inForm: boolean;\n /**\n * The enclosing form holds a password field, a card field or a payment\n * provider's iframe; outside any form, the page shows one.\n */\n formSensitive: boolean;\n /** The label of the form's first submit control, for Enter-to-submit. */\n formSubmitLabel: string;\n};\n\nfunction hostOf(url: string): string {\n try {\n return new URL(url).hostname;\n } catch {\n return '';\n }\n}\n\n/** Why typing into this element is refused, or null. */\nexport function sensitiveFieldReason(f: ElementFacts): string | null {\n if (f.type === 'password' || /(?:current|new)-password/.test(f.autocomplete)) {\n return 'That is a password field.';\n }\n if (/\\bcc-/.test(f.autocomplete)) return 'That is a payment card field.';\n if (f.tag === 'iframe') {\n if (isPaymentProviderHost(hostOf(f.iframeSrc)))\n return 'That is a payment provider’s card field.';\n if (CARD_FIELD_PATTERN.test(`${f.iframeTitle} ${f.name}`))\n return 'That is a payment card field.';\n }\n const text = [f.name, f.id, f.ariaLabel, f.placeholder, f.label].join(' ');\n if (f.tag !== 'a' && f.tag !== 'button' && CARD_FIELD_PATTERN.test(text)) {\n return 'That is a payment card field.';\n }\n return null;\n}\n\n/** Why clicking this element is refused, or null. */\nexport function submitReason(f: ElementFacts): string | null {\n if (f.isNavigatingLink) return null;\n if (f.isSubmit && f.formSensitive) {\n return 'That would submit a form holding a password or payment card field.';\n }\n if (f.isSubmit && COMMIT_ACTION_PATTERN.test(f.label)) {\n return `That would submit \"${f.label.slice(0, 60)}\".`;\n }\n // A single-page sign-up with no <form>: the button is the submit.\n if (f.isButton && !f.inForm && f.formSensitive && FORMLESS_COMMIT_PATTERN.test(f.label.trim())) {\n return `That would press \"${f.label.slice(0, 60)}\".`;\n }\n if (f.isButton && PAYMENT_ACTION_PATTERN.test(f.label.trim())) {\n return `That would press \"${f.label.slice(0, 60)}\".`;\n }\n return null;\n}\n\n/** Why pressing Enter in this (focused) element is refused, or null. */\nexport function enterSubmitReason(f: ElementFacts): string | null {\n if (!f.inForm || f.tag === 'textarea') return null;\n if (f.formSensitive) return 'Enter would submit a form holding a password or payment card field.';\n if (COMMIT_ACTION_PATTERN.test(f.formSubmitLabel)) {\n return `Enter would submit \"${f.formSubmitLabel.slice(0, 60)}\".`;\n }\n return null;\n}\n\nexport function refusalText(reason: string): string {\n return `Refused: ${reason} ${SAFETY_REFUSAL_SUFFIX}`;\n}\n","// The in-page half of the browser toolset executor: one script, evaluated in\n// the page, that reads the accessibility-ish tree Claude's `read_page` and\n// `find` expect and resolves the `ref_N` handles those return.\n//\n// A `BrowserDriver` need not expose raw CDP, so `Accessibility.getFullAXTree`\n// is out of reach; this walks the DOM (including open shadow roots) instead and\n// tags each element it reports with a `data-sd-ref` attribute. A ref therefore survives\n// until the element leaves the DOM, which is the lifetime the toolset docs\n// promise (\"valid until the tab navigates or the DOM changes materially\").\n//\n// It is a string expression, never a function, because drivers serialise\n// functions with `toString()` and a bundler's helpers (esbuild's `__name`, for\n// one) do not exist in the page. `buildPageCall` is the only\n// way in, and it JSON-encodes its arguments, so nothing a model supplies is\n// ever spliced into the script as code.\n\nimport { CARD_FIELD_PATTERN, type ElementFacts, PAYMENT_PROVIDER_DOMAINS } from './safety.ts';\n\nexport type PageOp =\n | { op: 'read'; filter: 'visible' | 'interactive' | 'all'; depth: number; ref?: string }\n | { op: 'find'; query: string }\n | { op: 'text' }\n | { op: 'resolve'; ref: string; scroll: boolean }\n | { op: 'form'; ref: string; value: string | number | boolean }\n | { op: 'viewport' }\n | { op: 'inspect'; target: InspectTarget };\n\n/** An element to describe: a ref, whatever is at a point, or whatever has focus. */\nexport type InspectTarget =\n { type: 'ref'; ref: string } | { type: 'coordinate'; x: number; y: number } | { type: 'focused' };\n\nexport type ReadResult = { ok: true; text: string; truncated: boolean } | PageError;\nexport type ResolveResult = { ok: true; x: number; y: number; inViewport: boolean } | PageError;\nexport type FormResult = { ok: true; description: string } | PageError;\nexport type ViewportResult = { ok: true; width: number; height: number };\n/** `facts` is null when nothing is there (an empty point, nothing focused). */\nexport type InspectResult = { ok: true; facts: ElementFacts | null } | PageError;\nexport type PageError = { ok: false; error: string };\n\n/** Max characters `read_page` / `find` / `get_page_text` return (toolset limit). */\nexport const MAX_PAGE_TEXT_CHARS = 50_000;\n\n// The safety guards' vocabulary, spliced in as JSON: constants of ours, never\n// anything a model supplied.\nconst CARD_PATTERN_SOURCE = JSON.stringify(CARD_FIELD_PATTERN.source);\nconst PAYMENT_DOMAINS_JSON = JSON.stringify(PAYMENT_PROVIDER_DOMAINS);\n\nconst SCRIPT = String.raw`\n(function (args) {\n var REF_ATTR = 'data-sd-ref';\n var MAX = ${MAX_PAGE_TEXT_CHARS};\n var w = window;\n if (typeof w.__sdRefSeq !== 'number') w.__sdRefSeq = 0;\n\n function refOf(el) {\n var existing = el.getAttribute(REF_ATTR);\n if (existing) return existing;\n w.__sdRefSeq += 1;\n var ref = 'ref_' + w.__sdRefSeq;\n el.setAttribute(REF_ATTR, ref);\n return ref;\n }\n\n function findRef(ref, root) {\n root = root || document;\n var hit = root.querySelector('[' + REF_ATTR + '=\"' + ref + '\"]');\n if (hit) return hit;\n var all = root.querySelectorAll('*');\n for (var i = 0; i < all.length; i++) {\n if (all[i].shadowRoot) {\n var inner = findRef(ref, all[i].shadowRoot);\n if (inner) return inner;\n }\n }\n return null;\n }\n\n function isHidden(el) {\n if (el.getAttribute('aria-hidden') === 'true') return true;\n var style = w.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') return true;\n if (parseFloat(style.opacity) === 0) return true;\n var rect = el.getBoundingClientRect();\n return rect.width === 0 && rect.height === 0;\n }\n\n function inViewport(el) {\n var r = el.getBoundingClientRect();\n return r.bottom > 0 && r.right > 0 && r.top < w.innerHeight && r.left < w.innerWidth;\n }\n\n var INPUT_ROLES = {\n button: 'button', submit: 'button', reset: 'button', image: 'button',\n checkbox: 'checkbox', radio: 'radio', range: 'slider', search: 'searchbox',\n email: 'textbox', tel: 'textbox', url: 'textbox', text: 'textbox',\n password: 'textbox', number: 'spinbutton', date: 'textbox', file: 'button'\n };\n var TAG_ROLES = {\n A: 'link', BUTTON: 'button', SELECT: 'combobox', TEXTAREA: 'textbox',\n H1: 'heading', H2: 'heading', H3: 'heading', H4: 'heading', H5: 'heading', H6: 'heading',\n IMG: 'img', NAV: 'navigation', MAIN: 'main', HEADER: 'banner', FOOTER: 'contentinfo',\n FORM: 'form', DIALOG: 'dialog', TABLE: 'table', UL: 'list', OL: 'list', LI: 'listitem',\n SUMMARY: 'button', LABEL: 'label', OPTION: 'option', IFRAME: 'iframe', VIDEO: 'video'\n };\n var INTERACTIVE_ROLES = {\n button: 1, link: 1, checkbox: 1, radio: 1, textbox: 1, searchbox: 1, combobox: 1,\n slider: 1, spinbutton: 1, switch: 1, tab: 1, menuitem: 1, menuitemcheckbox: 1,\n menuitemradio: 1, option: 1, treeitem: 1\n };\n\n function roleOf(el) {\n var explicit = el.getAttribute('role');\n if (explicit) return explicit.split(' ')[0];\n if (el.tagName === 'INPUT') {\n var t = (el.getAttribute('type') || 'text').toLowerCase();\n if (t === 'hidden') return null;\n return INPUT_ROLES[t] || 'textbox';\n }\n if (el.tagName === 'A' && !el.hasAttribute('href')) return null;\n if (el.tagName === 'IMG' && !el.getAttribute('alt')) return null;\n if (el.isContentEditable && el.parentElement && !el.parentElement.isContentEditable) return 'textbox';\n return TAG_ROLES[el.tagName] || null;\n }\n\n function isInteractive(el, role) {\n if (role && INTERACTIVE_ROLES[role]) return true;\n if (el.hasAttribute('onclick')) return true;\n var tab = el.getAttribute('tabindex');\n return tab !== null && Number(tab) >= 0 && !!role;\n }\n\n function clean(s) {\n return (s || '').replace(/\\s+/g, ' ').trim();\n }\n\n // Roles whose own text is their name. Containers (banner, list, table, form)\n // take a name only from an explicit label: their text is their children's,\n // which the tree already lists, and repeating it drowns \\`find\\`.\n var TEXT_NAMED = { heading: 1, option: 1, label: 1, text: 1, generic: 1, listitem: 0 };\n\n function nameOf(el, role) {\n var label = el.getAttribute('aria-label');\n if (label) return clean(label);\n var by = el.getAttribute('aria-labelledby');\n if (by) {\n var parts = by.split(/\\s+/).map(function (id) {\n var n = document.getElementById(id);\n return n ? n.textContent : '';\n });\n var joined = clean(parts.join(' '));\n if (joined) return joined;\n }\n if (el.id) {\n var forLabel = document.querySelector('label[for=\"' + CSS.escape(el.id) + '\"]');\n if (forLabel) return clean(forLabel.textContent);\n }\n var alt = el.getAttribute('alt') || el.getAttribute('title');\n if (alt) return clean(alt);\n if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {\n return clean(el.getAttribute('placeholder') || '');\n }\n if (role && !INTERACTIVE_ROLES[role] && !TEXT_NAMED[role]) return '';\n return clean(el.innerText || el.textContent).slice(0, 100);\n }\n\n function valueOf(el) {\n if (el.tagName === 'INPUT') {\n var t = (el.getAttribute('type') || 'text').toLowerCase();\n if (t === 'checkbox' || t === 'radio') return el.checked ? 'checked' : 'unchecked';\n if (t === 'password') return el.value ? '••••' : '';\n return el.value ? clean(el.value).slice(0, 80) : '';\n }\n if (el.tagName === 'TEXTAREA') return el.value ? clean(el.value).slice(0, 80) : '';\n if (el.tagName === 'SELECT') {\n var opt = el.options[el.selectedIndex];\n return opt ? clean(opt.text) : '';\n }\n return '';\n }\n\n function hasOwnText(el) {\n for (var c = el.firstChild; c; c = c.nextSibling) {\n if (c.nodeType === 3 && c.nodeValue && c.nodeValue.trim()) return true;\n }\n return false;\n }\n\n function children(el) {\n var out = Array.prototype.slice.call(el.children);\n if (el.shadowRoot) out = out.concat(Array.prototype.slice.call(el.shadowRoot.children));\n return out;\n }\n\n function describe(el, role) {\n var name = nameOf(el, role);\n var line = role + (name ? ' \"' + name.replace(/\"/g, \"'\") + '\"' : '');\n var value = valueOf(el);\n if (value) line += ' value=\"' + value.replace(/\"/g, \"'\") + '\"';\n if (el.tagName === 'A' && el.getAttribute('href')) {\n var href = el.getAttribute('href');\n if (href.indexOf('javascript:') !== 0) line += ' href=\"' + href.slice(0, 200) + '\"';\n }\n var description = clean(el.getAttribute('aria-description'));\n if (description) line += ' description=\"' + description.slice(0, 200).replace(/\"/g, \"'\") + '\"';\n if (el.disabled) line += ' disabled';\n // Semantics WAO (@stunt-double/wao) inferred rather than the page's authors\n // wrote: \\`data-wao=\"role name\"\\` lists what it set.\n var inferred = clean(el.getAttribute('data-wao'));\n if (inferred) line += ' inferred=\"' + inferred.replace(/\"/g, \"'\") + '\"';\n return line + ' [' + refOf(el) + ']';\n }\n\n function read(filter, depth, rootRef) {\n var root = document.body;\n if (rootRef) {\n root = findRef(rootRef);\n if (!root) return { ok: false, error: rootRef + ' is stale or not found on the current page. Re-read the page to get fresh references.' };\n }\n var lines = [];\n var size = 0;\n var truncated = false;\n function walk(el, level, reported) {\n if (truncated || !el || el.nodeType !== 1) return;\n if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'NOSCRIPT' || el.tagName === 'TEMPLATE') return;\n if (isHidden(el)) return;\n var role = roleOf(el);\n var interactive = isInteractive(el, role);\n var include;\n if (filter === 'interactive') include = interactive;\n else include = !!role || interactive || (hasOwnText(el) && clean(el.innerText).length > 0);\n if (include && filter !== 'all' && !inViewport(el)) include = false;\n var next = reported;\n if (include) {\n var line = ' '.repeat(Math.min(reported, 20)) + describe(el, role || (interactive ? 'generic' : 'text'));\n if (size + line.length + 1 > ${MAX_PAGE_TEXT_CHARS}) { truncated = true; return; }\n lines.push(line);\n size += line.length + 1;\n next = reported + 1;\n if (!interactive && !role) return;\n }\n // Depth counts levels of the reported tree, not DOM nesting: a real\n // page nests its buttons far deeper than 15 elements.\n if (next >= depth) return;\n var kids = children(el);\n for (var i = 0; i < kids.length; i++) walk(kids[i], level + 1, next);\n }\n walk(root, 0, 0);\n var text = lines.join('\\n');\n if (!text) text = filter === 'all' ? 'The page has no readable elements.' : 'No elements in the viewport match. Scroll, or read with filter \"all\".';\n if (truncated) text += '\\n[truncated: the page tree exceeded ' + MAX + ' characters; pass a ref or a smaller depth]';\n return { ok: true, text: text, truncated: truncated };\n }\n\n function find(query) {\n var terms = clean(query).toLowerCase().split(' ').filter(function (t) { return t.length > 1; });\n if (!terms.length) return { ok: false, error: 'find needs a query.' };\n var scored = [];\n var all = document.body.querySelectorAll('*');\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n if (isHidden(el)) continue;\n var role = roleOf(el);\n var interactive = isInteractive(el, role);\n if (!role && !interactive) continue;\n var hay = (role + ' ' + nameOf(el, role || 'generic') + ' ' + (el.getAttribute('aria-description') || '') + ' ' + (el.getAttribute('placeholder') || '') + ' ' + (el.getAttribute('name') || '')).toLowerCase();\n var score = 0;\n for (var j = 0; j < terms.length; j++) if (hay.indexOf(terms[j]) !== -1) score += 1;\n if (score > 0) scored.push({ el: el, role: role || 'generic', score: score + (interactive ? 0.5 : 0) + (inViewport(el) ? 0.25 : 0) });\n }\n scored.sort(function (a, b) { return b.score - a.score; });\n var top = scored.slice(0, 20);\n if (!top.length) return { ok: true, text: 'No elements match \"' + clean(query) + '\".', truncated: false };\n return { ok: true, text: top.map(function (s) { return describe(s.el, s.role); }).join('\\n'), truncated: false };\n }\n\n function pageText() {\n var main = document.querySelector('main, [role=\"main\"], article');\n var text = clean((main && main.innerText.length > 200 ? main.innerText : document.body.innerText) || '');\n var truncated = text.length > MAX;\n return { ok: true, text: truncated ? text.slice(0, MAX) + '\\n[truncated]' : text || 'The page has no text.', truncated: truncated };\n }\n\n function resolve(ref, scroll) {\n var el = findRef(ref);\n if (!el || !el.isConnected) return { ok: false, error: ref + ' is stale or not found on the current page. Re-read the page to get fresh references.' };\n // Instant, not the page's own scroll-behavior: smooth, which is still\n // moving when the box is measured, so the click would land short.\n if (scroll && !inViewport(el)) el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });\n var r = el.getBoundingClientRect();\n return { ok: true, x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2), inViewport: inViewport(el) };\n }\n\n function form(ref, value) {\n var el = findRef(ref);\n if (!el) return { ok: false, error: ref + ' is stale or not found on the current page. Re-read the page to get fresh references.' };\n var fire = function (type) { el.dispatchEvent(new Event(type, { bubbles: true })); };\n if (el.tagName === 'SELECT') {\n var want = String(value).toLowerCase();\n for (var i = 0; i < el.options.length; i++) {\n var o = el.options[i];\n if (o.value.toLowerCase() === want || clean(o.text).toLowerCase() === want) {\n el.selectedIndex = i; fire('input'); fire('change');\n return { ok: true, description: 'Selected \"' + clean(o.text) + '\"' };\n }\n }\n return { ok: false, error: 'No option in ' + ref + ' matches that value. Read the page to see its options.' };\n }\n if (el.tagName === 'INPUT' && (el.type === 'checkbox' || el.type === 'radio')) {\n var on = value === true || String(value).toLowerCase() === 'true' || String(value).toLowerCase() === 'checked';\n if (el.checked !== on) el.click();\n return { ok: true, description: (el.checked ? 'Checked ' : 'Unchecked ') + ref };\n }\n if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {\n var proto = el.tagName === 'INPUT' ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;\n var setter = Object.getOwnPropertyDescriptor(proto, 'value').set;\n el.focus();\n setter.call(el, String(value));\n fire('input'); fire('change');\n // The value can be a password or a code, and results are recorded, so only its length.\n return { ok: true, description: 'Set ' + ref + ' (' + String(value).length + ' characters)' };\n }\n if (el.isContentEditable) {\n el.focus(); el.textContent = String(value); fire('input');\n return { ok: true, description: 'Set ' + ref + ' text' };\n }\n return { ok: false, error: ref + ' is not a form field.' };\n }\n\n // --- inspect: the facts the safety guards decide on ----------------------\n\n var CARD_RE = new RegExp(${CARD_PATTERN_SOURCE}, 'i');\n var PAYMENT_DOMAINS = ${PAYMENT_DOMAINS_JSON};\n var ACTIONABLE = 'a[href],button,input,select,textarea,summary,iframe,[role=\"button\"],[role=\"link\"],[role=\"checkbox\"],[role=\"textbox\"],[contenteditable=\"true\"]';\n\n function isPaymentFrame(src) {\n var host = '';\n try { host = new URL(src, location.href).hostname.toLowerCase(); } catch (e) { return false; }\n for (var i = 0; i < PAYMENT_DOMAINS.length; i++) {\n var d = PAYMENT_DOMAINS[i];\n if (host === d || host.slice(-(d.length + 1)) === '.' + d) return true;\n }\n return false;\n }\n\n function isCardField(el) {\n if (el.tagName === 'IFRAME') {\n return isPaymentFrame(el.getAttribute('src') || '') || CARD_RE.test((el.getAttribute('title') || '') + ' ' + (el.getAttribute('name') || ''));\n }\n if (/(^|\\s)cc-/.test((el.getAttribute('autocomplete') || '').toLowerCase())) return true;\n var text = [el.getAttribute('name'), el.id, el.getAttribute('aria-label'), el.getAttribute('placeholder')].join(' ');\n return CARD_RE.test(text);\n }\n\n // Across the whole page only visible fields count: a login modal parked\n // in the DOM must not make every button on the page look like a submit.\n function holdsSensitive(root, visibleOnly) {\n if (!root) return false;\n var fields = root.querySelectorAll('input, iframe');\n for (var i = 0; i < fields.length; i++) {\n var f = fields[i];\n var t = (f.getAttribute('type') || '').toLowerCase();\n if (t === 'hidden' || t === 'submit' || t === 'button') continue;\n if (t !== 'password' && !isCardField(f)) continue;\n if (visibleOnly && isHidden(f)) continue;\n return true;\n }\n return false;\n }\n\n function deepActive() {\n var el = document.activeElement;\n for (var i = 0; el && i < 10; i++) {\n if (el.shadowRoot && el.shadowRoot.activeElement) { el = el.shadowRoot.activeElement; continue; }\n if (el.tagName === 'IFRAME') {\n var inner = null;\n try { inner = el.contentDocument && el.contentDocument.activeElement; } catch (e) { inner = null; }\n if (inner && inner !== el.contentDocument.body) { el = inner; continue; }\n }\n break;\n }\n return el && el !== document.body && el !== document.documentElement ? el : null;\n }\n\n function atPoint(x, y) {\n var el = document.elementFromPoint(x, y);\n for (var i = 0; el && el.shadowRoot && i < 10; i++) {\n var inner = el.shadowRoot.elementFromPoint(x, y);\n if (!inner || inner === el) break;\n el = inner;\n }\n return el;\n }\n\n function submitLabel(form) {\n var controls = form.querySelectorAll('button, input[type=\"submit\" i], input[type=\"image\" i]');\n for (var i = 0; i < controls.length; i++) {\n var c = controls[i];\n var t = (c.getAttribute('type') || 'submit').toLowerCase();\n if (t === 'submit' || t === 'image') return labelOf(c);\n }\n return '';\n }\n\n function labelOf(el) {\n var name = nameOf(el, roleOf(el) || 'generic');\n if (!name && el.tagName === 'INPUT') name = clean(el.value || '');\n return name;\n }\n\n function factsOf(raw) {\n if (!raw || raw.nodeType !== 1) return null;\n var el = raw.closest ? raw.closest(ACTIONABLE) || raw : raw;\n var tag = el.tagName.toLowerCase();\n var type = (el.getAttribute('type') || '').toLowerCase();\n if (tag === 'button' && !type) type = 'submit';\n var role = roleOf(el) || '';\n var form = el.form || (el.closest ? el.closest('form') : null);\n var href = tag === 'a' ? el.getAttribute('href') || '' : '';\n var navigating = tag === 'a' && !!href && href.charAt(0) !== '#' && href.toLowerCase().indexOf('javascript:') !== 0 && el.getAttribute('role') !== 'button';\n var isSubmit = !!form && ((tag === 'button' && type === 'submit') || (tag === 'input' && (type === 'submit' || type === 'image')));\n var isButton = tag === 'button' || role === 'button' || (tag === 'input' && (type === 'submit' || type === 'button' || type === 'image'));\n return {\n tag: tag,\n type: type,\n autocomplete: (el.getAttribute('autocomplete') || '').toLowerCase(),\n name: el.getAttribute('name') || '',\n id: el.id || '',\n ariaLabel: el.getAttribute('aria-label') || '',\n placeholder: el.getAttribute('placeholder') || '',\n label: labelOf(el).slice(0, 200),\n role: role,\n isNavigatingLink: navigating,\n isSubmit: isSubmit,\n isButton: isButton,\n iframeSrc: tag === 'iframe' ? (el.getAttribute('src') || '').slice(0, 500) : '',\n iframeTitle: tag === 'iframe' ? el.getAttribute('title') || '' : '',\n inForm: !!form,\n // Without a form, the page stands in for one: a single-page sign-up\n // with a password box and a \"Create account\" button is still a sign-up.\n formSensitive: form ? holdsSensitive(form, false) : holdsSensitive(document, true),\n formSubmitLabel: form ? submitLabel(form).slice(0, 200) : ''\n };\n }\n\n function inspect(target) {\n if (target.type === 'focused') return { ok: true, facts: factsOf(deepActive()) };\n if (target.type === 'coordinate') return { ok: true, facts: factsOf(atPoint(target.x, target.y)) };\n var el = findRef(target.ref);\n if (!el) return { ok: false, error: target.ref + ' is stale or not found on the current page. Re-read the page to get fresh references.' };\n return { ok: true, facts: factsOf(el) };\n }\n\n switch (args.op) {\n case 'read': return read(args.filter, args.depth, args.ref);\n case 'find': return find(args.query);\n case 'text': return pageText();\n case 'resolve': return resolve(args.ref, args.scroll);\n case 'form': return form(args.ref, args.value);\n case 'viewport': return { ok: true, width: w.innerWidth, height: w.innerHeight };\n case 'inspect': return inspect(args.target);\n }\n return { ok: false, error: 'unknown op' };\n})`;\n\n/** The expression to `evaluate` for one operation. Arguments are JSON, never code. */\nexport function buildPageCall(op: PageOp): string {\n return `${SCRIPT}(${JSON.stringify(op)})`;\n}\n","// Client-side executor for Anthropic's browser use toolset\n// (`browser_toolset_20260801`): Claude emits `tool_use` blocks\n// with `toolset_name: 'browser'` naming one of the toolset's members, and this\n// runs each against the `BrowserDriver` and builds the `tool_result` the API\n// expects back. Spec: https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool\n//\n// Contract points the API enforces or relies on, all handled here:\n// - every result echoes `toolset_name: 'browser'`;\n// - a turn's calls run sequentially, and after the first failure every later\n// call is answered with exactly `HALT_TEXT` rather than run;\n// - tab-management results are exactly one `browser_state` block; other\n// results may carry at most one, never on an error;\n// - navigation refuses anything but http(s), and the caller's network policy.\n//\n// No runtime imports beyond this package, so it runs under `node --test`, and\n// `@anthropic-ai/sdk` is needed for its types only.\n\nimport type Anthropic from '@anthropic-ai/sdk';\nimport type { BrowserDriver, DriverPage } from './driver.ts';\nimport { toPlaywrightKeySequence } from './keys.ts';\nimport {\n buildPageCall,\n type FormResult,\n type InspectResult,\n type InspectTarget,\n type PageOp,\n type ReadResult,\n type ResolveResult,\n} from './page-script.ts';\nimport {\n type BrowserSafetyOptions,\n containsCardNumber,\n type ElementFacts,\n enterSubmitReason,\n refusalText,\n sensitiveFieldReason,\n submitReason,\n} from './safety.ts';\n\ntype ToolResult = Anthropic.Messages.ToolResultBlockParam;\ntype ToolUse = Pick<Anthropic.Messages.ToolUseBlock, 'id' | 'name' | 'input' | 'toolset_name'>;\ntype ResultContent = Exclude<ToolResult['content'], string | undefined>;\ntype BrowserState = Anthropic.Messages.BrowserStateBlockParam;\n\n/** The tool entry for `tools`. Optional members (scripting, uploads, console,\n * network) stay disabled: the agent browses like a person, not a developer. */\nexport const BROWSER_TOOLSET: Anthropic.Messages.BrowserToolset20260801 = {\n type: 'browser_toolset_20260801',\n};\n\nexport const BROWSER_TOOLSET_NAME = 'browser';\n\n/** Exact text the API expects for calls skipped after an earlier failure. */\nexport const HALT_TEXT = 'Not executed: an earlier action in this turn failed.';\n\nconst DISABLED_MEMBERS = new Set([\n 'javascript_exec',\n 'file_upload',\n 'read_console',\n 'read_network',\n]);\nconst TAB_MEMBERS = new Set(['new_tab', 'list_tabs', 'switch_tab', 'close_tab']);\nconst MAX_WAIT_SECONDS = 30;\nconst SCROLL_NOTCH_PX = 100;\nconst FIELD_LIMIT = 4_096;\n\ntype Target = { type: 'ref'; ref: string } | { type: 'coordinate'; x: number; y: number };\n\nexport class ToolsetError extends Error {}\n\nexport type BrowserToolsetOptions = {\n /**\n * The caller's network policy: return an error message to refuse `url`, or\n * null to allow it. Scheme checks happen before this is called.\n */\n checkNavigation?: (url: string) => string | null;\n /**\n * Allow loopback, private and link-local hosts. Off by default: a browser\n * running inside your own infrastructure (a job runner, a container) can\n * otherwise be steered at the host's own services or the cloud metadata\n * endpoint by anything on a page that talks the model into navigating there.\n * Turn it on when reaching private hosts is the point (testing an intranet\n * or a local dev server); `checkNavigation` still applies.\n */\n allowPrivateHosts?: boolean;\n navigationTimeoutMs?: number;\n screenshotTimeoutMs?: number;\n /**\n * Guards that refuse paying, signing up and entering secrets (`safety.ts`).\n * Off by default: agents testing their own product fill forms with test data\n * on purpose.\n * A refusal is an ordinary result telling the model why, not an error, so\n * the rest of its turn still runs.\n */\n safety?: BrowserSafetyOptions;\n};\n\n/** One executed call, for the caller's transcript and evidence. */\nexport type ToolsetCallRecord = {\n toolUseId: string;\n name: string;\n input: Record<string, unknown>;\n text: string;\n screenshotBase64?: string;\n isError: boolean;\n};\n\nexport type ToolsetTurnResult = {\n /** In call order, one per browser `tool_use`, ready for the next user turn. */\n results: ToolResult[];\n records: ToolsetCallRecord[];\n};\n\n/**\n * Whether a URL's host is loopback, private or link-local, by name or literal\n * address. Names that resolve to a private address (DNS rebinding) are not\n * caught here; the in-browser domain policy is the layer for those.\n */\nexport function isPrivateHost(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/^\\[|\\]$/g, '');\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.internal'))\n return true;\n if (host === 'metadata.google.internal') return true;\n const v4 = host.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (v4) {\n const [a, b] = [Number(v4[1]), Number(v4[2])];\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 100 && b >= 64 && b <= 127)\n );\n }\n if (host.includes(':')) {\n return (\n host === '::1' ||\n host === '::' ||\n /^f[cd]/.test(host) ||\n /^fe[89ab]/.test(host) ||\n host.startsWith('::ffff:')\n );\n }\n return false;\n}\n\n/**\n * The input as it may be recorded: typed text and form values can be a\n * password or a code, so the transcript and events get their length, never the\n * value. The model still receives the real input; only records are redacted.\n */\nexport function redactInput(name: string, input: Record<string, unknown>): Record<string, unknown> {\n if (name === 'type' && typeof input.text === 'string') {\n return { ...input, text: `[${input.text.length} characters]` };\n }\n if (name === 'form_input' && 'value' in input) {\n const v = input.value;\n return { ...input, value: typeof v === 'boolean' ? v : `[${String(v).length} characters]` };\n }\n return input;\n}\n\n/** Strip what the toolset forbids in `browser_state` fields and cap the length. */\nexport function sanitiseStateField(value: string): string {\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f\\u2028\\u2029]+/g, ' ').slice(0, FIELD_LIMIT);\n}\n\nexport function isBrowserToolsetCall(block: {\n type: string;\n toolset_name?: string | null;\n}): boolean {\n return block.type === 'tool_use' && block.toolset_name === BROWSER_TOOLSET_NAME;\n}\n\nfunction parseTarget(raw: unknown, allow: 'any' | 'ref' | 'coordinate'): Target {\n const t = (raw ?? {}) as Record<string, unknown>;\n if (t.type === 'ref' && typeof t.ref === 'string' && allow !== 'coordinate') {\n return { type: 'ref', ref: t.ref };\n }\n if (\n t.type === 'coordinate' &&\n typeof t.x === 'number' &&\n typeof t.y === 'number' &&\n allow !== 'ref'\n ) {\n return { type: 'coordinate', x: Math.round(t.x), y: Math.round(t.y) };\n }\n const want =\n allow === 'ref'\n ? 'a ref target'\n : allow === 'coordinate'\n ? 'a coordinate target'\n : 'a ref or coordinate target';\n throw new ToolsetError(`Error: expected ${want}.`);\n}\n\nfunction clampNumber(value: unknown, min: number, max: number, fallback: number): number {\n const n = typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n return Math.min(max, Math.max(min, n));\n}\n\nexport class BrowserToolsetExecutor {\n /** Tab ids by the driver's stable page id (page objects are not stable). */\n private readonly tabIds = new Map<string, string>();\n private tabSeq = 0;\n\n // Plain fields rather than parameter properties: `node --test` runs this\n // file with type stripping only, which rejects parameter properties.\n private readonly driver: BrowserDriver;\n private readonly options: BrowserToolsetOptions;\n\n constructor(driver: BrowserDriver, options: BrowserToolsetOptions = {}) {\n this.driver = driver;\n this.options = options;\n }\n\n /** Turns run one at a time: two batches interleaving on one page would be nonsense. */\n private queue: Promise<unknown> = Promise.resolve();\n\n /**\n * Run every browser call in a turn, in order, halting after the first\n * failure. An aborted `signal` halts the rest the same way, so a caller that\n * gave up on a stuck turn does not have its remaining calls land on the\n * connection it rebuilt.\n */\n runTurn(\n calls: readonly ToolUse[],\n opts: { signal?: AbortSignal } = {}\n ): Promise<ToolsetTurnResult> {\n const run = this.queue.then(() => this.runTurnNow(calls, opts.signal));\n this.queue = run.catch(() => {});\n return run;\n }\n\n private async runTurnNow(\n calls: readonly ToolUse[],\n signal?: AbortSignal\n ): Promise<ToolsetTurnResult> {\n const results: ToolResult[] = [];\n const records: ToolsetCallRecord[] = [];\n let failed = false;\n for (const call of calls) {\n const input = redactInput(call.name, (call.input ?? {}) as Record<string, unknown>);\n if (failed || signal?.aborted) {\n results.push(this.errorResult(call.id, HALT_TEXT));\n records.push({\n toolUseId: call.id,\n name: call.name,\n input,\n text: HALT_TEXT,\n isError: true,\n });\n continue;\n }\n try {\n const content = await this.execute(\n call.name,\n (call.input ?? {}) as Record<string, unknown>\n );\n results.push({\n type: 'tool_result',\n tool_use_id: call.id,\n toolset_name: BROWSER_TOOLSET_NAME,\n content,\n });\n const text = content\n .map((b) =>\n b.type === 'text' ? b.text : b.type === 'browser_state' ? describeState(b) : ''\n )\n .filter(Boolean)\n .join('\\n');\n const image = content.find((b) => b.type === 'image');\n records.push({\n toolUseId: call.id,\n name: call.name,\n input,\n text,\n screenshotBase64:\n image && image.type === 'image' && image.source.type === 'base64'\n ? image.source.data\n : undefined,\n isError: false,\n });\n } catch (err) {\n failed = true;\n const message =\n err instanceof ToolsetError\n ? err.message\n : `Error: ${err instanceof Error ? err.message : String(err)}`;\n results.push(this.errorResult(call.id, message));\n records.push({ toolUseId: call.id, name: call.name, input, text: message, isError: true });\n }\n }\n return { results, records };\n }\n\n private errorResult(toolUseId: string, text: string): ToolResult {\n return {\n type: 'tool_result',\n tool_use_id: toolUseId,\n toolset_name: BROWSER_TOOLSET_NAME,\n is_error: true,\n content: text,\n };\n }\n\n /** Execute one member. Throws `ToolsetError` (or anything) to fail the call. */\n async execute(name: string, input: Record<string, unknown>): Promise<ResultContent> {\n if (DISABLED_MEMBERS.has(name)) {\n throw new ToolsetError(`Error: ${name} is not enabled in this environment.`);\n }\n if (TAB_MEMBERS.has(name)) return [await this.tabMember(name, input)];\n\n const page = await this.pageFor(input.tab_id);\n switch (name) {\n case 'navigate':\n return this.navigate(page, input);\n case 'screenshot':\n return [await this.screenshot(page)];\n case 'zoom':\n return [await this.zoom(page, input)];\n case 'left_click':\n case 'right_click':\n case 'middle_click':\n case 'double_click':\n case 'triple_click': {\n if (this.options.safety?.blockSubmit && name !== 'right_click') {\n const target = parseTarget(input.target, 'any');\n const facts = await this.inspect(page, target);\n const reason = facts && submitReason(facts);\n if (reason) return [text(refusalText(reason))];\n }\n return [text(await this.click(page, name, input))];\n }\n case 'hover': {\n const { x, y } = await this.point(page, parseTarget(input.target, 'any'));\n await page.hover(x, y);\n return [text(`Hovered at (${x}, ${y})`)];\n }\n case 'mouse_move': {\n const { x, y } = await this.point(page, parseTarget(input.target, 'coordinate'));\n await page.hover(x, y);\n return [text(`Moved the mouse to (${x}, ${y})`)];\n }\n case 'left_click_drag': {\n const from = await this.point(page, parseTarget(input.from, 'coordinate'));\n const to = await this.point(page, parseTarget(input.target, 'coordinate'));\n await page.dragAndDrop(from.x, from.y, to.x, to.y);\n return [text(`Dragged from (${from.x}, ${from.y}) to (${to.x}, ${to.y})`)];\n }\n case 'left_mouse_down':\n case 'left_mouse_up':\n case 'hold_key':\n throw new ToolsetError(\n `Error: ${name} is not supported by this browser. Use left_click, left_click_drag or key instead.`\n );\n case 'scroll':\n return [text(await this.scroll(page, input))];\n case 'scroll_to': {\n const target = parseTarget(input.target, 'ref') as Extract<Target, { type: 'ref' }>;\n const r = await this.pageCall<ResolveResult>(page, {\n op: 'resolve',\n ref: target.ref,\n scroll: true,\n });\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n return [text(`Scrolled ${target.ref} into view`)];\n }\n case 'type': {\n const value = String(input.text ?? '');\n const refusal = await this.typeRefusal(page, value);\n if (refusal) return [text(refusal)];\n await page.type(value);\n return [text(`Typed ${value.length} character${value.length === 1 ? '' : 's'}`)];\n }\n case 'key': {\n const keys = toPlaywrightKeySequence(String(input.text ?? ''));\n if (keys.length === 0) throw new ToolsetError('Error: key needs text.');\n const repeat = Math.round(clampNumber(input.repeat, 1, 100, 1));\n const refusal = await this.keyRefusal(page, keys);\n if (refusal) return [text(refusal)];\n for (let i = 0; i < repeat; i++) for (const k of keys) await page.keyPress(k);\n return [text(`Pressed ${keys.join(' ')}${repeat > 1 ? ` x${repeat}` : ''}`)];\n }\n case 'wait': {\n const seconds = clampNumber(input.duration, 0, MAX_WAIT_SECONDS, 1);\n await page.waitForTimeout(seconds * 1000);\n return [text(`Waited ${seconds}s`)];\n }\n case 'read_page': {\n const filter =\n input.filter === 'interactive' || input.filter === 'all' ? input.filter : 'visible';\n const depth = Math.round(clampNumber(input.depth, 1, 100, 15));\n const ref = typeof input.ref === 'string' ? input.ref : undefined;\n const r = await this.pageCall<ReadResult>(page, { op: 'read', filter, depth, ref });\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n return [text(r.text)];\n }\n case 'find': {\n const r = await this.pageCall<ReadResult>(page, {\n op: 'find',\n query: String(input.query ?? ''),\n });\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n return [text(r.text)];\n }\n case 'get_page_text': {\n const r = await this.pageCall<ReadResult>(page, { op: 'text' });\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n return [text(r.text)];\n }\n case 'form_input': {\n const target = parseTarget(input.target, 'ref') as Extract<Target, { type: 'ref' }>;\n const value = input.value;\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n throw new ToolsetError('Error: form_input needs a string, number or boolean value.');\n }\n if (this.options.safety?.refuseSensitiveInput && typeof value !== 'boolean') {\n const refusal =\n (containsCardNumber(String(value)) && refusalText('That value is a card number.')) ||\n (await this.fieldRefusal(page, target));\n if (refusal) return [text(refusal)];\n }\n const r = await this.pageCall<FormResult>(page, { op: 'form', ref: target.ref, value });\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n return [text(r.description)];\n }\n default:\n throw new ToolsetError(\n `Error: ${name} is not a browser toolset member this executor knows.`\n );\n }\n }\n\n // --- members -------------------------------------------------------------\n\n private async navigate(page: DriverPage, input: Record<string, unknown>): Promise<ResultContent> {\n const raw = String(input.url ?? '').trim();\n // History moves, which Claude reaches for as `navigate(\"back\")`.\n if (raw === 'back' || raw === 'forward') {\n await (raw === 'back' ? page.goBack() : page.goForward());\n return [text(`Went ${raw} to ${await page.url()}`), await this.browserState()];\n }\n let url: URL;\n try {\n // A scheme is `scheme://` or one of the schemes that take no slashes;\n // `example.com:8080` is a host and port, not a scheme called example.com.\n const hasScheme =\n /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) ||\n /^(javascript|data|file|about|blob|mailto|vbscript|chrome|view-source):/i.test(raw);\n url = new URL(hasScheme ? raw : `https://${raw}`);\n } catch {\n throw new ToolsetError(`Error: \"${raw.slice(0, 200)}\" is not a valid URL.`);\n }\n // A bare word (\"Pricing\") is not a site; without this it becomes\n // https://pricing/ and fails somewhere far less clear.\n if (\n /^https?:$/.test(url.protocol) &&\n !url.hostname.includes('.') &&\n url.hostname !== 'localhost'\n ) {\n throw new ToolsetError(\n `Error: \"${raw.slice(0, 200)}\" is not a URL. Pass a full address, or click the link with left_click.`\n );\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new ToolsetError('Error: Navigation refused. Only http and https URLs are allowed.');\n }\n if (!this.options.allowPrivateHosts && isPrivateHost(url.hostname)) {\n throw new ToolsetError(\n 'Error: Navigation refused. Local and private network addresses are not reachable from this browser.'\n );\n }\n const refusal = this.options.checkNavigation?.(url.href);\n if (refusal) throw new ToolsetError(`Error: Navigation refused. ${refusal}`);\n await page.goto(url.href, {\n waitUntil: 'domcontentloaded',\n ...(this.options.navigationTimeoutMs ? { timeoutMs: this.options.navigationTimeoutMs } : {}),\n });\n return [text(`Navigated to ${await page.url()}`), await this.browserState()];\n }\n\n private async screenshot(page: DriverPage): Promise<Anthropic.Messages.ImageBlockParam> {\n const bytes = await page.screenshot({\n type: 'png',\n ...(this.options.screenshotTimeoutMs ? { timeout: this.options.screenshotTimeoutMs } : {}),\n });\n return image(bytes.toString('base64'));\n }\n\n private async zoom(\n page: DriverPage,\n input: Record<string, unknown>\n ): Promise<Anthropic.Messages.ImageBlockParam> {\n const region = input.region;\n if (\n !Array.isArray(region) ||\n region.length !== 4 ||\n !region.every((n) => typeof n === 'number')\n ) {\n throw new ToolsetError('Error: zoom needs region [x0, y0, x1, y1].');\n }\n const [x0, y0, x1, y1] = region as number[];\n const width = Math.abs(x1 - x0);\n const height = Math.abs(y1 - y0);\n if (width < 1 || height < 1) throw new ToolsetError('Error: zoom region is empty.');\n // Captured at the page's own resolution: without an image library in this\n // package there is no upscale, so a small region reads as small as it is.\n const bytes = await page.screenshot({\n type: 'png',\n clip: { x: Math.min(x0, x1), y: Math.min(y0, y1), width, height },\n ...(this.options.screenshotTimeoutMs ? { timeout: this.options.screenshotTimeoutMs } : {}),\n });\n return image(bytes.toString('base64'));\n }\n\n private async click(\n page: DriverPage,\n name: string,\n input: Record<string, unknown>\n ): Promise<string> {\n if (Array.isArray(input.modifiers) ? input.modifiers.length > 0 : input.modifiers) {\n throw new ToolsetError(\n 'Error: modifier clicks are not supported by this browser. Click without modifiers, or use key.'\n );\n }\n const target = parseTarget(input.target, 'any');\n const { x, y } = await this.point(page, target);\n const button = name === 'right_click' ? 'right' : name === 'middle_click' ? 'middle' : 'left';\n const clickCount = name === 'double_click' ? 2 : name === 'triple_click' ? 3 : 1;\n await page.click(x, y, { button, clickCount });\n const where = target.type === 'ref' ? `${target.ref} at (${x}, ${y})` : `(${x}, ${y})`;\n return `${name.replace('_', ' ')} on ${where}`;\n }\n\n private async scroll(page: DriverPage, input: Record<string, unknown>): Promise<string> {\n const direction = input.scroll_direction;\n if (\n direction !== 'up' &&\n direction !== 'down' &&\n direction !== 'left' &&\n direction !== 'right'\n ) {\n throw new ToolsetError('Error: scroll_direction must be up, down, left or right.');\n }\n const amount = Math.round(clampNumber(input.scroll_amount, 1, 10, 3));\n const { x, y } = await this.point(page, parseTarget(input.target, 'any'));\n const delta = amount * SCROLL_NOTCH_PX;\n const dx = direction === 'left' ? -delta : direction === 'right' ? delta : 0;\n const dy = direction === 'up' ? -delta : direction === 'down' ? delta : 0;\n await page.scroll(x, y, dx, dy);\n return `Scrolled ${direction} ${amount} at (${x}, ${y})`;\n }\n\n private async tabMember(name: string, input: Record<string, unknown>): Promise<BrowserState> {\n const ctx = this.driver.context;\n if (name === 'list_tabs') return this.browserState();\n if (name === 'new_tab') {\n // Number the tabs already open first, so ids follow the order tabs were\n // opened in rather than the order Claude happened to mention them.\n for (const existing of await ctx.pages()) this.tabId(existing);\n const page = await ctx.newPage();\n return this.browserState([{ type: 'tab_opened', tab_id: this.tabId(page) }]);\n }\n const page = await this.pageFor(input.tab_id, true);\n if (name === 'switch_tab') {\n await ctx.setActivePage(page);\n return this.browserState();\n }\n // close_tab: keep a tab active afterwards if there is one to fall back to.\n const wasActive = (await ctx.activePage())?.id === page.id;\n await page.close();\n if (wasActive) {\n const remaining = await ctx.pages();\n if (remaining.length > 0) await ctx.setActivePage(remaining[remaining.length - 1]);\n }\n return this.browserState();\n }\n\n // --- helpers -------------------------------------------------------------\n\n private tabId(page: DriverPage): string {\n let id = this.tabIds.get(page.id);\n if (!id) {\n this.tabSeq += 1;\n id = `tab-${this.tabSeq}`;\n this.tabIds.set(page.id, id);\n }\n return id;\n }\n\n private async pageFor(tabId: unknown, required = false): Promise<DriverPage> {\n const ctx = this.driver.context;\n if (typeof tabId === 'string' && tabId.length > 0) {\n for (const page of await ctx.pages()) if (this.tabId(page) === tabId) return page;\n throw new ToolsetError(\n `Error: no tab with tab_id ${tabId}. Call list_tabs for the current tabs.`\n );\n }\n if (required) throw new ToolsetError('Error: tab_id is required.');\n const page = await ctx.activePage();\n if (!page) throw new ToolsetError('Error: the browser has no open tab. Call new_tab first.');\n return page;\n }\n\n private async point(page: DriverPage, target: Target): Promise<{ x: number; y: number }> {\n if (target.type === 'coordinate') return { x: target.x, y: target.y };\n let r = await this.pageCall<ResolveResult>(page, {\n op: 'resolve',\n ref: target.ref,\n scroll: true,\n });\n if (r.ok && !r.inViewport) {\n // A page with its own scroll handling (sticky headers, scroll-snap) can\n // still be settling; look once more before giving up.\n await page.waitForTimeout(250);\n r = await this.pageCall<ResolveResult>(page, {\n op: 'resolve',\n ref: target.ref,\n scroll: false,\n });\n }\n if (!r.ok) throw new ToolsetError(`Error: ${r.error}`);\n if (!r.inViewport) {\n throw new ToolsetError(\n `Error: ${target.ref} could not be scrolled into view. Try coordinates from a screenshot.`\n );\n }\n return { x: r.x, y: r.y };\n }\n\n // --- safety guards ------------------------------------------------------\n\n /** What the guards need to know about an element; null when nothing is there. */\n private async inspect(page: DriverPage, target: InspectTarget): Promise<ElementFacts | null> {\n const r = await this.pageCall<InspectResult>(page, { op: 'inspect', target });\n // A stale ref is refused by the action itself, with the usual message.\n return r.ok ? r.facts : null;\n }\n\n private async fieldRefusal(page: DriverPage, target: InspectTarget): Promise<string | null> {\n const facts = await this.inspect(page, target);\n const reason = facts && sensitiveFieldReason(facts);\n return reason ? refusalText(reason) : null;\n }\n\n /** Typing goes to whatever has focus, so that is what is checked. */\n private async typeRefusal(page: DriverPage, value: string): Promise<string | null> {\n const safety = this.options.safety;\n if (!safety) return null;\n if (safety.refuseSensitiveInput) {\n if (containsCardNumber(value)) return refusalText('That value is a card number.');\n const refusal = await this.fieldRefusal(page, { type: 'focused' });\n if (refusal) return refusal;\n }\n // A newline types as Enter, which submits the field's form.\n if (safety.blockSubmit && /[\\r\\n]/.test(value)) {\n const facts = await this.inspect(page, { type: 'focused' });\n const reason = facts && enterSubmitReason(facts);\n if (reason) return refusalText(reason);\n }\n return null;\n }\n\n /** Enter submits the focused field's form; Enter or Space presses a focused button. */\n private async keyRefusal(page: DriverPage, keys: readonly string[]): Promise<string | null> {\n if (!this.options.safety?.blockSubmit) return null;\n const pressesEnter = keys.some((k) => /(^|\\+)(Enter|NumpadEnter)$/.test(k));\n const pressesSpace = keys.some((k) => /(^|\\+)(Space| )$/.test(k));\n if (!pressesEnter && !pressesSpace) return null;\n const facts = await this.inspect(page, { type: 'focused' });\n if (!facts) return null;\n const reason =\n (facts.isButton && submitReason(facts)) || (pressesEnter && enterSubmitReason(facts));\n return reason ? refusalText(reason) : null;\n }\n\n private pageCall<R>(page: DriverPage, op: PageOp): Promise<R> {\n return page.evaluate<R>(buildPageCall(op));\n }\n\n private async browserState(stateChanges?: BrowserState['state_changes']): Promise<BrowserState> {\n const ctx = this.driver.context;\n const [pages, active] = await Promise.all([ctx.pages(), ctx.activePage()]);\n const tabs = await Promise.all(\n pages.slice(0, 100).map(async (page) => ({\n tab_id: this.tabId(page),\n title: sanitiseStateField(await page.title().catch(() => '')),\n url: sanitiseStateField(await Promise.resolve(page.url()).catch(() => '')),\n ...(active && page.id === active.id ? { active: true } : {}),\n }))\n );\n if (tabs.length > 0 && !tabs.some((t) => t.active)) tabs[tabs.length - 1].active = true;\n return {\n type: 'browser_state',\n tabs,\n ...(stateChanges && stateChanges.length > 0 ? { state_changes: stateChanges } : {}),\n };\n }\n}\n\nfunction text(value: string): Anthropic.Messages.TextBlockParam {\n return { type: 'text', text: value };\n}\n\nfunction image(data: string): Anthropic.Messages.ImageBlockParam {\n return { type: 'image', source: { type: 'base64', media_type: 'image/png', data } };\n}\n\nfunction describeState(state: BrowserState): string {\n const active = state.tabs.find((t) => t.active);\n return active ? `Tab ${active.tab_id}: ${active.url}` : `${state.tabs.length} tabs`;\n}\n","// Provider-neutral browser tools, run directly against the page with no model\n// call inside any of them.\n//\n// Instruction-style tools (`act(\"click the buy button\")`, `extract(...)`) hand\n// each step to a second model to resolve, so every step the agent takes pays\n// for a hidden model call as well, and is slowed by it. These tools read the\n// page as an accessibility tree with `ref_N` handles and act on those refs (or\n// on screenshot coordinates), through the same executor that serves Anthropic's\n// browser use toolset. The agent's own model does all of the reasoning.\n//\n// They are plain function tools rather than Anthropic's toolset type, because\n// other providers have no equivalent: any model with function calling can be\n// offered exactly these, so agents on different models stay comparable.\n//\n// No runtime imports beyond the executor, so it runs under `node --test`.\n\nimport type { BrowserDriver, DriverPage } from './driver.ts';\nimport { BrowserToolsetExecutor, type BrowserToolsetOptions, ToolsetError } from './executor.ts';\nimport type { BrowserSafetyOptions } from './safety.ts';\n\nexport type DirectToolName =\n | 'navigate'\n | 'read_page'\n | 'find'\n | 'get_page_text'\n | 'click'\n | 'type'\n | 'form_input'\n | 'key'\n | 'scroll'\n | 'screenshot'\n | 'done';\n\nexport type DirectToolResult = {\n text: string;\n screenshotBase64?: string;\n error?: string;\n};\n\ntype JsonSchema = {\n type: 'object';\n properties: Record<string, unknown>;\n required?: string[];\n};\n\nexport type DirectToolSpec = {\n name: DirectToolName;\n description: string;\n parameters: JsonSchema;\n};\n\n/**\n * Page reads are re-sent on every later turn, and not every provider caches\n * prompts, so a full 50k-character tree would be paid for again on each step.\n * 20k is enough for most pages' interactive tree; the note tells the model how\n * to narrow a read when it is not.\n */\nexport const MAX_READ_CHARS = 20_000;\n\n/** One tool call, end to end. Past this the page is treated as stuck. */\nexport const TOOL_TIMEOUT_MS = 45_000;\nconst NAVIGATION_TIMEOUT_MS = 30_000;\nconst SCREENSHOT_TIMEOUT_MS = 10_000;\n/** Settling after an input: long enough for a click's navigation to begin. */\nconst SETTLE_MS = 400;\nconst SETTLE_LOAD_TIMEOUT_MS = 5_000;\n\nconst TARGET_PROPERTIES = {\n ref: {\n type: 'string',\n description: 'An element ref from read_page or find, e.g. \"ref_12\". Preferred.',\n },\n x: {\n type: 'number',\n description: 'Viewport x coordinate in screenshot pixels, when there is no ref.',\n },\n y: {\n type: 'number',\n description: 'Viewport y coordinate in screenshot pixels, when there is no ref.',\n },\n};\n\n/**\n * The tools, in a provider-neutral shape; each agent loop adapts them into its\n * SDK's format. Flat parameters rather than a\n * nested target object, because every provider's function calling handles\n * those reliably.\n */\nexport const DIRECT_TOOL_SPECS: DirectToolSpec[] = [\n {\n name: 'navigate',\n description:\n 'Go to a URL, or \"back\" / \"forward\" in history. Returns the page it landed on and a screenshot.',\n parameters: {\n type: 'object',\n properties: { url: { type: 'string', description: 'A full URL, \"back\" or \"forward\".' } },\n required: ['url'],\n },\n },\n {\n name: 'read_page',\n description:\n 'Read the page as an accessibility tree. Each element worth acting on carries a ref (ref_N) to pass to click, type, form_input or scroll. The filter \"visible\" (default) covers what is on screen, \"interactive\" only controls, \"all\" the whole page. Pass a ref to read just that part of the page.',\n parameters: {\n type: 'object',\n properties: {\n filter: { type: 'string', enum: ['visible', 'interactive', 'all'] },\n ref: { type: 'string', description: 'Read only this element and its descendants.' },\n },\n },\n },\n {\n name: 'find',\n description:\n 'Find elements matching a description or text (e.g. \"search box\", \"Add to cart\"). Returns up to 20 matches with refs.',\n parameters: {\n type: 'object',\n properties: { query: { type: 'string', description: 'What to look for.' } },\n required: ['query'],\n },\n },\n {\n name: 'get_page_text',\n description: \"The page's visible text as plain text, for reading content such as a policy.\",\n parameters: { type: 'object', properties: {} },\n },\n {\n name: 'click',\n description:\n 'Click an element by ref, or at a coordinate from a screenshot. Returns the resulting page and a screenshot.',\n parameters: { type: 'object', properties: TARGET_PROPERTIES },\n },\n {\n name: 'type',\n description:\n 'Type text. With a ref (or coordinate), clicks that field first; without one, types into whatever has focus. Does not press Enter: use key for that.',\n parameters: {\n type: 'object',\n properties: {\n text: { type: 'string', description: 'The text to type.' },\n ...TARGET_PROPERTIES,\n },\n required: ['text'],\n },\n },\n {\n name: 'form_input',\n description:\n 'Set a form control directly by ref: choose a select option (by its label or value), tick or untick a checkbox or radio (\"true\" / \"false\"), or fill an input.',\n parameters: {\n type: 'object',\n properties: {\n ref: { type: 'string', description: 'The control ref from read_page or find.' },\n // A string on every provider: a union type is not something every\n // provider's function declarations take reliably, and the page script reads\n // \"true\" / \"false\" as a checked state.\n value: { type: 'string', description: 'The option, \"true\" / \"false\", or the text.' },\n },\n required: ['ref', 'value'],\n },\n },\n {\n name: 'key',\n description:\n 'Press a key or chord, e.g. \"Enter\", \"Escape\", \"Tab\", \"ctrl+a\". Space-separate several to press them in turn.',\n parameters: {\n type: 'object',\n properties: { text: { type: 'string', description: 'The key or keys.' } },\n required: ['text'],\n },\n },\n {\n name: 'scroll',\n description:\n 'Scroll the page (or the element at a ref or coordinate) up, down, left or right. amount is in wheel notches of about 100px, default 5.',\n parameters: {\n type: 'object',\n properties: {\n direction: { type: 'string', enum: ['up', 'down', 'left', 'right'] },\n amount: { type: 'number', description: 'Notches, 1 to 10.' },\n ...TARGET_PROPERTIES,\n },\n required: ['direction'],\n },\n },\n {\n name: 'screenshot',\n description: 'Look at the current page.',\n parameters: { type: 'object', properties: {} },\n },\n {\n name: 'done',\n description:\n 'Signal that the task is complete. Provide a concise summary of what was accomplished.',\n parameters: {\n type: 'object',\n properties: { message: { type: 'string', description: 'Summary.' } },\n required: ['message'],\n },\n },\n];\n\nexport const DIRECT_TOOL_NAMES: readonly DirectToolName[] = DIRECT_TOOL_SPECS.map((s) => s.name);\n\nexport function clampRead(text: string): string {\n if (text.length <= MAX_READ_CHARS) return text;\n return `${text.slice(0, MAX_READ_CHARS)}\\n[truncated at ${MAX_READ_CHARS} characters: read_page with a ref or the \"interactive\" filter, or find, reads less]`;\n}\n\n/**\n * The executor's target shape from our flat parameters. A ref wins over a\n * coordinate; neither is an error the model can correct.\n */\nexport function targetOf(\n input: Record<string, unknown>\n): { type: 'ref'; ref: string } | { type: 'coordinate'; x: number; y: number } | null {\n if (typeof input.ref === 'string' && input.ref.trim()) {\n return { type: 'ref', ref: input.ref.trim() };\n }\n const x = Number(input.x);\n const y = Number(input.y);\n if (input.x !== undefined && input.y !== undefined && Number.isFinite(x) && Number.isFinite(y)) {\n return { type: 'coordinate', x, y };\n }\n return null;\n}\n\nfunction blocksToText(blocks: readonly unknown[]): string {\n return blocks\n .map((b) => {\n const block = b as { type?: string; text?: string };\n return block.type === 'text' && typeof block.text === 'string' ? block.text : '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction withTimeout<T>(work: Promise<T>, ms: number, what: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new ToolsetError(`Error: ${what} did not finish within ${ms / 1000}s.`)),\n ms\n );\n });\n return Promise.race([work, timeout]).finally(() => clearTimeout(timer));\n}\n\nexport type DirectBrowserToolsOptions = {\n viewport: { width: number; height: number };\n /** Overridable for tests. */\n settleMs?: number;\n toolTimeoutMs?: number;\n /**\n * The executor's pay / sign-up / secrets guards. Turn them on (for example\n * with `INDEX_SESSION_SAFETY`) whenever the agent is browsing sites that are\n * not yours and must never pay or register for real.\n */\n safety?: BrowserSafetyOptions;\n /** Passed to the executor: see `BrowserToolsetOptions`. */\n allowPrivateHosts?: BrowserToolsetOptions['allowPrivateHosts'];\n checkNavigation?: BrowserToolsetOptions['checkNavigation'];\n};\n\n/**\n * Runs `DIRECT_TOOL_SPECS` calls against a driver. One per session: the\n * executor inside it holds the tab ids for the session's life.\n */\nexport class DirectBrowserTools {\n private readonly driver: BrowserDriver;\n private readonly executor: BrowserToolsetExecutor;\n private readonly viewport: { width: number; height: number };\n private readonly settleMs: number;\n private readonly toolTimeoutMs: number;\n\n constructor(driver: BrowserDriver, options: DirectBrowserToolsOptions) {\n this.driver = driver;\n this.executor = new BrowserToolsetExecutor(driver, {\n navigationTimeoutMs: NAVIGATION_TIMEOUT_MS,\n screenshotTimeoutMs: SCREENSHOT_TIMEOUT_MS,\n ...(options.safety ? { safety: options.safety } : {}),\n ...(options.allowPrivateHosts ? { allowPrivateHosts: true } : {}),\n ...(options.checkNavigation ? { checkNavigation: options.checkNavigation } : {}),\n });\n this.viewport = options.viewport;\n this.settleMs = options.settleMs ?? SETTLE_MS;\n this.toolTimeoutMs = options.toolTimeoutMs ?? TOOL_TIMEOUT_MS;\n }\n\n /**\n * Run one call. Never throws: a failure comes back as `error` with text the\n * model can act on, so a harness loop can always build its tool result.\n */\n async run(name: string, input: Record<string, unknown>): Promise<DirectToolResult> {\n try {\n return await withTimeout(this.execute(name, input), this.toolTimeoutMs, name);\n } catch (err) {\n const text =\n err instanceof ToolsetError\n ? err.message\n : `Error: ${err instanceof Error ? err.message : String(err)}`;\n return { text, error: text };\n }\n }\n\n private async execute(name: string, input: Record<string, unknown>): Promise<DirectToolResult> {\n switch (name) {\n case 'navigate': {\n const out = await this.executor.execute('navigate', { url: input.url });\n return this.afterInput(blocksToText(out.filter((b) => b.type === 'text')));\n }\n case 'read_page': {\n const out = await this.executor.execute('read_page', {\n filter: input.filter,\n ...(typeof input.ref === 'string' && input.ref ? { ref: input.ref } : {}),\n });\n return { text: clampRead(blocksToText(out)) };\n }\n case 'find': {\n const out = await this.executor.execute('find', { query: input.query });\n return { text: clampRead(blocksToText(out)) };\n }\n case 'get_page_text': {\n const out = await this.executor.execute('get_page_text', {});\n return { text: clampRead(blocksToText(out)) };\n }\n case 'click': {\n const target = targetOf(input);\n if (!target) throw new ToolsetError('Error: click needs a ref, or both x and y.');\n const out = await this.executor.execute('left_click', { target });\n return this.afterInput(blocksToText(out));\n }\n case 'type': {\n const value = String(input.text ?? '');\n const target = targetOf(input);\n const said: string[] = [];\n if (target) {\n said.push(blocksToText(await this.executor.execute('left_click', { target })));\n }\n said.push(blocksToText(await this.executor.execute('type', { text: value })));\n return this.afterInput(said.join('\\n'));\n }\n case 'form_input': {\n if (typeof input.ref !== 'string' || !input.ref) {\n throw new ToolsetError('Error: form_input needs a ref from read_page or find.');\n }\n const out = await this.executor.execute('form_input', {\n target: { type: 'ref', ref: input.ref },\n value:\n typeof input.value === 'boolean' || typeof input.value === 'number'\n ? input.value\n : String(input.value ?? ''),\n });\n return this.afterInput(blocksToText(out));\n }\n case 'key': {\n const out = await this.executor.execute('key', { text: input.text });\n return this.afterInput(blocksToText(out));\n }\n case 'scroll': {\n const target = targetOf(input) ?? {\n type: 'coordinate' as const,\n x: Math.round(this.viewport.width / 2),\n y: Math.round(this.viewport.height / 2),\n };\n const out = await this.executor.execute('scroll', {\n scroll_direction: input.direction,\n scroll_amount: input.amount ?? 5,\n target,\n });\n return this.afterInput(blocksToText(out));\n }\n case 'screenshot': {\n const page = await this.activePage();\n return {\n text: `Screenshot of ${await page.url()}`,\n screenshotBase64: await this.capture(page),\n };\n }\n case 'done':\n // A signal tool; every loop ends the session before it would get here.\n return { text: 'Done tool invoked' };\n default:\n throw new ToolsetError(\n `Error: ${name} is not an available tool. Use one of: ${DIRECT_TOOL_NAMES.join(', ')}.`\n );\n }\n }\n\n /**\n * After anything that can change the page: let it settle, then report where\n * the browser is with a screenshot. The agent sees the effect of its action\n * without spending a turn asking.\n */\n private async afterInput(said: string): Promise<DirectToolResult> {\n const page = await this.activePage();\n if (this.settleMs > 0) {\n await page.waitForTimeout(this.settleMs);\n await page.waitForLoadState('domcontentloaded', SETTLE_LOAD_TIMEOUT_MS).catch(() => {});\n }\n const [url, title] = await Promise.all([\n Promise.resolve(page.url()).catch(() => ''),\n page.title().catch(() => ''),\n ]);\n const where = title ? `Now on ${url} (${title.slice(0, 120)})` : `Now on ${url}`;\n return {\n text: said ? `${said}\\n${where}` : where,\n screenshotBase64: await this.capture(page),\n };\n }\n\n private async capture(page: DriverPage): Promise<string> {\n const bytes = await page.screenshot({ type: 'png', timeout: SCREENSHOT_TIMEOUT_MS });\n return Buffer.from(bytes).toString('base64');\n }\n\n private async activePage(): Promise<DriverPage> {\n const page = await this.driver.context.activePage();\n if (!page) throw new ToolsetError('Error: the browser has no open page.');\n return page;\n }\n}\n\n/**\n * Said to an agent that has used every step it was given, on one extra model\n * call offered only `done` (and made to call it). Without it a stepped-out run\n * ends on a bare \"Max steps reached\", which a grader scores near zero even when\n * the transcript holds the answer. The step budget is unchanged: this call can\n * only answer, never act. Shared so that loops on different models stay\n * comparable.\n */\nexport const STEP_BUDGET_FINAL_NOTE =\n 'You have used every step you were given. Do not call any other tool: call `done` now, and in its message give your answer from what you have already found. Say plainly which parts you could not find or finish.';\n\n/** The output of a run whose forced final answer could not be had either. */\nexport const STEP_BUDGET_FALLBACK_OUTPUT = 'Max steps reached';\n","// One process, one run at a time: the run lifecycle behind start_run,\n// evaluate_check and finish_run, and the browser tools in between.\n//\n// Everything a run produces is a file under `.stuntdouble/runs/<id>/`, written\n// as it happens: `run.json` is rewritten atomically after every verdict, and\n// `activity.jsonl` gains a line per tool call. A run whose process dies stays\n// `running` on disk with a dead pid, and the next start_run marks it\n// interrupted, so it can be resumed.\n\nimport {\n DirectBrowserTools,\n type DirectBrowserToolsOptions,\n} from '@stunt-double/browser-toolset/direct-tools';\nimport { isPrivateHost, redactInput } from '@stunt-double/browser-toolset/executor';\nimport { hostMatchesDomain, INDEX_SESSION_SAFETY } from '@stunt-double/browser-toolset/safety';\nimport { mkdir, mkdtemp, readFile, rm, stat } from 'node:fs/promises';\nimport { homedir, tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\nimport pkg from '../package.json' with { type: 'json' };\nimport { buildBrief, checkLabel } from './brief.ts';\nimport { type LaunchOptions, type LocalDriver, PlaywrightDriver } from './driver.ts';\nimport { cleanLocator, focusEvidence } from './evidence.ts';\nimport {\n type Actor,\n ActorSchema,\n appendJsonl,\n type Check,\n type Checklist,\n ChecklistSchema,\n type Config,\n ConfigSchema,\n isAlive,\n type Locator,\n readJson,\n readRuns,\n type Result,\n type Run,\n sdDir,\n writeFileAtomic,\n writeJsonAtomic,\n} from './files.ts';\nimport { startUi, type UiServer } from './ui-server.ts';\nimport { resolveSkippedChecks } from './vendor/scoring/conditions.ts';\nimport { resolveHaltedChecks } from './vendor/scoring/failure.ts';\nimport {\n describeRunScore,\n newChecklistScoring,\n scoreChecklistRun,\n} from './vendor/scoring/scoring.ts';\n\nexport const PACKAGE = `${pkg.name}@${pkg.version}`;\n\nexport type Launch = (options: LaunchOptions) => Promise<LocalDriver>;\n\nexport type Verdict = 'passed' | 'failed' | 'informational' | 'blocked';\n\nexport type ToolOutput = { text: string; image?: string; isError?: boolean };\n\nconst STOPPED =\n 'This run was stopped from the viewer. Do not use the browser again: call finish_run now with a summary of what you found.';\n/** What finish_run records for a check nobody judged; a resume judges it after all. */\nconst NOT_JUDGED = 'Not judged before the run finished.';\nconst PRIVATE_REFUSED =\n 'Local and private addresses other than the app under test are not reachable.';\n/** activity.jsonl is committed, so a page read is kept to its opening lines. */\nconst LOG_TEXT_CHARS = 500;\n\nconst PRESETS = {\n desktop: { width: 1280, height: 720, touch: false },\n tablet: { width: 1024, height: 1366, touch: true },\n mobile: { width: 390, height: 844, touch: true },\n};\n\ntype Active = {\n dir: string;\n run: Run;\n checklist: Checklist;\n actor: Actor;\n driver: LocalDriver;\n trusted: DirectBrowserTools;\n /** The same tools with the pay, sign-up and secrets guards on, for other hosts. */\n guarded: DirectBrowserTools | null;\n trustedHosts: string[];\n /** A fresh profile to delete when the run ends; null for a kept one. */\n tempProfile: string | null;\n everyScreenshot: boolean;\n stopped: boolean;\n};\n\n/** The checklist URL with its origin swapped for `local_base_url`. */\nexport function rebase(url: string, localBase: string | null | undefined): string {\n if (!localBase) return url;\n const target = new URL(url);\n // Not `target.host = base.host`: that keeps the checklist's port when the base has none.\n return new URL(target.pathname + target.search + target.hash, new URL(localBase).origin).href;\n}\n\n/** `20261103-142210`, in local time, so folders sort by when they started. */\nfunction stamp(date = new Date()): string {\n const p = (n: number) => String(n).padStart(2, '0');\n return `${date.getFullYear()}${p(date.getMonth() + 1)}${p(date.getDate())}-${p(date.getHours())}${p(date.getMinutes())}${p(date.getSeconds())}`;\n}\n\nconst now = () => new Date().toISOString();\n\nexport class LocalSession {\n readonly root: string;\n private readonly launch: Launch;\n private active: Active | null = null;\n private ui: UiServer | null = null;\n\n constructor(options: { project: string; launch?: Launch }) {\n this.root = sdDir(options.project);\n this.launch = options.launch ?? PlaywrightDriver.launch;\n }\n\n /** The viewer's address, starting it on first use. */\n async openUi(path = '/'): Promise<string> {\n this.ui ??= await startUi({ root: this.root });\n await writeJsonAtomic(join(this.root, '.live', 'server.json'), {\n port: this.ui.port,\n token: this.ui.token,\n pid: process.pid,\n });\n return this.ui.link(path);\n }\n\n async startRun(input: { checklist: string; resume?: boolean }): Promise<string> {\n if (this.active) return this.resumeActive(input);\n if (!/^[a-z0-9][a-z0-9-]*$/.test(input.checklist)) {\n throw new Error(`\"${input.checklist}\" is not a checklist slug (checklists/<slug>.json).`);\n }\n const config: Config = await readJson(\n join(this.root, 'stuntdouble.json'),\n ConfigSchema,\n '.stuntdouble/stuntdouble.json'\n );\n let checklist = await readJson(\n join(this.root, 'checklists', `${input.checklist}.json`),\n ChecklistSchema,\n `.stuntdouble/checklists/${input.checklist}.json`\n );\n const actor = await readJson(\n join(this.root, 'actors', `${checklist.actor}.json`),\n ActorSchema,\n `.stuntdouble/actors/${checklist.actor}.json`\n );\n await this.markInterrupted();\n\n let run: Run;\n let dir: string;\n if (input.resume) {\n const entry = (await readRuns(this.root)).find(\n (e) =>\n 'run' in e &&\n e.run.checklist === input.checklist &&\n e.run.runner.kind === 'local' &&\n (e.run.status === 'interrupted' || e.run.status === 'stopped')\n );\n if (!entry || !('run' in entry)) {\n throw new Error(`No interrupted or stopped run of ${input.checklist} to resume.`);\n }\n dir = join(this.root, 'runs', entry.id);\n // A resumed run is judged against the checks it started with.\n checklist = ChecklistSchema.parse(entry.run.checklist_snapshot);\n await this.clearStop(entry.id);\n run = {\n ...entry.run,\n results: entry.run.results.filter((r) => r.evidence !== NOT_JUDGED),\n status: 'running',\n pid: process.pid,\n finished_at: null,\n score: null,\n summary: null,\n };\n } else {\n dir = '';\n run = {\n format: 'stuntdouble.run/1',\n id: '',\n runner: { kind: 'local', package: PACKAGE, judged: false },\n status: 'running',\n pid: process.pid,\n url: rebase(checklist.url, config.local_base_url),\n started_at: now(),\n finished_at: null,\n checklist: input.checklist,\n actor: checklist.actor,\n checklist_snapshot: { ...checklist, cloud: undefined },\n results: [],\n score: null,\n summary: null,\n cloud: null,\n };\n }\n\n const trusted = [run.url, checklist.url, config.local_base_url]\n .filter((u): u is string => Boolean(u))\n .map((u) => new URL(u));\n const trustedHosts = [...new Set(trusted.map((u) => u.hostname))];\n // A private host is trusted by exact origin: trusting localhost:3000 must not\n // open every other local service (the viewer, a database console) to the actor.\n const trustedOrigins = new Set(trusted.map((u) => u.origin));\n const refuseNavigation = (href: string) => {\n const url = URL.parse(href);\n return url && isPrivateHost(url.hostname) && !trustedOrigins.has(url.origin)\n ? PRIVATE_REFUSED\n : null;\n };\n const preset = PRESETS[checklist.browser_config?.preset ?? 'desktop'];\n const toolOptions: DirectBrowserToolsOptions = {\n viewport: checklist.browser_config?.viewport ?? preset,\n allowPrivateHosts: true,\n checkNavigation: refuseNavigation,\n };\n\n // The browser first: a run folder is only made for a run that can start.\n const tempProfile = actor.persist_login\n ? null\n : await mkdtemp(join(tmpdir(), 'stuntdouble-profile-'));\n const profileDir = tempProfile ?? join(homedir(), '.stuntdouble', 'profiles', checklist.actor);\n await mkdir(profileDir, { recursive: true });\n let driver: LocalDriver;\n try {\n driver = await this.launch({\n profileDir,\n headless: config.headless,\n viewport: toolOptions.viewport,\n touch: checklist.browser_config?.touch ?? preset.touch,\n locale: checklist.browser_config?.locale,\n refuseNavigation,\n });\n } catch (err) {\n if (tempProfile) await rm(tempProfile, { recursive: true, force: true });\n throw err;\n }\n\n if (!input.resume) {\n await mkdir(join(this.root, 'runs'), { recursive: true });\n // Two runs started in the same second get a suffix rather than one folder.\n for (let n = 1; !dir; n++) {\n const id = `${stamp()}-${input.checklist}${n > 1 ? `-${n}` : ''}`;\n const made = await mkdir(join(this.root, 'runs', id)).then(\n () => true,\n (err: NodeJS.ErrnoException) => {\n if (err.code === 'EEXIST') return false;\n throw err;\n }\n );\n if (made) [run.id, dir] = [id, join(this.root, 'runs', id)];\n }\n }\n const gitignore = join(this.root, '.gitignore');\n if (!(await stat(gitignore).catch(() => null))) await writeFileAtomic(gitignore, '.live/\\n');\n\n this.active = {\n dir,\n run,\n checklist,\n actor,\n driver,\n trusted: new DirectBrowserTools(driver, toolOptions),\n guarded:\n config.safety === 'third_party'\n ? new DirectBrowserTools(driver, { ...toolOptions, safety: INDEX_SESSION_SAFETY })\n : null,\n trustedHosts,\n tempProfile,\n everyScreenshot: config.model_screenshots === 'every_action',\n stopped: false,\n };\n await this.save();\n await this.log('start_run', input, input.resume ? 'Resumed' : 'Started');\n\n const opened = await this.browserTool('navigate', { url: run.url });\n const pending = this.pending();\n const viewer = await this.openUi(`/runs/${run.id}`);\n return [\n `Run ${run.id} ${input.resume ? 'resumed' : 'started'}. Watch it or stop it at ${viewer}`,\n buildBrief({\n actor,\n checklist,\n url: run.url,\n checks: pending,\n resumed: Boolean(input.resume),\n }),\n opened.isError\n ? `The start page did not load: ${opened.text}\\nTry navigate again, or judge the checks you cannot reach as \"blocked\".`\n : opened.text,\n this.nextStep(),\n ].join('\\n\\n');\n }\n\n /** One of the browser toolset's direct tools, on the active run's browser. */\n async browserTool(name: string, input: Record<string, unknown>): Promise<ToolOutput> {\n const active = this.requireActive();\n if (await this.stopRequested(active)) return { text: STOPPED, isError: true };\n const page = await active.driver.context.activePage();\n const host = page ? (URL.parse(await page.url())?.hostname ?? '') : '';\n const tools =\n active.guarded && host && !active.trustedHosts.some((t) => hostMatchesDomain(host, t))\n ? active.guarded\n : active.trusted;\n const result = await tools.run(name, input);\n // The browser refused a navigation the tool did not make itself (driver.ts).\n // A redirect may have shown the page for a moment, so nothing read is kept.\n const refusal = active.driver.takeRefusal();\n if (refusal) {\n const text = `Error: Navigation refused. ${refusal}`;\n await this.log(name, redactInput(name, input), text);\n return { text, isError: true };\n }\n if (result.screenshotBase64)\n await this.saveFrame(Buffer.from(result.screenshotBase64, 'base64'));\n await this.log(name, redactInput(name, input), result.text);\n const image =\n result.screenshotBase64 && (name === 'screenshot' || active.everyScreenshot)\n ? result.screenshotBase64\n : undefined;\n return { text: result.text, ...(image ? { image } : {}), isError: Boolean(result.error) };\n }\n\n async evaluateCheck(input: {\n check: string;\n status: Verdict;\n evidence: string;\n locator?: Locator;\n }): Promise<string> {\n const active = this.requireActive();\n if (await this.stopRequested(active)) throw new Error(STOPPED);\n const { run, checklist } = active;\n const check = checklist.checks.find((c) => c.key === input.check);\n if (!check) {\n throw new Error(\n `No check \"${input.check}\" in this run. The keys are: ${checklist.checks.map((c) => c.key).join(', ')}.`\n );\n }\n const earlier = run.results.find((r) => r.check === check.key);\n if (earlier) {\n throw new Error(\n `[${check.key}] already has a verdict (${earlier.status}). Each check gets one.`\n );\n }\n if (\n check.type === 'informational' &&\n (input.status === 'passed' || input.status === 'failed')\n ) {\n throw new Error(\n `[${check.key}] is informational: record what you observed with status \"informational\".`\n );\n }\n if (check.type !== 'informational' && input.status === 'informational') {\n throw new Error(\n `[${check.key}] is scored: judge it \"passed\" or \"failed\", or \"blocked\" if you could not reach it.`\n );\n }\n\n const page = await active.driver.context.activePage();\n const query = cleanLocator(input.locator);\n const found = page ? await focusEvidence(page, query) : null;\n if (input.status === 'passed' && !found) {\n throw new Error(\n query.text || query.selector\n ? `Not recorded: the locator ${JSON.stringify(query.text ?? query.selector)} is not visible on the current page. Go back to where you saw the evidence, or cite text that is on screen now.`\n : 'Not recorded: a passed verdict needs a locator, {\"text\": \"...\"} visible on the page or {\"selector\": \"...\"}.'\n );\n }\n let screenshot: string | undefined;\n if (page) {\n try {\n const png = await page.screenshot({ type: 'png', timeout: 10_000 });\n screenshot = `shots/${check.key}.png`;\n await writeFileAtomic(join(active.dir, screenshot), png);\n await this.saveFrame(png);\n } catch {\n screenshot = undefined;\n }\n }\n run.results.push({\n check: check.key,\n status: input.status === 'blocked' ? 'error' : input.status,\n evidence: input.evidence,\n ...(query.text || query.selector\n ? {\n locator: {\n ...(query.text ? { text: query.text } : {}),\n ...(query.selector ? { selector: query.selector } : {}),\n },\n }\n : {}),\n ...(screenshot ? { screenshot } : {}),\n at: now(),\n });\n\n // Settle what this verdict decides: a failed stop check skips every check\n // left, and an unmet run condition skips its check and the chain after it.\n const statuses = new Map(run.results.map((r) => [r.check, r.status]));\n const halted = resolveHaltedChecks({\n checks: checklist.checks.map((c) => ({\n id: c.key,\n description: c.description,\n onFailure: c.on_failure,\n })),\n statuses,\n });\n const skipped = halted.length ? halted : this.conditionSkips(statuses);\n for (const s of skipped) {\n run.results.push({ check: s.checkId, status: 'skipped', evidence: s.reason, at: now() });\n }\n await this.save();\n const recorded = `Recorded [${check.key}] as ${input.status}.`;\n await this.log('evaluate_check', input, recorded);\n const notes = skipped.map((s) => `[${s.checkId}] ${s.reason}`);\n if (halted.length) notes.push('The run has stopped.');\n return [recorded, ...notes, this.nextStep()].join('\\n');\n }\n\n async finishRun(input: { summary: string }): Promise<string> {\n const active = this.requireActive();\n const stopped = await this.stopRequested(active);\n const { run, checklist } = active;\n for (const s of this.conditionSkips(new Map(run.results.map((r) => [r.check, r.status])))) {\n run.results.push({ check: s.checkId, status: 'skipped', evidence: s.reason, at: now() });\n }\n for (const check of this.pending()) {\n run.results.push({\n check: check.key,\n status: 'error',\n evidence: NOT_JUDGED,\n at: now(),\n });\n }\n const score = scoreLocalRun(checklist, run.results);\n run.score = {\n score: score.score,\n unit: score.scoreUnit,\n outcome: score.outcome,\n pass_threshold: score.passThreshold,\n grade: score.grade,\n };\n run.summary = input.summary;\n run.status = stopped ? 'stopped' : 'completed';\n run.finished_at = now();\n await this.save();\n await this.log('finish_run', input, `Finished: ${score.outcome}`);\n const viewer = await this.openUi(`/runs/${run.id}`);\n await this.endBrowser();\n\n const line = (r: Result) => `- [${r.check}] ${r.evidence}`;\n const failed = run.results.filter((r) => r.status === 'failed');\n const blocked = run.results.filter((r) => r.status === 'error');\n const counts = describeRunScore(score, {\n passedCount: run.results.filter((r) => r.status === 'passed').length,\n totalCount: checklist.checks.length,\n });\n return [\n `Run ${run.id} ${run.status}: ${score.outcome}. ${counts}. Self-judged: the actor recorded its own verdicts.`,\n failed.length ? `Failed:\\n${failed.map(line).join('\\n')}` : '',\n blocked.length ? `Not judged:\\n${blocked.map(line).join('\\n')}` : '',\n `Report: ${viewer}`,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n }\n\n /**\n * start_run while this process still has a run open, as when the person\n * interrupts the actor: resume carries on with it in the same browser.\n */\n private async resumeActive(input: { checklist: string; resume?: boolean }): Promise<string> {\n const active = this.requireActive();\n const { run } = active;\n if (!input.resume || run.checklist !== input.checklist) {\n throw new Error(\n `Run ${run.id} of ${run.checklist} is still going: call start_run with checklist \"${run.checklist}\" and resume to carry on with it, or finish_run to end it.`\n );\n }\n active.stopped = false;\n run.status = 'running';\n await this.clearStop(run.id);\n await this.save();\n await this.log('start_run', input, 'Resumed');\n const viewer = await this.openUi(`/runs/${run.id}`);\n return [\n `Run ${run.id} resumed. Watch it or stop it at ${viewer}`,\n buildBrief({\n actor: active.actor,\n checklist: active.checklist,\n url: run.url,\n checks: this.pending(),\n resumed: true,\n }),\n 'The browser is still open where the run left off: read the page before acting.',\n this.nextStep(),\n ].join('\\n\\n');\n }\n\n /** On shutdown: an unfinished run is marked interrupted, so it can be resumed. */\n async close(): Promise<void> {\n if (this.active) {\n this.active.run.status = this.active.stopped ? 'stopped' : 'interrupted';\n await this.save().catch(() => {});\n await this.endBrowser();\n }\n await this.ui?.close();\n this.ui = null;\n }\n\n private requireActive(): Active {\n if (!this.active) throw new Error('No run is going: call start_run first.');\n return this.active;\n }\n\n /** Checks with no result yet, in run order. */\n private pending(): Check[] {\n const active = this.requireActive();\n const judged = new Set(active.run.results.map((r) => r.check));\n return active.checklist.checks.filter((c) => !judged.has(c.key));\n }\n\n private conditionSkips(statuses: Map<string, Result['status']>) {\n return resolveSkippedChecks({\n checks: this.requireActive().checklist.checks.map((c) => ({\n id: c.key,\n description: c.description,\n runCondition: c.run_condition,\n })),\n statuses,\n });\n }\n\n private nextStep(): string {\n const pending = this.pending();\n if (pending.length === 0)\n return 'Every check has a verdict: call finish_run with a short summary.';\n const next = pending[0]!;\n return `Next: [${next.key}] ${checkLabel(next)} ${next.description} (${pending.length} left).`;\n }\n\n /** The viewer's Stop writes the run id to `.live/stop`; the run notices on its next call. */\n private async stopRequested(active: Active): Promise<boolean> {\n if (active.stopped) return true;\n const flag = await readFile(join(this.root, '.live', 'stop'), 'utf8').catch(() => '');\n if (flag.trim() !== active.run.id) return false;\n active.stopped = true;\n active.run.status = 'stopped';\n await this.save();\n await this.log('stop', {}, 'Stopped from the viewer');\n return true;\n }\n\n /** Runs left `running` by a process that is gone, or by this one before a restart. */\n private async markInterrupted(): Promise<void> {\n for (const entry of await readRuns(this.root)) {\n if (!('run' in entry) || entry.run.status !== 'running') continue;\n if (entry.run.pid !== process.pid && isAlive(entry.run.pid)) continue;\n await writeJsonAtomic(join(this.root, 'runs', entry.id, 'run.json'), {\n ...entry.run,\n status: 'interrupted',\n });\n }\n }\n\n private async endBrowser(): Promise<void> {\n const active = this.active;\n if (!active) return;\n this.active = null;\n await active.driver.close().catch(() => {});\n if (active.tempProfile) await rm(active.tempProfile, { recursive: true, force: true });\n await this.clearStop(active.run.id);\n }\n\n private async clearStop(id: string): Promise<void> {\n const stopFile = join(this.root, '.live', 'stop');\n const flag = await readFile(stopFile, 'utf8').catch(() => '');\n if (flag.trim() === id) await rm(stopFile, { force: true });\n }\n\n private async save(): Promise<void> {\n const active = this.requireActive();\n await writeJsonAtomic(join(active.dir, 'run.json'), active.run);\n }\n\n private async log(tool: string, input: unknown, text: string): Promise<void> {\n const active = this.requireActive();\n await appendJsonl(join(active.dir, 'activity.jsonl'), {\n at: now(),\n tool,\n input,\n text: text.length > LOG_TEXT_CHARS ? `${text.slice(0, LOG_TEXT_CHARS)}...` : text,\n });\n }\n\n /** The viewer's live frame, best effort. ponytail: one per project, so two sessions running at once share it. */\n private async saveFrame(png: Uint8Array): Promise<void> {\n await writeFileAtomic(join(this.root, '.live', 'latest.png'), png).catch(() => {});\n }\n}\n\n/** A finished run's verdict, by the same code that scores a cloud run. */\nexport function scoreLocalRun(checklist: Checklist, results: Result[]) {\n const scoring = newChecklistScoring({\n scoreType: checklist.score_type,\n scoreUnit: checklist.score_unit,\n passThreshold: checklist.pass_threshold,\n scoreBands: checklist.score_bands,\n checks: checklist.checks.map((c) => ({ checkType: c.type, weight: c.weight })),\n });\n return scoreChecklistRun({\n checks: checklist.checks.map((c) => ({\n id: c.key,\n checkType: c.type,\n weight: c.weight ?? 1,\n onFailure: c.on_failure,\n })),\n results: results.map((r) => ({ checkId: r.check, status: r.status })),\n passThreshold: scoring.pass_threshold,\n scoreBands: scoring.score_bands,\n scoreType: scoring.score_type,\n scoreUnit: scoring.score_unit,\n });\n}\n","{\n \"name\": \"stuntdouble\",\n \"version\": \"0.1.2\",\n \"description\": \"Stunt Double Local: run Stunt Double checklists in a local Chrome from your own coding agent session, with the runs kept as files and a local viewer\",\n \"keywords\": [\n \"stunt-double\",\n \"testing\",\n \"browser\",\n \"checklist\",\n \"mcp\",\n \"playwright\",\n \"agents\"\n ],\n \"license\": \"Apache-2.0\",\n \"author\": \"Turnout Labs Ltd\",\n \"homepage\": \"https://github.com/stunt-double/stuntkit/tree/main/packages/local#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/stunt-double/stuntkit/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stunt-double/stuntkit.git\",\n \"directory\": \"packages/local\"\n },\n \"type\": \"module\",\n \"bin\": {\n \"stuntdouble\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"type-check\": \"tsc -p tsconfig.json\",\n \"test\": \"node --conditions=@stunt-double/source --test \\\"test/**/*.test.ts\\\"\",\n \"prepack\": \"pnpm run build\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.32.2\",\n \"playwright-core\": \"^1.64.1\",\n \"zod\": \"^4.6.7\"\n },\n \"devDependencies\": {\n \"@stunt-double/browser-toolset\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22.18\"\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org\",\n \"provenance\": true\n }\n}\n","// What the actor is told at the start of a run: who it is, what to do, the\n// checks to judge, and how a local run works. The agent reading it is the\n// person's own session, so this is the whole of the actor's instructions.\n\nimport type { Actor, Check, Checklist } from './files.ts';\n\nconst CONDITION_LABELS: Record<Check['run_condition'], string> = {\n always: 'always',\n if_previous_passed: 'only if the check before it passed',\n if_previous_failed: 'only if the check before it failed',\n};\n\n/** `(weighted x2, always)`: how a check counts and when it runs. */\nexport function checkLabel(check: Check): string {\n const kind =\n check.type === 'informational'\n ? 'informational, record what you observe'\n : check.type === 'weighted'\n ? `weighted x${check.weight ?? 1}${(check.weight ?? 1) < 0 ? ', a penalty' : ''}`\n : 'pass/fail';\n const parts = [kind, CONDITION_LABELS[check.run_condition]];\n if (check.on_failure === 'stop') parts.push('the run stops if it fails');\n return `(${parts.join(', ')})`;\n}\n\nexport function checkLine(check: Check): string {\n return `- [${check.key}] ${checkLabel(check)} ${check.description}`;\n}\n\nexport function buildBrief(input: {\n actor: Actor;\n checklist: Checklist;\n url: string;\n /** The checks still to judge: all of them, or on a resume the ones without a verdict. */\n checks: Check[];\n resumed: boolean;\n}): string {\n const { actor, checklist, url, checks, resumed } = input;\n const persona = [\n `You are ${actor.name}${actor.description ? `: ${actor.description}` : ''}.`,\n actor.system_prompt ?? '',\n ]\n .filter(Boolean)\n .join('\\n');\n const knowledge = actor.knowledge.length\n ? [\n 'What you know:',\n ...actor.knowledge.map((k) => `### ${k.title} (${k.kind})\\n${k.content}`),\n ].join('\\n')\n : '';\n return [\n persona,\n knowledge,\n `Your task: ${checklist.instructions}`,\n `Start at ${url}${resumed ? ' (this run is resuming: earlier verdicts stand, judge only the checks below)' : ''}.`,\n `Checks to judge:\\n${checks.map(checkLine).join('\\n')}`,\n [\n 'How to work:',\n '- Stay in persona: act and judge as this person would, not as a tester.',\n '- Read the page with read_page and find. Ask for a screenshot only when how something looks is the question.',\n '- Judge each check with evaluate_check as soon as you have observed it, while the evidence is on screen.',\n '- A passed verdict needs a locator: text visible on the page, or a CSS selector, that shows the evidence. A failure may describe something missing.',\n '- Cite only what you saw in this run. Never pass a check on what a page probably does.',\n '- When something stops you (a sign-in wall, a broken page, a CAPTCHA), judge the checks you cannot reach as \"blocked\" and say what stopped you.',\n '- Always end with finish_run and a one or two sentence summary, even when blocked or stopped.',\n ].join('\\n'),\n ]\n .filter(Boolean)\n .join('\\n\\n');\n}\n","// A `BrowserDriver` over Playwright, adapted from the browser toolset's example\n// (`packages/browser-toolset/examples/src/playwright-driver.ts`), as its header\n// invites. The changes: playwright-core instead of playwright, a persistent\n// context on a profile directory, so a run gets the person's own Chrome with a\n// fresh profile (or the actor's kept one), never their everyday profile, and the\n// run's rule on private addresses applied to every navigation, not only the\n// navigate tool's.\n\nimport type {\n BrowserDriver,\n DriverActResult,\n DriverContext,\n DriverDomainPolicy,\n DriverLoadState,\n DriverLocator,\n DriverNavigateOptions,\n DriverPage,\n DriverScreenshotOptions,\n DriverWaitForSelectorOptions,\n} from '@stunt-double/browser-toolset/driver';\nimport { hostMatchesDomain } from '@stunt-double/browser-toolset/safety';\nimport { chromium, type BrowserContext, type Page, type Request } from 'playwright-core';\n\n/** Playwright's `Page` objects are stable, so a WeakMap gives each tab a lasting id. */\nconst pageIds = new WeakMap<Page, string>();\nlet pageSeq = 0;\n\nfunction idOf(page: Page): string {\n let id = pageIds.get(page);\n if (!id) {\n pageSeq += 1;\n id = `pw-${pageSeq}`;\n pageIds.set(page, id);\n }\n return id;\n}\n\nclass PlaywrightPage implements DriverPage {\n readonly id: string;\n readonly raw: Page;\n\n constructor(page: Page) {\n this.raw = page;\n this.id = idOf(page);\n }\n\n goto(url: string, options: DriverNavigateOptions = {}) {\n return this.raw.goto(url, {\n ...(options.waitUntil ? { waitUntil: options.waitUntil } : {}),\n ...(options.timeoutMs ? { timeout: options.timeoutMs } : {}),\n });\n }\n goBack() {\n return this.raw.goBack();\n }\n goForward() {\n return this.raw.goForward();\n }\n screenshot(options: DriverScreenshotOptions = {}): Promise<Buffer> {\n return this.raw.screenshot(options);\n }\n evaluate<R = unknown, Arg = unknown>(\n expression: string | ((arg: Arg) => R | Promise<R>),\n arg?: Arg\n ): Promise<R> {\n if (typeof expression === 'string') return this.raw.evaluate(expression) as Promise<R>;\n return this.raw.evaluate(expression as (arg: unknown) => R | Promise<R>, arg) as Promise<R>;\n }\n url() {\n return this.raw.url();\n }\n title() {\n return this.raw.title();\n }\n setViewportSize(width: number, height: number) {\n return this.raw.setViewportSize({ width, height });\n }\n waitForTimeout(ms: number) {\n return this.raw.waitForTimeout(ms);\n }\n waitForSelector(selector: string, options: DriverWaitForSelectorOptions = {}) {\n return this.raw.waitForSelector(selector, options);\n }\n waitForLoadState(state: DriverLoadState, timeoutMs?: number) {\n return this.raw.waitForLoadState(state, timeoutMs ? { timeout: timeoutMs } : undefined);\n }\n locator(selector: string): DriverLocator {\n return this.raw.locator(selector).first();\n }\n click(x: number, y: number, options: Parameters<DriverPage['click']>[2] = {}) {\n return this.raw.mouse.click(x, y, options);\n }\n hover(x: number, y: number) {\n return this.raw.mouse.move(x, y);\n }\n async scroll(x: number, y: number, deltaX: number, deltaY: number) {\n await this.raw.mouse.move(x, y);\n await this.raw.mouse.wheel(deltaX, deltaY);\n }\n async dragAndDrop(fromX: number, fromY: number, toX: number, toY: number) {\n await this.raw.mouse.move(fromX, fromY);\n await this.raw.mouse.down();\n await this.raw.mouse.move(toX, toY, { steps: 10 });\n await this.raw.mouse.up();\n }\n type(text: string) {\n return this.raw.keyboard.type(text);\n }\n keyPress(key: string) {\n return this.raw.keyboard.press(key);\n }\n close() {\n return this.raw.close();\n }\n}\n\nclass PlaywrightContext implements DriverContext {\n private readonly context: BrowserContext;\n private active: Page | undefined;\n private policy: DriverDomainPolicy = {};\n private routed = false;\n\n constructor(context: BrowserContext) {\n this.context = context;\n // Popups and `target=\"_blank\"` links become the active tab, as in a browser.\n context.on('page', (page) => {\n this.active = page;\n });\n }\n\n activePage(): DriverPage | undefined {\n const open = this.context.pages();\n if (!this.active || this.active.isClosed()) this.active = open[open.length - 1];\n return this.active ? new PlaywrightPage(this.active) : undefined;\n }\n pages(): DriverPage[] {\n return this.context.pages().map((p) => new PlaywrightPage(p));\n }\n async newPage(url?: string): Promise<DriverPage> {\n const page = await this.context.newPage();\n this.active = page;\n if (url) await page.goto(url);\n return new PlaywrightPage(page);\n }\n async setActivePage(page: DriverPage): Promise<void> {\n const raw = this.context.pages().find((p) => idOf(p) === page.id);\n if (!raw) throw new Error(`No open tab with id ${page.id}`);\n this.active = raw;\n await raw.bringToFront();\n }\n clearCookies() {\n return this.context.clearCookies();\n }\n async addInitScript(source: string): Promise<void> {\n await this.context.addInitScript(source);\n }\n /** Abort requests to blocked hosts, or to anything outside the allow list. */\n async setDomainPolicy(policy: DriverDomainPolicy): Promise<void> {\n this.policy = policy;\n if (this.routed) return;\n this.routed = true;\n await this.context.route('**/*', (route) => {\n let host = '';\n try {\n host = new URL(route.request().url()).hostname;\n } catch {\n return route.continue();\n }\n const { allowedDomains, blockedDomains } = this.policy;\n const blocked =\n (blockedDomains ?? []).some((d) => hostMatchesDomain(host, d)) ||\n (allowedDomains !== undefined &&\n allowedDomains.length > 0 &&\n !allowedDomains.some((d) => hostMatchesDomain(host, d)));\n return blocked ? route.abort('blockedbyclient') : route.continue();\n });\n }\n}\n\nexport type LaunchOptions = {\n profileDir: string;\n headless: boolean;\n viewport: { width: number; height: number };\n touch: boolean;\n locale?: string;\n /** Why the browser may not open `href`, or null when it may. */\n refuseNavigation: (href: string) => string | null;\n};\n\nexport type LocalDriver = BrowserDriver & {\n /** Why the browser refused to open a page since the last call, if it did; reading it resets it. */\n takeRefusal(): string | null;\n};\n\nexport class PlaywrightDriver implements LocalDriver {\n readonly context: PlaywrightContext;\n private readonly browserContext: BrowserContext;\n private refusal: string | null = null;\n\n private constructor(context: BrowserContext) {\n this.browserContext = context;\n this.context = new PlaywrightContext(context);\n }\n\n /**\n * The installed Google Chrome, or Playwright's own Chromium when Chrome is\n * missing and `npx playwright install chromium` has been run.\n */\n static async launch(options: LaunchOptions): Promise<PlaywrightDriver> {\n const settings = {\n headless: options.headless,\n viewport: options.viewport,\n hasTouch: options.touch,\n ...(options.locale ? { locale: options.locale } : {}),\n };\n let context: BrowserContext;\n try {\n context = await chromium.launchPersistentContext(options.profileDir, {\n ...settings,\n channel: 'chrome',\n });\n } catch (chromeError) {\n try {\n context = await chromium.launchPersistentContext(options.profileDir, settings);\n } catch {\n throw new Error(\n `Could not start Chrome (${(chromeError as Error).message.split('\\n')[0]}). Install Google Chrome, or run \\`npx playwright install chromium\\`.`\n );\n }\n }\n const driver = new PlaywrightDriver(context);\n // The navigate tool checks its own URL, but links, scripts, popups and frames\n // navigate without it, so every navigation is checked here too. A refusal in\n // a tab's main frame is kept for the next tool result to explain.\n const refuse = (request: Request) => {\n const reason = request.isNavigationRequest() ? options.refuseNavigation(request.url()) : null;\n if (reason && !request.frame().parentFrame()) driver.refusal = reason;\n return reason;\n };\n await context.route('**/*', (route) =>\n refuse(route.request()) ? route.abort('blockedbyclient') : route.fallback()\n );\n // A route never sees a redirect's later hops: Playwright sends them on before\n // any handler runs. So a frame redirected somewhere refused goes to about:blank.\n context.on('request', (request) => {\n if (request.redirectedFrom() && refuse(request)) {\n request\n .frame()\n .goto('about:blank')\n .catch(() => {});\n }\n });\n if (context.pages().length === 0) await driver.context.newPage();\n return driver;\n }\n\n takeRefusal(): string | null {\n const refusal = this.refusal;\n this.refusal = null;\n return refusal;\n }\n\n /** Instruction-style actions need a model of their own. Nothing here calls them. */\n async act(): Promise<DriverActResult> {\n throw new Error('act is not supported by this driver: use the browser toolset instead');\n }\n async extract(): Promise<unknown> {\n throw new Error('extract is not supported by this driver: use the browser toolset instead');\n }\n close() {\n return this.browserContext.close();\n }\n}\n","// A verdict's evidence on the page: found, scrolled into view and captured.\n//\n// Ported from the cloud runner's evidence focus: one `page.evaluate` that finds\n// the region a check was judged on (a selector first, then the smallest\n// rendered element whose text contains the cited fragment) and scrolls it to\n// the middle of the viewport, so a check about the footer is illustrated by the\n// footer rather than by wherever the actor stopped.\n//\n// Locally it also does the cloud's judge's least job without a model: a passed\n// verdict has to cite something that is on the page right now. A failure may\n// cite an absence, so for a failure the search is only for the screenshot.\n\nimport type { DriverPage } from '@stunt-double/browser-toolset/driver';\n\nimport type { Locator } from './files.ts';\n\n/** Matching is a case-insensitive substring test, so a long quote adds nothing. */\nexport const MAX_LOCATOR_TEXT = 120;\nconst MAX_LOCATOR_SELECTOR = 400;\n/** Lets lazy images and sticky headers settle after the scroll. */\nconst SETTLE_MS = 350;\n\ntype Query = { selector: string | null; text: string | null };\nexport type FoundVia = 'selector' | 'text' | null;\n\nexport function cleanLocator(locator: Locator | undefined): Query {\n const clean = (value: string | undefined) => value?.replace(/\\s+/g, ' ').trim() || null;\n const selector = clean(locator?.selector);\n return {\n selector: selector && selector.length <= MAX_LOCATOR_SELECTOR ? selector : null,\n text: clean(locator?.text)?.slice(0, MAX_LOCATOR_TEXT) ?? null,\n };\n}\n\n/**\n * Find the evidence and scroll it to the centre. Never throws: a page that is\n * navigating or a selector the page rejects is a miss, not a lost verdict.\n */\nexport async function focusEvidence(page: DriverPage, query: Query): Promise<FoundVia> {\n if (!query.selector && !query.text) return null;\n const via = await page.evaluate(findAndScroll, query).catch(() => null);\n if (via) await page.waitForTimeout(SETTLE_MS).catch(() => {});\n return via;\n}\n\n/**\n * Runs in the page, serialised with `toString()`, so it closes over nothing but\n * its argument. Text is matched against what a person reads on screen: rendered\n * text (`innerText`, which leaves out hidden elements and breaks lines at `<br>`\n * and blocks), open shadow roots included, plus the labels a control shows\n * without text content (placeholder, aria-label, alt, title, an input's value).\n * ponytail: frames are not searched, and text faded to opacity 0 inside a\n * visible parent still counts; search each frame through Playwright if a\n * check's evidence lives in one.\n */\nfunction findAndScroll(query: Query): FoundVia {\n const centre: ScrollIntoViewOptions = { block: 'center', inline: 'center' };\n const shown = (el: Element) => {\n const rect = el.getBoundingClientRect();\n return (\n rect.width > 0 &&\n rect.height > 0 &&\n el.checkVisibility({ visibilityProperty: true, opacityProperty: true })\n );\n };\n const norm = (s: string | null | undefined) => (s ?? '').replace(/\\s+/g, ' ').toLowerCase();\n const wanted = query.text ? norm(query.text) : null;\n const labelled = (el: Element, fragment: string) => {\n const labels = ['placeholder', 'aria-label', 'alt', 'title'].map((a) => el.getAttribute(a));\n if ('value' in el && typeof el.value === 'string') labels.push(el.value);\n return labels.some((l) => norm(l).includes(fragment));\n };\n const rendered = (el: Element) => norm(el instanceof HTMLElement ? el.innerText : '');\n\n if (query.selector) {\n let matches: Element[] = [];\n try {\n matches = Array.from(document.querySelectorAll(query.selector));\n } catch {\n // A selector the page cannot parse is a miss.\n }\n // The page itself is not evidence of anything, and with text given the\n // element has to show it.\n const el = matches.find(\n (m) =>\n m !== document.documentElement &&\n m !== document.body &&\n m.tagName !== 'MAIN' &&\n shown(m) &&\n (!wanted || labelled(m, wanted) || rendered(m).includes(wanted))\n );\n if (el) {\n el.scrollIntoView(centre);\n return 'selector';\n }\n }\n if (!wanted) return null;\n\n const elements: Element[] = [];\n const collect = (root: ParentNode) => {\n for (const el of Array.from(root.querySelectorAll('*'))) {\n elements.push(el);\n if (el.shadowRoot) collect(el.shadowRoot);\n }\n };\n collect(document.body);\n let best: Element | null = null;\n let bestLength = Number.POSITIVE_INFINITY;\n for (const el of elements) {\n if (['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName)) continue;\n // The smallest element containing the fragment is the one that shows it:\n // every ancestor contains it too. A label belongs to the element itself.\n let length = 0;\n if (!labelled(el, wanted)) {\n const text = rendered(el);\n length = text.includes(wanted) ? text.length : Number.POSITIVE_INFINITY;\n }\n if (length >= bestLength || !shown(el)) continue;\n best = el;\n bestLength = length;\n }\n if (!best) return null;\n best.scrollIntoView(centre);\n return 'text';\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAwLM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAgG7B,mBAAmB;AAAA,0BACtB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["text","text","text","image","page","text","text","SETTLE_MS","text","text","image","text","image"]}
@@ -1,10 +0,0 @@
1
- import {
2
- FIRST_PORT,
3
- startUi
4
- } from "./chunk-VWTYG7G4.js";
5
- import "./chunk-JAB46QV7.js";
6
- export {
7
- FIRST_PORT,
8
- startUi
9
- };
10
- //# sourceMappingURL=ui-server-AYIR7DTN.js.map