Browsers & sandboxes

glove-execution gives an existing agent browser workflows and a persistent coding sandbox. The agent keeps its model; adapters own the connection to the resource backend.

Use it when the agent needs to inspect websites, interact with a page, write files, execute commands, or run a service. There are two backends: Station 3 (glove-execution/station) and Kuiper (glove-execution/kuiper). The default package entrypoint uses portable JavaScript and Web APIs.

Where it fits

Glove core supplies the agent loop, independent packages mount capabilities, and Foundry optionally hosts their resources on its managed Station.
Choose capability packages independently, then choose the application's hosting and persistence adapters.
PackageResponsibility
glove-corePortable agent loop, model, tools, stores and transient context.
glove-jsA bounded interpreter for composing registered functions into one program.
glove-executionBrowser and sandbox mounts, with scoped resource access through adapters.
glove-working-environment / glove-vfsWorkspace files, scripts, checkpoints and artifact composition. They remain distinct from a live container or browser process.
glove-memoryContext, entities, episodes and resources. Applications choose scope and what to retrieve.
glove-foundryApplication assembly, instances, conversations, schedules and managed execution.

Mount on an existing agent

bash
pnpm add glove-execution station-browser-use station-client
Agent setuptypescript
import { mountBrowser, mountSandbox } from "glove-execution";

// The application supplies scoped adapters. Configure the agent's model normally.
const browser = mountBrowser(agent, { adapter: browserAdapter });
const sandbox = mountSandbox(agent, { adapter: sandboxAdapter });

try {
  await agent.processRequest("Inspect a site and build a small demo.");
} finally {
  await Promise.allSettled([browser.close(), sandbox.close()]);
}

Mounting exposes execute_browser and execute_sandbox. It does not open a browser or create a sandbox automatically. surface: "tools" exposes direct operations instead of scripting. The mount neither requires nor replaces a model.

Compose a workflow in one call

execute_browser programjavascript
const page = browser.open({});
browser.navigate({ sessionId: page.id, url: "https://example.com" });
const observation = browser.observe({ sessionId: page.id });
browser.screenshot({ sessionId: page.id });
observation;

Registered functions resolve automatically; await is optional. Use fns("browser") and describe("browser__interact") to discover schemas. Branch, inspect and act in the same program. The interpreter has no ambient host filesystem, imports or network access. Page evaluation is a separate adapter grant.

The newest screenshot reaches the next model iteration as native image content after the tool results. It is transient and bounded; image bytes stay out of script output, saved conversation history and runtime-context telemetry. Visual reasoning still requires a model turn. A failed script may already have changed the external resource, so it is not automatically replayed.

To judge a page without reading it, wrap the adapter with withClassifier(adapter, { classifier: jev() }) from glove-classifier. The added browser.judge({ sessionId, questions }) observes the page and returns only typed answers. Questions like “is this a login wall?” or “did checkout succeed?” get answered without the DOM entering context.

Kuiper's browsers and sandboxes

bash
pnpm add glove-execution @kuiper-sh/sdk playwright-core
Agent setuptypescript
import { Kuiper } from "@kuiper-sh/sdk";
import { kuiperBrowser, kuiperSandbox } from "glove-execution/kuiper";

const kuiper = new Kuiper({ apiKey: process.env.GATEWAY_KEY }); // a gateway key (kgw_…)
const browser = mountBrowser(agent, { adapter: kuiperBrowser({
  browsers: kuiper.browsers,
  session: { persistProfile: true },                  // Kuiper's own session options; never the model's
  guard: ({ url, method }) => method === "GET" || allowed.includes(url.host),
  onHandOff: ({ url }) => sendToThePerson(url),       // the live view, for a sign-in or a CAPTCHA
}) });
const sandbox = mountSandbox(agent, { adapter: kuiperSandbox({ sandboxes: kuiper.sandboxes, sandbox: { image: "kuiper/coding" } }) });
  • A real Chrome per session, driven over CDP with Playwright: open, navigate, observe (the accessibility tree or the text), interact on role, label or text targets, screenshot, close.
  • guard sees every request the browser makes while the agent is connected, before it leaves: pages, form posts, a page's own fetches, WebSockets. A blocked page load fails with blocked.
  • handOff sends Kuiper's interactive live view to your host code, never to the model, and keeps the agent off that browser for the rest of the run.
  • Sessions outlive the process. Save resourceIds() and grant them to the next run with sessionIds. cleanup releases them (the default), hibernates them, or keeps them for a person in the live view; connecting resumes a hibernated one.
  • Sandboxes are Kuiper's remote computers from ready-made images. A hibernated one resumes before anything runs in it.

Let Foundry own one Station

foundry.application.tstypescript
import { defineApplication } from "glove-foundry";
import { stationDaemon } from "glove-foundry/station";

export default defineApplication({
  name: "Assistant",
  daemon: stationDaemon({
    stationId: "assistant",
    resources: createResources, // application factory: { browser, sandbox }
    onReady: savePrivateConnection,
  }),
});

The application supplies the factory and private connection storage. Foundry invokes the factory once inside its managed Station daemon and owns startup and shutdown. The same Station runs agent jobs and resource APIs. Omit this option for jobs only, or return only the provider you need. Agent definitions continue to mount explicitly with per-run grants.

onReady receives a private operator connection, never a value to put in model context or frontend code. Station 3's operator API requires admin scope. Provider credentials remain in the resource adapters. Programmatic runtime setup supplies applicationFilePath so the daemon can load the application itself.

Choose lifetimes in the application

Browser pages, browser profiles, sandbox files, services, script bindings and conversation memory have different lifetimes. Retaining a scope does not keep its daemon alive. Persisted files and profiles depend on provider adapters; unfinished task tracking depends on the application's memory and prompting choices. Conversation-scoped memory remains the default.

Foundry requires Node 22+; Operator's SQLite memory needs Node 22.13+. Managed execution is local and stops with Foundry. The queue is currently in memory; durable Foundry activations can be reconstructed, but an interrupted live process is not restored.

Continue with the illustrated Operator walkthrough, adapter and lifecycle reference, or design story.