Architecture - tinyhumansai/tinyagents GitHub Wiki

Architecture

TinyAgents is a small, provider-neutral agent harness for Rust, plus a durable typed state-graph runtime. It takes its shape from LangChain (models, tools, middleware, structured output, streaming, usage/cost) and LangGraph (START/END, nodes, conditional edges, channels/reducers, checkpoints, interrupts, subgraphs, time travel), rebuilt as ordinary, typed Rust.

This page shows how the crates layer together.

The crates

Crate Path Owns
Definition crates/tinyagents-definition/ the host-owned agent definition vocabulary: identity, description, declared model/tools/delegates, and a read-only catalogue seam
Harness crates/tinyagents-harness/ provider-neutral model calls, typed tools, middleware, structured output, streaming, usage/cost, retry/limits, failover, cache, steering, typed terminal outcomes
Tasks crates/tinyagents-tasks/ graph-independent detached-task machinery: task model, TaskStore (in-memory and JSONL), DetachedTaskRegistry, restart reconciliation, the durable completion router, and the orchestrate_* control tools
Graph runtime crates/tinyagents-graph/ durable typed state graphs: START/END, nodes, edges, conditional routing, commands, Send fanout, reducers/channels, checkpoints, interrupts, subgraphs, streaming, observability, time travel; tinyagents_graph::orchestration is a re-export shim over Tasks
Registry crates/tinyagents-registry/ named capability catalog (models, tools, agents, graphs, routers) plus an offline model price/capability catalog and the model router
Session crates/tinyagents-session/ durable SQLite-backed session history, JSONL transcripts and a run ledger, separate from harness and graph durability
Runtime crates/tinyagents-runtime/ host-neutral stateful sessions over the harness and the transcript storage seam
Orchestration crates/tinyagents-orchestration/ host-neutral subagent invocation and lifecycle (SubagentDriver, spawn admission, job registry), plus teams and workflows
Live crates/tinyagents-live/ live (realtime voice) agent sessions that run a model's tool calls through the harness's own admission and middleware pipeline
Integration tests crates/tinyagents-integration-tests/ cross-crate tests and runnable examples; not a library dependency

There is no facade crate: a consumer depends directly on the crates it needs. See Quick Start for the dependency snippet and feature flags, and Capabilities for a full index of what each crate exposes.

Dependency direction

definition  <-  harness  <-  { tasks, registry, live }
                harness + tasks  <-  { graph, session }
                harness + session  <-  runtime
                graph + tasks + harness + session + runtime  <-  orchestration

Read A <- B as "B depends on A". The list below is the exact set of library dependencies from each crate's Cargo.toml ([dependencies], not dev-dependencies):

  • tinyagents-definition has no internal dependencies.
  • tinyagents-live depends on harness.
  • tinyagents-harness depends on tinyagents-definition.
  • tinyagents-tasks depends on harness (task tools are ordinary harness tools) and does not depend on the graph engine.
  • tinyagents-graph depends on harness and tasks.
  • tinyagents-registry depends on definition and harness.
  • tinyagents-session depends on harness (with its sqlite feature) and tasks.
  • tinyagents-runtime depends on harness and session.
  • tinyagents-orchestration depends on graph, tasks, harness, session, and runtime; none of those depend back on it.

Nothing in harness reads from session, and session is not required to run a harness or a graph at all — it is a separate persistence domain for history and search.

The crates also depend on vendored libraries: tinyinference-llm (vendor/tinyinference/crates/tinyinference-llm, messages and provider types) and tinytools (vendor/tinytools/crates/tinytools, the shared Tool trait); the harness additionally uses tinyinference-core and tinyinference-embeddings.

Request flow

