Control what it sees
Every model call is built from three things: the system prompt, the saved history, and whatever is true right now. Glove keeps the first two stable and delivers the third fresh before every call, so the agent always sees current state and the provider can still cache everything before it.
The shape of every call
system prompt stable: who the agent is, its rules
saved history stable: the conversation (after the last compaction)
─────────────────────────────────────────────────── cache boundary
runtime context fresh: current state from your providers
(never saved, never rewrites the prompt)The tempting mistake is to rebuild the system prompt on every turn with the current state pasted in. It works until it doesn't: the prompt cache misses on every call, old states linger in history when you echo them into messages, and between two tool calls in the same request the state the model sees is already stale. Runtime context fixes all three.
Live state: context providers
// Called before every model iteration, including between tool steps.
const stop = agent.addContextProvider(async () => {
const release = await releases.current(); // your data
if (!release) return null; // nothing to add this time
return [
`Release in progress: ${release.version} (${release.status}).`,
`Merged since last tag: ${release.changes.length} pull requests.`,
`Blocking: ${release.blockers.join(", ") || "nothing"}.`,
].join("\n");
});
// Later, when the release ships:
stop();A provider returns a string, content parts (an image of a dashboard, say) or null. The text arrives as a transient user-role message at the tail. Subscribers get a runtime_context event with what was sent (the text exactly, media as a placeholder), so you can trace it. glove-memory's pinned context, forms and goals all use this same seam; so does mountBrowser to show the model its latest screenshot.
Guidance on demand: skills
Long procedures don't belong in the system prompt. Give the agent a listing and let it read what it needs:
import { useReadSkill } from "glove-core";
useReadSkill(agent, [
{
name: "release-checklist",
description: "The team's release procedure. Read before tagging anything.",
content: "# Release checklist\n1. All CI green on main\n2. Changelog drafted ...",
sections: {
hotfix: "# Hotfix releases\nBranch from the last tag, cherry-pick, ...",
},
},
]);The agent gets one glove_read_skill tool whose description lists the skills; reading one returns its content, or a named section. Users can also type /release-checklist to load it into the next turn. Keep skills as files with loadContentSkillsFromFs from glove-core/content-skills-fs, the same shape as a coding agent's SKILL.md folders.
For commands that change the agent rather than add text, use hooks. glove.defineHook("compact", …) runs when a user types /compact, with full control: force compaction, swap the model, or answer without calling the model at all. See hooks, skills and subagents.
A plan the agent keeps: the task list
If the store implements tasks (MemoryStore does), every agent gets glove_update_tasks. The model writes its plan as tasks with a status and updates them as it works; your UI reads store.getTasks() to show progress. There is nothing to wire up.
Long sessions: compaction and summaries
- Compaction summarises the conversation when it nears
compaction_context_limit, using yourcompaction_instructions. The model continues from the summary; the store still has every message, so your UI shows the whole thread. Pending inbox items are carried into the summary, and tasks live in the store, so neither is lost. - Tool result summaries: turn on
enableToolResultSummary, give the toolgenerateToolSummary, and returngenerateSummaryArgsfrom itsdo(). A large result is then replaced by its summary in later requests. - Keep big data out entirely. A 5,000-row result belongs in a file or a REPL variable, not in context. That is step 5.
Already built for you
| Primitive | What you get | Instead of building |
|---|---|---|
| Runtime context providersglove-core | Live state appended after history before every model call, including between tool steps. Never saved to history, never rewrites the system prompt.glove.addContextProvider(() => renderCurrentState()) | rebuilding the system prompt every turn (and losing the prompt cache) |
| Compactionglove-core | Long sessions summarised automatically; the store keeps the full history for your UI.compaction_config: { compaction_instructions, compaction_context_limit } | a summariser and history-trimming logic |
| Content skillsglove-core | A listing of guides the agent can read in full, by section, when it needs them.useReadSkill(glove, skills) · loadContentSkillsFromFs(dir) | pasting every playbook into the system prompt |
| Hooks and slash skillsglove-core | /commands that change agent state or inject context before the user's message.glove.defineHook("compact", handler) · glove.defineSkill({ name, handler }) | parsing slash commands in your request handler |
| Task listglove-core | A built-in plan the agent keeps up to date and your UI can show.store with getTasks/addTasks → glove_update_tasks | a todo or planning tool |
| Pinned contextglove-memory | Standing facts every turn should see, editable by the agent, delivered as runtime context.useContext(glove, contextAdapter) | a user-preferences block in the system prompt |
Next
The agent sees the right things now, but forgets the team the moment the process restarts. In step 4 it gets a durable store, an inbox for answers that arrive later, and memory.