Core Concepts

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.

The Agent Loop

Every Glove app is driven by a loop. Here is what happens each time a user sends a message:

  1. The user's message is added to the conversation history
  2. The AI reads the full conversation and the list of available tools
  3. The AI either responds with text (ending the loop) or requests one or more tool calls
  4. Glove executes the requested tools and adds the results to the conversation
  5. Go back to step 2

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.

Tools

A tool is a capability your app exposes to the AI. Each tool has four parts:

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

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:

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.

Colocated Renderers

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.

Adapters

Glove uses four pluggable interfaces (called adapters) to stay flexible. Each adapter can be swapped without changing your application code:

For example, switching from OpenAI to Anthropic only requires changing the ModelAdapter — your tools, UI, and application logic stay the same.

Context Compaction

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 Inbox

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 via SqliteStore.resolveInboxItem() (from glove-sqlite) — a static method that can be called from any process with database access.

See the full Inbox guide for setup, external resolution patterns, and the coffee shop example.