Glove is a monorepo of small, independently versioned packages. Nothing here is required except the runtime — you install the pieces the app actually needs, and each one has a single job. This page is the tour: what each package solves, how to install it, and the smallest snippet that puts it to work.
Everything below assumes you already have an agent — either a Glove builder or a built runnable. If not, start with the Quickstart. Deployment (glovebox-*) has its own guide and is only summarised here.
| Family | Packages | Use it when |
|---|---|---|
| Runtime | glove-core, glove-react, glove-next | Always — this is the framework. |
| Voice | glove-voice, -native, -s2s, -avatar, -livekit | The interface is speech, not typing. |
| Memory & data | glove-memory, glove-scratchpad, glove-sql | The agent must remember, or must reason over more data than fits in context. |
| Sandboxes | glove-working-environment, glove-env-* | The agent produces artifacts — documents, spreadsheets, decks, media. |
| Code execution | glove-js, glove-python, glove-lisp, glove-egress | Dozens of tools would blow the context window, or data must not leak. |
| Generative media | glove-image | The agent generates and refines images — recurring characters, scenes, references. |
| Coordination | glove-mesh, glove-continuum-signal | More than one agent, or agents that run on a schedule. |
| Integration | glove-mcp | You want tools you did not write. |
| Deployment | glovebox-core, -kit, -client | Ship the agent as an isolated, addressable service. |
The agent loop, tool execution, model adapters, the display manager, stores, subscribers, hooks, skills and subagents. Everything else in this list plugs into it. Works in Node and in the browser.
pnpm add glove-core zodimport { Glove, MemoryStore, Displaymanager, createAdapter } from "glove-core";
import { z } from "zod";
export const agent = new Glove({
store: new MemoryStore("session-1"),
model: createAdapter({ provider: "anthropic", model: "claude-sonnet-4-20250514" }),
displayManager: new Displaymanager(),
systemPrompt: "You are a helpful assistant.",
compaction_config: { compaction_instructions: "Summarize the conversation so far." },
})
.fold({
name: "get_weather",
description: "Get current weather for a city",
inputSchema: z.object({ city: z.string() }),
async do(input) {
return { status: "success", data: await weather.lookup(input.city) };
},
})
.build();
await agent.processRequest("What's the weather in Tokyo?");→ Core API reference · Server-side agents
Hooks and components for the client: GloveClient, GloveProvider, useGlove, <Render>, defineTool for typed tools and display props, plus createRemoteStore. Bundles glove-core.
pnpm add glove-react zod"use client";
import { useGlove, Render } from "glove-react";
export function Chat() {
const glove = useGlove();
return <Render glove={glove} renderMessage={({ entry }) => <p>{entry.text}</p>} />;
}→ React reference · Display stack
One function that turns a route into a streaming (SSE) chat endpoint, holding your provider key server-side. Supports every provider the core adapter factory does, plus prompt caching and reasoning options.
pnpm add glove-nextimport { createChatHandler } from "glove-next";
export const POST = createChatHandler({
provider: "anthropic",
model: "claude-sonnet-4-20250514",
cache: true,
});The classic pipeline — VAD → STT → agent → TTS — with barge-in, push-to-talk, narration control and speech-gated noise robustness (mic audio only reaches the STT provider once the VAD confirms speech). ElevenLabs adapters ship in the box; the contracts are open.
pnpm add glove-voiceimport { createElevenLabsAdapters } from "glove-voice";
import { useGloveVoice } from "glove-react/voice";
const { stt, createTTS } = createElevenLabsAdapters({
// Tokens are minted server-side — the API key never reaches the browser.
getSTTToken: () => fetch("/api/voice/stt-token").then(r => r.json()).then(d => d.token),
getTTSToken: () => fetch("/api/voice/tts-token").then(r => r.json()).then(d => d.token),
voiceId: "JBFqnCBsd6RMkjVDRZzb",
});
const { runnable } = useGlove({ tools, sessionId });
const voice = useGloveVoice({ runnable, voice: { stt, createTTS } });
// voice.mode: "idle" | "listening" | "thinking" | "speaking"The pipeline is platform-neutral; only mic capture and playback are platform edges. This package supplies them for iOS and Android, backed by react-native-audio-api and onnxruntime-react-native (Silero VAD on-device).
pnpm add glove-voice-nativeimport { withNativeAudio } from "glove-voice-native";
import { SileroVADNativeAdapter } from "glove-voice-native/silero-vad";
const vad = new SileroVADNativeAdapter();
await vad.init();
const voice = useGloveVoice({
runnable,
voice: withNativeAudio({ stt, createTTS, vad }),
});Run a built Glove agent directly on a realtime speech-to-speech model (OpenAI Realtime, Gemini Live). The cascade's ~1.3–1.6s voice-to-voice collapses to ~500–800ms, turn-taking is decided by the model listening, and your tools run unchanged through the same Tool.run.
pnpm add glove-voice-s2simport { RealtimeAgent, createS2SAdapter } from "glove-voice-s2s";
// Provider/model/credentials from args, falling back to S2S_* env vars.
const adapter = createS2SAdapter({ provider: "openai" });
const rt = new RealtimeAgent({ agent, adapter });
await rt.start();
// Push a result in from elsewhere and have the agent speak about it.
rt.inject("The order shipped.", { respond: true });An avatar provider is a lip-sync renderer over an audio stream — the same shape as the PCM a transport-mode S2S adapter already emits. attachAvatar is the one-call bridge; Tavus (echo mode) and Anam (audio passthrough) adapters ship, both passing the conformance suite.
pnpm add glove-voice-avatarimport { TavusEchoAdapter, attachAvatar } from "glove-voice-avatar";
const avatar = new TavusEchoAdapter({
apiKey: process.env.TAVUS_API_KEY!, // server-side only
faceId: process.env.TAVUS_FACE_ID!,
// palId omitted → a minimal echo PAL is ensured, so the ONLY voice is the agent's.
sendInteraction: (event) => duct.send({ t: "avatar_interaction", event }),
});
const detach = await attachAvatar(rt, avatar);
avatar.view; // { kind: "webrtc-room", url: … } — hand this to the client→ Avatars
LiveKit as an adapter rather than a rewrite. LiveKitTransport is the room leg — join, publish the agent's voice as a paced track, feed remote mics back as PCM, carry JSON on the data channel, with server-authoritative barge-in. The LiveKit Tavus/Anam avatars join your room as a second participant.
pnpm add glove-voice-livekitimport { LiveKitTransport, attachRealtime, mintParticipantToken } from "glove-voice-livekit";
const transport = new LiveKitTransport({
url: process.env.LIVEKIT_URL!,
token: await mintParticipantToken(
{ apiKey: process.env.LIVEKIT_API_KEY!, apiSecret: process.env.LIVEKIT_API_SECRET! },
{ roomName: "call-42", identity: "agent" },
),
});
await transport.connect();
attachRealtime(rt, transport); // mics → model, model → track, interrupt → flush
await rt.start();→ LiveKit
Five orthogonal subsystems, each an independent bring-your-own-storage adapter with its own tool surface: an entity graph (typed nodes with deterministic identity keys), episodic memory (an append-only, time-indexed, semantically searchable timeline), resources (a POSIX-style virtual filesystem the agent walks with ls/read/grep), context (the user's standing brief, injected into the system prompt every turn), and forms (structured collection over a conversation — Zod-authored definitions, lazily loaded, with colocated executors).
pnpm add glove-memoryimport {
useMemoryReader, useEpisodicReader, useContext,
InMemoryEntityAdapter, InMemoryEpisodicAdapter, InMemoryContextAdapter,
} from "glove-memory";
// Reference in-memory adapters ship for dev/test; implement the contracts
// (EntityMemoryAdapter, EpisodicMemoryAdapter, …) against your own storage.
const entities = new InMemoryEntityAdapter({ schema: ontology });
useMemoryReader(agent, entities); // read-only entity tools
useEpisodicReader(agent, new InMemoryEpisodicAdapter());
useContext(agent, new InMemoryContextAdapter()); // injected every turn
// Writes belong to a separate curator instance:
// useMemoryCurator(curator, entities);import { FormRegistry } from "glove-memory/forms";
import { useFormRunner, InMemoryFormAdapter } from "glove-memory";
const registry = new FormRegistry().register("travel-claim", {
name: "Travel reimbursement claim",
description: "Claimant, trip, travel and approval details.",
load: () => import("./forms/travel-claim").then((m) => m.travelClaim),
});
// Folds seven glove_form_* tools and injects the one-line tier-0 status
// into the system prompt each turn.
const { runner } = useFormRunner(agent, new InMemoryFormAdapter({ schema }), {
registry,
subject: conversationId,
});The recommended shape is not to hang every memory tool off the main agent: build one subagent per retrieval task with defineSubAgent so each attaches only the adapter slice it needs and token cost scales with role, not with ontology size. Writes belong to a separate curator instance running over conversation history.
→ Memory reference · Why Memory
A database emulator for LLM tool use. Resources become tables and the agent drives everything through one execute_sql tool: information_schema is discovery, WHERE clauses push arguments down to your handlers, transactions stage outbound effects as a real dry-run, and every statement is parsed before any tool runs. Benchmarked at up to 35× less context than equivalent tool definitions.
pnpm add glove-scratchpadimport { Database, defineResource, mountDatabase } from "glove-scratchpad";
import { z } from "zod";
const db = await Database.create({ policy: { writes: true } });
db.register(defineResource({
name: "github_pull_requests",
volatility: "volatile",
schema: z.object({
number: z.number().int(),
title: z.string(),
state: z.string().describe("open | merged | closed"),
}),
keys: ["number"],
// WHERE state = 'open' arrives as a binding — an argument, not a post-filter.
select: (b) => github.listPRs({ state: b.one("state") }),
}));
mountDatabase(agent, { db }); // folds execute_sql + explain_sql, primes the promptA zero-dependency, pure-JS Postgres-subset engine: runtime-built tables, joins, CTEs, set operations, subqueries and window functions, serialisable to bytes. It is the default backend for the scratchpad, and usable on its own wherever you want SQL without a database.
pnpm add glove-sqlimport { MemoryBackend } from "glove-sql";
const be = await MemoryBackend.create();
await be.exec(`CREATE TABLE orders (id int, total numeric, region text)`);
await be.exec(`INSERT INTO orders VALUES (1, 42.00, 'emea')`);
const { rows } = await be.query(
`SELECT region, sum(total) AS revenue FROM orders GROUP BY region`,
);
const bytes = await be.dump(); // serialise the whole databaseA small, fast, in-memory sandboxed working environment: a virtual filesystem where state accumulates across tool calls. The agent writes scripts, runs them, inspects intermediates and iterates — with no networking, no host filesystem and no process spawning, because scripts only see the capabilities you inject. Zero-dependency core.
pnpm add glove-working-environmentimport { createWorkingEnvironment, mountWorkingEnvironment } from "glove-working-environment";
import { spreadsheets } from "glove-env-spreadsheets";
const env = await createWorkingEnvironment({
stdlib: [spreadsheets()],
limits: { runTimeoutMs: 30_000 },
});
await env.mount("./q3.xlsx", "/inbox/q3.xlsx"); // host-side door
mountWorkingEnvironment(agent, { env }); // model-facing verbs + preamble
const deliverables = await env.export("/out/**"); // → [{ path, bytes }]Back the tree with memory, a real directory (hostDirectory, copy-on-write), a snapshot, or object storage (cachedRemote). Expose your own libraries to the model with defineAdapter (I/O), defineBuilder (stateful builder APIs), or definePureModule (pure, synchronous computation) — and your own capabilities, an MCP server or a plain async function, with defineTools.
An adapter bridges a real host library into the tree; the model experiences it as a typed importable module plus docs at /std/<name>/. Install only the formats your agent handles.
| Package | Module | Gives the model |
|---|---|---|
glove-env-documents | env:documents | One document spec → PDF and DOCX; describe / merge / split / stamp; text extraction |
glove-env-spreadsheets | env:spreadsheets | .xlsx as plain-JSON records; write, append, CSV bridging, plus exceljs's own Workbook |
glove-env-images | env:images | Describe without decoding; resize / convert / crop / rotate / composite / contact sheets |
glove-env-slides | env:slides | PowerPoint decks from a spec, read back independently — outline, slide text, notes |
glove-env-zip | env:archives | zip / tar / tar.gz in and out, traversal- and bomb-safe. No dependencies |
glove-env-media | env:media | Video and audio via ffmpeg: describe, thumbnail, frames, clip, concat, transcode |
glove-env-render | env:render | Rasterize a PDF, deck or Word file to page PNGs — so the agent can look at what it made |
glove-env-motion | env:motion | A React scene → mp4, GIF, PNG frames or a still, rendered deterministically. Reanimated scenes work unchanged |
import { documents } from "glove-env-documents";
import { images } from "glove-env-images";
const env = await createWorkingEnvironment({ stdlib: [documents(), images()] });Three surfaces over the same ToolFn catalog — one set of functions mounts on any of them unchanged. Pick the language your models are most fluent in.
A sandboxed JavaScript interpreter (acorn parse → whitelist validation → fuel-budgeted evaluation). The model discovers capabilities in-band, keeps big intermediates in the REPL instead of its context, and can branch — decide-and-act — inside a single call.
pnpm add glove-js glove-scratchpadimport { JsSession, mountJs } from "glove-js";
import { fnsFromMcp } from "glove-scratchpad/fns/mcp";
const session = JsSession.create();
session.registerAll(await fnsFromMcp(githubConn)); // github__list_pull_requests, …
mountJs(agent, { session });The same surface in Python — the language most models reach for when the task is “manipulate this data”. Keyword arguments, list comprehensions, f-strings; the same discovery tiers and the same off-context data flow.
pnpm add glove-python glove-scratchpadimport { PySession, mountPy } from "glove-python";
const session = PySession.create();
session.registerAll(await fnsFromMcp(githubConn));
mountPy(agent, { session });A tiny Clojure-flavored Lisp over the same catalog, plus the scratchpad's ResourceTable contract — so it can also stage several outbound effects, preview them, and commit! or rollback! as a real dry run.
pnpm add glove-lisp glove-scratchpad;; What the model writes inside execute_lisp
(def prs (github_pull_requests {:state "open"})) ; 320 rows stay in the REPL
(if (empty? (pagerduty_incidents {:urgency "high"}))
(insert! :slack_messages {:channel "ops" :text "All clear."})
(insert! :emails {:to_addr "oncall@acme.io" :subject "Incidents live"}))The one-eval-tool boundary already makes an agent context-efficient. This package makes it a privacy boundary and gives you the instruments to measure it: quantitative-information-flow metering, an enforced egress gate where programs must end in a bounded decision, and red-team extraction simulation.
pnpm add glove-egressimport { egressFns, guardEffectFns, DEFAULT_EGRESS_POLICY, BoundaryMeter } from "glove-egress";
session.registerAll(egressFns(DEFAULT_EGRESS_POLICY)); // assert/count/choose/bucket/report
const guarded = guardEffectFns(catalog, DEFAULT_EGRESS_POLICY, onBlock);
const meter = new BoundaryMeter();
meter.cross("assertion", true, { decisionSpace: 2 });
meter.report(canaries); // what actually crossedImage generation as a workflow rather than a single call: a prompt pipeline of enhancer inbetweens, durable characters and scenes spliced verbatim into every prompt, reference images with roles, editing, deterministic assembly, an optional vision model to review its own output, and per-call cost tracking. The image model is an adapter you bring.
pnpm add glove-imageimport {
mountImage,
InMemoryImageAssetStore,
InMemoryImageLibrary,
expandCharacters,
expandScenes,
styleDirective,
openrouterImages,
} from "glove-image";
await mountImage(glove, {
adapter: openrouterImages(), // OPENROUTER_API_KEY
assets: new InMemoryImageAssetStore(),
library: new InMemoryImageLibrary(),
pipeline: [expandCharacters(), expandScenes(), styleDirective("gouache, muted palette")],
});
// The agent then works in asset ids:
// glove_image_character_save({ name: "mira", appearance: "..." })
// glove_image_generate({ intent: "Mira at the harbor", characters: ["mira"] })
// glove_image_regenerate({ asset: "img_...", tweak: "at dusk" })Direct, broadcast and acknowledged messaging between agents, riding the same inbox primitive the core already has, over a transport you bring (in-process, Redis, a queue, HTTP).
pnpm add glove-meshimport { mountMesh, MeshNetwork, InMemoryMeshAdapter } from "glove-mesh";
const network = new MeshNetwork();
await mountMesh(planner, {
adapter: new InMemoryMeshAdapter(network),
identity: {
id: "planner",
name: "Planner",
description: "Breaks work down and hands it to specialists.",
capabilities: ["planning"],
},
});
// Folds glove_mesh_send_message / _broadcast / _list_agents / _acknowledge.
// Inbound messages land in this agent's inbox and surface on its next turn —
// so the store must support the inbox methods.→ Mesh guide · The inbox
Discovery, supervision, observability and IPC for agents running as subprocesses. Triggered agents are cold and wake per event (a call, a schedule fire, an inbound mesh message), resume their persistent store, run a turn and exit. Concurrent agents stay warm and are notified inline.
pnpm add glove-continuum-signalimport { agent, z, ContinuumRunner, MemoryAdapter } from "glove-continuum-signal";
export const pizzaBaker = agent("pizza-baker")
.input(z.object({ orderId: z.string() }))
.triggered()
.timeout(60_000)
.retries(2)
.every("5m").withInput({ orderId: "tick" })
.factory(async (ctx) => buildGlove(ctx));
const runner = new ContinuumRunner({ adapter: new MemoryAdapter() });
runner.registerAgent(pizzaBaker, import.meta.url);
await runner.start();Bridge any MCP server's tools into an agent as first-class tools. A discovermcp subagent lets the model find and activate servers from a catalogue mid-conversation. The framework's only auth seam is getAccessToken(id) — for the spec OAuth flow, glove-mcp/oauth ships an opt-in runner and reference stores.
pnpm add glove-mcpimport { mountMcp } from "glove-mcp";
await mountMcp(runnable, {
adapter: myAdapter, // getActive / activate / deactivate / getAccessToken
entries: [{
id: "notion",
name: "Notion",
description: "Search, read, and edit pages in a Notion workspace.",
url: "https://mcp.notion.com/mcp",
tags: ["docs", "notes", "wiki"],
}],
clientInfo: { name: "my-app", version: "1.0.0" },
});glovebox-core (authoring kit + glovebox build CLI), glovebox-kit (the in-container runtime) and glovebox-client (the client SDK) package an agent as an isolated, network-addressable service: one authenticated WebSocket endpoint per session, files crossing the wire as FileRef rather than raw bytes, and a storage policy DSL that routes payloads by size.
It is a bigger surface than the rest of this page and has a guide of its own → Glovebox, with a worked example in the showcase.
The SQLite StoreAdapter. Still published and still works, but it receives no new features — implement StoreAdapter against whatever storage your app already runs instead. The contract is small and documented in the Core API reference.