# Steel > Steel is an open-source cloud browser infrastructure platform for AI agents. It gives your agent a real browser that can navigate pages, fill forms, solve CAPTCHAs, persist auth, and extract content from modern websites. Steel is the fastest remote browser in independent benchmarks: 0.89s average lifecycle, 1.7x to 9x faster than alternatives. ## Who Steel is for - **AI agent builders** who need reliable, authenticated web access in production. - **Scraping and data extraction teams** who want managed browser infrastructure without running headless Chrome themselves. - **Automation engineers** migrating Playwright, Puppeteer, or Selenium scripts to the cloud. - **Teams that need persistent browser state**: login sessions, cookies, credentials, and extensions that survive across runs. ## Quick reference - Install: `npm install steel-sdk` (Node.js) or `pip install steel-sdk` (Python) - CLI: `curl -sSf https://setup.steel.dev | sh` - Auth header: `steel-api-key: ` - Auth env var: `STEEL_API_KEY` - API base URL: `https://api.steel.dev` - WebSocket: `wss://connect.steel.dev?apiKey=&sessionId=` ## For coding agents - [Install guide](https://setup.steel.dev/install.md): Install the Steel CLI, add the `steel-browser` skill, authenticate, and verify with `steel scrape`. - [Agent setup prompt](https://steel.dev/agent-prompt): The full instructions for setting Steel up in a project, as plain text. - [Steel CLI](https://docs.steel.dev/overview/steel-cli): The terminal workflow, `steel browser` commands, and agent skill setup. - [Steel Skills](https://docs.steel.dev/overview/skills/available-skills): Choose the skill that matches your agent's workflow. - [Steel Browser skill](https://docs.steel.dev/overview/skills/available-skills/steel-browser): The skill for agents that operate a live Steel browser. ## Why Steel - **Performance**: 0.89s avg browser lifecycle (p95: 1.09s). 25.6% control-plane tax, the lowest measured in independent testing. - **Open source**: Core browser runtime at [github.com/steel-dev/steel-browser](https://github.com/steel-dev/steel-browser). Benchmark code at [github.com/steel-dev/browserbench](https://github.com/steel-dev/browserbench). - **Self-hostable**: Run Steel in your own infrastructure with [Docker](https://docs.steel.dev/overview/self-hosting/docker) or [Railway](https://docs.steel.dev/overview/self-hosting/railway) one-click deploy. No vendor lock-in. - **Framework agnostic**: Works with Playwright, Puppeteer, and Selenium. Native Node and Python SDKs. - **Production ready**: Profiles for persistent auth, CAPTCHA solving, stealth/proxy tooling, agent traces, and live session debugging. ## Key features - Steel runs isolated cloud browser sessions through an API, CLI, and SDKs. - Steel can return page content as HTML, markdown, screenshots, and PDFs. - Steel can persist browser state across runs with profiles, cookies, credentials, extensions, and auth context. - Steel includes anti-bot tooling: proxies, stealth settings, CAPTCHA solving, and mobile-mode browsing. - Steel includes debugging and observability tools: live sessions, past session embeds, recordings, WebRTC streaming, and agent traces. - Steel supports cloud, self-hosted, and local runtime workflows. ## SDKs - [Node SDK](https://github.com/steel-dev/steel-node): `steel-sdk` on npm, for JavaScript and TypeScript projects. - [Python SDK](https://github.com/steel-dev/steel-python): `steel-sdk` on PyPI, for Python projects. - [Node SDK blog post](https://steel.dev/blog/steel-node-sdk): Deep dive on the Node SDK. - [Python SDK blog post](https://steel.dev/blog/steel-python-sdk): Deep dive on the Python SDK. ## Core docs - [Intro to Steel](https://docs.steel.dev/overview/intro-to-steel): Product overview and why AI agents need browser infrastructure. - [Sessions API](https://docs.steel.dev/overview/sessions-api/overview): Create and control cloud browser sessions. - [Profiles API](https://docs.steel.dev/overview/profiles-api/overview): Reuse auth, cookies, extensions, credentials, and browser settings across sessions. - [Credentials API](https://docs.steel.dev/overview/credentials-api/overview): Store credentials for users or agents. - [Files API](https://docs.steel.dev/overview/files-api/overview): Upload, download, and manage files during active sessions. - [Extensions API](https://docs.steel.dev/overview/extensions-api/overview): Add Chrome extensions to browser sessions. - [CAPTCHAs API](https://docs.steel.dev/overview/captchas-api/overview): Detect and solve CAPTCHAs during browser automations. - [Browser Tools](https://docs.steel.dev/overview/browser-tools/overview): One-shot endpoints for scraping pages, capturing screenshots, and generating PDFs. - [Agent Traces](https://docs.steel.dev/overview/agent-traces/overview): Observability for browser agents: what they did, step by step. - [Human-in-the-loop](https://docs.steel.dev/overview/sessions-api/human-in-the-loop): Let users take control of a running session. - [Stealth: Proxies](https://docs.steel.dev/overview/stealth/proxies): Configure IP and location-aware browsing. - [Stealth: CAPTCHA solving](https://docs.steel.dev/overview/stealth/captcha-solving): How Steel handles CAPTCHA workflows. - [Self-hosting](https://docs.steel.dev/overview/self-hosting/docker): Run Steel Browser in your own infrastructure. - [Open-source Steel Browser](https://github.com/steel-dev/steel-browser): Self-hosted open-source browser/API runtime. - [Pricing/Limits](https://docs.steel.dev/overview/pricinglimits): Pricing and usage limits reference. - [Need Help?](https://docs.steel.dev/overview/need-help): Support, GitHub, Discord, and dashboard links. ## Getting started - [Sessions API quickstart](https://docs.steel.dev/overview/sessions-api/quickstart): Start your first Steel session. - [Beginner's Guide to Steel](https://steel.dev/blog/beginner-s-guide-to-steel): End-to-end walkthrough for new users. - [Happy Path for Automating the Web](https://steel.dev/blog/happy-path-for-automating-the-web-with-steel): Recommended workflow for web automation with Steel. - [Connect with Playwright](https://docs.steel.dev/cookbook/playwright): Drive Steel sessions from Playwright. - [Connect with Puppeteer](https://docs.steel.dev/cookbook/puppeteer): Drive Steel sessions from Puppeteer. - [Connect with Selenium](https://docs.steel.dev/cookbook/selenium): Drive Steel sessions from Selenium. - [Auth Context Starter](https://docs.steel.dev/cookbook/auth-context-starter): Persist login state for authenticated workflows. - [Credentials API Starter](https://docs.steel.dev/cookbook/credentials-starter): Bootstrap credential-backed automations. - [Files API Starter](https://docs.steel.dev/cookbook/files-starter): Handle uploads and downloads in-session. - [Extensions API Starter](https://docs.steel.dev/cookbook/extensions-starter): Start automations that require browser extensions. - [Self-Hosting Guide](https://steel.dev/blog/self-hosting-steel-browser): Deploy Steel Browser on your own infrastructure. ## Integrations - [Claude Code](https://docs.steel.dev/integrations/claude-code): Give Claude Code a Steel browser. - [Claude Agent SDK](https://docs.steel.dev/integrations/claude-agent-sdk): Build agents on the Claude Agent SDK that browse with Steel. - [Claude Computer Use](https://docs.steel.dev/integrations/claude-computer-use): Run Claude computer-use workflows on Steel. - [OpenAI Agents SDK](https://docs.steel.dev/integrations/openai-agents-sdk): Build typed agents on the OpenAI Agents SDK that browse with Steel. - [OpenAI Computer Use](https://docs.steel.dev/integrations/openai-computer-use): Run OpenAI computer-use agents on Steel browsers. - [Gemini Computer Use](https://docs.steel.dev/integrations/gemini-computer-use): Run Gemini computer-use workflows on Steel. - [Browser Use](https://docs.steel.dev/integrations/browser-use): Connect Browser Use to Steel sessions. - [Stagehand](https://docs.steel.dev/integrations/stagehand): Run natural-language browser automation on Steel. - [Notte](https://docs.steel.dev/integrations/notte): Run Notte tasks in live cloud browsers. - [CrewAI](https://docs.steel.dev/integrations/crewai): Build multi-agent workflows that browse the web. - [Agno](https://docs.steel.dev/integrations/agno): Add Steel browsing to Agno agents. - [AgentKit](https://docs.steel.dev/integrations/agentkit): Orchestrate agent networks with Steel browsers. - [Replit](https://docs.steel.dev/integrations/replit): Use Steel from Replit. - [StackBlitz Bolt.new](https://docs.steel.dev/integrations/stackblitz-bolt.new): Use Steel from Bolt.new. ## Site - [Pricing](https://steel.dev/pricing.md): Plans, what each includes, and a comparison of them. - [FAQ](https://steel.dev/faq.md): Answers to common questions about Steel. - [Blog](https://steel.dev/blog.md): Guides, engineering, comparisons, product news, and company news. - [Brand kit](https://steel.dev/brand-kit): Steel's logo, colours, and type, to download. - [Terms of Service](https://steel.dev/terms) - [Privacy Policy](https://steel.dev/privacy) ## Developer resources - [Changelog](https://docs.steel.dev/changelog): What has shipped, newest first. - [Docs home](https://docs.steel.dev): Main documentation site. - [Docs llms.txt](https://docs.steel.dev/llms.txt): The docs' own index for agents. - [llms-full.txt](https://docs.steel.dev/llms-full.txt): Flattened docs index for agents. - [API Reference](https://docs.steel.dev/api-reference): API reference. - [Dashboard](https://app.steel.dev): Manage API keys, usage, and sessions. - [GitHub](https://github.com/steel-dev): Open-source repos and examples. - [Steel Browser (OSS)](https://github.com/steel-dev/steel-browser): Open-source browser runtime and self-hosted API. - [Benchmark Code](https://github.com/steel-dev/browserbench): Open-source remote browser benchmark. - [Cookbook](https://github.com/steel-dev/steel-cookbook): Example projects and starters. - [Discord](https://discord.gg/steel-dev): Community support. - [Support](mailto:team@steel.dev): Contact Steel support. - [Legal](https://docs.steel.dev/overview/legal): Legal terms and conditions. ## Pricing Plans and a full comparison: [Pricing](https://steel.dev/pricing). - **Launch** ($0 per month + usage): Start for free with $30 of credits, and pay only for what your agents run. Includes $30 of credits, one time. 10 concurrent browser sessions; sessions up to 15 minutes; up to 3 seats; email and community support; no credit card needed. - **Scale** ($250 per month + usage): Production capacity, stable identity, dedicated support. Includes $100 of credits every month. 100 concurrent browser sessions; sessions up to 1 hour; dedicated IPs at $5 each; dedicated Slack channel; SSO + HIPAA-ready BAA. - **Enterprise** (Custom per month + usage): Your hardest workloads, with guarantees: custom rates, security and support. Includes custom usage credits. 1,000+ concurrent sessions; stealth browser; reserve browser pools; sessions up to 24 hours; support and uptime SLAs. ### Included usage | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Free usage credits | $30 (one time) | $100 / month | Custom | ### Rates | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Browser hours | $0.10 | $0.08 | Custom | | Proxy bandwidth | $10/GB | $6/GB | Custom | | CAPTCHA solves | $3/1k | $1/1k | Custom | | Browser tools (/scrape) | $5/1k | $5/1k | Custom | ### Features | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Concurrent browser sessions | 10 | 100 | 1,000+ | | Stealth browser | Not included | Not included | Included | | Dedicated IPs | Not included | $5/IP | Custom | | Max session time | 15 minutes | 1 hour | Up to 24 hours | | Number of seats | Up to 3 | Unlimited | Unlimited | | Reserve browser pools | Not included | Not included | Included | ### Limits | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Requests per minute | 60 | 600 | Custom | | Data retention | 7 days | 14 days | Custom | ### Support | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Email support | Included | Included | Included | | Community support | Included | Included | Included | | Dedicated Slack channel | Not included | Included | Included | | 911 email | Not included | Not included | Included | | Support and uptime SLAs | Not included | Not included | Included | ### Security | Feature | Launch | Scale | Enterprise | | - | - | - | - | | Enterprise SSO | Not included | Included | Included | | HIPAA-ready BAA | Not included | Included | Included | ## FAQ ### What is Steel and what does it do? Steel is cloud browser infrastructure for AI agents. It gives agents isolated cloud browser sessions with built-in anti-bot stealth, CAPTCHA solving and session persistence. ### Is Steel open source? Yes. Steel Browser is the open-source server on GitHub that you can host yourself. Steel Cloud is the managed version, adding scale, stealth, proxies and CAPTCHA solving. ### Is Steel an AI agent or AI infrastructure? Steel is infrastructure, not an agent. Your agent, built with the OpenAI Agents SDK, LangChain, CrewAI or your own code, connects to Steel, which handles the browser's lifecycle, stealth, session persistence and proxies. ### Can I use Steel with Playwright, Puppeteer or Selenium? Yes. Steel works with Playwright, Puppeteer, Selenium and its own Node and Python SDKs. You connect over a standard CDP WebSocket URL, so any CDP-compatible tool works out of the box. ### How fast does Steel start a browser session? Under a second on average, with no cold-start penalty. Each session launches into its own isolated Chromium in the cloud, with its proxy, cookies and storage already set up. ### Can I run headless browsers in the cloud for web scraping? Yes. Steel runs managed cloud browser sessions for scraping and AI automation, and handles anti-bot stealth, CAPTCHA solving, residential proxies and session persistence in one API, through Playwright, Puppeteer or Selenium. ### Can Steel bypass CAPTCHAs? Yes, on Steel Cloud. With CAPTCHA solving on, a session detects and solves CAPTCHAs as your agent browses, including Cloudflare Turnstile, PerimeterX press-and-hold and image CAPTCHAs, and the CAPTCHAs API reports each one's progress. Stealth and proxies keep many from appearing at all. ### Does Steel support human-in-the-loop workflows and reusing logins? Yes. A live session viewer lets people watch or take over running sessions. Persistent browser profiles keep cookies and session tokens, so agents pick up signed-in workflows without logging in again. ### How much does Steel cost, and is there a free tier? Launch includes $30 free credits, no monthly fee, and usage-based pricing after your one-time credits run out. Scale is $250 per month plus usage, and Enterprise pricing is custom. Steel Browser is free and open source. ## Blog: Build the agent. Don’t build its computer. https://steel.dev/blog/build-the-agent-not-its-computer · Oct 7, 2026 · By Nikola Balic > Should you build or buy AI agent infrastructure? Specify the computer your agent needs, test how an agent sandbox recovers, and start managed. Includes a provider map. **TL;DR:** Teams that build agents should specify the computer the agent needs, test how it recovers, and start with managed infrastructure. Build your own only for a hard requirement no provider meets or an advantage you can name. Agent runs are series systems, so compare providers on recovery and cost per accepted result. It answers: - What is an agent's computer? - Should you build or buy AI agent infrastructure? - How do you test an agent sandbox provider? - What is the difference between a sandbox and a persistent computer? - What is BYOC for agent infrastructure? - Which sandbox providers should I look at for my agent workload? ![An engineer builds a small robot while distant crews operate the compute infrastructure](https://steel.dev/blog/build-the-agent-not-its-computer/cover.png) If your team builds agents and has no execution platform that already fits, specify the computer, test how it recovers, and start managed unless owning it makes the agent better. ![You build the agent and specify the computer it runs on](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-1.svg) You build the agent and specify the computer it runs on **TL;DR** - The agent is your product and its computer is infrastructure. Write down what the computer has to do, test whether a provider actually does it, and start with managed infrastructure. Build it yourself only if: - **You have a hard requirement no provider meets**, or - **Owning it gives you an advantage you can name.** As we built internal agents for everything from code review to knowledge management and team communication, we kept running into the same question: where should they run, and how do we manage them at scale? An agent that works in a demo still needs a computer to keep working. It needs a browser that stays signed in, files that survive until tomorrow, tools, access rules, and a way to continue after something breaks. Teams answer that need in one of three ways: 1. They run the agent on **a laptop or a single server** until the agent outgrows that setup. 2. They **build their own platform** for agent computers. 3. Or they **buy one from a provider** and test that it does the job. This article argues for the third, with a middle ground for teams that must keep compute inside their own network. Take a **competitive-intelligence agent**. Every morning it opens ten competitors’ pricing pages, changelogs, and docs. It signs in to the few that sit behind a login, downloads their PDFs, compares everything with yesterday, and posts what changed to Slack. The demo works because the browser is already signed in, the files are on one machine, and someone is watching. Then it has to run again tomorrow. It needs yesterday’s snapshots to compare against and the same signed-in sessions, and **a saved browser profile does not guarantee a valid login.** When a login expires or a site starts to block it, a person must fix that without throwing away the morning’s work. And it must not post the same change to Slack twice. You could build that environment yourself. As more agents and users arrive, you would also allocate capacity, keep sessions apart, maintain images, investigate failed starts, and explain which work survived a failure. **Your team would own a second product:** the service that supplies computers to its agents. Every hour spent on provisioning, isolation, snapshots, scheduling, and recovery is an hour not spent on the agent. ### What we mean by an agent’s computer An **agent’s computer** is the working environment it can operate: its browser, files, tools, processes, access, and the state its task needs to continue. ![Browser, files, tools, access, and state form the working environment](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-2.svg) The working environment To learn how other teams solve these problems, we kept notes on the agents that companies build for their own work. The notes grew into the [Internal Agents Map](https://internal-agents.com/), which we now share as a public catalog. Its [definition of an internal agent](https://internal-agents.com/definitions) explains why the computer matters: > An internal agent is one that “works with company context and tools to carry out the organization’s own work.” > > “A model alone doesn’t know the codebase, follow the processes, or reach the tools. The organization supplies those.” The computer is where the agent reaches them. For the competitive-intelligence agent, the browser must reach the right pages with the right session. A tool must compare today’s snapshot with yesterday’s. The result must stay available for review with its source attached. The environment connects those steps with the right access. This environment may span several services, such as sandboxes, browser services, storage, or an existing development platform. It need not be a graphical desktop or a machine that runs forever. A temporary worker is enough if another service preserves the state the next worker needs. Some workloads benefit from a **persistent computer** rather than a disposable sandbox. In [Fly’s announcement](https://fly.io/news/fly-io-launches-computers-for-agents/), investor Martin Casado of Andreessen Horowitz described Fly as “giving agents real computers instead of disposable sandboxes.” A persistent computer can make sense **when setup is expensive or a process must keep running between steps.** Preserving more state can save setup time, but the trade-off depends on storage charges, idle billing, wake-up time, and recovery guarantees. A persistent workspace need not bill for compute while it sleeps. The tests later in this article show which one your task needs. The **harness** is a separate part of the system. It manages model calls and tool execution, and it can run outside the environment where commands execute. Some agents need no dedicated environment because remote tools already provide everything their tasks require. [Shopify describes the same split](https://internal-agents.com/agents/shopify-internal-agents) behind River, its internal Slack agent: a durable identity, a disposable agent loop, and isolated execution, each replaceable on its own. “The agent needs somewhere to execute code” leaves many questions open. “The agent must continue tomorrow’s comparison under the same user’s access rules” gives the team something to design and test. ### Scaling browsers is scaling computers A browser session looks like one program. Underneath, it is a computer. In our current fleet, each browser session runs in its own microVM, with its own kernel, memory, network interface, and files. To run more browsers, we run more computers. **A fleet of browsers needs the same capacity planning, isolation, and scheduling as any fleet of agent computers.** Small settings decide how fast an agent can start work. In our fleet, reducing each browser VM from four virtual CPUs to two improved cold starts under the same host load. In the last six months, we started three rebuilds of this infrastructure and changed its primitives each time. We stopped one before it reached production. The first version ran on an external vendor’s platform, and it still serves some sessions. **When the vendor had problems, our customers saw failed Steel sessions.** The version we are building now runs on our bare-metal servers, which gives us much more control over the runtime, scheduling, storage, and network path. Proxies taught us the same lesson from outside our own infrastructure. Customers reported proxy failures inside Steel sessions as Steel outages. Over 30 days, customer-supplied proxies failed to establish a tunnel about 9.0% of the time, and Steel-managed proxies about 0.4%. Those figures cover tunnel setup only. The user did not care which layer failed. They saw a task that stopped. Reliability engineers call this a **series system**: the whole works only when every part works. An agent run is a long series. Imagine a workflow where each step usually succeeds: ![Illustrative single-attempt success rates for seven steps, from 80 to 99 percent, multiply to a 61.8 percent reliable workflow](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-3.svg) Illustrative single-attempt success rates for a seven-step workflow Each step looks fine on its own. In this simplified example, every step must succeed, each gets one attempt, and the outcomes are independent, so the success rates multiply to 61.8%, and 38% of runs fail. Real workflows also have dependencies between steps, retries, and fallbacks, so model those too. Even so, if one layer is misconfigured, slow, or fails in a way that nobody can see, the agent’s user gets a broken product. **Every layer is a multiplier, and your users get the product of all of them.** When any layer fails, they see a stopped task in your product, even if you bought that layer. Each layer you operate yourself is one more rate your team must keep high. Each layer you buy should show which step failed and let the agent resume from there. A capable provider also brings operating experience from many customers, so check that experience during your evaluation. In [our proxy investigation](https://steel.dev/blog/developer-tools-come-with-an-assembly-required-sticker), we could check the health of proxies we managed and replace a failing one when changing the IP was safe. With customer-supplied proxies, blind rotation could break a session that depended on its network identity. We began work to make **each error name the active proxy, its owner, and the failure stage**, so a team can see which step in the series failed. We made this decision ourselves, one layer down. Every agent product sits on a stack: hardware, the runtime that turns hardware into computers, the computer the agent uses, the harness, and the agent itself. We started by buying the layer below us. We built it when we could name what owning it gives our customers: speed, density, and failure behavior that directly affect the product they use. That is the second reason to build, described later in this article. **For most teams that build agents, that layer is not the product**, and operating it creates a second product they do not need. ### Customize the environment, then choose who operates it Your team may need a customized environment: exact package versions, a base image with your internal tools, browser profiles that stay signed in, a fixed outbound IP or proxy region, a private connection to an internal service, data that stays in one region, and access limited to one customer’s records. **You decide which requirements are necessary, wherever the environment runs.** **Operating the service that delivers those requirements is a separate decision.** A provider can start workers and preserve specified state while your team controls the configuration. Your team owns configuration, integration, and the product result. The provider operates the service you contract for, and its experts back you up when something breaks. ![Your team decides agent behavior, environment configuration, and service operation; a provider can operate the service layer](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-4.svg) The provider operates only the service layer For the competitive-intelligence agent, the division might look like this: Division of responsibility | Responsibility | What your team must decide | | - | - | | Agent behavior | Which competitors and pages to watch, what counts as a change, and when to alert | | Environment configuration | Browser profiles and logins, proxy region, tools, file locations, and retention | | Service operation | Who supplies capacity, keeps sessions isolated, and restores the saved state | [WorkOS’s Horizon](https://workos.com/blog/project-horizon) shows this separation. WorkOS needed its complete development stack, programmatic lifecycle control, and explicit outbound network controls. It lacked an existing cloud development platform and considered building one on EC2. The team first used Codespaces for a working prototype, then selected Cloudflare as its requirements became clearer. **WorkOS retained its own orchestration outside the sandboxes.** A service that gets the prototype running may not provide the control the eventual workload requires. That can justify changing providers without justifying an internal compute platform. Buying the service leaves your team responsible for the product’s result. You still evaluate the outputs, decide how users access them, and decide which actions need a person’s approval. ### How to test an agent’s computer Return to tomorrow’s work. Before choosing an environment, identify the state that must survive. ![A run is interrupted; the next run resumes from saved state](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-5.svg) The next run starts from what was saved State that must survive | State | What continuation requires | | - | - | | Files | Yesterday’s snapshots, downloads, and completed comparisons remain available | | Browser identity | The agent can use the correct account, or a person can restore access | | Running processes | Necessary tools resume, or restart from saved inputs | | Agent progress | The next run knows what finished and what remains | | External actions | The application can determine which changes already reached another system | These states have different lifetimes. Preserving a conversation does not preserve a download. Restoring a disk does not restore a running process. An authenticated browser profile can outlive a worker while its login still expires at the website. The [hosted sandboxes in OpenAI’s Agents API](https://developers.openai.com/api/docs/guides/agents-api/environments/openai-hosted) show how separate these lifetimes are. Workspace files persist while the sandbox exists; published output artifacts can remain after it expires. Connected sandboxes receive keep-alives between turns. If both activity and keep-alives stop for an hour, the sandbox can be deleted. **Applications must distinguish saved session information from the lifetime of their working files.** For the competitive-intelligence agent, a temporary worker may be the right choice. It could save the snapshots and verified comparisons, then restart its tools when work resumes. If tool setup is expensive, preserve more process state. The task should determine how much state you pay to retain. **Human intervention belongs in this test.** Let the login expire halfway through the workflow. Check whether a person can identify the problem, regain access, and return control without restarting the comparison. Decide how agent actions pause while the person acts, including what happens if their connection drops. [Sentry’s Junior](https://internal-agents.com/agents/sentry-junior) makes this pause part of the network path. Its outbound proxy injects credentials for configured domains, or pauses the agent until a person authorizes access. Also interrupt an external action. Suppose the agent posts the day’s changes to Slack, but loses the response before it records success. Restarting the computer cannot tell it whether a retry will post a duplicate. An operation identifier helps only if the receiving system [uses it to deduplicate requests](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) or lets your application reliably find the original result. Otherwise, the application needs another reconciliation method before retrying. **That remains application work even with reliable compute.** Access requires the same care. Stephen O’Grady of RedMonk [writes](https://redmonk.com/sogrady/2026/09/16/new-new-kingmakers/) that sandboxes “exist to give the exploding number of agents more autonomy, while not trusting them.” Isolation keeps the agent away from other workloads. **It does not limit what the credentials inside the sandbox allow.** Define what the task may read and change, then test those limits. Keeping a long-lived token outside the worker reduces one exposure; it does not settle whether an authenticated action is appropriate. Write the expected results down as a contract. For the competitive-intelligence agent, it could look like this: Acceptance tests | Failure to inject | Pass when | Who is responsible | | - | - | - | | Replace the worker after some comparisons finish | Saved snapshots and finished comparisons are recoverable, and unfinished work is identifiable | The provider keeps the promised files; your application records progress | | Expire the login during the run | The agent pauses affected actions, keeps its progress, and resumes after a person signs in again | The provider supports the browser handoff; your application pauses and resumes | | Lose the response to a Slack post | The application records the outcome as uncertain and reconciles it before a retry | Your application and the receiving system, not the computer | | Disconnect the person during a handoff | Control does not return to the agent in an unsafe or undefined state | Your application, using the provider’s session controls | **Keep the first evaluation small.** 1. Run one typical task. 2. Disconnect the client and reconnect. 3. Separately, stop or replace the execution environment in a disposable test. 4. Attempt to continue from the state the service promises to preserve. 5. Record what resumes, what must restart, and what requires a person. 6. Check who has enough information to make each repair. This is a smoke test. Before production, repeat it with real concurrency, injected failures, and latency distributions. **A clean pause and resume does not prove recovery from an abrupt host failure.** **Measure time until useful work**, including authentication and tool setup. A fast machine start has limited value if the agent spends its next minute rebuilding the environment. [Stripe avoids most of that rebuild](https://internal-agents.com/agents/stripe-minions) by starting its Minions in pre-warmed devboxes that already hold recent copies of its main repositories. Include retries and review effort when you calculate cost per accepted result. ### When to build your own agent infrastructure There are two reasons to build the service yourself: 1. A hard requirement that no provider meets, such as a network boundary or a hardware configuration. 2. An advantage in the infrastructure itself, for your product or your economics, that is worth owning. **Everything else starts managed.** Buy first, learn what your agent needs, and then decide whether to build. Building a platform before you understand the workload is the wrong order. AI coding agents make a platform look quick to build, and **that is the trap**: the code is the small part, and operating it is the second product described at the start of this article. A requirement about where the computer runs does not always mean you must build the service. Two middle options keep the compute inside your network: - With **managed BYOC** (bring your own cloud), the provider operates its service inside your cloud account: Northflank can run sandboxes in your account, and Steel can operate its browsers in yours. - With **self-hosting**, your team operates someone else’s software: Microsandbox is an open-source microVM runtime that you can run on your own hardware, and [Steel’s open-source browser API](https://docs.steel.dev/overview/self-hosting/docker) can be self-hosted. **Where the compute runs and who operates it are separate questions.** Before you compare these options, specify who owns patching, capacity, backups, recovery, and incident response. Try them before you build the full service. An existing internal platform is a special case, because you already own it. Compare the work to extend and support it with the integration a provider would require. [Airbnb](https://internal-agents.com/agents/airbnb-airchat) is one example. Its Dev AI team spent months on an internal agent orchestrator that never shipped, then moved to a thin wrapper over vendor coding agents. Those agents run in AirDev, Airbnb’s existing remote development workspaces. **Airbnb bought the agent and kept the computer it already operated.** [Sierra built the infrastructure for Pinecone](https://sierra.ai/blog/pinecone-harnessing-the-wisdom-of-the-workforce), its internal cloud agent. Different roles needed different combinations of tools, data, and permissions, and Sierra says it **could not find a suitable cloud agent provider** at the time. The same infrastructure also supports Ghostwriter, its customer-facing agent product. Sierra had both reasons: a requirement no provider met, and a second product that uses the same infrastructure. “We could build it” is not a reason. ![Three options: buy a managed service, run it in your own cloud through managed BYOC or self-hosting, or build the service yourself](https://steel.dev/blog/build-the-agent-not-its-computer/diagram-6.svg) Three ways to get an agent computer **Before taking ownership, name the requirement and the engineer or team responsible for operating the service.** Include maintenance after the original authors move on. At sufficient utilization, the economics may justify that commitment; at low utilization, staffing can dominate the cost. **Before you sign, ask the provider:** - where browser profiles, files, logs, and backups live - who can access them - who handles upgrades and incidents - what recovery it promises - what evidence you get when something fails - what you can export or delete when you leave **If you buy, plan your exit.** Keep environment recipes and accepted results in forms you can move. Test what happens to unfinished work when you recreate its environment elsewhere. A different SDK is manageable; discovering that essential state cannot leave is a larger problem. We build browser infrastructure and list Steel in the map below, so we have a commercial stake in this recommendation. Any service, including ours, should earn its place by meeting your workload’s requirements and reducing what your team has to operate. ### A map of agent sandbox providers The tests above show that there is no single best provider. **The best one fits your workload**: the agent, the state it must keep, and the result it must deliver. Some providers optimize for inexpensive short-lived execution. Others optimize for persistent workspaces, many parallel sandboxes, development environments, GPUs, or real browsers. The categories below are **starting points, not rankings**, and they overlap. Where to start, Q4 2026 | If you care most about | Providers to look at | Why | | - | - | - | | Low-cost CPU execution in ComputeSDK’s model | Sandbox0, Upstash Box, Isorun | These three are the lowest-cost options in [ComputeSDK’s displayed pricing scenario](https://www.computesdk.com/compare): 60 seconds on an 8 vCPU, 16 GiB sandbox. That is a modeled compute price, not a measured cost to finish your workload. Re-price your own workload before you choose. | | A general-purpose managed sandbox | E2B, Daytona, Blaxel, Vercel Sandbox, Cloudflare Sandbox, CreateOS | Start here when the agent needs to run code in an isolated Linux environment with a shell, files, processes, networking, and an API-managed lifecycle, and your team does not want to operate the worker fleet. All six appear in [ComputeSDK’s sandbox benchmarks](https://www.computesdk.com/benchmarks/sandboxes). | | Coding and software-engineering agents | Daytona, Runloop, Namespace, Together Code Sandbox | Look here when the agent clones repositories, installs dependencies, compiles, runs tests, or starts development servers. Together Code Sandbox is built on CodeSandbox’s microVM technology. | | Long-running or stateful agents | Isorun, Sail, Archil, Sandbox0, Fly Sprites | Evaluate these when work must survive long gaps, setup is too expensive to repeat, or sandboxes must sleep and resume. Check what each one keeps. Isorun and Archil document that files, memory, and running processes survive a pause. By default, a Sandbox0 pause keeps the filesystem but not processes, memory, sockets, or live sessions; a mode that also keeps memory is experimental. Fly Sprites keep their disk while they sleep, and their checkpoints capture the full filesystem; a restore rewinds files but does not resume a paused process. | | Many parallel sandboxes | Modal, Northflank | Look here when every user, task, rollout, or branch needs its own environment. Modal cites more than 100,000 concurrent sandboxes; Northflank cites more than 10,000 isolated workloads. These are vendor-reported figures, so check your account limits and the resource size behind them. | | GPUs or compute-heavy work | Modal, Beam, Northflank, Daytona | Start here when the workload needs GPUs for inference, fine-tuning, or other compute-heavy work. Each lets you attach GPUs to a sandbox. Daytona’s GPU sandboxes are ephemeral and use up to 8 GPUs. | | Control over where the sandbox runs | Northflank, Microsandbox | Consider these when the infrastructure boundary is itself a requirement. Northflank can run sandboxes in your own cloud account. Microsandbox is an open-source microVM runtime that runs locally; its managed cloud is in private beta, and your own cloud or hardware is available on request. | | Cloud browsers for agents that use real websites | Steel, Browserbase, Kernel | Look here when the agent must use real websites: sign in, keep a session, download files, and get through bot protection. Steel is our product. Its browser API is [open source](https://github.com/steel-dev/steel-browser), so you can read the code and self-host it. Steel also offers managed BYOC, where Steel operates the browsers in your cloud account. Browserbase is a managed service and maintains the open-source Stagehand framework. Kernel is a managed browser service that also publishes open-source browser infrastructure. | This table is not complete, and it does not replace a test. A provider can look right on a feature page and still have the wrong lifecycle for your task. Public benchmarks measure narrow things, such as how fast many sandboxes start at once or how long one build takes. None of them tells you whether your agent can continue tomorrow. **Run your own benchmark on your real workload.** When you have two or three possible providers, run the tests from the previous section. Interrupt a real task and resume it. Check the files, processes, credentials, network access, agent progress, and external actions that matter to the result. Then **compare cost per accepted result, not cost per second of compute**, and buy the least infrastructure that reliably gives the agent the environment its job requires. ### Frequently asked questions #### What is an agent’s computer? An agent’s computer is the working environment the agent operates: its browser, files, tools, processes, access, and the state its task needs to continue. It can span several services, such as sandboxes, browser services, and storage. A temporary worker is enough if another service keeps the state the next worker needs. #### What is an agent sandbox? An agent sandbox is an isolated environment where an agent runs code, often code that a model wrote, away from other workloads. It covers code execution; the browser, files, and saved state are other parts of an agent’s computer. Isolation does not limit what the credentials inside the sandbox allow, so define what the task may read and change, and test those limits. #### Should you build or buy AI agent infrastructure? Most teams should buy it. Start with a managed service, and build your own only when no provider meets a hard requirement, such as a network boundary or a hardware configuration, or when owning the infrastructure gives you an advantage you can name. Building a platform before you understand the workload is the wrong order. #### What is the difference between a sandbox and a persistent computer? A persistent computer keeps its state between steps, and a disposable sandbox is thrown away after the work. Persistence makes sense when setup is expensive or a process must keep running. It can save setup time, but compare storage charges, idle billing, wake-up time, and recovery guarantees first. #### What is BYOC? BYOC means bring your own cloud. With managed BYOC, the provider operates its service inside your cloud account, so the compute stays in your network and your team does not build the service. Self-hosting is the other middle option: your team runs someone else’s software, such as an open-source microVM runtime. #### How do you test an agent sandbox provider? Start with one typical task. Disconnect the client and reconnect it, then stop or replace the environment in a disposable test and try to continue from the state the service promises to keep. Record what resumes, what has to restart, and what needs a person. When you compare providers, use cost per accepted result rather than cost per second of compute. ### For the browser layer For the browser part of your environment, start with Steel’s examples for [authentication context](https://docs.steel.dev/cookbook/auth-context), [files](https://docs.steel.dev/cookbook/files), and [computer use](https://docs.steel.dev/cookbook/claude-computer-use). Each example covers one capability. Run the acceptance tests above against any browser layer you consider, including ours. ## Blog: Developer tools come with an assembly required sticker https://steel.dev/blog/developer-tools-come-with-an-assembly-required-sticker · Jul 30, 2026 · By Graham Losee, Michael Spina > Customer-supplied proxies failed 22x more often than managed ones; the harder problem was explaining that inside our own product. **TL;DR:** Over 30 days, customer-supplied proxies failed to establish a tunnel about 9% of the time, against 0.4% for Steel-managed proxies: a 22x gap. Steel does not retry customer proxies because it lacks their semantics, so its errors will name the proxy, its owner, the failing stage and a next step. It answers: - Why do customer-supplied proxies fail more often than managed proxies? - Why doesn't Steel retry or rotate customer-supplied proxies? - What causes a proxy tunnel to fail to establish? - What should a proxy connection failure error tell the user? **TL;DR** - Failures in customer-owned components still feel like product failures. - Better diagnosis can matter more than automatic recovery. - Product observability must cover the full assembled system, not just what you operate. Steel runs browser infrastructure for AI agents. Every session assembles parts from at least two companies: our browsers and network on one side; the customer’s harness, credentials, and sometimes proxies on the other. When the assembled system fails, the error appears on our screen regardless of whose part broke. This is the story of one error, **“Proxy Connection Failed,”** and an investigation into whose failure it actually was. We came out with clear data, a partial fix, and questions we still can’t answer. We’re writing it up because every developer tool that requires assembly has a version of this problem, and we’d like to compare notes. ### Investigating the Root Cause of Proxy Connection Failures Customers kept hitting “Connection Failed” inside Steel sessions. From where they sat, the evidence was damning: a Steel session, a Steel error screen, a dead workload. Tickets treated it as a Steel outage. That was fair. The product gave them no reason to think anything else. ```jsx nocopy Customer’s agent │ ▼ Steel browser ───► Proxy ───► Target website Steel ? ? owns customer or Steel-managed ``` We pulled 30 days of tunnel-establishment data across the fleet and all customers. Customer-supplied proxies failed to establish a tunnel about **9% of the time.** Steel-managed proxies failed about **0.4%** of the time: a **22x gap across thousands of browser workers.** The cleanest comparison came from customers running both modes against the same targets on the same days. Their proxies failed on nearly every session. Ours failed on essentially none. Inside the failing pools, some IPs were blocked by one or two sites, other pools were blocked across hundreds of domains, and in the clearest cases the proxy couldn’t reach a basic IP-echo endpoint. It was down. So, mostly, it wasn’t us. Sheesh, crisis mildly averted. ```jsx nocopy Tunnel-establishment failure rate Customer-supplied proxies ██████████████████████ 9.0% Steel-managed proxies █ 0.4% 22× higher ``` ### Why Customer-Supplied Proxies Are Difficult to Diagnose A customer hands us a proxy as a URL: scheme, host, port, credentials. The URL doesn’t say whether it points at a rotating residential network, a fixed datacenter server, or a sticky session whose exit IP has to survive for the workload to make sense. The failure says little more. A tunnel that never establishes could mean rejected credentials, an unreachable host, or an upstream that dropped the CONNECT. A tunnel that dies mid-session could mean rotation, rate limits, or a destination blocking the exit IP. We sit in the middle of that connection and often can’t tell these cases apart. The customer, one layer further out, has even less to go on. Both sides end up staring at the same screen holding the same incomplete signal. ### Why Managed and Customer-Supplied Proxies Behave Differently Steel-managed proxies get a resilience layer: we health-check endpoints, track pool quality, swap bad proxies, and retry, because we know what each proxy URL represents and when a new IP is safe. Customer-supplied proxies get none of that. The first failed connection kills the session. We still think that asymmetry is the right call. Blind rotation of a proxy we don’t understand could be useless (rotating within a dead pool), expensive (burning the customer’s metered bandwidth), or destructive (breaking a sticky session the workload depends on). Retrying safely requires semantics we don’t have. The trouble is what this reasonable decision produces: a customer with a bad proxy gets a generic failure inside our product, with no pointer toward their own infrastructure. Their vendor flaked; we returned the confusion. ### Better Error Attribution Is a Product Responsibility We already had every fact the investigation needed: which proxy was active, who supplied it, where the tunnel died. Once we knew to look, the analysis was easy. Knowing to look was the problem. Nothing in our monitoring flagged these failures; a customer had to report them. The customer took just as long to notice. The failures dissolved into the volume of sessions they were running. The facts existed the whole time. Neither side’s tooling surfaced them. We’re changing the failure path so they show up where the failure does: the error names the active proxy and its owner, the failing stage when it’s distinguishable, and a concrete next step. When the CONNECT gets a 407, the error should say “the proxy rejected your credentials” and point at the provider. The principle underneath: we can’t always recover from a failure in a component we don’t run, but we can always describe it. ![](https://steel.dev/assets/framerusercontent.com/images/F7YNv62mqhBf4IRuCprut8XgA.png) ### Open Questions About Monitoring Customer-Owned Infrastructure This is where a blog post would usually present the solution. Instead, here are the questions we’re stuck on. They sound proxy-shaped because proxies are where we got burned this time, but each one applies to any component a customer brings, including ones that don’t look like components until they fail. ##### **1. How do you notice a failure in a component you don’t run?** Our trigger was a customer report, and it arrived late; an elevated failure rate reads as noise while the successes keep coming. A proxy-health baseline would have caught this one, but only because we now know proxies are worth watching. The next weak component won’t announce itself. ##### **2. Should a vendor actively test the parts customers bring?** A health check from our fleet would let us say “your proxy is down” before a workload dies. It would also spend the customer’s metered bandwidth, hit their provider’s rate limits, and exercise their credentials outside any session they started. The same tension applies to testing a customer’s harness or their LLM provider. We haven’t found where that line is. ##### **3. Should customers describe the parts they bring?** A declared semantic like rotating, static, or sticky would tell us which proxy retries are safe. Generalized, that means asking customers to document a contract for every component they plug in: more configuration burden, and a wrong declaration produces worse behavior than none. **Where does attribution actually change behavior?** We can name the failing component on the error screen, in the API response, in a webhook, or on the dashboard. We don’t yet know which surface makes a customer check their own infrastructure instead of filing a ticket with us. ### The Reliability Blind Spot in Composable Developer Tools The uncomfortable part of the data wasn’t the 22x gap. It was that our dashboards looked healthy the whole time: sessions starting, traffic flowing, and 9% of customer proxy tunnels quietly dying underneath. **Usage looked like success and wasn’t.** This time the weak part was proxies. Next time it could be the harness, the LLM driving it, or the router a cleaning crew unplugged at the customer’s office. Any developer tool that requires assembly has this blind spot somewhere: a component the vendor doesn’t run, failing inside an experience the vendor owns. If you’ve drawn this responsibility line in your own product, especially if you’ve decided whether to probe infrastructure your customers bring, we’d like to hear how you did it. And if enough of us are traumatised with the same thing, we’ll open a #couch-support channel in the Steel Discord and work through it together. ## Blog: Steel now ships as a browser plugin for Hermes Agent https://steel.dev/blog/hermes-steel-plugin · Jul 22, 2026 · By Nikola Balic > Give Hermes Agent a Steel cloud browser in two commands, with live session viewing, page extraction, and per-site proxy and CAPTCHA controls built in. **TL;DR:** A standalone plugin for Hermes 0.18.0 and newer routes Hermes Agent's existing browser tools through a Steel cloud browser over CDP. Two commands install it; it returns a live session URL, adds steel_scrape for one-shot page extraction, and lets Hermes turn on residential proxies or CAPTCHA solving. Sessions cap at 30 minutes. It answers: - How do I use Steel as the browser for Hermes Agent? - Which Hermes version does the Steel plugin need? - How does the Hermes Steel plugin connect Hermes to a browser? - Can Hermes use residential proxies and CAPTCHA solving through Steel? - Does the Hermes Steel plugin work with self-hosted Steel? [Hermes](https://github.com/NousResearch/hermes-agent), Nous Research's open-source personal AI agent, can now run web tasks through Steel browser. The standalone plugin routes Hermes' existing browser tools through a cloud browser and adds live session viewing and page extraction. Hermes can take on trickier parts of the web, while you watch what it sees. Back in April, [we wired Steel into Hermes and sent it through Google Travel and Booking.com](https://steel.dev/blog/steel-is-now-a-native-browser-provider-in-hermes). Hermes found a hotel, gave us a live view of the session, and built a skill from the run. Trying it yourself meant running our Hermes branch. Hermes now has a plugin interface for browser providers. We moved the integration into its own repo and released it for Hermes 0.18.0 and newer. You need: - Hermes 0.18.0 or newer - A [Steel API key](https://app.steel.dev/settings/api-keys). The free plan is enough to start. ```sh hermes plugins install steel-dev/hermes-steel --enable hermes config set browser.cloud_provider steel ``` The installer asks for your API key. The config command selects Steel; without it, Hermes keeps using local Chromium. Then give Hermes a task: ```js nocopy Navigate to news.ycombinator.com, summarize the front page, and give me the live session URL. ``` The first browser result includes a link like `https://app.steel.dev/sessions/`. Open it and you can see the page the agent sees, which is usually where the answer is when a run gets weird. ### How Hermes browser calls reach Steel Instead of teaching Hermes a second browser toolset, the plugin swaps the provider beneath the tools it already knows. On the first browser call, `SteelBrowserProvider` creates a Steel session and returns its [Chrome DevTools Protocol (CDP)](https://chromedevtools.github.io/devtools-protocol/) WebSocket URL. Hermes' existing browser stack connects to that URL, so `browser_navigate`, `browser_click`, `browser_screenshot`, and the rest of the agent loop do not need Steel-specific versions. The result hook adds the backend name and live-viewer URL to the first successful browser response. Hermes can pass both back to you instead of making you dig through logs or the Steel dashboard. ### Read a page without opening a browser Not every page needs a browser. `steel_scrape` calls Steel's scrape API directly and returns markdown, HTML, readability output, or cleaned HTML. It avoids starting a browser when Hermes only needs to read. Anything that clicks, types, or submits still uses the browser tools. `steel_scrape` is for one-shot extraction, such as reading a list of documentation pages before Hermes writes a summary. ### Session options and timeouts For interactive pages, Hermes stays in the browser. If a site blocks the session or asks for verification, Hermes can call `steel_session_options`, enable a residential proxy or CAPTCHA solving, and try again. These options are set when the browser starts, so changing either one closes the current session and the next browser call creates a new one. Sessions get a 30-minute cap by default. A separate 10-minute inactivity timeout catches sessions left behind when a Hermes process disappears. ### Why it lives in its own repo We took two runs at adding Steel to Hermes itself. [PR #5555](https://github.com/NousResearch/hermes-agent/pull/5555) proved out the provider. By [PR #21182](https://github.com/NousResearch/hermes-agent/pull/21182), Hermes had a proper interface for browser-provider plugins, so we rebuilt on that. We moved the code into its own repo and cut [v1.0.0](https://github.com/steel-dev/hermes-steel/releases/tag/v1.0.0) against that interface. The separate package now tests against Hermes' real plugin loader and the live Steel API without adding Steel-specific code to their repo. ### A few edges Residential proxies and CAPTCHA solving are off by default. They require a verified Steel Launch account: [add at least $10 in paid balance](https://app.steel.dev/settings/billing) to verify the account, and that balance goes toward usage. If either feature is unavailable, the session still starts and Hermes tells you what was dropped. The plugin also supports self-hosted Steel through `STEEL_BASE_URL`. An API key is optional for a custom endpoint, and the plugin sends no credentials when one is not configured. ### Try it Install the [Steel plugin for Hermes](https://github.com/steel-dev/hermes-steel) and run the example above. Humans use Chrome. Agents use Steel. ## Blog: Steel Browser is live in Stripe Projects https://steel.dev/blog/steel-browser-is-live-in-stripe-projects · Jul 15, 2026 · By Nikola Balic > Steel Browser is live in Stripe Projects, giving agents on-demand cloud browser sessions for live web work. **TL;DR:** Steel joins the Stripe Projects developer preview as a co-design and launch partner. Running stripe projects add steel/browser creates a Steel org, project and scoped API key in .env, giving agents cloud browser sessions with live view and recordings, billed through Stripe Projects. Free pay-as-you-go and paid plans exist. It answers: - How do I add Steel to a Stripe project? - What does stripe projects add steel/browser create? - How is Steel billed through Stripe Projects? - What can agents do with Steel browser sessions in Stripe Projects? Today, Steel joins the [Stripe Projects](https://projects.dev/) developer preview as a co-design and launch partner. If you haven't used Stripe Projects yet, it provisions the services your app needs, things like databases, auth, hosting, and analytics, from the terminal or your coding agent. One command creates the account, provisions the resource, and syncs the credentials to your environment: no signing up for each service by hand, no copy-pasting keys between dashboards. Your whole stack comes from one place, and every account and resource stays in an account you own. Steel is the piece that reaches past those services to the rest of the web. Run `stripe projects add steel/browser` and your projects get cloud browser sessions for the live web. Start one session or run many in parallel. The command creates a Steel project, writes Steel API key, and gives your agents browser sessions they can use for real web work: login flows, site verification, vendor workflows with no API, and runs you need to inspect when they break. The hard part is giving agents the things they need around the code: credentials, state, a browser session, and evidence when something goes wrong. That is what Steel adds. > "Agents are becoming the newest and soon-to-be biggest users of the web. We'll be able to delegate any task that we can do online to our agents so we can focus on the biggest needle movers. With Steel in Stripe Projects, agents can get access to browser environments as a true utility to work, research, and purchase for us without a human in the loop." > > Hussien Hussien, CEO & co-founder, Steel ![Run stripe projects add steel/browser and your projects get cloud browser sessions for the live web. ](https://steel.dev/assets/framerusercontent.com/images/VKQOnII1FehcHnnbmSLZaJNUrY.png) ### Steel Browser in Stripe Projects We built the Steel provider integration for the Agentic Provisioning Protocol, so an agent can provision cloud browser sessions the same deterministic way it provisions a database or an auth service. ```md # 0. install the Stripe CLI https://docs.stripe.com/stripe-cli/install # 1. scaffold your project stripe projects init my-app # 2. add Steel stripe projects add steel/browser ``` The credentials point at a Steel org that's yours: your sessions and API keys live in your own Steel account, billed through the payment method you authorized in Stripe Projects. > "Agents need deterministic setup as much as deterministic execution. In Stripe Projects, adding Steel creates the org, project, scoped key, and dashboard handoff in one path, so browser sessions are provisioned like infrastructure, not handled as a manual setup step." > > Nasr Mohamed, CTO & co-founder, Steel ### How Stripe Projects provisions Steel browser sessions Two handoffs happen. **First:** Stripe Projects gives your app a Steel key and writes it into `.env`. **Second:** Steel gives the run a browser session with memory: a session that can stay logged in, a live view while it runs, and a recording when it breaks. Your agent can run one session or many in parallel when the job needs more than one browser. For login-walled sites, Steel's Credentials API can inject secrets into the session while keeping them out of the model's context and off the live viewer. With Steel you can watch the click, the page, the pause, and the exact place where the web pushed back. ### Agent workflows that need a real browser Steel is useful when the work has to happen inside a website. Think vendor portals, invoices behind dashboards, login flows with site verification steps, and internal tools that were never built for agents. In those cases, the browser is the integration. ### Steel is the open browser you actually own Steel is built on ownership, transparency, and openness: an Apache-2.0 runtime you can inspect and self-host, with your sessions, profiles, and credentials living in an account you control. ### Add Steel Browser to a Stripe project Steel is in the Stripe Projects developer preview on a free pay-as-you-go plan and a paid plan for higher limits. See [pricing and limits](https://docs.steel.dev/overview/pricinglimits) for current rates, or follow the [Stripe Projects setup guide](https://docs.steel.dev/overview/stripe-projects) for provisioning, credentials, and SDK usage. Now your agents can reach the rest of the web, one command inside Stripe Projects. ```md stripe projects add steel/browser ``` ## Blog: Launch Week v3: everything we shipped https://steel.dev/blog/launch-week-v3-recap · Jun 29, 2026 · By Nikola Balic > Launch Week v3 recap: Stealth Browser, Dedicated IPs, native Rust and Go SDKs, Atlas, and pricing updates for production agent workloads running on Steel. **TL;DR:** Steel Launch Week v3 shipped Stealth Browser (a Chromium fork for agents), Dedicated IPs at $5 per IP per month on Scale, native Rust and Go SDKs, the open-source Atlas deep research harness, and new pricing with Launch, Scale and Enterprise plans. Signups rose 26% and sessions 18%. It answers: - What did Steel ship in Launch Week v3? - How much do Steel Dedicated IPs cost? - Which languages have native Steel SDKs? - What is Atlas from Steel? - What are Steel's pricing plans? Launch Week v3 is done. Five days, five releases. Shipping all of it on a tight, self-imposed schedule was not easy. We loved it anyway. By the looks of it, you did too: signups rose 26%, sessions rose 18%, and resurrected organizations rose 28%. Thank you for making our calendar look slightly less unreasonable. Agents are workloads now. A demo succeeds once. A workload has to succeed on Monday and again on Tuesday, with the same identity, inside the same budget. You see what it spent. This week shipped those parts. ### Stealth Browser, a Chromium built for agents On Day 1, we shipped [Stealth Browser](https://steel.dev/blog/stealth-browser), our fork of Chromium for agents that run the web, not just click through it. The profile loads inside the browser process at startup, before page JavaScript reads navigator, screen, WebGL, fonts, and the rest. It also cuts stock-Chromium code a headless agent never touches. Available now for enterprise customers on request. [Video on YouTube](https://www.youtube.com/watch?v=7EjchdTlG0Q) ### Dedicated IPs, a fixed home for browser agents On Day 2, we launched [Dedicated IPs](https://steel.dev/blog/dedicated-ips), a stable network origin for production agents. Lease a stable IP, attach it to a profile, and let the agent return as the same signed-in visitor every run. They are self-serve on Scale at $5 per IP per month, with custom Enterprise pricing. Reserve up to 25. Pin one to a session, or let Steel write one from your pool to the profile. [Video on YouTube](https://www.youtube.com/watch?v=mOF1NADON_U) ### Rust and Go SDKs, native clients in four languages On Day 3, we added [native Rust and Go SDKs](https://steel.dev/blog/rust-go-sdks), so you can drive Steel from the stack your agents already run in. Steel now has native clients alongside TypeScript and Python. The session flow stays the same: create a session, set profile and proxy options, hand the WebSocket URL to your CDP tool, then release the session from that client. Rust is async-first. Go is context-first. [Video on YouTube](https://www.youtube.com/watch?v=cZjEM5PFXJs) ### Atlas, a deep research harness you can own On Day 4, we open-sourced [Atlas](https://steel.dev/blog/atlas-sdk), a deep research harness (`@steel-dev/atlas`) with your models, sources, and domain tools. It runs a research loop, not a single prompt: plan, gather evidence, synthesize, audit, patch gaps, then ground citations. The ledger sits outside the model's context window. Atlas can use Steel for JS-rendered or login-gated pages. EDGAR, PubMed, arXiv, or internal docs can come in through `researchTool`. Find Atlas on [GitHub](https://github.com/steel-dev/atlas). [Video on YouTube](https://www.youtube.com/watch?v=8HzzKsAVrWI) ### Simpler pricing, built for how agents run On Day 5, we [rebuilt pricing](https://steel.dev/blog/pricing-update) for how agents actually run, with metered rates and visible usage. The three plans are Launch ($0 + usage), Scale ($250 + usage), and Enterprise (custom). Launch includes a one-time $30 usage credit. Scale includes $100 each month. The dashboard shows spend by meter. Existing customers can keep their legacy plan. [Video on YouTube](https://www.youtube.com/watch?v=47-aqXBpRTA) ### What it adds up to Together they are what an agent needs after the demo: a place to run that does not drift, the same identity each time, a stack you build in, output you check, and cost you can see before the bill lands. That is the week. Agents run like workloads now, and the way you pay for them matches. ### What's next We are already working on the next set of improvements for agents. If there is a feature, workflow, or rough edge you want us to prioritize, send it our way. Join us in [Discord](https://discord.gg/steel-dev), tag us on X [@steeldotdev](https://twitter.com/steeldotdev), or reply with the feature request you want Steel to build next. → [**Start a project**](https://app.steel.dev/) See the full week at [steel.dev/launch-week](https://steel.dev/launch-week). ## Blog: Simpler pricing, built for how agents run https://steel.dev/blog/pricing-update · Jun 26, 2026 · By Hussien Hussien > Steel pricing now has Launch, Scale, and Enterprise plans with metered browser rates, usage credits, visible usage, and plan limits for agent workloads. **TL;DR:** Steel pricing now has three plans on one metering model: Launch ($0 plus usage, a one-time $30 credit), Scale ($250 plus usage, $100 credit each month, 100 concurrent sessions) and custom Enterprise. CAPTCHA solving unlocks on Launch with a $10 deposit. A dashboard shows spend by meter. It answers: - What are Steel's pricing plans and what do they cost? - What are the limits on Steel's Launch, Scale and Enterprise plans? - How do I get CAPTCHA solving on Steel without a subscription? - What usage credit do Steel plans include? - Do existing Steel customers have to move to the new pricing? - What does Steel meter and charge for? > Launch Week v3 -> Day 05 / 05 Today we're launching simpler pricing: three plans on one metering model, rates you can read before an agent runs, and a dashboard that shows where every dollar goes. It's the biggest change to how Steel is priced since we started. The old pricing got in the way of the one thing agents are for: doing the work. Anti-bot, the part that makes Steel useful, was locked behind a monthly subscription. So the builder pointing an agent at a task couldn't watch it work before paying for it. And the fixed bundles underneath never matched how agents run, with usage that spikes one week and goes quiet the next. Here's the moment it used to break. You send an agent to do a job for you, and partway through it hits a CAPTCHA. On the old plan, solving it meant upgrading before you'd seen the workflow run, so you closed the tab. Now Launch gives you `$30` in credits to try Steel without a subscription. If your agent needs CAPTCHA solving, you unlock it with a `$10` deposit, and your credits cover the work. You prove Steel works on your hardest use case before committing. One customer put it plainly: "I didn't want another subscription. I just want to pay for what I use." This is us agreeing. So we rebuilt it. You pay for what you run. Pricing closes Launch Week v3. After [Stealth Browser](https://steel.dev/blog/stealth-browser) for runtime, [Dedicated IPs](https://steel.dev/blog/dedicated-ips) for identity, [Rust and Go SDKs](https://steel.dev/blog/rust-go-sdks) for backend services, and [Atlas](https://steel.dev/blog/atlas-sdk) for deep research. Pricing has to keep up. ### The new pricing model The base meters (browser hours, proxy bandwidth, CAPTCHA solves, and `/scrape`) are all on the [pricing page](https://steel.dev/pricing). ![](https://steel.dev/assets/framerusercontent.com/images/jurDil3Zfhwo7G9dygDnEmxyQZM.png) The limits are visible: Launch starts with 10 concurrent browser sessions, 15-minute max sessions, up to 3 seats, 60 requests per minute, and 7-day data retention. Scale moves to 100 concurrent sessions, 1-hour sessions, unlimited seats, 600 requests per minute, 14-day retention, dedicated support, Enterprise SSO, and Dedicated IPs at `$5 / IP`. Enterprise is custom, with 1,000+ concurrent sessions, sessions up to 24 hours, reserved browser pools, Stealth Browser, custom Dedicated IP pricing, and support and uptime SLAs. ### Usage credit is explicit Launch includes a one-time `$30` usage credit, valid for 90 days. Scale includes `$100` in usage credit each month. Enterprise is custom. You see the credit up front, then monitor it. Browser hours and proxy bandwidth each show up as their own meter. ### A real usage dashboard When the spend is real, you want to see it. The dashboard breaks it down by meter: session time, proxy bandwidth, CAPTCHA solves, and your remaining balance. No waiting for an invoice to find out where the money went. Top up credits, or turn on auto top-up so a run never stalls for lack of balance. [Video](https://steel.dev/assets/framerusercontent.com/images/N8kQIbeBRSKUAOYS6nz9fg898.mp4) ### Why we priced it this way We matched the model to how AI builders buy infrastructure: start on usage, watch what you spend, then move up when the workload needs it. Scale's **$250** buys capacity. CAPTCHA solves, proxy bandwidth, and browser hours all cost less on Scale. It gets you 100 concurrent sessions, hour-long sessions, Dedicated IPs, Enterprise SSO, and dedicated support. It fills the gap teams used to fall into: past the entry limits, not ready for a custom contract. Enterprise covers high-volume and regulated workloads that need custom limits, Stealth Browser, reserved pools, and SLAs. We win on being open source, on showing you the bill, and on the hard problems (anti-bot, proxies, CAPTCHA) that we actually solve. And when something breaks, we answer. ### If you're already a customer No forced migration. Existing Starter, Developer, and Startups customers see their legacy plan in the dashboard and can switch when they choose. ### Get started - [Pricing and limits](https://docs.steel.dev/overview/pricinglimits) - [Dashboard](https://app.steel.dev/) Questions? [Discord](https://discord.gg/steel-dev) or [@steeldotdev](https://twitter.com/steeldotdev). > This post is Day 5 of Steel Launch Week v3. See the full week at [steel.dev/launch-week](https://steel.dev/blog/launch-week-v3-recap). [Video on YouTube](https://www.youtube.com/watch?v=47-aqXBpRTA) ## Blog: Atlas: a deep research harness you can own https://steel.dev/blog/atlas-sdk · Jun 25, 2026 · By JunHyoung Ryu > Atlas is an open-source deep research harness that turns web evidence into cited reports, with a ledger for coverage, audit, and citation grounding. **TL;DR:** Atlas is an open-source deep research harness (npm install @steel-dev/atlas) that turns a question into a cited report through a ledger of slots: plan, gather, synthesize, audit, patch and ground citations. On Perplexity's DRACO benchmark it scores level with Perplexity Sonar on GPT-5.5 and near Exa on Opus 4.8. It answers: - What is Atlas by Steel? - How does Atlas ground citations in deep research reports? - How does Atlas score on the DRACO benchmark? - How do I add a custom domain source like PubMed to Atlas? - How do I install the Atlas deep research SDK? - How much does an Atlas research run cost? > Launch Week v3 -> Day 04 / 05 [Atlas](https://github.com/steel-dev/atlas) is an open-source deep research harness you can run, fork, and bend around your own domain. It gives you the loop in one SDK: ask a question, gather evidence across the open web, and get back a cited report. Before it lands, Atlas checks the draft against the ledger and grounds the citations. The job is simple: stop every team from rebuilding the same brittle research loop, while leaving the knobs exposed. Swap models, search providers, browser fetches, and domain sources. This is the next step in Launch Week v3: [Stealth Browser](https://steel.dev/blog/stealth-browser) for the browser runtime, [Dedicated IPs](https://steel.dev/blog/dedicated-ips) for stable network identity, [Rust and Go SDKs](https://steel.dev/blog/rust-go-sdks) for the services doing the work, and Atlas for the research layer. Install it: ```sh npm install @steel-dev/atlas ``` Then bring your model provider, wire in search or browser fetches, and start shaping the loop around your domain. ![](https://steel.dev/assets/framerusercontent.com/images/tdyEZ4Ks5MDtnbdoNvUliqi0RE.png) ### Why deep research is hard to build A lot of teams are putting deep research agents inside their products. Some already use Steel for the browser side. The painful part is everything around it: planning the search, keeping sources straight, tracking what’s still unanswered, and staying inside a budget. The common failure is quieter than a fake source. A long run reads a pile of pages, the early evidence gets summarized away, the writer drops a real fact it found twenty turns ago, and the final report looks clean while quietly missing the thing you asked for. Atlas is built around that failure. ### How Atlas works A single run is a loop built around a ledger. The ledger is a coverage contract. Atlas breaks the question into slots: the things the answer has to cover. A slot pairs a question with its eventual grounded finding. Atlas strips numeric literals out of slot phrasing, so the planner has to earn the answer from evidence. As evidence arrives, each slot becomes grounded, computed from other slots, or marked exhausted with a reason. The run moves through the ledger like this: - **Plan.** Seed the ledger with slots from the question. - **Gather.** A research agent searches, fetches, and reads, closing slots as it lands grounded answers. - **Synthesize.** Write the full report from what’s been gathered. - **Audit.** Walk the draft back against the ledger and find the gaps: open slots, facts the writer gathered then dropped. - **Patch and reconcile.** Fetch what’s missing, fill the gaps, then a final pass using only sources already in the store. - **Ground.** Renumber every citation, validate that each `[n]` points at a real fetched source, and surface any sentence whose citation doesn’t hold up. The audit step is where the ledger pays for itself. Atlas checks the finished draft against what it meant to answer, then catches dropped facts, unanswered slots, and citations that point nowhere. Because grounding happens last, the result includes `result.unsupportedSentences`: the sentences whose citations did not survive. ### How it scores on DRACO DRACO is Perplexity's open deep research benchmark: 100 real-world tasks across 10 domains, each graded against \~39 weighted criteria where wrong answers count against you. We ran a simple setup of Atlas on it next to the commercial agents. ![](https://steel.dev/assets/framerusercontent.com/images/PoqTzghGLRsxuEm2Bl3OwDVcdr0.png) The band is tight. Atlas on GPT-5.5 lands level with Perplexity Sonar, a hair above on Perplexity's own benchmark, and at lower cost. Atlas on Opus 4.8 sits next to Exa. Nobody runs away with it. That is the point: an open harness you can fork and aim at your own sources scores in the same range as the closed products, not a tier below them. ### Watch one run Here’s a real run: Claude Opus 4.8 at `balanced` effort, reading a single SEC filing. The question asks Atlas to analyze a margin shift and adjust it for a one-time relocation loss: > Analyze how Rubber Leaf’s strategic shift from direct to indirect supply models has affected underlying profitability by examining gross margin performance across Q1 2023 and Q1 2024 \[…] then determine what Rubber Leaf’s Q1 2024 gross margin would be when excluding the factory relocation loss from total cost of sales. Atlas turned that into a 15-slot ledger. A sample: | ask | shape | importance | | - | - | - | | Year-over-year change in gross margin from Q1 2023 to Q1 2024 (in percentage points) | `value` | central | | Rubber Leaf’s adjusted Q1 2024 gross margin excluding the factory relocation loss | `value` | central | | How the indirect supply model alters cost structure and margin economics versus the direct model | `causal` | central | | Verdict on whether margin pressure is driven by fundamental challenges in the indirect model or by temporary relocation costs | `verdict` | central | | Other one-time or non-recurring items affecting Q1 2024 cost of sales beyond the relocation loss | `split` | peripheral | The asks avoid guessing the values. They name what Atlas has to establish. Each one closes as **grounded** with a value, quote, and source, or **computed** with `run_code` or synthesis across sources. The report starts with the computed result: gross margin fell from **+2.86%** to **-6.65%**, a 9.52-point drop. Stripping out the **$190,703** one-time relocation loss lifts it only to **-0.20%**. The more interesting part is what the run caught on the way: > the 10-Q’s MD\&A “Gross profit margin” table states Total margins of 7% (2024) and 3% (2023) \[…] Those stated percentages do not reconcile with the negative gross profit reported on the company’s own income statement and appear to be a misstatement; the analysis below uses the gross profit and revenue figures from the consolidated statement of operations. Atlas caught the contradiction in the filing and recomputed from the primary statements. The whole run took about four minutes, and **zero** sentences failed citation grounding: | phase | time | | - | - | | seed (ledger) | 9.8s | | gather | 89s | | synthesize | 44s | | coverage audit | 5.8s | | patch | 24s | | reconcile | 16s | Three pages fetched, one cited, the filing itself. ### Orchestrate researchers By default, a run follows one spine. When a question needs more than one angle, you can register multiple researchers. Atlas decomposes the question, routes each sub-task to the best-fit worker (`query → report`), runs them in isolation, then synthesizes one cited report. ```ts import { Atlas, exa, perplexity, parallel } from "@steel-dev/atlas"; import { anthropic } from "@ai-sdk/anthropic"; const atlas = new Atlas({ model: anthropic("claude-fable-5"), researchers: { exa: exa.agent(), perplexity: perplexity.agent(), parallel: parallel.agent(), }, }); await atlas.research("Compare X across academic and shopping angles"); ``` Any `query → report` worker plugs in: Exa deep research, Perplexity Sonar, parallel.ai, or your own. `atlas.asResearcher(describe)` exposes an Atlas instance as a worker, so fan-out can recurse. Omit `researchers` and Atlas stays on the single-spine path. ### Uses Steel for browser fetches Atlas can use Steel for browser fetches, so the loop reaches pages a basic fetch misses: JavaScript-rendered sites, sessions that need login state, pages that need a real browser to stay alive long enough to be useful. Atlas can start with direct extraction and fall back to a Steel browser scrape when the page needs it. Search can come from Exa, Tavily, Brave, or the model provider’s own web search, so a run can use more than one source path. ![](https://steel.dev/assets/framerusercontent.com/images/vCdZ9GCmgYX2WRLKFTGvtSqG6I.png) ### The parts builders ask about A single `model` runs everything. If you want more control, override `research` and `write`; unset roles fall back to the lead model. ```ts import { Atlas, exa, steel, basic } from "@steel-dev/atlas"; import { anthropic } from "@ai-sdk/anthropic"; import { openai } from "@ai-sdk/openai"; const atlas = new Atlas({ model: anthropic("claude-fable-5"), models: { research: openai("gpt-5-mini") }, search: exa.search(), fetch: [basic.fetch(), steel.fetch({ proxy: true })], }); ``` Every model goes through the same wrapper: budget accounting, retries, caching, tracing, and the provider quirks that would otherwise leak into your code. **It keeps a trail.** Atlas records searches and dead-end fetches so a later step can pick a new angle after a query goes nowhere. The trail is a plain record of what already wasted time. **It streams.** Start a run and subscribe to events: `report.delta` carries the draft as it’s written, `report.reset` precedes each rewrite, `source.fetched` fires as pages come in. Await the final result when you want it. ```ts const run = atlas.start(question, { effort: "balanced" }); for await (const e of run.events()) { if (e.type === "report.reset") preview = ""; if (e.type === "report.delta") preview += e.text; } const result = await run.result(); ``` **It resumes.** Journaled runs replay completed model, search, and fetch calls at no cost after a crash or deploy. **It returns structured output.** Pass a `schema` and Atlas runs a final extraction pass over the finished report. You get a typed object alongside the markdown, on the single-spine path or the orchestrated path. ```ts import { z } from "zod"; const r = await atlas.research("Acme's latest annual revenue and CEO?", { schema: z.object({ revenue: z.string(), ceo: z.string() }), }); r.object; r.report; ``` ### Safety Atlas treats untrusted web content as data. Fetches pass SSRF guards. `run_code` runs in a memory-capped V8 isolate with no network, filesystem, or host access. Direct fetch honors robots.txt. Treat the SSRF guard as defense-in-depth; for hostile targets, run behind network-level egress controls. The [README](https://github.com/steel-dev/atlas) has the full picture. ### Built with agents Most of Atlas was written through AI pair-programming. The structural decisions are human: the shape of the loop, where the audit sits, what the ledger holds. The code came fast. The hard part was choosing what lives outside the model: the ledger, the budget meter, the trail of searches, the point where the run stops and checks its own work. ### Built for verticals Generic research tools hit a ceiling fast. Atlas is meant to be extended into a domain: a finance researcher that reads filings, a health researcher that works through clinical literature, a workflow with its own weird sources and review rules. `researchTool` is for that. If the right source is EDGAR, PubMed, arXiv, OpenAlex, ClinicalTrials.gov, or your internal docs, wire it in directly; anything you add through `ctx.addSource` flows into the same source store, the same report, and the same grounding pass. For a full example, the [Atlas public company research demo ](https://github.com/steel-experiments/atlas-demo)shows the pattern applied to public biotech research with SEC filings, trial registries, PubMed, and optional Steel browser fetches. ```ts import { Atlas, researchTool } from "@steel-dev/atlas"; import { z } from "zod"; const atlas = new Atlas({ model, tools: { pubmed_search: researchTool({ description: "Search PubMed.", inputSchema: z.object({ query: z.string() }), execute: async ({ query }, ctx) => { const studies = await pubmed.search(query, { signal: ctx.signal }); for (const s of studies) ctx.addSource({ url: s.url, title: s.title, content: s.abstract }); return studies.map((s) => `- ${s.title}`).join("\u000a"); }, }), }, }); ``` ![](https://steel.dev/assets/framerusercontent.com/images/948ZTWl3y3PDXl5lGwQsOLV7Wo.png) ### Where Atlas needs care Atlas grounds and audits. Weak sources still produce weak evidence; paywalled, missing, or contradictory sources still need a human’s judgment. The better you know your domain’s sources and failure modes, the better you can tune Atlas around them. It also costs real money. A balanced run targets a couple of dollars, so runs need explicit budgets and caps. `maxUSD` is a best-effort target; provider calls can still land above it. If you bring a custom model, pass its `pricing` so the budget math stays honest. Before you spend real money tuning a vertical, use the [Browser Agent Leaderboards](https://leaderboard.steel.dev/) to narrow the model list. [DRACO](https://leaderboard.steel.dev/leaderboards/draco/) is the closest fit for Atlas because it grades full research reports across 100 expert-scored tasks, not short answers. Treat it as a filter, not a verdict: each row mixes a model, harness, judge, budget, and tool setup. ### Get started - Install: `npm install @steel-dev/atlas` - Package: [@steel-dev/atlas on npm](https://www.npmjs.com/package/@steel-dev/atlas) - [GitHub](https://github.com/steel-dev/atlas) - [Quickstart](https://github.com/steel-dev/atlas#readme) - [CLI example](https://github.com/steel-dev/atlas/blob/main/examples/cli.ts) - [Local web UI example](https://github.com/steel-dev/atlas/blob/main/examples/serve.ts) - [Domain tool examples](https://github.com/steel-dev/atlas/tree/main/examples) - [Evals](https://github.com/steel-dev/atlas/tree/main/evals) - [Browser Agent Leaderboards](https://leaderboard.steel.dev/) - [Public company research demo](https://github.com/steel-experiments/atlas-demo): a biotech-company memo workflow using SEC EDGAR, ClinicalTrials.gov, PubMed, optional web search, and optional Steel browser fetches. Questions? [Discord](https://discord.gg/steel-dev) or [@steeldotdev](https://twitter.com/steeldotdev). > This post is Day 4 of Steel Launch Week v3. See the full week at [steel.dev/launch-week](https://steel.dev/blog/launch-week-v3-recap). [Video on YouTube](https://www.youtube.com/watch?v=8HzzKsAVrWI) ## Blog: Steel Speaks Rust and Go https://steel.dev/blog/rust-go-sdks · Jun 24, 2026 · By JunHyoung Ryu > Native Rust and Go SDKs for Steel: drive the browser and the computer from the stacks where agent infrastructure already runs. **TL;DR:** Steel shipped native Rust and Go SDKs as typed API clients for sessions, profiles, credentials, files, extensions, screenshots, scraping, PDFs and the computer endpoint. They create and release sessions and hand the WebSocket URL to CDP tools such as chromiumoxide, chromedp or rod. Cookbook examples ship alongside. It answers: - Does Steel have a Rust SDK? - Does Steel have a Go SDK? - How do I create and release a Steel session from Rust or Go? - Which CDP libraries work with the Steel Rust and Go SDKs? - What do the Steel Rust and Go SDKs cover? > Launch Week v3 -> Day 03 / 05 A lot of agent infrastructure is written in Rust and Go: edge runtimes, orchestration layers, services where latency matters, the backend jobs that have to run again tomorrow. Until now, if you lived in that world, you had to maintain your own bindings against the Steel API. Not anymore. Steel now has **native Rust and Go SDKs.** This is where Launch Week v3 has been heading: [Stealth Browser ](https://steel.dev/blog/stealth-browser)for the runtime, [Dedicated IPs](https://steel.dev/blog/dedicated-ips) for stable network identity, and now Rust and Go SDKs for the services doing the work. ### Why we built native SDKs We felt the gap ourselves. We rewrote [our own CLI ](https://steel.dev/blog/steel-cli-and-agent-skill)in Rust, and we kept running into the same thing our customers did: Rust and Go both have real browser tooling, *chromiumoxide* on the Rust side and *chromedp* or *rod* on the Go side, but there was no Steel client for either. So we built both. ![](https://steel.dev/assets/framerusercontent.com/images/QMziEa5muy2w5s18ajX4atP28.png) ### How the SDKs work The latest Rust and Go SDKs are typed API clients for Steel. They cover the resources a workload usually needs: sessions, profiles, credentials, files, extensions, screenshots, scraping, PDFs, and the session computer endpoint. ![](https://steel.dev/assets/framerusercontent.com/images/23ruAutD9Q1EDnotHXDCfxTS7g.png) The SDK handles the Steel side of the loop. Create a Steel session from Rust or Go, set profile and proxy options in the same client, then pass the returned WebSocket URL to the CDP tool you already use: chromiumoxide, chromedp, rod, or something else. When the run is done, release the session from that same SDK. Both also plug into our computer API, so CUA agents can use OS-level actions through the same client. In Rust, the session lifecycle stays in one client: ```rust use steel::Steel; let client = Steel::new(std::env::var("STEEL_API_KEY")?); let session = client.sessions().create(steel::SessionCreateParams::default()).await?; println!("CDP: {}", session.websocket_url); client.sessions().release(&session.id, steel::SessionReleaseParams::default()).await?; ``` The Go version keeps the same shape, with context and explicit errors: ```go client := steel.NewClient(os.Getenv("STEEL_API_KEY")) session, err := client.Sessions.Create(ctx, steel.SessionCreateParams{}) if err != nil { return err } fmt.Println("CDP:", session.WebsocketURL) _, err = client.Sessions.Release(ctx, session.ID, steel.SessionReleaseParams{}) ``` Rust is async-first. Go is context-first. Both keep session, proxy, and response config typed. If you’ve used the TypeScript or Python SDK, the loop will feel familiar. It does not cover every helper yet; v1 is about creating sessions, configuring them, handing off the CDP URL, and releasing them without leaving Rust or Go. ### Cookbook examples are live We also shipped the examples around the SDKs, because the client alone is not where the integration work ends. #### Browser automation These show the handoff from a Steel session to the CDP tool, then release the session from the SDK. - [chromedp](https://docs.steel.dev/cookbook/chromedp) in Go - [rod](https://docs.steel.dev/cookbook/rod) in Go - [Playwright](https://docs.steel.dev/cookbook/playwright#go) in Go - [chromiumoxide](https://docs.steel.dev/cookbook/chromiumoxide) in Rust - [headless_chrome](https://docs.steel.dev/cookbook/headless-chrome) in Rust #### Direct Steel APIs These cover the parts people usually had to wrap themselves: logged-in state, credential fill, extension attachment, file upload, screenshot, PDF, and scrape. - scrape in [Rust](https://docs.steel.dev/cookbook/scrape#rust) and [Go](https://docs.steel.dev/cookbook/scrape#go) - profiles in [Rust](https://docs.steel.dev/cookbook/profiles#rust) and [Go](https://docs.steel.dev/cookbook/profiles#go) - auth context in [Rust](https://docs.steel.dev/cookbook/auth-context#rust) and [Go](https://docs.steel.dev/cookbook/auth-context#go) - credentials in [Rust](https://docs.steel.dev/cookbook/credentials#rust) and [Go](https://docs.steel.dev/cookbook/credentials#go) - extensions in [Rust](https://docs.steel.dev/cookbook/extensions#rust) and [Go](https://docs.steel.dev/cookbook/extensions#go) - files in [Rust](https://docs.steel.dev/cookbook/files#rust) and [Go](https://docs.steel.dev/cookbook/files#go) #### Agent frameworks - [Genkit](https://docs.steel.dev/cookbook/genkit) in Go - [Eino](https://docs.steel.dev/cookbook/eino) in Go - [LangChainGo](https://docs.steel.dev/cookbook/langchaingo) in Go - [Google ADK](https://docs.steel.dev/cookbook/google-adk#go) in Go - MCP in [Rust](https://github.com/steel-dev/steel-cookbook/tree/main/examples/mcp-rs) and [Go](https://github.com/steel-dev/steel-cookbook/tree/main/examples/mcp-go) - [Rig](https://docs.steel.dev/cookbook/rig) in Rust - [Swiftide](https://docs.steel.dev/cookbook/swiftide) in Rust #### Computer use - Claude in [Rust](https://docs.steel.dev/cookbook/claude-computer-use#rust) and [Go](https://docs.steel.dev/cookbook/claude-computer-use#go) - OpenAI in [Rust](https://docs.steel.dev/cookbook/openai-computer-use#rust) and [Go](https://docs.steel.dev/cookbook/openai-computer-use#go) - Gemini in [Rust](https://docs.steel.dev/cookbook/gemini-computer-use#rust) and [Go](https://docs.steel.dev/cookbook/gemini-computer-use#go) ### Get started - Rust package: [steel-rs on crates.io](https://crates.io/crates/steel-rs) - Rust source: [github.com/steel-dev/steel-rust](https://github.com/steel-dev/steel-rust) - Go package: [github.com/steel-dev/steel-go on pkg.go.dev](https://pkg.go.dev/github.com/steel-dev/steel-go) - Go source: [github.com/steel-dev/steel-go](https://github.com/steel-dev/steel-go) - Cookbook: [docs.steel.dev/cookbook](https://docs.steel.dev/cookbook) Try the loop in the stack you already ship, then tell us which language support or helper surface you need next. Questions? [Discord](https://discord.gg/steel-dev) or [@steeldotdev](https://x.com/steeldotdev). > This post is Day 3 of Steel Launch Week v3. See the full week at [steel.dev/launch-week](https://steel.dev/blog/launch-week-v3-recap). [Video on YouTube](https://www.youtube.com/watch?v=cZjEM5PFXJs) ## Blog: Dedicated IPs give production agents a stable network origin https://steel.dev/blog/dedicated-ips · Jun 23, 2026 · By Dane Wilson > Dedicated IPs give production agents a stable network origin. **TL;DR:** Dedicated IPs give a reused browser profile the same egress IP across runs, so sites stop seeing a login jump across the world. Paid plans can lease up to 25 IPs from Settings > Network and pin a session to one, or let Steel pick one and persist it to the profile. A fixed IP suits authorized logged-in workflows, not bulk scraping. It answers: - How do I pin a Steel session to a dedicated IP? - Why do logged-in browser agents get logged out when their IP changes? - How many dedicated IPs can I lease on Steel? - When is a fixed IP the wrong choice for a browser agent? - Which Steel plans include dedicated IPs? > Launch Week v3 -> Day 02 / 05 Agents that log in need a stable network origin. If you reuse a profile across sessions, you’ve probably run into the impossible travel problem, even if you never had a name for it. Steel can save the cookies and fingerprint so the browser looks the same. Then the next run comes from a different IP. To Google, Amazon, or Discord, the account just jumped across the world between requests. That is how you end up logged out, challenged, or flagged. Dedicated IPs close that loop. Your agent comes back from the same network origin, so the login has a fair chance to stick. ![](https://steel.dev/assets/framerusercontent.com/images/HqV2u3ZX27ZmnlJc92HsdtOGdU.png) ### How Dedicated IPs work You can lease up to 25 dedicated IPs yourself from **Settings > Network**. If you need more, contact support. Then pin a session to a specific IP, or let Steel choose from the IPs your org owns. ```ts // Use any of your dedicated IPs const session = await client.sessions.create({ useProxy: { type: "fixed" } }); // Or pin to a specific one const session = await client.sessions.create({ useProxy: { type: "fixed", id: "fixed:8SdfYs77me" } }); ``` If you pass an `id`, that session uses that exact IP. If you leave `id` out, Steel chooses one at random from your pool and persists it to the profile, so the same profile reuses the same IP across runs. The load still spreads across what you own. When you no longer need an IP, release it. ### Lease and manage your IPs The **Network** page in Settings handles the whole lifecycle: lease IPs, see each IP’s status, check available locations before you lease, and release an IP when you no longer need it. ![](https://steel.dev/assets/framerusercontent.com/images/CWVzfmaWar4Itx8yoO0GmgKwgE.png) ### Why stable IPs keep logins working Logged-in workflows usually break in one place: the first run after you thought you were done with 2FA. Cookies and fingerprints help, but the site still sees the network origin change. The browser looks familiar. The IP does not. Pair a Dedicated IP with a persistent profile and the boring thing happens: same browser identity, same egress IP. You log in once, then the agent can reuse that auth state across runs without a human stepping in every time. Profiles preserve browser identity. Dedicated IPs preserve network identity. For authorized, logged-in, audited workflows, stable egress is what you want. The site sees the same returning visitor. You can allowlist a known egress IP. Your security team knows which IP belongs to which workload. If you’re reusing authentication at scale, that is the use case. ### When a fixed IP is the wrong choice Dedicated IPs have a narrow job. For high-volume, unapproved collection, a single IP can collect negative signals quickly. A fixed IP does not make a bad workflow acceptable. A burned account still needs remediation. Site policies still apply. The strongest pattern is the boring one: a durable profile, a dedicated IP, consistent behavior, and permission to run the workflow. ### Get started Dedicated IPs are available on all paid plans. You can lease up to 25 self-serve from **Settings > Network**, with optional geo targeting. - [Documentation](https://docs.steel.dev/overview/sessions-api/dedicated-ips) - [Dashboard](https://app.steel.dev/settings/network) - [Pricing](https://steel.dev/pricing) Questions? [Discord](https://discord.gg/steel-dev) or [@steeldotdev](https://x.com/steeldotdev). > This post is Day 2 of Steel Launch Week v3. See the full week at [steel.dev/launch-week](https://steel.dev/blog/launch-week-v3-recap). ## Blog: Stealth Browser: A Chromium Built for Agents https://steel.dev/blog/stealth-browser · Jun 22, 2026 · By Jagadesh P > Stealth Browser is Steel's custom Chromium fork for agent workloads: stable browser-level signals from startup and a lighter runtime. **TL;DR:** Stealth Browser is Steel's own Chromium fork that applies the browser profile in the browser process, so GPU, WebGL and other fingerprint signals stay consistent from the first request instead of being patched by injected JavaScript. It is open to every enterprise customer at no additional cost, with access gated per workload. It answers: - What is Steel Stealth Browser? - Why do injected JavaScript stealth patches fail against strong antibot systems? - How does a Chromium fork help agents avoid bot detection? - Who can use Stealth Browser and what does it cost? > Launch Week→ Day 01 Today we're shipping **Stealth Browser**: our own Chromium fork, hardened at the source so user-permissioned agents stop getting blocked by the web's toughest antibot systems. It's been running in production with a handful of beta customers for a few weeks. Starting today, we're opening it to every enterprise customer at no additional cost. ![](https://steel.dev/assets/framerusercontent.com/images/ARCMl8mYA5gEpIlJZd8hsUzLQI.png) ### Stealth is usually the first thing that breaks Most agent work is mundane and authorized. Signing into an account you own, pulling a report from an internal tool, running a workflow a customer asked for. But the open web can't tell your agent from a malicious bot. So legitimate, user-permissioned agents get challenged and blocked by sites that have no reason to block them. For a lot of teams, this is the number one blocker. The antibot fires, the workflow stalls, and your use case doesn't matter: you're banned by default. Our job is to get you past that. We patch the signals that tell a site you're running automation, so your agent looks like a person on a normal browser. ### Patching on top got us far, then hit a ceiling Until today, most of that work was a layer on top of a stock browser. Start Chrome, patch the user agent, add a proxy, inject JavaScript to clean up the obvious fingerprint surfaces. That unblocks a huge range of common sites. For most jobs, it's enough. But the heaviest antibot systems still flagged our customers. They read browser-level signals you can't reach from an injected script: GPU and WebGL fingerprints, media devices, OS-level details. Injected patches are also late. They race the page's own code, miss browser-level surfaces, and leave inconsistencies the challenge can detect. To get past those systems, we had to go lower. ### So we forked Chromium Stealth Browser moves the fingerprint work out of injected JavaScript and into Chromium itself. The browser profile is applied in the browser process, before any page script can read the surfaces that describe it. GPU and WebGL are the clearest example. Those values come from the browser, so repairing them from a script is already too late. Patch them at the source and they stay consistent from the first request. Forking also let us strip Chromium down. We cut bloat we don't need, which keeps sessions lighter, and we fixed consistency issues that made long-running sessions flaky. ### What you get A consistent browser state from the first request means fewer false bot signals and fewer challenges. The hardest sites get more reachable, sessions run a bit faster, and long-running jobs fail less often. Because the evasion happens at the fingerprint level, you often clear antibot systems entirely, with no challenge left to solve. In addition to antibot evasion, you also get a win and cost win. Captcha solving can take up to two minutes per challenge, and you pay for every browser minute it idles there. Often you'll be able to skip the solving process entirely, saving you time and money. ### Request access Stealth Browser is an enterprise feature, and we're opening access deliberately. New access is gated because it's support-intensive. We want to understand each workload before we enable it. If you want in, bring us your hardest job. The fastest way in is to [book a call with our founders](https://cal.com/hussien-hussien-fjxt3x/intro-chat-w-steel-founders) - join and tell us what's breaking, and we'll get you set up. > This post is Day 1 of Steel Launch Week v3. See the full week at [https://steel.dev/launch-week](https://steel.dev/blog/launch-week-v3-recap) [Video on YouTube](https://www.youtube.com/watch?v=7EjchdTlG0Q) ## Blog: Introducing Projects https://steel.dev/blog/introducing-projects · Jun 12, 2026 · By Dane Wilson > Projects bring project-level isolation to your Steel org. Scope sessions, credentials, and profiles per project. **TL;DR:** Steel Projects are separate namespaces inside one organization that scope sessions, credentials, profiles and API keys. Every org gets a Default project with existing resources and keys migrated, so nothing breaks. Projects start as Development and can be promoted one-way to Production; org admins manage them in the dashboard. It answers: - What are Projects in Steel? - Do my existing Steel API keys still work after Projects launched? - How do I separate dev and prod workloads in Steel? - Can a Production project be changed back to Development? - Can I create Steel projects through the API or SDK? Projects are live in Steel today: separate environments and namespaces inside a single organization. Create one for dev, one for prod, one per team or product, and scope your sessions, credentials, and profiles to each. > TL;DR Sessions, credentials, profiles, and API keys are scoped to projects. Every existing org gets a **Default project** with your current resources and keys already migrated in, so nothing breaks. Switch projects from the new **Project Selector** in the top-left nav, and promote a project from **Development** to **Production** when it's ready. We held off on this longer than most would. Our honest opinion was that simplicity trumps complexity: one org, one bucket, nothing to configure. For a developer running agents in 2025, that was the right call. But one bucket doesn't scale with agents. The challenge wasn't headcount, it was proliferation: one agent became a fleet, and simple scripts became multi-step workflows carrying their own credentials and profiles. All of it in the same namespace, with no line between a Tuesday experiment and the workload your customers depend on. The only way to draw one was standing up a second organization. Projects work here the way they work everywhere else in your stack. Same org, same billing, clean boundaries. ### What a project is A project is an independent namespace. Nothing created in one project leaks into another. Right now that covers: - Sessions - Credentials - Profiles That's the whole idea: separation without the overhead of a second organization. ![Two terminals run the same curl against /v1/sessions. The dev project key returns three dev sessions; the prod project key returns one prod session. The key decides what exists.](https://steel.dev/assets/framerusercontent.com/images/naP8pLyy35SCRbiVGbTNSaTpQUE.png) ### Your resources already moved Every organization now has a **Default project**, and your existing resources already migrated into it. Your code keeps working. So do your keys. There's no cutover step. Rename the Default project or leave it alone. It's where you start; after that, the dashboard reopens whichever project you used last. ![The Project Selector](https://steel.dev/assets/framerusercontent.com/images/409m9i62UCYY8b9hqF08cRIuYxk.png) ### The Project Selector The dashboard now has a Project Selector in the top-left nav, next to the Organization Selector. Switch projects there, check each one's environment, or open Project Settings to: - Create a project - Rename a project - Set a different project as default - Promote a project to Production - Delete a project (and revoke its API keys) ### Development and Production Projects carry an environment designation. A new project starts as Development. Promote it and it turns Production, with a green badge. Promotion is one-way. You can't downgrade a Production project back to Development. We chose a visible badge over silent config so you can tell prod from dev at a glance. ### Project-scoped API keys It works as expected. Every API key belongs to one project. When you create a key, you pick its project; the UI defaults to the one you're viewing in the Project Selector. Your existing keys moved into the Default project, so they keep working. Sessions, credentials, and profiles you create with a key land in that key's project. A key for your prod app can't reach your dev sessions. ```python from steel import Steel dev = Steel(steel_api_key=DEV_PROJECT_KEY) prod = Steel(steel_api_key=PROD_PROJECT_KEY) session = dev.sessions.create() dev.sessions.list() # includes the new session prod.sessions.list() # never will ``` Projects themselves are managed by org admins in the dashboard. The SDK and API keys can't create, promote, or delete them. ### Where this goes Every control we want to build needs a boundary to act on, so the boundary ships first: - Production-specific permissions and deployment safeguards - Environment-based access controls and governance Projects also show us something we couldn't see before: how you structure your workloads, and what "production" means for an agent running on the web. That shapes what we build next. ### Try it Open [app.steel.dev](https://app.steel.dev/) and check the top-left nav. Your Default project is already holding everything you've built. Spin up a second project for dev, mint a project-scoped key, and point one environment at it. ## Blog: Building a Deep Research Agent That Survives Its Own Failures https://steel.dev/blog/durable-researcher · Jun 11, 2026 · By Nikola Balic > What we learned from making a research agent durable, routed, visible, and eval-driven. **TL;DR:** Durable Researcher is a browser-native deep research agent that checkpoints every model step to Postgres and rebuilds state from the transcript, so crashed runs resume. Routing tasks as lookup, extraction or synthesis gave the biggest lift; a citation verifier first cut citation quality by up to 0.22 and was fixed before re-use. It answers: - How do you make a research agent resume after a crash? - Why did a citation verifier make a research agent worse? - How does Durable Researcher score on ResearchRubrics and DRACO? - Why should a research agent classify the task before planning? - How do you stop a research agent from looping on searches without browsing? - What stack does Durable Researcher use? I have been fascinated by deep research agents for a while now, studying every shape I stumble on. In [part one](https://steel.dev/blog/claude-code-deep-research-autopsy) I took apart the deep-research harness inside Claude Code. This is what happened when I built my own and pointed real evals at it. Then rebuilt it. > TLDR: **Durable Researcher** is a browser-native deep research agent that checkpoints every model step, rebuilds state from the transcript, routes tasks by answer shape, and uses eval failures as the product loop. The point was to make every failure leave enough state behind that I could resume it, inspect it, and turn it into the next change. Make a better agent. The first version was good at the wrong thing. It wrote beautiful overviews. It planned sub-queries, browsed in parallel, took notes, checked its coverage, and produced a polished report. Ask it to survey a field and it shined. Ask it for one number, like a cash-flow figure from a filing, and it handed you a thoughtful essay about the company instead. So I added a citation verifier. It checked every claim in the report against the agent's notes and triggered a rewrite when the evidence was thin. Clean. Obviously the kind of thing a research agent should have. The evals said it made things worse. I switched it off. Build what seems right. Test it on real tasks. Read the failures. And when the data turns on you, quarantine your cleverness until you understand it. ### The Durable Research Loop Durable Researcher takes a topic, plans sub-queries, runs them in parallel against real browser sessions on [Steel](https://steel.dev/), takes structured notes, checks its own coverage, fills the gaps, and writes a report. Steel matters because the agent needs browsers, not HTTP fetches. The useful research lives on pages that render late, redirect, block scrapers, or hide their content behind behavior. A plain fetch sees none of it. Every model message is checkpointed to Postgres as it happens. If the agent dies, the next run resumes from the last checkpoint. The model never knows it crashed. That is only the first layer. Notes, visited URLs, claims, and source ledgers rebuild from the transcript. Scraped pages are cached in Postgres, keyed by task and URL, so a crash does not mean paying Steel to browse the same page again. Verification attempts are checkpointed too, which means a bad rewrite leaves a trail: claim verdicts, pass rate, unsupported lines, and reasons. The stack: Bun and TypeScript, [Pi](https://github.com/earendil-works/pi) for the agent loop, [Absurd](https://github.com/earendil-works/absurd) for durable execution, Steel for browsers, Postgres for persistence, a GLM-5.1 (cheap, fast and smart) model for reasoning, and Ink for the terminal UI. ![System architecture: User, terminal UI, Pi agent loop, Steel browser sessions, Postgres checkpoints, verifier calls, and final report.](https://steel.dev/assets/framerusercontent.com/images/JwPt5X8YOCOc4o8LOuf8twWq9jg.png) None of it is exotic. The product lives in the fit between the pieces. Campaign mode pushes the same idea further. Long research runs are split into bounded pulses, each with its own task ID, objective, report, judge decision, usage, and source inventory. That avoids one giant long conversation while preserving the user-visible behavior of one continuous run. Part one found Claude Code's harness stuck in a single-pass pattern: scope once, search once, never let a finding change the next query. Mine had the same limitation until I taught it to take the second hop, which is most of what follows. ### The Numbers, With the Caveats Up Front There are two useful academic benchmarks for this kind of system. [ResearchRubrics](https://labs.scale.com/papers/researchrubrics), from Scale AI, has 101 tasks with weighted criteria judged by an LLM. [DRACO](https://huggingface.co/datasets/perplexity-ai/draco), from Perplexity, has 100 tasks with a similar shape. | Benchmark | Judge | Tasks | Score | How to read it | | - | - | - | - | - | | ResearchRubrics | Gemini default | 10/101 | `59.8%` | Promising, partial | | DRACO | Gemini 3.1 Pro | 10/100 | `47.1%` | Paper-comparable, weak sample | For reference, the published full-set ResearchRubrics scores are Gemini Deep Research at `61.5%`, OpenAI Deep Research at `59.7%`, and Perplexity Deep Research at `48.7%`. The judge belongs in the headline of the metric. LLM-as-judge numbers are useful, but only if every chart clearly says which judge produced them. So the honest claim is narrow: promising on ResearchRubrics, lower-middle on the Gemini-judged DRACO sample. The full 100-task DRACO run still matters most. But for speed of the loop I have opted for a random 10% subsample. ### Sprint One: Make Failure Resumable The first sprint built the substrate. Every assistant message becomes a step in the durable engine. On resume, the engine replays the completed steps and hands the conversation back to the model. There is no notes table. No visited-URL table. Tool calls and their results live in the message log, and the app rebuilds notes and URLs by walking it. The transcript is the canonical artifact. If the log replays, the state replays. The browse cache was the second practical piece. When a page has already been opened for a task, the content is stored with its title and raw length. On resume, the agent can reuse that page instead of pretending the crash erased the web it already saw. That mattered more once verification entered the loop. The verifier needs source excerpts, not just a final markdown report. After a crash, the in-memory excerpt store is empty, so the agent rebuilds it from cached browse content before checking claims. Without that, a resumed run could have the right sources in its history and still fail its own citations. On top of that I built the research loop. ![Research loop: user topic to plan, Steel prefetch, structured notes, coverage check, and either gap fill or final report.](https://steel.dev/assets/framerusercontent.com/images/xd74HJJOZW2s5QE9ASjkaj34E.png) Planning produces sub-queries. Prefetch fans them out through Steel, searching and scraping the top results for each one in parallel. The agent reads the sources, takes structured notes, and calls an evaluation tool that summarizes coverage. Weak coverage, search again; good coverage, write. The early version failed in boring, specific ways. A broad query about agent-driven web automation came back with WhatsApp pages, German dictionaries, and a recipe site. The failure was retrieval, not browsing, so I added ranking signals for topic fit, URL paths, and domains that should never win. Bad candidates should lose before the browser spends time on them. The additional tweak was to lean into what the model already knows: if we already know a relevant URL, browse it directly before searching again. Searching the open web before visiting one lets SEO fight your research before the agent has read the obvious page. Another failure mode: search loops. The agent would fire seventeen searches in a row, tweaking keywords each time, browsing nothing. Every search cost money and taught it almost nothing. The fix was simple: no more than two searches in a row without a browse. The exact number mattered less than the behavior it named. Searching without browsing is reading the card catalog and never opening a book, and the prompt had to say so. Both rules are one rule with two faces: **take the second hop.** Read something and let it change what you do next, instead of firing another query into the dark. The Claude Code harness I took apart in part one never takes that hop. Most of this project was teaching mine to. I built the eval harness too. It downloads the DRACO and ResearchRubrics benchmark datasets, runs the agent as a subprocess per task, and judges the output with an LLM. Multiple judges. Batch APIs where they exist. The harness changed how I read metrics. Once I saw how far a score could swing on the judge alone, I stopped trusting any single number. ### The Diagnosis After the first sprint, I had Codex review the eval outputs on its own. It named the problem better than I had: > Durable Researcher was a good synthesizer with weak routing into exact lookup, primary-document extraction, and precision answering. Exactly right. The agent nailed "write me a thoughtful overview of X." It flubbed "what was Apple's free cash flow in fiscal Q3 2024?" It used the same report-writing shape for both. The roadmap was: - classify the task before planning - use a different first turn for exact tasks - build a real path to primary documents - produce an evidence table before prose for extraction tasks - gate completion on whether required values were actually captured - adapt the output style to the task type Acting on it took me weeks. Routing was an architecture change. ### Sprint Two: Route Before You Research The second sprint started from one premise: **the first turn matters more than another tool.** Before planning, the system now classifies the prompt as `lookup`, `extraction`, or `synthesis`. The mode picks the report template and the stop condition. 1. **Lookup** answers first and cites one strong source. 2. **Extraction** produces an evidence table as the deliverable, with tight analysis underneath. 3. **Synthesis** keeps the original report shape. I added primary-source paths too. Financial and extraction-heavy queries needed a way to reach source documents instead of drifting around the open web. PDF text extraction handles the investor reports that normal scrapers turn to garbage. It exposed a harder truth: having the path is not enough. The agent has to choose it at the right moment. ![Without routing, every prompt produces the same five-section report. With routing, the first turn picks the output shape: one number, an evidence table, or a structured report.](https://steel.dev/assets/framerusercontent.com/images/jejeGRbeTPdEeLPVsjE3Shxos.png) ### The Citation Verifier That Made Things Worse The verifier was supposed to be the easy quality win. After the report was written, the system parsed every citation, found its source, pulled the supporting notes, and asked a utility model one question: do verbatim excerpts back this claim? If the pass rate dropped below a threshold, it injected a steering message and let the agent rewrite. Each verifier attempt was committed as its own checkpoint, with the claim verdicts, pass rate, cited source numbers, and failure reasons. That made the autopsy much less mystical. On a small eval comparison, citation quality fell by 0.19 to 0.22 absolute points on a 0-1 scale. This was the same lesson one layer down: **a verifier can make a system worse when its failure mode is correlated with the writer's.** The lensed query expansion I had added in the same sprint, which generated definition, recency, criticism, and primary-source angles for every sub-query, was dragging the system toward tutorials and news. The hotfixes were predictable. Source-authority weighting. Stronger source-selection prompts. The extraction heuristic. Peer-reviewed and government domains up, PR wires and thin aggregators down. > Learning: keep measurement on, but make losing behavior easy to switch off. The rewrite was not wrong in principle. My implementation had specific, findable bugs. Blame it on the agent. So I fixed the verifier, not the writer: semantic matching, OR scoring for grouped citations, edit-narration stripping, a best-version guard, and a skeptic refuter at the decision boundary. Then I turned it back on by default. The real lesson is: **switching the losing build off gave me room to see why it lost.** ![The verifier arc: v1 regressed citation quality by 0.22, was quarantined, three bugs were found during autopsy (verbatim matching, multi-cite AND scoring, edit narration), then v2 was re-enabled with semantic matching and OR scoring.](https://steel.dev/assets/framerusercontent.com/images/zI0pwUDqUe38ZM0iuB7poleOI.png) ### Sprint Three: When The Question Is The Hard Part Routing decided what kind of answer to produce. It did not help when the question itself was hard to interpret. **Some questions are disguised:** a homophone, a paraphrased proper noun, or a reference buried in a casual phrase. The old planner decomposed every topic along literal research lenses, so a question in costume got searched at face value and never cracked. Now the planner reasons about the question before it writes a single query. It lists explicit interpretations, decodes the oblique reading, and searches both the literal and the lateral version. It treats the user's stated details as fallible clues, because people misremember and approximate. And it carries a needle prior: if a question is dressed up as hard but its literal phrasing would be trivial to search, the surface reading is probably a decoy, and the lateral readings get the weight. One bug from that work shows how these systems leak. The planner generated the interpretations correctly. A downstream parser dropped the field before it reached the agent, and the renderer never showed it. The lateral reasoning was real, then silently thrown away. Wiring it through changed the agent's behavior more than any prompt line did. The next problem was subtler, because it sounded like good judgment. A single reasoning chain would reach the right answer and then talk itself out of it. On one homophone needle in the haystack problem (love the "Run Forrest Run 5k" one), the agent found the correct answer, decided it was too cute to be real, and dropped it. Re-rolling the same chain is not a second opinion. The same model with the same framing tends to fall into the same hole. A second opinion only helps if it can fail differently from the first. The fix was redundancy, not a better prompt. Several workers now attack the same question from different readings, each told not to self-reject, and their evidence pools so independent agreement builds into confidence. A full extra agent gets spent only on a top answer nobody has confirmed yet. Then a separate adversarial pass judges whether the answer is correct, which is a different question from whether a citation is grounded. Skeptics vote. Abstentions are safe. Refuted answers stay in the report as a transparency block instead of vanishing. On the needle that started all this, the chain now surfaces the right answer and lands at medium confidence with a caveat, instead of confidently wrong or confidently silent. The last piece was depth. Routing made lookups short, which exposed the opposite problem on broad synthesis: reports that were accurate and thin. Survey mode runs several research passes and merges them deterministically into one report, with a single global source list and every citation marker remapped to match, then spends one constrained model pass on the prose. A gap-fill loop and citation chasing push for density instead of letting the writer quit early. On by default. ![The verifier arc: v1 regressed citation quality by 0.22, was quarantined, three bugs were found during autopsy (verbatim matching, multi-cite AND scoring, edit narration), then v2 was re-enabled with semantic matching and OR scoring.](https://steel.dev/assets/framerusercontent.com/images/6mgldIfADV3LCZzfXsDV2xNZyUA.png) ### Make Agent Work Visible At first the CLI was a wall of logs. Long stretches of nothing, and no way to tell whether the agent was thinking, browsing, stuck, or dead. So I built a terminal UI: findings, activity stream, agent status, a token meter, streamed assistant text between tool calls, per-tool progress, a verification indicator. Once the browser work ran through Steel, the UI had to show those sessions too: searches launched, pages opened, scrapes done, failures returned. The real win is steering. You can type a redirect mid-run, and it lands as a tagged user message before the next model turn. The system prompt teaches the model how to treat it. ![](https://steel.dev/assets/framerusercontent.com/images/ycWZEuLxj0KGlFWoxZK3gzPKo.png) ### The Dogfood Loop The most productive workflow was not complicated: 1. Run the agent on a real, non-trivial topic. 2. Read the full log (feed it to the coding agent). 3. Compare the saved report against the scraped pages and task rows in Postgres. 4. Name each problem in one sentence. 5. Write a failing test. 6. Make the change. 7. Read the diff back as a hostile reviewer. 8. Fix what the review finds. ![Eight steps in the dogfood loop: Run, Read log, Compare, Name it, Write test, Make change, Hostile review, Fix review.](https://steel.dev/assets/framerusercontent.com/images/Ge4ap17ChTLqph4YAkTVI0W2Ac.png) That became the discipline: plan, diff, review, fix the review. The agent helps at every step. ### What Stuck **Routing beats tooling.** The biggest quality lift I saw came from deciding what kind of task the user asked for before running the loop. Some prompts want a number, some a table, some a report. **Width and depth are tools, not maturity levels.** The useful system routes first, then spends parallelism, iteration, or neither based on the shape of the question. **Durability works when the transcript is enough.** Store tool-derived state anywhere else and you end up with a second source of truth. If notes, URLs, claims, and verifier context rebuild from committed messages and caches, crashes become annoying instead of existential. **Browser infrastructure is product infrastructure.** For a research agent, the browser is not plumbing. It decides what the model can know, and the evals only matter if they test the system against that reality. ### The Same Discipline Applies Building one agent with help from another collapsed the line between product and tool. Reading the research agent's outputs carefully turned out to be the same discipline as reading the coding agent's diffs carefully. Make the work visible. Route before acting. Refuse cheap fixes. Retire features when the evidence turns on them. Ask me again after some more work lands. Something else will have failed by then, and that is the point. ### Find the Experiment The code, tests, eval harness, and vibes are here: [steel-experiments/durable-researcher](https://github.com/steel-experiments/durable-researcher). It is not the final production shape. It spends tokens like it found a company card. But it survives its own failures now, and that made it finally worth improving. ## Blog: An autopsy of Claude Code's deep research https://steel.dev/blog/claude-code-deep-research-autopsy · Jun 10, 2026 · By Nikola Balic > Claude Code's deep-research workflow, pulled from its binary and dissected. Wide search, no second hop. **TL;DR:** Claude Code's deep-research workflow (v2.1.170) runs five phases once: scope, parallel search, fetch and extract, three-skeptic verification, synthesis. It searches wide but never takes a second hop, unlike hybrid systems from Anthropic, OpenAI, Gemini and Perplexity. Depth costs latency and tokens, so match topology to difficulty. It answers: - How does Claude Code's deep research workflow work? - Are deep research agents wide or deep? - How do OpenAI, Gemini and Perplexity deep research differ from Claude Code's workflow? - Why do reference research harnesses stay single-pass? - Does majority voting by verifier agents catch research errors? - When should a research agent go wide versus deep? *I had Claude Code pry its own deep-research workflow out of its binary, then pointed that workflow at a question about itself. The verdict: it searches wide and never doubles back. The systems it resembles do. The whole game is that second hop.* ![Wide single-pass fan-out versus deep iterative search, where each hop feeds the next](https://steel.dev/assets/framerusercontent.com/images/19XQBJNMF6AFHVaWjDj5Ks33Ss.png) Claude Code ships as a single compiled binary with its brain welded shut. Somewhere inside is the workflow it runs when you ask it to research the web: scope the question, fan out searches, read, vote, write. It does not ship as a readable file, so, for research purposes, I had Claude Code reconstruct the workflow from inside its own binary. A few minutes later the tool had performed surgery on itself and handed me the deep-research.js. I had a theory: **these "deep research" agents are not deep, they are wide.** They take your question, spray it across a handful of parallel searches, pile up the results, and stamp the word *deep* on the box. So I pointed the exact workflow at one question, *how do deep-research harnesses actually work, are they wide or deep*, and let it research its own autopsy. What I watched it do confirmed the theory and broke it in the same run. ### What I was actually holding This is a [Dynamic workflow](https://code.claude.com/docs/en/workflows), the kind Claude Code runs when you ask it to research the web. It executes on your own machine every time you run `/deep-research`. Everything in this piece describes the workflow as it ships in Claude Code v2.1.170; later builds may change it. The header comment says it was "ported from a bughunter architecture," swapping `git` and `grep` for `WebSearch` and `WebFetch`. Someone built a bug-hunting agent, then noticed the same skeleton finds facts about the world as easily as it finds null-pointer dereferences. Reference code is how patterns spread. People read it to learn the shape, then they ship the shape. So what matters is less what it does than what it teaches everyone who copies it. ### How the deep-research workflow works Five phases. Top to bottom. Once. ![Scope, Search, Fetch, Verify, Synthesize: the five phases run once, with no arrow pointing back](https://steel.dev/assets/framerusercontent.com/images/1bIoXCo8HcE96lthLWPFNfrKM0.png) | Phase | What it does | | - | - | | **Scope** | One agent splits the question into 5 angles: broad, technical, recent, contrarian, practitioner. | | **Search** | Five agents run in parallel, one per angle, each blind to the others. | | **Fetch + extract** | Dedup URLs, cap at 15 sources. Each source yields 2-5 *falsifiable* claims, each with a direct quote and a source-quality grade. | | **Verify** | Three skeptics per claim, each told to refute it. Two rejections out of three and the claim dies. | | **Synthesize** | One agent merges survivors, ranks by confidence, writes the report with a note listing what got killed. | **The extraction step does not trust a webpage; it demands a checkable statement plus the quote that backs it.** Verification is adversarial on purpose. If you wanted to teach someone how a research agent hangs together, you could do far worse than handing them this file. Good skeleton. ### The thing I couldn't stop looking at Then I read the prompt the harness hands each searcher. Every searcher is told to rank its results by relevance to *the original question*. The instruction is verbatim: *"Rank by relevance to the ORIGINAL question, not just the search query."* Every one of them starts from the same prompt the scoping agent wrote at second zero. Nothing any searcher finds ever changes what gets searched. No agent reads a result, feels the tug of *wait, that implies something*, and forms a sharper question from it. The orchestrator does not loop. Scope happens once. Search happens once. The report is built from whatever that single sweep dragged up off the seabed. That is the gap between wide and deep, and it fits in one picture. ![Wide: the question fans into five queries, all derived from the original, through fetch and aggregate to a report in one pass. Deep: each search and read produces a thought that becomes the next query, hop by hop, until the report.](https://steel.dev/assets/framerusercontent.com/images/OQqox0YqfEPmvwYOj1pqsadCyNk.png) Think about how you actually research something that matters. You search, you read, and the reading rewrites the next question. The answer to hop one becomes the input to hop two. A genealogist hits a misspelled surname in a parish record and that misspelling becomes the next query. A reporter notices the dates don't line up and chases the dates. Nobody writes five queries in advance and stops. The reference harness cannot follow a thread. That is the missing second hop. Then I checked whether the big hosted products do the same thing under nicer branding. ### Where my theory fell over They don't. I read their own engineering writeups, and almost every serious one is a hybrid: parallel fan-out inside a round, genuine iteration across rounds. ![Comparison table: every major system takes a second hop except the Claude Code deep-research workflow](https://steel.dev/assets/framerusercontent.com/images/oYzC7Eo9ZWdi0USsaz6rHQ52oSY.png) Anthropic's [multi-agent research system](https://www.anthropic.com/engineering/multi-agent-research-system) looks like the most wide-open thing in the field: a lead agent spinning up three to five subagents *in parallel rather than serially*. But read the next sentence. The lead agent "synthesizes these results and decides whether more research is needed, and if so, it can create additional subagents or refine its strategy." That is a loop. Even the system that looks the most like a fan is iterating at the top. The rest line up the same way: - **OpenAI** [**Deep Research**](https://openai.com/index/introducing-deep-research/) is one reasoning model, trained with the same recipe as o1, browsing and "pivoting as needed in reaction to information it encounters." The second hop is baked into the weights. - [**Gemini**](https://ai.google.dev/gemini-api/docs/deep-research) ships an explicit "Plan, Search, Read, Iterate, Output" loop and calls itself "an iterative, multi-step system, not a single-pass model." - [**Perplexity**](https://www.perplexity.ai/hub/blog/introducing-perplexity-deep-research) "reasons about what to do next, refining its research plan as it learns more." Open source splits down the same seam. [GPT Researcher](https://github.com/assafelovic/gpt-researcher) runs wide fan-out by default, then offers a separate recursive "Deep Research" mode for the tree. [dzhng/deep-research](https://github.com/dzhng/deep-research) puts `breadth` and `depth` right there as knobs and feeds each level's findings into the next level's queries. [LangChain's open_deep_research](https://docs.langchain.com/oss/python/deepagents/deep-research) goes the other way and tells its supervisor to "start with one sub-agent for most queries." The freshest data point landed while I was editing this. Anthropic's [Claude Fable 5 system card](https://www-cdn.anthropic.com/d00db56fa754a1b115b6dd7cb2e3c342ee809620.pdf) benchmarks three multi-agent harness shapes head to head on BrowseComp, and the one that most resembles this workflow, an orchestrator that fans out subagents and blocks until they all return, loses to both non-blocking designs on latency and tokens. Their diagnosis is structural: **every round is gated by its slowest subagent, and each freshly spawned subagent pays to re-establish context.** Even at the frontier, the barrier is the tax. So "wide, not deep" is wrong as a description of the field. What survives is more precise: > The bundled reference harness is single-pass wide. The systems it superficially resembles are hybrid: wide within a round, deep across rounds. The whole difference is the second hop, and the second hop is the part that costs. That last clause is the one I keep coming back to. ### Why the reference stays shallow Why one pass and not a loop? Three reasons: - **Depth doesn't parallelize.** A second hop has to wait on the first, so you forfeit the wall-clock win that makes fan-out attractive. - **Depth rabbit-holes.** Left to iterate, an agent chases a tangent through a dozen queries because nothing tells it to stop. - **Width is legible.** You can hold the whole pipeline in your head, which is exactly why it is the shape people copy. And depth is expensive. Anthropic reports that an agent burns roughly **4× the tokens of a chat, and a multi-agent system about 15×**. Their multi-agent setup beat a single agent by 90.2% on an internal eval, but they concede it only pays off on high-value work. (Self-reported, internal, breadth-first. Take it as direction, not fact.) ![Token cost versus a chat: 1x for chat, 4x for a single agent, 15x for multi-agent](https://steel.dev/assets/framerusercontent.com/images/KG4TISARXPM1iCH2f96JpKJe208.png) A reference is built to be understood. Single-pass and wide is the most legible shape there is, which is why it is the one that propagates. ### The limits of adversarial verification The harness leans hard on verification to make up for shallow search: three skeptics vote on every claim, two rejections kill it. It is the most sophisticated part of the pipeline. But majority voting only works if the three skeptics fail *independently*. [Self-consistency](https://arxiv.org/abs/2203.11171) earns its accuracy gains exactly under that assumption: sample diverse reasoning paths, let independent errors cancel. [Recent work on deep-research agents](https://arxiv.org/pdf/2601.15808) finds the opposite. Their errors are correlated: "Errors arising in one run also tend to recur in other runs." When three skeptics share one blind spot, three votes is one vote billed three times. So the two weaknesses are really one. The search is shallow, and the net built to catch its mistakes has the same holes the search does. ### I pointed it at real questions To watch the shape move, I gave the harness a genuine one: does LoRA match full fine-tuning for open models in the 7B-70B range? It ran for about thirteen minutes and spawned 98 agents: five searches, sixteen fetches, seventy-five verifiers, then synthesis. The run was on Opus 4.8. We never logged the input/output split, so the bill is an estimate, but at list prices ($5 per million tokens in, $25 out) those 2.36 million tokens can't be cheap. For one question. ![One run by the numbers: 98 agents, 533 tool calls, 2.36M tokens, about 13 minutes](https://steel.dev/assets/framerusercontent.com/images/5ykOJAX3dsQF3qJkR93Q9nfYnk.png) The verifiers earned their keep. They killed the "new variant nearly closes the gap" claims, the easy, independent failures a skeptic can refute with one contradicting source. You can watch the whole winnowing in one chart: sources breathe in, claims breathe out, the guillotine takes the rest. ![The winnowing: 30 search results, 16 novel URLs, 77 claims extracted, 25 verified, 21 confirmed, 7 findings](https://steel.dev/assets/framerusercontent.com/images/g1tIkMs8yWiB81GJ32Y1O1BfER4.png) But the report also flagged its own gap: the 70B evidence is thin, and the strongest claims lean on a non-peer-reviewed blog. It saw the hole and had nowhere to put it. A deep harness would have turned that caveat into the next query. This one moved straight to synthesis, because it has no next query to make. That is the missing second hop, on a real question. Then I ran it on a question built to break it, a needle from Perplexity's [DRACO benchmark](https://arxiv.org/abs/2602.11685): name the 5K race at California's old Great America park with "bubble gum" in its title. The answer hides in a homophone. Bubba Gump, the Forrest Gump shrimp brand, runs a "Run Forrest Run" 5K, exactly the suspiciously neat connection a single deep chain tends to find and then talk itself out of. The harness got it, and for a structural reason. Four of the five parallel searches surfaced the homophone independently, so no lone reasoner had to trust a lucky hunch. The adversarial verifiers then kept the system from overclaiming that a shrimp brand "is" bubble gum. What it wrote was careful: the premise has a homophone error, here is the real race, medium confidence. That careful answer is what width and skepticism produce when they fight each other. Sometimes the fan is exactly the right shape. The Fable 5 system card puts numbers on when. Multi-agent teams posted Anthropic's highest BrowseComp score (93.3%), but the gain lives almost entirely in the hard tail: on problems most models already solve, the median multi-agent speedup was 0.8×, slower than one agent, because coordination overhead eats the parallelism. Width is neither a free lunch nor a scam. It is a bet that your question sits in the hard tail, and the needle questions do. ### What I'd tell someone building one - **Know which pattern you started from.** Most research agents begin life as a copied reference implementation. Ship the Claude Code deep-research shape unchanged and you ship single-pass width: cheap, fast, legible, and blind to anything that needs a second look. - **Match topology to difficulty.** Go wide when the angles are independent and the answer is a synthesis of parallel facts. Pay for depth only when the next question genuinely depends on the last answer. - **Budget for the second hop.** When you go deep, expect serial latency, a \~15× token bill, and a verification layer that must assume its own votes are correlated. The frontier is not more width. It is a disciplined second hop: **knowing when to take it, when to quit, and how to trust what you find.** Nobody has cleanly solved that. It is the part we are building toward. The harness proved its own ceiling for me. I watched it research this exact question, fan my prompt five ways, and never look back at what it found. So I built the deep version: a research agent that routes before it searches, browses before it loops, and verifies its own claims. It got worse before it got better. The verifier I was proudest of dropped my report quality, and it took me a while to work out why. That is the next piece: *Durable Researcher: building a research agent that survives its own failures*. I'm still thinking about the second hop. But now I have one. ## Blog: Introducing Steel Skills https://steel.dev/blog/introducing-steel-skills · Jun 3, 2026 · By Nasr Mohamed > Today we are launching Steel Skills, a catalog of five agent skills for the web. **TL;DR:** Steel Skills is a catalog of five agent skills: steel-browser, steel-developer, steel-session-debugging, steel-reliability and steel-skill-creator. Each does one job and hands off to the next when the job changes. Install them with the Steel CLI, npx skills, the Claude Code marketplace or by hand. It answers: - What is Steel Skills? - What are the five Steel agent skills and what does each one do? - How do I install Steel agent skills in Claude Code? - Why split one browser skill into several narrow skills? - How does steel-skill-creator write a skill from real browser runs? - Why can eval scores mislead when optimizing agent skills? Today we are launching Steel Skills: five agent skills that give your agent a real browser, plus the judgment to know which one the job needs. Install one, or install the set, in Claude Code, Cursor, Codex, opencode, Pi, or any compatible agent. We [shipped `steel-browser` first](https://steel.dev/blog/steel-cli-and-agent-skill), and one problem kept surfacing. With it loaded, the agent would reach for a browser even when the job was writing SDK code or debugging a failed run. Even after tuning the trigger words. One broad skill fires at the wrong moment and pulls the work sideways. A single skill is a tool; a set of skills narrow enough to know when *not* to fire, and when to hand off, is a system. So we split the work and built the rest. ### TL;DR - 5 skills: `steel-browser`, `steel-developer`, `steel-session-debugging`, `steel-reliability`, `steel-skill-creator` - Install one or the whole catalog by hand, with `npx skills`, or as a Claude Code plugin - Each skill does one job and routes to the next when the job changes ![](https://steel.dev/assets/framerusercontent.com/images/aPWUrvMIco69UStvFqP1KNOqzx0.png) ### Why skills, why now Agents are getting better at using computers. What still trips them up is the procedure around the task: when to open a browser instead of writing code, when to stop and inspect a failed run, when a block is a login and not a broken selector. The judgment, not the keystrokes. A prompt tells an agent what you want once. A skill tells it how a job runs every time: when to fire, which tools to use, what to return, where to hand off. > Skills are becoming portable operating procedures for agents. In short time, skills went from a Claude feature to an [open format other agents read](https://agentskills.io/). Steel Skills is that, for the web. Not one giant web skill. Not five unrelated prompts. A small system that moves the way web work moves: **operate, build, debug, recover, then compile the path you keep repeating.** ### A skill is a contract, not a prompt A `SKILL.md` says when to act, how to run, what good output looks like, and what to do when it blocks. Less glue to rebuild per project. One path that holds across agents. Scope was the harder call. A skill that tries to do everything triggers at the wrong moment and helps with none of it, so each of ours does one job. The narrow description is the feature. It's what makes a skill fire when it should. What we kept coming back to was the handoff. Web work doesn't stay in one mode. You browse, it breaks, you need to know why, then you need it fixed. So `steel-browser` routes a dead session to `steel-session-debugging`, which gathers the evidence and hands it to `steel-reliability`. We'd rather compile a skill than write one. A [Steel Agent Trace](https://steel.dev/blog/why-we-built-agent-traces) already holds the selectors, the wait points, and the input values. `steel-skill-creator` reads two real runs and writes the skill from what happened in the browser, not from what someone remembered to type. ![Steel-skill-creator reads two real runs and writes the skill from what happened in the browser.](https://steel.dev/assets/framerusercontent.com/images/ph36io2ber47BkKgCzIKeiORh8.png) Skills execute real work, so we treat them like operational code: narrow scope, explicit failure modes, clear handoffs, no magic bypass. `steel-reliability` starts with the cheapest fix and never promises a guaranteed way past bot detection. And the `SKILL.md` is portable. Write the capability once; it follows you when your tools change. ### The five skills New here? Start with `steel-browser` and `steel-developer`. Add the rest when you're running repeated workflows or chasing failed sessions. Less is more with skills, so feel free to optimize. | Skill | What it does | Reach for it when | | - | - | - | | [`steel-browser`](https://docs.steel.dev/overview/skills/available-skills/steel-browser) | Scrapes, clicks, fills forms, takes screenshots, exports PDFs, logs in, and gets through pages that only render with JavaScript. | A live web task, right now | | [`steel-developer`](https://docs.steel.dev/overview/skills/available-skills/steel-developer) | Writes the code instead of doing the task: SDKs, REST, Playwright, Puppeteer, Stagehand, Browser Use. | Reusable code you'll run later | | [`steel-session-debugging`](https://docs.steel.dev/overview/skills/available-skills/steel-session-debugging) | Works backward from a failed run. Pulls the logs, traces, replay, and network calls, builds a timeline, and tells you what broke. No guessing. | A run failed and you need to know why | | [`steel-reliability`](https://docs.steel.dev/overview/skills/available-skills/steel-reliability) | Diagnoses what's blocking you (CAPTCHA loops, proxy failures, bot detection, login state that won't stick) and works up from the cheapest fix. | You keep getting blocked | | [`steel-skill-creator`](https://docs.steel.dev/overview/skills/available-skills/steel-skill-creator) | The strange one. Runs a task you repeat twice, diffs the runs to find which parts are inputs, writes a skill from the difference, and verifies it on a third run. | You keep doing the same flow by hand | This was a team build: Jun made `steel-browser`, Dane wrote `steel-developer`, Nas owns both `steel-session-debugging` and `steel-reliability`, and Niko built `steel-skill-creator`. ### Install A few ways to install a skill: the Steel CLI, `npx skills`, the Claude Code marketplace, or by hand. Every skill drives the Steel CLI, so install and authenticate it once first: ```sh curl -fsS https://setup.steel.dev | sh steel login ``` **With the Steel CLI.** You just installed it, so this is the shortest path: ```sh steel skills list steel skills install steel-browser ``` **With** **`npx skills`.** List what is available, then add one: ```sh npx skills add steel-dev/skills --list npx skills add steel-dev/skills --skill steel-browser ``` **Through the Claude Code marketplace.** Add the catalog, then install skills by name: ```sh /plugin marketplace add steel-dev/skills /plugin install steel-browser@steel-skills ``` **By hand.** A skill is just a folder with a `SKILL.md`, so you can copy it straight into your agent's skills directory: ```sh git clone https://github.com/steel-dev/skills cp -r skills/steel-browser ~/.claude/skills/ ``` After installing, restart your agent client so it can discover the skill, then verify it loaded: ```sh steel skills doctor ``` ### What we learned Before we shipped, we spent real time optimizing these skills against evals, and learned how easily a score lies. A skill could return the right-looking answer while running commands that don't exist, and the eval would rank it first. Tuning the wording to win taught it to look good on cases we'd already seen. On held-out tasks, the same skill cleared barely half. What held up was checking the run itself: did the commands work, did the session do what the skill claimed. That's why `steel-skill-creator` builds from real traces. ### Try it Install one skill. Run one real flow. Then tell us one blocker and one pass in [Discord](https://discord.gg/steel-dev). Learn from our docs how skills help coding agents to use Steel cloud browsers correctly: ## Blog: Errors Are the Interface Agents Actually Read https://steel.dev/blog/errors-are-the-interface-agents-actually-read · Jun 2, 2026 · By Nasr Mohamed > Write API errors that correct the agent mid-failure, before the user calls it broken. **TL;DR:** For agent-facing APIs, errors are the one surface an agent reads unfiltered at the moment it must act, so each should say what happened, which input was wrong and how to fix it. Changing Steel's session ID error to ask for a valid UUID cut that endpoint's error rate by more than half. Accepting Bearer auth may have been wrong. It answers: - How should API error messages be written for AI agents? - Why are error messages important for agent-facing products? - What auth header does the Steel API use? - What is the difference between hard errors, soft warnings and passive context for agents? - How much did a clearer error message reduce Steel's error rate? > *TL;DR: Error messages are not just cleanup copy. For agent-facing products, they are one of the few places where the product gets to correct the agent while it is actively screwing up, before the user decides the whole thing is broken.* We've been overhauling errors recently. Not as a polish pass. Not because someone found religion about microcopy. Because the logs kept showing us the same ugly thing: people were failing, agents were confidently making it worse, and our product was giving them almost nothing useful at the moment it mattered. When an error fails to do its job, the user (or the agent acting on their behalf) walks away assuming something is broken on our end. Usually it isn't. They just couldn't tell. The principle I keep coming back to is simple. Every bad error message is a missed activation. Every one. The error is the first place your product talks back to someone who is, right then, deciding whether you're worth their next hour. If it sounds confused, you sound confused. If it points them at the fix, you just bought yourself a little bit of trust. I want to walk through what we're working on right now. Including one fight I had with a coding agent that I'm reversing my position on, live, in this article. ### What we're moving every error toward The shape we're trying to push every error into is roughly this: what happened, in the language of the user's input; which assumption or input was wrong; and how to do it correctly. Plus a link, ideally to a docs page that's structured to be parsed by an LLM as well as read by a human. Clean markdown. We've built the mechanism to include docs links directly in JSON error responses, so when the user (or their agent) logs the failure, the link sits next to the message. Why this matters: a lot of what we were seeing came from people and agents who hadn't read the docs, or there was a page in the docs that already explained exactly what they'd hit, and they had no way to find it from inside their context. Agents amplify that problem. They will confidently act on a bad prior unless the product corrects them at the moment of failure. That's the loop we're trying to close: the failure should carry the next useful pointer, instead of forcing the user or the agent to go hunting for it somewhere else. Honestly though, the bigger lever has been embarrassingly simple: write the error like the person or agent reading it has never seen your docs, because they usually haven’t. Not more words. More context. A single sentence that names the cause and the fix beats a docs link the agent has to click to get to the same information. ![](https://steel.dev/assets/framerusercontent.com/images/rtCDnwZjRlyrKd5S6jFQn4OZ8.png) ### The UUID example The cleanest before/after I have is the session ID error. You can optionally pass [your own session ID](https://docs.steel.dev/overview/sessions-api/quickstart) when you create a session in Steel, and it has to be a UUID. Users (and increasingly, coding agents) kept passing in random strings. Our original error was something like "format incorrect." It didn't really mean anything. The agent would see it, shrug, try a different random string. Technically accurate. Functionally useless. The worst category. We changed it to tell the user, in one line, to pass a valid UUID. That was it. The error rate on that endpoint dropped by more than half. And this was before agents got good. Even regular users were hitting that one bad sentence and bouncing. A vague error doesn't fail safely. It fails quietly into the category I'd describe as "the user concludes the product doesn't work." A specific error turns that same moment into a one-second correction. The happy path gets users interested. The failure path decides whether they come back. ### The Bearer fight Steel doesn't use the `Authorization` header at all. Our [auth header](https://docs.steel.dev/api-reference) is a custom one: `steel-api-key: `. The reason was boring and practical: Steel spends a lot of time around other people’s websites, cookies, proxies, sessions, and auth state, so we wanted our control-plane auth to be extremely explicit. `steel-api-key` is ugly in a useful way: you can see exactly what it is. People kept sending us `Authorization: Bearer ` anyway. Mostly coding agents. The agents were confused in the most boring possible way: they had seen ten million APIs use Bearer auth, and decided ours did too. The model would confidently tell users that's how the API works, they'd paste the snippet, the request would fail. We'd see the same pattern over and over in the logs. First push: a clear error that said we don't use `Bearer`, with a link to the docs. The error fired. It barely moved the needle. My best guess is that the model already had `Bearer` baked in deep, from an old crawl or just generic API priors, and one error wasn't going to dent it. At that point, the practical answer looked obvious: accept what agents were already sending. So we caved. We started accepting `Authorization: Bearer ` as a silent alias alongside the canonical header. The bet was we could remove the alias later, once the data showed people were converging on the right one. This is the kind of sentence engineers write shortly before creating permanent compatibility debt. Note: I am now increasingly convinced this was wrong, which is annoying because I was there when we did it. Talking it through with the team changed how I think about what an error actually is. ### The last surface that doesn't get distilled Almost everything else we ship gets quietly absorbed by the model companies on the other end. Docs go into training dataset, then through retrieval, then through whatever summarization the agent runs before it acts. SDK examples get rewritten in whatever the model already prefers. Blog posts get compressed into one-liners long before they reach a decision. ![](https://steel.dev/assets/framerusercontent.com/images/YmjppM3575De8uyKdnMVwuzKo.png) By the time any of that lands in the loop where the agent is choosing how to call Steel, it's been through several layers of compression. Errors are different. The error fires inside the agent's loop in real time, because the request just failed and the agent has to decide what to do next. Nothing sits in between. No retrieval pass, no distillation, no helpful little summary. The model is reading what we actually wrote, at the moment it has to act on it. That's a different writing surface from anything else we ship. Docs depend on training cycles. SDK examples get filtered through whatever the model already thinks your API looks like. Errors don't. They're the last place where you can still teach the model something specific about your product, and have a reasonable expectation that the message lands intact. Which is exactly why accepting Bearer may have been the wrong call. We traded away the one surface where the product could correct the agent, in exchange for short-term ergonomics. We took ourselves out of the loop. ### Hard errors, soft errors, and the FYI channel Once you start thinking this way, you notice that a lot of what an agent needs from you isn't even an error. CLIs have had this pattern forever. Warning, you're probably doing this wrong, FYI, I'll still let you run it. That same shape belongs in agent-facing surfaces, and we're not doing nearly enough of it. The agent creates a session, the request succeeds, and somewhere in the next two minutes the session is going to break because they're on a site that [needs a proxy](https://steel.dev/blog/anti-bot-defense). A passive "FYI, you probably want a proxy here" is worth more than the hard error five minutes later, when the session is already trashed. ![](https://steel.dev/assets/framerusercontent.com/images/qFzc2GmujUQ3kM92auiNimxPE4.png) So the model in my head is roughly three channels. **The hard error:** the request failed, here's the cause and the fix, return them inline. **The soft warning:** the request worked, but you're heading somewhere you'll regret. And **passive context:** things that are just true about the environment the agent is operating in (this site, this session, this auth state) that the agent should know without having to ask. The third one is where I think the most interesting work is. ### Agent notes, and generating errors from the data There's a version of that third channel we've been kicking around. Internally we call them agent notes. Small, scoped pieces of context, attached at whatever level of granularity makes sense. Some are general; about sessions, about antibot. Some are per-site or per-URL. "On this page, the visible login button doesn't actually submit, click the second one." That kind of thing. You don't have to hand-write all of them. We already capture session tags and events for the common situations users and agents end up in. Cluster those, find the recurring failure shapes, and you've got the raw material to either write better errors or generate per-context notes against the actual distribution of what's going wrong. Past a certain volume, the loop automates itself. Detect the cluster, draft the note, ship the fix. A lot of what we've historically tried to handle with [skills](https://steel.dev/blog/steel-cli-and-agent-skill) (pre-written instructions for an agent about how to use Steel) collapses if the tool teaches well enough in the moment. The skill is doing extra work because the error isn't. ### Progressive disclosure, and the CLI logs thing The other thing we are working on, partly in service of all of this, is real-time [CLI](https://docs.steel.dev/overview/steel-cli) logs the agent can follow, grep, tail, head. Basically consume the way a human consumes a long log file. Open one log at a time. Scan. Decide whether you need more. It's the same shape Anthropic uses in Claude Code. A small model decides whether the doc is needed at all, then which section, then the full file if it has to. Progressive disclosure. I think that's exactly how agent-facing logs should work. The agent doesn't want everything. It wants the next useful thing, and the ability to ask for more if that's not enough. The reason this matters for errors: the error is the entry point into that log. If the error is good, the agent might not need the log at all. If the error is bad, the log is where the agent has to go to recover. Right now, for most infra products, that path doesn't really exist. ### Why "activation" is the right word I want to come back to why I think activation is the right frame, and not, say, developer experience, agent experience, or error UX. When a user (or an agent) hits a confused error, the default assumption isn't "I made a mistake." It's "this product is broken." Or, almost as bad: "this is too hard to figure out, not for me." Either way, you've lost the activation. You don't get a second chance to teach them. They leave, and honestly they're right to leave, because from where they're sitting you didn't give them any reason to stay. A good error does the opposite. The product told them, in one line, what they got wrong and how to fix it. That sounds small, but it changes what the user assumes about the system. ### What I think we're really doing The biggest unlock for anything agentic is the feedback loop (we already do a version of this with [Agent Traces](https://steel.dev/blog/why-we-built-agent-traces)). Close it, and the agent figures everything out on its own. Leave it open, and nothing on the prompt side rescues you. Errors are the densest part of that loop. They're the moment the system tells the agent something it didn't already know. There's a version of the future where this gets its own channel. An **agent mailbox**, parallel to the API response, where we can push context and warnings that a human user doesn't need but an agent does. We're not there yet. But it points at the same thing the rest of this points at. The failure path of your product is a teaching surface. And right now it's the only surface the agent actually has to read. Try this in your own product: take the 10 most common API errors, rewrite each one as a recovery instruction, and measure whether retries improve. ## Blog: Why we built inactivity timeouts https://steel.dev/blog/why-we-built-inactivity-timeouts · Jun 2, 2026 · By JunHyoung Ryu > Steel's inactivityTimeout releases a session after a set window of silence, so idle and crashed runs stop billing. **TL;DR:** Steel's inactivityTimeout releases a session when nothing is sent to the browser for a set window, so crashed or abandoned runs stop billing before the hard timeout (5 minutes by default, up to 24 hours). It is off by default, approximate, always capped by the hard timeout, and records inactivity_timeout as the reason. It answers: - How do I make a Steel session release itself when my agent goes idle? - What is the difference between timeout and inactivityTimeout in Steel? - What counts as activity for Steel's inactivity timeout? - How do I stop paying for a browser session after my script crashes? A Steel session can release itself when your agent goes quiet. You set `inactivityTimeout` when you create a session, and if agent doesn't talk to the browser for that long, Steel shuts it down for you. So you stop paying for an idle browser when a run finishes or your agent quietly dies before it can clean up behind itself. Apparently this is becoming a series. The last one was [Why we built Agent Traces](https://steel.dev/blog/why-we-built-agent-traces); this one is smaller. ### One number was doing two jobs Every session runs through a simple [lifecycle](https://docs.steel.dev/overview/sessions-api/session-lifecycle), and the hard `timeout` you set is the longest it can stay Live before Steel releases it. Five minutes by default, up to 24 hours. Sessions are metered by the minute, so that single number ends up carrying a trade-off it was never meant to carry. Set it short and a long task gets cut off mid-run. Set it long to be safe, and the failure flips: if your script crashes or you walk away, the browser stays Live and keeps metering until the full cap runs out. A 24-hour cap is fine right up until a dead job sits Live for 24 hours. The honest answer was always to release the session yourself the moment you're done. And you should. But `release()` assumes your code reaches the `release()` call. Crashes don't. Hangs don't. Abandoned runs don't. The cases where you most want the session gone are the ones where your cleanup never runs. So we built a floor that doesn't depend on your code getting there. ### A shorter fuse under the cap ```js import Steel from 'steel-sdk'; const client = new Steel(); // Release the session after 1 minute with no CDP or input activity, // capped at a 10-minute hard limit. const session = await client.sessions.create({ timeout: 600000, // 10 minutes (hard cap) inactivityTimeout: 60000, // release after 1 minute of inactivity }); ``` The session stays Live as long as you keep driving it, up to the 30-minute cap. Go quiet for 60 seconds and Steel releases it. What counts as activity is deliberately simple: anything you send the browser resets the clock. CDP commands from Puppeteer or Playwright, clicks and typing through the live view. Each one starts the count over. What doesn't count is the browser working on its own. A slow page load, or a scrape that's just sitting on the network, sends nothing. To the timer, nothing is silence. If a quiet stretch runs longer than your window, it trips mid-task. So set the window longer than the longest gap you expect between commands. ### It's a floor, not a stopwatch A few things to know before you lean on it. The hard `timeout` always wins, so inactivity only helps when it's the shorter of the two. It's off by default; omit it and nothing changes. And it's approximate: the check runs on the session's heartbeat, so expect "about a minute of silence," not an exact one. A session closed this way records its reason as `inactivity_timeout`, so later you can tell "we cleaned it up" apart from "it fell over." None of this replaces releasing sessions yourself, which is still the cleanest way to stop the meter the instant you're done. Think of the hard `timeout` as how long a session may live, and `inactivityTimeout` as how long it may sit doing nothing before Steel assumes you've gone. It's a small safety net for the runs where your code never gets the chance. ### Try it It's live in the SDKs now. Pass `inactivityTimeout` to `client.sessions.create()` alongside your existing `timeout` and give a long-running job a short idle fuse. - [Why we built Agent Traces](https://steel.dev/blog/why-we-built-agent-traces) - [Docs](https://docs.steel.dev/) - [Free API key](https://app.steel.dev/sign-up) - [Discord](https://discord.gg/steel-dev) Humans use Chrome. Agents use Steel. ## Blog: Browser Agent Leaderboards, rebuilt https://steel.dev/blog/browser-agent-leaderboards-rebuilt · May 29, 2026 · By Hussien Hussien > Benchmarks go stale fast. We rebuilt the Steel agent leaderboards to track results as they land. **TL;DR:** Steel rebuilt its Browser Agent Leaderboards (v4, at leaderboard.steel.dev) to add new results with their date, source and methodology as they land. Ten benchmarks sit on the homepage, including OSWorld, where Claude Opus 4.8 leads at 83.4%. Scores do not compare across benchmarks; use a rank to shortlist, not as a verdict. It answers: - What is the Steel browser agent leaderboard? - Which AI agent ranks #1 on OSWorld? - Why aren't scores comparable across different agent benchmarks? - How often is the Steel leaderboard updated? - How do I get my agent's score on the leaderboard? - Should I pick an agent based on its leaderboard rank? Benchmarks rot. A paper reports a score once and freezes. The new results show up on Twitter, in model cards, in someone's blog post, and the original source never gets updated to match. Six months later the number you're citing is neither the latest nor the highest, and you'd never know. It's worse under the surface. Methodology drifts: a model gets deprecated, a website in the task set goes offline, the scoring harness changes, and the "same" benchmark is quietly measuring something else. No two benchmarks measure the same thing, so you can't line one score up against another. And every model release reports on a different set of benchmarks, so even the model cards are hard to compare. Instead of chasing that across a dozen sources that each went stale on a different day, we keep one current. We rebuilt the Steel leaderboards to track results as they actually land, with the source and methodology on the page, across the benchmarks that matter for browser and computer-use agents. Live now at [leaderboard.steel.dev](https://leaderboard.steel.dev/). [Video on YouTube](https://www.youtube.com/watch?v=JFNXJXoPnjE) ### Why we maintain it We've kept this leaderboard running since February 2025 and reshaped it three times since. This is v4, and we're honestly still hunting for the right form. We maintain it for a selfish reason. Every Steel project starts with the same question: which benchmark do we even trust here? Our customers ask us the other half: which agent is actually best, and how do we build on it? This is our running answer to both, in the open. ### Everything on one page Ten benchmarks, on the homepage instead of buried a click deep: WebVoyager, BrowseComp, WebArena, SWE-bench Verified, OSWorld, GAIA, ClawBench, Online-Mind2Web, τ-bench, and AgentBench. You see the whole field the moment you land. (The green pixel-cursor mark from the site's first release is back next to the title. Small thing. We missed it.) ### We keep it current This is the part that rots everywhere else. When a new result lands, we add it, with its date. Claude Opus 4.8 went to #1 on OSWorld at 83.4%, and it was on the board the day it shipped. The original OSWorld paper still reports what it reported at publication. Every result also carries where it came from: source, repo, who reported it. Self-reported or third-party verified. ### Scores don't compare across benchmarks We put this at the top of the results page, not in a footnote: a 92% on one benchmark and an 80% on another is not a ranking. It's two different exams, with different tasks, scoring, and scope. Even inside a single benchmark, rows vary by evaluator, harness, attempt budget, tool access, or verification level. Results is the one place to see it all: filter by category or benchmark, search by agent or company, freshest first. ### The small stuff - Copy any results table as markdown in one click. - A table of contents that follows you down long pages. - Sharper share cards when you drop a link in Slack or X. ### Come add to it The board stays current because it's maintained, not because it's finished. If you've got a verifiable score we're missing, the [contribution guide](https://github.com/steel-dev/leaderboard/blob/main/CONTRIBUTING.md) spells out exactly what evidence to bring, and there's a separate bar for proposing a whole new leaderboard. ### Go look Live now at [**leaderboard.steel.dev**](https://leaderboard.steel.dev/). Open a benchmark that matters to your work and read past the top row. - See the benchmark: [leaderboard.steel.dev](https://leaderboard.steel.dev/) - Build the agent you want on the board: [get a free Steel API key](https://app.steel.dev/sign-up) - Spot a result we missed: [Discord](https://discord.gg/steel-dev) or [@steeldotdev](https://x.com/steeldotdev) ### FAQ **What is the Steel browser agent leaderboard?** A public set of leaderboards at leaderboard.steel.dev tracking how AI agents and models perform across 10 benchmarks spanning browser agents, computer use, research, and coding: WebVoyager, BrowseComp, WebArena, SWE-bench Verified, OSWorld, GAIA, ClawBench, Online-Mind2Web, τ-bench, and AgentBench. Each result carries its source, repo, and reporter, and each benchmark page explains the task set and scoring. **How often is the leaderboard updated?** Continuously. When a new result lands, we add it with its date, rather than waiting for an original paper to revise, which it usually never does. Claude Opus 4.8, for example, was on the OSWorld board at #1 (83.4%) the day it shipped. Each row also notes when methodology drifts, like a deprecated model or a changed scoring harness, so an aging number doesn't masquerade as a current one. **Why aren't scores comparable across different benchmarks?** Each benchmark is a different exam. WebVoyager scores task completion across 643 web tasks evaluated by GPT-4V. OSWorld scores desktop automation across 369 tasks via execution-based validators in a VM. BrowseComp scores short-answer research questions. Different tasks, scoring, and scope, so a percentage on one says nothing about a percentage on another. **Which AI agent ranks #1 on OSWorld?** As of 2026-05-28, Claude Opus 4.8 leads OSWorld with 83.4%, above the 72.36% human baseline from the original paper. OSWorld measures multimodal desktop automation across 369 tasks using execution-based validators that check the final computer state after the agent acts. **How do I get my agent's score on the leaderboard?** Open a submission following the contribution guide on GitHub. It defines the evidence a score needs (source, reproducibility, and verification level) and the separate bar for a whole new leaderboard. Scores get listed on evidence, not announcements. **Should I pick an agent based on its leaderboard rank?** Treat the rank as a directional signal, not a verdict. The board shows methodology, provenance, and verification for each score, but it can't tell you about latency, cost per task, anti-bot resilience, or how an agent generalizes to your specific sites. Use it to shortlist, then test candidates on the workflows you actually run. ## Blog: Why we built Agent Traces https://steel.dev/blog/why-we-built-agent-traces · May 28, 2026 · By JunHyoung Ryu > Agent Traces records every browser-agent session as a replayable timeline. Inside the rebuild, and why intent is what agent observability still has to solve. **TL;DR:** Agent Traces replaced Agent Logs, which stored raw CDP events as coordinates and key codes. Capture moved into the page, so each action records the element, its name and stable selectors, and the trace collapses raw events into readable rows. A markdown export feeds coding agents, but a trace still lacks the agent's intent. It answers: - What is Steel Agent Traces? - Why did Steel rebuild Agent Logs into Agent Traces? - Why capture browser interactions inside the page instead of through CDP? - How does Agent Traces collapse raw input events into readable steps? - Can one browser agent trace be turned into a reusable skill? - How do I turn a Steel session into a script with a coding agent? Agent Traces records what a browser agent actually did during a browsing session: every click, keystroke, and navigation, captured as a readable timeline and as a document you can hand to another agent to replay the same task. It started as a side effect of a different problem I was trying to extract reusable skills from real browser sessions. Give the agent a task, let it figure out the path, then turn that path into something another agent could replay. We'd been kicking the skill idea around inside Steel for almost a year, long before the current wave of skill formats. Every time I sat down to actually build it, I hit the same wall: I didn't have enough signal in the session. What I had was Agent Logs. Agent Logs was the v0 of all this. It captured raw CDP events as they flew past, click, type, navigate, and stored them as a flat list. A click row told you a click happened at some coordinates. It didn't tell you what was clicked. Typing showed up as N rows of single-key events. There was no "the agent typed an email into the email field," because there was no notion of fields. Just coordinates and key codes. You can't generate a reliable skill from that. You can replay it on the same page at the same viewport, but it breaks as soon as the layout shifts. So I rebuilt it from the capture layer up. ![New session view with Agent Traces](https://steel.dev/assets/framerusercontent.com/images/Q31NvmTZJO1ut4qVWGLCgpBjdmE.png) ### Instead of scraping CDP, capture the page The first instinct was the lazy one. We were already proxying every CDP event. Why not, when a click fires, send another CDP command back to the page to ask what element lives at those coordinates? I tried. It works, technically. It's also slow enough that the timeline drifts under real load, and slow enough that you start missing the second event in a burst. CDP round-trips are not free, and adding one to every interaction turns the capture path into a bottleneck. The fix was to move the point of capture into the page itself. Now each interaction is recorded the moment it happens, with the context only the page has at that instant: what the element actually was, the name a person would use for it, a few stable ways to find it again, and where it sat on the page. That's the difference between "a click landed at (x, y)" and "the agent clicked the Sign in button." Everything else in the product is downstream of that. The readable timeline, the frame-accurate seek, the markdown export. All of it works because the capture point moved from outside the page to inside it. It was harder to ship than I'd guessed. The injection has to survive navigations, weird frame contexts, and the various ways pages try to defend themselves against scripts they don't recognize. ### Collapsing is opinionated Raw events are the source of truth, and we keep them. But the raw stream is just a log, and a log of human-shaped activity is unreadable. When you type the word `apple`, that's five separate input events, one per character. Storage keeps all five. The trace layer collapses them into a single `input(len=5)` row against the email field. Two clicks on the same element within half a second collapse into a double-click, because our capture stream sees individual clicks and we'd rather show the intent than two raw rows. Gaps over ten seconds become idle dividers, so a long pause doesn't disappear into whitespace. The collapsing rules are opinionated. Aggressive enough to be readable, conservative enough that you can still drop into the underlying raw events when you need them. The boundary lives in the backend, not in storage, which means we can change our minds later without rewriting history. ![Raw CDP events vs. a collapsed agent trace](https://steel.dev/assets/framerusercontent.com/images/kUNHrEG2OLy4i6cgeIwuvSIdOs.png) ### A trajectory is one happy path Here's the limit you hit fast: **one trace is one path through the site.** If you take that single trace and try to turn it into a generalizable skill, you get a script that works on exactly the conditions you captured. The cookie banner you didn't see the second time. The A/B variant you didn't get. The retry the network didn't need. It seems a handful of trajectories is sometimes enough to start generalizing from. The first bit of signal should come from the agents themselves: they need to tell us whether a run succeeded or failed. Once we have that, a stack of traces becomes more than just recordings. One thing we're looking at is stacking several runs of the same task to see where they line up and where they diverge. The parts that repeat are the reliable spine of a skill; the parts that vary are where it has to adapt. The interesting work sits on top of that, and it only works if the capture underneath is faithful enough to trust. ### A trace without intent can't teach an agent The feature people keep grabbing onto is the markdown export. Click a button, get a document built to paste into another agent: page headings, numbered steps, stable selectors per step, idle markers, redacted secrets. Paste it into Claude Code, Cursor or Codex and ask for a Steel script that reproduces the run. ![In Agent Traces now you can Copy as prompt](https://steel.dev/assets/framerusercontent.com/images/AxvVxSpwkFpmues8eCAvOqVJy8.png) But what it gives you is the trajectory: the path the agent took, and nothing more. The trace knows the agent clicked "Sign in." It doesn't know whether the agent meant to, or thought it was clicking something else, or which branches it weighed and dropped, or whether the run even worked. That gap matters more than it looks. A trajectory without intent is replayable but uneducable. Another agent can redo the exact steps, but it can't learn from them, because nothing in the trace records the reasoning that produced them. This is the part of agent observability I think everyone is going to have to solve. Logs told us what a server did. Distributed traces told us how a request moved through a system. Agent traces have to go one further: **capture not just what the agent touched but what it was trying to do**, or they stay very detailed recordings of moves nobody can explain. So that's what we're building next. ### Try it Agent Traces is live in the dashboard. Open any session at [app.steel.dev](https://app.steel.dev/) and you'll find the Agent Traces tab in the session console, next to Details and Logs. Run a task, copy the session as markdown, paste it into your editor and ask for a script back. ## Blog: Agent Traces: every browser session as a prompt https://steel.dev/blog/agent-traces · May 22, 2026 · By Nikola Balic > Every Steel browser session is now a readable timeline synced to the video, and a markdown prompt you can paste into your coding agent. **TL;DR:** Agent Traces turns every recorded Steel session into a readable timeline of coalesced actions, synced frame-accurately to the video via HLS program-date-time. Rows expand to element details and prioritized selectors, a session copies as a markdown prompt for coding agents, and data comes live over WebSocket or via REST. It answers: - What is Steel Agent Traces? - How do I turn a browser session into a prompt for a coding agent? - How do I reproduce a browser agent run as a script? - What API endpoint returns the agent traces for a Steel session? - What is the difference between the agent-traces and agent-logs endpoints? - Can I watch agent actions live while a session runs? Today we're shipping Agent Traces. Open any recorded session in your dashboard and you'll find a new tab that turns the whole run into a readable timeline. Every action the agent took, synced to the video frame by frame. Click a row, the video seeks. Click "copy as markdown" and the entire session becomes a prompt you can paste into your next agent. [Video on YouTube](https://www.youtube.com/watch?v=dxALbPqs7Bw) ### What you see Each row in the timeline is one coalesced activity. Each row reads like a sentence: `click on Sign in`, `input(len=17) on Email field`, `navigate to /home`. Typing collapses into one row, not seventeen keystrokes. Idle stretches over ten seconds show up as `idle 23s` dividers. The label is whatever a person would actually call the element. No more `