A hook-first runtime for agents that live alongside people.
Bub is a small Python runtime for building agents in shared environments. It started in group chats, where multiple humans and agents had to work in the same conversation without hidden state, hand-wavy memory, or framework-specific magic.
Core Bub stays intentionally small. Every turn stage is a pluggy hook. Builtins are included but replaceable. The same runtime drives CLI, Telegram, and any channel you add.
pip install bubOr from source:
git clone https://github.com/bubbuild/bub.git
cd bub
uv syncuv run bub chat # interactive session
uv run bub run "summarize this repo" # one-shot task
uv run bub gateway # channel listener mode- Hook-first runtime. Every turn stage is a hook. Override one stage or replace the whole flow without forking the runtime.
- Tape context. Context is rebuilt from append-only records, not carried around as mutable session state. Easier to inspect, replay, and hand off.
- One runtime across surfaces. The same inbound pipeline runs across CLI, Telegram, and custom channels. Adapters change the surface, not the runtime model.
- Batteries included. CLI, Telegram, tools, skills, and model execution ship with the core runtime. Use the defaults first, replace them later.
- Skills as documents. Skills are plain
SKILL.mdfiles with validated frontmatter. They stay readable, auditable, and easy to override.
Every inbound message goes through one turn pipeline. Each stage is a hook.
resolve_session → load_state → build_prompt → run_model
↓
dispatch_outbound ← render_outbound ← save_state
Builtins are registered first. External plugins load after them. At runtime, later plugins take precedence. There are no framework-only shortcuts.
If AGENTS.md exists in the workspace, it is appended to the system prompt automatically.
Key source files:
- Turn orchestrator:
src/bub/framework.py - Hook contract:
src/bub/hookspecs.py - Builtin hooks:
src/bub/builtin/hook_impl.py - Skill discovery:
src/bub/skills.py
from bub import hookimpl
class EchoPlugin:
@hookimpl
def build_prompt(self, message, session_id, state):
return f"[echo] {message['content']}"
@hookimpl
async def run_model(self, prompt, session_id, state):
return prompt[project.entry-points."bub"]
echo = "my_package.plugin:EchoPlugin"See the Extending docs for hook guides, packaging, and plugin structure.
| Command | Description |
|---|---|
bub chat |
Interactive REPL |
bub run MESSAGE |
One-shot turn |
bub gateway |
Channel listener (Telegram, etc.) |
bub install |
Install or sync Bub plugin deps |
bub update |
Upgrade Bub plugin deps |
bub login openai |
OpenAI Codex OAuth |
Lines starting with , enter internal command mode (,help, ,skill name=my-skill, ,fs.read path=README.md).
bub hooks still exists for diagnostics, but it is hidden from top-level help. bub install and bub update manage a separate uv project for Bub plugins, defaulting to ~/.bub/bub-project or BUB_PROJECT.
| Variable | Default | Description |
|---|---|---|
BUB_MODEL |
openrouter:qwen/qwen3-coder-next |
Model identifier |
BUB_API_KEY |
— | Provider key (optional with bub login openai) |
BUB_API_BASE |
— | Custom provider endpoint |
BUB_API_FORMAT |
completion |
completion, responses, or messages |
BUB_CLIENT_ARGS |
— | JSON object forwarded to the underlying model client |
BUB_MAX_STEPS |
50 |
Max tool-use loop iterations |
BUB_MAX_TOKENS |
1024 |
Max tokens per model call |
BUB_MODEL_TIMEOUT_SECONDS |
— | Model call timeout (seconds) |
Bub is shaped by one constraint: real collaboration is messier than a solo demo. In shared environments, operators need visible boundaries, auditable history, and extension points that do not collapse into framework sprawl.
Read more:
- Getting Started — install Bub and run the first turn
- Architecture — the mental model behind the runtime
- Channels — run Bub in CLI, Telegram, or your own channel
- Skills — discover, inspect, and author
SKILL.mdskills - Extending — write plugins, override hooks, ship tools and skills
- Deployment — Docker, environment, upgrades
- Posts — design notes
just install
uv run ruff check .
uv run mypy src
uv run pytest -q
just docs
just docs-testSee CONTRIBUTING.md.