Skip to main content
Define your entire agent team in a single YAML file. The Composer parses the spec and builds a live agent tree - no Python wiring needed.

Quick Start

orx.yaml

Architecture

The Composer is built around three modular registries under builders/:
Each registry is independently extensible — register a custom agent type, model provider, built-in tool, or whole new tool type without touching any other code.

YAML Schema

defaults

Global settings inherited by all agents.

models

Named model configurations. Agents reference them by name instead of repeating inline config.
Any key beyond provider, name, and temperature is forwarded directly to the LangChain model constructor (e.g. max_tokens, api_key, base_url, timeout, etc.). Agents reference models by name:
Or use inline model: for one-off overrides:

tools

Named tool definitions referenced by agents. Every tool entry must set exactly one of function, mcp, builtin, agent, transfer, or custom.
The custom: type is the escape hatch for third-party tool kinds. Register a resolver with register_tool_resolver("webhook", my_resolver) and the composer will dispatch custom.type == "webhook" entries to your callable. See Extending.

skills

Named skill definitions, referenced by agents. Each agent that lists skills gets its own in-memory skill store with list_skills and load_skill tools. Skills can be defined inline, loaded from a directory (Agent Skills Protocol), or fetched from a FastMCP server.
Directory skills are loaded via scan_skill_directory() and support the full 3-tier progressive disclosure model with load_skill_resource for on-demand resource files. See Skills for details. Agents reference skills by name:

agents

Flat dict of agent definitions. Agents reference each other by name.

Agent Types

The composer’s schema validator enforces the Required fields column at YAML-parse time:
  • a2a agents must set url.
  • sequential / parallel / loop agents must set a non-empty agents list.
It also emits warnings.warn for fields that are silently ignored by an agent type (e.g. tools on a composite, model on an a2a, planner on non-LLM types). Running with -W error turns those into hard failures.Custom agent types registered via register_builder are exempt from both checks — they’re free to consume any field.

main_agent

The entry-point agent name.

runner

Optional. Enables Composer.runner_from_yaml().
When compaction is set, the Runner automatically summarizes old session events after each invocation based on character count (not event count). Uses the default model for LLM-based summarization. See Session Compaction for details.

server

Optional. Enables Composer.server_from_yaml() for A2A.

Identity, trust, and attestation

Three optional blocks wire the trust layer into the Runner: sign every emitted event, verify incoming ones against a policy, and audit the whole stream through an attestation provider. Every block is opt-in — leave them out and the Runner behaves exactly as before.
Requires the auth extra:

identity: — Ed25519 signing key

Attaches a signing identity to every agent under the Runner. When set, every Event the tree emits is signed with Ed25519 over the canonical event payload (including prev_signature to form a hash chain), and verifiers downstream can prove the event hasn’t been tampered with.
Generate a key with the CLI:
Then either declare it in YAML as above, or pass it on the command line: orx orx.yaml --identity ./keys/agent.key.

trust: — signature verification policy

Installs TrustMiddleware on the Runner. Requires an identity: block (verification needs keys).
In strict mode, events that fail verification are dropped from the stream. In permissive mode they’re passed through with event.metadata["trust"] = {"verified": False, "reason": ...} so downstream consumers can flag them.

attestation: — claim issuance + audit log

Installs AttestationMiddleware on the Runner. Every event is appended to the provider’s audit log; notable actions (agent transfers, tool invocations) also produce typed claims.
Three provider flavors ship in-box:
  • noop — records nothing. Matches the default when no attestation: block is present.
  • local — JSON-on-disk at path, SHA-256 hash-chained, every entry signed with the identity: key. Zero external deps.
  • <dotted.import.path> — anything else is treated as an import path to a user-supplied AttestationProvider. Plug in a vendor SDK, a blockchain anchor, or your own implementation.
See the AttestationProvider protocol for the four-method interface your adapter needs to satisfy: issue_claim, verify_claim, append_audit, revoke.

Examples

Transfer Routing

Sequential Pipeline

Loop with Exit

Extending with Registries

Custom Agent Types

Then in YAML:
The build function receives (name, agent_def, spec, *, helpers) where helpers provides:
  • helpers.resolve_model(agent_def) - merge agent/default model config
  • helpers.resolve_tools(agent_def) - resolve all tool references
  • helpers.build_agent(name) - recursively build a sub-agent by name

Custom Model Providers

Built-in providers: openai, anthropic, google. Any unrecognized provider string is treated as a dotted import path to a custom BaseChatModel class.

Custom Builtin Tools

Then reference it in YAML:

Custom tool types (register_tool_resolver)

The five built-in ToolDef shapes (function, mcp, builtin, agent, transfer) cover most needs — but if you need a whole new kind of tool (a webhook, an HTTP RPC, a proprietary bus), reach for the custom: field + a resolver.
Async resolvers work transparently — the composer awaits whichever shape you return.

Python API