Repository navigation
Protocol ru
English · Русский
Как agents-room хранит и передаёт состояние координации.
комната пространство имён. Сессия сама входит в repo:<имя> и может
войти в любое число общих комнат — так агенты из разных
репозиториев достают друг друга.
сессия один процесс агента: харнесс, cwd, pid, метка, теги, автономия
сообщение одна отправка: отправитель, селектор, тема, тело, срок
доставка одна строка на разрешённого получателя, хранит состояние
лиза заявка на путь: владелец, режим, когда взята, когда истекает
чтение хеш содержимого, который сессия последней видела по пути
событие неизменяемая строка аудита
Хранилище — одна база SQLite в режиме WAL по адресу ~/.agents-room/room.db.
Переопределяется через AGENTS_ROOM_DB. Демона нет.
room_join возвращает id сессии. Все остальные инструменты принимают его
обратно как явную ручку.
Неизвестный id сессии — ошибка, а не пустой ответ. Чтение по id, за
которым ничего нет, возвращает ноль строк — ровно то же, что возвращает
пустой ящик; вызывающий, передавший не тот id, не может их различить. Один
агент неделями звал msg_recv с id сессии харнесса, каждый раз получал
«no unread messages» и решил, что ему ничего не присылали, пока в комнате
не накопилось девятнадцать сообщений.
Ошибка называет id, который скорее всего имелся в виду:
- точный ключ харнесса разрешается в его сессию;
- обрезанный id ищется среди id сессий и меток;
- id правильной формы, за которым ничего нет, сообщается как возможно ушедший или из другого хранилища.
| Id | Форма | Куда идёт |
|---|---|---|
| ключ харнесса | UUID, например 2b50133e-...
|
в room_join как harness_key; харнесс кладёт его в окружение |
| id сессии | 16 строчных hex | везде остальное, как session_id
|
Для модели, которая держит оба, они похожи, и передача не того раньше была невидима.
Чат, думающий над долгой правкой, и чат, вышедший час назад, по нему
неотличимы, поэтому room_who сообщает отдельно liveness: alive, gone
или unknown:
-
aliveиgoneопределяются по ключу харнесса — сканирование запущенных процессов отвечает, какие ключи есть в чьём окружении. Чат сохраняет ключ через перезапуск MCP-сервера, а pid — нет. -
unknownзначит, что в строке вообще нет pid, и это не то же самое, что gone.
prune удаляет сессию только когда она молчала дольше порога и она
gone. Gone он определяет той же функцией, что room_who, из того же
сканирования.
Только wake может начать ход в простаивающем чате. Ниже него сигнал едет
на ходе, который агент и так делает, поэтому простаивающая сессия на
notify или hold недостижима и выглядит точно как сломанный хук.
Уровень сохраняется, если повторный вход его явно не поднимает:
- не указано — сохранённый уровень остаётся;
- выше — применяется, с упором в кап комнаты;
- ниже — отказ с предупреждением, в котором назван оставшийся уровень.
Понижение — решение оператора, и у него один путь: кап комнаты.
| Селектор | Во что разрешается |
|---|---|
* |
все сессии в комнате, кроме отправителя |
<session-id> |
одна сессия; неизвестный id — ошибка, а не тишина |
file:<glob> |
сессии, держащие живую лизу по шаблону |
tag:<tag> |
сессии, вошедшие с этим тегом |
Пути нормализуются в абсолютные до записи, поэтому src/wire.go и
/abs/src/wire.go из одного каталога спорят за одну лизу. Селектор file:
сопоставляется с путём лизы в трёх формах: полный путь, путь относительно
рабочего каталога держателя и голое имя файла.
Якорь — каталог держателя, а не комнаты: в общей комнате могут быть сессии
из нескольких репозиториев, и src/wire.go означает в каждом свой файл.
Селектор, не нашедший никого, — не ошибка: широковещание в комнате из одного — законная отправка с нулём получателей. Число сообщается обратно, чтобы отправитель не принял сохранённое сообщение за доставленное.
Доставки отвечают «что меня ждёт». Они не могут ответить «что здесь решено», потому что создаются в момент отправки: у сессии, вошедшей позже, их нет.
На второй вопрос отвечает room_history. Он читает message_rooms, где на
каждую пару (сообщение, комната) лежит строка для каждой комнаты, в которой
отправитель был в момент отправки. Именно это делает историю общей комнаты
полной.
История только для чтения и никогда не отмечает доставку прочитанной.
msg_recv, вернувший пусто, — один и тот же ответ и для сессии, которая всё
прочитала, и для сессии в не той комнате. Поэтому пустой ответ несёт блок
inbox: комнаты сессии, сколько доставок у неё было вообще, сколько
прочитано и когда создана последняя.
У каждого получателя своя строка. Состояние только движется вперёд.
| Состояние | Значение |
|---|---|
stored |
записано, сигнал не предпринимался |
offered |
сигнал был послан, но принят ли он — неизвестно |
accepted |
харнесс принял сигнал |
read |
получатель забрал тело |
acked |
получатель подтвердил |
expired |
срок истёк до того, как тело забрали |
undeliverable |
у этого харнесса нет рычага пробуждения |
Сигнал пробуждения посылается один раз на сообщение, а потом повторяется на
событиях инструментов, когда доставка старше окна повтора. Повторяется
только offered: accepted значит, что ход уже идёт, и повтор запустил бы
второй.
lease_claim — единственная операция, которая обязана быть атомарной между
процессами. Она идёт внутри BEGIN IMMEDIATE, убирает истёкшие лизы,
проверяет держателей и вставляет.
| Режим | Правило |
|---|---|
exclusive |
один держатель; блокирует любую другую заявку |
shared |
несколько держателей; блокирует исключительные заявки, разрешает другие общие |
note_read хранит SHA-256 файла в момент, когда сессия его читала.
drift_check сравнивает файл на диске с этим хешем.
| Степень | Условие |
|---|---|
SAFE |
не изменился, или сессия не записывала базовый хеш |
MINOR |
изменился, и никто другой не держит лизу на нём |
CRITICAL |
изменился, пока лизу держала другая сессия |
| Инструмент | Аргументы | Возвращает |
|---|---|---|
room_join |
room?, extra_rooms?, label?, tags?, autonomy?, harness?, harness_key?
|
session_id, rooms, autonomy, warning?
|
room_leave |
session_id |
ничего, выходит из всех комнат |
room_leave_room |
session_id, room
|
ничего, остаётся в остальных |
room_who |
session_id?, room?
|
сессии, у каждой liveness
|
room_set_autonomy_cap |
session_id?, room?, autonomy
|
room, autonomy
|
msg_send |
session_id, to, subject?, body?, reply_to?, ttl_s?
|
message_id, recipients, warning?
|
msg_recv |
session_id, limit?
|
сообщения, отмеченные прочитанными; inbox когда пусто |
msg_ack |
session_id, message_id
|
ничего |
msg_unread |
session_id |
count |
msg_sent |
session_id, limit?
|
messages: у каждого states, pending, left, sent
|
room_history |
session_id?, room?, limit?
|
room, messages (только чтение) |
lease_claim |
session_id, path, ttl_s?, mode?
|
ok, holder, reason
|
lease_renew |
session_id, path, ttl_s?
|
продлённая лиза |
lease_release |
session_id, path
|
ничего |
lease_check |
session_id, path
|
free, lease
|
lease_list |
session_id |
лизы |
note_read |
session_id, path
|
hash |
drift_check |
session_id, path
|
path, severity, changed, holder, since, message
|
**agents-room** — agents in different repositories, one room.
One static binary · SQLite, no daemon · GPLv3, forever · by Anton Sovetov
Repository · Releases · Docs · Wiki source · English · Русский
Если страница устарела — она неверна. Правьте `docs/wiki/` в репозитории вместе с кодом, в одном коммите.