LLM agent built to run anywhere

TUI, ACP agent, server — or embed as Rust library.

openheim TUI home screen

or if you prefer GUI you can check openheim app

One agent.
Many ways
to run it.

From your editor to production APIs.

ACP transport
Run as a native ACP stdio agent (openheim acp) inside Zed, JetBrains, and any ACP-compatible editor. Deploy as a WebSocket server for remote agent access.
Read the docs on ACP transport
MCP integration
Connect any MCP server over stdio or Streamable HTTP. Tools are auto-discovered at startup and exposed as server__tool — no code changes needed.
Read the docs on MCP integration
Four run modes
Interactive TUI, headless CLI (openheim run), ACP stdio (openheim acp), or REST + WebSocket server (openheim serve) — one binary, four entry points.
Read the docs on Four run modes
Provider-agnostic
OpenAI, Anthropic, Gemini, Ollama, vLLM, and any OpenAI-compatible endpoint. Switch with one line in config.toml — no vendor lock-in.
Read the docs on Provider-agnostic
Skills system
Drop Markdown files into ~/.openheim/skills/ and they inject into the system prompt. Pass skills per-session via CLI or from any ACP client.
Read the docs on Skills system
Subagents
Hand a self-contained task to another agent with its own persona, model and tools, and get back only the answer. Keep the orchestrator on a strong model and send routine subtasks to a cheaper one.
Read the docs on Subagents
Conversation history
Every session persists to disk. Resume any conversation from the TUI (:sessions), headless CLI, or over ACP with session/load — the agent continues exactly where it left off.
Read the docs on Conversation history
Long-term memory
Ask the agent to remember something and it saves a note it can search in later sessions. Keyword search works offline with SQLite. Add an embedding provider and search becomes semantic. Nothing is stored unless the agent calls a memory tool.
Read the docs on Long-term memory
Built for long sessions
When a session outgrows the model's context window, the oldest turns are left out of the request, and a tool call is never split from its result. The saved history keeps everything. Rate limits and server errors retry with backoff.
Read the docs on Built for long sessions

Or just open a terminal.

Run openheim with no arguments and you get a full chat UI. The footer shows the live context size, and tool calls wait for your approval.

openheim TUI home screen with the green theme
+ gray, cyan, magenta, yellow, pink
  • :modelsSwitch model mid-session and keep the conversation going
  • :sessionsBrowse saved conversations and pick up any of them
  • :mcpSee which MCP servers are connected
  • :skillsList the skills you can load
Read the docs on the terminal UI

Works inside your editor.

openheim implements the Agent Client Protocol (ACP) — run it as a subprocess inside your editor or deploy it as a remote WebSocket server. The same agent logic powers both.

openheim acp

Native editor agent

Runs as a subprocess — no port, no daemon. Register it in your editor's ACP agent settings and you have a full tool-equipped agent inside Zed, JetBrains, or any ACP-compatible client. Skills, MCP servers, and conversation history all work identically.

# ACP stdio — register in your editor's agent settings
openheim acp

# Compatible with Zed, JetBrains,
# and any ACP-compliant editor extension.
ZedJetBrainsAny ACP client
openheim serve

Self-hosted server

Exposes ACP over a multiplexed WebSocket and a REST API. Multiple clients connect simultaneously — each session gets isolated context. Conversation history survives WebSocket reconnections and server restarts.

# Start the WebSocket + REST server
openheim serve

# ws://localhost:1217/ws   (ACP agent + filesystem)
# http://localhost:1217/api/*  (config, models, sessions)
Remote ACP clientsCustom frontendsAutomation pipelines

Run openheim in any environment.

Home, cloud, or enterprise — choose the right operating mode for your team.

01

Home / Local

  • —Interactive TUI for conversational workflows — :sessions to restore any past conversation
  • —openheim run "<prompt>" for quick one-off headless tasks
  • —openheim acp to integrate directly with Zed, JetBrains, or any ACP editor
02

Cloud / API

  • —openheim serve exposes ACP over WebSocket and a REST API on port 1217
  • —Multiple ACP clients connect simultaneously — each session is fully isolated
  • —Conversation history survives WebSocket reconnections and server restarts
03

Enterprise

  • —Provider-agnostic — route to any OpenAI-compatible endpoint, Azure OpenAI, or Bedrock
  • —Filesystem access scoped to workspace root; add auth at the nginx or Caddy proxy layer
  • —Run one instance per tenant — separate config, history directory, and API keys

What the agent can do

execute_commandopt-in

Run shell commands: scripts, build tools, git, anything the shell can do

read_file

Read the full contents of a file by path

write_file

Write or overwrite a file at the given path, creating it if it does not exist

edit_file

Replace an exact string in a file without rewriting the whole thing

list_dir

List the immediate contents of a directory, non-recursively

search

Ripgrep-style regex search across files, respecting .gitignore

web_fetch

Fetch a public web page or text resource and return it as plain text

delegate_task

Hand off a self-contained task to a subagent with its own persona, model, and tools

remember

Save a note to long-term memory so later sessions can find it

search_memory

Search saved notes by keyword, or by meaning when an embedding provider is set

edit_memory

Update a saved note in place

forget

Delete a saved note by id

Extend with MCP — add any MCP server to ~/.openheim/config.toml and its tools are auto-discovered at startup, namespaced as server__tool.

Safe by default.

An agent that runs tools needs limits. These are on before you change a single setting.

Read the docs on sandboxing

Shell is opt-in

execute_command stays hidden from the model until you set allow_shell = true in config.toml.

Files stay in work_dir

File tools can't read or write outside the work directory, and symlinks can't be used to escape it.

You approve tool calls

The TUI and ACP editors ask before each call: allow once, allow always, or reject. "Always" on a shell command covers only those exact arguments.

