Rust AI agent toolkit that reimplements ideas from earendil-works/pi as a terminal-first coding agent and reusable agent crates.
Simplified Chinese | Changelog | Spec
Current workspace version: 0.3.0.
opi is a working terminal coding agent. It includes an interactive ratatui TUI, text and NDJSON non-interactive modes, eight built-in tools, image attachments, model and session pickers, shell completion generation, layered TOML config, per-provider proxy config, multi-provider streaming, JSONL session persistence, context compaction, retry/backoff, configurable keybindings/themes, token usage accumulation, and best-effort cost summaries.
opi-web-ui remains a placeholder crate with publish = false; it does not contain a real web UI yet.
opi borrows pi's ideas and design boundaries, but it is not API-compatible with pi and does not read pi config or session files by default.
| Area | pi direction | opi treatment |
|---|---|---|
| Product surface | Minimal terminal coding harness | Terminal-first Rust coding agent and reusable Rust crates |
| Core coding tools | Default read, write, edit, bash |
Same interactive default tool set |
| Read-only navigation | read, grep, find, ls |
Same core read-only set; glob is an extra convenience and core workflows should not depend on it |
| Extensibility | Extensions, skills, prompt templates, themes, packages | Phase 4 focuses on RPC, SDK, extensions, skills, prompt fragments, themes, and packages |
| Workflow-heavy features | MCP, sub-agents, plan mode, todos, and permission gates stay outside core | Keep them as extension/package examples instead of built-in core policy |
| Config and sessions | .pi JSON settings and pi session files |
TOML config and opi JSONL sessions |
| Web UI | Available in pi's package set | Deferred until RPC/SDK surfaces are useful |
Cargo workspace with lockstep versioning. Every crate inherits version, edition, license, repository, and authors from [workspace.package].
| Crate | Published | Description |
|---|---|---|
opi-ai |
yes | Provider-neutral LLM API, streaming events, image content, registry, retry, HTTP pooling/proxy, usage and cost helpers |
opi-agent |
yes | Agent loop, tool execution, hooks, events, queues, sessions, compaction, transport abstraction |
opi-tui |
yes | Ratatui widgets, transcript rendering, diff view, select list, terminal images, themes, keybindings |
opi-coding-agent |
yes | The opi binary and embeddable coding harness |
opi-web-ui |
no (publish = false) |
Reserved web chat component crate |
Internal dependency shape:
opi-agent -> opi-ai
opi-web-ui -> opi-ai
opi-coding-agent -> opi-ai + opi-agent + opi-tui -> opi binary
The executable is named opi and is produced by the opi-coding-agent crate.
cargo install opi-coding-agent
opi --versionPre-built binaries for Linux, macOS, and Windows on x64 and arm64 are attached to GitHub Releases.
Set credentials for the provider you want to use:
export ANTHROPIC_API_KEY=sk-ant-...
# or OPENAI_API_KEY, OPENROUTER_API_KEY, MISTRAL_API_KEY, GEMINI_API_KEY
# or AWS credentials for Bedrock, AZURE_OPENAI_API_KEY, VERTEX_ACCESS_TOKENRun the interactive TUI:
opiRun one prompt and print assistant text to stdout:
opi "List the Rust crates in this workspace."Emit NDJSON events for automation:
opi --json "Summarize the latest session state."Attach images to the initial prompt:
opi --image screenshot.png "Review this UI."
opi --image before.png --image after.png "Compare these images."Pick a model with provider:model syntax:
opi -m anthropic:claude-sonnet-4-5-20250514 "Explain crates/opi-agent/src/lib.rs"
opi -m openai:gpt-4o "Review the public API shape."
opi -m openai-responses:gpt-4o-mini "Find documentation gaps."
opi -m openrouter:openai/gpt-4o-mini "List TODO comments."
opi -m mistral:codestral-latest "Explain the tool modules."
opi -m gemini:gemini-2.5-flash "Summarize the README files."
opi -m bedrock:anthropic.claude-sonnet-4-20250514-v2:0 "Summarize this repo."
opi -m azure:my-deployment "Use my Azure OpenAI deployment."
opi -m vertex:gemini-2.5-flash "Use Vertex AI."Provider support is implemented in opi-ai and wired into opi-coding-agent.
| Model prefix | Backend | Default credentials |
|---|---|---|
anthropic: |
Anthropic Messages SSE | ANTHROPIC_API_KEY |
openai: |
OpenAI Chat Completions SSE | OPENAI_API_KEY |
openai-responses: |
OpenAI Responses SSE | OPENAI_API_KEY |
openrouter: |
OpenAI-compatible OpenRouter profile | OPENROUTER_API_KEY |
mistral: |
OpenAI-compatible Mistral profile | MISTRAL_API_KEY |
gemini: |
Gemini streamGenerateContent SSE |
GEMINI_API_KEY |
bedrock: |
AWS Bedrock Converse streaming with SigV4 | AWS env vars or shared AWS config/credentials |
azure: |
Azure OpenAI Chat Completions deployment | AZURE_OPENAI_API_KEY plus config endpoint |
vertex: |
Google Vertex AI Gemini streaming | VERTEX_ACCESS_TOKEN plus config project/location |
Use opi --list-models to list models advertised by configured providers. Add --json for machine-readable output.
Tools are implemented by opi-coding-agent and exposed through the opi-agent::Tool trait.
| Tool | Args | Execution | Mutating |
|---|---|---|---|
read |
path, optional offset, limit |
parallel | no |
ls |
path, optional max_entries, max_depth |
parallel | no |
glob |
pattern |
parallel | no |
find |
pattern, optional path |
parallel | no |
grep |
pattern |
parallel | no |
write |
path, content |
sequential | yes |
edit |
path, old_string, new_string |
sequential | yes |
bash |
command, optional timeout_secs |
sequential | yes |
All file paths are constrained to the harness workspace root. Mutating tools require --allow-mutating or defaults.allow_mutating_tools = true, so unattended and edge-device runs stay read-only unless the caller explicitly opts into writes or shell execution.
Tool selection flags:
opi --tools read,grep "Inspect the code without edits."
opi --no-tools "Answer using only conversation context."
opi --no-builtin-tools "Run without built-in tools."Config layers merge user config, project config, and explicit config files. Model precedence is:
--modelOPI_MODELwhen--configwas not passedmodelin--config <FILE><CWD>/.opi/config.toml- User config (
%APPDATA%\opi\config.tomlon Windows,~/.config/opi/config.tomlon Unix) - Built-in defaults
Example:
[defaults]
model = "anthropic:claude-sonnet-4"
max_iterations = 50
tool_timeout_ms = 30000
max_image_bytes = 20971520
theme = "default"
allow_mutating_tools = false
[thinking]
enabled = true
budget_tokens = 10000
[retry]
max_attempts = 3
initial_delay_ms = 1000
max_delay_ms = 60000
[compaction]
enabled = true
threshold_tokens = 100000
[keybindings]
submit = "enter"
abort = "escape"
new_line = "alt+enter"
[providers.anthropic]
api_key_env = "ANTHROPIC_API_KEY"
# base_url = "https://api.anthropic.com"
[providers.openai]
api_key_env = "OPENAI_API_KEY"
# base_url = "https://api.openai.com"
[providers.openai_responses]
api_key_env = "OPENAI_API_KEY"
# base_url = "https://api.openai.com"
[providers.openrouter]
api_key_env = "OPENROUTER_API_KEY"
# base_url = "https://openrouter.ai/api"
# referer = "https://example.com"
[providers.mistral]
api_key_env = "MISTRAL_API_KEY"
# base_url = "https://api.mistral.ai"
[providers.gemini]
api_key_env = "GEMINI_API_KEY"
# base_url = "https://generativelanguage.googleapis.com"
[providers.bedrock]
region = "us-east-1"
# profile = "default"
# base_url = "https://bedrock-runtime.us-east-1.amazonaws.com"
# secret_access_key_env = "AWS_SECRET_ACCESS_KEY"
# session_token_env = "AWS_SESSION_TOKEN"
[providers.azure]
api_key_env = "AZURE_OPENAI_API_KEY"
endpoint = "https://my-resource.openai.azure.com"
api_version = "2024-06-01"
deployments = ["my-deployment"]
[providers.vertex]
access_token_env = "VERTEX_ACCESS_TOKEN"
project = "my-gcp-project"
location = "us-central1"
models = ["gemini-2.5-flash", "gemini-2.5-pro"]
[providers.openai.proxy]
url = "http://proxy.example.com:8080"
no_proxy = "localhost,127.0.0.1"When a provider proxy is not configured, opi falls back to standard HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables.
With no prompt args, opi starts the ratatui TUI. Useful commands inside the input box:
| Command | Effect |
|---|---|
/model |
Open the model picker for the active provider |
/session |
Open the session picker and resume a stored session |
/image <path> |
Queue an image for the next prompt |
exit or quit |
Exit the TUI |
Default keybindings are enter to submit, escape to abort/exit, and alt+enter for a new line. They can be changed in [keybindings].
Sessions are JSONL files written automatically by the coding harness.
Default location:
- Windows:
%LOCALAPPDATA%\opi\sessions\ - Unix:
~/.local/share/opi/sessions/
Override with OPI_SESSIONS_DIR.
opi --list-sessions
opi --resume <session-id> "Continue from this session."
opi --delete-session <session-id>Session files store a header plus message, compaction, and leaf entries. Resume reconstructs the active branch and preserves compaction summaries. --json emits session events, retry events, compaction events, thinking-level events, and a final session summary with token totals and optional cost totals.
The coding harness discovers AGENTS.md and CLAUDE.md from the workspace directory upward to the git root, then from the user config directory. Files over 128 KiB and empty files are ignored. OPI.md is intentionally not loaded.
The workspace uses Rust edition 2024, requiring Rust 1.85 or newer.
cargo build
cargo build --release
cargo run -p opi-coding-agent -- --help
cargo test --workspace --all-targets
cargo test -p opi-ai
cargo fmt --check --all
cargo clippy --workspace --all-targets -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-depsopi chooses a mode at startup:
- Session/model/completion commands are handled early and exit.
- Non-interactive mode is selected by prompt args,
--non-interactive, or--json; it builds a provider and runsNonInteractiveRunner. - Interactive mode is the default with no prompt args; it builds a
CodingHarnesswith interactive hooks and launches the ratatui TUI.
Both interactive and non-interactive modes use the same agent loop:
transform_context
-> convert_to_llm
-> provider.stream(Request)
-> accumulate assistant stream events
-> detect tool calls
-> validate JSON Schema args
-> before_tool_call
-> execute tools in parallel or sequential batches
-> after_tool_call
-> should_stop_after_turn
-> prepare_next_turn
-> poll steering/follow-up queues
Key abstractions:
opi_ai::Provider: streaming LLM backend interface.opi_ai::AssistantStreamEvent: provider-neutral stream event model for text, thinking, tool calls, completion, and errors.opi_agent::Tool: JSON Schema based tool contract with parallel/sequential execution modes.opi_agent::AgentHooks: lifecycle hooks around message conversion, tool policy, tool results, stopping, and next-turn preparation.opi_agent::SessionWriter/SessionReader: append-only JSONL session storage with crash recovery.opi_agent::CompactionEngine: threshold/manual/overflow compaction support.opi_agent::Transport: reserved transport abstraction; Phase 4 must either make it real RPC/proxy infrastructure, mark it unstable, or remove it before stable API claims.
- Sub-agents, permission gates, plan/todo workflows, and skills as extension/package product features.
- Prompt template registry.
- MCP tool server integration through an extension/package path.
- OAuth or subscription login flows.
- Real web UI widgets in
opi-web-ui.
Releases publish to GitHub Releases and crates.io with the opi-release skill (.claude/skills/opi-release/skill.md).
- All publishable crates use the same version.
- Publish order follows internal dependencies.
- Pushing a
v*tag triggers.github/workflows/release.yml. - Rollback uses
git revertand tag deletion, never history rewriting.
- Use Conventional Commits.
- Keep crate metadata inherited from
[workspace.package]. - Run
cargo fmt --check --all,cargo clippy --workspace --all-targets -- -D warnings, tests, and docs as appropriate. - See
AGENTS.mdfor repository working rules used by humans and agents.
Bug reports and PRs are welcome at https://github.com/OdradekAI/opi/issues.
MIT (c) OdradekAI. See LICENSE.