A Rust agent runtime that serves AI SDK, CopilotKit, A2A, and MCP from the same backend, recovers from mid-stream LLM failures, and treats configuration as the control plane.
awaken is the canonical crate (transferred to this project from
@brayniac; see the acknowledgement below).
awaken-agent is a thin compatibility republish from when the project shipped
under that name. Import path is awaken either way. MSRV: Rust 1.93.
Docs: GitHub Pages · Chinese docs · Changelog
- Multi-protocol from one backend. A single runtime serves AI SDK v6, AG-UI / CopilotKit, A2A, and MCP. The same
/v1/runspowers all of them. - Streaming LLM calls survive transient failures. Mid-stream interruptions and idle stalls are detected and recovered through four explicit plans (continue text, replay completed tool calls, restart with a cancelled-tool hint, or whole restart).
Retry-Afteris honored. AStreamCheckpointStorecontract extends the same recovery across process restarts. (details) - Threads have a parent-child lineage. Sub-agent runs create child threads; deletion is explicit (
reject/detach/cascade). Filters and cursors on/v1/threadsmake hierarchical UIs straightforward. - Secrets stay redacted.
ProviderSpec.api_key, admin and A2A bearer tokens are wrapped inRedactedString—Debug/Displayprint***, the buffer is zeroized on drop, and the JSON wire format is unchanged. - Configuration is the control plane. Models, providers, prompts, reminders, permissions, and tool-loading policy live behind
/v1/config/*and/v1/capabilities. Apply bursts can be debounced and provider executors are reused across unchanged specs. - Type-safe state and tools. Typed
StateKeys with merge strategies, generated JSON Schema forTypedTool, atomic batched commits after each phase.unsafe_code = "forbid"workspace-wide.
- Tools — implement
Tooldirectly, orTypedToolwithschemars-generated JSON Schema. - Agents — system prompt, model binding, allowed tools. The LLM orchestrates through natural language; there is no DAG.
- State — typed run/thread state plus persistent profile and shared state for cross-thread or cross-agent coordination.
- Plugins — lifecycle hooks for permission, observability, context management, skills, MCP, generative UI.
The runtime drives 9 typed phases per round, including a pure ToolGate before tool execution. State mutations are batched and committed atomically.
Prerequisites: Rust 1.93+ and an OpenAI-compatible API key.
[dependencies]
awaken = { version = "0.4.0" }
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 --test readme_quickstartLive provider validation is opt-in so CI does not depend on external model services:
OPENAI_API_KEY=<your-key> cargo test -p awaken --test readme_live_provider -- --ignoredStart 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?;| 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 reads /v1/capabilities and writes through
/v1/config/* to manage agents, models, providers, MCP servers, and plugin
config sections. Plugins expose their schema via the same typed
PluginConfigKey used at runtime, so saving an agent's permission,
reminder, generative-ui, or deferred_tools section publishes a new
registry snapshot that takes effect on the next /v1/runs request.
OpenAI-compatible providers (including BigModel) use the openai adapter
with their own base_url; non-secret extras go in ProviderSpec.adapter_options.
| Tuning surface | Where it lives |
|---|---|
| Base prompt | AgentSpec.system_prompt |
| Model and provider routing | AgentSpec.model_id + /v1/config/models + /v1/config/providers |
| System reminders and prompt injection | reminder plugin section |
| Generative UI prompt guidance | generative-ui plugin section |
| Tool policy and context cost | permission and deferred_tools plugin sections |
React + AI SDK v6:
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
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>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.
The facade full feature pulls in the plugins below. Use
default-features = false to opt out. awaken-ext-deferred-tools is not
re-exported by the facade and is added as a direct dependency.
| Plugin | What it does | Feature flag |
|---|---|---|
| Permission | Allow/Deny/Ask rules with glob and regex matching on tool name and arguments. Deny beats Allow beats Ask; Ask suspends the run via the mailbox for HITL. | permission |
| Reminder | Injects system or conversation-level context messages when a tool call matches a configured pattern. | reminder |
| Observability | OpenTelemetry traces and metrics aligned with the GenAI Semantic Conventions; OTLP, file, and in-memory exports. | 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 a ToolSearch step, then re-defers tools that have been idle for a configurable number of turns using a discounted Beta usage model. |
direct crate: awaken-ext-deferred-tools |
Custom tool interception goes through ToolGateHook via
PluginRegistrar::register_tool_gate_hook(). BeforeToolExecute is reserved
for execution-time hooks that only run when a tool is actually about to execute.
- You want a Rust backend for AI agents with compile-time guarantees.
- You need to serve AI SDK, CopilotKit, A2A, and/or MCP from a single backend.
- Tools need to share state safely during concurrent execution, and runs need auditable history with checkpoints and resume.
- You're comfortable registering your own tools and providers instead of relying on batteries-included defaults.
- 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 or LangGraph Studio.
- You want Python and rapid prototyping — consider LangGraph, AG2, or PydanticAI.
- You need an LLM-managed memory subsystem where the agent decides what to remember — consider Letta.
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
| 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 --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 |
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.
The awaken crate name on crates.io was generously transferred from
@brayniac, who maintained an earlier crate
under the same name and offered to hand it over so this project could publish
canonically. Versions 0.1–0.3 of awaken on crates.io belong to that
earlier project; this codebase resumes the line that previously shipped as
awaken-agent 0.2.x and starts at 0.4.0 to skip past the prior versions.
Thank you.
Awaken is also a ground-up rewrite of tirea and is not
backwards-compatible with it. The tirea 0.5 codebase remains archived on the
tirea-0.5 branch.
Dual-licensed under MIT or Apache-2.0.