Plain Agent
A small agent that calls OpenAI compatible LLM APIs.
This project starts with a streaming agent loop:
Interactive terminal
-> reads your prompt
-> streams assistant text as it arrives
-> detects tool calls from the model
-> runs workspace tools when requested
-> sends tool results back to the model
-> repeats until the assistant gives a final answer
Install
Plain Agent is available on PyPI and can be installed with pipx:
pipx install plain-agent
If pipx is not installed, follow the official installation guide.
For local development, this project uses uv to track the Python environment.
If uv is not installed, follow the official installation guide.
uv sync
On Linux, install Bubblewrap to enable the run_command tool:
# Debian / Ubuntu
sudo apt install bubblewrap
# Fedora / RHEL
sudo dnf install bubblewrap
# Arch Linux
sudo pacman -S bubblewrap
Plain Agent verifies that Bubblewrap is usable at startup. If it is missing or the kernel does not
permit the required user and network namespaces, command execution is disabled and the file tools
remain available. There is no unsandboxed command fallback. To keep the sandbox launcher outside
the workspace trust boundary, Plain Agent checks only /usr/bin/bwrap and /bin/bwrap; it does
not resolve Bubblewrap through PATH.
Configuration
Create a local .env file or export environment variables in your shell. See .env.example for more examples.
For DeepSeek:
export DEEPSEEK_API_KEY="your-api-key"
export LLM_PROVIDER="deepseek"
export LLM_MODEL="deepseek-v4-flash"
For OpenAI:
export OPENAI_API_KEY="your-api-key"
export LLM_PROVIDER="openai"
export LLM_MODEL="gpt-5.4-mini"
You can still set LLM_BASE_URL when you want to override the provider default, such as pointing at a local OpenAI compatible server like Ollama.
Context compaction runs automatically when the estimated conversation history reaches 200k tokens.
Set LLM_COMPACTION_AUTO_MAX_TOKENS to change that threshold, or set it to 0 to disable automatic compaction.
You can also run /compact in the terminal to compact manually.
Web search
The Exa-backed web_search tool is enabled by default and every query requires explicit approval
before Plain Agent connects to mcp.exa.ai. Search results include links and bounded relevant
excerpts. No Exa API key is required.
To disable web search:
export PLAIN_AGENT_ENABLE_NETWORK="false"
This setting controls only the built-in search tool. The run_command sandbox remains offline.
Run
uv run plain-agent
Command sandbox (Linux)
Every run_command request requires user approval and still runs through Bubblewrap after it is
approved. The approval prompt shows the requested mode and an unambiguous shell-quoted
representation of the exact argument vector. Backslashes and non-printable characters are escaped
so command arguments cannot rewrite the terminal prompt. Approval is a user decision; Bubblewrap
is the independent OS enforcement boundary.
Commands are passed as an argument array and never receive an implicit shell. For example,
["rg", "TODO", "."] runs directly, while shell syntax must be explicit as
["bash", "-lc", "printf '%s\\n' *.py"].
Two modes are available:
read-onlyis the default and mounts the workspace read-only.workspace-writepermits persistent workspace changes, except protected paths.
Both modes have networking disabled, including host loopback. The sandbox starts with an empty
filesystem view and exposes only the workspace, required read-only system runtimes, a minimal set
of /etc files, and temporary in-memory /tmp, /run, and HOME directories. The environment is
cleared; only a filtered PATH, locale, terminal, and color settings are retained. API keys and
arbitrary parent variables are not inherited by commands.
In workspace-write, existing .git and .venv directories are rebound read-only. .agents,
.codex, and .sandbox are hidden. .env plus recognized private-key and certificate files are
masked in both modes, as are existing pathname Unix sockets. In-process file tools continue to use
their existing workspace permission checks and are not run through Bubblewrap.
Additional toolchains can be exposed read-only with an OS-path-separated list of absolute, existing paths:
export PLAIN_AGENT_SANDBOX_ADDITIONAL_READ_ROOTS="/opt/toolchain:/home/me/.local/share/special-runtime"
Each extra root expands the confidentiality boundary: sandboxed commands can read everything below it. Paths are canonicalized and deduplicated before use. Avoid exposing home directories or credential stores.
The command sandbox is deliberately offline. Package downloads, remote Git operations, and calls
to local network services fail. Linux is the only supported command-sandbox platform in this
milestone; macOS and Windows keep run_command disabled. Seccomp syscall filtering is planned as
the next Linux hardening step after the filesystem and network policy is stable.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plain_agent-0.1.7.tar.gz.
File metadata
- Download URL: plain_agent-0.1.7.tar.gz
- Upload date:
- Size: 52.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95e34c329b54cdcbb25faa58c85818864607daa6880987b7cbb04534909e0c14
|
|
| MD5 |
6a3d528c36e9b3a432bc006b2bf8df3c
|
|
| BLAKE2b-256 |
ecc8b7fab567167c4eac727e055af2cbed27d64c2adfffeb2257f2b42492447b
|
File details
Details for the file plain_agent-0.1.7-py3-none-any.whl.
File metadata
- Download URL: plain_agent-0.1.7-py3-none-any.whl
- Upload date:
- Size: 45.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce053525f6849f8127f15b2821748e961bfb542b2b20455f4afc347751b2c0f4
|
|
| MD5 |
1b83421a73434d63f10c2d25b5f6a0bc
|
|
| BLAKE2b-256 |
f1cc2d89288a026e423d617b758dd6a130fc2fb5744978c6b1211027f439bb60
|