Build it up · Step 4 of 8

Make it remember

State is everything that outlives one model call. A harness needs four kinds: the conversation itself, work waiting on something outside, what it has learned, and what it is trying to finish. Glove has a primitive for each, and they all sit behind adapters you can point at your own storage.

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.

The conversation: a store

Every Glove has a StoreAdapter. It holds the messages, token and turn counts, and optionally tasks, permission decisions, inbox items and child stores for subagents. MemoryStore implements all of it in memory. For a store that survives a restart, implement the interface over your database, or extend MemoryStore and write through to disk:

the StoreAdapter contract (abridged)typescript
interface StoreAdapter {
  identifier: string
  getMessages(): Promise<Message[]>
  appendMessages(msgs: Message[]): Promise<void>
  getTokenCount(): Promise<number>
  addTokens(tokens: TokenConsumptionCounter): Promise<void>
  getTurnCount(): Promise<number>
  incrementTurn(): Promise<void>
  resetCounters(): Promise<void>

  // Optional. Implement them and the matching feature switches on.
  getTasks?(); addTasks?(); updateTask?()                       // → glove_update_tasks
  getPermission?(); setPermission?()                            // → requiresPermission
  getInboxItems?(); addInboxItem?(); updateInboxItem?(); getResolvedInboxItems?()  // → inbox
  createSubAgentStore?(namespace, durable?)                     // → subagents with their own history
}

glove foundry init generates lib/file-store.ts, a complete file-backed store built exactly this way; copy it for a single-host app. Several hosts need a database-backed store. In step 7, Foundry asks you for one store per conversation and calls it for you.

Work that finishes later: the inbox

"Tag the release once CI is green" can't finish now. With a store that supports the inbox, the agent gets glove_post_to_inbox: it posts the request (blocking or not) and moves on. Anything (a webhook handler, a job, another agent) resolves it later, and the result is injected at the start of the next request:

ci-webhook.tstypescript
// The agent earlier called glove_post_to_inbox({ tag: "ci", request: "Tell me when main is green", blocking: true })
const pending = (await store.getInboxItems!()).filter((i) => i.tag === "ci" && i.status === "pending");
for (const item of pending) {
  await store.updateInboxItem!(item.id, {
    status: "resolved",
    response: "main is green at 9f3c2a1",
    resolved_at: new Date().toISOString(),
  });
}
// Then run a turn so the agent sees it, e.g. agent.processRequest("CI finished.")

While a blocking item is pending, every model call is reminded that it may not act on the missing result. That is the whole protocol; there is no polling to write. Mesh delivers messages between agents through this same inbox, and Kuiper's email tools use it to wait for replies.

What it learns: glove-memory

glove-memory splits memory into what each kind is for: an entity graph (people, projects, how they relate), an episodic timeline (what happened when), a resource filesystem (documents the agent files), and pinned context (standing facts every turn should see). Each is an adapter with reader and curator tool sets, and every write carries provenance. The release assistant pins the team's conventions:

release.tstypescript
import { MemorySchema } from "glove-memory";
import { createSqliteMemoryAdapters } from "glove-memory/sqlite";
import { useContext } from "glove-memory/tools";

const memory = createSqliteMemoryAdapters({
  file: "./.data/memory.db",          // a durable file, Node 22.13+
  namespace: "team:acme/release",     // trusted ownership key, never model input
  schema: new MemorySchema(),
});

// glove_context_set / _get / _update / _unset, and every pinned entry
// delivered as runtime context before each model call (step 3's seam).
useContext(agent, memory.context);

Now "we never ship on Fridays" becomes a pinned entry the agent sees on every call, including next week. For recall over people and history, don't hang every memory tool on the main agent: give a subagent the reader tools and let the main agent ask it. Memory explains why.

What it is trying to finish: facts, forms and goals

Pick the smallest primitive that fits what you need to collect:

You need…Use
Every argument now, from the modelA plain tool
Standing information on every turnPinned context: useContext
Details people mention early, reused laterFacts: useFacts → record_fact
A specific set of validated answers, maybe in steps, then an actionForms: defineForm + useFormRunner
Exact input or a final confirm that must not pass through the modelA display-stack form: pushAndWait
Obligations across the whole conversationGoals: useGoalRunner

Forms and goals show their live state through runtime context, so the agent never loses track of what is left, however much the person wanders. Goal-oriented agents builds one end to end.

Already built for you

PrimitiveWhat you getInstead of building
Storeglove-coreMessages, token and turn counts, and optional tasks, permissions, inbox and subagent stores behind one interface.StoreAdapter (MemoryStore built in; bring your own)a conversations table and token accounting
Inboxglove-coreWork that can't finish now is posted, resolved later by anything, and injected on the next turn. Blocking items keep the agent from acting on missing results.glove_post_to_inbox (registered when the store supports it)polling, callbacks and 'check back later' state
Memoryglove-memoryAn entity graph, an episodic timeline and a resource filesystem with provenance, behind adapters you own (SQLite included).useMemoryReader · useEpisodicReader · useResourcesReader (+ curators)a vector store and an ad hoc remember tool
Formsglove-memoryValidated answers collected over a conversation: steps, branches, skips, undo and executors that act on completion.defineForm(…) · useFormRunner(glove, adapter, { registry, subject })a slot-filling state machine
Goalsglove-memoryObjectives that survive interruptions, with versioned progress and deferrals.defineGoalProgram(…) · useGoalRunner(glove, adapter, config)tracking what the agent is supposed to finish inside the prompt
Factsglove-factsThings people mention early, recorded once and reused by forms and goals.useFacts(glove, factStore, source)asking the user for something they already said

Next

The assistant remembers the team. To draft real release notes it needs to read diffs, run scripts and produce files. In step 5 it gets a workspace.

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