nav.groups.ops
ManagedRuntime
The top-level orchestrator — AgentRuntime + ComponentRegistry + ShutdownToken in a single handle.
ManagedRuntime
The top-level orchestrator.
ManagedRuntime unifies AgentRuntime, ComponentRegistry, and a root ShutdownToken into a single handle. It provides coordinated lifecycle (init_all → start_all → serve → stop_all), typed component access, aggregated health probes, and a drain-aware hot-reload entry point.
The full file is src/runtime/managed.rs.
Construction
Via build_managed
The simplest path:
use behest::config::AgentConfigBuilder;
let managed = AgentConfigBuilder::default()
.with_file("config.toml")?
.build_managed()
.await?;
build_managed constructs the AgentRuntime, registers all [[component]] entries from the configuration via the FactoryRegistry, runs init_all + start_all, and returns a fully wired ManagedRuntime.
Manual construction
use behest::runtime::ManagedRuntime;
let managed = ManagedRuntime::new(
runtime,
registry,
shutdown, // ShutdownToken
);
API
impl ManagedRuntime {
// Accessors
pub fn runtime(&self) -> &AgentRuntime;
pub fn registry(&self) -> &ComponentRegistry;
pub fn shutdown_token(&self) -> ShutdownToken;
pub fn extensions(&self) -> Arc<Extensions>;
// Typed component lookup
pub fn component<T: Component>(&self, name: &str)
-> Result<Arc<T>, ManagedError>;
// Lifecycle
pub async fn serve(&self) -> Result<(), ManagedError>;
// Health
pub async fn health(&self) -> HashMap<String, HealthStatus>;
pub async fn overall_health(&self) -> HealthStatus;
pub async fn is_healthy(&self) -> bool;
pub async fn is_ready(&self) -> bool;
pub async fn healthz_json(&self) -> serde_json::Value;
// Hot-reload
pub async fn reload<T: Component>(
&self, name: &str, new_instance: T,
) -> Result<Arc<T>, ManagedError>;
pub async fn reload_raw(
&self, name: &str, new_instance: Box<dyn AnyComponent>,
) -> Result<Arc<dyn AnyComponent>, ManagedError>;
}
Lifecycle
build_managed → init_all → start_all → serve → signal_shutdown → stop_all
serve() waits for the root shutdown token to fire. On shutdown, components stop in reverse dependency order.
Health aggregation
health() collects probes from every initialized component:
let map = managed.health().await;
// { "provider.openai": Healthy, "store.session": Degraded, ... }
overall_health() aggregates via worst-case semantics:
- Healthy — all components are healthy.
- Degraded — at least one is degraded, none unhealthy.
- Unhealthy — at least one is unhealthy.
is_ready() returns true if every component is at least operational (healthy or degraded) — suitable for load-balancer readiness probes.
healthz_json() builds a JSON response:
{
"status": "healthy",
"components": {
"provider.openai": { "status": "healthy", "detail": null },
"store.session": { "status": "healthy", "detail": null }
}
}
See Health Aggregation for the full protocol.
Hot-reload
reload::<T> performs a drain-aware component replacement:
- Calls
pre_replace_hookon the old instance. - Starts the new instance. If this fails, the old instance remains in place.
- Atomically swaps the instance in the registry.
- Calls
post_replace_hookon the old instance (best-effort). - Returns the old
Arc<T>so the caller can await explicit drain.
let old = managed.reload::<MyComponent>("my_comp", new_instance).await?;
// old is the previous Arc<T> — drop it to release the reference
See Hot Reload for the full protocol.
Errors
All errors are ManagedError:
| Variant | When |
|---|---|
ComponentNotFound(name) | Lookup or reload target not registered |
Registry(RegistryError) | Underlying registry failure |
Reload { name, message } | Hot-swap protocol failure |
See also
- Hot Reload — the drain-aware replace protocol.
- Health Aggregation — probes and readiness.
- ComponentRegistry — the lifecycle orchestrator.