+-------------+        +-------------------------------------------+
| Application |------->| Capability Registry                       |
| Rust code   |        | models | tools | agents | graphs | routers |
+------+------+        +---------------------+---------------------+
       |                                     |
       |                                     v
       |              +-------------------------------------------+
       +------------->| Durable Graph Runtime                     |
                      | typed state | nodes | edges | checkpoints |
                      +---------------------+---------------------+
                                            |
                                            v
                      +-------------------------------------------+
                      | Agent Harness                             |
                      | prompts | tools | middleware | usage/cost |
                      +----+--------------------------+-----------+
                           |                          |
                           v                          v
                 +------------------+        +------------------+
                 | Model Providers  |        | Typed Tools      |
                 | OpenAI and       |        | local functions  |
                 | compatible hosts |        | external systems |
                 +------------------+        +------------------+

A graph node can call into the harness to run a model or a tool. The harness does not depend on the graph: it runs standalone as a single agent loop, or as one node's implementation inside a larger graph.

Composition: agents calling agents, graphs embedding graphs

Two composition features let larger systems be built out of the same typed pieces, rather than a separate execution model:

  • Sub-agents. tinyagents_orchestration::subagent (SubAgent, SubAgentSession, SubAgentTool, SubagentDriver) wraps a whole harness agent as an ordinary Tool. A parent agent calling a sub-agent is just a tool call; the child run gets its own root_run_id/parent_run_id and its usage and cost roll up to the parent. SubagentDriver is the single engine for an agent step, so team members and workflow agent children run through it too. See Harness.
  • Subgraphs. tinyagents_graph::subgraph (adapter_subgraph_node, shared_subgraph_node) embeds one compiled graph as a single node inside another. The embedded graph's checkpoints are namespaced under the embedding node id, so durability composes the same way state does. See Graph Runtime.

tinyagents_graph::recursion (RecursionPolicy, RecursionStack, RunTree) tracks parent/child run lineage for both of the above and enforces a depth limit, so a chain of sub-agents or nested subgraphs stays bounded and observable rather than growing without a cap. It is a safety and observability mechanism, not a distinct architectural layer.

Harness

The harness owns nondeterministic agent work:

  • model request construction and provider adapter calls
  • tool registration and execution
  • middleware hooks, structured output strategy, streaming deltas
  • provider error normalization, usage and cost records
  • retries, limits, cancellation, cache, memory/embeddings
  • a typed TerminalOutcome for why a run ended, per-turn events (TurnStarted / TurnCompleted / MessageAppended), steering, and nested tool calls (off by default)
  • deterministic model and tool test doubles (tinyagents_harness::testkit)

See Harness.

Graph runtime

The graph owns deterministic state movement:

  • typed state, reducers/channels, and updates
  • nodes, static and conditional edges, commands, Send fanout
  • checkpoints, interrupts, time travel, topology export, run status, observability
  • subgraphs (tinyagents_graph::subgraph): a node embeds another CompiledGraph

Child-task orchestration (typed orchestrate_* task controls, task stores, the detached-task registry, restart reconciliation, and the durable completion router) lives in tinyagents-tasks, not in the graph engine; tinyagents_graph::orchestration only re-exports it.

See Graph Runtime.

Tasks and orchestration

tinyagents-tasks owns the durable bookkeeping for detached child work: TaskStore (InMemoryTaskStore, JsonlTaskStore), TaskStoreRegistry, DetachedTaskRegistry, reconcile_orphaned_tasks, SteeringRegistry/OrchestrationTool, and the completions module (CompletionRouter, CompletionStore, NotifyMode, CompletionFormatter). tinyagents-orchestration builds subagents, teams and workflows on top of it: SubAgentJobRegistry is backed by DetachedTaskRegistry. See Graph Runtime and Harness.

Registry

The registry owns named capabilities, letting Rust code refer to a model, tool, agent, graph, or router by a stable name instead of a process global, and carries the offline model price/capability catalog. See Registry.

Design rule

Each crate keeps its contract narrow:

  • the harness calls providers and tools
  • the graph moves typed state through explicit topology
  • the registry binds names to capabilities
  • tasks own durable child-task bookkeeping, and orchestration drives subagents, teams and workflows through one SubagentDriver

No layer bypasses another layer's policy, observability, or validation boundary: a sub-agent call and a subgraph both pass through the same run-limit and event machinery as a top-level run.