Read-only architect mode

Switch a session to architect mode and the agent only gets tools that can't change anything.

Guarded web fetch

web_fetch refuses private, loopback and cloud metadata addresses, and stops after 20 seconds or 256 KiB.

Bounded commands

Each shell command runs in its own process group, is killed after 120 seconds, and has its output capped at 64 KiB.

Shell commands can still use absolute paths outside work_dir. For full isolation, run openheim in a container.

Ship it your way.

01

Local binary

cargo install openheim or build from source. Run the TUI, openheim run for one-shot prompts, openheim acp for editor integration, or openheim serve for the WebSocket + REST server.

cargo install openheim
02

Docker

Multi-stage Dockerfile keeps the image lean. The Compose file passes API keys as environment variables and mounts a volume for ~/.openheim, so config and conversation history survive restarts.

docker compose up
03

Cloud / self-hosted

Deploy the binary or Docker image anywhere that runs Linux — Fly.io, Railway, EC2, a Raspberry Pi. Serve on port 1217 behind your proxy of choice, managed with systemd.

any linux host
04

Rust library

Embed the agent runtime directly in your app via the openheim crate. A single OpenheimClient facade covers sessions, streaming, skills, and MCP.

cargo add openheim

Extend it in Rust.

Embed the agent in your own app, or plug in tools, model backends and approval flows. Each one is a single trait.

OpenheimClient

Embed the runtime

Sessions, streaming, history, skills, memory and MCP behind one client. Leave out the CLI, TUI and server with default-features = false.

let client = OpenheimClient::builder().build().await?;
let session = client.new_session().cwd("/my/project").start().await?;

session.prompt("Summarise the README", |event| {
    if let StreamEvent::LlmResponse { content } = event {
        print!("{content}");
    }
}).await?;
Read the docs on library usage
ToolHandler

Add your own tools

Implement two methods and the agent can call your code. Tools get the same work_dir boundary and approval flow as the built-ins.

#[async_trait]
impl ToolHandler for TicketLookup {
    fn definition(&self) -> Tool { /* name, description, JSON schema */ }

    async fn execute(&self, args: &str, turn: &TurnContext<'_>) -> Result<String> {
        // run your code; the string goes back to the model
    }
}

OpenheimClient::builder().tool(Box::new(TicketLookup)).build().await?;
Read the docs on custom tools
LlmClient

Bring any model

Wire in a backend that isn't built in: a private API, a research endpoint, or a mock for tests. OpenAI-compatible servers need no code at all.

#[async_trait]
impl LlmClient for MyProvider {
    async fn send(&self, messages: &[Message], tools: &[Tool]) -> Result<Choice> {
        // call your model and map its reply to a Choice:
        // text, or tool calls for the agent to run
    }
}
Read the docs on custom LLM providers
PermissionGate

Own the approval flow

Decide how tool calls get approved: a CLI prompt, a Slack message, a policy check. Subagent calls go through the same gate.

#[async_trait]
impl PermissionGate for SlackApproval {
    async fn check(&self, req: &PermissionRequest<'_>) -> PermissionDecision {
        // ask someone about req.tool_name and req.arguments
        PermissionDecision::AllowOnce
    }
}

let session = client.new_session().start().await?
    .permission_gate(Arc::new(SlackApproval));
Read the docs on permission gates

Common questions

How do I install openheim? +

Run cargo install openheim — it's published on crates.io. Docker images and prebuilt binaries work too. See docs.rs/openheim for full API documentation.

Does openheim require a specific LLM provider? +

No. openheim works with OpenAI, Anthropic, Gemini, and any OpenAI-compatible endpoint — including local models via Ollama, vLLM, or LM Studio. Switch providers with one line in config.toml.

Does openheim work inside my editor? +

Yes. Run openheim acp to start an ACP (Agent Client Protocol) stdio agent. Register it in your editor's ACP agent settings and you get a full tool-equipped agent inside Zed, JetBrains, and any other ACP-compatible client — no separate service needed.

Is it safe to let openheim run shell commands? +

The shell tool is off until you set allow_shell = true, and file tools can't reach outside the configured work_dir. In the TUI and in ACP editors, openheim asks before each tool call. Shell commands can still use absolute paths, so for full isolation run openheim in a container.

Can I connect external MCP servers? +

Yes. Add any MCP server to [mcp_servers] in ~/.openheim/config.toml using either stdio or Streamable HTTP transport. Tools are discovered at startup and namespaced as server__tool — available to the agent automatically.

Can I resume a conversation later? +

Yes. Every session is persisted to disk with a UUID. Resume from the interactive TUI with :sessions, via headless CLI, or over ACP with session/load — the agent continues exactly where it left off.

Does openheim remember things between sessions? +

Yes, when you ask it to. The agent saves notes with the remember tool and finds them in later sessions with search_memory. Out of the box that's keyword search in a local SQLite file, with no network calls. Add an embedding provider under [memory] and search becomes semantic. Nothing is stored unless the agent calls a memory tool.

Can openheim split work across multiple agents? +

Yes. The delegate_task tool hands a self-contained task to a subagent with its own persona, model and tools, and only the final answer comes back. Save reusable profiles as Markdown files in ~/.openheim/agents/, or let the agent define one-off subagents inline.

Can I add custom prompt context or domain expertise? +

Yes. Drop Markdown files into ~/.openheim/skills/ and reference them by name with --skills. They are injected into the system prompt at runtime — no code changes needed. ACP clients can also pass skills per-session.

Can I use openheim as a library instead of a CLI? +

Yes. Add openheim as a dependency with default-features = false to embed it directly in your own Rust application, without pulling in the CLI, TUI, or server extras.