@agentic-patterns/runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pattern Stack
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,402 @@
1
+ # @agentic-patterns/runtime
2
+
3
+ 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.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @agentic-patterns/runtime @agentic-patterns/core ai zod
9
+ ```
10
+
11
+ ## API Overview
12
+
13
+ ### Runner (`src/runner/`)
14
+
15
+ The `AgentRunner` executes agents using a tool loop on the Vercel AI SDK.
16
+
17
+ ```typescript
18
+ import { AgentRunner } from "@agentic-patterns/runtime";
19
+
20
+ const runner = new AgentRunner(model, eventBus);
21
+
22
+ const result = await runner.run(agent, "Analyze this data", {
23
+ toolExecutor: myExecutor,
24
+ maxIterations: 10,
25
+ history: previousMessages,
26
+ });
27
+
28
+ // result: { response, inputTokens, outputTokens, toolCallsCount, iterations, finishReason }
29
+ ```
30
+
31
+ Key types:
32
+ - `RunResult` -- execution outcome with token counts and finish reason
33
+ - `RunOptions` -- configuration: toolExecutor, maxIterations, history
34
+ - `RunnerProtocol` -- interface for custom runner implementations
35
+ - `ToolExecutor` -- tool execution handler interface
36
+ - `AgentLike` -- minimal agent shape required by the runner
37
+ - `CanonicalMessage` / `CanonicalMessagePart` -- normalized message format
38
+ - `convertHistory()` -- convert CanonicalMessage[] to CoreMessage[] for the AI SDK
39
+
40
+ #### MockRunner
41
+
42
+ Deterministic runner for testing agents without LLM calls. Pattern-based response routing with tool call simulation.
43
+
44
+ ```typescript
45
+ import { MockRunner } from "@agentic-patterns/runtime";
46
+
47
+ const mock = new MockRunner()
48
+ .addResponse("analyze", { content: "Revenue up 15%", inputTokens: 10, outputTokens: 20 })
49
+ .addResponse("summarize", {
50
+ content: "Summary complete",
51
+ toolCalls: [{ name: "write_file", arguments: { path: "out.md" }, result: "ok" }],
52
+ })
53
+ .addResponse("*", { content: "Default fallback" }); // wildcard
54
+
55
+ const result = await mock.run(agent, "analyze Q4");
56
+
57
+ // Verify calls
58
+ mock.callHistory; // [{ message, agentName, model, timestamp }]
59
+
60
+ // Streaming mode
61
+ for await (const event of mock.stream(agent, "analyze")) {
62
+ // yields full event lifecycle: message.start -> tool events -> message.complete
63
+ }
64
+
65
+ // Reset
66
+ mock.clear();
67
+ ```
68
+
69
+ Features:
70
+ - Substring trigger matching, `*` wildcard, auto-fallback
71
+ - Tool call simulation with results
72
+ - Delay and error simulation (`delayMs`, `error` fields)
73
+ - Full event lifecycle emission in `stream()` mode
74
+ - Fluent API and call history recording
75
+
76
+ #### ClaudeCodeRunner
77
+
78
+ Runner backed by the Claude Agent SDK. Delegates to Claude Code's subprocess architecture.
79
+
80
+ ```typescript
81
+ import { ClaudeCodeRunner } from "@agentic-patterns/runtime";
82
+
83
+ const runner = new ClaudeCodeRunner({
84
+ defaults: { model: "sonnet" },
85
+ });
86
+
87
+ const result = await runner.run(agent, "Fix the bug in auth.ts");
88
+ ```
89
+
90
+ ### Events (`src/events/`)
91
+
92
+ Typed pub/sub event system with discriminated union events.
93
+
94
+ **Event Types:**
95
+ `MessageStartEvent`, `MessageChunkEvent`, `MessageCompleteEvent`, `ReasoningEvent`, `ToolCallIntent`, `ToolCallRejectedEvent`, `ToolCallStartEvent`, `ToolCallEndEvent`, `IterationStartEvent`, `IterationEndEvent`, `LLMCallStartEvent`, `LLMCallEndEvent`, `ErrorEvent`
96
+
97
+ All events carry trace fields: `traceId`, `runId`, `spanId`, `parentSpanId`, `timestamp`.
98
+
99
+ **Sandbox Events** for multi-agent communication:
100
+ `AgentMessageEvent`, `AgentBroadcastEvent`, `AgentJoinEvent`, `AgentLeaveEvent`, `TaskCreateEvent`, `TaskUpdateEvent`, `TaskAssignEvent`, `HealthPingEvent`, `HealthPongEvent`, `NodeLifecycleEvent`
101
+
102
+ ```typescript
103
+ import { EventBus, AgentEventBus, EventProfile, subscribeProfile } from "@agentic-patterns/runtime";
104
+
105
+ const bus = new AgentEventBus();
106
+
107
+ bus.subscribe("agent.message.complete", (event) => {
108
+ console.log(event.response);
109
+ });
110
+
111
+ subscribeProfile(bus, EventProfile.UX, (event) => {
112
+ // Receives message.start, message.chunk, message.complete, tool events, errors
113
+ });
114
+ ```
115
+
116
+ **Event Profiles:** `UX`, `OBSERVABILITY`, `DEBUG`, `TOOLS`, `STREAMING`
117
+
118
+ ### Gates (`src/gates/`)
119
+
120
+ Gate chain intercepts tool call intents for safety, approval, rate limiting, and auditing.
121
+
122
+ ```typescript
123
+ import { AgentEventBus, SafetyGate, HumanApprovalGate, AuditGate } from "@agentic-patterns/runtime";
124
+
125
+ const bus = new AgentEventBus();
126
+
127
+ // Gates are checked in category order: SAFETY -> RATE_LIMIT -> APPROVAL -> AUDIT
128
+ bus.addGate(new SafetyGate(["rm", "drop_table"]));
129
+ bus.addGate(new HumanApprovalGate(async (event) => {
130
+ return confirm(`Allow ${event.toolName}?`);
131
+ }));
132
+ bus.addGate(new AuditGate((event) => auditLog.append(event)));
133
+ ```
134
+
135
+ Gate types: `SafetyGate`, `HumanApprovalGate`, `RateLimitGate`, `AuditGate`
136
+
137
+ ### Workflows (`src/workflows/`)
138
+
139
+ Composable workflow patterns for multi-step and iterative agent execution. All patterns implement `PatternProtocol` and share common types.
140
+
141
+ #### Base Types
142
+
143
+ - `PatternContext` -- shared context (`Record<string, unknown>`) threaded through steps
144
+ - `MessageTemplate` -- static string or `(context) => string` function
145
+ - `Step` -- agent + message template + optional output key + context extractor
146
+ - `StepResult` -- execution result with `.content` accessor
147
+ - `PatternResult` -- interface: totalInputTokens, totalOutputTokens, succeeded, finalContent
148
+ - `PatternEvent` -- discriminated union: start, step.start, step.complete, step.error, iteration.start, iteration.complete, complete
149
+ - `PatternHooks` -- callbacks for pattern lifecycle events
150
+ - `GoalEvaluatorProtocol` -- `evaluate()` returning `[achieved, reason, confident]`
151
+
152
+ Helpers: `resolveMessage()`, `makeStepName()`, `executeStep()`
153
+
154
+ #### Sequential
155
+
156
+ Chain agents in sequence, threading context through the pipeline.
157
+
158
+ ```typescript
159
+ import { Sequential } from "@agentic-patterns/runtime";
160
+
161
+ const pipeline = new Sequential([
162
+ { agent: researcher, messageTemplate: "Research the topic", outputKey: "research" },
163
+ { agent: writer, messageTemplate: (ctx) => `Write about: ${ctx.research}` },
164
+ ], { continueOnError: false });
165
+
166
+ const result = await pipeline.run({ topic: "AI" }, { runner });
167
+ result.steps; // StepResult[]
168
+ result.finalContext; // accumulated context
169
+ ```
170
+
171
+ Supports nested patterns (Sequential/Parallel as steps) and `continueOnError`.
172
+
173
+ #### Parallel
174
+
175
+ Fan-out agents in parallel with optional concurrency limiting and result consolidation.
176
+
177
+ ```typescript
178
+ import { Parallel, collectByName, collectContents } from "@agentic-patterns/runtime";
179
+
180
+ const fanout = new Parallel(
181
+ [
182
+ { agent: analystA, messageTemplate: "Analyze market", name: "market" },
183
+ { agent: analystB, messageTemplate: "Analyze tech", name: "tech" },
184
+ ],
185
+ { maxConcurrency: 2, consolidator: collectByName },
186
+ );
187
+
188
+ const result = await fanout.run({}, { runner });
189
+ result.successful; // StepResult[]
190
+ result.failed; // [index, Error][]
191
+ result.allSucceeded; // boolean
192
+ result.consolidatedOutput; // { market: "...", tech: "..." }
193
+ ```
194
+
195
+ Built-in consolidators: `collectContents` (string[]), `collectByName` (Record). Custom consolidators accepted.
196
+
197
+ #### Goal Evaluators
198
+
199
+ Four implementations of `GoalEvaluatorProtocol`, ranked cheapest to most expensive:
200
+
201
+ | Evaluator | Strategy | LLM? |
202
+ |-----------|----------|------|
203
+ | `SimpleGoalEvaluator` | Pattern matching against output | No |
204
+ | `SelfEvalGoalEvaluator` | Parses `GOAL_STATUS`/`PROGRESS` markers | No |
205
+ | `LLMGoalEvaluator` | Sends goal + result to evaluator agent | Yes |
206
+ | `EvaluatorChain` | Tries in order, stops on first confident result | Mixed |
207
+
208
+ All return `[achieved: boolean, reason: string, confident: boolean]`.
209
+
210
+ ```typescript
211
+ import { EvaluatorChain, SimpleGoalEvaluator, LLMGoalEvaluator } from "@agentic-patterns/runtime";
212
+
213
+ const chain = new EvaluatorChain([
214
+ new SimpleGoalEvaluator({ successPatterns: ["TASK_COMPLETE"] }),
215
+ new LLMGoalEvaluator({ agent: evaluatorAgent, runner }),
216
+ ]);
217
+ ```
218
+
219
+ #### TaskLoop
220
+
221
+ Goal-driven iteration: run agent, evaluate progress, repeat.
222
+
223
+ ```typescript
224
+ import { TaskLoop, SimpleGoalEvaluator } from "@agentic-patterns/runtime";
225
+
226
+ const loop = new TaskLoop(agent, new SimpleGoalEvaluator({
227
+ successPatterns: ["TASK_COMPLETE"],
228
+ failurePatterns: ["CANNOT_PROCEED"],
229
+ }), { maxIterations: 5 });
230
+
231
+ const result = await loop.run("Fix all failing tests", {}, { runner });
232
+ result.exitReason; // "goal_achieved" | "max_iterations" | "explicit_stop" | "error"
233
+ result.iterations; // number of iterations executed
234
+ ```
235
+
236
+ Features: history summarization in prompts, configurable stop phrases, goal evaluation per iteration.
237
+
238
+ #### EvaluatorLoop
239
+
240
+ Producer-evaluator refinement: producer generates, evaluator scores + critiques, producer refines.
241
+
242
+ ```typescript
243
+ import { EvaluatorLoop, RubricEvaluator, CompositeRefinementEvaluator } from "@agentic-patterns/runtime";
244
+
245
+ const rubric = new RubricEvaluator([
246
+ { name: "clarity", description: "Clear and concise", weight: 0.4 },
247
+ { name: "accuracy", description: "Factually correct", weight: 0.6 },
248
+ ], { runner });
249
+
250
+ const loop = new EvaluatorLoop(producer, rubric, {
251
+ maxRefinements: 3,
252
+ qualityThreshold: 0.8,
253
+ });
254
+
255
+ const result = await loop.run("Write a blog post about RAG");
256
+ result.exitReason; // "quality_met" | "max_refinements" | "no_improvement" | "error"
257
+ result.bestOutput; // highest-scoring version across all refinements
258
+ ```
259
+
260
+ Evaluator implementations: `LLMRefinementEvaluator`, `RubricEvaluator`, `CompositeRefinementEvaluator` (weighted average).
261
+
262
+ #### RetryLoop
263
+
264
+ Generic async retry wrapper. Not agent-specific -- wraps any `() => Promise<T>`.
265
+
266
+ ```typescript
267
+ import { RetryLoop, ExponentialBackoff, JitteredBackoff, FixedBackoff } from "@agentic-patterns/runtime";
268
+
269
+ const retry = new RetryLoop({
270
+ maxAttempts: 5,
271
+ backoff: new ExponentialBackoff({ initialMs: 100, maxMs: 5000 }),
272
+ retryableErrors: [RateLimitError],
273
+ timeoutMs: 30_000,
274
+ onRetry: (attempt, error) => console.log(`Retry ${attempt}: ${error.message}`),
275
+ });
276
+
277
+ const result = await retry.run(() => callExternalAPI());
278
+ ```
279
+
280
+ Backoff strategies: `FixedBackoff`, `ExponentialBackoff`, `JitteredBackoff`.
281
+
282
+ #### ConversationLoop
283
+
284
+ Multi-turn conversation orchestration with external input/output callbacks.
285
+
286
+ ```typescript
287
+ import { ConversationLoop } from "@agentic-patterns/runtime";
288
+
289
+ const loop = new ConversationLoop(agent, {
290
+ maxExchanges: 10,
291
+ exitPhrases: ["goodbye", "exit"],
292
+ inputFn: async () => getUserInput(),
293
+ outputFn: async (response) => displayToUser(response),
294
+ });
295
+
296
+ const result = await loop.run({ runner });
297
+ result.exitReason; // "exit_phrase" | "max_exchanges" | "error"
298
+ result.exchangeCount; // number of exchanges completed
299
+ ```
300
+
301
+ Integrates with `ConversationStoreProtocol` for persistence via `MemoryStore`.
302
+
303
+ ### Transport (`src/transport/`)
304
+
305
+ Message transport for multi-agent communication.
306
+
307
+ ```typescript
308
+ import { InProcessTransport, MessagingToolbox } from "@agentic-patterns/runtime";
309
+
310
+ const transport = new InProcessTransport();
311
+ transport.subscribe("agency.*.messages", (msg) => { /* ... */ });
312
+ await transport.publish("agency.worker.messages", payload);
313
+
314
+ const toolbox = new MessagingToolbox(transport, senderAddress, agency);
315
+ ```
316
+
317
+ `Transport` interface can be implemented for external systems (NATS, Redis, etc.).
318
+
319
+ ### Runtime (`src/runtime/`)
320
+
321
+ Multi-agent execution runtime.
322
+
323
+ ```typescript
324
+ import { Agency } from "@agentic-patterns/core";
325
+ import { AgencyRuntime, AgentNode } from "@agentic-patterns/runtime";
326
+
327
+ const runtime = new AgencyRuntime(agency, runner, "run-123");
328
+ await runtime.start();
329
+ await runtime.injectCoordinator("Begin processing");
330
+ const status = runtime.status(); // { coordinator: "running", worker: "running" }
331
+ await runtime.stop();
332
+ ```
333
+
334
+ `AgentNode` wraps a single agent with message batching, idle timeout, max turns, and lifecycle events.
335
+
336
+ ### Conversation (`src/conversation/`)
337
+
338
+ Conversation state management with structured persistence.
339
+
340
+ ```typescript
341
+ import { Conversation, MemoryStore } from "@agentic-patterns/runtime";
342
+
343
+ // In-memory persistence
344
+ const store = new MemoryStore();
345
+
346
+ const convo = new Conversation("conv-123", "agent-name", { store });
347
+ convo.addExchange({
348
+ userMessage: "Hello",
349
+ assistantMessage: "Hi there!",
350
+ inputTokens: 10,
351
+ outputTokens: 8,
352
+ toolCalls: [],
353
+ timestamp: new Date().toISOString(),
354
+ });
355
+ ```
356
+
357
+ `ConversationStoreProtocol` provides full CRUD for conversations, messages, and message parts:
358
+ - `createConversation()`, `getConversation()`, `updateConversation()`
359
+ - `addMessage()`, `getMessages()`, `getMessageParts()`
360
+
361
+ `MemoryStore` is the built-in in-memory implementation. Implement `ConversationStoreProtocol` for database-backed persistence.
362
+
363
+ ### Exporters (`src/exporters/`)
364
+
365
+ Observability exporters that subscribe to EventBus events.
366
+
367
+ | Exporter | Output |
368
+ |----------|--------|
369
+ | `ConsoleExporter` | Terminal output via configurable logger |
370
+ | `LangfuseExporter` | Langfuse trace spans |
371
+ | `OTelExporter` | OpenTelemetry trace spans |
372
+
373
+ ```typescript
374
+ import { ConsoleExporter, createConsoleExporter } from "@agentic-patterns/runtime";
375
+
376
+ const exporter = createConsoleExporter(bus);
377
+ exporter.start();
378
+ // ... run agent ...
379
+ exporter.stop();
380
+ ```
381
+
382
+ All exporters extend `BaseExporter` which manages EventBus profile subscription lifecycle.
383
+
384
+ ### Presets (`src/presets/`)
385
+
386
+ Pre-built roles, judgments, and responsibilities for common agent patterns.
387
+
388
+ **Roles:**
389
+ - `coordinatorRole(name, persona)` -- orchestration coordinator
390
+ - `orchestratorRole(name, persona)` -- task orchestrator
391
+ - `analystRole(name, persona)` -- research/analysis specialist
392
+ - `retrievalRole(name, persona)` -- information retrieval specialist
393
+
394
+ **Judgments:** `ROUTING`, `QUALITY_REVIEW`, `INTENT_CLASSIFICATION`, `RETRIEVAL_STRATEGY`, `EVIDENCE_QUALITY`
395
+
396
+ **Responsibilities:** `ORCHESTRATION`, `QUALITY_GATE`, `INTENT_ROUTING`, `RESPONSE_SYNTHESIS`, `INFORMATION_RETRIEVAL`, `ANALYSIS`
397
+
398
+ ```typescript
399
+ import { coordinatorRole, ROUTING, ORCHESTRATION } from "@agentic-patterns/runtime";
400
+
401
+ const role = coordinatorRole("lead", persona);
402
+ ```