A compositional agent framework for TypeScript. Agents are built by composing frozen, immutable primitives upward through layers -- from atoms to organisms -- then executed by a runtime with events, gates, workflows, and exporters.
| Package | Description |
|---|---|
@agentic-patterns/core |
Atoms, protocols, molecules, rendering, organisms |
@agentic-patterns/runtime |
Runner, events, gates, workflows, transport, multi-agent, exporters |
Runtime depends on core. Core never imports runtime.
pnpm install
pnpm build
pnpm testAgents are composed from small, frozen primitives. Each primitive answers one question about the agent's behavior.
import { Persona } from "@agentic-patterns/core";
const persona = new Persona({
identity: "a research assistant specializing in data analysis",
tone: "professional and precise",
priorities: ["accuracy over speed"],
});import { Mission } from "@agentic-patterns/core";
const mission = new Mission({
objective: "Analyze the provided dataset and produce a summary.",
success_criteria: [
"Identify top 3 trends",
"Include statistical backing",
],
constraints: ["Only use provided data, no external sources"],
});import { Judgment } from "@agentic-patterns/core";
const sourceQuality = new Judgment({
domain: "source-quality",
heuristics: [
"Prefer peer-reviewed sources",
"Cross-reference statistics across multiple sources",
],
escalation_triggers: [
"Contradictory data from equally credible sources",
],
});A Role composes primitives into a reusable template. Build one with
the fluent RoleBuilder.
import { RoleBuilder, Responsibility } from "@agentic-patterns/core";
const role = new RoleBuilder("research-assistant")
.withPersona(persona)
.withJudgment(sourceQuality)
.withResponsibility(new Responsibility({
key: "analysis",
name: "Data Analysis",
description: "Produce accurate, well-sourced quantitative analysis",
}))
.build();An Agent is a Role instantiated with a Mission and optional context.
import { AgentBuilder } from "@agentic-patterns/core";
const agent = new AgentBuilder(role)
.withMission(mission)
.withModel("claude-sonnet-4-20250514")
.build();The runtime package provides AgentRunner, an event bus, and
exporters for observability.
import {
AgentRunner,
AgentEventBus,
ConsoleExporter,
} from "@agentic-patterns/runtime";
const bus = new AgentEventBus();
const exporter = new ConsoleExporter(bus);
exporter.start();
const runner = new AgentRunner(model, bus);
const result = await runner.run(agent, "Analyze Q4 revenue trends.");
console.log(result.response);
exporter.stop();The workflow layer provides composable patterns for multi-step and iterative agent execution.
import { Sequential } from "@agentic-patterns/runtime";
const pipeline = new Sequential([
{ agent: researcher, messageTemplate: "Research: {{topic}}" },
{ agent: writer, messageTemplate: (ctx) => `Write about: ${ctx.research}`, outputKey: "draft" },
{ agent: editor, messageTemplate: (ctx) => `Edit: ${ctx.draft}` },
]);
const result = await pipeline.run({ topic: "AI trends" }, { runner });import { Parallel, collectByName } from "@agentic-patterns/runtime";
const fanout = new Parallel(
[
{ agent: analystA, messageTemplate: "Analyze market", name: "market" },
{ agent: analystB, messageTemplate: "Analyze competitors", name: "competitors" },
{ agent: analystC, messageTemplate: "Analyze technology", name: "tech" },
],
{ maxConcurrency: 2, consolidator: collectByName },
);
const result = await fanout.run({}, { runner });
// result.consolidatedOutput => { market: "...", competitors: "...", tech: "..." }Run an agent iteratively toward a goal, evaluating progress each turn.
import { TaskLoop, SimpleGoalEvaluator } from "@agentic-patterns/runtime";
const evaluator = new SimpleGoalEvaluator({
successPatterns: ["TASK_COMPLETE"],
failurePatterns: ["CANNOT_PROCEED"],
});
const loop = new TaskLoop(agent, evaluator, { maxIterations: 5 });
const result = await loop.run("Fix the failing test suite", {}, { runner });
// result.exitReason => "goal_achieved" | "max_iterations" | "explicit_stop" | "error"Self-critique loop: a producer generates, an evaluator scores and critiques, and the producer refines.
import { EvaluatorLoop, RubricEvaluator } from "@agentic-patterns/runtime";
const evaluator = new RubricEvaluator([
{ name: "clarity", description: "Writing is clear and concise", weight: 0.4 },
{ name: "accuracy", description: "Claims are factually correct", weight: 0.6 },
], { runner });
const loop = new EvaluatorLoop(writer, evaluator, {
maxRefinements: 3,
qualityThreshold: 0.8,
});
const result = await loop.run("Write a technical blog post about RAG");
// result.bestOutput => highest-scoring version across all refinementsGeneric retry wrapper for any async operation. Not agent-specific.
import { RetryLoop, ExponentialBackoff } from "@agentic-patterns/runtime";
const retry = new RetryLoop({
maxAttempts: 5,
backoff: new ExponentialBackoff({ initialMs: 100, maxMs: 5000 }),
retryableErrors: [RateLimitError],
timeoutMs: 30_000,
});
const result = await retry.run(() => callExternalAPI());Multi-turn conversation with external input/output callbacks.
import { ConversationLoop } from "@agentic-patterns/runtime";
const loop = new ConversationLoop(agent, {
maxExchanges: 10,
exitPhrases: ["goodbye", "exit"],
inputFn: async () => getUserInput(),
outputFn: async (response) => displayToUser(response),
});
const result = await loop.run({ runner });
// result.exitReason => "exit_phrase" | "max_exchanges" | "error"MockRunner enables deterministic testing without LLM calls.
import { MockRunner } from "@agentic-patterns/runtime";
const mock = new MockRunner()
.addResponse("analyze", { content: "Analysis: revenue up 15%" })
.addResponse("*", { content: "Default response" });
const result = await mock.run(agent, "analyze Q4 data");
// result.response => "Analysis: revenue up 15%"
// Verify interactions
console.log(mock.callHistory); // [{ message, agentName, model, timestamp }]For coordination across multiple agents, define an Agency and run it
with the AgencyRuntime.
import { Agency } from "@agentic-patterns/core";
import { AgencyRuntime } from "@agentic-patterns/runtime";
const agency = new Agency({
name: "sales-team",
description: "Coordinates lead research and outreach",
agents: [
{ role: "coordinator", is_coordinator: true, model: "claude-sonnet-4-20250514" },
{ role: "researcher", is_coordinator: false, model: "claude-sonnet-4-20250514" },
],
});
const runtime = new AgencyRuntime(agency, runner);
await runtime.start();
await runtime.injectCoordinator("Research Acme Corp");
await runtime.stop();Agent = Role x Background x Awareness x Mission
Role = Persona + Judgments + Capabilities + Responsibilities
Capability = Toolbox + Manual + Playbook
| Layer | Package | Location | Purpose |
|---|---|---|---|
| 0 | core | src/atoms/ |
Frozen Zod-validated models with toPrompt() |
| 1 | core | src/protocols/ |
Vendor-agnostic domain interfaces |
| 2 | core | src/molecules/ |
Toolbox, Manual, Capability, Playbook, ToolSchema |
| 3 | core | src/rendering/ |
PromptRenderer with composable sections |
| 4 | core | src/organisms/ |
RoleBuilder, AgentBuilder, Role, Agent |
| 5 | runtime | src/events/ |
Typed EventBus, AgentEventBus |
| 6 | runtime | src/gates/ |
Safety, approval, rate-limit, audit |
| 7 | runtime | src/runner/ |
AgentRunner (Vercel AI SDK), MockRunner, ClaudeCodeRunner |
| 8 | runtime | src/transport/ |
InProcessTransport, MessagingToolbox |
| 9 | runtime | src/runtime/ |
AgentNode, AgencyRuntime |
| 10 | runtime | src/workflows/ |
Sequential, Parallel, TaskLoop, EvaluatorLoop, RetryLoop, ConversationLoop |
| 11 | runtime | src/conversation/ |
Conversation, ConversationStoreProtocol, MemoryStore |
| 12 | runtime | src/exporters/ |
Console, Langfuse, OpenTelemetry |
| 13 | runtime | src/presets/ |
Pre-built roles, judgments, responsibilities |
- Zod schemas for all data models -- define schema,
z.infer<>for types - Immutability --
Object.freeze()+Readonly<>on atom data - ESM-first -- tsup produces both ESM and CJS outputs
- Strict TypeScript --
noUncheckedIndexedAccess,noUnusedLocals - Async throughout -- all protocol methods return
Promise<T> - Fluent builders --
.with*()methods returnthisfor chaining
pnpm build # tsup compile both packages
pnpm typecheck # tsc --noEmit strict mode
pnpm test # vitest run all tests
pnpm lint # biome check
pnpm check # all of the above- zod -- schema validation and type inference
- ai (Vercel AI SDK) -- LLM provider abstraction
- tsup -- bundler (ESM + CJS)
- vitest -- test runner
- biome -- formatter and linter
- TypeScript 5.7+ -- strict mode compilation
MIT