This page explains how Glove works under the hood. If you haven't built your first app yet, start with Getting Started — it takes 15 minutes and you'll come back here with useful context.
Every Glove app is driven by a loop. Here is what happens each time a user sends a message:
The key insight: the AI decides which tools to call and in what order. You don't write if/else logic or state machines. You define capabilities, and the AI uses them to fulfill user requests.
This loop is implemented by the Agent class. On the client side, the useGlove hook manages this loop automatically.
A tool is a capability your app exposes to the AI. Each tool has four parts:
"get_weather")Tools can be pure functions (compute something, fetch data, call an API) or interactive — they can push UI to the display stack to show the user results or ask for input.
See ToolConfig for the full type definition.
For tools with display UI, use defineTool from glove-react — it provides typed display props, typed resolve values, and colocated renderResult for history rendering. See the Display Stack guide for examples.
The display stack is what makes Glove an app framework, not just a chatbot. When a tool runs, it can push a React component onto a stack that your app renders. This is how tools show UI to the user — product grids, forms, confirmation dialogs, data cards, anything.
The do function receives a display parameter with two methods:
pushAndForget — push a component and keep the tool running. Use this for showing results: data cards, product grids, status updates. The tool returns normally.pushAndWait — push a component and pause the tool until the user responds. Use this for collecting input: forms, confirmations, preference pickers. The tool resumes when the user submits.Think of it like this: pushAndForget is like printing a receipt — here is your result. pushAndWait is like handing someone a clipboard — fill this out and give it back.
Tools can also control when their display slots are visible using display strategies: "stay" (always visible), "hide-on-complete" (hidden after the user responds), and "hide-on-new" (hidden when a newer slot from the same tool appears). The <Render> component from glove-react handles this visibility logic automatically.
On the React side, the useGlove hook exposes slots (the current stack) and renderSlot() (renders a slot using the tool's render function). See ToolDisplay for the full API.
When you define a tool in glove-react, you can include a render function alongside the do function. This means the tool's logic and its UI live together in the same object — no separate component files, no string-based lookups.
When you call display.pushAndWait({ input }) from a tool that has a render function, Glove automatically uses the tool's name to match the slot to the renderer. The useGlove hook builds the renderer map and provides renderSlot() to your component.
For type-safe colocated renderers, use defineTool instead of raw ToolConfig. It adds typed props and resolve in the render function, plus a renderResult function for showing read-only views from history. See the React API reference for details.
Glove uses four pluggable interfaces (called adapters) to stay flexible. Each adapter can be swapped without changing your application code:
MemoryStore ships in glove-core for prototyping; bring your own implementation for Postgres, Redis, remote APIs, or anything else.SubscriberEvent union covering model events (text_delta, tool_use, model_response_complete), executor events (tool_use_result), observer events (compaction_start, compaction_end, token_consumption), and extension events (hook_invoked, skill_invoked, subagent_invoked, subagent_completed). Use it for logging, analytics, or real-time streaming.For example, switching from OpenAI to Anthropic only requires changing the ModelAdapter — your tools, UI, and application logic stay the same.
StoreAdapter exposes one optional method specifically for subagent isolation: createSubAgentStore(namespace, durable?). A subagent factory typically calls parentStore.createSubAgentStore(name, durable) to derive a child store before building the child Glove.
durable: false (the default) returns a fresh store on every invocation — the subagent has no memory across calls.durable: true returns the same store for the same namespace, so the subagent accumulates message history across invocations.MemoryStore implements this out of the box; custom store implementations can opt in by supplying their own factory.
AI models have a limited context window — the maximum amount of conversation they can read at once. Long conversations eventually hit this limit.
Glove handles this automatically: when the conversation gets too long, it summarizes everything so far and injects the summary as a new message. This is called compaction. The store preserves the full message history — compaction never deletes messages. Instead, it calls resetCounters() on the store to reset token and turn counts, and appends a compaction summary message marked with is_compaction: true.
When the agent loop calls Context.getMessages(), the result is split at the last compaction boundary — the model only sees messages from the most recent compaction onward. This keeps the context window small and focused while the underlying store retains every message ever exchanged.
Because full history is preserved, the frontend can read directly from the store to display the complete conversation — including messages from before compaction — even though the model never sees them. Task state is also preserved across compaction boundaries, so sessions can run indefinitely without losing track of what they were doing.
You can configure compaction behavior with CompactionConfig.
The display stack handles synchronous interactions — the user clicks a button, the tool gets the result immediately. But some things can't be resolved in the moment: a product is out of stock, a payment is processing, an approval is needed from someone else.
The inbox is a persistent async mailbox. An agent posts a request it can't fulfill now, and an external service resolves it later. The next time the agent runs, resolved items are automatically injected into the conversation. This works across sessions, server restarts, and different instances of the same agent.
When your store implements the four optional inbox methods (getInboxItems, addInboxItem, updateInboxItem, getResolvedInboxItems), Glove auto-registers the glove_post_to_inbox tool — just like it auto-registers glove_update_tasks when task methods exist. The agent can call it whenever it decides something needs async tracking.
Both the request and response are plain text — the agent writes in natural language, and the external service responds in natural language. Items can be blocking (the agent is told to wait) or non-blocking (the agent continues, result arrives later). Pending inbox items survive context compaction — they're preserved in the summary so the agent never forgets what it's waiting for.
On the React side, useGlove() returns inbox: InboxItem[] alongside tasks, so your UI can show what the agent is tracking. External services resolve items by calling your store's updateInboxItem (from a webhook handler, cron job, or admin script) — any process with access to the same backing store can resolve an item.
See the full Inbox guide for setup, external resolution patterns, and the coffee shop example.
Glove ships three extension primitives for shaping a turn before — or instead of — calling the model:
/name directives. The handler runs before the model with full AgentControls access — it can rewrite the user text, force compaction, swap the model mid-conversation, or short-circuit the turn entirely./name directives or are pulled in by the agent via the auto-registered glove_invoke_skill tool when exposeToAgent: true. They return text or ContentPart[] that lands as a synthetic user message marked is_skill_injection: true.glove_invoke_subagent tool. Each invocation calls a factory that builds and returns a fresh IGloveRunnable; the dispatcher runs processRequest(prompt) on it and returns the final agent text as the tool result.When a user-side directive binds, the original /name token is replaced with a non-triggerable placeholder of the form [invoked_extension__hook_<name>] or [invoked_extension__skill_<name>]. The placeholder survives in the persisted user message so transcripts can still show what the user typed without the directive re-firing on a future parse. Unbound /name tokens (filesystem paths, etc.) are left untouched.
Subagent runs are bracketed by subagent_invoked / subagent_completed subscriber events with guaranteed 1:1 symmetry — the executor fires both events around every glove_invoke_subagent call, even on abort. While the child is running, the parent's subscribers fan out to it, so streaming UIs see the child's text_delta and tool_use events as part of the same stream.
See the full Extensions guide for types, dispatch order, and worked examples.