npm.io
0.1.0 • Published yesterday

glove-lisp

Licence
MIT
Version
0.1.0
Deps
3
Size
119 kB
Vulns
0
Weekly
0
Stars
7

Glove

Build entire apps as conversations.

An open-source TypeScript framework for AI-powered apps.
You define tools — things your app can do. An AI agent decides when to use them.

Docs · Website · Examples


What is Glove?

Traditional apps encode user flows in UI — pages, routes, navigation hierarchies. Glove replaces that wiring with an agent. You define capabilities as tools. The agent orchestrates when to call them based on what users ask for.

User: "Find me running shoes under $100"
  → Agent calls search_products tool → pushes product grid to display
User: "Add the Nike ones to my cart and check out"
  → Agent calls add_to_cart → calls checkout → pushes payment form → waits for user

Works with OpenAI, Anthropic, Google Gemini, OpenRouter, and more. Bridge external tool servers (Notion, Linear, Gmail, ...) via MCP, or add real-time voice with glove-voice.

Packages

Package Description npm
glove-core Core agent framework — builder, tools, model adapters, stores npm
glove-react React hooks, <Render> component, defineTool, client bindings npm
glove-next Next.js API route handlers (SSE streaming) npm
glove-voice Voice pipeline — STT/TTS/VAD adapters, ElevenLabs integration npm
glove-mcp Model Context Protocol integration — bridge MCP servers' tools, on-demand discovery, opt-in OAuth runner npm
glove-memory Memory layer — entity / episodic / resources / context primitives, schema-first, BYO storage npm
glove-mesh Inter-agent mesh networking — direct/broadcast/ack messaging on top of the inbox primitive, BYO transport npm
glove-scratchpad A database emulator for LLM tool use — expose an agent's capabilities as a relational database it queries with one execute_sql tool. Resources become tables, WHERE pushes arguments down, information_schema is discovery, transactions stage outbound effects, and every statement is parsed before any tool runs; default backend is glove-sql npm
glove-sql Zero-dependency, pure-JS Postgres-subset SQL engine — runtime-built tables, joins/CTEs/set-ops/subqueries/window functions, serialises to bytes; the default backend for glove-scratchpad npm
glove-lisp A Lisp REPL for LLM tool use — the same resource catalog as glove-scratchpad, exposed as functions in a tiny sandboxed Clojure-flavored Lisp behind one execute_lisp tool. Branch (decide-and-act) in one call, def keeps intermediates out of context, effects are exactly-once by construction (exploration) npm
glove-continuum-signal Subprocess-based runtime for triggered (async) and concurrent (warm) agents — discovery, supervision, observability, IPC npm
glovebox-core Authoring kit + glovebox build CLI for shipping a Glove agent as a sandboxed container npm
glovebox-kit In-container runtime — WebSocket server, storage adapters, auto-injected skills/hooks npm
glovebox-client Client SDK for talking to a deployed Glovebox server npm

Quick Start

Server-side (Node.js / CLI)
npm install glove-core
import { Glove } from "glove-core/glove";
import { Displaymanager } from "glove-core/display-manager";
import { SqliteStore } from "glove-core";
import { createAdapter } from "glove-core/models/providers";
import { z } from "zod";

// 1. Pick a model
const model = createAdapter({
  provider: "anthropic",    // or "openai", "openrouter", "gemini", etc.
  model: "claude-sonnet-4-20250514",
  stream: true,
});

// 2. Create the agent
const app = new Glove({
  store: new SqliteStore({ dbPath: "./glove.db", sessionId: "my-session" }),
  // Or use a simple in-memory store — see glove-core docs
  model,
  displayManager: new Displaymanager(),
  systemPrompt: "You are a helpful assistant.",
  compaction_config: {
    compaction_instructions: "Summarize the conversation so far.",
  },
});

// 3. Register tools
app.fold({
  name: "get_weather",
  description: "Get current weather for a city",
  inputSchema: z.object({ city: z.string() }),
  async do(input) {
    const res = await fetch(`https://wttr.in/${input.city}?format=j1`);
    return await res.json();
  },
});

// 4. Run
const agent = app.build();
const response = await agent.processRequest("What's the weather in Tokyo?");
Full-stack (React + Next.js)
npm install glove-react glove-next

Serverapp/api/chat/route.ts:

import { createChatHandler } from "glove-next";

export const POST = createChatHandler({
  provider: "anthropic",
  model: "claude-sonnet-4-20250514",
});

Clientapp/providers.tsx:

"use client";
import { GloveProvider, GloveClient } from "glove-react";

