@agentic-patterns/runtime
v0.3.3
Published
Agent execution runtime — runner, events, gates, multi-agent, conversation, exporters
Maintainers
Readme
@agentic-patterns/runtime
Execution runtime for agentic-patterns agents. Provides the runner loop (Vercel AI SDK), typed event bus, gate chain, workflow compositions and loops, multi-agent transport and runtime, conversation persistence, observability exporters, and pre-built role presets.
Installation
bun add @agentic-patterns/runtime @agentic-patterns/core ai zodAPI Overview
Runner (src/runner/)
The AgentRunner executes agents using a tool loop on the Vercel AI SDK.
import { AgentRunner } from "@agentic-patterns/runtime";
const runner = new AgentRunner(model, eventBus);
const result = await runner.run(agent, "Analyze this data", {
toolExecutor: myExecutor,
maxIterations: 10,
history: previousMessages,
});
// result: { response, inputTokens, outputTokens, toolCallsCount, iterations, finishReason }Key types:
RunResult-- execution outcome with token counts and finish reasonRunOptions-- configuration: toolExecutor, maxIterations, historyRunnerProtocol-- interface for custom runner implementationsToolExecutor-- tool execution handler interfaceAgentLike-- minimal agent shape required by the runnerCanonicalMessage/CanonicalMessagePart-- normalized message formatconvertHistory()-- convert CanonicalMessage[] to CoreMessage[] for the AI SDK
MockRunner
Deterministic runner for testing agents without LLM calls. Pattern-based response routing with tool call simulation.
import { MockRunner } from "@agentic-patterns/runtime";
const mock = new MockRunner()
.addResponse("analyze", { content: "Revenue up 15%", inputTokens: 10, outputTokens: 20 })
.addResponse("summarize", {
content: "Summary complete",
toolCalls: [{ name: "write_file", arguments: { path: "out.md" }, result: "ok" }],
})
.addResponse("*", { content: "Default fallback" }); // wildcard
const result = await mock.run(agent, "analyze Q4");
// Verify calls
mock.callHistory; // [{ message, agentName, model, timestamp }]
// Streaming mode
for await (const event of mock.stream(agent, "analyze")) {
// yields full event lifecycle: message.start -> tool events -> message.complete
}
// Reset
mock.clear();Features:
- Substring trigger matching,
*wildcard, auto-fallback - Tool call simulation with results
- Delay and error simulation (
delayMs,errorfields) - Full event lifecycle emission in
stream()mode - Fluent API and call history recording
ClaudeCodeRunner
Runner backed by the Claude Agent SDK. Delegates to Claude Code's subprocess architecture.
import { ClaudeCodeRunner } from "@agentic-patterns/runtime";
const runner = new ClaudeCodeRunner({
defaults: { model: "sonnet" },
});
const result = await runner.run(agent, "Fix the bug in auth.ts");Events (src/events/)
Typed pub/sub event system with discriminated union events.
Event Types:
MessageStartEvent, MessageChunkEvent, MessageCompleteEvent, ReasoningEvent, ToolCallIntent, ToolCallRejectedEvent, ToolCallStartEvent, ToolCallEndEvent, IterationStartEvent, IterationEndEvent, LLMCallStartEvent, LLMCallEndEvent, ErrorEvent
All events carry trace fields: traceId, runId, spanId, parentSpanId, timestamp.
Sandbox Events for multi-agent communication:
AgentMessageEvent, AgentBroadcastEvent, AgentJoinEvent, AgentLeaveEvent, TaskCreateEvent, TaskUpdateEvent, TaskAssignEvent, HealthPingEvent, HealthPongEvent, NodeLifecycleEvent
import { EventBus, AgentEventBus, EventProfile, subscribeProfile } from "@agentic-patterns/runtime";
const bus = new AgentEventBus();
bus.subscribe("agent.message.complete", (event) => {
console.log(event.response);
});
subscribeProfile(bus, EventProfile.UX, (event) => {
// Receives message.start, message.chunk, message.complete, tool events, errors
});Event Profiles: UX, OBSERVABILITY, DEBUG, TOOLS, STREAMING
Gates (src/gates/)
Gate chain intercepts tool call intents for safety, approval, rate limiting, and auditing.
import { AgentEventBus, SafetyGate, HumanApprovalGate, AuditGate } from "@agentic-patterns/runtime";
const bus = new AgentEventBus();
// Gates are checked in category order: SAFETY -> RATE_LIMIT -> APPROVAL -> AUDIT
bus.addGate(new SafetyGate(["rm", "drop_table"]));
bus.addGate(new HumanApprovalGate(async (event) => {
return confirm(`Allow ${event.toolName}?`);
}));
bus.addGate(new AuditGate((event) => auditLog.append(event)));Gate types: SafetyGate, HumanApprovalGate, RateLimitGate, AuditGate
Workflows (src/workflows/)
Composable workflow patterns for multi-step and iterative agent execution. All patterns implement PatternProtocol and share common types.
Base Types
PatternContext-- shared context (Record<string, unknown>) threaded through stepsMessageTemplate-- static string or(context) => stringfunctionStep-- agent + message template + optional output key + context extractorStepResult-- execution result with.contentaccessorPatternResult-- interface: totalInputTokens, totalOutputTokens, succeeded, finalContentPatternEvent-- discriminated union: start, step.start, step.complete, step.error, iteration.start, iteration.complete, completePatternHooks-- callbacks for pattern lifecycle eventsGoalEvaluatorProtocol--evaluate()returning[achieved, reason, confident]
Helpers: resolveMessage(), makeStepName(), executeStep()
Sequential
Chain agents in sequence, threading context through the pipeline.
import { Sequential } from "@agentic-patterns/runtime";
const pipeline = new Sequential([
{ agent: researcher, messageTemplate: "Research the topic", outputKey: "research" },
{ agent: writer, messageTemplate: (ctx) => `Write about: ${ctx.research}` },
], { continueOnError: false });
const result = await pipeline.run({ topic: "AI" }, { runner });
result.steps; // StepResult[]
result.finalContext; // accumulated contextSupports nested patterns (Sequential/Parallel as steps) and continueOnError.
Parallel
Fan-out agents in parallel with optional concurrency limiting and result consolidation.
import { Parallel, collectByName, collectContents } from "@agentic-patterns/runtime";
const fanout = new Parallel(
[
{ agent: analystA, messageTemplate: "Analyze market", name: "market" },
{ agent: analystB, messageTemplate: "Analyze tech", name: "tech" },
],
{ maxConcurrency: 2, consolidator: collectByName },
);
const result = await fanout.run({}, { runner });
result.successful; // StepResult[]
result.failed; // [index, Error][]
result.allSucceeded; // boolean
result.consolidatedOutput; // { market: "...", tech: "..." }Built-in consolidators: collectContents (string[]), collectByName (Record). Custom consolidators accepted.
Goal Evaluators
Four implementations of GoalEvaluatorProtocol, ranked cheapest to most expensive:
| Evaluator | Strategy | LLM? |
|-----------|----------|------|
| SimpleGoalEvaluator | Pattern matching against output | No |
| SelfEvalGoalEvaluator | Parses GOAL_STATUS/PROGRESS markers | No |
| LLMGoalEvaluator | Sends goal + result to evaluator agent | Yes |
| EvaluatorChain | Tries in order, stops on first confident result | Mixed |
All return [achieved: boolean, reason: string, confident: boolean].
import { EvaluatorChain, SimpleGoalEvaluator, LLMGoalEvaluator } from "@agentic-patterns/runtime";
const chain = new EvaluatorChain([
new SimpleGoalEvaluator({ successPatterns: ["TASK_COMPLETE"] }),
new LLMGoalEvaluator({ agent: evaluatorAgent, runner }),
]);TaskLoop
Goal-driven iteration: run agent, evaluate progress, repeat.
import { TaskLoop, SimpleGoalEvaluator } from "@agentic-patterns/runtime";
const loop = new TaskLoop(agent, new SimpleGoalEvaluator({
successPatterns: ["TASK_COMPLETE"],
failurePatterns: ["CANNOT_PROCEED"],
}), { maxIterations: 5 });
const result = await loop.run("Fix all failing tests", {}, { runner });
result.exitReason; // "goal_achieved" | "max_iterations" | "explicit_stop" | "error"
result.iterations; // number of iterations executedFeatures: history summarization in prompts, configurable stop phrases, goal evaluation per iteration.
EvaluatorLoop
Producer-evaluator refinement: producer generates, evaluator scores + critiques, producer refines.
import { EvaluatorLoop, RubricEvaluator, CompositeRefinementEvaluator } from "@agentic-patterns/runtime";
const rubric = new RubricEvaluator([
{ name: "clarity", description: "Clear and concise", weight: 0.4 },
{ name: "accuracy", description: "Factually correct", weight: 0.6 },
], { runner });
const loop = new EvaluatorLoop(producer, rubric, {
maxRefinements: 3,
qualityThreshold: 0.8,
});
const result = await loop.run("Write a blog post about RAG");
result.exitReason; // "quality_met" | "max_refinements" | "no_improvement" | "error"
result.bestOutput; // highest-scoring version across all refinementsEvaluator implementations: LLMRefinementEvaluator, RubricEvaluator, CompositeRefinementEvaluator (weighted average).
RetryLoop
Generic async retry wrapper. Not agent-specific -- wraps any () => Promise<T>.
import { RetryLoop, ExponentialBackoff, JitteredBackoff, FixedBackoff } from "@agentic-patterns/runtime";
const retry = new RetryLoop({
maxAttempts: 5,
backoff: new ExponentialBackoff({ initialMs: 100, maxMs: 5000 }),
retryableErrors: [RateLimitError],
timeoutMs: 30_000,
onRetry: (attempt, error) => console.log(`Retry ${attempt}: ${error.message}`),
});
const result = await retry.run(() => callExternalAPI());Backoff strategies: FixedBackoff, ExponentialBackoff, JitteredBackoff.
ConversationLoop
Multi-turn conversation orchestration 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"
result.exchangeCount; // number of exchanges completedIntegrates with ConversationStoreProtocol for persistence via MemoryStore.
Transport (src/transport/)
Message transport for multi-agent communication.
import { InProcessTransport, MessagingToolbox } from "@agentic-patterns/runtime";
const transport = new InProcessTransport();
transport.subscribe("agency.*.messages", (msg) => { /* ... */ });
await transport.publish("agency.worker.messages", payload);
const toolbox = new MessagingToolbox(transport, senderAddress, agency);Transport interface can be implemented for external systems (NATS, Redis, etc.).
Runtime (src/runtime/)
Multi-agent execution runtime.
import { Agency } from "@agentic-patterns/core";
import { AgencyRuntime, AgentNode } from "@agentic-patterns/runtime";
const runtime = new AgencyRuntime(agency, runner, "run-123");
await runtime.start();
await runtime.injectCoordinator("Begin processing");
const status = runtime.status(); // { coordinator: "running", worker: "running" }
await runtime.stop();AgentNode wraps a single agent with message batching, idle timeout, max turns, and lifecycle events.
Conversation (src/conversation/)
Conversation state management with structured persistence.
import { Conversation, MemoryStore } from "@agentic-patterns/runtime";
// In-memory persistence
const store = new MemoryStore();
const convo = new Conversation("conv-123", "agent-name", { store });
convo.addExchange({
userMessage: "Hello",
assistantMessage: "Hi there!",
inputTokens: 10,
outputTokens: 8,
toolCalls: [],
timestamp: new Date().toISOString(),
});ConversationStoreProtocol provides full CRUD for conversations, messages, and message parts:
createConversation(),getConversation(),updateConversation()addMessage(),getMessages(),getMessageParts()
MemoryStore is the built-in in-memory implementation. Implement ConversationStoreProtocol for database-backed persistence.
Exporters (src/exporters/)
Observability exporters that subscribe to EventBus events.
| Exporter | Output |
|----------|--------|
| ConsoleExporter | Terminal output via configurable logger |
| LangfuseExporter | Langfuse trace spans |
| OTelExporter | OpenTelemetry trace spans |
import { ConsoleExporter, createConsoleExporter } from "@agentic-patterns/runtime";
const exporter = createConsoleExporter(bus);
exporter.start();
// ... run agent ...
exporter.stop();All exporters extend BaseExporter which manages EventBus profile subscription lifecycle.
Presets (src/presets/)
Pre-built roles, judgments, and responsibilities for common agent patterns.
Roles:
coordinatorRole(name, persona)-- orchestration coordinatororchestratorRole(name, persona)-- task orchestratoranalystRole(name, persona)-- research/analysis specialistretrievalRole(name, persona)-- information retrieval specialist
Judgments: ROUTING, QUALITY_REVIEW, INTENT_CLASSIFICATION, RETRIEVAL_STRATEGY, EVIDENCE_QUALITY
Responsibilities: ORCHESTRATION, QUALITY_GATE, INTENT_ROUTING, RESPONSE_SYNTHESIS, INFORMATION_RETRIEVAL, ANALYSIS
import { coordinatorRole, ROUTING, ORCHESTRATION } from "@agentic-patterns/runtime";
const role = coordinatorRole("lead", persona);