Build it up · Step 5 of 8

Give it a workspace

Past a handful of tools, adding another tool for every operation stops working. The model has to read every definition on every call, and each step of a multi-step job costs a round-trip. A workspace gives the agent somewhere to keep files and run code instead. It writes one program that reads, loops and decides, and only the answer comes back to the conversation.

Surfacechat · voice · APIContextprompt · history · live stateModelany providerStatestore · memory · inboxToolsvalidated · gatedTeamsubagents · meshWorkspacefiles · code · webloopuntil the model answersprompttool callsresultshistorymessageanswerdisplay stack: show · ask · waita subagent is a toolacts on
One request through a Glove harness. The message is saved to state; context is assembled from state before every model call; the model asks for tools; results are saved; the cycle repeats until the model answers. Tools can render UI on the surface and wait for a reply, act on a workspace, or hand work to a subagent.

Pick the surface by the work

The work looks like…Give itPackage
A few well-defined actionsPlain toolsglove-core
Many operations, chained with logic in betweenOne code tool over a function catalogueglove-js · glove-python · glove-lisp
Lots of read-only data to filter and joinCapabilities as SQL tablesglove-scratchpad
Building files over many steps: drafts, data, documentsA working environment: files, scripts, checkpointsglove-working-environment
Third-party services that already speak MCPMounted MCP serversglove-mcp
The real web, or real code in a real OSA browser or a sandboxglove-execution

These combine. The release assistant uses three of them.

One tool instead of fifty: a REPL

release.tstypescript
import { JsSession, defineFn, mountJs } from "glove-js";
import { z } from "zod";

const session = JsSession.create();
session.register(defineFn({
  name: "git__merged",
  description: "Pull requests merged since a date.",
  input: z.object({ since: z.string() }),
  readOnlyHint: true,
  handler: ({ since }) => git.merged(String(since)),        // your Git client
}));
session.register(defineFn({
  name: "git__diffstat",
  description: "Files and line counts changed by a pull request.",
  input: z.object({ number: z.number() }),
  readOnlyHint: true,
  handler: ({ number }) => git.diffstat(Number(number)),
}));

mountJs(agent, { session });

The agent now has execute_js plus discovery: it can search the catalogue and describe a function before calling it. To size a release it writes one program (fetch the merged PRs, get each diffstat, total the lines, keep the five largest) and gets back five rows instead of fifty tool results. Bindings persist for the session, so the next program can use what the last one computed. The interpreter has no network, filesystem or process access beyond the functions you registered. Python and Lisp work the same way.

Files that persist: the working environment

release.tstypescript
import { createWorkingEnvironment, mountWorkingEnvironment } from "glove-working-environment";
import { documents } from "glove-env-documents";

const env = await createWorkingEnvironment({ stdlib: [documents()] });
await env.mount({ text: changelogTemplate }, "/inbox/changelog-template.md");
mountWorkingEnvironment(agent, { env });

// … after the run, collect what it produced:
const files = await env.export("/out/**");

The agent gets a closed set of verbs over a virtual filesystem: write_file, edit_file, read_file, ls, grep, run_script, checkpoint, undo and more. Scripts are named files that run in worker threads and can import only what you inject (here env:documents, so it can write the notes as a PDF or DOCX). Big data stays in files; only what the agent reads enters context. Other adapters add spreadsheets, slides, images, media, archives, HTTP with a domain policy, and a renderer so the agent can look at its own output. The tree is a glove-vfs filesystem: back it with a directory or object storage to make it outlive the process.

Services that speak MCP

mountMcp(agent, { adapter, entries }) takes a catalogue of servers your app supports and an adapter that remembers which are active and resolves their tokens. The agent gets each active server's tools, plus a discovery subagent that can find and switch on another server mid-conversation. Your adapter keeps the tokens; the model never sees them. See MCP.

The real web and real machines

mountBrowser(agent, { adapter }) and mountSandbox(agent, { adapter }) from glove-execution add execute_browser and execute_sandbox: the same script-the-whole-step idea, over a browser session or a persistent container. Screenshots reach the model as images on its next call. Station is the first backend; in Foundry, one managed Station can host both. On Kuiper, use Station's containers on the unit, or call Kuiper's browsers and sandboxes through @kuiper-sh/sdk from your own tools once the unit references ${{browsers.…}} or ${{sandboxes.…}}; there is no glove-execution adapter for Kuiper's gateway yet. See browsers and sandboxes and the coding agent blueprint.

Already built for you

PrimitiveWhat you getInstead of building
Virtual filesystemglove-vfsOne tree shared by the working environment, memory resources and REPLs, with path-scoped access rules.mountFs([...]) · withAccess(fs, policy) · inMemoryFs() · hostDirectory(dir) · cachedRemote(store)letting tools read and write the host filesystem
Working environmentglove-working-environmentFiles, named scripts in worker threads, checkpoints, undo, run history and artifact export, with no ambient network or host access.mountWorkingEnvironment(glove, { env: await createWorkingEnvironment({ stdlib }) })a read/write/run tool set and a code runner
Format adaptersglove-env-*PDF, DOCX, XLSX, PPTX, images, audio, video, archives, HTTP and secrets as script imports, plus rendering pages so the agent can look at its output.stdlib: [documents(), spreadsheets(), slides(), images(), media(), render(), fetchFiles()]wiring pdf, docx and spreadsheet libraries into tools
Browsersglove-executionexecute_browser: the model scripts observe, act and verify in one call, and screenshots arrive as images.mountBrowser(glove, { adapter })Playwright wrapped in click/type/screenshot tools
Sandboxesglove-executionexecute_sandbox over persistent containers: files, commands, terminals and long-running services, with ownership and cleanup.mountSandbox(glove, { adapter })docker exec wrappers and container bookkeeping

Next

One agent is now doing everything: reading code, drafting, checking and publishing. In step 6 we split it: a reviewer subagent with read-only tools checks the notes before anything ships.

The whole path

  1. 01A loop and a toolLoop · Tools
  2. 02Show your workSurface
  3. 03Control what it seesContext
  4. 04Make it rememberState
  5. 05Give it a workspaceWorkspace
  6. 06Split the workTeam
  7. 07Make it a systemIdentity · Time · Arrival · Oversight · Access
  8. 08Put it in productionRun · Keep · Reach · Watch