const client = new GloveClient({
  endpoint: "/api/chat",
  systemPrompt: "You are a helpful assistant.",
  // Optional: fetch session ID from your backend instead of passing one directly
  // getSessionId: () => fetch("/api/session").then(r => r.json()).then(d => d.sessionId),
});

export function Providers({ children }: { children: React.ReactNode }) {
  return <GloveProvider client={client}>{children}</GloveProvider>;
}

Clientapp/chat.tsx:

"use client";
import { useGlove } from "glove-react";

export function Chat() {
  const { timeline, busy, sendMessage, sessionReady } = useGlove();

  if (!sessionReady) return <div>Loading session...</div>;

  return (
    <div>
      {timeline.map((entry, i) => (
        <div key={i}>
          <strong>{entry.kind === "user" ? "You" : "Agent"}:</strong>{" "}
          {entry.kind === "tool" ? `[${entry.name}]` : entry.text}
        </div>
      ))}
      <input
        onKeyDown={(e) => {
          if (e.key === "Enter") {
            sendMessage(e.currentTarget.value);
            e.currentTarget.value = "";
          }
        }}
        disabled={busy}
      />
    </div>
  );
}

Core Concepts

Tools

Tools are capabilities your app exposes to the agent. Each tool has a name, description, Zod schema, and an async handler:

app.fold({
  name: "search_products",
  description: "Search the product catalog",
  inputSchema: z.object({ query: z.string() }),
  async do(input, display) {
    const results = await catalog.search(input.query);

    // pushAndForget — show UI without blocking the tool
    await display.pushAndForget({
      renderer: "product_grid",
      input: results,
    });

    return results;
  },
});

Tools can also pause and wait for user input:

app.fold({
  name: "checkout",
  description: "Start checkout process",
  inputSchema: z.object({ cartId: z.string() }),
  async do(input, display) {
    const cart = await carts.get(input.cartId);

    // pushAndWait — tool execution pauses until user submits
    const payment = await display.pushAndWait({
      renderer: "payment_form",
      input: cart,
    });

    return await orders.create(cart, payment);
  },
});
Model Providers

Glove supports multiple providers through a unified adapter interface:

Provider Env Variable Default Model
openai OPENAI_API_KEY gpt-4.1
anthropic ANTHROPIC_API_KEY claude-sonnet-4-20250514
openrouter OPENROUTER_API_KEY anthropic/claude-sonnet-4
gemini GEMINI_API_KEY gemini-2.5-flash
minimax MINIMAX_API_KEY MiniMax-M2.5
kimi MOONSHOT_API_KEY kimi-k2.5
glm ZHIPUAI_API_KEY glm-4-plus
mimo MIMO_API_KEY (+ optional MIMO_BASE_URL) mimo-v2.5
ollama (none) (user-specified)
lmstudio (none) (user-specified)
bedrock AWS_ACCESS_KEY_ID anthropic.claude-3-5-sonnet-20241022-v2:0
import { createAdapter } from "glove-core/models/providers";

const model = createAdapter({
  provider: "openai",
  model: "gpt-4.1",
  stream: true,
});
Local Models

Ollama and LM Studio run locally with no API key. Pass your model name directly:

const model = createAdapter({
  provider: "ollama",
  model: "llama3",
  baseURL: "http://localhost:9999/v1", // optional, defaults to :11434
});

Or use the adapter classes directly:

import { AnthropicAdapter } from "glove-core/models/anthropic";
import { OpenAICompatAdapter } from "glove-core/models/openai-compat";
Reasoning Models

The OpenAI-compat adapter captures provider-emitted reasoning traces (reasoning_content / reasoning) from DeepSeek-R1 / V4, Qwen3-Thinking, GLM-4.5 / 4.6, Kimi K2, MiniMax M2.5, OpenRouter, and any other OpenAI-shape endpoint that follows the convention. Set reasoning: true for sensible defaults, or pass an object for fine-grained control:

// Capture reasoning into Message.reasoning_content; echo it back on tool turns
// (required by DeepSeek V4 and MiMo for multi-turn tool flows).
createAdapter({ provider: "openai", reasoning: true });

// Hint thinking depth — works with GPT-5 / o-series, GLM, MiniMax, Kimi, etc.
createAdapter({ provider: "openai", reasoning: { effort: "high" } });

// OpenRouter-style unified reasoning object.
createAdapter({
  provider: "openrouter",
  reasoning: { reasoningObject: { effort: "high", max_tokens: 2000 } },
});

