Layered dependencies
src/ is split into six layers (L0–L5). Dependencies flow upward only. An import from a higher layer into a lower one is not allowed and is caught at review time.
| Layer | Modules | Role |
|---|---|---|
| L0 | contracts/, config/, utils/ | Cross-boundary types, IDs, TOML schema |
| L1 | core/memory/, core/persona/, core/providers/, core/sessions/, core/subagents/, core/experience/, core/eval/ | Durable state, identity, provider abstraction, shared runtime domain |
| L2 | core/tools/, security/ | Tool system, approval, policy, taint, governance |
| L3 | core/agent/, core/affect/, media/ | Turn executor, affect detection, multimodal processing |
| L4 | runtime/services/, runtime/diagnostics/, runtime/observability/ | Composition root, DI, telemetry |
| L5 | transport/, cli/, platform/, plugins/, ui/, onboard/ | Gateway, channels, CLI, desktop plugins |
What the layering buys
Section titled “What the layering buys”The layering exists to protect one property: continuity state (L1) does not know what surface is reading it.
- Memory, persona, and sessions never import from
transport/orcli/. They cannot be accidentally coupled to “the shape of a Discord message” or “the shape of an HTTP request”. - Core agent logic (L3) cannot skip past the security / tool layer (L2) to talk directly to a channel.
- Transports (L5) are free to change — a new channel, a desktop panel, a new gateway route — without touching the state they display.
If any of those barriers were porous, the runtime would be one refactor away from Discord-shaped memory or gateway-flavored persona. The layering is what lets the shared turn pipeline stay honest.
The composition root
Section titled “The composition root”Everything is wired together in src/runtime/services/. That module:
- Initializes auth, security policy, memory, the rate limiter
- Builds the provider (wrapped in reliability and OAuth recovery layers)
- Assembles the tool registry
- Constructs the
ExecutionContextpassed into every tool call
The composition root is the only place that knows about all the pieces at once. Every other module sees a narrow slice.
Key trait surfaces
Section titled “Key trait surfaces”Three traits carry most of the weight at module boundaries:
Memory— a supertrait combiningMemoryWriter(append_event),MemoryReader(recall_scoped,resolve_slot), andMemoryGovernance(health_check,forget_slot). Backends: PostgreSQL (default) or Markdown fallback.Tool—name(),description(),parameters_schema(),execute(args, ctx). Registered incore/tools/registry.rs.Provider— single required methodchat_with_system(). Wrapped byReliableProvider(retry + circuit-breaker) andOAuthRecoveryProvider. Implementations: Anthropic, OpenAI, OpenRouter, Ollama, Gemini.
These three are the contract across which most cross-layer communication happens. If you are tracing a change and you land on one of these, you have found the right seam.
Experimental code
Section titled “Experimental code”Experimental or retired code is not part of the production layering contract unless it is exported by the active module tree and covered by the architecture checks. The current companion runtime is owned by the layers above; old planner, simulation, and evolution surfaces are not product-bearing entrypoints.