Complete API reference for the core runtime package. Contains the agent loop, tool execution engine, display manager, context management, model adapters, and all foundational types.
The top-level builder and runtime entry point. Use the builder pattern to register tools and subscribers, then call build() to produce a runnable agent.
import { Glove, MemoryStore, Displaymanager, createAdapter } from "glove-core";
import { z } from "zod";
const agent = new Glove({
store: new MemoryStore("session-1"),
model: createAdapter({ provider: "anthropic" }),
displayManager: new Displaymanager(),
systemPrompt: "You are a helpful assistant.",
compaction_config: {
compaction_instructions: "Summarize the conversation.",
max_turns: 30,
},
})
.fold({
name: "get_weather",
description: "Get weather for a city.",
inputSchema: z.object({ city: z.string() }),
async do(input) {
const res = await fetch(`https://api.weather.example/v1?city=${input.city}`);
return { status: "success", data: await res.json() };
},
})
.build();
const result = await agent.processRequest("What is the weather in Tokyo?");store is optional — when omitted, Glove constructs a fresh MemoryStore internally. You can also defer the store decision until build() by passing it there: new Glove({ ... }).build(myStore).
new Glove(config: GloveConfig)
| Property | Type | Description |
|---|---|---|
| store? | StoreAdapter | Conversation persistence. Optional — defaults to a fresh MemoryStore. May also be supplied later via build(store) / rebuild(store). |
| model | ModelAdapter | The model adapter for language model communication. Required. |
| displayManager | DisplayManagerAdapter | The display manager adapter for UI slot management. Required. |
| systemPrompt | string | The system prompt sent with every model request. Required. |
| serverMode? | boolean | Default false. Hint to integrations (e.g. mountMcp) that no UI is present. Drives default permission gating. |
| maxRetries? | number | Maximum number of retries for failed tool executions. Passed to the Executor. |
| compaction_config | CompactionConfig | Configuration for automatic context window compaction. Required. |
| enableToolResultSummary? | boolean | Default false. When true, the PromptMachine swaps each older tool result's data for its summary before sending messages to the model. Pairs with the per-tool generateToolSummary handler. See Tool result summaries below. |
For the in-message /hook and /skill directive system and the subagent factory pattern, see the dedicated Hooks, Skills & Subagents guide.
| Method | Returns | Description |
|---|---|---|
| fold<I>(args: GloveFoldArgs<I>) | IGloveBuilder | Register a tool with the agent. Legal at any time, including after build(). Returns the builder for chaining. |
| defineHook(name, handler) | IGloveBuilder | Register a /name hook that runs before the model with full agent controls. See the Hooks, Skills & Subagents guide. |
| defineSkill(args) | IGloveBuilder | Register a /name skill that injects context as a synthetic user message. Object form: { name, handler, description?, exposeToAgent? }. exposeToAgent: true exposes the skill via the glove_invoke_skill tool. |
| defineSubAgent(args) | IGloveBuilder | Register a subagent factory the main agent can route to via the auto-registered glove_invoke_subagent tool. Object form: { name, factory, description? }. The factory builds and returns a fully-configured child IGloveRunnable for each invocation. |
| addSubscriber(subscriber: SubscriberAdapter) | IGloveBuilder | Add a subscriber that receives streaming events. Returns the builder for chaining. |
| removeSubscriber(subscriber: SubscriberAdapter) | void | Detach a previously added subscriber. |
| setDisplayManager(dm: DisplayManagerAdapter) | IGloveBuilder | Swap the display manager. Builder-form (chainable) and runtime-form. Subagents typically call this on the child to share the parent's display stack mid-run. |
| build(store?: StoreAdapter) | IGloveRunnable | Finalize configuration and return a runnable agent. If no store was supplied to the constructor and one is supplied here, the executor's already-folded tools (including auto-registered skill/subagent dispatch tools) and subscribers are transferred onto the freshly-built executor. |
| rebuild(store?: StoreAdapter) | IGloveRunnable | Same body as build(); chainable variant useful for swapping the store late. |
| processRequest(request, signal?) | Promise<ModelPromptResult | Message> | Send a request string or ContentPart[] to the agent and receive the result. Available after build(). |
| setModel(model: ModelAdapter) | void | Replace the model adapter at runtime. Useful for model switching mid-session. |
| setSystemPrompt(prompt: string) | void | Update the system prompt for this session. Only safe to call when no request is in progress. |
| getSystemPrompt() | string | Return the current system prompt. |
| Property | Type | Description |
|---|---|---|
| displayManager | DisplayManagerAdapter | Read-only access to the display manager instance. |
| model | ModelAdapter | Read-only access to the active model adapter. |
| serverMode | boolean | Whether the agent was constructed with serverMode: true. |
| Property | Type | Description |
|---|---|---|
| name | string | Unique name for the tool. |
| description | string | Description of what the tool does. The model reads this to decide when to invoke it. |
| inputSchema? | z.ZodType<I> | Zod schema for the tool's input. Validated locally on each call. Provide either inputSchema or jsonSchema. |
| jsonSchema? | Record<string, unknown> | Raw JSON Schema — for tools bridged from MCP, OpenAPI, etc. Skips local validation. Provide either inputSchema or jsonSchema. |
| requiresPermission? | boolean | When true, checks the store for permission before execution. Defaults to false. |
| unAbortable? | boolean | When true, the tool runs to completion even if the abort signal fires (e.g. from voice barge-in). Use for mutation-critical tools. Defaults to false. |
| do | (input: I, display: DisplayManagerAdapter, glove: IGloveRunnable, signal?: AbortSignal) => Promise<ToolResultData> | The tool's implementation. Receives validated input, the parent's display manager, the running Glove instance (use to fold further tools at runtime), and the active request's AbortSignal. Forward the signal into long-running internal work so abort propagates. |
| generateToolSummary? | (summaryArgs?: unknown) => Promise<string> | Optional. When the tool's do() returns a ToolResultData with generateSummaryArgs set, the Executor calls this with those args and stores the returned string on result.summary. The summary replaces data in older context when Glove was constructed with enableToolResultSummary: true. |
The interface returned by build(). Represents a fully configured, ready-to-run agent. Most builder methods are also part of this interface — folding tools and registering hooks/skills/subagents is legal at any time, including after build.
| Member | Type | Description |
|---|---|---|
| processRequest(request, signal?) | (request: string | ContentPart[], signal?: AbortSignal) => Promise<ModelPromptResult | Message> | Send a user request to the agent and get the response. Parses /hook and /skill directives; @mentions reach the model verbatim. |
| fold<I>(args: GloveFoldArgs<I>) | IGloveRunnable | Fold a tool. Legal at any time, including after build. |
| defineHook(name, handler) | IGloveRunnable | Register a /name hook handler. |
| defineSkill(args) | IGloveRunnable | Register a /name skill handler. |
| defineSubAgent(args) | IGloveRunnable | Register a subagent factory. |
| rebuild(store?) | IGloveRunnable | Rebuild the agent's internals, optionally swapping the store. Tools folded before rebuild are transferred onto the new executor. |
| setModel(model) | (model: ModelAdapter) => void | Swap the model adapter at runtime. |
| setSystemPrompt(prompt) | (prompt: string) => void | Update the system prompt. |
| getSystemPrompt() | () => string | Read the current system prompt. |
| setDisplayManager(dm) | (dm: DisplayManagerAdapter) => void | Swap the display manager. Subagents typically call this on the child Glove to share the parent's display stack mid-run. |
| addSubscriber(s) | (s: SubscriberAdapter) => void | Attach a SubscriberAdapter to the prompt machine, executor, and observer. |
| removeSubscriber(s) | (s: SubscriberAdapter) => void | Detach a previously attached SubscriberAdapter. |
| displayManager | DisplayManagerAdapter | Read-only reference to the active display manager. |
| model | ModelAdapter | Read-only reference to the active model adapter. |
| serverMode | boolean | Whether the agent was constructed with serverMode: true. |
Manages the display stack: a ordered collection of UI slots that tools push and users resolve. Implements DisplayManagerAdapter.
import { Displaymanager } from "glove-core/display-manager";
const dm = new Displaymanager();
dm.subscribe((stack) => {
console.log("Display stack changed:", stack);
});| Method | Returns | Description |
|---|---|---|
| registerRenderer<I,O>(renderer: Renderer<I,O>) | void | Register a named renderer with input/output schemas. |
| pushAndForget<I>(slot: { renderer?: string; input: I }) | Promise<string> | Push a slot onto the stack without blocking. Returns the slot ID. |
| pushAndWait<I,O>(slot: { renderer?: string; input: I }) | Promise<O> | Push a slot and block until resolved or rejected. Returns the resolved value. |
| subscribe(listener: ListenerFn) | UnsubscribeFn | Subscribe to stack changes. The listener is called with the current stack whenever it changes. Returns an unsubscribe function. |
| notify() | Promise<void> | Manually trigger all subscribed listeners with the current stack state. |
| resolve<O>(slot_id: string, value: O) | void | Resolve a pushAndWait slot by ID, unblocking the waiting tool. |
| reject(slot_id: string, error: string) | void | Reject a pushAndWait slot by ID, causing the pushAndWait promise to throw. |
| removeSlot(id: string) | void | Remove a slot from the stack by ID. |
| clearStack() | Promise<void> | Remove all slots from the display stack and notify listeners. |
The interface that DisplayManager implements. Any custom display manager must conform to this shape.
| Member | Type | Description |
|---|---|---|
| renderers | Array<Renderer<unknown, unknown>> | Registry of named renderers. |
| stack | Slot<unknown>[] | The current display stack, ordered from bottom to top. |
| listeners | Set<ListenerFn> | Set of subscribed listener functions. |
| resolverStore | Map<string, { resolve: ResolverFn<unknown>; reject: RejectFn }> | Internal map of pending pushAndWait resolvers keyed by slot ID. |
| registerRenderer(renderer) | void | Register a renderer. |
| pushAndForget(slot) | Promise<string> | Push without blocking. |
| pushAndWait(slot) | Promise<unknown> | Push and block until resolved. |
| notify() | Promise<void> | Trigger listeners. |
| subscribe(listener) | UnsubscribeFn | Subscribe to changes. |
| resolve(slot_id, value) | void | Resolve a pending slot. |
| reject(slot_id, error: any) | void | Reject a pending slot. |
| removeSlot(id) | void | Remove a slot by ID. |
| clearStack() | Promise<void> | Clear all slots. |
Represents a single entry on the display stack. Pushed by tools, rendered by the UI layer.
| Property | Type | Description |
|---|---|---|
| id | string | Unique identifier for this slot instance. |
| renderer | string | Name of the renderer to use for displaying this slot. |
| input | I | Input data passed to the renderer. Shape depends on the tool that created the slot. |
A named renderer definition registered with the display manager.
| Property | Type | Description |
|---|---|---|
| name | string | Unique name identifying this renderer. |
| inputSchema | z.ZodType<I> | Zod schema for validating the input data. |
| outputSchema? | z.ZodType<O> | Optional Zod schema for validating the resolved output. |
Wraps a StoreAdapter and provides a simplified interface for reading and writing conversation data, messages, and tasks.
import { Context } from "glove-core";
const ctx = new Context(store);
const messages = await ctx.getMessages();
await ctx.appendMessages([{ sender: "user", text: "Hello" }]);new Context(store: StoreAdapter)
| Method | Returns | Description |
|---|---|---|
| getMessages() | Promise<Message[]> | Retrieve messages for the model. Applies splitAtLastCompaction internally: finds the last message with is_compaction set to true and returns only messages from that point onward. This means the model sees the compaction summary plus any subsequent messages, not the full raw history. To access the complete unfiltered history, use the store's getMessages() directly. |
| appendMessages(msgs: Message[]) | Promise<void> | Append messages to the conversation history. |
| getTasks() | Promise<Task[]> | Retrieve all tasks from the store. Requires store to implement getTasks. |
| addTasks(tasks: Task[]) | Promise<void> | Add tasks to the store. Requires store to implement addTasks. |
| updateTask(taskId: string, updates: Partial<Task>) | Promise<void> | Update a task by ID. Requires store to implement updateTask. |
Manages model prompting: sends messages and tool definitions to the model adapter and collects the response. Notifies subscribers of streaming events.
import { PromptMachine } from "glove-core";
const pm = new PromptMachine(model, ctx, "You are a helpful assistant.");
pm.addSubscriber(subscriber);
const result = await pm.run(messages, tools);new PromptMachine(model: ModelAdapter, ctx: Context, systemPrompt: string, enableToolResultSummary?: boolean)
The optional enableToolResultSummary flag (default false) is wired through from GloveConfig.enableToolResultSummary. When set, every call to run() first passes the message list through summarizeOlderToolResults.
| Method | Returns | Description |
|---|---|---|
| addSubscriber(subscriber: SubscriberAdapter) | void | Add a subscriber to receive model events (text_delta, tool_use, model_response_complete). |
| summarizeOlderToolResults(messages: Message[]) | Message[] | Pure transform. Finds the index of the latest non-tool user message and, for every message with tool_results at or before that index, replaces result.data with result.summary when summary is present. Untouched messages (including the current turn's tool results) are returned by reference. Called automatically by run() when enableToolResultSummary is true. |
| run(messages: Message[], tools?: Tool<unknown>[], signal?: AbortSignal) | Promise<ModelPromptResult> | Prompt the model with messages and optional tools. When enableToolResultSummary is true, the message list is first passed through summarizeOlderToolResults. Returns the model's response including token counts. |
The tool execution engine. Maintains a registry of tools and a call stack. Executes tool calls from the model, validates inputs, handles errors, and returns results.
import { Executor } from "glove-core";
const executor = new Executor(3, store);
executor.registerTool(myTool);
executor.addSubscriber(subscriber);
executor.addToolCallToStack({ tool_name: "get_weather", input_args: { city: "Tokyo" } });
const results = await executor.executeToolStack();new Executor(MAX_RETRIES?: number, store?: StoreAdapter)
| Property | Type | Description |
|---|---|---|
| tools | Tool[] | Array of registered tools. |
| MAX_RETRIES | number | Maximum retry attempts for failed tool calls. |
| Method | Returns | Description |
|---|---|---|
| registerTool(tool: Tool<unknown>) | void | Add a tool to the executor's registry. |
| addSubscriber(subscriber: SubscriberAdapter) | void | Add a subscriber to receive tool execution events (tool_use, tool_use_result). |
| addToolCallToStack(call: ToolCall) | void | Queue a tool call for execution. |
| executeToolStack(handOver?: HandOverFunction, signal?: AbortSignal) | Promise<ToolResult[]> | Execute all queued tool calls and return their results. Clears the stack after execution. |
Monitors the context window size and triggers compaction when limits are exceeded. Tracks turn counts and token consumption. Compaction is history-preserving: messages are never deleted from the store. Instead, a compaction summary is appended with is_compaction: true, and resetCounters() resets the token and turn counts. The model only sees post-compaction messages because Context.getMessages() applies splitAtLastCompaction() internally.
import { Observer } from "glove-core";
const observer = new Observer(
store,
ctx,
promptMachine,
"Summarize the conversation so far.",
30, // max turns
100000 // context compaction token limit
);
await observer.turnComplete();
await observer.tryCompaction();new Observer(store: StoreAdapter, ctx: Context, prompt: PromptMachine, compaction_instructions: string, max_turns?: number, context_compaction_limit?: number, escape_compaction_threshold?: number)
| Property | Type | Description |
|---|---|---|
| MAX_TURNS | number | Maximum turns per request. Defaults to 120. Exceeding this aborts the loop with a polite error message. |
| CONTEXT_COMPACTION_LIMIT | number | Token count above which compaction runs. Defaults to 100,000. |
| ESCAPE_COMPACTION_THRESHOLD | number | Pre-emptive compaction percentage (0-100). Defaults to 90. When the current consumption is at or above this fraction of CONTEXT_COMPACTION_LIMIT, Agent.ask runs compaction immediately after a model turn that contained tool_use calls — keeps tool_use / tool_result pairs from being split across a compaction boundary. |
| Method | Returns | Description |
|---|---|---|
| setCompactionInstructions(instruction: string) | void | Update the compaction instructions at runtime. |
| setMaxTurns(new_max: number) | void | Update the maximum turn threshold. |
| setContextCompactionLimit(new_limit: number) | void | Update the token consumption threshold. |
| turnComplete() | Promise<void> | Notify the observer that a turn has completed. Increments the turn counter in the store. |
| getCurrentTurns() | Promise<number> | Get the current turn count from the store. |
| addTokensConsumed(args: TokenConsumptionCounter) | Promise<void> | Add { tokens_in, tokens_out } to the store and emit a token_consumption subscriber event. |
| isCompactionImminent() | Promise<boolean> | Returns true when current consumption has crossed the ESCAPE_COMPACTION_THRESHOLD fraction of CONTEXT_COMPACTION_LIMIT. Used by Agent.ask to compact pre-emptively after tool-call turns. |
| getCurrentTokenConsumption() | Promise<number> | Get the current total token consumption from the store. |
| tryCompaction() | Promise<void> | Check if compaction is needed (turns or tokens exceeded) and perform it if so. Summarizes the conversation and appends the summary as a new message with is_compaction set to true. Calls resetCounters() to reset token and turn counts without deleting messages. The full message history is preserved in the store for frontend display, while Context.getMessages() uses splitAtLastCompaction to ensure the model only sees messages from the latest compaction onward. |
| runCompactionNow() | Promise<void> | Same body as tryCompaction() but skips the token-threshold guard. Used by hook handlers via AgentControls.forceCompaction() to compact on demand. |
Orchestrates the core agent loop: prompt the model, check for tool calls, execute tools, feed results back, repeat until the model responds with text only.
import { Agent } from "glove-core";
const agent = new Agent(store, executor, context, observer, promptMachine);
const result = await agent.ask(userMessage);new Agent(store: StoreAdapter, executor: Executor, context: Context, observer: Observer, prompt_machine: PromptMachine)
| Method | Returns | Description |
|---|---|---|
| ask(message: Message, handOver?: HandOverFunction, signal?: AbortSignal) | Promise<ModelPromptResult> | Run the full agent loop for a user message. Prompts the model, executes any tool calls, loops until the model produces a final text response. Returns the final result with token counts. |
Custom error class thrown when an agent request is aborted via an AbortSignal. Has name set to "AbortError".
import { AbortError } from "glove-core";
try {
await agent.processRequest("Hello", signal);
} catch (err) {
if (err instanceof AbortError) {
console.log("Request was aborted.");
}
}new AbortError(message?: string)
Interface for language model providers. Implement this to connect any LLM to Glove.
interface ModelAdapter {
name: string;
prompt(
request: PromptRequest,
notify: NotifySubscribersFunction,
signal?: AbortSignal
): Promise<ModelPromptResult>;
setSystemPrompt(systemPrompt: string): void;
}| Member | Type | Description |
|---|---|---|
| name | string | Display name of the model or provider. |
| prompt(request, notify, signal?) | Promise<ModelPromptResult> | Send messages and tools to the model. Call notify() to emit streaming events. Returns the complete response with token counts. |
| setSystemPrompt(systemPrompt) | void | Update the system prompt used for subsequent requests. |
| Property | Type | Description |
|---|---|---|
| messages | Message[] | The conversation messages to send to the model. |
| tools? | Tool<unknown>[] | Optional array of tools the model can invoke. |
| Property | Type | Description |
|---|---|---|
| messages | Message[] | Response messages from the model (typically one agent message). |
| tokens_in | number | Input tokens consumed by this prompt. |
| tokens_out | number | Output tokens generated by this prompt. |
Every model adapter emits events via the notify callback during prompting. These events are fully typed using a discriminated union so that subscribers (and custom adapter authors) get compile-time safety.
A discriminated union of all event shapes. Each variant has a type field plus event-specific data.
type SubscriberEvent =
| { type: "text_delta"; text: string }
| { type: "tool_use"; id: string; name: string; input: unknown }
| { type: "model_response"; text: string; tool_calls?: ToolCall[];
stop_reason?: string; tokens_in?: number; tokens_out?: number }
| { type: "model_response_complete"; text: string; tool_calls?: ToolCall[];
stop_reason?: string; tokens_in?: number; tokens_out?: number }
| { type: "tool_use_result"; tool_name: string; call_id?: string;
result: ToolResultData }
| { type: "compaction_start"; current_token_consumption: number }
| { type: "compaction_end"; current_token_consumption: number;
summary_message: Message }
| { type: "token_consumption"; consumption: TokenConsumptionCounter }
| { type: "hook_invoked"; name: string }
| { type: "skill_invoked"; name: string; source: "user" | "agent"; args?: string }
| { type: "subagent_invoked"; name: string; prompt: string }
| { type: "subagent_completed"; name: string; status: "success" | "error"; message?: string };| Event | Emitted by | Description |
|---|---|---|
| text_delta | Model adapter (streaming) | Incremental text fragment from the model. Use to render streaming text in the UI. |
| tool_use | Model adapter (streaming) | The model is invoking a tool. Contains the tool id, name, and parsed input. |
| model_response | Model adapter (non-streaming) | Complete model response in non-streaming mode. Contains text, optional tool_calls, stop_reason, and token counts. |
| model_response_complete | Model adapter (streaming) | Final aggregated response after streaming finishes. Same shape as model_response. |
| tool_use_result | Executor | Result of executing a tool. Includes tool_name, call_id, and the full ToolResultData. |
| compaction_start | Observer | Conversation compaction is beginning. Contains current token consumption. |
| compaction_end | Observer | Compaction finished. Contains the new token count and the summary message. |
| token_consumption | Observer | Tokens were just added to the running totals. Carries the per-turn TokenConsumptionCounter. |
| hook_invoked | Glove | A user-side /name hook handler is about to run. |
| skill_invoked | Glove / skill dispatch tool | A skill handler is about to run. source: "user" for /name directive invocations, "agent" when the model called glove_invoke_skill. |
| subagent_invoked | Executor | A subagent's child Glove run is about to start. Carries the subagent name and the prompt the model supplied. |
| subagent_completed | Executor | Closes the subagent_invoked bracket with a 1:1 guarantee — fired even on abort or factory failure. |
A mapped type that extracts the data shape (everything except type) for each event. Use this when implementing a SubscriberAdapter or handling events in a switch statement.
type SubscriberEventDataMap = {
[E in SubscriberEvent as E["type"]]: Omit<E, "type">;
};
// Example: SubscriberEventDataMap["text_delta"] = { text: string }Interface for receiving events. Both the React hook subscriber and GloveVoice implement this. The record method is generic over the event type.
interface SubscriberAdapter {
record: <T extends SubscriberEvent["type"]>(
event_type: T,
data: SubscriberEventDataMap[T],
) => Promise<void>;
}When building a custom ModelAdapter, you must emit the correct events via the notify callback. Here is the minimal contract:
import type { ModelAdapter, NotifySubscribersFunction, PromptRequest, ModelPromptResult } from "glove-core";
class MyAdapter implements ModelAdapter {
name = "my-provider:model-name";
private systemPrompt?: string;
setSystemPrompt(systemPrompt: string) {
this.systemPrompt = systemPrompt;
}
async prompt(
request: PromptRequest,
notify: NotifySubscribersFunction,
signal?: AbortSignal,
): Promise<ModelPromptResult> {
// ... call your LLM API ...
// Non-streaming: emit a single model_response event
await notify("model_response", {
text: responseText,
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
stop_reason: finishReason ?? undefined,
tokens_in: usage.promptTokens,
tokens_out: usage.completionTokens,
});
return { messages: [message], tokens_in: ..., tokens_out: ... };
}
}For streaming adapters, emit events incrementally:
// During streaming — emit text fragments as they arrive
notify("text_delta", { text: chunk });
// When a tool call is fully assembled
await notify("tool_use", { id: toolCallId, name: toolName, input: parsedArgs });
// After the stream completes — emit the final aggregated response
await notify("model_response_complete", {
text: fullText,
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
stop_reason: finishReason ?? undefined,
});Key rules:
model_response (one event per prompt call).text_delta for each text chunk, tool_use for each completed tool call, and model_response_complete once at the end.stop_reason should be undefined (not null) when unavailable. Use ?? undefined to coerce provider SDK nulls.tool_use_result, compaction_start, and compaction_end are emitted by the framework — adapters should not emit these.Interface for conversation persistence. Implement this to store messages, token counts, tasks, and permissions in any backend.
interface StoreAdapter {
identifier: string;
getMessages(): Promise<Message[]>;
appendMessages(msgs: Message[]): Promise<void>;
getTokenCount(): Promise<number>;
addTokens(args: TokenConsumptionCounter): Promise<void>;
getTurnCount(): Promise<number>;
incrementTurn(): Promise<void>;
resetCounters(): Promise<void>;
// Optional — tasks:
getTasks?(): Promise<Task[]>;
addTasks?(tasks: Task[]): Promise<void>;
updateTask?(taskId: string, updates: Partial<Task>): Promise<void>;
// Optional — permissions:
getPermission?(toolName: string): Promise<PermissionStatus>;
setPermission?(toolName: string, status: PermissionStatus): Promise<void>;
// Optional — inbox:
getInboxItems?(): Promise<InboxItem[]>;
addInboxItem?(item: InboxItem): Promise<void>;
updateInboxItem?(
itemId: string,
updates: Partial<Pick<InboxItem, "status" | "response" | "resolved_at">>,
): Promise<void>;
getResolvedInboxItems?(): Promise<InboxItem[]>;
// Optional — subagent stores:
createSubAgentStore?(namespace: string, durable?: boolean): Promise<StoreAdapter>;
}
interface TokenConsumptionCounter {
tokens_in: number;
tokens_out: number;
}| Member | Type | Description |
|---|---|---|
| identifier | string | Unique identifier for the store instance (typically a session ID). |
| getMessages() | Promise<Message[]> | Retrieve all conversation messages. |
| appendMessages(msgs) | Promise<void> | Append messages to the history. |
| getTokenCount() | Promise<number> | Get the cumulative token count. |
| addTokens(args) | Promise<void> | Add { tokens_in, tokens_out } to the cumulative counts. getTokenCount() still returns a single sum. |
| getTurnCount() | Promise<number> | Get the current turn count. |
| incrementTurn() | Promise<void> | Increment the turn counter. |
| resetCounters() | Promise<void> | Reset token and turn counts to zero without deleting messages. Called during compaction to reset thresholds while preserving the full message history in the store. |
| getTasks?() | Promise<Task[]> | Retrieve all tasks. Optional. Enables the built-in task tool when present. |
| addTasks?(tasks) | Promise<void> | Add tasks. Optional. |
| updateTask?(taskId, updates) | Promise<void> | Update a task by ID. Optional. |
| getPermission?(toolName) | Promise<PermissionStatus> | Check permission status for a tool. Optional. |
| setPermission?(toolName, status) | Promise<void> | Set permission status for a tool. Optional. |
| getInboxItems?() | Promise<InboxItem[]> | Retrieve all inbox items. Optional. See the Inbox guide. |
| addInboxItem?(item) | Promise<void> | Persist a new inbox item. Optional. |
| updateInboxItem?(itemId, updates) | Promise<void> | Mutate an inbox item's status / response / resolved_at. Optional. |
| getResolvedInboxItems?() | Promise<InboxItem[]> | Retrieve only items in the resolved status. Optional. |
| createSubAgentStore?(namespace, durable?) | Promise<StoreAdapter> | Derive a child store for a subagent. With durable: false (default) every invocation returns a fresh store; with durable: true the same child store is returned for the same namespace so the subagent accumulates history across calls. Optional — when absent, subagent factories must construct their own store. |
In-process implementation of StoreAdapter shipped with glove-core. Used as the default store when Glove is constructed without one. All data lives in memory and is lost when the instance is garbage-collected — perfect for prototypes, scripts, tests, and short-lived sessions.
MemoryStore implements every optional method, including createSubAgentStore, so subagents work out of the box without any extra setup. With durable: true, derived sub-stores are cached per namespace so a subagent can carry message history across invocations within the same parent process.
import { MemoryStore } from "glove-core";
const store = new MemoryStore("session-1");
// Sub-stores
const ephemeral = await store.createSubAgentStore("reviewer"); // fresh per call
const durable = await store.createSubAgentStore("planner", true); // cached per namespacenew MemoryStore(identifier: string)
Interface for observing agent events. record is generic over the event type via SubscriberEventDataMap, so the compiler enforces that the data shape matches the event name.
interface SubscriberAdapter {
record: <T extends SubscriberEvent["type"]>(
event_type: T,
data: SubscriberEventDataMap[T],
) => Promise<void>;
}| Member | Type | Description |
|---|---|---|
| record(event_type, data) | Promise<void> | Called whenever an event occurs. The event_type discriminates the data shape via the SubscriberEventDataMap. |
For the full list of events and payload shapes, see the SubscriberEvent reference above.
Represents a single message in the conversation history.
interface Message {
sender: "user" | "agent";
id?: string;
text: string;
pre_modified_text?: string;
content?: ContentPart[];
tool_results?: ToolResult[];
tool_calls?: ToolCall[];
is_compaction?: boolean;
is_compaction_request?: boolean;
is_skill_injection?: boolean;
reasoning_content?: string;
}| Property | Type | Description |
|---|---|---|
| sender | "user" | "agent" | Who sent the message. |
| id? | string | Optional unique identifier for the message. |
| text | string | The text content of the message. After hooks rewrite a user turn, this holds the rewritten text. |
| pre_modified_text? | string | Original user text before any hook rewrite. Useful for transcript renderers that want to show the user what they actually typed. |
| content? | ContentPart[] | Optional multimodal content parts (images, documents, etc.). |
| tool_results? | ToolResult[] | Tool execution results attached to this message (agent messages responding to tool calls). |
| tool_calls? | ToolCall[] | Tool calls the model wants to execute (present in agent messages). |
| is_compaction? | boolean | When true, marks this message as a compaction summary. Context.getMessages() uses this flag to split the history at the last compaction point, so the model only sees messages from the most recent compaction onward. |
| is_compaction_request? | boolean | Internal marker on the synthetic user message that prompts the model for a compaction summary. |
| is_skill_injection? | boolean | When true, marks this message as a synthetic user turn produced by a /skill invocation (see Hooks, Skills & Mentions). Use it in transcript renderers to distinguish injected context from real user turns. |
| reasoning_content? | string | Provider-emitted reasoning trace captured by the OpenAI-compat adapter (when reasoning is enabled) or the MiMo adapter. DeepSeek V4 and MiMo require this to be echoed back on subsequent tool-calling turns — the adapters handle that round-trip automatically. |
Represents a multimodal content element within a message.
interface ContentPart {
type: "text" | "image" | "video" | "document";
text?: string;
source?: {
type: string;
media_type: string;
data?: string;
url?: string;
};
}| Property | Type | Description |
|---|---|---|
| type | "text" | "image" | "video" | "document" | The type of content. |
| text? | string | Text content. Used when type is "text". |
| source? | object | Source information for binary content. Contains type, media_type, and either data (base64) or url. |
| source.type | string | Source type (e.g., "base64", "url"). |
| source.media_type | string | MIME type (e.g., "image/png", "application/pdf"). |
| source.data? | string | Base64-encoded content data. |
| source.url? | string | URL pointing to the content. |
The core tool interface used by the Executor. This is the runtime representation, distinct from ToolConfig in glove-react which adds the render property.
| Property | Type | Description |
|---|---|---|
| name | string | Unique tool name. |
| description | string | Description for the model. |
| input_schema? | z.ZodType<I> | Zod schema for input validation and JSON Schema generation. Provide either input_schema or jsonSchema. |
| jsonSchema? | Record<string, unknown> | Raw JSON Schema for tools bridged from MCP / OpenAPI / etc. Skips local validation. Provide either input_schema or jsonSchema. |
| requiresPermission? | boolean | Whether the tool requires explicit permission before execution. |
| unAbortable? | boolean | When true, the tool runs to completion despite abort signals. Essential for tools that perform mutations the user has committed to (e.g. checkout, payment). |
| run(input, handOver?, signal?) | Promise<ToolResultData> | Execute the tool with validated input. handOver delegates to the renderer / display stack. signal is the active request's AbortSignal — forward it into long-running internal work (e.g. a child Glove run) so abort propagates. Tools marked unAbortable should ignore signal. |
| generateSummary?(args) | (args: unknown) => Promise<string> | Optional. Called by the Executor after run() resolves when the result includes generateSummaryArgs. The returned string lands on result.summary and is swapped in for data in older messages when enableToolResultSummary is on. |
| Property | Type | Description |
|---|---|---|
| tool_name | string | Name of the tool to invoke. |
| input_args | unknown | Arguments to pass to the tool (validated against the tool's input schema at runtime). |
| id? | string | Optional call identifier for correlating calls with results. |
| Property | Type | Description |
|---|---|---|
| tool_name | string | Name of the tool that produced this result. |
| call_id? | string | Identifier correlating this result with its ToolCall. |
| result | ToolResultData | The execution result. See ToolResultData below. |
The shape of the result field on a ToolResult. Contains the data returned by the tool, a status indicator, an optional error message, and an optional client-only rendering payload.
interface ToolResultData {
status: "success" | "error" | "aborted";
data: unknown; // Sent to the AI model
message?: string; // Error / abort message
renderData?: unknown; // Client-only — NOT sent to model, used by renderResult
summary?: string; // Set by the Executor from generateSummary — swapped in for data in older context when enableToolResultSummary is on
generateSummaryArgs?: unknown; // Opaque args passed to the tool's generateSummary handler
}| Property | Type | Description |
|---|---|---|
| status | "success" | "error" | "aborted" | Outcome of the tool. The Executor synthesizes "aborted" results when an abort signal interrupts an abortable tool. |
| data | unknown | The tool's return value. This is the data sent to the AI model as the tool result. |
| message? | string | Error message describing what went wrong. Typically present when status is "error". |
| renderData? | unknown | Client-only data for rendering tool results from history. Model adapters explicitly strip this field before sending to the AI — safe for sensitive client-only data like email addresses or UI state. Used by the renderResult function in glove-react tools. |
| summary? | string | Compact string description of the result. Set by the Executor after run()/do() resolves, by calling the tool's generateSummary(generateSummaryArgs). When the Glove was constructed with enableToolResultSummary: true, the PromptMachine substitutes summary for data on every tool result older than the most recent user message before sending to the model. Untouched in the store and in renderers. |
| generateSummaryArgs? | unknown | Opaque payload the tool's do() returns to drive its generateSummary handler — e.g. the line range it just read, the URL it just fetched, the query it just executed. Omit it to skip summary generation for a given call. |
Model adapters (Anthropic, OpenAI-compat) explicitly destructure and only send data, status, and message to the API. The renderData field is preserved in the message store for client-side rendering via renderResult but is never sent to the AI model.
Long-running agents that read files, fetch URLs, or run queries blow through tokens fast — the model rarely needs the full payload of a tool call once a few turns have passed. Tool result summaries are an opt-in optimization: each tool produces a compact description of what it did, and that description is what older context carries instead of the raw payload.
The mechanism has three coordinated pieces:
generateSummaryArgs. The tool's do() includes whatever the summary handler needs (line range, URL, query, row count) on the result.generateToolSummary. After do() resolves, if the tool defined a generateToolSummary handler and the result has generateSummaryArgs, the Executor awaits it and assigns the returned string to result.summary.data for summary in older context. When Glove is constructed with enableToolResultSummary: true, PromptMachine.summarizeOlderToolResults rewrites every tool result that sits at or before the most recent non-tool user message: result.data is replaced with result.summary. Tool results from the current turn are untouched, so the model still has full fidelity for what it just asked about.import { z } from "zod";
import type { GloveFoldArgs } from "glove-core";
export const readFile: GloveFoldArgs<{ path: string; from?: number; to?: number }> = {
name: "read_file",
description: "Read a slice of a file.",
inputSchema: z.object({
path: z.string(),
from: z.number().optional(),
to: z.number().optional(),
}),
async do(input) {
const content = await fs.readFile(input.path, "utf8");
const slice = sliceLines(content, input.from, input.to);
return {
status: "success",
data: slice,
// What the summary handler needs to describe this call later.
generateSummaryArgs: { path: input.path, from: input.from, to: input.to, lineCount: slice.split("\n").length },
};
},
async generateToolSummary(args) {
const { path, from, to, lineCount } = args as { path: string; from?: number; to?: number; lineCount: number };
const range = from != null || to != null ? ` lines ${from ?? 1}-${to ?? "EOF"}` : "";
return `Read ${path}${range} (${lineCount} lines).`;
},
};const agent = new Glove({
store: new MemoryStore("session"),
model: createAdapter({ provider: "anthropic" }),
displayManager: new Displaymanager(),
systemPrompt: "You are a helpful assistant.",
compaction_config: { compaction_instructions: "Summarize so far." },
enableToolResultSummary: true,
})
.fold(readFile)
.build();Imagine the agent has executed read_file three turns ago and again on the current turn. With enableToolResultSummary: true:
read_file result is sent as Read src/lib/auth.ts lines 40-120 (81 lines). — the summary, not the file contents.read_file result is sent with the full file slice intact, so the model can reason about it.The store keeps both data and summary untouched on every result, so re-renders, transcripts, and post-hoc analytics still have the full record. Only the array of messages handed to the model adapter is rewritten.
Tools that omit generateToolSummary, or omit generateSummaryArgs on a particular call, leave summary unset. The pruner only substitutes when summary is a non-empty string, so partially-instrumented tool catalogues still work — instrumented tools shrink in older context, uninstrumented tools keep their original data.
Compaction collapses an entire run of messages into a single summary message once token use crosses a threshold (see Observer). Tool result summaries shrink individual tool payloads on every turn before compaction would have fired. The two compose: tool summaries delay the point at which the Observer needs to compact, and compaction still runs when the instrumented context eventually grows large enough.
Represents a tracked task in the agent's task list.
| Property | Type | Description |
|---|---|---|
| id | string | Unique identifier for the task. |
| content | string | Description of the task in imperative form (e.g., "Fix the login bug"). |
| activeForm | string | Present-continuous form shown during execution (e.g., "Fixing the login bug"). |
| status | TaskStatus | Current status: "pending", "in_progress", or "completed". |
type TaskStatus = "pending" | "in_progress" | "completed";type PermissionStatus = "granted" | "denied" | "unset";| Type | Signature | Description |
|---|---|---|
| NotifySubscribersFunction | <T extends SubscriberEvent['type']>(event_name: T, data: SubscriberEventDataMap[T]) => Promise<void> | Type-safe callback passed to ModelAdapter.prompt for emitting events to subscribers. |
| HandOverFunction | (input: unknown) => Promise<unknown> | Delegation callback passed to tool execution for handing control to another tool or system. |
| ListenerFn | (stack: Slot<unknown>[]) => Promise<void> | Display stack change listener. Called whenever the stack is modified. |
| UnsubscribeFn | () => void | Returned by subscribe() to remove a listener. |
| ResolverFn<RI> | (value: RI) => void | Internal resolver for pushAndWait promises. |
| RejectFn | (reason?: any) => void | Internal rejector for pushAndWait promises. |
| Name | Value | Description |
|---|---|---|
| SUBAGENT_DISPATCH_TOOL_NAME | "glove_invoke_subagent" | Tool name of the auto-registered subagent dispatch tool. The Executor recognises calls to this tool name and brackets them with subagent_invoked / subagent_completed events. Match against this constant rather than the literal string when filtering events or tool calls. |
Compaction is history-preserving. When triggered, the full conversation is summarized and the summary is appended as a new message with is_compaction: true. No messages are deleted from the store, so frontends can still display the complete history. The model only sees messages from the last compaction point onward, courtesy of splitAtLastCompaction() in Context.getMessages().
| Property | Type | Description |
|---|---|---|
| compaction_instructions | string | Instructions given to the model when summarizing the conversation. Required. |
| max_turns? | number | Maximum turns before compaction is triggered. |
| compaction_context_limit? | number | Maximum token count before compaction is triggered. |
The framework provides a built-in tool for task management. It is automatically registered when the store supports tasks (implements getTasks, addTasks, updateTask).
import { createTaskTool } from "glove-core";
const taskTool = createTaskTool(context);
// taskTool.name === "glove_update_tasks"function createTaskTool(context: Context): Tool<TaskToolInput>| Property | Type | Description |
|---|---|---|
| todos | Array<{ content: string; activeForm: string; status: TaskStatus }> | The complete task list. Each call replaces the entire list. |
The tool name is glove_update_tasks. The model calls it to create, update, or complete tasks. Each invocation sends the full current task list, enabling additions, status changes, and removals in a single call.
The glove-core/models/providers module exports factory functions for creating model adapters from supported providers.
import { createAdapter, getAvailableProviders } from "glove-core/models/providers";
const model = createAdapter({
provider: "anthropic",
model: "claude-sonnet-4-20250514",
maxTokens: 4096,
stream: true,
});
const available = getAvailableProviders();
// [{ id: "openai", name: "OpenAI", ... }, ...]function createAdapter(opts: CreateAdapterOptions): ModelAdapter| Property | Type | Description |
|---|---|---|
| provider | string | Provider ID. One of: openai, anthropic, openrouter, gemini, minimax, kimi, glm, mimo, ollama, lmstudio, bedrock. |
| model? | string | Model name to use. Defaults to the provider's default model. |
| apiKey? | string | API key. Defaults to the provider's environment variable. |
| maxTokens? | number | Maximum output tokens. Defaults to the provider's default. |
| stream? | boolean | Whether to use streaming. Defaults to true. |
| baseURL? | string | Override the provider's default base URL (e.g., custom port for local LLMs). |
| timeout? | number | Request timeout in milliseconds. Useful for slow local LLMs. Defaults to 10 minutes (600_000). |
| reasoning? | boolean | OpenAICompatReasoningOptions | Reasoning / thinking support for OpenAI-compatible providers. Pass true for sensible defaults (capture provider-emitted reasoning_content / reasoning into Message.reasoning_content, echo on tool turns) or an object for fine-grained control (effort, reasoningObject, thinking, extraBody, includeInText, echo). Ignored by the Anthropic, Bedrock, and MiMo paths. |
| reasoningEffort? | "minimal" | "low" | "medium" | "high" | Hint how much the model should think. Sent as the top-level reasoning_effort request field on the OpenAI-compat path (GPT-5/o-series, GLM-4.5/4.6, MiniMax M2.5, Kimi K2, DeepSeek V4) and mapped onto MiMo's existing knob. "minimal" is GPT-5-specific. On adaptive models like mimo-v2.5-pro, "low"/"medium" can suppress thinking — use "high" for consistently deep reasoning. |
| includeReasoningInText? | boolean | When true, wrap reasoning in <think>…</think> and prepend to the visible message text. Defaults to false — the trace stays on Message.reasoning_content. Honoured by the OpenAI-compat and MiMo adapters. |
Fine-grained reasoning configuration for OpenAI-compatible providers. Captures provider-emitted reasoning traces (reasoning_content per the DeepSeek / Qwen / GLM / Kimi / MiniMax / MiMo convention, or reasoning per OpenRouter normalization) and routes thinking-related request knobs.
interface OpenAICompatReasoningOptions {
/** Wrap reasoning in <think>...</think> and prepend to visible text. Default false. */
includeInText?: boolean;
/** Echo Message.reasoning_content back on tool-calling turns. Default true. */
echo?: boolean;
/** Top-level reasoning_effort request field. */
effort?: "minimal" | "low" | "medium" | "high";
/** OpenRouter-style reasoning object — sent verbatim. */
reasoningObject?: {
effort?: "low" | "medium" | "high";
max_tokens?: number;
exclude?: boolean;
enabled?: boolean;
};
/** Anthropic-style thinking object — for OpenAI shims that forward it. */
thinking?: { type: "enabled" | "disabled"; budget_tokens?: number };
/** Escape hatch — merged into request body. For Qwen3 dashscope's enable_thinking etc. */
extraBody?: Record<string, unknown>;
}Common patterns:
// Sensible defaults: capture + echo on tool-calling turns.
createAdapter({ provider: "openai", reasoning: true });
// Hint thinking depth — DeepSeek V4, GLM, MiniMax, Kimi, GPT-5/o-series.
createAdapter({ provider: "openai", reasoning: { effort: "high" } });
// OpenRouter's unified reasoning object.
createAdapter({
provider: "openrouter",
reasoning: { reasoningObject: { effort: "high", max_tokens: 2000 } },
});
// 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.
createAdapter({ provider: "openai", reasoning: { includeInText: true } });
// Disable echo (DeepSeek-R1 specifically — newer V4 needs echo on).
createAdapter({ provider: "openai", reasoning: { echo: false } });Returns an array of provider configurations that have API keys available in the current environment.
function getAvailableProviders(): Array<{ id: string; name: string; available: boolean; models: string[]; defaultModel: string }>| ID | 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 | mimo-v2.5 |
| ollama | (none) | (user-specified) |
| lmstudio | (none) | (user-specified) |
| bedrock | AWS_ACCESS_KEY_ID | anthropic.claude-3-5-sonnet-20241022-v2:0 |
Each provider has properties: id, name, baseURL, envVar, defaultModel, models[], format (either "openai", "anthropic", or "bedrock"), defaultMaxTokens, and requiresApiKey.
Local providers (ollama and lmstudio) don't require an API key and have no default model — you must pass a model name. Use baseURL to override the default port if needed:
const model = createAdapter({
provider: "ollama",
model: "llama3",
baseURL: "http://localhost:9999/v1", // optional, defaults to :11434
});