// Provider-specific extras (e.g. Qwen3 dashscope's `enable_thinking`).
createAdapter({
  provider: "openai",
  baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
  reasoning: { extraBody: { enable_thinking: true, thinking_budget: 1024 } },
});

// Surface reasoning in the visible message text (wrapped in <think>…</think>).
createAdapter({ provider: "openai", reasoning: { includeInText: true } });

The MiMo provider has its own dedicated adapter — keep using { provider: "mimo", reasoningEffort, includeReasoningInText } for it.

Prompt Caching

Enable provider prompt caching with the cache option. Pass true for sensible defaults or an object to tune the lifetime:

// Anthropic: cache_control breakpoints on the tools + system prefix and the
// latest turn, so each follow-up request reuses the prior context.
createAdapter({ provider: "anthropic", cache: true });

// Tune the cache lifetime (Anthropic / OpenRouter honour the TTL).
createAdapter({ provider: "anthropic", cache: { ttl: "1h" } });

// Same switch on the Next.js handler.
createChatHandler({ provider: "anthropic", cache: true });

What the switch does per provider:

Provider Behaviour when cache is enabled
anthropic cache_control ephemeral breakpoints on the stable prefix (tools + system) and the latest turn; ttl ("5m" / "1h") honoured
bedrock cachePoint checkpoints after tools, after system, and on the latest turn (cache-capable models only); ttl maps onto Bedrock's CacheTTL
openrouter cache_control breakpoints forwarded to the upstream Anthropic / Gemini model
openai, gemini, minimax, kimi, glm, mimo, ollama, lmstudio Cache automatically — no request-side effect

Every adapter also reports provider cache usage on ModelPromptResult.cache_creation_input_tokens / cache_read_input_tokens (and on the model_response / model_response_complete subscriber events) regardless of the cache setting — inspect cache_read_input_tokens to confirm cache hits.

Cache usage for billing. The counts flow through the token-accounting path so downstream clients can bill on them: the per-turn token_consumption subscriber event carries cache_creation_input_tokens / cache_read_input_tokens (TokenConsumptionCounter), MemoryStore.getTokenConsumption() returns the cumulative session total, and in React useGlove().stats exposes the running cache totals. For Next.js, createChatHandler reports provider cache usage on the SSE done event so the client-side agent loop threads it into stats.

Stores

Stores handle conversation persistence. Implement the StoreAdapter interface for any backend:

  • MemoryStore (from glove-react) — in-memory, great for prototyping
  • SqliteStore (from glove-core) — persistent, good for server-side agents
  • createRemoteStore (from glove-react) — delegates to your own API endpoints
  • Custom StoreAdapter — implement the StoreAdapter interface for any backend (Redis, Postgres, etc.)
Subscribers

Observe agent events in real time:

import type { SubscriberAdapter } from "glove-core";

const subscriber: SubscriberAdapter = {
  async record(event_type, data) {
    switch (event_type) {
      case "text_delta":      // streaming text chunk
        process.stdout.write(data.text);
        break;
      case "tool_use":        // tool invocation started
        console.log(`Calling: ${data.name}`);
        break;
      case "tool_use_result": // tool finished
        console.log(`Result: ${data.result.status}`);
        break;
    }
  },
};

app.addSubscriber(subscriber);
Voice

Add real-time voice interaction with glove-voice:

import { createElevenLabsAdapters } from "glove-voice";
import { useGloveVoice } from "glove-react/voice";

// Set up adapters with server-side token auth
const { stt, createTTS } = createElevenLabsAdapters({
  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",
});

// In your React component
const { runnable } = useGlove({ tools, sessionId });
const voice = useGloveVoice({ runnable, voice: { stt, createTTS } });
// voice.mode: "idle" | "listening" | "thinking" | "speaking"

Two turn modes: VAD (hands-free with barge-in) and Manual (push-to-talk). Token-based auth keeps API keys server-side.

MCP Integration

Bridge tools from any Model Context Protocol server into a Glove agent with glove-mcp. The model can discover and activate MCP servers from a static catalogue mid-conversation via the discovermcp subagent.

import { mountMcp } from "glove-mcp";
import type { McpAdapter, McpCatalogueEntry } from "glove-mcp";

const ENTRIES: McpCatalogueEntry[] = [
  {
    id: "notion",
    name: "Notion",
    description: "Search, read, and edit pages in a Notion workspace.",
    url: "https://mcp.notion.com/mcp",
    tags: ["docs", "notes", "wiki"],
  },
];

await mountMcp(runnable, {
  adapter: myAdapter,        // implements McpAdapter — getActive / activate / deactivate / getAccessToken
  entries: ENTRIES,
  clientInfo: { name: "my-app", version: "1.0.0" },
});

