# Registry The **registry** (`src/registry/`) is TinyAgents' named capability catalog — the third of the five surfaces in the [Architecture](Architecture.md). It is the piece that makes recursion *resolvable*: every model, tool, graph, router, and reducer is registered under a **name**, and the declarative `.rag` and imperative `.ragsh` languages bind to those names rather than to Rust types. That indirection is exactly what lets a model spawn a sub-agent or sub-graph it never hardcoded — it references a name, and the registry resolves it to a real, allow-listed capability. See [Recursion and RLM](Recursion-and-RLM.md). The registry is deliberately **explicit**. It is not a hidden global runtime: you own one per application, test, tenant, workspace, or server. It does not execute graph nodes or call providers — it answers "which capabilities exist, under what names, and is this name allowed?" ## What the registry holds The implemented core has three submodules: - `registry::component` — identity and discovery types. - `registry::capability` — the name-addressable `CapabilityRegistry`. - `registry::catalog` — the offline model catalog (prices, context windows, capabilities). ### Component identity (`registry::component`) Three types describe *what* a registered capability is, independent of any live handle (they serialize, diff, and render in a UI long after the registering process exits): - `ComponentId(String)` — a stable, durable identifier. It is a newtype over the registered **name** (e.g. `"gpt-4o"`, `"lookup_user"`), not a Rust type path, so a capability survives module moves, renames, and serialization. - `ComponentKind` — the kind that *partitions the namespace*. Lookups, duplicate detection, aliasing, and discovery are all scoped by kind, so `(Model, "x")` and `(Tool, "x")` are independent entries. Variants: | Kind | Stores | Status | | --- | --- | --- | | `Model` | `Arc>` | executable handle | | `Tool` | `Arc>` | executable handle | | `Graph` | `Blueprint` (a `.rag` graph) | serializable value | | `Router` | name-only descriptor | presence only | | `Reducer` | name-only descriptor | presence only | | `Store` | name-only descriptor | reserved | | `Agent` | name-only descriptor | reserved | - `ComponentMetadata { id, kind, description, tags, aliases }` — discovery / UI metadata. Every registration records one (even name-only ones, where the description and tags start empty); it is the **source of truth for presence**. > The broader registry *spec* (`docs/modules/registry/README.md`) anticipates > first-class stores, middleware, listeners, and an event bus. Today those are > reserved name-only kinds or live in the harness; document and use what exists. ### The capability registry (`registry::capability`) `CapabilityRegistry` is the name-addressable catalog. It is generic over the application `State` because models and tools are generic over it; `State = ()` is the common stateless default. Storage is partitioned by kind: models and tools keep executable handles, graphs keep a serializable `Blueprint`, and routers/reducers (plus the reserved store/agent kinds) are name-only descriptors. The `meta` map — keyed by `(kind, canonical name)` — is the uniform presence index behind `has` and `names`. It is intentionally **distinct** from the harness' per-run `ModelRegistry` and `ToolRegistry` (which are executable stores). The `CapabilityRegistry` is the *catalog* that declarative source is validated against. **Registering capabilities:** ```rust let mut reg = CapabilityRegistry::<()>::new(); reg.register_model("gpt-4o", model)?; // duplicate name -> error reg.register_tool(tool)?; // keyed by Tool::name() reg.register_graph_blueprint("triage", blueprint)?; reg.register_router("route_by_intent")?; // name-only descriptor reg.register_reducer("append_messages")?; // name-only descriptor reg.alias(ComponentKind::Model, "fast", "gpt-4o")?; ``` - `register_*` fails with `TinyAgentsError::DuplicateComponent` if the `(kind, name)` pair already exists; the `replace_*` variants overwrite while preserving any richer metadata already attached. - `register_descriptor(kind, name)` backs the name-only kinds and is exposed for the reserved `Store` / `Agent` kinds. - `alias(kind, alias, target)` declares an alternate name; it validates the target exists and the alias is free, then records the alias on the target's metadata for discovery. **Looking things up:** `model(name)`, `tool(name)`, and `graph_blueprint(name)` resolve a name *or one alias hop* to a live handle/blueprint via `resolve_name`. `has(kind, name)` answers presence, `names(kind)` lists canonical names (sorted), and `names_including_aliases(kind)` lists canonical names *and* aliases — the exact set declarative source may reference. `metadata(kind, name)` returns the `ComponentMetadata`. **Handing off to the runtime:** - `to_model_registry()` builds a harness `ModelRegistry` from the registered models, binding alias names to the same handle. - `to_tool_registry()` builds a harness `ToolRegistry` (tools are keyed by their own schema `Tool::name()`, so tool-level aliases are intentionally not propagated — a tool is always invoked under its canonical name). - `capability_resolver()` builds the language layer's `CapabilityResolver` — the bridge described below. ## Binding `.rag` / `.ragsh` capabilities by name This is the registry's reason to exist. `CapabilityResolver::from_registry(reg)` (in `language::compiler`) snapshots `names_including_aliases` for every kind into allow-lists: ```rust Self { models: reg.names_including_aliases(Model), tools: reg.names_including_aliases(Tool), subgraphs: reg.names_including_aliases(Graph), routers: reg.names_including_aliases(Router), reducers: reg.names_including_aliases(Reducer), node_kinds: DEFAULT_NODE_KINDS, // model, tool, ... } ``` When a `.rag` blueprint or a `.ragsh` program names a model, tool, subgraph, router, or reducer, the compiler checks it against these allow-lists (`bind_blueprint`, `bind_capabilities_with_registry`). An unknown reference is a compile error — *before* anything runs. This is what makes **agent-authored** source safe: a model can emit a `.rag` blueprint, but it can only wire together capabilities a human already registered and allowed. Because graphs are registered as `Graph` components, a blueprint can reference another blueprint by name as a **subgraph** — the registry is where graph-runs-graph recursion gets its names. The resolver also supports incremental `allow_model` / `allow_tool` / `allow_subgraph` / `allow_router` / `allow_reducer` extensions for tests and controlled escalation. ```mermaid flowchart LR Rust[Rust: register_* by name] --> Reg[CapabilityRegistry] Reg -->|names_including_aliases| Res[CapabilityResolver] Rag[".rag / .ragsh source"] -->|references names| Res Res -->|validated| Graph[Graph + Harness runtime] ``` ## The model catalog (`registry::catalog`) The model catalog is an **offline snapshot** of provider model metadata — not a live source of truth. It gives TinyAgents deterministic behavior for cost estimates, context-window checks, capability discovery, model-picker UIs, tests, and request validation *before* a provider call. A checked-in seed snapshot (`docs/modules/registry/model-catalog.snapshot.json`) is compiled into the crate. - `ModelCatalog::seed()` loads the bundled snapshot; `from_json` / `from_snapshot` load a custom one. `get(provider, model_id)` and `get_by_model_id(model_id)` resolve an entry by exact id or alias. - `ModelCatalogSnapshot` carries `schema_version`, `snapshot_id`, `created_at`, `currency`, `unit`, provenance `sources` (name / url / `retrieved_at`), and the `models` list. - `ModelCatalogEntry` describes one model: `provider`, `model_id`, `aliases`, `mode`, `max_input_tokens` / `max_output_tokens`, optional `deprecation_date`, `pricing`, `capabilities`, and per-row `source` provenance. - `ModelPricing` gives per-token input/output prices plus cache-read, cache-creation, audio, and reasoning rates. - `ModelCapabilities` is the feature matrix: `streaming`, `tool_calling`, `parallel_tool_calling`, `json_schema`, `system_messages`, `vision`, `audio_input` / `audio_output`, `pdf_input`, `prompt_caching`, `reasoning`. The catalog is metadata only — it never calls a provider. See [Providers](Providers.md) for the live model surface and `docs/modules/registry/model-catalog.md` for snapshot provenance rules. ## When to use the registry Use a `CapabilityRegistry` whenever capabilities must be referenced **by name**: to compile and validate `.rag` blueprints or `.ragsh` programs, to let one agent or graph spawn another it did not hardcode, to power a discovery UI over available models/tools/graphs, or to hand a curated, allow-listed set of models and tools to a harness. Use the **model catalog** whenever you need offline cost or capability facts about a model without a network call. ## See also - [Architecture](Architecture.md) — the five surfaces and how they compose. - [Graph Runtime](Graph-Runtime.md) — what registered `Graph` blueprints become. - [Recursion and RLM](Recursion-and-RLM.md) — name-binding as the substrate for self-authoring and sub-agent/sub-graph recursion. - Module spec: `docs/modules/registry/README.md`, `design.md`, `model-catalog.md`.