diff --git a/.changeset/config.json b/.changeset/config.json index b6abd7a..391db2e 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["@stunt-double/browser-toolset-examples", "@stunt-double/wao-examples"] + "ignore": ["@stuntkit/browser-toolset-examples", "@stunt-double/wao-examples"] } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6f384f7..1ee0e90 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "StuntKit", "source": "./skills", - "description": "Skills and agents for @stunt-double/browser-toolset, wao, localeizer and icons: when to reach for each package, how to wire it up, the mistakes to avoid, and agents that build and review browser agents" + "description": "Skills and agents for @stuntkit/browser-toolset, wao, localeizer and icons: when to reach for each package, how to wire it up, the mistakes to avoid, and agents that build and review browser agents" } ] } diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 26afbcb..b66fd8c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -10,7 +10,7 @@ body: attributes: label: Package options: - - '@stunt-double/browser-toolset' + - '@stuntkit/browser-toolset' - '@stunt-double/icons' - '@stunt-double/index-methodology' - '@stunt-double/localeizer' diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 09c9335..3aecf3b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -7,7 +7,7 @@ body: attributes: label: Package options: - - '@stunt-double/browser-toolset' + - '@stuntkit/browser-toolset' - '@stunt-double/icons' - '@stunt-double/index-methodology' - '@stunt-double/localeizer' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 88cad10..29a6672 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ ## Packages touched -- [ ] `@stunt-double/browser-toolset` +- [ ] `@stuntkit/browser-toolset` - [ ] `@stunt-double/icons` - [ ] `@stunt-double/index-methodology` - [ ] `@stunt-double/localeizer` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f866262..ca83a0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ pnpm format # Prettier pnpm build # tsup: ESM plus .d.ts into each package's dist/ ``` -Run one package with `pnpm --filter @stunt-double/browser-toolset test`. +Run one package with `pnpm --filter @stuntkit/browser-toolset test`. Conventions: @@ -82,9 +82,9 @@ Then, in the monorepo, override the package with a link to your checkout. pnpm 1 ```yaml # stuntdouble/pnpm-workspace.yaml (do not commit) overrides: - '@stunt-double/browser-toolset': link:../stuntkit/packages/browser-toolset + '@stuntkit/browser-toolset': link:../stuntkit/packages/browser-toolset ``` -Run `pnpm install` in the monorepo. The link resolves through the package's `exports`, so either build here once and keep it fresh with `pnpm --filter @stunt-double/browser-toolset exec tsup --watch`, or, in a monorepo project whose TypeScript and runtime can read source, add the `@stunt-double/source` condition (`customConditions` in its `tsconfig.json`, `--conditions=@stunt-double/source` for Node) to use the TypeScript source directly. +Run `pnpm install` in the monorepo. The link resolves through the package's `exports`, so either build here once and keep it fresh with `pnpm --filter @stuntkit/browser-toolset exec tsup --watch`, or, in a monorepo project whose TypeScript and runtime can read source, add the `@stunt-double/source` condition (`customConditions` in its `tsconfig.json`, `--conditions=@stunt-double/source` for Node) to use the TypeScript source directly. Remove the override and run `pnpm install` again before committing in the monorepo; once the change is released, bump the version there instead. diff --git a/README.md b/README.md index 08feeb4..8ceead4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Open source building blocks for AI agents that use the web. Extracted from [Stun | Package | What | | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| [`@stunt-double/browser-toolset`](./packages/browser-toolset) | Browser tools for AI agents over a provider-neutral `BrowserDriver`, with opt-in guards that refuse payments, sign-ups and secrets | +| [`@stuntkit/browser-toolset`](./packages/browser-toolset) | Browser tools for AI agents over a provider-neutral `BrowserDriver`, with opt-in guards that refuse payments, sign-ups and secrets | | [`@stunt-double/icons`](./packages/icons) | The Continuity icon pack: 235 icons as geometry, SVG files and React components | | [`@stunt-double/index-methodology`](./packages/index-methodology) | How the Stunt Double Index scores a site for agents: categories, weights, blend and curve, every task and rubric, and the grader's prompt | | [`@stunt-double/localeizer`](./packages/localeizer) | Whole-word American, British and Canadian spelling localisation | diff --git a/SECURITY.md b/SECURITY.md index f6f5930..431d3c0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,6 +15,6 @@ Fixes land in the latest published minor version of each package. Pre-1.0 packag ## Scope -In scope: the published packages in this repository, including bypasses of `@stunt-double/browser-toolset`'s safety guards (`BrowserSafetyOptions`) and navigation checks (`isPrivateHost`, `checkNavigation`) by content a page can control. +In scope: the published packages in this repository, including bypasses of `@stuntkit/browser-toolset`'s safety guards (`BrowserSafetyOptions`) and navigation checks (`isPrivateHost`, `checkNavigation`) by content a page can control. The guards read the DOM the page shows, and are documented as a strong default rather than a sandbox, so a report is most useful when it shows a realistic page getting past them. diff --git a/packages/browser-toolset/README.md b/packages/browser-toolset/README.md index a0fd989..3f3610f 100644 --- a/packages/browser-toolset/README.md +++ b/packages/browser-toolset/README.md @@ -1,4 +1,4 @@ -# @stunt-double/browser-toolset +# @stuntkit/browser-toolset Browser tools for AI agents, over a provider-neutral `BrowserDriver`. @@ -14,19 +14,19 @@ Extracted from [Stunt Double](https://stuntdouble.io), where it drives every age ## Install ```sh -pnpm add @stunt-double/browser-toolset -# or: npm install @stunt-double/browser-toolset +pnpm add @stuntkit/browser-toolset +# or: npm install @stuntkit/browser-toolset ``` Packages are published to [GitHub Packages](https://github.com/orgs/stunt-double/packages): add `@stunt-double:registry=https://npm.pkg.github.com` and a GitHub token with `read:packages` to your `.npmrc`. `@anthropic-ai/sdk` and `ai` are optional peer dependencies, used for types only; nothing imports them at runtime. Their types are referenced by these declarations: -| Import | Needs the types of | -| ------------------------------------------------------------------------- | ---------------------------- | -| `@stunt-double/browser-toolset`, `@stunt-double/browser-toolset/executor` | `@anthropic-ai/sdk` | -| `@stunt-double/browser-toolset/prune` | `@anthropic-ai/sdk` and `ai` | -| `./direct-tools`, `./safety`, `./driver`, `./keys`, `./page-script` | Neither | +| Import | Needs the types of | +| ------------------------------------------------------------------- | ---------------------------- | +| `@stuntkit/browser-toolset`, `@stuntkit/browser-toolset/executor` | `@anthropic-ai/sdk` | +| `@stuntkit/browser-toolset/prune` | `@anthropic-ai/sdk` and `ai` | +| `./direct-tools`, `./safety`, `./driver`, `./keys`, `./page-script` | Neither | So a project on the direct tools alone, with no Anthropic SDK, imports from the subpaths (or sets `skipLibCheck`). @@ -43,7 +43,7 @@ import { BrowserToolsetExecutor, INDEX_SESSION_SAFETY, isBrowserToolsetCall, -} from '@stunt-double/browser-toolset'; +} from '@stuntkit/browser-toolset'; const client = new Anthropic(); const executor = new BrowserToolsetExecutor(driver, { safety: INDEX_SESSION_SAFETY }); @@ -69,7 +69,7 @@ for (;;) { ### Any model, on the direct tools ```ts -import { DIRECT_TOOL_SPECS, DirectBrowserTools } from '@stunt-double/browser-toolset'; +import { DIRECT_TOOL_SPECS, DirectBrowserTools } from '@stuntkit/browser-toolset'; const tools = new DirectBrowserTools(driver, { viewport: { width: 1280, height: 800 } }); @@ -89,7 +89,7 @@ Complete, type-checked loops are in [`examples/`](./examples): Playwright with t A line in a prompt saying "never pay or sign up" is a request the model may ignore, and an agent under test ignoring it is exactly the failure you cannot afford on a real site. So the guards live in the tools: when one refuses, the action never reaches the page, and the model gets an ordinary result (not an error) telling it what was refused and to report what it saw instead. The rest of its turn still runs. ```ts -import { INDEX_SESSION_SAFETY } from '@stunt-double/browser-toolset'; +import { INDEX_SESSION_SAFETY } from '@stuntkit/browser-toolset'; new BrowserToolsetExecutor(driver, { safety: INDEX_SESSION_SAFETY }); new DirectBrowserTools(driver, { viewport, safety: INDEX_SESSION_SAFETY }); @@ -164,15 +164,15 @@ Pages are read by a script evaluated in the page, which tags each element it rep Each subpath can be imported on its own; the root re-exports all of them except `./prune`. -| Import | What | -| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `@stunt-double/browser-toolset/executor` | `BrowserToolsetExecutor` (`runTurn`, `execute`), `BROWSER_TOOLSET`, `BROWSER_TOOLSET_NAME`, `HALT_TEXT`, `isBrowserToolsetCall`, `isPrivateHost`, `redactInput`, `sanitiseStateField`, `ToolsetError`, and the option and record types | -| `@stunt-double/browser-toolset/direct-tools` | `DirectBrowserTools`, `DIRECT_TOOL_SPECS`, `DIRECT_TOOL_NAMES`, `clampRead`, `targetOf`, `MAX_READ_CHARS`, `TOOL_TIMEOUT_MS`, `STEP_BUDGET_FINAL_NOTE`, `STEP_BUDGET_FALLBACK_OUTPUT` | -| `@stunt-double/browser-toolset/safety` | `BrowserSafetyOptions`, `INDEX_SESSION_SAFETY`, `paymentBlocklistFor`, `containsCardNumber`, `luhnValid`, the reason functions and patterns, `refusalText` | -| `@stunt-double/browser-toolset/driver` | The `BrowserDriver`, `DriverContext` and `DriverPage` interfaces, and `activePageOf` | -| `@stunt-double/browser-toolset/prune` | `pruneAnthropicImages`, `pruneAiSdkImages`, `isAiSdkImagePart`, `PRUNED_SCREENSHOT`: keep the newest screenshots in a transcript, replacing older ones with a placeholder | -| `@stunt-double/browser-toolset/keys` | `toPlaywrightChord`, `toPlaywrightKeySequence`: the toolset's xdotool key names to Playwright's | -| `@stunt-double/browser-toolset/page-script` | `buildPageCall` and the result types of the in-page script, for drivers or tools of your own | +| Import | What | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `@stuntkit/browser-toolset/executor` | `BrowserToolsetExecutor` (`runTurn`, `execute`), `BROWSER_TOOLSET`, `BROWSER_TOOLSET_NAME`, `HALT_TEXT`, `isBrowserToolsetCall`, `isPrivateHost`, `redactInput`, `sanitiseStateField`, `ToolsetError`, and the option and record types | +| `@stuntkit/browser-toolset/direct-tools` | `DirectBrowserTools`, `DIRECT_TOOL_SPECS`, `DIRECT_TOOL_NAMES`, `clampRead`, `targetOf`, `MAX_READ_CHARS`, `TOOL_TIMEOUT_MS`, `STEP_BUDGET_FINAL_NOTE`, `STEP_BUDGET_FALLBACK_OUTPUT` | +| `@stuntkit/browser-toolset/safety` | `BrowserSafetyOptions`, `INDEX_SESSION_SAFETY`, `paymentBlocklistFor`, `containsCardNumber`, `luhnValid`, the reason functions and patterns, `refusalText` | +| `@stuntkit/browser-toolset/driver` | The `BrowserDriver`, `DriverContext` and `DriverPage` interfaces, and `activePageOf` | +| `@stuntkit/browser-toolset/prune` | `pruneAnthropicImages`, `pruneAiSdkImages`, `isAiSdkImagePart`, `PRUNED_SCREENSHOT`: keep the newest screenshots in a transcript, replacing older ones with a placeholder | +| `@stuntkit/browser-toolset/keys` | `toPlaywrightChord`, `toPlaywrightKeySequence`: the toolset's xdotool key names to Playwright's | +| `@stuntkit/browser-toolset/page-script` | `buildPageCall` and the result types of the in-page script, for drivers or tools of your own | The pruners are pure: they return a new array and never mutate the messages they are given, so you can prune what you send while keeping the full history you store. diff --git a/packages/browser-toolset/examples/README.md b/packages/browser-toolset/examples/README.md index f97e71a..471635b 100644 --- a/packages/browser-toolset/examples/README.md +++ b/packages/browser-toolset/examples/README.md @@ -11,10 +11,10 @@ From the repository root: ```sh pnpm install -pnpm --filter @stunt-double/browser-toolset-examples exec playwright install chromium +pnpm --filter @stuntkit/browser-toolset-examples exec playwright install chromium export ANTHROPIC_API_KEY=... -pnpm --filter @stunt-double/browser-toolset-examples anthropic "What is the heading on https://example.com?" -pnpm --filter @stunt-double/browser-toolset-examples ai-sdk "What is the heading on https://example.com?" +pnpm --filter @stuntkit/browser-toolset-examples anthropic "What is the heading on https://example.com?" +pnpm --filter @stuntkit/browser-toolset-examples ai-sdk "What is the heading on https://example.com?" ``` The scripts run the TypeScript directly (Node 22.18 or later strips the types) against the package source, through the `@stunt-double/source` export condition, so there is no build step. CI type-checks the examples but does not run them. diff --git a/packages/browser-toolset/examples/package.json b/packages/browser-toolset/examples/package.json index e3d33e8..687ae3a 100644 --- a/packages/browser-toolset/examples/package.json +++ b/packages/browser-toolset/examples/package.json @@ -1,8 +1,8 @@ { - "name": "@stunt-double/browser-toolset-examples", + "name": "@stuntkit/browser-toolset-examples", "version": "0.0.0", "private": true, - "description": "Runnable examples for @stunt-double/browser-toolset", + "description": "Runnable examples for @stuntkit/browser-toolset", "type": "module", "scripts": { "type-check": "tsc -p tsconfig.json", @@ -12,7 +12,7 @@ "dependencies": { "@ai-sdk/anthropic": "^4.0.78", "@anthropic-ai/sdk": "0.133.0", - "@stunt-double/browser-toolset": "workspace:*", + "@stuntkit/browser-toolset": "workspace:*", "ai": "^7.0.137", "playwright": "^1.64.0", "zod": "^4.6.5" diff --git a/packages/browser-toolset/examples/src/ai-sdk-loop.ts b/packages/browser-toolset/examples/src/ai-sdk-loop.ts index 619691b..a6d49c0 100644 --- a/packages/browser-toolset/examples/src/ai-sdk-loop.ts +++ b/packages/browser-toolset/examples/src/ai-sdk-loop.ts @@ -10,8 +10,8 @@ import { DirectBrowserTools, type DirectToolResult, INDEX_SESSION_SAFETY, -} from '@stunt-double/browser-toolset'; -import { pruneAiSdkImages } from '@stunt-double/browser-toolset/prune'; +} from '@stuntkit/browser-toolset'; +import { pruneAiSdkImages } from '@stuntkit/browser-toolset/prune'; import { generateText, jsonSchema, type JSONSchema7, stepCountIs, tool, type ToolSet } from 'ai'; import { chromium } from 'playwright'; diff --git a/packages/browser-toolset/examples/src/anthropic-loop.ts b/packages/browser-toolset/examples/src/anthropic-loop.ts index 0766ba9..0a3db95 100644 --- a/packages/browser-toolset/examples/src/anthropic-loop.ts +++ b/packages/browser-toolset/examples/src/anthropic-loop.ts @@ -10,8 +10,8 @@ import { BrowserToolsetExecutor, INDEX_SESSION_SAFETY, isBrowserToolsetCall, -} from '@stunt-double/browser-toolset'; -import { pruneAnthropicImages } from '@stunt-double/browser-toolset/prune'; +} from '@stuntkit/browser-toolset'; +import { pruneAnthropicImages } from '@stuntkit/browser-toolset/prune'; import { chromium } from 'playwright'; import { PlaywrightDriver } from './playwright-driver.ts'; diff --git a/packages/browser-toolset/examples/src/playwright-driver.ts b/packages/browser-toolset/examples/src/playwright-driver.ts index c32bc5d..fac9cef 100644 --- a/packages/browser-toolset/examples/src/playwright-driver.ts +++ b/packages/browser-toolset/examples/src/playwright-driver.ts @@ -1,5 +1,5 @@ // A `BrowserDriver` over Playwright, enough to run every tool in -// `@stunt-double/browser-toolset`. Copy it into your project and adapt it: the +// `@stuntkit/browser-toolset`. Copy it into your project and adapt it: the // package deliberately does not depend on any browser library. import type { @@ -13,8 +13,8 @@ import type { DriverPage, DriverScreenshotOptions, DriverWaitForSelectorOptions, -} from '@stunt-double/browser-toolset/driver'; -import { hostMatchesDomain } from '@stunt-double/browser-toolset/safety'; +} from '@stuntkit/browser-toolset/driver'; +import { hostMatchesDomain } from '@stuntkit/browser-toolset/safety'; import type { Browser, BrowserContext, Page } from 'playwright'; /** Playwright's `Page` objects are stable, so a WeakMap gives each tab a lasting id. */ @@ -196,7 +196,7 @@ export class PlaywrightDriver implements BrowserDriver { /** * Instruction-style actions need a model of their own, which is what the - * toolset exists to avoid. Nothing in `@stunt-double/browser-toolset` calls these. + * toolset exists to avoid. Nothing in `@stuntkit/browser-toolset` calls these. */ async act(): Promise { throw new Error('act is not supported by this driver: use the browser toolset instead'); diff --git a/packages/browser-toolset/package.json b/packages/browser-toolset/package.json index 9f05ad2..7a111ed 100644 --- a/packages/browser-toolset/package.json +++ b/packages/browser-toolset/package.json @@ -1,5 +1,5 @@ { - "name": "@stunt-double/browser-toolset", + "name": "@stuntkit/browser-toolset", "version": "0.2.1", "description": "Browser tools for AI agents over a provider-neutral BrowserDriver: an executor for Anthropic's browser use toolset, provider-neutral function tools, and opt-in guards that refuse payments, sign-ups and secrets", "keywords": [ @@ -106,6 +106,7 @@ }, "publishConfig": { "access": "public", - "registry": "https://npm.pkg.github.com" + "registry": "https://registry.npmjs.org", + "provenance": true } } diff --git a/packages/index-methodology/src/grading.ts b/packages/index-methodology/src/grading.ts index 042656b..9627761 100644 --- a/packages/index-methodology/src/grading.ts +++ b/packages/index-methodology/src/grading.ts @@ -8,7 +8,7 @@ * The rule every agent arm is given about what a session must never do, and * that the site it is on does not give it orders. Sessions never pay or sign * up for real; the browser tools refuse those actions too (`INDEX_SESSION_SAFETY` - * in `@stunt-double/browser-toolset/safety`), and this rule is what lets an + * in `@stuntkit/browser-toolset/safety`), and this rule is what lets an * agent stop cleanly rather than find out by being refused. */ export const INDEX_SAFETY_RULE = @@ -16,7 +16,7 @@ export const INDEX_SAFETY_RULE = /** * The system prompt for a browser task, for arms that call tools through a - * tool-use loop. The tools are the `@stunt-double/browser-toolset` direct + * tool-use loop. The tools are the `@stuntkit/browser-toolset` direct * tools; the task prompt itself (`TASKS`) is the first user message. */ export const INDEX_AGENT_PROMPT = diff --git a/packages/local/README.md b/packages/local/README.md index eaeb8b2..de3c441 100644 --- a/packages/local/README.md +++ b/packages/local/README.md @@ -46,7 +46,7 @@ Needs Node 22.18 or later and Google Chrome (or Playwright's Chromium, from `npx | Tool | What it does | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `navigate`, `read_page`, `find`, `get_page_text`, `click`, `type`, `form_input`, `key`, `scroll`, `screenshot` | The direct tools from [`@stunt-double/browser-toolset`](../browser-toolset). Screenshots go back to the model only from `screenshot`, unless `model_screenshots` is `every_action`. | +| `navigate`, `read_page`, `find`, `get_page_text`, `click`, `type`, `form_input`, `key`, `scroll`, `screenshot` | The direct tools from [`@stuntkit/browser-toolset`](../browser-toolset). Screenshots go back to the model only from `screenshot`, unless `model_screenshots` is `every_action`. | | `start_run` | Validates the files (naming every field to fix), opens a fresh Chrome at the checklist's URL and returns the viewer link, the actor's brief and the checks. `resume` continues an interrupted run, or one still open in this session. | | `evaluate_check` | Records one verdict. A pass must cite text or a selector that is on the page now. Scrolls the evidence into view and saves `shots/.png`. | | `finish_run` | Settles the checks left, scores the run and closes the browser. | diff --git a/packages/local/package.json b/packages/local/package.json index 4028438..a2a49da 100644 --- a/packages/local/package.json +++ b/packages/local/package.json @@ -43,7 +43,7 @@ "zod": "^4.6.7" }, "devDependencies": { - "@stunt-double/browser-toolset": "workspace:*" + "@stuntkit/browser-toolset": "workspace:*" }, "engines": { "node": ">=22.18" diff --git a/packages/local/src/cloud.ts b/packages/local/src/cloud.ts index a7b7b82..ade18f3 100644 --- a/packages/local/src/cloud.ts +++ b/packages/local/src/cloud.ts @@ -15,7 +15,7 @@ import type { OAuthClientMetadata, OAuthTokens, } from '@modelcontextprotocol/sdk/shared/auth.js'; -import { isPrivateHost } from '@stunt-double/browser-toolset/executor'; +import { isPrivateHost } from '@stuntkit/browser-toolset/executor'; import { randomBytes } from 'node:crypto'; import { mkdir, readFile, rm } from 'node:fs/promises'; import { basename, join } from 'node:path'; diff --git a/packages/local/src/driver.ts b/packages/local/src/driver.ts index fdb0e7f..fc957cd 100644 --- a/packages/local/src/driver.ts +++ b/packages/local/src/driver.ts @@ -17,8 +17,8 @@ import type { DriverPage, DriverScreenshotOptions, DriverWaitForSelectorOptions, -} from '@stunt-double/browser-toolset/driver'; -import { hostMatchesDomain } from '@stunt-double/browser-toolset/safety'; +} from '@stuntkit/browser-toolset/driver'; +import { hostMatchesDomain } from '@stuntkit/browser-toolset/safety'; import { chromium, type BrowserContext, type Page, type Request } from 'playwright-core'; /** Playwright's `Page` objects are stable, so a WeakMap gives each tab a lasting id. */ diff --git a/packages/local/src/evidence.ts b/packages/local/src/evidence.ts index ade3970..32012a4 100644 --- a/packages/local/src/evidence.ts +++ b/packages/local/src/evidence.ts @@ -10,7 +10,7 @@ // verdict has to cite something that is on the page right now. A failure may // cite an absence, so for a failure the search is only for the screenshot. -import type { DriverPage } from '@stunt-double/browser-toolset/driver'; +import type { DriverPage } from '@stuntkit/browser-toolset/driver'; import type { Locator } from './files.ts'; diff --git a/packages/local/src/mcp.ts b/packages/local/src/mcp.ts index 3a10c39..9d5d69c 100644 --- a/packages/local/src/mcp.ts +++ b/packages/local/src/mcp.ts @@ -5,7 +5,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; -import { DIRECT_TOOL_SPECS } from '@stunt-double/browser-toolset/direct-tools'; +import { DIRECT_TOOL_SPECS } from '@stuntkit/browser-toolset/direct-tools'; import { dirname } from 'node:path'; import { z } from 'zod'; diff --git a/packages/local/src/run.ts b/packages/local/src/run.ts index 29610b2..bdd5734 100644 --- a/packages/local/src/run.ts +++ b/packages/local/src/run.ts @@ -10,9 +10,9 @@ import { DirectBrowserTools, type DirectBrowserToolsOptions, -} from '@stunt-double/browser-toolset/direct-tools'; -import { isPrivateHost, redactInput } from '@stunt-double/browser-toolset/executor'; -import { hostMatchesDomain, INDEX_SESSION_SAFETY } from '@stunt-double/browser-toolset/safety'; +} from '@stuntkit/browser-toolset/direct-tools'; +import { isPrivateHost, redactInput } from '@stuntkit/browser-toolset/executor'; +import { hostMatchesDomain, INDEX_SESSION_SAFETY } from '@stuntkit/browser-toolset/safety'; import { createHash } from 'node:crypto'; import { mkdir, mkdtemp, readdir, readFile, rm, stat } from 'node:fs/promises'; import { homedir, tmpdir } from 'node:os'; diff --git a/packages/local/src/runner.ts b/packages/local/src/runner.ts index 7aa8972..c813030 100644 --- a/packages/local/src/runner.ts +++ b/packages/local/src/runner.ts @@ -6,7 +6,7 @@ // Policy line: this is the only importer of model.ts. It is reached from the // Studio (studio.ts) and `stuntdouble run` (cli.ts), never from mcp.ts. -import { DIRECT_TOOL_SPECS } from '@stunt-double/browser-toolset/direct-tools'; +import { DIRECT_TOOL_SPECS } from '@stuntkit/browser-toolset/direct-tools'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { z } from 'zod'; diff --git a/packages/local/tsup.config.ts b/packages/local/tsup.config.ts index 831b3ca..f13d817 100644 --- a/packages/local/tsup.config.ts +++ b/packages/local/tsup.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ clean: true, // browser-toolset is on GitHub Packages, not npm: bundled from its source, so // installing this package needs nothing from there. - noExternal: ['@stunt-double/browser-toolset'], + noExternal: ['@stuntkit/browser-toolset'], esbuildOptions(options) { options.conditions = ['@stunt-double/source']; }, diff --git a/packages/wao/README.md b/packages/wao/README.md index 244a58e..617226b 100644 --- a/packages/wao/README.md +++ b/packages/wao/README.md @@ -2,7 +2,7 @@ **Web Agent Optimiser.** A small drop-in script that makes existing and legacy websites work for AI agents, without a rebuild. -Agents read a page through its accessibility tree (Playwright's aria snapshot, CDP's Accessibility domain, `read_page` in [`@stunt-double/browser-toolset`](../browser-toolset)) and through screenshots. Legacy pages fail the first. A `
` is not a button, an icon link has no name, and a field's label is the table cell beside it. To an agent, those controls are missing or unnamed. WAO repairs them in place: +Agents read a page through its accessibility tree (Playwright's aria snapshot, CDP's Accessibility domain, `read_page` in [`@stuntkit/browser-toolset`](../browser-toolset)) and through screenshots. Legacy pages fail the first. A `
` is not a button, an icon link has no name, and a field's label is the table cell beside it. To an agent, those controls are missing or unnamed. WAO repairs them in place: Before (Chromium's aria snapshot of a legacy page): @@ -120,7 +120,7 @@ console.log(await page.locator('body').ariaSnapshot()); // The repaired tree. const { changes, issues } = await page.evaluate(() => window.wao!.report()); ``` -A report is plain data: each entry's `element` is a non-enumerable property, so it stays in the page and the rest serialises. With Puppeteer, pass the same combined content to `page.evaluateOnNewDocument`. Over raw CDP, pass it as the `source` of `Page.addScriptToEvaluateOnNewDocument`. Pair it with [`@stunt-double/browser-toolset`](../browser-toolset), whose `read_page` reads the same tree. +A report is plain data: each entry's `element` is a non-enumerable property, so it stays in the page and the rest serialises. With Puppeteer, pass the same combined content to `page.evaluateOnNewDocument`. Over raw CDP, pass it as the `source` of `Page.addScriptToEvaluateOnNewDocument`. Pair it with [`@stuntkit/browser-toolset`](../browser-toolset), whose `read_page` reads the same tree. With Playwright MCP, write the options and the script into one file and pass it as an init script: diff --git a/packages/wao/src/optimise.ts b/packages/wao/src/optimise.ts index a6eac40..2b66a8f 100644 --- a/packages/wao/src/optimise.ts +++ b/packages/wao/src/optimise.ts @@ -4,7 +4,7 @@ // accessibility tree), in place and reversibly. // // Agents see a page as its accessibility tree (Playwright's aria snapshot, -// CDP's Accessibility domain, `read_page` in @stunt-double/browser-toolset) and as +// CDP's Accessibility domain, `read_page` in @stuntkit/browser-toolset) and as // screenshots. Legacy pages fail the first: a `
` is not a button, // an icon link has no name, a field's label is a table cell beside it. WAO // fixes those with attributes alone. It never replaces, moves or restyles a diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d64f74c..7210f13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,7 +62,7 @@ importers: '@anthropic-ai/sdk': specifier: 0.133.0 version: 0.133.0(zod@4.6.5) - '@stunt-double/browser-toolset': + '@stuntkit/browser-toolset': specifier: workspace:* version: link:.. ai: @@ -111,7 +111,7 @@ importers: specifier: ^4.6.7 version: 4.6.7 devDependencies: - '@stunt-double/browser-toolset': + '@stuntkit/browser-toolset': specifier: workspace:* version: link:../browser-toolset diff --git a/scripts/publish-npm.mjs b/scripts/publish-npm.mjs index 02a7986..9f60589 100644 --- a/scripts/publish-npm.mjs +++ b/scripts/publish-npm.mjs @@ -12,7 +12,7 @@ import { execFileSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; -const NPM_PACKAGES = ['packages/local']; +const NPM_PACKAGES = ['packages/browser-toolset', 'packages/local']; const run = (cmd, args, cwd) => execFileSync(cmd, args, { cwd, stdio: ['ignore', 'pipe', 'inherit'] }) diff --git a/skills/.claude-plugin/plugin.json b/skills/.claude-plugin/plugin.json index 058d0e0..61b04f8 100644 --- a/skills/.claude-plugin/plugin.json +++ b/skills/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "stuntkit", "displayName": "StuntKit", "version": "1.3.0", - "description": "Skills and agents for @stunt-double/browser-toolset, wao, localeizer, icons and index-methodology: when to reach for each package, how to wire it up, the mistakes to avoid, and agents that build and review browser agents", + "description": "Skills and agents for @stuntkit/browser-toolset, wao, localeizer, icons and index-methodology: when to reach for each package, how to wire it up, the mistakes to avoid, and agents that build and review browser agents", "author": { "name": "Stunt Double", "url": "https://stuntdouble.io" diff --git a/skills/agents/browser-agent-builder.md b/skills/agents/browser-agent-builder.md index 4ab6990..bd92956 100644 --- a/skills/agents/browser-agent-builder.md +++ b/skills/agents/browser-agent-builder.md @@ -1,10 +1,10 @@ --- name: browser-agent-builder -description: Use to scaffold a working browser agent in the user's project on @stunt-double/browser-toolset, end to end, from a short brief (the model or SDK, the browser engine, the sites it will visit). Writes the BrowserDriver, the agent loop, the safety settings, screenshot pruning and an optional WAO injection, then type-checks and smoke-tests them. Examples - "build me a Claude agent that checks our pricing page", "add a browser agent on the AI SDK with Playwright", "wire browser-toolset into our Puppeteer harness". +description: Use to scaffold a working browser agent in the user's project on @stuntkit/browser-toolset, end to end, from a short brief (the model or SDK, the browser engine, the sites it will visit). Writes the BrowserDriver, the agent loop, the safety settings, screenshot pruning and an optional WAO injection, then type-checks and smoke-tests them. Examples - "build me a Claude agent that checks our pricing page", "add a browser agent on the AI SDK with Playwright", "wire browser-toolset into our Puppeteer harness". tools: Read, Write, Edit, Glob, Grep, Bash --- -You build browser agents on `@stunt-double/browser-toolset` inside the user's own project. Load the `stunt-double-browser-toolset` skill and read its references before writing anything; they are the source of truth for the API. +You build browser agents on `@stuntkit/browser-toolset` inside the user's own project. Load the `stunt-double-browser-toolset` skill and read its references before writing anything; they are the source of truth for the API. ## 1. Settle the brief @@ -17,7 +17,7 @@ Read the project first (`package.json`, the package manager lockfile, `tsconfig. ## 2. Install -Use the project's package manager. Add `@stunt-double/browser-toolset`, the engine (`playwright`, and `playwright install chromium`), and the model SDK if missing. The package is on GitHub Packages: if `.npmrc` has no `@stunt-double:registry=https://npm.pkg.github.com` line, add it and tell the user they need a token with `read:packages` (never write a token into the repository). +Use the project's package manager. Add `@stuntkit/browser-toolset`, the engine (`playwright`, and `playwright install chromium`), and the model SDK if missing. The package is on GitHub Packages: if `.npmrc` has no `@stunt-double:registry=https://npm.pkg.github.com` line, add it and tell the user they need a token with `read:packages` (never write a token into the repository). ## 3. Write the code diff --git a/skills/agents/browser-agent-reviewer.md b/skills/agents/browser-agent-reviewer.md index 5dc172c..050c601 100644 --- a/skills/agents/browser-agent-reviewer.md +++ b/skills/agents/browser-agent-reviewer.md @@ -1,14 +1,14 @@ --- name: browser-agent-reviewer -description: Use to review an existing integration of @stunt-double/browser-toolset (an agent loop, a BrowserDriver implementation, safety settings) for the mistakes that break agents in production - results sent per call instead of per turn, guards off on third-party sites, unstable tab ids, screenshots sent as text, unpruned transcripts, private-host and step-budget handling. Read-only; reports findings with file and line. Examples - "review our browser agent", "why does our agent halt after one click", "check the driver we wrote for Puppeteer". +description: Use to review an existing integration of @stuntkit/browser-toolset (an agent loop, a BrowserDriver implementation, safety settings) for the mistakes that break agents in production - results sent per call instead of per turn, guards off on third-party sites, unstable tab ids, screenshots sent as text, unpruned transcripts, private-host and step-budget handling. Read-only; reports findings with file and line. Examples - "review our browser agent", "why does our agent halt after one click", "check the driver we wrote for Puppeteer". tools: Read, Glob, Grep, Bash --- -You review code that uses `@stunt-double/browser-toolset`. You do not edit files. Load the `stunt-double-browser-toolset` skill and its references first: they define correct use. +You review code that uses `@stuntkit/browser-toolset`. You do not edit files. Load the `stunt-double-browser-toolset` skill and its references first: they define correct use. ## Find the integration -Grep for `@stunt-double/browser-toolset`, `BrowserToolsetExecutor`, `DirectBrowserTools`, `BrowserDriver`, `DriverPage` and `runTurn`. Read each hit's whole file, and follow the loop from the model call to the tool results. +Grep for `@stuntkit/browser-toolset`, `BrowserToolsetExecutor`, `DirectBrowserTools`, `BrowserDriver`, `DriverPage` and `runTurn`. Read each hit's whole file, and follow the loop from the model call to the tool results. ## Check, in order of severity diff --git a/skills/stunt-double-browser-toolset/SKILL.md b/skills/stunt-double-browser-toolset/SKILL.md index 1b2f75f..c8570fd 100644 --- a/skills/stunt-double-browser-toolset/SKILL.md +++ b/skills/stunt-double-browser-toolset/SKILL.md @@ -1,14 +1,14 @@ --- name: stunt-double-browser-toolset -description: Use when building an AI agent that drives a real browser with @stunt-double/browser-toolset, on Anthropic's browser use toolset (BrowserToolsetExecutor) or on any model's function calling (DirectBrowserTools, the Vercel AI SDK, OpenAI, Gemini), including writing the agent loop, implementing BrowserDriver over Playwright, Puppeteer, Stagehand, a hosted browser or CDP, turning on the payment, sign-up and secret guards, blocking payment providers at the network layer, pruning screenshots from long transcripts, and debugging an agent that halts, loops or cannot see a control. +description: Use when building an AI agent that drives a real browser with @stuntkit/browser-toolset, on Anthropic's browser use toolset (BrowserToolsetExecutor) or on any model's function calling (DirectBrowserTools, the Vercel AI SDK, OpenAI, Gemini), including writing the agent loop, implementing BrowserDriver over Playwright, Puppeteer, Stagehand, a hosted browser or CDP, turning on the payment, sign-up and secret guards, blocking payment providers at the network layer, pruning screenshots from long transcripts, and debugging an agent that halts, loops or cannot see a control. --- -# Browser toolset (`@stunt-double/browser-toolset`) +# Browser toolset (`@stuntkit/browser-toolset`) Browser tools for agents over a provider-neutral `BrowserDriver`. No tool calls a model: pages are read as an accessibility-style tree with `ref_N` handles and acted on by ref or screenshot coordinate, so the agent's own model does the reasoning. No runtime dependencies; `@anthropic-ai/sdk` and `ai` are optional peers used for types only. ```sh -pnpm add @stunt-double/browser-toolset +pnpm add @stuntkit/browser-toolset ``` Releases are on GitHub Packages: the project `.npmrc` needs `@stunt-double:registry=https://npm.pkg.github.com` and a token with `read:packages`. Node 20+, or any runtime with `Buffer` (Workers with `nodejs_compat`, Bun, Deno). diff --git a/skills/stunt-double-browser-toolset/references/agent-loops.md b/skills/stunt-double-browser-toolset/references/agent-loops.md index 482c7d6..41a425d 100644 --- a/skills/stunt-double-browser-toolset/references/agent-loops.md +++ b/skills/stunt-double-browser-toolset/references/agent-loops.md @@ -11,8 +11,8 @@ import { BrowserToolsetExecutor, INDEX_SESSION_SAFETY, isBrowserToolsetCall, -} from '@stunt-double/browser-toolset'; -import { pruneAnthropicImages } from '@stunt-double/browser-toolset/prune'; +} from '@stuntkit/browser-toolset'; +import { pruneAnthropicImages } from '@stuntkit/browser-toolset/prune'; import { chromium } from 'playwright'; import { PlaywrightDriver } from './playwright-driver.ts'; @@ -68,8 +68,8 @@ import { DirectBrowserTools, type DirectToolResult, INDEX_SESSION_SAFETY, -} from '@stunt-double/browser-toolset'; -import { pruneAiSdkImages } from '@stunt-double/browser-toolset/prune'; +} from '@stuntkit/browser-toolset'; +import { pruneAiSdkImages } from '@stuntkit/browser-toolset/prune'; import { generateText, jsonSchema, type JSONSchema7, stepCountIs, tool, type ToolSet } from 'ai'; const VIEWPORT = { width: 1280, height: 800 }; @@ -122,7 +122,7 @@ const answer = done ? (done.input as { message?: string }).message : result.text `DIRECT_TOOL_SPECS` entries are `{ name, description, parameters }` with a plain JSON Schema object, so they map one to one onto most function-calling formats: ```ts -import { DIRECT_TOOL_SPECS, DirectBrowserTools } from '@stunt-double/browser-toolset/direct-tools'; +import { DIRECT_TOOL_SPECS, DirectBrowserTools } from '@stuntkit/browser-toolset/direct-tools'; const functions = DIRECT_TOOL_SPECS.map((s) => ({ type: 'function' as const, @@ -153,7 +153,7 @@ Gemini takes the same specs as `functionDeclarations`. The parameters are flat s import { STEP_BUDGET_FALLBACK_OUTPUT, STEP_BUDGET_FINAL_NOTE, -} from '@stunt-double/browser-toolset/direct-tools'; +} from '@stuntkit/browser-toolset/direct-tools'; // After the last allowed step, one more call that can only answer: const final = await generateText({ diff --git a/skills/stunt-double-browser-toolset/references/drivers.md b/skills/stunt-double-browser-toolset/references/drivers.md index 575f166..07f13cb 100644 --- a/skills/stunt-double-browser-toolset/references/drivers.md +++ b/skills/stunt-double-browser-toolset/references/drivers.md @@ -1,6 +1,6 @@ # Implementing `BrowserDriver` -Types come from `@stunt-double/browser-toolset/driver`. The tools use only: +Types come from `@stuntkit/browser-toolset/driver`. The tools use only: - `driver.context`: `activePage()`, `pages()`, `newPage(url?)`, `setActivePage(page)`. `activePage` and `pages` may be sync or async. - On `DriverPage`: `id`, `goto`, `goBack`, `goForward`, `url`, `title`, `screenshot`, `evaluate` (always a **string** expression), `click(x, y, { button, clickCount })`, `hover`, `scroll(x, y, dx, dy)`, `dragAndDrop`, `type` (into the focused element), `keyPress` (one key or chord in Playwright spelling, `Control+A`, `PageDown`), `waitForTimeout`, `waitForLoadState` and `close`. diff --git a/skills/stunt-double-browser-toolset/references/safety.md b/skills/stunt-double-browser-toolset/references/safety.md index 8ccf601..a971dc4 100644 --- a/skills/stunt-double-browser-toolset/references/safety.md +++ b/skills/stunt-double-browser-toolset/references/safety.md @@ -20,7 +20,7 @@ A prompt saying "never pay or sign up" is a request the model may ignore. The gu The guards read the DOM, so a page built to disguise a payment form can get past them. Block payment providers too: ```ts -import { paymentBlocklistFor } from '@stunt-double/browser-toolset/safety'; +import { paymentBlocklistFor } from '@stuntkit/browser-toolset/safety'; const target = new URL(startUrl).hostname; await driver.context.setDomainPolicy({ blockedDomains: paymentBlocklistFor(target) }); diff --git a/skills/stunt-double-wao/references/harness.md b/skills/stunt-double-wao/references/harness.md index a3bc4b8..82f0bd5 100644 --- a/skills/stunt-double-wao/references/harness.md +++ b/skills/stunt-double-wao/references/harness.md @@ -51,7 +51,7 @@ await cdp.send('Page.addScriptToEvaluateOnNewDocument', { source: waoInit({ rule They expose a Playwright context (`stagehand.context`, or `browser.contexts()[0]` after `connectOverCDP`). Call `addInitScript` on it before the first `goto`. Pages that were already open when you injected need a reload. -## With `@stunt-double/browser-toolset` +## With `@stuntkit/browser-toolset` Inject on the Playwright context inside your driver's `launch`, before `newPage`. `read_page` and `find` then read the repaired tree with no other change. Repaired elements carry `data-wao`, which shows in the tree only as the role and name it produced. diff --git a/skills/stuntkit-agent-ready-site/SKILL.md b/skills/stuntkit-agent-ready-site/SKILL.md index f96fab2..17913f0 100644 --- a/skills/stuntkit-agent-ready-site/SKILL.md +++ b/skills/stuntkit-agent-ready-site/SKILL.md @@ -1,11 +1,11 @@ --- name: stuntkit-agent-ready-site -description: Use when asked to make a website or web app work for AI agents (agent-ready, agent-friendly, "agents can't use our site", browser-use or computer-use agents failing on it), or to audit how an agent sees a site. A workflow over @stunt-double/wao and @stunt-double/browser-toolset - measure the accessibility tree an agent reads, repair it with WAO and rules, fix the source where you can, and prove the result with a real agent task. +description: Use when asked to make a website or web app work for AI agents (agent-ready, agent-friendly, "agents can't use our site", browser-use or computer-use agents failing on it), or to audit how an agent sees a site. A workflow over @stunt-double/wao and @stuntkit/browser-toolset - measure the accessibility tree an agent reads, repair it with WAO and rules, fix the source where you can, and prove the result with a real agent task. --- # Making a site agent-ready -Agents read a page through its accessibility tree and screenshots. A control with no role or name is, to an agent, missing. This workflow finds those gaps, repairs them, and proves the repair with an agent. It uses `@stunt-double/wao` (see `stunt-double-wao`) and, for the proof, `@stunt-double/browser-toolset` (see `stunt-double-browser-toolset`). +Agents read a page through its accessibility tree and screenshots. A control with no role or name is, to an agent, missing. This workflow finds those gaps, repairs them, and proves the repair with an agent. It uses `@stunt-double/wao` (see `stunt-double-wao`) and, for the proof, `@stuntkit/browser-toolset` (see `stunt-double-browser-toolset`). Work on the user's own site, or one they are authorised to test. Turn on `INDEX_SESSION_SAFETY` for any agent run on a site with real payments or sign-ups. diff --git a/skills/stuntkit/SKILL.md b/skills/stuntkit/SKILL.md index 83666ac..3994bb1 100644 --- a/skills/stuntkit/SKILL.md +++ b/skills/stuntkit/SKILL.md @@ -1,19 +1,19 @@ --- name: stuntkit -description: Use when deciding which StuntKit package (@stunt-double/browser-toolset, wao, localeizer, icons) fits a task, when installing any @stunt-double package from GitHub Packages (.npmrc, tokens, CI), or when combining them, for example a browser agent that also repairs legacy pages, or a site made ready for AI agents and checked by one. Routes to the per-package skills. +description: Use when deciding which StuntKit package (@stuntkit/browser-toolset, wao, localeizer, icons) fits a task, when installing any @stunt-double package from GitHub Packages (.npmrc, tokens, CI), or when combining them, for example a browser agent that also repairs legacy pages, or a site made ready for AI agents and checked by one. Routes to the per-package skills. --- # StuntKit Open source building blocks for AI agents that use the web, extracted from [Stunt Double](https://stuntdouble.io). Four independent packages with no runtime dependencies; use one or any combination. -| Task | Package | Skill | -| ------------------------------------------------------------------ | ------------------------------- | ------------------------------ | -| An agent that drives a real browser (any model) | `@stunt-double/browser-toolset` | `stunt-double-browser-toolset` | -| A site, legacy page or web app that agents struggle to read or use | `@stunt-double/wao` | `stunt-double-wao` | -| American, British and Canadian spelling from one source | `@stunt-double/localeizer` | `stunt-double-localeizer` | -| Icons (lucide-compatible) or AI provider and coding agent logos | `@stunt-double/icons` | `stunt-double-icons` | -| Make a site agent-ready and prove it with an agent | wao + browser-toolset | `stuntkit-agent-ready-site` | +| Task | Package | Skill | +| ------------------------------------------------------------------ | --------------------------- | ------------------------------ | +| An agent that drives a real browser (any model) | `@stuntkit/browser-toolset` | `stunt-double-browser-toolset` | +| A site, legacy page or web app that agents struggle to read or use | `@stunt-double/wao` | `stunt-double-wao` | +| American, British and Canadian spelling from one source | `@stunt-double/localeizer` | `stunt-double-localeizer` | +| Icons (lucide-compatible) or AI provider and coding agent logos | `@stunt-double/icons` | `stunt-double-icons` | +| Make a site agent-ready and prove it with an agent | wao + browser-toolset | `stuntkit-agent-ready-site` | Load the matching skill before writing code; each has references for the details.