Agent messaging tower - an ATC-style triage tower for agent and human callsigns
Project description
crystalia-tower
An ATC-style triage tower for agent and human callsigns.
Work performed by an agent session is invisible until a human relays it, which
makes the human the transport wire between concurrently-running sessions.
crystalia-tower replaces that wire: every participant -- agent or human --
is a node with a callsign, an inbox, and presence. Messages are addressed, typed
from a fixed eight-verb vocabulary, and carry refs rather than bulk.
The thing it is for: an agent queues "need your ruling", the human answers, the agent unblocks -- with no live coordinator session and no copy-paste.
Install
pip install crystalia-tower
Python 3.13 or newer. Installs three console scripts: ctower, ctower-cab
and ctower-launch.
Quickstart
A tower is a directory. init is the only verb that will not guess where it is;
the rest read AGENT_CONTROL_TOWER_HOME, or take --tower.
ctower init --tower ~/towers/demo
export AGENT_CONTROL_TOWER_HOME=~/towers/demo
export AGENT_CONTROL_TOWER_CALLSIGN=vlad
# hand work to an agent callsign that need not exist yet
ctower send ASSIGN gatto-1 unit=DEMO note='look at the failing test'
# the agent's side: what is waiting, and what it owes a readback
ctower check --callsign gatto-1
ctower ack <msg-id> --callsign gatto-1
# the tower, both axes, every strip
ctower status
ctower roll
check --wait 300 blocks until traffic lands instead of polling, and exits 0
with interrupted: true in the envelope if a signal cuts the wait short --
a killed wait found nothing out, and that is not the same as a quiet tower.
The eight verbs
ASSIGN REPORT WILCO ROGER STANDBY UNABLE SAY-AGAIN MAYDAY
The set is closed. A ninth token is a validation failure, not an extension
point. A message carries unit=, status=, reason=, ref= and note=;
note= bytes are stored byte-identically, never parsed, never interpolated
into a shell, and never sent anywhere off the machine.
The three commands
| Command | What it is |
|---|---|
ctower |
the CLI: send, inbox, check, ack, status, init, roll |
ctower-cab |
a terminal UI over your own inbox and the strip tower |
ctower-launch |
start an agent process with its queued traffic handed over as its brief |
Machine-readable output
Every verb takes --json and emits a versioned envelope. The seven schemas that
pin it ship in the source distribution under docs/json-schema/v1/, one per
verb, identified as urn:crystalia-tower:json-schema:v1:<verb>. The
envelope is a public API: it is pinned by test, not described by prose.
Exit codes are frozen: 0 success, 2 usage, 3 validation, 4 unknown
callsign, 5 unknown msg-id, 6 permission, 7 tower unavailable, 8 launch
collision. ctower-launch adds 1 for a failure to start the child -- the
tower committed its work and what failed was the exec -- and otherwise exits
with the child's own code, reporting a signalled child as 128+N. Three
non-happy outcomes are exit 0 by design: a --wait deadline expiring, a
message to a callsign never seen before, and a launch over an empty queue.
What is built, and what is not
Single-host works. A tower is a directory on one machine, and every participant reaches it through the filesystem.
Multi-host is NOT built. There is no server, no network listener, no
signing and no authentication in this release: --tower takes a path, not a
URL. Remote towers are Phase 2 of the TOWER-01 multi-host implementation plan,
which lives in the development workspace and is not shipped with this
distribution. Nothing in this package should be read as promising it yet.
Traffic is pull-only. No notifier, no cron, no hook, no watcher, no daemon
sits on the message path -- you learn you have mail by asking. The single
narrow exception is crash telemetry, which is on by default and switched off
with AGENT_CONTROL_TOWER_SENTRY_DISABLED=1. It never carries note= bytes.
Working on the source
The source distribution carries the tests and the schemas. The TOWER-01 design documents are not shipped; they live in the development workspace.
uv sync --extra dev
uv run pytest
uv run ruff check src tests
uv run mypy src/crystalia_tower
Always use uv run; never source .venv/bin/activate.
Two invariants are easy to break and are enforced by lint rather than by
review: sqlite3 may be imported only under
src/crystalia_tower/store/sqlite/, and subprocess only by
src/crystalia_tower/launch.py. Nothing is swallowed either -- a caught
exception is re-raised, translated to a named error type, or surfaced. There is
no fourth option.
Licence
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 crystalia_tower-0.0.1.tar.gz.
File metadata
- Download URL: crystalia_tower-0.0.1.tar.gz
- Upload date:
- Size: 318.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93c465ad3acd2ef30472ca3d046eb57f0910ec451d7816003bef868c76bae190
|
|
| MD5 |
023789632f61ad2584336bc59da0c62f
|
|
| BLAKE2b-256 |
614901ce14ef3b02da1f9a7d2a48b6db2cd179a16d83efc17ba5c7a126fbeff7
|
File details
Details for the file crystalia_tower-0.0.1-py3-none-any.whl.
File metadata
- Download URL: crystalia_tower-0.0.1-py3-none-any.whl
- Upload date:
- Size: 103.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c918c9e8085938ede298bbb58d437a039043eea717924d97384bf5d7267365c
|
|
| MD5 |
41c0d9d0399b6ca9c1d75511557b3b83
|
|
| BLAKE2b-256 |
2064f62d7354b0ad998bd571065b9510ad6f69c1a8e20f09687f88483c229138
|