Skip to main content

Crate mobius

Crate mobius 

Source
Expand description

A small, modular Rust framework for one linear agent session.

Applications compose an agent::Agent from explicit model, sandbox, checkpoint, and middleware adapters. Frontends remain separate: they submit protocol::Op values and render the frontend-neutral protocol::Event stream.

§Embedded composition

The caller owns every runtime dependency. Include exactly one message-handling middleware, give new sessions a non-empty protocol::SessionContext::bot_id, and keep draining events while commands are active.

use std::path::Path;
use std::sync::Arc;

use mobius::Result;
use mobius::agent::{Agent, AgentConfig, create_agent};
use mobius::backend::checkpoint::{CheckpointStore, sqlite::SqliteCheckpoint};
use mobius::backend::model::{Model, ModelRouter, openai::OpenAi};
use mobius::backend::sandbox::{ApprovalPolicy, Sandbox, local::LocalSandbox};
use mobius::middleware::{Middleware, MiddlewareStack};
use mobius::middleware::{messages::Messages, tools::Tools};
use mobius::protocol::SessionContext;

async fn build_agent(
    workspace: &Path,
    api_key: String,
    model_id: &str,
) -> Result<Agent> {
    let model: Arc<dyn Model> = Arc::new(OpenAi::new(
        api_key,
        "https://api.openai.com/v1",
        model_id,
    )?);
    let models = Arc::new(ModelRouter::new("default", model));
    let sandbox = Arc::new(Sandbox::new(
        Arc::new(LocalSandbox::new(workspace)?),
        ApprovalPolicy::Ask,
    ));
    let checkpoints: Arc<dyn CheckpointStore> =
        Arc::new(SqliteCheckpoint::new(workspace.join("mobius.sqlite3"))?);
    let middleware: Vec<Arc<dyn Middleware>> = vec![
        Arc::new(Messages::default()),
        Arc::new(Tools::coding()),
    ];

    create_agent(
        AgentConfig::new(
            models,
            sandbox,
            checkpoints,
            MiddlewareStack::new(middleware)?,
            "You are a concise coding agent.",
        )
        .session_context(SessionContext {
            bot_id: "embedded".into(),
            ..SessionContext::default()
        }),
    )
    .await
}

A custom provider implements backend::model::Model and must return normalized output. backend::model::ModelEventSink is synchronous and fallible; propagate its error rather than silently losing a streamed event. This example also uses serde_json.

use serde_json::json;

use mobius::{BoxFuture, Result};
use mobius::backend::model::{Model, ModelEventSink, ModelOutput, ModelRequest};
use mobius::protocol::{ModelEvent, ModelInfo, TokenUsage};

struct EchoModel;

impl Model for EchoModel {
    fn info(&self) -> ModelInfo {
        ModelInfo {
            model: "echo".into(),
            reasoning_effort: None,
        }
    }

    fn respond<'a>(
        &'a self,
        _request: ModelRequest<'a>,
        events: ModelEventSink,
    ) -> BoxFuture<'a, Result<ModelOutput>> {
        Box::pin(async move {
            events(ModelEvent::TextDelta("done".into()))?;
            ModelOutput::from_output(
                vec![json!({
                    "type": "message",
                    "role": "assistant",
                    "content": [{"type": "output_text", "text": "done"}]
                })],
                true,
                TokenUsage::default(),
            )
        })
    }
}

A capability implements middleware::Middleware and joins the declaration-ordered middleware::MiddlewareStack. Static prompt sections are composed once at agent creation.

use std::sync::Arc;

use mobius::Result;
use mobius::middleware::{Middleware, MiddlewareStack, PromptSection, RuntimeContext};
use mobius::middleware::messages::Messages;

struct Policy;

impl Middleware for Policy {
    fn name(&self) -> &'static str {
        "policy"
    }

    fn prompt_section(&self, _runtime: &RuntimeContext) -> Result<Option<PromptSection>> {
        Ok(Some(PromptSection::new("Follow the repository policy.")))
    }
}

fn middleware_stack() -> Result<MiddlewareStack> {
    MiddlewareStack::new(vec![Arc::new(Messages::default()), Arc::new(Policy)])
}

§Runtime contracts

  • Error and ProviderError preserve actionable failure classes and retry metadata; callers should not infer policy by matching display strings.
  • agent::create_agent validates composition and unwinds started middleware on startup failure. agent::AgentSender documents bounded submission and sender-drop shutdown; drain agent::AgentEvents::recv until the stream closes.
  • backend::checkpoint::CheckpointStore::save_with_events is the atomic logical boundary for checkpoint, transcript, execution, and event state. Backend contracts specify durability and which optional history operations are supported.
  • backend::sandbox::Sandbox owns approval and background-process cleanup around an injected backend::sandbox::SandboxBackend. Backends must keep cancellation cleanup for resources they launch; the default authorized path fails closed.
  • In mobius-gateway, signal shutdown through GatewayServer::serve_until and await it; dropping the serving future does not perform graceful shutdown.

Modules§

agent
Agent handles and the single linear command dispatch loop.
backend
Runtime adapters used by the agent loop.
middleware
Ordered middleware and capability registration.
protocol
The small event protocol shared by agent frontends.

Structs§

ProviderError
A model-provider failure with retry metadata preserved for callers.

Enums§

Error
Errors returned by möbius modules.

Type Aliases§

BoxFuture
A boxed asynchronous operation used by runtime-pluggable interfaces.
Result
Result type shared by möbius modules.