Browser tools for your agent, built on pi.
Point any model at a Kernel cloud browser: pick the tools, get plain agent objects back, and run whatever loop you already have.
import { loop } from "@onkernel/loop";
import { attach } from "@onkernel/loop/pi";
const kb = attach({ client, browser });
const { model, agentTools, models } = kb.compile({
model: "anthropic:claude-opus-5",
tools: loop.toolsets.browser(),
});Already in pi? Install the extension instead and keep pi's session, UI, and model selection:
pi install npm:@onkernel/loop
pi -p --browser-tools browser,browser-act "open example.com and report the heading"Frontier models expose computer use through different protocols: native computer/browser declarations, predefined browser action sets, ordinary function tools, different coordinate systems, and different screenshot/result contracts.
All of them expect you to:
- Run a real browser somewhere (locally is annoying, on a server is hard).
- Translate every action into an actual SDK call against that browser.
- Capture appropriate feedback from each action so the model can verify whether it had the intended effect.
- Know which of those protocols the model you picked actually accepts.
This repo does all of that and stops there. @onkernel/loop represents the
provider differences as an explicit, identity-keyed tool catalog; you choose the
exact tools, and provider transforms compose only the declarations and request
fields those identities require. It does not supply an agent class, a session
format, or a front-end — your framework already has those.
packages/
├── loop/ # @onkernel/loop - tools, catalog compilation, pi bindings, pi extension
└── ptywright/ # @onkernel/ptywright - development-only PTY/TUI test infrastructure
| Entry point | What it ships |
|---|---|
@onkernel/loop |
Canonical actions, tool factories/toolsets, catalog compilation, the tool menu, and Kernel-browser execution. |
@onkernel/loop/pi |
attach(): binds a Kernel browser and compiles a (model, tools) pair into plain pi objects, plus model resolution and provider adapters. |
pi.extensions |
A pi extension contributing those tools to pi's own agent session. |
@onkernel/ptywright |
Development-only PTY/TUI test infrastructure. |
flowchart LR
core["@onkernel/loop"]
pibind["@onkernel/loop/pi"]
ext["pi extension"]
pi["pi-agent-core / pi-ai / pi-coding-agent"]
sdk["@onkernel/sdk"]
core --> pibind
core --> ext
pibind --> ext
pi --> pibind
pi --> ext
sdk --> core
sdk --> ext
attach() binds the browser once; compile() turns a (model, tools) pair into
plain pi objects. Nothing here is a Loop type you have to learn:
import Kernel from "@onkernel/sdk";
import { loop } from "@onkernel/loop";
import { Agent, attach } from "@onkernel/loop/pi";
const client = new Kernel({ apiKey: process.env.KERNEL_API_KEY! });
const browser = await client.browsers.create({ stealth: true });
const kb = attach({ client, browser });
const { model, agentTools, models } = kb.compile({
model: "anthropic:claude-opus-5",
tools: [...loop.toolsets.browser(), loop.tools.browser.act()],
});
const agent = new Agent({
streamFn: (selected, context, options) => models.streamSimple(selected, context, options),
initialState: { model, tools: [...agentTools], systemPrompt: "Use the supplied browser tools." },
});
try {
await agent.prompt("Open example.com and report the heading.");
} finally {
await kb.dispose();
await client.browsers.deleteByID(browser.session_id);
}The compiled model carries the transport its tools derive: selecting a
provider-native browser or computer surface can change model.api, so the pair
has to reach the agent together. See
packages/loop/README.md for the harness variant,
swapping tools on a running session, and tool contexts.
Not every model accepts every tool. Ask, rather than guess:
import { loopToolMenu } from "@onkernel/loop";
import { getLoopModel } from "@onkernel/loop/pi";
for (const entry of loopToolMenu(getLoopModel("openai:gpt-5.6-sol"))) {
console.log(entry.label, entry.available ? "ok" : `unavailable: ${entry.unavailableReason}`);
}Availability is decided by compiling the candidate catalog, so the menu cannot drift from what the compiler accepts, and the reason shown for an unavailable tool is the compiler's own. It is also pairwise: two providers' native surfaces cannot coexist, so rebuild the menu after each change rather than caching a per-tool verdict.
- Model layer —
@onkernel/loop/piopens pi-ai's whole model catalog and composes provider declarations, headers, and payload transforms around it. Catalog compilation is declaration-only: it never sees an executor. - Execution layer —
@onkernel/loopmaterializes the caller's exact catalog over one shared resource pool and executes canonical actions through Kernel's computer API or a raw-CDP browser executor. - Transport — the compiled catalog derives
model.apifrom the selected tools, so a provider-native surface reaches the wire with the transport, headers, and payload shape it requires. - Browser — a Kernel cloud browser with optional profile and proxy. The model requests screenshots explicitly when it needs visual feedback.
See docs/architecture.md for the full end-to-end flow.
npm ci
npm run typecheck
npm run build --workspace @onkernel/loop
npm test --workspace @onkernel/loopBuild before testing: the pi print/RPC test loads the extension the way pi does,
through the package's own entry points. Live end-to-end tests skip unless
LOOP_E2E_LIVE=1 is set, and integration tests run separately via
npm run test:integration.
MIT