adk-managed
Managed agent runtime for ADK-Rust — a provider-neutral, durable, resumable agent execution engine.
Overview
adk-managed provides the ManagedAgentRuntime trait and its default implementation. It takes a declarative ManagedAgentDef, builds a runnable agent, and operates it as a durable, resumable, event-streaming background session. The runtime composes existing shipping components behind a unified lifecycle trait.
This is the execution engine inside a managed agent service. It is a library, not a service — the platform hosts it.
Key Capabilities
- Provider-neutral: Identical event sequences regardless of LLM provider (Gemini, OpenAI, Anthropic, Ollama, OpenAI-compatible)
- Durable sessions: Checkpoint after every event; survive process crashes with zero event loss
- Resumable: Rehydrate from checkpoint and continue from last consistent state
- Event streaming: Uniform
SessionEventstream with monotonic sequence numbers and SSE replay support - Custom tool parking: Client-executed tools park the loop until results arrive (or timeout)
- Composable: Injected services (sessions, sandbox, memory) — no platform dependencies
- Additive: Feature-gated; existing
Runner/LlmAgentunchanged when feature is off
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Platform Layer (ep-* crates) │
│ HTTP Routes │ Auth │ Billing │ Multi-tenancy │
└──────────────────────────┬──────────────────────────────────┘
│ Rust trait calls (in-process)
▼
┌─────────────────────────────────────────────────────────────┐
│ Runtime Layer (adk-managed — this crate) │
│ │
│ ManagedAgentRuntime trait + DefaultManagedAgentRuntime │
│ ─────────────────────────────────────────────────── │
│ • Builds runnable agents from ManagedAgentDef │
│ • Runs supervised session loop (durable, resumable) │
│ • Emits provider-neutral SessionEvent stream │
│ • Manages custom tool parking, checkpoints, interrupts │
│ • Resolves ModelRef → Arc<dyn Llm> │
│ │
│ Composes existing crates: │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │adk-runner│ │adk-session│ │adk-model │ │adk-tool │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
Quick Start
Add to your Cargo.toml:
[]
= "1.0"
= "1.0"
= "1.0"
= { = "1", = ["full"] }
= "0.3"
= "0.1"
Or via the umbrella crate:
[]
= { = "1.0", = ["managed-runtime"] }
Minimal Example
use Arc;
use ;
use InMemorySessionService;
use async_trait;
use StreamExt;
// A resolver that returns a scripted LLM (no API key needed)
async
Core Types
ManagedAgentRuntime Trait
The central async trait defining the full agent lifecycle:
| Method | Description |
|---|---|
create(def) |
Register an agent definition → AgentHandle |
start_session(agent, env?) |
Start a new session (initial status: Queued) |
send_event(session, event) |
Send a UserEvent to the session loop |
stream_events(session, from_seq?) |
Subscribe to SessionEvent stream |
interrupt(session) |
Stop at next boundary, emit status.idle |
pause(session) |
Checkpoint and pause processing |
resume(session) |
Resume from pause or process restart |
status(session) |
Query current SessionStatus |
archive(session) |
Terminal state (data retained for read) |
delete_session(session) |
Remove session and its data |
ManagedAgentDef
Declarative agent definition with a builder API:
let def = new
.with_system
.with_description
.with_tools;
SessionEvent (Agent → Client)
Provider-neutral event stream with monotonic seq:
| Type | Description |
|---|---|
agent.message |
Assistant text content |
agent.tool_use |
Built-in tool invocation (server-side) |
agent.custom_tool_use |
Client-executed custom tool (loop parks) |
agent.mcp_tool_use |
MCP tool invocation |
status.running |
Turn started |
status.idle |
Turn complete (includes stop_reason) |
error |
Execution error |
UserEvent (Client → Agent)
| Type | Description |
|---|---|
user.message |
Send content to the agent |
user.interrupt |
Stop the current turn |
user.tool_confirmation |
Allow/deny tool execution |
user.custom_tool_result |
Return custom tool results |
user.tool_result |
Built-in tool result (self-hosted only) |
user.define_outcome |
Set success criteria |
ModelRef
Provider-neutral model reference:
// Shorthand (provider inferred from name prefix)
Shorthand // → Gemini
Shorthand // → OpenAI
Shorthand // → Anthropic
// Structured (explicit provider)
Structured
SessionStatus
Lifecycle state machine:
Queued → Running → Idle (per turn) → Running (next turn)
→ Rescheduling → Running (retry success) | Failed (exhaust)
→ Paused → Running (on resume)
→ Completed / Failed / Archived
Features
Durable Sessions
Every event is checkpointed atomically. On process crash, resume() rehydrates from the last consistent checkpoint with no event loss.
Custom Tool Parking
When the agent emits agent.custom_tool_use, the session loop parks until the client sends user.custom_tool_result or a configurable timeout elapses (default: 5 minutes).
Event Replay (SSE Reconnection)
// Reconnect from seq 42 — replays events 43, 44, ... then live tail
let stream = runtime.stream_events.await?;
Provider Parity
An identical ManagedAgentDef run against all five providers produces byte-identical event type sequences (verified by golden fixture F-8).
Testing with ScriptedLlm
ScriptedLlm is a deterministic LLM test double that exercises the full runtime pipeline. Only the provider API call is replaced:
use ;
use json;
let llm = new;
This is NOT a mock — it implements the real Llm trait and exercises the full runtime (parking, checkpoints, replay, event mapping). Per-commit gate, $0 cost.
Golden Fixture Tests
Eight fixture JSON files (F-1 through F-8) define conformance scenarios:
| Fixture | Tests |
|---|---|
| F-1 Hello | Basic message → response → idle |
| F-2 MCP Tool | MCP tool call flow |
| F-3 Custom Tool | Park → deliver → resume |
| F-4 Confirmation | Tool confirmation request → approve |
| F-5 Resume | Crash → resume from checkpoint |
| F-6 Replay | Historical event replay |
| F-7 Interrupt | Interrupt stops at boundary |
| F-8 Provider Parity | Identical sequences across providers |
Run conformance tests:
Module Structure
adk-managed/src/
├── lib.rs # Feature gate, exports
├── runtime.rs # ManagedAgentRuntime trait
├── default_runtime.rs # DefaultManagedAgentRuntime implementation
├── types/
│ ├── mod.rs # Re-exports
│ ├── agent_def.rs # ManagedAgentDef, builder
│ ├── content.rs # ContentBlock
│ ├── model_ref.rs # ModelRef, Provider, ModelConfig
│ ├── tools.rs # ToolConfig, McpServerConfig, SkillRef, PermissionPolicy
│ ├── events.rs # UserEvent, SessionEvent, StopReason
│ ├── session.rs # SessionStatus
│ └── error.rs # RuntimeError
├── resolver.rs # ModelRef → Arc<dyn Llm>
├── agent_builder.rs # ManagedAgentDef → runnable agent
├── session_loop.rs # Supervised loop: run turns, park, checkpoint
├── parking.rs # Custom tool parking (channel-based wait)
├── checkpoint.rs # Atomic event+state persistence
├── sequence.rs # Monotonic seq counter per session
├── replay.rs # Event replay with from_seq
├── event_mapping.rs # Provider-neutral Runner → SessionEvent mapping
├── schema_normalization.rs # Cross-provider MCP schema normalization
├── usage.rs # Uniform usage reporting
└── testing.rs # ScriptedLlm, ScriptedTurn, ScriptedToolCall
Feature Flags
| Flag | Description |
|---|---|
sandbox |
Enable adk-sandbox integration for isolated tool execution |
memory |
Enable adk-memory integration for cross-session memory |
full |
Enable all optional features |
Smoke Test Example
A standalone example crate is provided:
Runs fixture F-1 end-to-end with ScriptedLlm (no API key required). Platform teams can clone this to smoke-test integration.
Stability
EXPERIMENTAL — This crate is additive and feature-gated behind
managed-runtimeon the umbrella crate. It does not affect existingRunner/LlmAgentAPIs when disabled. The API surface may change in future releases.
License
Apache-2.0
Part of ADK-Rust
This crate is part of the ADK-Rust framework for building AI agents in Rust.