Kuralle is a TypeScript framework for building conversational AI agents with structured flows, routing, and durable tool execution.
Most agent frameworks give you a prompt and a tool-call loop. That's fine until the conversation has steps — collect these fields, confirm, then book. Kuralle puts those steps in a flow: a small graph of typed nodes with real control flow, so your procedure lives in code you can test, not in a 600-line system prompt.
One tagless primitive (defineAgent) derives its behavior from the fields you set. Add flows to get a structured flow agent. Add routes to get a router. Add agents to compose them. The runtime, session management, and streaming stay the same across all three.
Rule of thumb: if you're pasting more than ~20 lines of procedure into a system prompt, it belongs in a flow.
- Agents —
defineAgentwith instructions and tools. Behavior is derived from what you populate, not a type flag. - Flows — node graphs (
reply,collect,action,decide) where each node returns its next transition. Your SOP becomes a typed state machine you didn't have to hand-write. - Dynamic + durable flows — a flow is also data: the JSON
FlowDefinitiondialect is validated with repair actions and registered on a live runtime (runtime.addDynamicFlows), andkind: 'flow'runs journal every step so they resume after a crash. See the dynamic flows guide. - Tools —
defineToolwith a Zod input schema and an async executor. Every tool effect is logged so a retried turn never double-executes. - Routing / Handoffs — model-reasoned routing (
routes/agents, derived from agent shape) picks the right specialist without leaking dispatch text to the user.handoffstransfer session context between agents. - Runtime —
createRuntimewires agents, sessions, and streaming. Pi is the recommended application driver; Core keeps a portable AI SDK loop as its zero-configuration fallback.runtime.run()returns aTurnHandle: stream events withhandle.events, await the result, pipe to HTTP withhandle.toUIMessageStreamResponse()(AI SDK native, foruseChat), or usehandle.toResponseStream('sse')for rawStreamPartJSON-SSE. - Observability — every run is captured as a structured
AgentTrace.runtime.runOnce()returns one complete trace for evals; tracing is on by default (MemoryTraceStore), durable on Redis/Postgres/Cloudflare DO-SQLite, exportable to OTLP/Langfuse, and inspectable viakuralle traceor the embeddable@kuralle-agents/trace-uiviewer.
Procedures belong in flows, not prompts. The form-filler example in packages/core/examples/agents/form-filler.ts replaces a 584-line v1 state machine with ~60 lines.
Durable tool execution. Every defineTool call is logged in an append-only effect log. Retries replay against the log — a payment tool doesn't charge twice, a booking tool doesn't double-book.
Few primitives, composed. defineAgent, defineFlow, defineTool, createRuntime. That's the core API. Routing, multi-agent composition, and session persistence are all derived from these.
npm install @kuralle-agents/core @kuralle-agents/pi-driver @earendil-works/pi-ai @ai-sdk/openai ai zodPeer dependencies: ai@^6, zod. Bring your own provider package (@ai-sdk/openai, @ai-sdk/anthropic, etc.).
import { openai } from '@ai-sdk/openai';
import { createModels } from '@earendil-works/pi-ai';
import { openaiProvider } from '@earendil-works/pi-ai/providers/openai';
import { PiDriver } from '@kuralle-agents/pi-driver';
import { z } from 'zod';
import { defineAgent, defineTool, createRuntime } from '@kuralle-agents/core';
const echo = defineTool({
name: 'echo',
description: 'Echo back the provided text',
input: z.object({ text: z.string() }),
execute: async ({ text }) => ({ echoed: text }),
});
const agent = defineAgent({
id: 'support',
name: 'Support Agent',
instructions: 'Helpful support agent. Use the echo tool when asked.',
model: openai('gpt-4o-mini'),
tools: { echo }, // model-visible and wired through the durable executor
});
const models = createModels();
models.setProvider(openaiProvider());
const piModel = models.getModel('openai', 'gpt-4o-mini');
if (!piModel) throw new Error('Pi model is not registered');
const runtime = createRuntime({
agents: [agent],
defaultAgentId: 'support',
driver: new PiDriver({
model: piModel,
models,
getApiKey: () => process.env.OPENAI_API_KEY,
}),
});
let sessionId: string | undefined;
async function chat(input: string) {
const handle = runtime.run({ input, sessionId });
for await (const part of handle.events) { // events is a property, not a method
if (part.type === 'text-delta') process.stdout.write(part.payload.delta);
if (part.type === 'done') sessionId = part.payload.sessionId;
}
await handle;
}
await chat('Use echo to say "hello"');Run it:
OPENAI_API_KEY=sk-... npx tsx agent.tsMore examples: packages/core/examples/agents/ — form-filler, transfer-agent, basic-chat, sales-with-leads.
Voice is out of scope. Cascaded voice and telephony transports (LiveKit, WebSocket, SIP, Twilio, SmartPBX) live in kuralle/kuralle-livekit. Provider-native speech-to-speech has been removed from this repo; Kuralle is text-first. Inbound voice notes are still supported as multimodal audio input — see the Multimodal guide.
| Package | Use when |
|---|---|
@kuralle-agents/core |
Always — agents, flows, runtime, session, tools, and the portable AI SDK fallback |
@kuralle-agents/build |
Deterministically compiling folder agents into immutable artifacts |
@kuralle-agents/deployment |
Versions, releases, sticky thread pins, runtime binding, D1/Node deployment adapters |
@kuralle-agents/pi-driver |
Recommended model/tool loop for production applications |
@kuralle-agents/hono-server |
Serving agents over HTTP/SSE/WebSocket on Node.js or Bun |
@kuralle-agents/cf-agent |
Deploying to Cloudflare Workers with Durable Objects |
@kuralle-agents/tools |
CAG tools for grounded retrieval and answering |
@kuralle-agents/rag |
RAG primitives — knowledge sources, chunkers, retrieval |
@kuralle-agents/redis-store |
Redis-backed session persistence (RedisSessionStore) |
@kuralle-agents/trace-ui |
Embeddable read-only trace waterfall and span detail viewer |
@kuralle-agents/postgres-store |
Postgres-backed session persistence |
@kuralle-agents/cli |
kuralle CLI for agents and scripts — JSON turns, approvals, memory, simulation, trace inspection |
@kuralle-agents/tui |
kuralle-tui — the human chat surface, with the trace and memory rail and the approval panel |
@kuralle-agents/client |
One Session interface over in-process, hosted HTTP, and Cloudflare targets |
- agents.kuralle.com — guides, API reference, concepts
node_modules/@kuralle-agents/core/guides/— the same guides, matching the version you installed- CONTRIBUTING.md — monorepo dev setup, build, test, publish
- CHANGELOG.md — release history
- LICENSE
Install the Kuralle skill into Claude Code, Cursor, OpenCode, Codex, and other agents:
npx skills add kuralle/kuralle-agents --skill kuralle-agentsIt routes to the right guide, maps the packages, and carries verified signatures for
defineAgent, defineTool, defineFlow, and createRuntime. Source: skills/kuralle-agents/.