Counting visitors…
Browse all docs
Start · 9Find your path through RunifoldLearn Runifold in 45 minutesUnderstand the complete Runifold platformYour first trustworthy runChoose the right execution APIChoose crates and Cargo featuresBuild common Runifold applicationsRunifold frequently asked questionsTroubleshoot Runifold applications
Execution kernel · 7Understand RunContextCoordinate external effects safelyBound work with budgets and cancellationHandle errors and retries safelyEvents, journals, and execution evidenceDesign capability-safe executionRecover safely from checkpoints
Models & providers · 7Route across models without duplicate outputChoose and configure a providerUse the provider-neutral model protocolBuild on the Provider Runtime contractUse OpenAI control-plane and Realtime APIsTest and benchmark provider adaptersSet up OpenAI, Anthropic, Gemini, and Ollama
Agents · 7Build and configure an AgentGive an Agent typed toolsAdd conversations and semantic memoryDelegate to child Agents safelyReturn structured Rust valuesStream without losing semanticsGround an Agent with retrieval
Durable workflows · 7Compose deterministic workflowsMake workflows durableOperate durable workflow workersCoordinate timers, signals, and durable waitsRun multi-tenant workflow infrastructureRun parallel branches and safe racesVersion and evolve durable workflows
Integrations · 7Connect through MCPChoose stores and persistence boundariesExpose durable work through MCP TasksBuild and evaluate retrieval pipelinesUse MCP Resources, Prompts, and SamplingCache MCP responses without crossing authorityDeploy Runifold in a Rust web service
Quality & operations · 10Test without the networkEvaluate quality and prevent regressionsObserve the complete run treeRun safely in browsers and at the edgeRead reliability claims preciselyRun reproducible evaluations in CIOperate Runifold with SLOsGovern Task retention and deletionArchive audit evidence to S3-compatible WORM storageManage compatibility and trusted releases
Docs/Build agents
NEW TO RUNIFOLD?Build the complete mental model in 45 minutes
Build agents

Give an Agent typed tools

Expose async Rust functions through JSON Schema without granting ambient authority.

Practical guide·12 min

Define a tool

A Runifold Tool has a stable descriptor, JSON input schema, effect classification, and an asynchronous execution boundary. Prefer the #[runifold::tool] macro for ordinary typed functions.

Add the derive dependencies once:

cargo add serde --features derive
cargo add schemars
use runifold::{JsonSchema, ToolContext, ToolError, tool};
use serde::{Deserialize, Serialize};
 
#[derive(Debug, Deserialize, JsonSchema)]
struct WeatherInput {
    city: String,
}
 
#[derive(Debug, Serialize, JsonSchema)]
struct Weather {
    city: String,
    celsius: i32,
}
 
#[tool(description = "Get the current temperature for a city")]
async fn current_weather(
    input: WeatherInput,
    _context: ToolContext,
) -> Result<Weather, ToolError> {
    weather_service::lookup(&input.city).await
}

Use domain input types instead of a bag of strings. Validation should happen at the boundary, before external work begins.

Register the tool

Register the tool on only the Agent that needs it:

let agent = runtime
    .agent("travel-planner")
    .tool(current_weather_tool())
    .max_turns(6)
    .build()?;

Duplicate names and collisions with child-Agent routes are build errors. A registered tool does not automatically become available to every child Run; execution still requires the matching capability grant.

Return rich results

Since 0.9.0, a manual Tool returns ToolOutput, not one scalar value. Choose the constructor that matches the real boundary:

NeedConstructorMeaning
ordinary JSON or textToolOutput::model_visible(value)keeps structured JSON and a text fallback
ordered text, image, audio, document, or resource partsToolOutput::rich(parts)preserves the provider-neutral content sequence
recoverable domain failureToolOutput::model_error(parts)tells the model the Tool ran but the application rejected the request
host-only dataToolOutput::host_only(parts)fails closed if code tries to expose it to a model

Attach separately validated output with with_structured_content, and keep application-only annotations in namespaced with_metadata entries. A ToolError still means the Tool runtime itself failed; model_error is a completed, model-visible application result.

Code written against 0.3.x that constructs ToolOutput { value, ... } must migrate to one of these constructors.

Keep binary artifacts out of transcripts

Large Tool outputs should remain references in conversations and checkpoints. Configure an ArtifactStore and a validated scope once on the Agent:

use std::sync::Arc;
use runifold::{ArtifactScope, InMemoryArtifactStore};
 
let artifact_store = Arc::new(InMemoryArtifactStore::new());
let agent = runtime
    .agent("report-analyst")
    .artifacts(ArtifactScope::parse("tenant.acme")?, artifact_store)
    .tool(render_chart_tool())
    .build()?;

Inside the Tool, read the configured store and scope from ToolContext, write with a stable idempotency key, then return the resulting ArtifactRef as a MediaSource::Artifact. Runifold resolves and verifies the bytes only at the Provider transport boundary.

Use InMemoryArtifactStore for tests, SqliteStore for one durable process, or PostgresStore for shared production workers. Every reference binds scope, MIME type, byte length, SHA-256 digest, optional name, and expiry. Listing is bounded and cursor-based; deletion and expiry purging stay scope-bound.

Provider support is explicit: OpenAI Responses and Gemini preserve native multimodal Tool results; Anthropic supports text, image, and resource forms; Bedrock supports JSON, images, and documents. Text-only Chat Completions and Ollama reject unsupported media instead of silently flattening it.

Authority and effects

Tool registration answers “what could this Agent call?” A RunContext capability answers “what may this execution call?” Both must agree.

Classify external actions accurately:

  • pure reads can usually be retried;
  • idempotent writes require stable idempotency keys;
  • non-idempotent writes must fail closed after ambiguous transport failure;
  • destructive or high-risk operations should pass through application policy.

For recoverable writes, use the write-ahead effect boundary so intent is recorded before execution and a completed result can be replayed without repeating the action.

Handle tool errors

ToolErrorPolicy controls whether a tool failure becomes model-visible input or terminates the Agent. Do not expose secrets, raw credentials, database URLs, or internal stack traces in model-visible error text.

An operational error should preserve enough structure for the application to decide whether to retry, compensate, ask a human, or fail the run.