Primitives Overview
Nerva provides 8 primitives. Each is a Protocol (Python), interface (TypeScript), or interface (Go) with one or more default implementations. Use one, use all, replace any.
The Primitives
0. ExecContext — The Connective Tissue
Every method in every primitive receives an ExecContext. It carries:
- Identity — request ID, trace ID, user ID, session ID
- Permissions — what this user/agent can access
- Memory scope — user, session, agent, or global
- Observability — trace spans, structured events, token usage
- Lifecycle — creation time, timeout, cooperative cancellation
- Streaming — optional stream sink for real-time token delivery
Without it, you end up passing 6 separate arguments to every function, or building a god-object later. Every project that starts without this adds it in v2.
from nerva import ExecContext
ctx = ExecContext.create( user_id="user_123", session_id="session_abc", memory_scope="session", timeout_seconds=30,)import { ExecContext } from "nerva";
const ctx = ExecContext.create({ userId: "user_123", sessionId: "session_abc", memoryScope: "session", timeoutSeconds: 30,});import nctx "github.com/otomus/nerva/go/context"
ctx := nctx.NewContext( nctx.WithUserID("user_123"), nctx.WithSessionID("session_abc"), nctx.WithMemoryScope(nctx.ScopeSession), nctx.WithTimeout(30),)1. Router — Intent Classification
Takes a raw message and returns a structured intent with confidence score and matched handler.
Provided strategies:
| Strategy | How it works | Trade-off |
|---|---|---|
RuleRouter | Regex/keyword matching | Fast, deterministic, no LLM cost |
EmbeddingRouter | Cosine similarity against handler descriptions | Fast, no LLM call, needs embedding model |
LLMRouter | Structured output from LLM with handler catalog | Accurate, slower, costs tokens |
HybridRouter | Embedding pre-filter then LLM re-rank | Best accuracy, moderate cost |
from nerva.router.rule import RuleRouter, Rule
router = RuleRouter( rules=[ Rule(pattern=r"weather", handler="weather_agent", intent="weather"), Rule(pattern=r"calendar|schedule", handler="calendar_agent", intent="calendar"), ], default_handler="general_agent",)
result = await router.classify("What's the weather?", ctx)# intent="weather", confidence=1.0, handler="weather_agent"import { RuleRouter } from "nerva/router/rule";
const router = new RuleRouter( [ { pattern: "weather", handler: "weather_agent", intent: "weather" }, { pattern: "calendar|schedule", handler: "calendar_agent", intent: "calendar" }, ], "general_agent",);
const result = await router.classify("What's the weather?", ctx);// intent="weather", confidence=1.0, handler="weather_agent"import "github.com/otomus/nerva/go/router"
r, err := router.NewRuleRouter( []router.Rule{ {Pattern: "weather", Handler: "weather_agent", Intent: "weather"}, {Pattern: "calendar|schedule", Handler: "calendar_agent", Intent: "calendar"}, }, "general_agent",)
result, err := r.Classify(ctx, "What's the weather?")// Intent="weather", Confidence=1.0, Handler="weather_agent"2. Runtime — Agent Execution
Executes agent code in isolation, manages lifecycle, collects structured output.
Provided strategies:
| Strategy | Isolation | Use case |
|---|---|---|
InProcessRuntime | None — runs in main process | Development, simple agents |
SubprocessRuntime | Process boundary | Production, untrusted agents |
ContainerRuntime | Docker/Firecracker | Maximum isolation |
Built-in: timeout, circuit breaker, structured output parsing, error classification, streaming.
3. Tools — Discovery and Invocation
Discovers tools, sandboxes their execution, injects them into agent context.
Provided strategies:
| Strategy | Source | Use case |
|---|---|---|
FunctionToolManager | Plain functions | Simple tools, testing |
MCPToolManager | MCP protocol servers | Production, sandboxed tools |
CompositeToolManager | Multiple sources combined | Mixed environments |
Built-in: permission model, sandboxing, connection pooling, schema validation, result size limits.
4. Memory — Tiered Context Storage
Agents read from and write to tiered memory with automatic lifecycle management.
Tiers:
| Tier | Scope | Storage |
|---|---|---|
| Hot | Current session — conversation + working state | In-memory / Redis |
| Warm | Recent episodes and extracted facts | Key-value store |
| Cold | Long-term searchable knowledge | Vector DB |
Built-in: episode extraction, fact deduplication, tier promotion/demotion, token budget fitting, scope isolation.
5. Responder — Output Formatting
Takes raw agent output and formats it for the target channel.
Provided strategies:
| Strategy | Behavior |
|---|---|
PassthroughResponder | Returns raw output (APIs, programmatic consumers) |
ToneResponder | Rewrites output with personality/tone |
MultimodalResponder | Attaches media, cards, buttons based on channel capabilities |
6. Registry — Component Catalog
Unified catalog of everything in the system. Every other primitive queries it instead of implementing its own discovery.
Provided backends:
| Backend | Persistence | Use case |
|---|---|---|
InMemoryRegistry | None | Testing, simple deployments |
SqliteRegistry | Disk | Single-node production |
RedisRegistry | Distributed | Multi-node clusters |
7. Policy — Governance
Layered rules that govern execution. Declarative defaults in config, per-agent overrides via decorators, runtime adaptation.
Provided strategies:
| Strategy | Behavior |
|---|---|
NoopPolicyEngine | Allow everything (development/testing) |
YamlPolicyEngine | Load policies from nerva.yaml, evaluate at runtime |
AdaptivePolicyEngine | Extends YAML engine with runtime condition monitoring |
Policy covers: rate limits, cost budgets, approval gates, safety screening, execution depth limits, circuit breakers.
Composition
The Orchestrator wires all 8 primitives together and threads ExecContext through the pipeline:
Message -> ExecContext -> Policy -> Router -> Runtime (Tools + Memory) -> Responder -> OutputBut you do not need the Orchestrator. Use any primitive standalone, compose a subset, or build your own orchestration logic.