Every agent harness is the same seven parts
A coding agent, a personal assistant and a support desk look like three different products. Underneath, each one is the same seven parts in different proportions. Once you can see the parts, a new harness is mostly an assembly job, and most of the parts are already built.
What a harness is
A model takes text and images in and gives text and tool calls back. That is all it does. It cannot run the tool it just asked for. It cannot remember yesterday, open a file, wait for someone to approve a refund, or notice that a ticket arrived at three in the morning.
Everything that does those things is the harness. Claude Code is a harness. So is the support bot on your bank's website, and so is the assistant that texts you when your flight moves. They feel different because their harnesses are tuned for different jobs, not because they are made of different things.
Here is one request going through a Glove harness:
The person's message is saved. Context is assembled from what was saved, plus whatever is true right now. The model reads it and asks for tools. The tools run, and their results are saved. Then it goes round again, until the model answers instead of asking for another tool. Every harness you have used works this way, whether or not it says so.
The seven parts
Each part answers one question. Every harness has to answer all seven, even if the answer is "not much".
- Loop: how does it act? Call the model, run the tools it asks for, feed back the results, stop when it answers. Then the details: retries, a turn limit, cancelling halfway, streaming text, counting tokens.
- Tools: what can it do? Every capability is a name, a description the model reads, a schema and a function. Search, refund, run a shell command, call another service.
- Context: what does it see? The instructions, the history, and the state of the world right now. Plus guidance it loads only when it needs it, and a way to keep a long session inside the window.
- State: what does it remember? The conversation, a plan, what it learned about this person, what it is still waiting on, what it is trying to finish.
- Workspace: where does it work? Files it can write and read back, code it can run, a browser, a container. A place where big data and finished artifacts live outside the context window.
- Surface: how do people reach it? A chat window, a terminal, a phone call, an API. And the way a tool shows a result or asks a question and waits for the answer.
- Team: who does it work with? A subagent that takes a narrow job with its own tools, or another agent it messages.
Three harnesses, side by side
Put three very different products next to each other and fill in the parts.
| Part | Coding agent | Personal assistant | Support desk |
|---|---|---|---|
| Loop | Long, many tool calls per request | Short, a few calls per message | Short, but many at once |
| Tools | Read, edit, run, search the repository | Calendar, email, web, reminders | Order lookup, refunds, ticket replies |
| Context | Repository conventions, the current plan | Who you are, what is on today | Policy, the customer's account right now |
| State | A task list, the session | Memory of you, pending reminders | Ticket history, the details collected so far |
| Workspace | A sandboxed checkout and a shell | A browser, documents it writes | Usually none |
| Surface | Terminal, editor, permission prompts | Telegram, email, voice notes | Chat widget, email, an approval queue |
| Team | An explorer subagent, a reviewer | A research subagent | A triage classifier, a specialist per topic |
The columns differ in proportion, not in kind. A coding agent spends its effort on the workspace and the loop. A personal assistant spends it on state and on acting at the right time. A support desk spends it on events arriving from outside and on people approving what it does.
That is where the interesting work is. The rest, the loop, the tool plumbing, saving the conversation, keeping context fresh, showing a prompt and waiting, is the same in all three. It should not be written three times.
Take apart one you already use
Try it on a coding agent in your terminal. Every piece you see maps to one part, and to one Glove primitive:
| What you see | Part | The Glove primitive |
|---|---|---|
| It keeps calling tools until the change is done | Loop | processRequest on a built Glove |
| Read, edit, search and run commands | Tools | fold, or one interpreter over many functions |
| "Allow this command?" | Tools, surface | requiresPermission and the display stack |
| A project instructions file and skills it reads when relevant | Context | A stable system prompt and useReadSkill |
| A todo list that updates as it works | Context, state | The built-in glove_update_tasks |
| Long sessions that keep going after the window fills | Context | compaction_config |
| Your checkout and a shell | Workspace | mountSandbox or a working environment |
| An explorer it sends off to search the codebase | Team | defineSubAgent |
Nothing in that list is specific to coding. Swap the tools and the workspace and the same harness is a data analyst. Swap the surface for a phone call and it is a voice agent.
What Glove gives you for each part
Glove is built as one primitive per part, so the shape of the framework is the shape of the problem. The smallest harness is a loop and one tool:
import { Glove, Displaymanager } from "glove-core";
import { createAdapter } from "glove-core/models/providers";
import { z } from "zod";
const agent = new Glove({
model: createAdapter({ provider: "anthropic", model: "claude-sonnet-4-20250514" }),
displayManager: new Displaymanager(),
systemPrompt: "You help a team ship releases. Use tools for facts.",
compaction_config: { compaction_instructions: "Summarise the release work so far." },
})
.fold({
name: "list_merged_changes",
description: "List pull requests merged since a date, newest first.",
inputSchema: z.object({ since: z.string().describe("ISO date") }),
async do({ since }) {
// mergedSince is your own lookup: GitHub, a database, a file.
return { status: "success", data: await mergedSince(since) };
},
})
.build();
const result = await agent.processRequest("What shipped since Monday?");That is already a full loop with validated input, retries, a turn limit, compaction and a store. Each other part is one more call on the same agent:
- Surface. A tool calls
display.pushAndWait(…)to show a confirmation and resume with the answer.glove-reactrenders it;glove-voicespeaks it. - Context.
agent.addContextProvider(() => currentState())puts live state after the history on every model call, without touching the system prompt. - State. Any
StoreAdapterkeeps the conversation, tasks and inbox.glove-memoryadds what the agent learns; forms and goals track what it is trying to get done. - Workspace.
mountWorkingEnvironmentgives it files and scripts;mountJsgives it an interpreter;mountBrowserandmountSandboxgive it a browser and a container. - Team.
agent.defineSubAgent(…)registers a child agent the model can call like any tool.
None of these replace the loop or the model. They all attach to the agent you already have. That is a deliberate rule: capability packages mount onto an agent, they never wrap it.
When a harness becomes a system
A harness serves the person in front of it. The moment there is more than one person, or work has to happen with nobody in the chat, five new questions show up. Glove Foundry answers them:
- Identity: whose agent is this? One definition in code, many instances in data, each with its own conversations, context and installed tools.
- Time: when does it act? Schedules, sleep and spawn are tools the agent calls itself, backed by durable records.
- Arrival: what wakes it up? Transmissions turn Slack, email or a webhook into typed events; playbooks route them, and can create the agent that handles them.
- Oversight: who keeps it in check? A gated tool becomes an expiring approval record; running work can be steered; an inspector shows every call.
- Access: how does other software call it? A typed client, REST, an async run API and OpenAI-compatible endpoints.
These are not features you add to a harness. They are what turns harnesses into a service. Teams that skip Foundry tend to build them anyway, one table and one cron job at a time.
When a system meets production
A system still has to run somewhere. That brings the last four questions, and Kuiper answers them:
- Run: where does it run? Kuiper detects a Foundry app and deploys it with no Dockerfile.
- Keep: where does state live? A
/datadisk for Foundry's data and transcripts, managed databases, and sealed secrets. - Reach: what does it reach? One model gateway in front of your own provider keys, real browsers, sandboxes and email addresses.
- Watch: how do you see it? Logs, the agent's runs read into the dashboard, every model call, usage and budgets.
Why the model matters
The most expensive mistake we see is not a bug. It is a part that already existed, built again by hand. A job queue for "remind me tomorrow" when the agent could schedule itself. A system prompt rebuilt every turn to include the current cart, which also throws away the prompt cache. An approvals table with its own pause and resume. Fifty tool definitions where one interpreter would do. Each one works, and each one is code someone now owns.
Coding agents make the same mistake faster. Ask one to "add reminders" and it will happily write a scheduler. It does not know the primitive exists unless something tells it.
So the docs now start from the parts. Before you build anything, find the part it belongs to and check what is already there.