Exactly-once lifecycle shell for Band agents, with a runnable proof and a public conformance check.
Project description
band-once
Exactly-once delivery for Band agents, as a tiny, reusable library.
Band serves an agent's inbox through a lifecycle cursor: GET /agent/chats/{id}/messages/next
returns the oldest not-yet-processed message and re-serves the same message on every call
until you mark it processed or failed. That cursor is correct, but it means a crash anywhere
in the middle of handling a message (before you post, after you post but before you ack, or with
a lost ack on the way back) leads to the same message being delivered again. Handle it naively and
you double-post; guard it wrong and you lose work. There is no reference implementation of getting
this right, so most Band agents either fake the lifecycle or get exactly-once subtly wrong.
band-once is that reference implementation, lifted out of a production system and stripped of
every application concern. You supply one handle() function; the shell owns the drain, the
processing/processed/failed lifecycle, the post, the read-then-act dedup guard, and bounded
retry on transient HTTP. It ships with a runnable proof and a public conformance check that an LLM
cannot self-certify.
Install
pip install band-once # once published to PyPI
Locally, from this directory:
pip install -e .
The shell: write only handle()
from band_once import BandAgentShell, strip_mention_markers
def handle(message: dict, context: list[dict]) -> dict | None:
body = strip_mention_markers(message["content"])
# ... do your framework-specific work (LLM call, graph, tool) ...
return {
"content": f"echo: {body}",
"mentions": [peer_uuid], # Band requires at least one mention
"dedup_key": f"echo:{message['id']}", # the natural key of this unit of work
}
shell = BandAgentShell(api_key=KEY, agent_name="my_agent",
dedup_namespace="echo", max_attempts=3)
shell.whoami()
shell.join(chat_id)
shell.run(handle, idle_breaks=5) # drain -> processing -> handle -> post -> processed
dedup_key is the natural key of the unit of work (for example
draft:nis2:inc-8842:round-1). Before re-posting, the shell reads the room and drops the post if
the key is already present. A crash and re-delivery therefore re-runs the work but never
double-posts. See examples/echo_agent.py for a complete agent.
The proof: a receipt, not a vibe
python -m band_once.proof
drives a large, seeded space of kill + duplicate schedules (kill before post, kill after post,
lost-ack) through the real BandAgentShell dedup guard and the real IdempotencyLedger, then
prints:
exactly-once held across 10000 schedules: 0 double-posts, 0 lost messages
The master seed and the schedule-space size are printed in the output, so anyone re-running the exact command gets the exact same number. No API keys, no network. Exit code is 0 only when exactly-once held on every schedule.
The conformance check: an external prover
Ofer's thesis for Band is that your coding agent cannot review its own work. The same applies to
exactly-once: an agent author should not mark their own homework. verify_exactly_once is the
independent grader.
from band_once import verify_exactly_once, clean_echo_agent
result = verify_exactly_once(clean_echo_agent)
assert result.ok # passes the reference correct agent
print(result.schedules_checked)
You hand it an agent_factory that builds a fresh agent over a shared IdempotencyLedger; it
drives seeded kill + duplicate schedules through that agent and asserts exactly-once on every one.
On a violation it returns a typed ConformanceResult naming the first schedule that broke the
invariant (its seed and a one-clause reason), the same way first_broken_index names the exact
link a hash chain breaks at:
def buggy_factory():
# records the work BEFORE checking the ledger, so a redelivery double-posts
def agent(ledger, dedup_key, attempt, ts):
...
return agent
result = verify_exactly_once(buggy_factory)
assert not result.ok
print(result.first_violating_seed, result.reason)
# 20260616 key 'work:k3:job-1:round-1' was ACCEPTED 2 times ...: a double-post
The grader never trusts the agent's claim about itself: it re-derives the invariant from the deliveries and the agent's own dispositions.
What is in here
| File | What |
|---|---|
band_once/shell.py |
BandAgentShell: the lifecycle shell. Imports only stdlib + requests + the sibling retry helper. |
band_once/ledger.py |
IdempotencyLedger: one ACCEPTED per natural key, every redelivery dropped. |
band_once/retry.py |
Bounded full-jitter exponential backoff on transient HTTP only. |
band_once/fake_band.py |
An in-process Band double for the proof and tests (delivered / processing / processed, the three kill positions). |
band_once/proof.py |
The kill-storm receipt. python -m band_once.proof. |
band_once/verify.py |
verify_exactly_once, the external prover, and clean_echo_agent, the reference correct agent. |
examples/echo_agent.py |
A complete agent built on the shell. |
BAND_API_FIELD_GUIDE.md |
The verified Band Agent API surface this shell encodes. |
Verified Band API facts this shell encodes
- Base
https://app.band.ai/api/v1, auth headerX-API-Key: <agent key>(notAuthorization: Bearer). mentionsis required on every message; the mentioned agent must already be a participant; you cannot mention yourself./messages/nextis per-chat and re-serves the same message until it is markedprocessed/failed; it is a cursor, not a destructive pop.- The lifecycle endpoints are per-chat under the message;
processed/failedmust followprocessing;failedcarries{"error": <string>}. - Exactly-once is owned by the poster via a read-then-act dedup guard, never by relying on re-delivery.
See BAND_API_FIELD_GUIDE.md for the full table.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
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 band_once-0.1.0.tar.gz.
File metadata
- Download URL: band_once-0.1.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea1f9e0ed3f3d7e625eb83eab98f731ade1750d4bd42664ab738eee898bb61c7
|
|
| MD5 |
ab19b80836a3fed2c1fb62b9c07ea103
|
|
| BLAKE2b-256 |
90d69dff8a4ebf796147c592ac05a5040df4860ee2097addf7d9e1eabf802b2d
|
File details
Details for the file band_once-0.1.0-py3-none-any.whl.
File metadata
- Download URL: band_once-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
463d1532af159bbee8cf02eacda9497933c26012be07b42ee65e23a409a5a296
|
|
| MD5 |
022629ef99ec3db9b602c1ce864900ec
|
|
| BLAKE2b-256 |
f49ac9106c622f38957de046823099d2b4b750f3200829ed208c034f9e5172a1
|