Sign inConnect your agent
infochanged

Agents, Browser Run - Agents SDK: Preview dev servers in the browser without public URLs

Announced
Tags
Agents, Browser-Run
Severity
info, set by reef/rss-rule-v1
Source
https://developers.cloudflare.com/changelog/post/2026-10-28-browser-internal-hostnames/

<p>Agents that build websites need to check their work in a real browser. Internal hostnames let your agent's browser preview a dev server running in a container without exposing that server to the public Internet.</p> <p>With internal hostnames, the <a href="https://developers.cloudflare.com/agents/tools/browser/">persistent browser</a> in the <a href="https://developers.cloudflare.com/agents/">Cloudflare Agents SDK</a> can open sites that only your agent serves. Give the browser hostnames such as <code>preview.internal</code>, and its requests to them go to your agent instead of the Internet. The model can check a dev server in its sandbox, a folder in its workspace, or another Worker in the same browser it uses for the web, and nothing is exposed publicly.</p> <img src="https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=960,height=524,format=svg/_astro/browser-internal-hostnames.FAVHfafR.svg" alt="An animated diagram of three kinds of hostname: example.com goes out to the Internet; the model points preview.internal at port 5173 with browser.assignHost(), then the browser opens it and your agent serves it from the sandbox; and api.internal goes straight to another Worker, skipping your agent" loading="lazy" decoding="async" width="960" height="524"> <p>Each internal host has a <code>fetch</code> function that runs in your Durable Object, so it can use the object's bindings and state. A host can also send its requests straight to another Worker through a service binding. Add a <code>target</code> schema to a host, and the model chooses what it serves, such as which port or which folder.</p> <p>Internal hostnames are a capability of the browser, independent of your agent harness. Use the built-in adapters for Pi Durable, AI SDK, and TanStack AI, or connect another harness by implementing a tool with <a href="https://developers.cloudflare.com/agents/tools/browser/#use-another-harness"><code>browser.session()</code></a>. No prebuilt adapter is required.</p> <p>This example adds a private dev-server preview to a <a href="https://developers.cloudflare.com/agents/tools/browser/#use-it-with-pi-durable">Pi Durable agent</a>. Start with that guide's packages, bindings, and request handler, and install <a href="https://github.com/cloudflare/agents/releases/tag/agents%400.31.0" target="_blank" rel="noopener">Agents SDK v0.31.0<span class="external-link"> ↗︎</span></a> or later. The example has three parts:</p> <ul> <li><strong>Browser configuration:</strong> <code>internalHosts</code> defines how the browser reaches the dev server.</li> <li><strong>Durable Object wiring:</strong> Lifecycle and <code>fetchInternalHost()</code> connect the browser to your application.</li> <li><strong>Harness integration:</strong> <code>browserTool</code> from <code>agents/browser/pi</code> registers the tool with Pi.</li> </ul> <div><div data-nb-tabs data-nb-sync-key="workersExamples" class><div class="relative flex border-b border-border" role="tablist" data-nb-tabs-list><span class="bg-primary pointer-events-none absolute -bottom-px h-0.5 rounded-t-sm transition-[left,width] duration-200 ease-out" data-nb-tabs-indicator aria-hidden="true"></span></div><div class="mt-3"><div role="tabpanel" data-nb-tabs-content data-nb-tab-label="JavaScript" class><figure class="nb-code-figure" data-nb-lang="js"><pre class="astro-code astro-code-themes github-light github-dark nb-shiki-c6xiwz" tabindex="0" data-language="js" data-nb-lang="js"><code><span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { DurableObject } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "cloudflare:workers"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createModels } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "@earendil-works/pi-ai/models"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createRegistry, Harness } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "@earendil-works/pi-durable"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { Browser, browserRun } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { browserTool } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser/pi"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { PiHarness } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/harness/pi"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { Lifecycle } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/lifecycle"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createAI } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/models/pi-ai"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { z } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "zod"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-21nrsd">// Your own code: reach a port on your sandbox and return its Response.</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { fetchSandboxPort } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "./hosts"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd">// Browser Run calls back into your Worker through this entrypoint.</span></span> <span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-140thh"> { InternalHostRouter } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> class</span><span class="nb-shiki-1t8gfj"> BrowserAssistant</span><span class="nb-shiki-1itgoe"> extends</span><span class="nb-shiki-1t8gfj"> DurableObject</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-1jdh33"> ai</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createAI</span><span class="nb-shiki-140thh">({ binding: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">AI</span><span class="nb-shiki-140thh"> });</span></span> <span class="line"><span class="nb-shiki-1jdh33"> registry</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createRegistry</span><span class="nb-shiki-140thh">();</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> browser</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Browser</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> provider: </span><span class="nb-shiki-1t8gfj">browserRun</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">BROWSER</span><span class="nb-shiki-140thh">, {</span></span> <span class="line"><span class="nb-shiki-140thh"> internalHosts: {</span></span> <span class="line"><span class="nb-shiki-140thh"> router: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.ctx.exports.InternalHostRouter,</span></span> <span class="line"><span class="nb-shiki-140thh"> hosts: {</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "preview.internal"</span><span class="nb-shiki-140thh">: {</span></span> <span class="line"><span class="nb-shiki-140thh"> description: </span><span class="nb-shiki-mdbnqw">"Your dev servers"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // What the model passes to browser.assignHost() to choose</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // what this host serves. fetch receives it as its second argument.</span></span> <span class="line"><span class="nb-shiki-140thh"> target: z.</span><span class="nb-shiki-1t8gfj">object</span><span class="nb-shiki-140thh">({ port: z.</span><span class="nb-shiki-1t8gfj">number</span><span class="nb-shiki-140thh">().</span><span class="nb-shiki-1t8gfj">int</span><span class="nb-shiki-140thh">().</span><span class="nb-shiki-1t8gfj">min</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">1024</span><span class="nb-shiki-140thh">) }),</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">: (</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, { </span><span class="nb-shiki-1jdh33">port</span><span class="nb-shiki-140thh"> }) </span><span class="nb-shiki-1itgoe">=></span><span class="nb-shiki-1t8gfj"> fetchSandboxPort</span><span class="nb-shiki-140thh">(port, request),</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> }),</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> harness</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> PiHarness</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> harness</span><span class="nb-shiki-140thh">: ({ </span><span class="nb-shiki-1jdh33">storage</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">context</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">onWake</span><span class="nb-shiki-140thh"> }) </span><span class="nb-shiki-1itgoe">=></span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> models</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createModels</span><span class="nb-shiki-140thh">();</span></span> <span class="line"><span class="nb-shiki-140thh"> models.</span><span class="nb-shiki-1t8gfj">setProvider</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.ai.provider);</span></span> <span class="line"><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.registry.</span><span class="nb-shiki-1t8gfj">install</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> name: </span><span class="nb-shiki-mdbnqw">"browser"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> tools: [</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> browserTool</span><span class="nb-shiki-140thh">({ browser: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.browser, loader: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">LOADER</span><span class="nb-shiki-140thh"> }),</span></span> <span class="line"><span class="nb-shiki-140thh"> ],</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-140thh"> Harness.</span><span class="nb-shiki-1t8gfj">open</span><span class="nb-shiki-140thh">(</span></span> <span class="line"><span class="nb-shiki-140thh"> storage,</span></span> <span class="line"><span class="nb-shiki-140thh"> { models, registry: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.registry, onWake },</span></span> <span class="line"><span class="nb-shiki-140thh"> context,</span></span> <span class="line"><span class="nb-shiki-140thh"> );</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> defaults: { model: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">ai</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"@cf/zai-org/glm-5.3-flash"</span><span class="nb-shiki-140thh">) },</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> lifecycle</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> Lifecycle.</span><span class="nb-shiki-1t8gfj">install</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">).</span><span class="nb-shiki-1t8gfj">use</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.browser).</span><span class="nb-shiki-1t8gfj">use</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.harness);</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd"> // InternalHostRouter calls this method to reach your Browser.</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> fetchInternalHost</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">message</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.browser.</span><span class="nb-shiki-1t8gfj">fetchInternalHost</span><span class="nb-shiki-140thh">(message);</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> async</span><span class="nb-shiki-1t8gfj"> ask</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">prompt</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-140thh"> { </span><span class="nb-shiki-dzsirb">text</span><span class="nb-shiki-140thh"> } </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.harness.</span><span class="nb-shiki-1t8gfj">prompt</span><span class="nb-shiki-140thh">(prompt);</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-140thh"> text;</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"><span class="nb-shiki-140thh">}</span></span></code></pre></figure></div><div role="tabpanel" data-nb-tabs-content data-nb-tab-label="TypeScript" class><figure class="nb-code-figure" data-nb-lang="ts"><pre class="astro-code astro-code-themes github-light github-dark nb-shiki-c6xiwz" tabindex="0" data-language="ts" data-nb-lang="ts"><code><span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { DurableObject } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "cloudflare:workers"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createModels } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "@earendil-works/pi-ai/models"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createRegistry, Harness } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "@earendil-works/pi-durable"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { Browser, browserRun, </span><span class="nb-shiki-1itgoe">type</span><span class="nb-shiki-140thh"> InternalHostMessage } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { browserTool } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser/pi"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { PiHarness } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/harness/pi"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { Lifecycle } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/lifecycle"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { createAI } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/models/pi-ai"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { z } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "zod"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-21nrsd">// Your own code: reach a port on your sandbox and return its Response.</span></span> <span class="line"><span class="nb-shiki-1itgoe">import</span><span class="nb-shiki-140thh"> { fetchSandboxPort } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "./hosts"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd">// Browser Run calls back into your Worker through this entrypoint.</span></span> <span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-140thh"> { InternalHostRouter } </span><span class="nb-shiki-1itgoe">from</span><span class="nb-shiki-mdbnqw"> "agents/browser"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> class</span><span class="nb-shiki-1t8gfj"> BrowserAssistant</span><span class="nb-shiki-1itgoe"> extends</span><span class="nb-shiki-1t8gfj"> DurableObject</span><span class="nb-shiki-140thh">&lt;</span><span class="nb-shiki-1t8gfj">Env</span><span class="nb-shiki-140thh">&gt; {</span></span> <span class="line"><span class="nb-shiki-1jdh33"> ai</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createAI</span><span class="nb-shiki-140thh">({ binding: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">AI</span><span class="nb-shiki-140thh"> });</span></span> <span class="line"><span class="nb-shiki-1jdh33"> registry</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createRegistry</span><span class="nb-shiki-140thh">();</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> browser</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Browser</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> provider: </span><span class="nb-shiki-1t8gfj">browserRun</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">BROWSER</span><span class="nb-shiki-140thh">, {</span></span> <span class="line"><span class="nb-shiki-140thh"> internalHosts: {</span></span> <span class="line"><span class="nb-shiki-140thh"> router: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.ctx.exports.InternalHostRouter,</span></span> <span class="line"><span class="nb-shiki-140thh"> hosts: {</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "preview.internal"</span><span class="nb-shiki-140thh">: {</span></span> <span class="line"><span class="nb-shiki-140thh"> description: </span><span class="nb-shiki-mdbnqw">"Your dev servers"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // What the model passes to browser.assignHost() to choose</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // what this host serves. fetch receives it as its second argument.</span></span> <span class="line"><span class="nb-shiki-140thh"> target: z.</span><span class="nb-shiki-1t8gfj">object</span><span class="nb-shiki-140thh">({ port: z.</span><span class="nb-shiki-1t8gfj">number</span><span class="nb-shiki-140thh">().</span><span class="nb-shiki-1t8gfj">int</span><span class="nb-shiki-140thh">().</span><span class="nb-shiki-1t8gfj">min</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">1024</span><span class="nb-shiki-140thh">) }),</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">: (</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, { </span><span class="nb-shiki-1jdh33">port</span><span class="nb-shiki-140thh"> }) </span><span class="nb-shiki-1itgoe">=&gt;</span><span class="nb-shiki-1t8gfj"> fetchSandboxPort</span><span class="nb-shiki-140thh">(port, request),</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> }),</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> harness</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> PiHarness</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> harness</span><span class="nb-shiki-140thh">: ({ </span><span class="nb-shiki-1jdh33">storage</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">context</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">onWake</span><span class="nb-shiki-140thh"> }) </span><span class="nb-shiki-1itgoe">=&gt;</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> models</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1t8gfj"> createModels</span><span class="nb-shiki-140thh">();</span></span> <span class="line"><span class="nb-shiki-140thh"> models.</span><span class="nb-shiki-1t8gfj">setProvider</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.ai.provider);</span></span> <span class="line"><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.registry.</span><span class="nb-shiki-1t8gfj">install</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> name: </span><span class="nb-shiki-mdbnqw">"browser"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> tools: [</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> browserTool</span><span class="nb-shiki-140thh">({ browser: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.browser, loader: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.env.</span><span class="nb-shiki-dzsirb">LOADER</span><span class="nb-shiki-140thh"> }),</span></span> <span class="line"><span class="nb-shiki-140thh"> ],</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-140thh"> Harness.</span><span class="nb-shiki-1t8gfj">open</span><span class="nb-shiki-140thh">(</span></span> <span class="line"><span class="nb-shiki-140thh"> storage,</span></span> <span class="line"><span class="nb-shiki-140thh"> { models, registry: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.registry, onWake },</span></span> <span class="line"><span class="nb-shiki-140thh"> context,</span></span> <span class="line"><span class="nb-shiki-140thh"> );</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh"> defaults: { model: </span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.</span><span class="nb-shiki-1t8gfj">ai</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"@cf/zai-org/glm-5.3-flash"</span><span class="nb-shiki-140thh">) },</span></span> <span class="line"><span class="nb-shiki-140thh"> });</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1jdh33"> lifecycle</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> Lifecycle.</span><span class="nb-shiki-1t8gfj">install</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">).</span><span class="nb-shiki-1t8gfj">use</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.browser).</span><span class="nb-shiki-1t8gfj">use</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">this</span><span class="nb-shiki-140thh">.harness);</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd"> // InternalHostRouter calls this method to reach your Browser.</span></span> <span class="line"><span class="nb-shiki-1t8gfj"> fetchInternalHost</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">message</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-1t8gfj"> InternalHostMessage</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.browser.</span><span class="nb-shiki-1t8gfj">fetchInternalHost</span><span class="nb-shiki-140thh">(message);</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> async</span><span class="nb-shiki-1t8gfj"> ask</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">prompt</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-dzsirb"> string</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-140thh"> { </span><span class="nb-shiki-dzsirb">text</span><span class="nb-shiki-140thh"> } </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-dzsirb"> this</span><span class="nb-shiki-140thh">.harness.</span><span class="nb-shiki-1t8gfj">prompt</span><span class="nb-shiki-140thh">(prompt);</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-140thh"> text;</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"><span class="nb-shiki-140thh">}</span></span></code></pre></figure></div></div></div><script type="module" src="https://developers.cloudflare.com/home/runner/work/cloudflare-docs/cloudflare-docs/src/components/ui/tabs/Tabs.astro?astro&type=script&index=0&lang.ts"></script></div> <p>To use another harness, keep the browser configuration. Choose a <a href="https://developers.cloudflare.com/agents/tools/browser/#use-another-harness">browser-tool adapter</a> if one is available, or implement a tool using <code>browser.session()</code>.</p> <p><code>browserTool</code> tells the model about each host, including the schema of its <code>target</code>. Because <code>preview.internal</code> has a <code>target</code> schema, the model can choose which port it serves. To check its dev server, the model calls <code>browser.assignHost()</code> with a target that matches the schema, such as <code>{ port: 5173 }</code>, and opens the URL it gets back:</p> <figure class="nb-code-figure" data-nb-lang="js"><pre class="astro-code astro-code-themes github-light github-dark nb-shiki-c6xiwz" tabindex="0" data-language="js" data-nb-lang="js"><code><span class="line"><span class="nb-shiki-1itgoe">const</span><span class="nb-shiki-140thh"> { </span><span class="nb-shiki-dzsirb">url</span><span class="nb-shiki-140thh"> } </span><span class="nb-shiki-1itgoe">=</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> browser.</span><span class="nb-shiki-1t8gfj">assignHost</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> host: </span><span class="nb-shiki-mdbnqw">"preview.internal"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> target: { port: </span><span class="nb-shiki-dzsirb">5173</span><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh">});</span></span> <span class="line"><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> browser.</span><span class="nb-shiki-1t8gfj">cdp</span><span class="nb-shiki-140thh">({</span></span> <span class="line"><span class="nb-shiki-140thh"> method: </span><span class="nb-shiki-mdbnqw">"Page.navigate"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> params: { url },</span></span> <span class="line"><span class="nb-shiki-140thh"> sessionId: </span><span class="nb-shiki-mdbnqw">"active"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh">});</span></span></code></pre></figure> <p>The target is checked against the schema and stored, so it lasts across turns, browser restarts, and redeploys. Each request to <code>http://preview.internal/</code> then calls the host's <code>fetch</code> function with <code>{ port: 5173 }</code>.</p> <p>This release also simplifies <code>browserTool</code>. It runs the model's code in its own <a href="https://developers.cloudflare.com/dynamic-workers/">Dynamic Worker</a> and no longer needs a <code>CodemodeRuntime</code> export. Refer to the <a href="https://github.com/cloudflare/agents/releases/tag/agents%400.31.0" target="_blank" rel="noopener">v0.31.0 upgrade notes<span class="external-link"> ↗︎</span></a> if you already use <code>browserTool</code>.</p> <p>For the complete example, limits, and security guidance, refer to <a href="https://developers.cloudflare.com/agents/tools/browser/#internal-hostnames">Internal hostnames</a>.</p>

The text above is the source's. reef added the severity, the dates it could read from the text, and the endpoints and scopes named in code.