Expand description
§mentra
Mentra is an agent runtime for building tool-using LLM applications.
MSRV: Rust 1.88.
§Current Features
- streaming model response handling
- provider-neutral token usage reporting across OpenAI, OpenRouter, Anthropic, Gemini, Ollama, and LM Studio
- optional tool authorization with structured previews and fail-closed execution blocking
- recoverable malformed tool-call input handling that feeds retry guidance back to the model
- custom tool execution through
ToolDefinition + ToolExecutor, withToolSpec::builder(...)as the convenience metadata API - builtin
shell,background_run,check_background, andfilestools - builtin
tasksubagents with isolated child context and parent-side tracking - persistent agent teams with
team_spawn,team_send,broadcast,team_read_inbox, and generic request-response protocols viateam_request,team_respond, andteam_list_requests - three-layer context compaction with silent tool-result shrinking, auto-summary compaction, and a builtin
compacttool - Model Context Protocol servers over stdio and the legacy HTTP+SSE transport, with their tools bridged into the runtime
- agent events and snapshots for CLI or UI watchers
- Anthropic provider support
- Gemini Developer API provider support
- OpenAI provider support via the Responses API
- OpenRouter provider support via the Responses API
- Ollama provider support via the OpenAI-compatible Responses API
- LM Studio provider support via the OpenAI-compatible Responses API
- image inputs for OpenAI and Anthropic, plus inline image bytes for Gemini
§Quickstart Example
Clone the repository and run the workspace quickstart example:
cargo run -p mentra-examples --example quickstart -- "Summarize the benefits of tool-using agents."The quickstart example accepts a prompt from CLI args or stdin. Set MENTRA_MODEL to force a specific OpenAI model; otherwise it resolves the newest available OpenAI model automatically.
§Building A Runtime
Use Runtime::builder() when you want Mentra’s builtin runtime tools, or Runtime::empty_builder() when you want to opt into every tool explicitly.
use mentra::{BuiltinProvider, Runtime};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
.with_optional_provider(
BuiltinProvider::OpenRouter,
std::env::var("OPENROUTER_API_KEY").ok(),
)
.with_optional_provider(
BuiltinProvider::Gemini,
std::env::var("GEMINI_API_KEY").ok(),
)
.with_ollama()
.with_lmstudio()
.build()?;
let _ = runtime;
Ok(())
}with_ollama() targets http://127.0.0.1:11434/ and with_lmstudio() targets
http://127.0.0.1:1234/, using each server’s OpenAI-compatible API surface.
§Custom Compatible Providers
If you need a non-default OpenAI-compatible or Anthropic-compatible endpoint,
register a provider-core instance with a customized ProviderDefinition.
Using a distinct provider ID lets you keep the builtin provider alongside your
custom endpoint.
use mentra::{ModelSelector, ProviderId, Runtime};
let mut definition = mentra::provider_core::responses::openai_definition();
definition.descriptor.id = ProviderId::new("custom-openai-compatible");
definition.descriptor.display_name = Some("Custom OpenAI-Compatible".to_string());
definition.base_url = Some("https://llm.example.com/".to_string());
let runtime = Runtime::builder()
.with_registered_provider(mentra::provider_core::responses::ResponsesProvider::new(
definition,
mentra::provider_core::StaticCredentialSource::new(std::env::var("CUSTOM_API_KEY")?),
))
.build()?;
let model = runtime
.resolve_model(
ProviderId::new("custom-openai-compatible"),
ModelSelector::NewestAvailable,
)
.await?;Anthropic-compatible endpoints follow the same pattern:
use mentra::{ProviderId, Runtime};
let mut definition = mentra::provider_core::anthropic::definition();
definition.descriptor.id = ProviderId::new("custom-anthropic-compatible");
definition.descriptor.display_name = Some("Custom Anthropic-Compatible".to_string());
definition.base_url = Some("https://claude.example.com/".to_string());
let runtime = Runtime::builder()
.with_registered_provider(
mentra::provider_core::anthropic::AnthropicProvider::with_definition_and_credential_source(
definition,
mentra::provider_core::StaticCredentialSource::new(std::env::var("CUSTOM_API_KEY")?),
),
)
.build()?;If your compatible endpoint needs different auth or extra headers, mutate the
definition’s auth_scheme, headers, query_params, or retry fields before
registering it.
§Architecture
Mentra is organized around four runtime subsystems:
- execution: model providers, runtime policy, hooks, turn execution, and shell/background command routing
- persistence: agent records, run state, task snapshots, leases, team state, background notifications, and memory
- tooling: builtin and custom tools, optional skills, and typed app context
- collaboration: persistent teammates, team inbox/request flows, and background task wakeups
Persistent teammates are hosted as async actors on a shared Tokio runtime. Live actors are wake-driven rather than steady-state polled: inbox appends, protocol updates, background task completion, explicit resume, and autonomy timers wake the actor to process durable state already written to the store. After a restart, the persisted team inbox, protocol requests, and background notifications remain the source of truth, and Runtime::resume(...) revives teammate actors against that stored state.
§Resolving A Model
Use Runtime::resolve_model(...) when you want provider-aware model selection without reimplementing discovery or ModelInfo construction in application code.
use mentra::{BuiltinProvider, ModelSelector, Runtime};
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
.build()?;
let model = runtime
.resolve_model(
BuiltinProvider::OpenAI,
std::env::var("MENTRA_MODEL")
.map(ModelSelector::Id)
.unwrap_or(ModelSelector::NewestAvailable),
)
.await?;
let _ = model;§Coding Agent Setup
Runtime::builder() registers Mentra’s builtin tools, including shell, background_run, check_background, files, and the runtime/task/team intrinsics. Shell and background execution remain disabled by default, so coding-agent setups must opt in with a runtime policy. If you want semantic review before tools execute, install a ToolAuthorizer.
The builtin local executor is a host executor, not a filesystem or network
sandbox. RuntimePolicy::permissive() therefore grants the model the same host
access as the Mentra process. Use it only inside a disposable container or
another boundary you trust. On a normal host, install an OS-enforced custom
executor with RuntimeBuilder::with_executor(...); authorization and shell
validation decide whether a command may start, but they do not contain an
allowed command.
For Responses API transport, xipe-compatible endpoints, and provider-side state
options, see the workspace
Responses Coding Agent Guide.
use async_trait::async_trait;
use mentra::{BuiltinProvider, Runtime, RuntimePolicy};
use mentra::tool::{
ToolAuthorizationDecision, ToolAuthorizationRequest, ToolAuthorizer,
};
struct AllowAllAuthorizer;
#[async_trait]
impl ToolAuthorizer for AllowAllAuthorizer {
async fn authorize(
&self,
_request: &ToolAuthorizationRequest,
) -> Result<ToolAuthorizationDecision, mentra::error::RuntimeError> {
Ok(ToolAuthorizationDecision::allow())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
// Full host shell access. Use only inside a trusted external sandbox.
.with_policy(RuntimePolicy::permissive())
.with_tool_authorizer(AllowAllAuthorizer)
.build()?;
let _ = runtime;
Ok(())
}§Runtime Policy Defaults
Mentra’s builtin runtime tools are available by default, but command execution is not:
Runtime::builder()registers the builtin shell, background, file, task, team, and memory-oriented intrinsics- foreground shell execution is disabled by default
- background command execution is disabled by default
RuntimePolicy::permissive()enables both shell and background command executionRuntimePolicy::workspace_bounded(...)andRuntimePolicy::read_only(...)keep shell execution disabled; their roots constrain builtin file tools and the requested shell working directory, not shell process effects- builtin shell commands run through
/bin/sh -con Unix andcmd.exe /Con Windows - the local executor clears unlisted environment variables and enforces timeouts, output caps, and process-tree cleanup on timeout, but it does not restrict filesystem or network access
- semantic review is opt-in through
RuntimeBuilder::with_tool_authorizer(...)
Use the default policy when you want a safer runtime surface. Opt into
RuntimePolicy::permissive() only when an external sandbox already contains the
entire Mentra process and full host access is intentional.
If you need different command semantics, such as PowerShell on Windows, or
filesystem/network confinement, replace the default local executor with
RuntimeBuilder::with_executor(...). A workspace-bounded or read-only policy
can then explicitly enable foreground and background shell switches; Mentra
treats that executor as a trusted enforcement boundary and does not fall back
to the local executor.
§Tool Authorization
Mentra can run a caller-provided authorization pass before any tool executes. This is the recommended integration point for LLM-based security review, human approval, or custom policy engines.
- no authorizer installed: tools run under the remaining hard runtime constraints
- authorizer returns
Allow: the tool executes - authorizer returns
PromptorDeny: Mentra blocks execution and returns an errortool_result - authorizer timeout or error: Mentra fails closed and blocks execution
Every authorization request includes a ToolAuthorizationPreview with tool metadata plus structured input. Builtin tools provide more specific previews:
shellandbackground_runinclude the raw command, resolved working directory, timeout, background flag, and justificationfilesincludes resolved paths and operation kinds such asread,search,set,move, anddelete, without file contents
use async_trait::async_trait;
use mentra::tool::{
ToolAuthorizationDecision, ToolAuthorizationRequest, ToolAuthorizer,
};
struct DenyDeletes;
#[async_trait]
impl ToolAuthorizer for DenyDeletes {
async fn authorize(
&self,
request: &ToolAuthorizationRequest,
) -> Result<ToolAuthorizationDecision, mentra::error::RuntimeError> {
let structured = &request.preview.structured_input;
let denies_delete = structured
.get("operations")
.and_then(|value| value.as_array())
.is_some_and(|ops| ops.iter().any(|op| op.get("op").and_then(|v| v.as_str()) == Some("delete")));
if request.tool_name == "files" && denies_delete {
Ok(ToolAuthorizationDecision::deny("delete operations require manual approval"))
} else {
Ok(ToolAuthorizationDecision::allow())
}
}
}Registering a skills directory also makes the builtin load_skill tool available:
use mentra::{BuiltinProvider, Runtime};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
.with_skills_dir("./skills")?
.build()?;
let _ = runtime;
Ok(())
}§App Context
If your tools need access to typed host-side state, register it on the runtime and retrieve it from ToolContext or ParallelToolContext:
use std::sync::Arc;
use async_trait::async_trait;
use mentra::{
BuiltinProvider, Runtime,
tool::{ToolContext, ToolDefinition, ToolExecutor, ToolResult, ToolSpec},
};
use serde_json::{Value, json};
struct AppState {
api_base: String,
}
struct InspectStateTool;
impl ToolDefinition for InspectStateTool {
fn descriptor(&self) -> ToolSpec {
ToolSpec::builder("inspect_state")
.description("Return the configured API base URL.")
.input_schema(json!({
"type": "object",
"properties": {}
}))
.build()
}
}
#[async_trait]
impl ToolExecutor for InspectStateTool {
async fn execute_mut(&self, ctx: ToolContext<'_>, _input: Value) -> ToolResult {
let state = ctx.app_context::<AppState>()?;
Ok(state.api_base.clone())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
.with_context(Arc::new(AppState {
api_base: "https://api.example.com".to_string(),
}))
.with_tool(InspectStateTool)
.build()?;
let _ = runtime;
Ok(())
}§Custom Tools
Use ToolSpec::builder(...) to define custom tools without hand-assembling the metadata struct:
use async_trait::async_trait;
use mentra::tool::{
ParallelToolContext, ToolCapability, ToolDefinition, ToolDurability, ToolExecutor,
ToolResult, ToolSideEffectLevel, ToolSpec,
};
use serde_json::{Value, json};
struct UppercaseTool;
impl ToolDefinition for UppercaseTool {
fn descriptor(&self) -> ToolSpec {
ToolSpec::builder("uppercase_text")
.description("Uppercase the provided text")
.input_schema(json!({
"type": "object",
"properties": {
"text": { "type": "string" }
},
"required": ["text"]
}))
.capability(ToolCapability::ReadOnly)
.side_effect_level(ToolSideEffectLevel::None)
.durability(ToolDurability::ReplaySafe)
.execution_timeout(std::time::Duration::from_secs(5))
.build()
}
}
#[async_trait]
impl ToolExecutor for UppercaseTool {
async fn execute(&self, _ctx: ParallelToolContext, input: Value) -> ToolResult {
let text = input
.get("text")
.and_then(|value| value.as_str())
.ok_or_else(|| "text is required".to_string())?;
Ok(text.to_uppercase())
}
}ToolSpec::execution_timeout(...) is enforced by Mentra around the tool future itself, which is useful for network-backed tools that need a tighter budget than the overall agent run.
Internally, Mentra translates ToolSpec into a runtime-only RuntimeToolDescriptor, but custom runtime integrations should continue to treat ToolSpec::builder(...) as the supported public metadata surface. ExecutableTool remains available in this release as a compatibility trait alias over ToolDefinition + ToolExecutor.
When a tool needs disposable delegated work, ParallelToolContext::spawn_subagent() can create a child agent that inherits the current runtime and model defaults. See the subagent_tool example in the workspace examples crate for a complete usage pattern.
Override ToolExecutor::authorization_preview(...) when your custom tool needs to expose structured metadata to the installed ToolAuthorizer. The default preview includes the resolved working directory, tool capabilities, side-effect level, durability, the raw JSON input, and the same JSON as structured_input.
§Tooling Layers
Mentra now separates tool contracts into explicit layers:
ProviderToolSpecinmentra-providerfor provider-facing serializationRuntimeToolDescriptorin Mentra for scheduling, approval, and durability metadataToolDefinition + ToolExecutorfor executable runtime tools
Provider adapters should serialize provider-facing tool specs only. Runtime integrations should continue to implement custom tools with ToolSpec::builder(...), ToolDefinition, and ToolExecutor.
§Hosted Tool Search
Mentra can mark custom tools as deferred and let a provider load them on demand with native hosted tool search.
Mark a tool as deferred in its ToolSpec:
use async_trait::async_trait;
use mentra::tool::{ParallelToolContext, ToolDefinition, ToolExecutor, ToolResult, ToolSpec};
use serde_json::{Value, json};
struct LookupOrderTool;
impl ToolDefinition for LookupOrderTool {
fn descriptor(&self) -> ToolSpec {
ToolSpec::builder("lookup_order")
.description("Look up an order by id.")
.input_schema(json!({
"type": "object",
"properties": {
"order_id": { "type": "string" }
},
"required": ["order_id"]
}))
.defer_loading(true)
.build()
}
}
#[async_trait]
impl ToolExecutor for LookupOrderTool {
async fn execute(&self, _ctx: ParallelToolContext, _input: Value) -> ToolResult {
Ok("order loaded".to_string())
}
}Enable hosted tool search per agent with ProviderRequestOptions:
use mentra::agent::AgentConfig;
use mentra::provider::{ProviderRequestOptions, ReasoningEffort, ReasoningOptions, ToolSearchMode};
let config = AgentConfig {
provider_request_options: ProviderRequestOptions {
tool_search_mode: ToolSearchMode::Hosted,
reasoning: Some(ReasoningOptions {
effort: Some(ReasoningEffort::Medium),
summary: None,
}),
..Default::default()
},
..Default::default()
};Current provider support:
- OpenAI: supported through the Responses API hosted
tool_searchsurface - Anthropic: supported through the Messages API BM25 tool-search server tool
- Gemini: deferred custom tools are not supported; Mentra returns
InvalidRequest
Reasoning effort support:
- The shared levels are
low,medium,high,xhigh, andmax; omitting effort leaves the provider default unchanged. - OpenAI and OpenRouter: Mentra forwards all five levels as
reasoning.efforton the Responses API. - Anthropic: Mentra writes the requested level to
output_config.effortand enables adaptive thinking on models that support it. Opus 4.5 acceptslow/medium/higheffort without adaptive thinking; availability ofxhighandmaxdepends on the Claude model. - Gemini: Mentra maps the shared
low,medium, andhighlevels tothinkingLevelon Gemini 3 models, subject to that model’s accepted values.xhighandmaxreturnInvalidRequestinstead of being silently downgraded. - Anthropic models without effort support and Gemini models older than 3 return
InvalidRequestwhen unified reasoning effort is set.
Deferred tools are filtered through ToolProfile just like immediate tools. If you force a deferred tool with ToolChoice::Tool { name }, Mentra serializes that specific tool as immediate for the request so explicit invocation still works.
§Model Context Protocol Servers
Mentra connects to external MCP servers and bridges every tool they advertise
into the runtime under a namespaced mcp__<server>__<tool> name. Bridged tools
run through the same authorization, result limiter, and paging path as builtin
and custom tools.
Two transports are supported, selected by which configuration type you register.
stdio spawns the server as a child process:
use mentra::{BuiltinProvider, McpServerConfig, Runtime};
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::Anthropic, std::env::var("ANTHROPIC_API_KEY")?)
.with_mcp_server(McpServerConfig {
name: "filesystem".to_string(),
command: "npx".to_string(),
args: vec![
"-y".to_string(),
"@modelcontextprotocol/server-filesystem".to_string(),
"/tmp".to_string(),
],
env: Default::default(),
cwd: None,
})
.build_async()
.await?;Legacy HTTP+SSE reaches a hosted server over the network:
use mentra::{BuiltinProvider, McpSseServerConfig, Runtime};
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::Anthropic, std::env::var("ANTHROPIC_API_KEY")?)
.with_mcp_sse_server(
McpSseServerConfig::new("observability", "https://mcp.example.com/sse")
.with_bearer_token(std::env::var("MCP_TOKEN")?),
)
.build_async()
.await?;A server that answers 404 on /mcp but serves /sse needs this transport.
§HTTP+SSE is not Streamable HTTP
McpSseServerConfig speaks the transport from MCP protocol revision
2024-11-05, which is a different protocol from the newer Streamable HTTP:
| legacy HTTP+SSE | Streamable HTTP | |
|---|---|---|
| Endpoints | a GET stream plus a separate POST URL | one URL for both |
| POST target | named by the server in an endpoint event | the configured URL |
| Responses | always on the GET stream | in the POST response or a stream |
| Session | a query parameter in the endpoint URL | the Mcp-Session-Id header |
The client opens the configured URL with Accept: text/event-stream, waits for
an endpoint event naming the POST URL, then posts initialize, a
notifications/initialized notification, and a paginated tools/list. Servers
answer each POST 202 Accepted and deliver the actual JSON-RPC result as a
message event on the stream.
§Security and failure behavior
The endpoint URL is chosen by the server, so it is validated before anything is
sent to it. A resolved endpoint must match the configured URL’s scheme, host,
and effective port; a cross-origin endpoint, a protocol-relative //other.host
value, embedded credentials, and non-http(s) schemes are all refused. Redirects
are never followed on either request.
Configured headers are sent on both the stream and every POST, stored as
SecretString so they never appear in Debug output, errors, or logs.
Configuring headers against a plaintext http:// URL on a non-loopback host is
rejected unless allowing_plaintext_credentials() is set. No error carries a
response body or SSE payload, so a malicious server cannot write text into your
logs.
Losing the stream ends the session — the client fails closed rather than
hanging, and never reconnects or re-sends a tools/call. A call whose response
never arrived surfaces as McpSseError::RequestIndeterminate, because the POST
and the response travel on different connections: the tool may have run. Treat
that differently from a rejected POST, which definitely did not execute.
§Using the client directly
Hosts that need their own allowlist, redaction, or evidence policy can drive
McpSseClient without registering anything:
use mentra::{McpSseClient, McpSseServerConfig};
let config = McpSseServerConfig::new("observability", "https://mcp.example.com/sse")
.with_bearer_token(std::env::var("MCP_TOKEN")?);
let client = McpSseClient::connect(&config).await?;
for tool in client.tools() {
println!("{}", tool.name);
}
let result = client
.call_tool("search_logs", Some(serde_json::json!({"query": "error"})))
.await?;
println!("{}", result.is_error);
client.shutdown().await;§Tool Profiles
Register tools once on the runtime, then use AgentConfig::tool_profile to expose different subsets for different operating modes.
use mentra::{BuiltinProvider, ModelSelector, Runtime};
use mentra::agent::{AgentConfig, ToolProfile};
let runtime = Runtime::builder()
.with_provider(BuiltinProvider::OpenAI, std::env::var("OPENAI_API_KEY")?)
.build()?;
let model = runtime
.resolve_model(
BuiltinProvider::OpenAI,
ModelSelector::Id("gpt-5.4-mini".to_string()),
)
.await?;
let queue_mode = AgentConfig {
tool_profile: ToolProfile::only([
"shell",
"background_run",
"check_background",
"files",
"task",
]),
..Default::default()
};
let direct_mode = AgentConfig {
tool_profile: ToolProfile::hide(["task", "background_run"]),
..Default::default()
};
let _queue_agent = runtime.spawn_with_config("Queue Agent", model.clone(), queue_mode)?;
let _direct_agent = runtime.spawn_with_config("Direct Agent", model, direct_mode)?;This is the recommended pattern when one application needs multiple tool surfaces such as a queue-backed agent with delegation enabled and a direct mode that keeps the same runtime but hides long-running or task-oriented tools.
§CLI Integration Pattern
For CLI-style coding or analysis tools, the usual setup is:
- register a superset of builtin and custom tools on one runtime
- scope shell and file access with
RuntimePolicy - keep application-specific output paths in app context for custom tools
- switch behavior per mode by changing
AgentConfig::tool_profile, not by rebuilding the runtime - inspect
agent.history()after the run when you want to render a compact tool log or transcript summary
The cli_runtime example in the workspace examples crate shows this pattern end to end with custom tools, policy setup, mode-specific tool surfaces, and transcript inspection.
§Disposable Tasks vs Persistent Teams
Mentra supports two different delegation models:
- use the builtin
tasktool orParallelToolContext::spawn_subagent()for short-lived disposable delegation that should return a single summary to the parent - use
team_spawn,team_send,team_read_inbox,team_request, andteam_respondwhen you want a persistent teammate with a durable mailbox and request/response workflow across turns
The task path is ideal for one-off decomposition inside a single run. The team_* tools are for longer-lived collaborators that should keep state, receive follow-up work, and participate in approval or shutdown flows.
§Sending Images
You can attach image blocks alongside text when sending a user turn:
agent
.send(vec![
ContentBlock::text("What is happening in this screenshot?"),
ContentBlock::image_bytes("image/png", std::fs::read("screenshot.png")?),
])
.await?;For already-hosted assets, use ContentBlock::image_url(...) instead. Gemini currently supports inline image_bytes(...) inputs only and rejects image_url(...).
§Long-Term Memory
Agents automatically recall from long-term memory by default. When you use Runtime::builder(), the builtin runtime intrinsics include:
memory_searchfor explicit recallmemory_pinfor writing important factsmemory_forgetfor tombstoning a specific memory record
MemoryConfig controls recall and write behavior per agent. The default configuration enables automatic recall and memory write tools, which is useful for long-running assistants and teammate workflows. Disable write tools when you want recall without model-initiated mutation.
§Context Compaction
Agents compact context by default:
- old tool results are micro-compacted in outbound requests
- when estimated request context exceeds roughly 50k tokens, Mentra writes the full transcript to the default transcript directory and replaces older history with a model-generated summary
- the model can also call the builtin
compacttool explicitly
You can tune or disable this per-agent with CompactionConfig:
use mentra::agent::{AgentConfig, CompactionConfig};
let config = AgentConfig {
compaction: CompactionConfig {
auto_compact_threshold_tokens: Some(75_000),
..Default::default()
},
..Default::default()
};§Data And Persistence Defaults
For non-test builds, Mentra keeps all default persisted state under a workspace-scoped app-data directory:
- store:
<platform data dir>/mentra/workspaces/<workspace-hash>/runtime.sqlite - runtime-scoped stores:
<platform data dir>/mentra/workspaces/<workspace-hash>/runtime-<runtime-id>.sqlite - team state:
<platform data dir>/mentra/workspaces/<workspace-hash>/team/ - task state:
<platform data dir>/mentra/workspaces/<workspace-hash>/tasks/ - transcripts:
<platform data dir>/mentra/workspaces/<workspace-hash>/transcripts/
If the platform data directory cannot be resolved, Mentra falls back to .mentra/workspaces/<workspace-hash>/... inside the current workspace.
Override these defaults when needed:
- use
Runtime::builder().with_store(...)for the SQLite store - customize
AgentConfig::task.tasks_dir,AgentConfig::team.team_dir, andAgentConfig::compaction.transcript_dirfor task, team, and transcript storage
§Persistence Extension Points
The public persistence surface is intentionally split into narrower traits:
AgentStorefor agent records and working-memory snapshotsRunStorefor turn and run lifecycle trackingTaskStorefor the dependency-aware task boardLeaseStorefor runtime ownership and resume coordination
RuntimeStore composes those traits with TeamStore, BackgroundStore, and MemoryStore. SqliteRuntimeStore is the default all-in-one backend. HybridRuntimeStore keeps SQLite runtime state and swaps in the hybrid memory engine for richer long-term memory behavior.
§Testing With MockRuntime
Enable the test-utils feature when you want a deterministic scripted runtime for unit and integration tests.
mentra::test::MockRuntime wraps a real runtime with:
- a scripted provider
- a
VolatileRuntimeStore, so a mock writes nothing to disk and two mocks never share state — passMockRuntimeBuilder::with_storeaSqliteRuntimeStorewhen a test needs state that outlives the mock - deterministic per-turn helper methods for assistant text, streamed text, tool-call turns, and provider failures
This is the recommended way to test Mentra-based agents and tools without live API keys.
The common pattern is:
- build a
MockRuntime - register the same custom tools you use in production
- spawn an agent with the
AgentConfigorToolProfileyou want to verify - assert against
mock.recorded_requests()to confirm the runtime exposed the expected tools and tool-choice hints
See mentra::test and the crate tests for a full example of asserting runtime assembly with custom tools and filtered tool surfaces.
§Interactive Repo Example
Clone the repository when you want the richer interactive demo with provider selection, persisted runtime inspection, skills loading, and team/task visibility.
Set OPENAI_API_KEY, OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or GEMINI_API_KEY, then run. The example lets you choose a provider and shows up to 10 models from that provider ordered newest to oldest.
cargo run -p mentra-examples --example chatAdditional focused examples live in the same crate:
cargo run -p mentra-examples --example custom_tool
cargo run -p mentra-examples --example subagent_tool
cargo run -p mentra-examples --example team_collaboration
cargo run -p mentra-examples --example cli_runtime -- --mode directcli_runtime is the closest example to a real integration. It combines runtime policy setup, custom tools, mode-specific ToolProfile selection, and transcript inspection after the run.
§Run Checks
cargo fmt --all --check
cargo check --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspaceRe-exports§
pub use provider::Provider;pub use provider::ProviderRegistry;pub use agent::Agent;pub use agent::AgentConfig;pub use agent::AgentWaitFuture;pub use agent::AgentWaitHandle;pub use agent::FinalOutput;pub use agent::QueueMode;pub use agent::ReasoningChange;pub use agent::RoundAdjustment;pub use agent::RoundBoundary;pub use agent::RoundContext;pub use agent::RoundDecision;pub use agent::RoundStrategy;pub use agent::RoundToolResult;pub use agent::SpawnedAgentStatus;pub use agent::SpawnedAgentSummary;pub use agent::SteeringHandle;pub use agent::TerminalOutputSpec;pub use agent::ToolResultPagingConfig;pub use background::BackgroundNotification;pub use background::BackgroundTaskStatus;pub use background::BackgroundTaskSummary;pub use compaction::CompactionEngine;pub use compaction::CompactionMode;pub use compaction::StandardCompactionEngine;pub use mcp::McpClientError;pub use mcp::McpManager;pub use mcp::McpServerConfig;pub use mcp::McpServerStatus;pub use mcp::McpServerSummary;pub use mcp::McpSseClient;pub use mcp::McpSseConfigError;pub use mcp::McpSseError;pub use mcp::McpSseLimits;pub use mcp::McpSseServerConfig;pub use runtime::AgentStore;pub use runtime::AuditStore;pub use runtime::HybridRuntimeStore;pub use runtime::LeaseStore;pub use runtime::NewTask;pub use runtime::PermissionRuleStore;pub use runtime::RunStore;pub use runtime::Runtime;pub use runtime::RuntimeBuilder;pub use runtime::RuntimePolicy;pub use runtime::ShellValidationMode;pub use runtime::SkillInfo;pub use runtime::SkillLoadError;pub use runtime::TaskBoard;pub use runtime::TaskBoardError;pub use runtime::TaskPatch;pub use runtime::TaskStore;pub use session::PermissionDecision;pub use session::PermissionRequest;pub use session::RememberedRule;pub use session::RuleKey;pub use session::RuleStore;pub use session::Session;pub use session::SessionEvent;pub use session::SessionEventReceiver;pub use session::SessionId;pub use session::SessionMetadata;pub use session::SessionPermissionHandle;pub use session::SessionStatus;pub use session::SubagentHandle;pub use team::TeamDispatch;pub use team::TeamMemberStatus;pub use team::TeamMemberSummary;pub use team::TeamMessage;pub use team::TeamMessageKind;pub use team::TeamProtocolRequestSummary;pub use team::TeamProtocolStatus;pub use tool::FileToolProfile;pub use transcript::AgentTranscript;pub use transcript::BranchError;pub use transcript::CompactionSummary;pub use transcript::DelegationArtifact;pub use transcript::DelegationEdge;pub use transcript::DelegationKind;pub use transcript::DelegationStatus;pub use transcript::EntryId;pub use transcript::TranscriptItem;pub use transcript::TranscriptKind;pub use mentra_provider as provider_core;
Modules§
- agent
- Agent configuration, lifecycle, and event handling.
- background
- Background task coordination types and services.
- compaction
- Transcript compaction engine and related types.
- error
- mcp
- Model Context Protocol (MCP) client and tool bridge. Model Context Protocol (MCP) client support.
- memory
- Working-memory journal and long-term memory services.
- provider
- Provider integrations and transport-neutral request/response types.
- runtime
- Runtime orchestration, persistence, policies, and agent APIs.
- session
- Session types, metadata, and event stream primitives.
- team
- Team coordination types and collaboration services.
- tool
- Tool traits, metadata, and builtin tools.
- transcript
- Canonical runtime transcript primitives.
Structs§
- Anthropic
Request Options - Anthropic-specific request options.
- Gemini
Request Options - Gemini-specific request options.
- Message
- Provider-neutral chat message content.
- Model
Info - Metadata describing a model available from a provider.
- Provider
Capabilities - Capabilities advertised by a provider instance.
- Provider
Credentials - Provider credentials resolved at runtime.
- Provider
Definition - Serializable provider definition used by runtime and adapter layers.
- Provider
Descriptor - Human-facing metadata about a provider.
- Provider
Id - Stable identifier for a registered provider implementation.
- Provider
Request Options - Provider-specific request options that should be forwarded on the wire.
- Reasoning
Options - Provider-neutral reasoning controls supported across multiple providers.
- Reasoning
Provenance - Origin required to decide whether opaque reasoning metadata is safe to replay.
- Request
- Provider request assembled by the runtime before dispatch.
- Responses
Request Options - Shared Responses-family request options.
- Retry
Policy - Retry configuration for provider transport calls.
- Token
Usage - Provider-neutral token usage metadata for a completed or in-progress response.
Enums§
- Builtin
Provider - Builtin provider families Mentra can construct from presets.
- Content
Block - A provider-neutral content block exchanged with models.
- Content
Block Delta - Content
Block Start - Image
Source - Image payload supported by model providers.
- Model
Selector - Selection strategy used when resolving a model from a provider.
- Provider
Error - Errors returned by provider implementations and stream adapters.
- Provider
Event - Reasoning
Effort - Shared reasoning effort levels supported by Mentra’s public API.
- Reasoning
Format - Provider-specific format carried by a provider-neutral reasoning block.
- Responses
State Mode - Provider-side conversation state strategy for Responses-family providers.
- Responses
Transport - Streaming transport used by Responses-family providers.
- Role
- Provider-neutral chat role labels.
- Tool
Choice - Provider-neutral tool choice hint passed to model APIs.
- Tool
Search Mode - Provider-neutral tool search behavior requested for a model call.
- WireApi
- Wire protocol supported by a provider.
Functions§
- collect_
response_ from_ stream - Rebuilds a full response from a provider event stream.
- provider_
event_ stream_ from_ response - Converts a response into a provider event stream.