Open Agent Loops
A minimal, provider-agnostic agent loop.
Model, memory, tools, stop conditions — every piece sits behind a swappable interface. Headless by default, so you bring your own front end. The pieces to build your own agent: your Jarvis, your Cortana, your Samantha.
From zero to a running agent
Define a tool, hand it to the loop, render the stream. That's the whole API surface.
import { runAgent, SessionMemoryStore, defineTool } from "@open-agent-loops/core";
import { OpenAICompatibleModel } from "@open-agent-loops/core/providers/openai";
import { z } from "zod";
// A tool is a name, a schema, and a function.
const weather = defineTool({
name: "weather",
description: "Get the weather for a city.",
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => ({ content: `Sunny in ${city}` }),
});
const result = await runAgent({
model: new OpenAICompatibleModel({ baseURL, apiKey, model }),
memory: new SessionMemoryStore(),
sessionId: "demo",
prompt: "What's the weather in Paris?",
tools: [weather],
onEvent: (e) => render(e), // the loop is headless — render events your way
});
console.log(result.messages.at(-1)?.content); // "It's sunny in Paris."Swap any seam
The loop depends only on the interface. Re-route a station — file store to Redis, mock model to a real one — without touching the line. Click a card to swap its implementation.
Bring your own front end
The loop never writes to a screen — it emits one typed AgentEvent stream, so a trace is just data. Scrub the same run and watch three front ends rebuild in lockstep.
- agent_startsession paris-demo
- turn_startturn 1
- reasoning_deltareasoning…
- tool_startweather({"city":"Paris"})
- tool_endweather → Sunny in Paris
- turn_startturn 2
- text_delta"It's sunny"
- text_delta"in Paris."
- agent_end2 steps
Ride the line
One pass through the loop, seam by seam — each is just a field on runAgent(). Scroll to ride along.
memory: new SessionMemoryStore()Conversation history loads before the first turn.
model: new OpenAICompatibleModel({
baseURL, apiKey, model,
})The one seam that talks to an LLM — a single stream() method.
tools: [
defineTool({ name: "weather", parameters, execute }),
]Tool calls run in parallel; results fold back into the loop.
stopWhen: maxSteps(10)Stop on a final answer, a terminate flag, or your own predicate.
hooks: {
gateToolCalls: permissionGate(store, prompter),
}Five optional hooks: gate tools, reshape context, steer mid-run.
Works with any OpenAI-compatible model
The ModelClient seam targets the OpenAI chat-completions wire format, so any endpoint that speaks it drops straight in. Exercised across these open-model families on Featherless:
Runs anywhere
No platform APIs, a single zod dependency, universal ESM. The same build drives a CLI and a browser tab — unchanged.
Composable Building Blocks
Skills, planning, sub-agents, channels — each built over runAgent(), never into it. Add what you need, ignore the rest.
Streaming by Default→
stream() returns an async iterable of StreamEvents — reasoning, text, and tool calls arrive incrementally.
Skills→
Bundle instructions, tools, and reference material the model loads on demand — then guard the bundle with a secret and an approval.
Planning Tools→
Give the model durable working memory: a to-do list and a scratchpad it keeps across turns, freezable into a replayable workflow.
Composable Agents→
Wrap an agent as a tool another agent calls — a multi-agent orchestrator over one chat, each sub-agent context-isolated.
Channels & Steering→
Feed a live, bursty transport (Slack, Discord) through one bounded, coalescing queue, and inject messages mid-run.
Goal Loops→
An outer runGoal loop with a grader seam drives the inner loop until the goal is met.
Tracing Built In→
A passive Tracer records the run as a timestamped timeline and per-turn trajectory, off the hot path.
Permissioned Tool Calls→
Gate the whole turn's tool calls up front with an allow / deny / ask policy — no race with parallel execution.
Independently Testable→
Every seam is verified in isolation with deterministic test doubles — zero network.
Build your agent
Start from the loop, plug in your seams, render it your way.