The framework's only auth seam is McpAdapter.getAccessToken(id) — return a bearer token however you obtained it. For the MCP authorization spec OAuth flow, glove-mcp/oauth ships an opt-in runMcpOAuth runner and reference OAuthStore implementations.

See the glove-mcp README and examples/mcp-cli for the full picture.

Glovebox

Glovebox packages a Glove agent as an isolated, network-addressable service. Wrap a built runnable with glovebox.wrap(runnable, config), run glovebox build, ship the generated dist/ (Dockerfile + nixpacks alternative + esbuild server bundle + manifest + bearer key) to any container host. The deployed server exposes one authenticated WebSocket endpoint per session; glovebox-client speaks to it. Files cross the wire as FileRef (inline | url | server | s3 | gcs), never raw bytes.

Five base images cover the common toolsets (glovebox/base, glovebox/media, glovebox/docs, glovebox/python, glovebox/browser). A storage policy DSL routes inputs and outputs by size — small payloads go inline, larger ones park on the server (or S3) and the client pulls them through the same SDK call. The kit auto-injects an environment skill, workspace skill, /output hook, and /clear-workspace hook, and prepends an environment preamble to the agent's system prompt at boot.

import { glovebox, rule, composite } from "glovebox-core"
import { agent } from "./my-agent"

export default glovebox.wrap(agent, {
  base: "glovebox/media",
  packages: { apt: ["ffmpeg"] },
  storage: {
    outputs: composite([
      rule.inline({ below: "1MB" }),
      rule.localServer({ ttl: "1h" }),
    ]),
  },
})
glovebox build ./glovebox.ts
docker run -p 8080:8080 -e GLOVEBOX_KEY="$(cat dist/glovebox.key)" my-app

See the Glovebox Guide and the per-package READMEs for the full picture: glovebox-core, glovebox-kit, glovebox-client.

Architecture

Glove is built on five adapter interfaces. Swap any layer without changing application logic.

┌─────────────────────────────────────────┐
│  Agent        — the agentic loop        │
│  PromptMachine — model wrapper          │
│  Executor     — tool runner (Zod+Effect)│
│  Observer     — session tracking        │
│  DisplayManager — UI state machine      │
│  GloveVoice   — voice pipeline          │
└─────────────────────────────────────────┘
       ▼              ▼            ▼
  ModelAdapter    StoreAdapter   VoiceAdapters
  (any LLM)      (any DB)       (STT/TTS/VAD)

Documentation

Examples

The repo includes six example agents:

Weather Agent

A simple terminal agent with weather lookup and activity suggestions using Ink.

pnpm weather:agent
Coding Agent

A full-featured coding assistant with file operations, bash, git tools, and a React web UI.

# Terminal mode
pnpm coding:agent

# Server + web UI
pnpm coding:server
pnpm coding:client
Next.js Trip Planner

A trip planning agent using defineTool, <Render>, renderResult, and display strategies.

cd examples/nextjs-agent && pnpm dev
Coffee Shop

An e-commerce coffee ordering experience with product catalog, cart, checkout, and voice interaction — built with defineTool, <Render>, display strategies, and glove-voice.

cd examples/coffee && pnpm dev
Lola

A voice-first movie companion with TMDB-powered tools, SileroVAD, and a cinematic amber/charcoal UI.

cd examples/lola && pnpm dev
MCP CLI

A multi-MCP server-side agent — connects to hosted MCP servers (Notion, Linear, Gmail) with the discovermcp subagent. Includes reference OAuth-flow CLIs that exercise glove-mcp/oauth.

pnpm mcp:notion-mcp-auth   # one-time OAuth dance
pnpm mcp:cli                # multi-MCP agent with discovery

Claude Code Skill

This repo includes an Agent Skill that gives Claude Code (and other compatible agents) deep knowledge of the Glove framework — architecture, API reference, patterns from the examples, and common gotchas.

Install with npx skills
npx skills add porkytheblack/glove -a claude-code

Or install globally (available in all projects):

npx skills add porkytheblack/glove -a claude-code -g

Once installed, Claude Code automatically uses the skill when you work with Glove code. You can also invoke it directly with /glove.

Manual install

Copy the .claude/skills/glove/ directory into your project's .claude/skills/ folder.

Development

# Install dependencies
pnpm install

# Build all packages
pnpm build

# Typecheck
pnpm typecheck

Grants

Glove Voice is supported by ElevenLabs.

ElevenLabs Startup Grant

License

MIT