hh 82029bb10d fix(ui): a stream that went quiet reconnects, the rail says so, the panel opens on the master
A phone that slept brings back an SSE socket that never errored and never
delivers: the panel read as live for hours while showing nothing new. Every
chunk now marks the connection alive and a watchdog drops the socket after
40s of silence - twice the server's keepalive - so the loop reconnects.
Each attempt gets its own controller, which is what lets the watchdog (and
a manual refresh) end one connection without ending the loop.

The connection is admitted in one dot beside the rail's search box, tapped
to reconnect at once rather than waiting out the backoff - the pill that
used to say this was dropped in the redesign and the panel had no way to
own up to being stale.

With nothing picked, the shell lands on the freshest open master instead of
a prompt to pick something: what a phone wants when the panel opens. An
explicit pick, and following the active note, still win.
2026-09-05 00:20:03 +02:00
2026-05-19 11:26:47 +02:00
2026-05-20 13:00:08 +02:00
2026-05-19 11:26:47 +02:00
2026-05-19 11:26:47 +02:00

beaver-gateway

A gateway for personal agents built on the Claude Agent SDK: conversations with a lifecycle, windows to talk through, jobs, memory hooks, and a policy boundary - without an opinion about what the agent is for. A setup is one config.py (or a package next to it) that assembles a Gateway; this repo holds the primitives, beaver-agent is a full setup built on them.

What it gives a setup

  • agents/ - what an agent is: a Claude agent with a prompt per conversation kind, skill sets per kind, gateway tools, a PreToolUse policy; a Raycast agent for cheap fast answers.
  • backends/ - how a turn runs: the Claude Agent SDK subprocess with a session pool, transcripts and session stores; the Raycast wire.
  • conversations/ - the model of a conversation: kinds (master, branch, deep, job, fork), rows and windows, the inject queue with priorities, seeds, turns, questions, closing through a distiller, master rotation, the envelope, the in-process gateway tools.
  • jobs/ - cron, webhook and event jobs on pgqueuer, deferred injects, the subscription budget.
  • frontends/ - the windows: Telegram (master = General, topic = branch), markdown files in a vault, /api + the admin SPA, an Anthropic-compatible /anthropic/v1/messages, MCP re-exposure at /mcp/<name>.
  • mcp/ - MCP servers a setup declares (stdio, http, python tools), aggregated in-process and handed to agents by name.
  • vault/ - watching a directory of notes for the envelope.
  • security/ - bearer tokens, the audit log, credential redaction.
  • events/ - the in-process bus every frontend taps, and the stream event protocol backends emit.
  • storage/ - SQLModel tables and the Postgres session store.

config.py defines Gateway and loads a setup; app.py builds the runtime from it; cli.py is the entrypoint. Every string the gateway puts in front of a model or a user has an English default and is overridable (ConversationTexts, TelegramTexts).

Run

cd examples
CLAUDE_CODE_OAUTH_TOKEN=$(claude setup-token) docker compose up --build

examples/config.py is the smallest setup that starts: one agent, one python tool, one cron job, the built-in frontends on port 8000; the compose file next to it adds Postgres. Without Docker: uv sync, build the SPA with cd ui && bun install && bun run build, then run uv run beaver-gateway with DATABASE_URL and CONFIG_PATH set. A setup's own secrets come from .env next to its config (load_dotenv runs before the config is executed).

Develop

make check   # ruff format --check, ruff check, ty, pytest
make fix

No comments in code: modules carry a one-line docstring saying what they are for, public configuration fields carry a one-line docstring saying what they mean; everything else is the code.

S
Description
Agentic registry, gateway and head of operations for Beaver agent
Readme
1.8 MiB
Languages
Python 67.7%
Svelte 23.2%
TypeScript 7.4%
CSS 1.3%
Dockerfile 0.2%
Other 0.1%