Technical Architecture Deep-Dive
Composition All
the Way Down
How markdown files become AI agent systems —
the architecture of amplifier-foundation
Active — amplifier-foundation
February 2026
The Foundation Layer
8,645 lines of composition intelligence
amplifier-foundation is the library that turns 11-line YAML bundles into running AI agents.
Without it, every application would need hundreds of lines of wiring code.
📦
Bundle Loading
load_bundle() — parses YAML front-matter + markdown body into the Bundle dataclass
🔗
Composition Engine
Bundle.compose() — merges bundles with 5 distinct strategies per field type
📎
@mention Resolution
Namespace-aware file references with recursive loading, SHA-256 dedup, max depth 3
⬇️
Module Activation
ModuleActivator — downloads modules from source URIs to local cache, installs deps
🌱
Spawn Utilities
Create child sessions with context inheritance, provider preferences, and session resume
🏭
PreparedBundle Factory
The expensive one-time step: activate all modules, build resolver, then create sessions cheaply
LIBRARY CORE
8,645 LOC
TOTAL WITH MODULES
22,996 LOC
PYTHON FILES
97
Data Structure
The Bundle dataclass — 14 fields in 4 groups
@dataclass
class Bundle:
name: str
version: str = "1.0.0"
description: str = ""
includes: list[str] = []
session: dict[str, Any] = {}
providers: list[dict] = []
tools: list[dict] = []
hooks: list[dict] = []
spawn: dict[str, Any] = {}
agents: dict[str, dict] = {}
context: dict[str, Path] = {}
instruction: str | None = None
base_path: Path | None = None
source_base_paths: dict[str, Path] = {}
Plus _pending_context for deferred namespace resolution.
The source_base_paths dict is the key to multi-bundle @mention resolution —
it maps every composed namespace to its filesystem root.
Composition Semantics
Bundle.compose(*others) — 5 merge strategies
Returns a new Bundle. The original is never mutated. Later bundles win for conflicts.
| Field |
Strategy |
Effect |
| session |
deep_merge() |
Nested dicts merged recursively; scalars: later wins |
| spawn |
deep_merge() |
Same recursive merge as session |
| providers |
merge_module_lists() |
Index by module key; deep merge per module ID |
| tools |
merge_module_lists() |
Same — matching modules merge, new modules append |
| hooks |
merge_module_lists() |
Same — preserves module identity across compositions |
| agents |
dict.update() |
Later overrides entirely by agent name |
| context |
accumulate |
Namespaced with bundle_name: prefix — no collision |
| instruction |
replace |
Later replaces earlier entirely |
| source_base_paths |
accumulate |
First write wins per namespace; never overwrites |
Lists are NOT merged by deep_merge — child replaces parent. Module lists use a special
merge_module_lists() that indexes by the 'module' key for identity-preserving merges.
The Prepare Step
Bundle.prepare() — the expensive step, done once
Generate mount plan via bundle.to_mount_plan() — raw config dict
Create ModuleActivator with install_deps flag and base_path
Install bundle's own package via uv pip install (pyproject.toml) — critical: before modules
Install included bundles' packages from all source_base_paths
Collect ALL module specs across providers, tools, hooks, orchestrator, context, and agents
activator.activate_all() — download each module from source URI to local cache
activator.finalize() — persist install state for fast subsequent startups
Return PreparedBundle with resolver + mount plan + bundle_package_paths
@dataclass
class PreparedBundle:
mount_plan: dict[str, Any]
resolver: BundleModuleResolver
bundle: Bundle
bundle_package_paths: list[str]
Key insight
prepare() is expensive (network I/O, package installs). But it's done once.
After that, create_session() and spawn()
are cheap — they reuse the resolver.
From Prepared to Session
PreparedBundle.create_session()
From 11 lines of YAML to a running agent loop. This is the wiring that
foundation does so you don't have to.
AmplifierSession(mount_plan) — instantiate the kernel session with the prepared config
Mount BundleModuleResolver at module-source-resolver capability slot
Register bundle_package_paths capability for child session inheritance
Register session.working_dir — the filesystem root for @mention resolution
session.initialize() — kernel loads all modules from mount plan
Resolve pending context refs — deferred namespaced @mentions now have source_base_paths
Register mention_resolver + deduplicator as session capabilities
Create system prompt factory — re-reads all @mentioned files every turn (dynamic!)
Register factory with context manager — or fallback to pre-resolved static prompt
Dynamic system prompts
The system prompt factory is re-called on every get_messages_for_request().
Files are re-read fresh each turn. Edit AGENTS.md mid-session and changes are picked up immediately.
Context Sink Architecture
40+ agents, zero context poisoning
Heavy documentation loads only when the expert spawns. The root session never sees it.
Context flows through a two-parameter model: depth × scope.
Root
"bundle system exists, delegate to foundation-expert"
~30 lines
↓ spawn()
Expert
@foundation:docs/BUNDLE_GUIDE.md, PATTERNS.md ...
15,000+ tok
↓ return
Result
Distilled answer — structured summary
~500 tok
Depth — how much history
none — clean slate
recent — last N turns (default 5)
all — full conversation
Scope — which content
conversation — text only
agents — + delegate results
full — + all tool results
3 depth options × 3 scope options = 9 context inheritance strategies.
Messages >2,000 chars are truncated. Tool use/result blocks are stripped in non-full scopes.
Namespace Resolution
How @mentions resolve — a 3-layer pipeline
parse_mentions(text) → list[str]
class BaseMentionResolver:
load_mentions(text, resolver, dedup,
relative_to, max_depth=3)
Namespace rules
Namespace comes from bundle.name, always.
Not the repo name, not the directory. Root bundles establish namespaces;
composed bundles share the parent's.
YAML vs Markdown syntax
YAML sections: no @ prefix
my-bundle:context/guide.md
Markdown body: @ prefix required
@my-bundle:context/guide.md
Opposite semantics, intentionally
context.include propagates upward (parent sees it).
@mentions stay local (only the loading session sees the content).
Spawn — Agent Delegation
PreparedBundle.spawn()
async def spawn(self,
child_bundle: Bundle,
instruction: str,
*,
compose: bool = True,
parent_session: Any = None,
session_id: str = None,
orchestrator_config: dict = None,
parent_messages: list = None,
provider_preferences: list = None,
self_delegation_depth:int = 0,
) → dict[str, Any]:
Compose child with parent bundle (inherits parent's tools)
Generate child mount plan + merge orchestrator config
Apply provider preferences with glob pattern resolution
Create AmplifierSession with parent_id linking
Mount resolver, register working_dir, initialize
Inject parent_messages via context manager (new sessions only)
Register system prompt factory for child
Hook orchestrator:complete to capture metadata
session.execute(instruction) — the agent does its work
Return result dict — session_id enables resume
The Three-Layer Architecture
Why this works
Bundles — User Intent
11-line YAML files. What agents to include, what behaviors to compose.
Thin, declarative, composed via includes:
Foundation — Composition Intelligence
8,645 LOC. Bundle loading, compose(), prepare(), spawn(), @mentions, activation.
The library that makes 11-line bundles possible.
Kernel — Mechanisms Only
4,492 LOC. Session lifecycle, module loading, event dispatch, coordinator.
Five protocols. Zero opinions about composition.
8,645
LOC foundation core
The foundation layer is the secret weapon. Like a programming language's standard library —
not part of the spec, but essential to making the language usable.
Structural Isomorphism
The design system parallel
Both systems solve the same problem: composable, constrained, semantically meaningful
primitives enabling diverse creators to build consistent, high-quality experiences.
Protocol types
≅
Design tokens
Configured modules
≅
Semantic tokens
Behaviors
≅
Design patterns
.amplifier/settings.yaml
≅
Themes
Running session
≅
Rendered application
Atomic primitives → meaningful mappings → reusable compositions → complete units →
environmental application → running output. The pattern is fractal.
Sources
Research Methodology
Data as of: February 26, 2026
Feature status: Active
Research performed:
- Line counts: find amplifier_foundation/ -name "*.py" | xargs wc -l (8,645 LOC library core; 22,996 total across 97 files)
- Kernel LOC: find amplifier_core/ -name "*.py" | xargs wc -l (4,492 LOC production; 12,865 total across 57 files)
- Bundle dataclass: bundle.py lines 29–81 (14 fields verified)
- compose() method: bundle.py lines 98–213; merge.py (deep_merge, merge_module_lists)
- prepare() method: bundle.py lines 245–383
- PreparedBundle class: bundle.py lines 890–909
- create_session(): bundle.py lines 1,030–1,158
- spawn() method: bundle.py lines 1,160–1,377
- @mention system: mentions/parser.py, resolver.py, loader.py, models.py
- Minimal bundles: wc -l behaviors/*.yaml (11-line smallest verified)
- Context inheritance: tool-delegate/__init__.py lines 564–641 (depth × scope model)
Repositories: amplifier-foundation (local), amplifier-core (local submodule)
Gaps: Production session counts and performance benchmarks not available in source code. Community bundle count not surveyed.
Composition All the Way Down
Three layers. Two libraries.
Five protocols. Eleven lines of YAML.
Infinite possibility.
That's composition all the way down.
Bundles
Foundation
Kernel
More Amplifier Stories