From 0f67b3791bb3ff639c89a3d695204cd741bd3bfe Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 29 Sep 2026 01:43:26 +0600 Subject: [PATCH] [2026-09-29] eagle: personal/plans/balda-callee-prism-plan.md --- personal/plans/balda-callee-prism-plan.md | 268 ++++++++++++++++++---- 1 file changed, 226 insertions(+), 42 deletions(-) diff --git a/personal/plans/balda-callee-prism-plan.md b/personal/plans/balda-callee-prism-plan.md index 79723da3..983ace34 100755 --- a/personal/plans/balda-callee-prism-plan.md +++ b/personal/plans/balda-callee-prism-plan.md @@ -116,6 +116,167 @@ Kraken разгружается: уходят Балда, MCP-сервер, obsi --- +## 1.1a Формула прав + +**`права = права процесса × смонтированное × разрешённое в графе`** + +| Уровень | Кто задаёт | Пример | +|---|---|---| +| Чем запущен процесс | вы, при старте | контейнер / хост-юзер / отдельный системный юзер | +| Что смонтировано | compose | `~/agents:/agents` — и ничего больше | +| Что разрешено в графе | автор воркфлоу | какие роли и скрипты вообще есть в каталоге | + +🔴 **`permissions.mode: allow_all` у роли — это про права модели ВНУТРИ ACP-сессии** +(править файлы, запускать команды). Оно **не** расширяет, что процесс видит в файловой +системе. Это два разных ограничения, не путать. + +### Где callee — там и движки, и Script'ы + +🔴 **ACP-провайдер — дочерний процесс callee.** Движок на хосте + callee в контейнере +технически невозможны: это один процесс-тред. + +| Исполнитель | Что исполняет | Права | +|---|---|---| +| callee | сам граф: циклы, роли, state | права процесса, которыми запущен | +| движок (codex/claude/dsh) | модель: думает, правит файлы | наследует + своя песочница | +| Script | shell: сборки, тесты, ssh | наследует права процесса | + +**Следствие:** «callee на хосте» = **всё исполнение на хосте целиком**. Промежуточного +варианта нет. + +### Динамических маунтов не существует + +На macOS Docker маунты задаются **при создании контейнера**; live `mount --bind` контейнер +не видит; симлинк на внешний путь битый. **Вопрос «как модель запросит доступ к папке» +не имеет решения через маунты.** + +| Подход | Как | Оценка | +|---|---|---| +| A. Смонтировать всё заранее | `~/Developer:ro` | запись нельзя | +| B. Пересоздание контейнера | правка compose + `up -d` (~3 с) | нужен механизм вне контейнера | +| C. Хост-агент ставит права сам | callee на хосте | полный доступ, ноль изоляции | +| **D. Проект = рабочая копия** ✅ | модель работает в `~/agents/<проект>`, результат уходит в `~/Developer` через git | доступ задаётся наличием репозитория, а не маунтом | + +**Решение: D.** Модель всегда работает в `~/agents/<проект>` (уже смонтировано), результат +попадает в проект через `git`. Право выдаётся точечно. + +### Доступ не запрашивается — он выбирается маршрутом + +Модель **не просит** прав: она не может эскалировать. Если у роли нет доступа, задача +**падает**, а не спрашивает. Доступ задаёт автор графа. + +``` +Роутер в графе + ├─ задача требует ~/agents → роль в контейнере + ├─ задача требует Xcode/lume → роль на хост-исполнителе + └─ задача требует хоста по ssh → роль со ssh-ключом (command=) +``` + +**Разграничение = статический роутинг по типам задач**, а не динамические права. + +### Гибрид «контейнер + узкий хост-исполнитель» + +| Вариант | Права | Динамика | Оценка | +|---|---|---|---| +| callee в контейнере | узкие, контролируемые | только `~/agents` | безопасно; Xcode/lume/docker недоступны | +| callee на хосте | **полный доступ пользователя** | всё работает | ноль изоляции | +| **гибрид** ✅ | контейнер для основного + узкий хост-исполнитель для Xcode/lume/docker | маунты не нужны | рекомендован | + +🟡 Хост-исполнитель — дыра в изоляции **по определению**, но **узкая и явная**: три операции +(xcodebuild, lume, docker build), а не «что угодно». + +--- + +## 1.1b Роли: жизненный цикл и контроль + +### Что такое роль + +**Роль — YAML/MD-файл в каталоге.** Не «право», не «токен», а **описание листа графа**: +какой провайдер, какой промпт, какие параметры, какой таймаут. + +```yaml +apiVersion: callee.metalagman.dev/v1alpha1 +kind: Role +spec: + description: Собирает Xcode-проект + provider: + type: generic_acp + cmd: dsh-acp + model: deepseek-chat + timeout: 20m +--- +Собери проект {{ .Params.project }} и верни отчёт об ошибках. +``` + +### Кто их раздаёт и откуда они берутся + +| Источник | Что даёт | +|---|---| +| Каталог по умолчанию | 6 стартовых ресурсов (`callee setup`) | +| Импорт из git | `callee agent import baldaworks/prism-callee --prefix prism` | +| **Наши собственные** | файлы в рабочем каталоге проекта / `~/.callee/` | + +**Роли не выдаются динамически — они ЛЕЖАТ в каталоге.** Кто положил файл, тот и «выдал» +роль. Никакого рантайм-механизма выдачи прав нет. + +### Что раздаётся, а что нет + +| Сущность | Динамически выдаётся? | Чем регулируется | +|---|---|---| +| Роль (файл графа) | ❌ статично | наличие файла в каталоге | +| Провайдер (тип, `cmd`) | ❌ статично, или из `State` через DynamicRole | автор графа | +| **Файлы** | ❌ | монтирование + рабочая копия (§1.1a) | +| **SSH-ключ** | ❌ | наличие файла у процесса | +| **Разрешение модели внутри сессии** | ⚠️ **да** — `allow_all`/`ask`/`deny_all` | конфиг Балды | + +🔴 **Единственное, что реально запрашивается в рантайме — разрешение модели внутри ACP-сессии** +(`ask` → вопрос в чат). Всё остальное статично. + +### Ваша гипотеза: проверяю по пунктам + +> «я ставлю задачу → приходит запрос "дать такую-то роль для такой-то задачи" → апрув → доступ появился» + +| Часть гипотезы | Вердикт | +|---|---| +| Задача приходит в систему | ✅ да | +| **Приходит запрос «выдать роль»** | ❌ **нет такого механизма.** Роли не выдаются, они лежат в каталоге | +| **Апрув → у агента появляется доступ** | ⚠️ **частично — не к инструментарию, а к действию** | +| **Без апрува доступа нет** | ✅ **верно, но иначе** | + +**Что происходит на самом деле:** доступ у роли **есть всегда** — он определяется тем, чем +запущен callee и что смонтировано. Апрув не выдаёт доступ, апрув **разрешает выполнить +операцию**, которая уже технически возможна. + +### Значит правильная формулировка + +``` +Роль лежит в каталоге (доступ есть по определению) + │ + ▼ +Граф доходит до узла → Human («собрать Xcode в VM macos-27?») → пауза + │ + ├─ апрув → Script исполняется, потому что права у процесса УЖЕ были + └─ отказ → граф останавливается, ничего не исполняется +``` + +🔴 **Разница принципиальная.** Вы описали **capability-модель** (права выдаются на задачу). +Callee работает по **статической модели** (права постоянны, гейты контролируют исполнение). + +**Что это значит для вашей безопасности:** гейт `Human` защищает от **действия**, но не от +**возможности**. Если у процесса есть ssh-ключ — ключ есть всегда, вне зависимости от апрувов. +Изоляцию даёт **только то, чем процесс запущен и что смонтировано** — не граф. + +### Что можно сделать, чтобы приблизиться к вашей модели + +| Способ | Как | +|---|---| +| **Разные профили запуска** | контейнер «узкий» (без ssh, без docker) и хост-исполнитель «широкий» — выбор профиля роутером | +| **Роль на задачу** | задача → своя роль с нужным `cmd`/таймаутом; доступ = какой профиль она выбрала | +| **Апрув как гейт исполнения** | `Human` перед дорогой/опасной операцией | +| **Белый список команд в Script** | `Script` не «любой shell», а конкретный скрипт с фиксированной командой | + +--- + ## 1.2 Правила работы: Hermes-конфигурация в Балде В Hermes у Alex накоплены: правила ведения документации (Obsidian — источник истины), @@ -248,21 +409,20 @@ spec: ## 2.5 Карта стека: кто кого дёргает +> ⚠️ Обновлено под топологию §1 (стек на Mac). Прежний вид — «Балда на Kraken + MCP + +> мост → callee» — **устарел**, оставлен ниже как справка по демонтированным узлам. + +### Актуально (топология «стек на Mac») + ``` -ВЫ (Mattermost / Telegram) +ВЫ (Mattermost) │ задача словами - ▼ -Балда-оркестратор — Kraken, balda-mm:0.1.13, DeepSeek - │ понимает задачу, читает Obsidian, решает куда идти, отвечает + ▼ MM API — исходящее соединение с Mac +Балда-оркестратор — Mac, balda-mm:0.1.13, DeepSeek + │ понимает задачу, читает Obsidian, планирует, держит крон, отвечает │ НЕ пишет код, НЕ собирает, НЕ ходит по хостам - ▼ MCP-инструмент executor_run -balda-executor-mcp — Kraken, :8890, HTTP + X-Bridge-Secret - │ транспорт без логики - ▼ Cloudflare Tunnel (agents-bridge.qentra.top) -мост agents-bridge — Mac, :8790, launchd - │ транспорт без логики: идемпотентность, таймаут 900с, лимит 1 - ▼ docker exec -callee — Mac, контейнер balda-executor + ▼ +callee — Mac │ ДВИЖОК ВОРКФЛОУ: компилирует граф, валидирует целиком до запуска │ МОДЕЛИ НЕ СОДЕРЖИТ: дирижёр — раздаёт роли, гоняет циклы, собирает артефакт ├─ Role ────► ACP-процесс ───► МОДЕЛЬ (DeepSeek / позже Codex) @@ -270,15 +430,24 @@ callee — Mac, контейнер balda-executor └─ Human ───► пауза, ответ реплаем в треде MM ▼ bd (Beads) — трекинг Story → Tasks, живёт между сессиями + ▼ +артефакт → Балда → Mattermost ``` +### Демонтированные узлы (не нужны в новой топологии) + +| Узел | Почему больше не нужен | +|---|---| +| `balda-executor-mcp` (:8890) | его работа — пропихнуть вызов к мосту; Балда на Mac → пропускать нечего | +| мост `agents-bridge` (:8790) | был границей хостов; границы нет | +| Cloudflare Tunnel (для этого потока) | остаётся только для внешнего доступа к MM | +| `obsidian-mcp` на Kraken | Obsidian рядом с Балдой | + ### Кто есть кто | Компонент | Где | Что это | |---|---|---| -| Балда | Kraken | Оркестратор верхнего уровня: понимает смысл задачи | -| balda-executor-mcp | Kraken | Проброс по HTTP, логики нет | -| мост `:8790` | Mac | Идемпотентность, таймаут, лимит 1, логики нет | +| Балда | **Mac** | Оркестратор верхнего уровня: понимает смысл задачи, планирует, крон | | **callee** | Mac | Движок воркфлоу. Граф, роли, циклы. **Не модель** | | Role / DynamicRole | Mac | Узел, который зовёт модель | | **ACP** | Mac | Протокол «редактор ↔ агент». Транспорт, не модель | @@ -286,8 +455,8 @@ bd (Beads) — трекинг Story → Tasks, живёт между сесси | **Codex / Claude CLI** | Mac | Агент-харнесс: модель + умение править файлы и запускать команды | | **bd (Beads)** | Mac | Память задач: что сделано, что нет | | **prism** | Mac | Воркфлоу: specify → design → breakdown → human → apply → verify | -| tunnels | — | Cloudflare: Kraken→Mac по HTTP. **Не VPN** | -| **lume** | Mac | macOS-виртуалки. Только для задач, требующих настоящую macOS | +| **lume** | Mac (хост) | macOS-виртуалки: `macos-15-golden`, `macos-27-golden`, `uitest-26.4.1-golden`, `pdf-hud-macos27` | +| MM-сервер | **Kraken** | Только чат-сервер 24/7. Больше ничего | 📌 **`callee` не умеет ничего сам** — ни думать, ни писать код. Он берёт описание графа, проверяет его и по шагам вызывает: роль → модель, скрипт → shell, human → пауза. @@ -399,6 +568,9 @@ callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix pr ## 5. Фазы +> ⚠️ Переписано под топологию §1 (стек на Mac). Прежняя фаза C3 («переписать `PROVIDERS` +> в мосте») **отменена**: мост в новой топологии не нужен. + ### C0 — ✅ ЗАКРЫТА 2026-09-29 Разведка npm: готовый ACP-адаптер к DeepSeek существует (§2.1). Свой адаптер не нужен. @@ -407,22 +579,39 @@ callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix pr `/-/v1/search?text=...`. ⛔ Поиск по `scope:normahq` и `scope:baldaworks` даёт пусто — scoped-пакеты так не индексируются. -### C1 — установка в контейнер `balda-executor` (1-2 ч) +### C1 — переезд стека на Mac (2-3 ч) -Ни `callee`, ни `bd` нет ни в контейнере, ни на хосте. +Объём вырос относительно исходного варианта: теперь это не «установка callee в контейнер», +а **переезд Балды на Mac + установка инструментов рядом**. | # | Шаг | Проверка | |---|---|---| -| 1 | `npm i -g @baldaworks/callee` | `callee --version` | -| 2 | `npm i -g @openma/deepseek-harness-acp` | `dsh-acp --help` | -| 3 | `callee setup codex` → стартовые ресурсы | `callee agent list` содержит `workflows/investigate` | -| 4 | Установить Beads `bd` | `bd where` | -| 5 | Импорт prism-пака | `callee agent view prism/lifecycle --json` → `Router` | -| 6 | Прогон стартового `investigate` | артефакт в stdout, exit 0 | +| 1 | Запустить второй инстанс `balda-mm:0.1.13` на Mac | контейнер Up, лог `Balda bot started` | +| 2 | 🔴 **Отдельный MM-бот и токен для Mac-инстанса** | два процесса на один WebSocket MM рвут события (`bad_seq`) | +| 3 | `npm i -g @baldaworks/callee` на Mac | `callee --version` | +| 4 | `npm i -g @openma/deepseek-harness-acp` | `dsh-acp --help` | +| 5 | `callee setup codex` → стартовые ресурсы | `callee agent list` содержит `workflows/investigate` | +| 6 | Установить Beads `bd` | `bd where` | +| 7 | Импорт prism-пака | `callee agent view prism/lifecycle --json` → `Router` | +| 8 | `obsidian-mcp` рядом с Балдой (том `~/obsidian:/vault`) | `/sse` отвечает | +| 9 | Прогон стартового `investigate` | артефакт в stdout, exit 0 | -📌 Персистентность: установки живут в слое контейнера. Чтобы переживали пересборку — +**Решения по хранению (принять на C1):** установки должны переживать пересборку образа — том или `Dockerfile`. +### C1a — доступы и исполнители (объём зависит от развилки) + +Закрывает шесть сценариев из §2.5. **Объём определяется выбором в §6.7:** + +| Что нужно | Как | +|---|---| +| `~/.ssh` в контейнере | монтирование **конкретных ключей** с `command=` в `authorized_keys`, не каталога целиком | +| `docker build` | ❌ **не через `docker.sock`**; либо обёртка на хосте, либо хост-исполнитель | +| `lume` | ❌ в контейнер не пробрасывается; хост-исполнитель | +| `xcodebuild` | ❌ в Linux-контейнере невозможно; **lume VM с macOS** | +| `~/Developer` | рабочая копия в `~/agents/<проект>` + git (см. §1.1a) | +| Доступ к хостам (TrueNAS, Kraken) | ssh-ключ с ограничением `command=` | + ### C2 — Role к DeepSeek (1 ч) Описать `Role` по §2.3 и проверить один визит вживую. @@ -430,24 +619,14 @@ scoped-пакеты так не индексируются. **Гейт:** в логе видны `role_model` и `role_reasoning`. До успешного рендера провайдера эти поля опускаются — их появление означает, что рендер прошёл. -### C3 — подключение к мосту (2-3 ч) +### C3 — ~~подключение к мосту~~ ОТМЕНЕНА -1. Бэкап `~/Developer/agents-bridge/executor.py`. -2. Замена argv-таблицы `PROVIDERS`: - -```python -PROVIDERS = { - "callee": ["callee", "agent", "run", "prism/lifecycle", "--message"], -} -``` - -3. `EXECUTOR_PROVIDER=callee` в `~/Docker/balda-executor/.env`. -4. Перезапуск моста: `launchctl bootout` + `bootstrap`. -5. Прогон задачи через мост, проверка `exit_code`. +Мост `agents-bridge` в топологии «стек на Mac» не участвует. Фаза сохраняется только как +**план отката** к топологии «Kraken-оркестратор», если переезд не оправдается. ### C4 — таск-трекинг (1-2 ч) -1. `bd` инициализируется в `/agents`. +1. `bd` инициализируется в рабочей области. 2. Прогон `prism:story` на реальной задаче. 3. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте. @@ -472,9 +651,14 @@ PROVIDERS = { | 1 | Провайдер обеих сторон | DeepSeek | команда Alex | | 2 | Оркестрация | callee | документированная архитектура вместо самодельной | | 3 | Таск-трекинг | prism + Beads | задание Alex | -| 4 | Host coding agent | callee CLI в контейнере `balda-executor` | Node 24 — родная среда; там смонтирован `~/agents`; мост уже умеет `docker exec` | -| 5 | Мост `:8790` | сохранить, сменить argv | идемпотентность, таймауты, лимит 1 задача уже проверены; выбрасывать работающий слой ради эстетики незачем | +| 4 | Где живёт стек | **Mac целиком** | исполнение работает с файлами Mac → обязано быть рядом | +| 5 | Где MM-сервер | **Kraken** | единственный, кому нужна доступность 24/7 | | 6 | Тип провайдера | `generic_acp` | список типов закрытый, DeepSeek в нём нет | +| 7 | 🔴 **Открыто: доступ к Xcode/lume/docker** | **не решено** | узкий контейнер (три операции недоступны) vs гибрид с хост-исполнителем | +| 8 | Роли: где живут | статично, файлами в каталоге | рантайм-выдачи ролей в callee не существует (§1.1b) | +| 9 | Права | `права процесса × монтирование × граф` | гейт `Human` защищает действие, но не возможность (§1.1a) | +| 10 | Мост `:8790` / MCP `:8890` | **демонтируются** | лишние посредники без содержательной работы | +| 11 | Старый инстанс на Kraken | **оставить живым как запасной** | откат без пересборки; ⚠️ требует отдельного MM-бота | ---