awaken-runtime 0.2.0

Phase-based execution engine, plugin system, and agent loop for Awaken
Documentation

Awaken

English | 中文

CI crates.io License MSRV

Production AI agent runtime for Rust — type-safe state, multi-protocol serving, plugin extensibility.

Published on crates.io as awaken-agent; keep importing it in Rust as awaken. The workspace uses Rust 1.93.0 for development; the crate MSRV is 1.85.

Docs: GitHub Pages | Chinese docs

Highlights

  • Rust-first agent runtime: typed tools, generated JSON Schema, typed state keys, scoped snapshots, and atomic state commits.
  • One runtime, many clients: HTTP/SSE run API, AI SDK v6, AG-UI/CopilotKit, A2A, and MCP JSON-RPC from the same backend.
  • Configuration-first optimization: choose models/providers, tune prompts, reminders, permissions, generative UI, and deferred tools through /v1/config/*, /v1/capabilities, and the admin console.
  • Production control paths: mailbox-backed background runs, HITL decisions, cancellation/interrupt, SSE replay, retries, fallback models, circuit breakers, metrics, and health probes.
  • Plugin surface: permission gates, reminders, OpenTelemetry, MCP tools, skills, generative UI, and deferred tool loading with an explicit probability model.

30-second mental model

  1. Tools — implement Tool directly or TypedTool with schemars-generated JSON Schema
  2. Agents — each agent has a system prompt, a model, and a set of allowed tools; the LLM drives orchestration through natural language — no predefined graphs
  3. State — typed run/thread state plus persistent profile/shared state for cross-thread or cross-agent coordination
  4. Plugins — lifecycle hooks for permissions, observability, context management, skills, MCP, and more

Your agent picks tools, calls them, reads and updates state, and repeats — all orchestrated by the runtime through 9 typed phases, including a pure ToolGate before tool execution. Every state change is committed atomically after the gather phase.

Try it in 5 minutes

Prerequisites: Rust 1.85+ for the published crate, or the pinned rust-toolchain.toml toolchain when working from this repository, plus an LLM provider API key.

  • Rust 1.85 or newer. This repository pins Rust 1.93.0 for local development.
  • An OpenAI-compatible API key.
[dependencies]
awaken = { package = "awaken-agent", version = "0.2" }
tokio = { version = "1.51.0", features = ["full"] }
async-trait = "0.1.89"
serde_json = "1.0.149"
export OPENAI_API_KEY=<your-key>

Copy this into src/main.rs and run cargo run:

use std::sync::Arc;
use serde_json::{json, Value};
use async_trait::async_trait;
use awaken::contract::tool::{Tool, ToolDescriptor, ToolResult, ToolOutput, ToolError, ToolCallContext};
use awaken::contract::message::Message;
use awaken::engine::GenaiExecutor;
use awaken::registry_spec::AgentSpec;
use awaken::registry::ModelBinding;
use awaken::{AgentRuntimeBuilder, RunRequest};

struct EchoTool;

#[async_trait]
impl Tool for EchoTool {
    fn descriptor(&self) -> ToolDescriptor {
        ToolDescriptor::new("echo", "Echo", "Echo input back to the caller")
            .with_parameters(json!({
                "type": "object",
                "properties": { "text": { "type": "string" } },
                "required": ["text"]
            }))
    }

    async fn execute(&self, args: Value, _ctx: &ToolCallContext) -> Result<ToolOutput, ToolError> {
        let text = args["text"].as_str().unwrap_or_default();
        Ok(ToolResult::success("echo", json!({ "echoed": text })).into())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let agent_spec = AgentSpec::new("assistant")
        .with_model_id("gpt-4o-mini")
        .with_system_prompt("You are a helpful assistant. Use the echo tool when asked.")
        .with_max_rounds(5);

    let runtime = AgentRuntimeBuilder::new()
        .with_agent_spec(agent_spec)
        .with_tool("echo", Arc::new(EchoTool))
        .with_provider("openai", Arc::new(GenaiExecutor::new()))
        .with_model_binding("gpt-4o-mini", ModelBinding {
            provider_id: "openai".into(),
            upstream_model: "gpt-4o-mini".into(),
        })
        .build()?;

    let request = RunRequest::new(
        "thread-1",
        vec![Message::user("Say hello using the echo tool")],
    )
    .with_agent_id("assistant");

    // The quickstart only needs the final result. Use run(..., sink) when
    // streaming events to SSE, WebSocket, protocol adapters, or tests.
    let result = runtime.run_to_completion(request).await?;
    println!("response: {}", result.response);
    println!("termination: {:?}", result.termination);

    Ok(())
}

The quickstart path is covered without network access:

cargo test -p awaken-agent --test readme_quickstart

Live provider validation is opt-in so CI does not depend on external model services:

OPENAI_API_KEY=<your-key> cargo test -p awaken-agent --test readme_live_provider -- --ignored

Serve over any protocol

Start the built-in server and connect from React, Next.js, or another agent — no code changes:

use awaken::prelude::*;
use awaken::stores::{InMemoryMailboxStore, InMemoryStore};
use std::sync::Arc;

let store = Arc::new(InMemoryStore::new());
let runtime = Arc::new(runtime);
let mailbox = Arc::new(Mailbox::new(
    runtime.clone(),
    Arc::new(InMemoryMailboxStore::new()),
    store.clone(),
    "default-consumer".into(),
    MailboxConfig::default(),
));

let state = AppState::new(
    runtime.clone(),
    mailbox,
    store,
    runtime.resolver_arc(),
    ServerConfig::default(),
);
serve(state).await?;

Frontend protocols

Protocol Endpoint Frontend
AI SDK v6 POST /v1/ai-sdk/chat React useChat()
AG-UI POST /v1/ag-ui/run CopilotKit <CopilotKit>
A2A POST /v1/a2a/message:send Other agents
MCP POST /v1/mcp JSON-RPC 2.0

The optional admin console uses /v1/capabilities and /v1/config/* to edit agents, models, providers, MCP servers, and plugin config sections in the browser. Plugins expose JSON Schema through the same typed PluginConfigKey path used by runtime hooks, so saving an agent section such as permission, reminder, generative-ui, or deferred_tools publishes a new registry snapshot and applies to subsequent /v1/runs requests. OpenAI-compatible providers, including BigModel, use the openai adapter with a provider-specific base_url.

The design intent is that agent optimization stays data-driven: model choice, provider endpoints, base prompts, system reminders, generated-UI instructions, permission policy, and tool-loading policy should be configured through the same schema-backed path rather than hard-coded into the agent loop.

Tuning surface Configuration path
Base prompt AgentSpec.system_prompt on the agent entry
Model and provider routing AgentSpec.model_id, /v1/config/models, /v1/config/providers
System reminders and prompt context injection reminder plugin section, using system or suffix_system targets
Generative UI prompt guidance generative-ui plugin section (catalog_id, examples, or full instructions)
Tool policy and context cost permission and deferred_tools plugin sections
Prompt semantic hooks Not a built-in plugin yet; add them as typed PluginConfigKey sections with schema-backed hooks

React + AI SDK v6:

import { useChat } from "ai/react";

const { messages, input, handleSubmit } = useChat({
  api: "http://localhost:3000/v1/ai-sdk/chat",
});

Next.js + CopilotKit:

import { CopilotKit } from "@copilotkit/react-core";

<CopilotKit runtimeUrl="http://localhost:3000/v1/ag-ui/run">
  <YourApp />
</CopilotKit>

Managed configuration

Wire a ConfigStore into AppState to manage agents, models, providers, and MCP servers through /v1/config/*. Use the configuration-driven tuning guide to tune providers, model bindings, tools, and plugin sections. The Admin Console in apps/admin-console uses the same API and reads VITE_BACKEND_URL for the server base URL.

Built-in plugins

Facade features are enabled by default via the full feature. Use default-features = false to opt out. Workspace extension crates that are not re-exported by the facade, such as deferred tools, are added as direct dependencies.

Plugin What it does Feature flag
Permission Firewall-style tool access control with Deny/Allow/Ask rules, glob/regex matching, and HITL suspension via mailbox. permission
Reminder Injects system or conversation-level context messages when tool calls match configured patterns. reminder
Observability OpenTelemetry telemetry aligned with GenAI Semantic Conventions; supports OTLP, file, and in-memory export. observability
MCP Connects to external MCP servers and registers their tools as native Awaken tools. mcp
Skills Discovers skill packages and injects a catalog before inference so the LLM can activate skills on demand. skills
Generative UI Streams declarative UI components to frontends via A2UI, JSON Render, and OpenUI Lang integrations. generative-ui
Deferred Tools Hides large tool schemas behind ToolSearch, then uses a discounted Beta probability model to re-defer idle promoted tools. direct crate: awaken-ext-deferred-tools

Deferred tools are configured through the deferred_tools agent section when the ext-deferred-tools plugin is registered. See Use Deferred Tools for setup, the activation heuristic, and the DiscBeta probability model.

Custom interception hooks should use ToolGateHook via PluginRegistrar::register_tool_gate_hook(). BeforeToolExecute is reserved for execution-time hooks that run only when a tool is actually about to execute.

Why Awaken

  • One backend serves multiple protocols: AI SDK v6, AG-UI, A2A, MCP, plus native HTTP/SSE routes.
  • Configuration is the control plane: model/provider routing, prompts, reminders, permissions, and tool-loading policy use schema-backed config that can be validated and applied at runtime.
  • The LLM orchestrates: define the agent identity, model binding, and tool access; no hand-coded DAG is required.
  • Runtime-managed configuration updates agents, model bindings, providers, and MCP servers through the Config API or Admin Console.
  • Plugin extension points are typed: 9 lifecycle phases, PhaseHook, ToolGateHook, scheduled actions, effects, request transforms, and plugin-provided tools.
  • State is type-safe: StateKey binds each key to Rust value/update types, scopes it to run/thread/profile, and applies declared merge strategies before commit.
  • Operational surfaces are built in: LLM retry/backoff, per-model circuit breaker, request timeout, graceful shutdown, Prometheus metrics, health probes, and mailbox retry/backoff.
  • Zero unsafe — the entire workspace forbids unsafe and relies on the Rust compiler for memory safety.

When to use Awaken

  • You want a Rust backend for AI agents with compile-time safety
  • You need to serve multiple frontend or agent protocols from one backend
  • Your tools need to safely share state during concurrent execution
  • You need auditable thread history, checkpoints, and resumable control paths
  • You are comfortable wiring your own tools, providers, and model registry instead of relying on batteries-included defaults

When NOT to use Awaken

  • You need built-in file/shell/web tools out of the box — consider OpenAI Agents SDK, Dify, or CrewAI
  • You want a visual workflow builder — consider Dify, LangGraph Studio
  • You want Python and rapid prototyping — consider LangGraph, AG2, PydanticAI
  • You need a stable, slow-moving surface area more than an evolving runtime platform
  • You need LLM-managed memory (agent decides what to remember) — consider Letta

Architecture

Awaken is split into three runtime layers. awaken-contract defines the shared contracts: agent specs, model/provider specs, tools, events, transport traits, and the typed state model. awaken-runtime resolves an AgentSpec into ResolvedExecution: local agents become a ResolvedAgent with an ExecutionEnv built from plugins, while endpoint-backed agents run through an ExecutionBackend. It also executes the phase loop and manages active runs plus external control such as cancellation and HITL decisions. awaken-server exposes that same runtime through HTTP routes, SSE replay, mailbox-backed background execution, and protocol adapters for AI SDK v6, AG-UI, A2A, and MCP.

Around those layers sit storage and extensions. awaken-stores provides memory, file, and PostgreSQL persistence for threads and runs; memory, file, and PostgreSQL config stores; memory and SQLite mailbox stores; and memory/file profile stores. awaken-ext-* crates extend the runtime at phase and tool boundaries.

awaken                   Facade crate with feature flags
├─ awaken-contract       Contracts: specs, tools, events, transport, state model
├─ awaken-runtime        Resolver, phase engine, loop runner, runtime control
├─ awaken-server         Routes, mailbox, SSE transport, protocol adapters
├─ awaken-stores         Memory, file, PostgreSQL, and SQLite-backed stores
├─ awaken-tool-pattern   Glob/regex matching used by extensions
└─ awaken-ext-*          Optional runtime extensions

Examples and learning paths

Example What it shows
live_test Basic LLM integration
multi_turn Multi-turn with persistent threads
tool_call_live Tool calling with calculator
ai-sdk-starter React + AI SDK v6 full-stack
copilotkit-starter Next.js + CopilotKit full-stack
openui-chat OpenUI Lang chat frontend
admin-console Config API management UI
export OPENAI_API_KEY=<your-key>
cargo run --package awaken-agent --example multi_turn

cd examples/ai-sdk-starter && npm install && npm run dev

# Terminal 1: starter backend for admin console
AWAKEN_STORAGE_DIR=./target/admin-sessions cargo run -p ai-sdk-starter-agent

# Terminal 2: admin console
npm --prefix apps/admin-console install
npm --prefix apps/admin-console run dev
Goal Start with Then
Build your first agent Get Started Build Agents
See a full-stack app AI SDK starter CopilotKit starter
Manage runtime config Admin Console Configure Agent Behavior
Explore the API Reference docs cargo doc --workspace --no-deps --open
Understand the runtime Architecture Run Lifecycle and Phases
Migrate from tirea Migration guide

Contributing

See CONTRIBUTING.md and DEVELOPMENT.md for setup details.

Good first issues are a great entry point. Quick contribution flow: fork → create a branch → write tests → open a PR.

Areas where contributions are especially welcome:

  • Additional mailbox, config, and storage backends beyond the built-in memory/file/PostgreSQL/SQLite options
  • Built-in tool implementations (file read/write, web search)
  • Token cost tracking and budget enforcement
  • Model fallback/degradation chains

Join the conversation on GitHub Discussions.


Awaken is a ground-up rewrite of tirea; it is not backwards-compatible. The tirea 0.5 codebase is archived on the tirea-0.5 branch.

License

Dual-licensed under MIT or Apache-2.0.