Bridge is an event-driven handoff bus for autonomous agents.
It gives you a bounded place to create, receive, track, and close agent-to-agent work without turning every agent vault into a shared memory swamp.
- creates explicit handoff records between agents
- stores an authoritative canonical record plus recoverable sender and recipient queue views
- enforces route policy and status transitions
- exposes a local HTTP API for wrappers and automation
- supports durable at-least-once lifecycle events plus immediate compatibility notifications
- supports read-only event consumers for surfacing lifecycle events into chat/UI adapters
- includes patrol tooling for reminders and unresolved-handoff detection
Bridge ships with no built-in agent IDs and no default route graph.
You choose your own agent names. Examples in this README use placeholders like agent-a and agent-b, but those are only sample values.
Route policy is optional:
- if
BRIDGE_ALLOWED_ROUTESis unset, Bridge allows any sender -> recipient pair - if
BRIDGE_ALLOWED_ROUTESis set, Bridge only allows the configured pairs - API auth tokens are discovered from
BRIDGE_TOKEN_<AGENT_ID>entries, so your deployed agent IDs come from config, not from the codebase
You do not need to edit bridge_core/models.py or bridge_core/policy.py to adopt your own ecosystem.
For deeper docs, start with docs/README.md and docs/bridge-v2-reliability.md.
bridge_core/- core models, policy, auth, repository, and service logicscripts/- CLI, wrappers, API server, patrol tooling, intake watcher, and token rotationtests/- pytest coverage for core flows, API, wrappers, patrol, and intake watchersconfig/bridge_api.example.env- example config for API tokens and notify hooksdeploy/systemd/- generic systemd templates for self-hosted deploymentdocs/- architecture, deployment, and runtime documentationexamples/handoffs/- example markdown payloads for common handoff patterns
git clone https://github.com/camaragon/bridge.git
cd bridgeBridge uses the Python standard library at runtime.
python3 --versionExpected: Python 3.10+
python3 -m pytest -qThis is the fastest way to see Bridge working.
python3 scripts/bridge_cli.py create \
--sender agent-a \
--recipient agent-b \
--issue-type task \
--subject "Demo handoff" \
--requested-action "Inspect the repo and report back" \
--minimal-context "Started from the README quickstart" \
--idempotency-key "demo-create-1"You should get JSON like:
{
"handoff_id": "HND-20260426-000000-abcd",
"outbox": ".../bridge/outgoing/agent-a/HND-...md",
"inbox": ".../bridge/incoming/agent-b/HND-...md"
}List open handoffs for the recipient:
python3 scripts/bridge_cli.py list-open --agent agent-bMark it closed:
python3 scripts/bridge_cli.py set-status --actor agent-b HND-... closed --outcome "Completed and reported back"Archive it:
python3 scripts/bridge_cli.py archive --actor agent-b HND-...The wrappers prefer the local API.
mkdir -p config
cp config/bridge_api.example.env config/bridge_api.env
mkdir -p bridge/incoming bridge/outgoing bridge/archive bridge/auditEdit config/bridge_api.env and replace:
BRIDGE_TOKEN_AGENT_ABRIDGE_TOKEN_AGENT_BBRIDGE_TOKEN_AGENT_C
with real secret values.
You usually do not need to change BRIDGE_ROOT or BRIDGE_API_CONFIG if you are running from this repo checkout.
python3 scripts/bridge_api_server.pyBy default it listens on 127.0.0.1:8427.
curl http://127.0.0.1:8427/v1/healthExpected:
{"ok": true, "service": "bridge-api"}python3 scripts/bridge_agent.py --agent agent-a create \
--recipient agent-b \
--issue-type task \
--subject "Wrapper demo" \
--requested-action "Verify API-backed handoff creation" \
--minimal-context "Started from the README API quickstart"List open handoffs:
python3 scripts/bridge_agent.py --agent agent-b list-openAcknowledge receipt:
python3 scripts/bridge_agent.py --agent agent-b ack HND-...Close with a resolution summary:
python3 scripts/bridge_agent.py --agent agent-b close HND-... --outcome "Verified and complete"GET /v1/healthPOST /v1/handoffsGET /v1/handoffsGET /v1/handoffs/{handoff_id}POST /v1/handoffs/{handoff_id}/ackPOST /v1/handoffs/{handoff_id}/blockPOST /v1/handoffs/{handoff_id}/closePOST /v1/handoffs/{handoff_id}/cancelPOST /v1/handoffs/{handoff_id}/statusPOST /v1/handoffs/{handoff_id}/archive
Create accepts Idempotency-Key or idempotency_key. Lifecycle writes accept expected_revision or an If-Match header and return HTTP 409 for stale revisions.
Durable outbox and reconciliation commands:
python3 scripts/bridge_cli.py outbox-list --state pending
python3 scripts/bridge_cli.py outbox-deliver --limit 100
python3 scripts/bridge_cli.py outbox-replay EVT-...
python3 scripts/bridge_cli.py outbox-recover
python3 scripts/bridge_cli.py outbox-resolve-transaction EVT-... --action enqueue --reason "verified missing delivery"
python3 scripts/bridge_cli.py reconcile --check-onlyBridge supports two layers of notification behavior:
-
recipient notify URLs
- configured with
BRIDGE_NOTIFY_URL_<AGENT> - Bridge POSTs lifecycle events to recipient listeners
- configured with
-
read-only lifecycle event consumers
- configured with
BRIDGE_NOTIFY_EVENT_COMMAND_<AGENT> bridge_intake_watch.pycan execute a command forhandoff_closedandhandoff_blocked- the command receives raw JSON on stdin
- this keeps Bridge core separate from Telegram, BlueBubbles, Slack, or other UI-specific adapters
- configured with
-
read-only intake event consumers
- configured with
BRIDGE_INTAKE_EVENT_COMMAND_<AGENT>or genericBRIDGE_INTAKE_EVENT_COMMAND - agent-scoped commands take precedence over the generic command; CLI
--intake-event-commandoverrides both bridge_intake_watch.pycan execute a command after auto-acknowledging a newly received handoff- the command receives a generic
handoff_acknowledgedJSON event on stdin - use this for local work-queue mirroring, indexing, or alerting without baking those systems into Bridge
- configured with
Run a recipient listener:
python3 scripts/bridge_intake_watch.py --agent agent-b --listen --port 8522Run a one-shot inbox check:
python3 scripts/bridge_intake_watch.py --agent agent-b --onceBridge includes patrol tooling for follow-up pressure without mutating the API surface.
See docs/runtime-layout.md for runtime directory expectations.
Run patrol manually:
python3 scripts/bridge_patrol.py --stuck-hours 24Patrol can:
- detect unacknowledged open handoffs
- re-hit notify endpoints after a delay
- emit active unresolved alerts for placeholder summaries like
pending - deduplicate reminders through
bridge/audit/patrol-reminders.json
Main runtime variables:
BRIDGE_PROJECT_ROOTBRIDGE_ROOTBRIDGE_API_CONFIGBRIDGE_API_HOSTBRIDGE_API_PORTBRIDGE_TOKEN_AGENT_ABRIDGE_TOKEN_AGENT_BBRIDGE_TOKEN_AGENT_CBRIDGE_NOTIFY_URL_<AGENT_ID>BRIDGE_NOTIFY_EVENT_COMMAND_<AGENT_ID>BRIDGE_ALLOWED_ROUTES
Bridge is intentionally split like this:
- Bridge core owns handoff state, auditability, and lifecycle events
- adapters/listeners decide how to surface those events to humans or agent runtimes
That keeps the core OSS-friendly and prevents messaging-platform logic from being baked into the handoff engine.
Generic systemd templates live in:
deploy/systemd/bridge-api.servicedeploy/systemd/bridge-intake-watch@.servicedeploy/systemd/example-agent.env
These are templates for self-hosted deployments, not host-specific copies.
Run the full test suite:
pytest -qCompile-check scripts and tests:
python3 -m py_compile bridge_core/*.py scripts/*.py tests/*.pyApache-2.0