Reads the page
get_page_text for posts, articles and results; get_links for real URLs, so it never guesses one.
Autonomous browser agent
Give it a goal in plain English and it does what a person at a keyboard would: reads pages, clicks, types, logs in, fills forms, handles files and tabs, and keeps going until the job is done.
npx slapify init
Watch it work
Every run can be recorded: one annotated frame per step, a video and a replay you can scrub through. These two ran on demo sites.

Goal: Go to the store. Find a waterproof jacket that costs less than $150, add it to the cart in size M with gift wrap, and tell me the cart total.Searches, compares, reads the product page, picks a size from a dropdown, ticks gift wrap and adds to cart.
Goal: Reply to the two posts about TypeScript with a useful tip each, and confirm each reply appears.Finds the posts' real URLs with get_links, writes each reply, has it reviewed against a policy before it posts, then checks it appeared.Capabilities
get_page_text for posts, articles and results; get_links for real URLs, so it never guesses one.
Click, type, hover, double-click, drag, checkboxes, native dropdowns, even controls hidden for keyboard users.
Uploads sandboxed to one folder, downloads with sanitized names, sign-in popups and new tabs followed.
Credential profiles, cookie and session injection, and 2FA codes relayed over Telegram.
Optional vision for CAPTCHAs, charts and canvases. Screenshots go to the model as real images.
Stealth mode patches only real automation tells, and persistent profiles keep you signed in.
Persistent runs only a human can stop, schedules, memory across sessions and hard time limits.
Annotated frames, a video, a replay.html you can scrub through, and an optional live view.
Held keys and coordinate clicks over CDP reach canvases and cross-origin iframes, games included.
Models
Use a Claude or ChatGPT subscription instead of API credits, or any of six API providers with automatic fallback.
provider: claude-cliRuns every turn through your local claude -p on a Claude Pro or Max plan. No API key.
slapify login codexSign in with ChatGPT once. Native tool calls and images through the Codex endpoint. No API key.
anthropic · openai · google · mistral · groq · ollamaList several and Slapify falls back down the chain when one rate-limits or goes down.
The harness
Every tool declares what it can do to the world, from reading a page to buying something. Clicks are judged by the element they hit.
Repeats are never blocked. Repeated failures get guidance, and a post that already went through is held instead of sent twice.
Irreversible actions are checked against your plain-English policy first, and held when no reviewer can answer.
Transient failures retry, outages and usage limits are waited out, and one bad step never ends a run.
Long histories are summarised without ever paraphrasing the goal, so a week-long run keeps its instructions.
Optional Jev or OpenAI Decisions answer "which element?" and "is this true?" in under a second, with the LLM as fallback.
How it works
Give it a goal in plain English. The agent plans, browses, logs in, recovers from errors and keeps going until the job is done, even if that takes days.
Executes .flow files: test scripts that describe user journeys step by step in plain English. Think Playwright, written like a checklist.
createAgent() runs the same harness as the CLI from JS or TS: inline config, streamed events and resumable sessions, plus perf audits and flows.
Quick start
# Set up: pick an API key, or your Claude or ChatGPT plan
npx slapify init
# Give it a goal (and keep a replay of the run)
npx slapify task "Find a waterproof jacket under $150
and add it to the cart in size M" --record runs/jacket
# Plain-English E2E tests
npx slapify run tests/checkout.flow --reportimport { createAgent } from "slapify";
const agent = createAgent({ stealth: true });
const run = await agent.run(
"Check the status of my last three orders on example-shop.com"
);
console.log(run.finalSummary);
for await (const e of agent.stream("Summarise my unread email")) {
if (e.type === "status_update") console.log(e.message);
}