diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 025a58b5..1e180697 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,7 @@ Then, install and activate the environment with: uv sync ``` -1. Install prek to run linters/formatters at commit time: +4. Install prek to run linters/formatters at commit time: ```bash uv run prek install @@ -96,7 +96,7 @@ Now, validate that all unit tests are passing: just test ``` -9. Before raising a pull request you should also run tox. +8. Before raising a pull request you should also run tox. This will run the tests across different versions of Python: ```bash diff --git a/README.md b/README.md index a6a427bf..f15b3323 100644 --- a/README.md +++ b/README.md @@ -1,120 +1,126 @@ # Bub -[Bub](https://github.com/bubbuild/bub) is a common shape for agents. +**A common shape for agents that live alongside people.** -It starts from a simple question: if there are many agents in the world, what kind of agent is a Bub? +Bub started in group chats. Not as a demo or a personal assistant, but as a teammate that had to coexist with real humans and other agents in the same messy conversations — concurrent tasks, incomplete context, and nobody waiting. -A Bub is an agent that can live inside shared operator environments with explicit boundaries, visible execution evidence, and safe handoff. +It is hook-first, built on [pluggy](https://pluggy.readthedocs.io/), with a small core (~200 lines) and builtins that are just default plugins you can replace. Context comes from [tape](https://tape.systems), not session accumulation. The same pipeline runs across CLI, Telegram, and any channel you add. -The point is not only to complete tasks, but to remain understandable, reviewable, and continuable when more humans and agents join the work. +[Website](https://bub.build) · [GitHub](https://github.com/bubbuild/bub) -## Current Implementation - -This repository is the current Python implementation of Bub. -It is hook-first, built on `pluggy`, and keeps the core small while builtins and plugins provide behavior. -In this implementation, Bub uses [Republic](https://github.com/bubbuild/republic) as its context runtime and [constructs context from tape](https://tape.systems). +## Quick Start -- CLI bootstrap: `src/bub/__main__.py` (Typer app) -- Turn orchestrator: `src/bub/framework.py` -- Hook contract: `src/bub/hookspecs.py` -- Builtin hooks/runtime: `src/bub/builtin/hook_impl.py` + `src/bub/builtin/engine.py` -- Skill discovery and validation: `src/bub/skills.py` +```bash +pip install bub +``` -## Quick Start +Or from source: ```bash git clone https://github.com/bubbuild/bub.git cd bub uv sync -uv run bub --help ``` ```bash -# Runtime off: falls back to model_output=prompt -BUB_RUNTIME_ENABLED=0 uv run bub run "hello" +uv run bub chat # interactive session +uv run bub run "summarize this repo" # one-shot task +uv run bub gateway # channel listener mode ``` -```bash -# Internal command mode (line starts with ',') -BUB_RUNTIME_ENABLED=0 uv run bub run ",help" -``` +## How It Works -```bash -# Model runtime (hosted providers usually require a key) -BUB_API_KEY=your_key uv run bub run "Summarize this repository" -``` +Every inbound message goes through one turn pipeline. Each stage is a hook. -```bash -# OpenAI Codex OAuth (no provider API key required) -uv run bub login openai -BUB_MODEL=openai:gpt-5-codex uv run bub chat ``` +resolve_session → load_state → build_prompt → run_model + ↓ + dispatch_outbound ← render_outbound ← save_state +``` + +Builtins are plugins registered first. Later plugins override earlier ones. No special cases. -## CLI Commands +If `AGENTS.md` exists in the workspace, it is appended to the system prompt automatically. -- `bub run MESSAGE`: execute one inbound turn and print outbound messages -- `bub login openai`: persist OpenAI Codex OAuth credentials for later runs -- `bub hooks`: print hook-to-plugin bindings -- `bub install PLUGIN_SPEC`: install plugin from PyPI or `owner/repo` (GitHub shorthand) +Key source files: -## Runtime Behavior +- Turn orchestrator: [`src/bub/framework.py`](src/bub/framework.py) +- Hook contract: [`src/bub/hookspecs.py`](src/bub/hookspecs.py) +- Builtin hooks: [`src/bub/builtin/hook_impl.py`](src/bub/builtin/hook_impl.py) +- Skill discovery: [`src/bub/skills.py`](src/bub/skills.py) -- Regular text input: uses `run_model`; if runtime is unavailable, output falls back to the prompt text -- Comma commands: `,help`, `,tools`, `,fs.read ...`, etc. -- Unknown comma commands: executed as `bash -lc` in workspace -- Session event log: `.bub/runtime/.jsonl` -- `AGENTS.md`: if present in workspace, appended to runtime system prompt +## What Sets It Apart -## Skills +Bub grew up in multi-person chats with multiple agents running at the same time. Single-user flows hide structural problems; shared environments expose them fast. That shaped a few things: -- Discovery roots with deterministic override: - 1. `/.agent/skills` - 2. `~/.agent/skills` - 3. `src/skills` -- Each skill directory must include `SKILL.md` -- Supported frontmatter fields: - - required: `name`, `description` - - optional: `license`, `compatibility`, `metadata`, `allowed-tools` +- **Context from tape.** History is append-only facts. Anchors mark phase transitions. Context is assembled on demand — not accumulated, not compressed into lossy summaries. +- **Hooks all the way down.** The turn pipeline *is* hooks. Override `build_prompt`, `run_model`, or `render_outbound` to change behavior. The core does not privilege its own builtins. +- **One pipeline across channels.** CLI and Telegram share the same `process_inbound()` path. Hooks don't know which channel they're in. +- **Skills as documents.** Skills are `SKILL.md` files with validated frontmatter, not code modules with magic registration. -## Plugin Development +## Extend It -Plugins are loaded from Python entry points in `group="bub"`: +```python +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 +``` ```toml [project.entry-points."bub"] -my_plugin = "my_package.my_plugin" +echo = "my_package.plugin:EchoPlugin" ``` -Implement hooks with `@hookimpl` following `BubHookSpecs`. +See the [Extension Guide](https://bub.build/extension-guide/) for hook semantics and plugin packaging. -## Runtime Environment Variables +## CLI -- `BUB_RUNTIME_ENABLED`: `auto` (default), `1`, `0` -- `BUB_MODEL`: default `openrouter:qwen/qwen3-coder-next` -- `BUB_API_KEY`: runtime provider key; optional when using `openai:*` models with `bub login openai` -- `BUB_API_BASE`: optional provider base URL -- `BUB_API_FORMAT`: upstream API shape; default `completion` - Use `responses` for OpenAI Responses-compatible providers and `messages` for chat-completions-style providers. -- `BUB_RUNTIME_MAX_STEPS`: default `8` -- `BUB_RUNTIME_MAX_TOKENS`: default `1024` -- `BUB_RUNTIME_MODEL_TIMEOUT_SECONDS`: default `90` +| Command | Description | +|---------|-------------| +| `bub chat` | Interactive REPL | +| `bub run MESSAGE` | One-shot turn | +| `bub gateway` | Channel listener (Telegram, etc.) | +| `bub login openai` | OpenAI Codex OAuth | +| `bub hooks` | Print hook-to-plugin bindings | -```bash -# Use a Responses-compatible upstream API. -BUB_MODEL=openai:gpt-5-codex \ -BUB_API_FORMAT=responses \ -uv run bub chat -``` +Lines starting with `,` enter internal command mode (`,help`, `,tools`, `,fs.read path=README.md`). + +## Configuration -## Documentation +| 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_RUNTIME_MAX_STEPS` | `50` | Max tool-use loop iterations | +| `BUB_RUNTIME_MAX_TOKENS` | `1024` | Max tokens per model call | +| `BUB_RUNTIME_MODEL_TIMEOUT_SECONDS` | — | Model call timeout (seconds) | -- `docs/index.md`: overview -- `docs/architecture.md`: lifecycle, precedence, and failure isolation -- `docs/skills.md`: skill discovery and frontmatter constraints -- `docs/cli.md`: CLI usage and comma command mode -- `docs/features.md`: implemented capabilities and limits +## Background -## Development Checks +We care less about whether an agent can finish a demo task, and more about whether it can coexist with real people under real conditions. Context is not baggage to carry forever — it is a working set, constructed when needed and let go when done. + +Read more: [Context from Tape](https://tape.systems) · [Socialized Evaluation and Agent Partnership](https://bub.build/posts/2026-03-01-bub-socialized-evaluation-and-agent-partnership/) + +## Docs + +- [Architecture](https://bub.build/architecture/) — lifecycle, hook precedence, error handling +- [Features](https://bub.build/features/) — what ships today and current boundaries +- [CLI](https://bub.build/cli/) — commands and comma mode +- [Skills](https://bub.build/skills/) — discovery and authoring +- [Extension Guide](https://bub.build/extension-guide/) — hooks, tools, plugin packaging +- [Channels](https://bub.build/channels/) — adapters and sessions +- [Deployment](https://bub.build/deployment/) — Docker, Telegram, operations + +## Development ```bash uv run ruff check . @@ -122,6 +128,8 @@ uv run mypy src uv run pytest -q ``` +See [CONTRIBUTING.md](./CONTRIBUTING.md). + ## License [Apache-2.0](./LICENSE) diff --git a/docs/cli.md b/docs/channels/cli.md similarity index 93% rename from docs/cli.md rename to docs/channels/cli.md index 9b556984..5d98c3ec 100644 --- a/docs/cli.md +++ b/docs/channels/cli.md @@ -1,6 +1,6 @@ # CLI -`bub` currently exposes five builtin commands: `run`, `gateway`, `chat`, `login`, and the hidden compatibility command `message`. +`bub` exposes four main commands (`run`, `gateway`, `chat`, `login`) plus two hidden ones (`hooks` for diagnostics, `message` as a compatibility alias for `gateway`). ## `bub run` diff --git a/docs/channels/index.md b/docs/channels/index.md index 0206a580..3a0b2390 100644 --- a/docs/channels/index.md +++ b/docs/channels/index.md @@ -1,13 +1,11 @@ # Channels -Bub uses channel adapters to run the same agent pipeline across different I/O endpoints. +Bub uses channel adapters to run the same pipeline across different I/O endpoints. Hooks don't know which channel they're in. ## Builtin Channels -- `cli`: local interactive terminal channel (`uv run bub chat`) -- `telegram`: Telegram bot channel (`uv run bub gateway`) - -See [Telegram](telegram.md) for channel-specific configuration and runtime behavior. +- `cli`: local interactive terminal — see [CLI](cli.md) +- `telegram`: Telegram bot — see [Telegram](telegram.md) ## Run Modes diff --git a/docs/core/index.md b/docs/core/index.md deleted file mode 100644 index bfb1064f..00000000 --- a/docs/core/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# Core Overview - -This section groups core runtime design and behavior. - -## Includes - -- [Architecture](../architecture.md): execution lifecycle, hook precedence, and error semantics. -- [Key Features](../features.md): high-level capability summary and current boundaries. - -## Suggested Reading Order - -1. [Architecture](../architecture.md) -2. [Key Features](../features.md) diff --git a/docs/features.md b/docs/features.md index 96afd4e2..1b60df75 100644 --- a/docs/features.md +++ b/docs/features.md @@ -1,35 +1,45 @@ # Key Features -## Framework Core +## Hook-First Runtime -- Hook-first architecture powered by `pluggy`. -- Deterministic turn pipeline in `BubFramework.process_inbound()`. -- Safe fallback to prompt text when `run_model` returns no value (with `on_error` notification). -- Automatic fallback outbound when `render_outbound` produces nothing. +Every turn stage is a [pluggy](https://pluggy.readthedocs.io/) hook. +Builtins are ordinary plugins — override any stage by registering your own. +Both first-result hooks (override) and broadcast hooks (observer) are supported. +Safe fallback to prompt text when `run_model` returns no value (with `on_error` notification). +Automatic fallback outbound when `render_outbound` produces nothing. -## Runtime And Commands +## Tape-Based Context -- Builtin CLI commands: `run`, `hooks`, `message`, `chat`. -- Builtin `RuntimeEngine`: - - normal input goes through model + tool loop (Republic) - - comma-prefixed input enters internal command mode (`,help`, `,tools`, `,fs.read`, etc.) - - unknown internal commands fall back to shell execution via the `bash` tool -- Runtime events are persisted to tapes (default under `~/.bub/tapes`). +Runtime events are recorded to tapes (default under `~/.bub/tapes`). +Context is reconstructed from tape records, not accumulated in session state. -## Channel Capability +## Builtin Batteries -- Builtin channels: `cli` and `telegram`. -- `message` mode runs the same framework pipeline for channel-driven traffic. -- Outbound delivery is routed by `ChannelManager`, keeping business hooks channel-agnostic. +- **CLI**: `run`, `chat`, `gateway`, `login`, `hooks` via Typer. +- **Model runtime**: agent loop with tool use, backed by [Republic](https://github.com/bubbuild/republic). +- **Comma commands**: `,help`, `,tools`, `,fs.read`, etc. Unknown commands fall back to shell. +- **Channels**: `cli` and `telegram` ship as defaults. + +All of these are hook implementations. Replace what you need. + +## Channel-Agnostic Pipeline + +CLI and Telegram use the same `process_inbound()` path. +Hooks don't know which channel they're in. +Outbound routing is handled by `ChannelManager`. + +## Skills + +Skills are `SKILL.md` files with validated frontmatter. +Plugins can ship their own by including a `skills/` directory. ## Plugin Extensibility -- External plugins are loaded via Python entry points (`group="bub"`). -- Later-registered plugins run first and can override builtin behavior. -- Supports both first-result hooks (override style) and broadcast hooks (observer style). +External plugins are loaded via Python entry points (`group="bub"`). +Later-registered plugins run first and can override builtin behavior. ## Current Boundaries -- No strict envelope schema: `Envelope` is intentionally flexible. +- `Envelope` is intentionally weakly typed (`Any` + accessor helpers). - No centralized key contract for shared plugin `state`. -- Core repository does not currently ship a builtin Discord channel adapter. +- No builtin Discord adapter — implement one via `provide_channels`. diff --git a/docs/index.md b/docs/index.md index a6b09eea..0d570a92 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,13 @@ # Bub -[Bub](https://github.com/bubbuild/bub) is a common shape for agents. +**A common shape for agents that live alongside people.** -It exists to answer a harder question than "can an agent finish a task?": -when many humans and agents work in the same environment, what kind of agent remains understandable, reviewable, and safe to continue? +Bub started in group chats. Not as a demo or a personal assistant, but as a teammate that had to coexist with real humans and other agents in the same messy conversations — concurrent tasks, incomplete context, and nobody waiting. -Bub's answer is an agent form with explicit boundaries, visible evidence, and safe handoff. -The current repository is one implementation of that idea, using hook-based composition, [Republic](https://github.com/bubbuild/republic) as the context runtime, and [constructing context from tape](https://tape.systems) as the current context model. +It is hook-first, built on [pluggy](https://pluggy.readthedocs.io/), with a small core (~200 lines) and builtins that are just default plugins you can replace. Context comes from [tape](https://tape.systems) via [Republic](https://github.com/bubbuild/republic), not session accumulation. The same pipeline runs across CLI, Telegram, and any channel you add. ## Quick Start -Install dependencies and create local config: - ```bash git clone https://github.com/bubbuild/bub.git cd bub @@ -19,35 +15,30 @@ uv sync cp env.example .env ``` -Run interactive local chat: - ```bash -uv run bub chat +uv run bub chat # interactive session +uv run bub run "summarize this repo" # one-shot task +uv run bub gateway # channel listener mode ``` -Run a one-shot task: - -```bash -uv run bub run "summarize this repository" -``` +## How It Works -Start channel listener mode: +Every inbound message goes through one turn pipeline. Each stage is a hook. -```bash -uv run bub gateway +``` +resolve_session → load_state → build_prompt → run_model + ↓ + dispatch_outbound ← render_outbound ← save_state ``` -## Deployment - -For production setup and operations, read: - -- [Deployment Guide](deployment.md) -- [Channels Overview](channels/index.md) -- [Telegram Channel](channels/telegram.md) +Builtins are plugins registered first. Later plugins override earlier ones. No special cases. ## Read Next -- [Core Overview](core/index.md): architecture and capability summary in one place -- [Workflows Overview](workflows/index.md): CLI and skills usage in one place -- [Extension Guide](extension-guide.md): build and publish hook-based extensions -- [Posts](posts/index.md): project notes and updates +- [Architecture](architecture.md) — lifecycle, hook precedence, error handling +- [Features](features.md) — what ships today and current boundaries +- [Channels](channels/index.md) — CLI, Telegram, and custom adapters +- [Skills](skills.md) — discovery and authoring +- [Extension Guide](extension-guide.md) — hooks, tools, plugin packaging +- [Deployment](deployment.md) — Docker, environment, upgrades +- [Posts](posts/index.md) — design notes diff --git a/docs/posts/index.md b/docs/posts/index.md index 6ce63c5d..cf0d5933 100644 --- a/docs/posts/index.md +++ b/docs/posts/index.md @@ -1,8 +1,6 @@ # Posts -Project posts and long-form notes. +Design notes and project updates. -## Entries - -- [Baby Bub: From Inspiration to Bootstrap Milestone](2025-07-16-baby-bub-bootstrap-milestone.md) -- [Bub: Socialized Evaluation and Agent Partnership](2026-03-01-bub-socialized-evaluation-and-agent-partnership.md) +- [Socialized Evaluation and Agent Partnership](2026-03-01-bub-socialized-evaluation-and-agent-partnership.md) — why Bub is designed for teams, not demos +- [Baby Bub: From Inspiration to Bootstrap Milestone](2025-07-16-baby-bub-bootstrap-milestone.md) — the first steps toward a self-improving agent diff --git a/docs/workflows/index.md b/docs/workflows/index.md deleted file mode 100644 index e3ad1413..00000000 --- a/docs/workflows/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workflows Overview - -This section groups day-to-day operator workflows. - -## Includes - -- [CLI](../cli.md): interactive chat, one-shot runs, hook inspection, and channel listener commands. -- [Skills](../skills.md): skill discovery rules, frontmatter validation, and runtime usage. - -## Common Flow - -1. Use [CLI](../cli.md) to run and verify behavior quickly. -2. Add or tune skills following [Skills](../skills.md). diff --git a/mkdocs.yml b/mkdocs.yml index 67aa4e97..87b33477 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ site_name: bub repo_url: https://github.com/bubbuild/bub site_url: https://bub.build -site_description: "An agent partner for real teams: visible execution, reliable collaboration." +site_description: "A common shape for agents that live alongside people." site_author: Bub Build edit_uri: edit/main/docs/ repo_name: bubbuild/bub @@ -10,16 +10,13 @@ copyright: Copyright (c) 2026 Bub Build None: def create_cli_app(self) -> typer.Typer: """Create CLI app by collecting commands from hooks. Can be used for custom CLI entry point.""" - app = typer.Typer(name="bub", help="Batteries-included, hook-first AI framework", add_completion=False) + app = typer.Typer( + name="bub", help="A common shape for agents that live alongside people.", add_completion=False + ) @app.callback(invoke_without_command=True) def _main(