[2026-10-15] eagle: personal/plans/balda-callee-prism-plan.md personal/projects/archive/balda-callee-prism-plan-through-2026-10-14.md personal/projects/archive/balda-mattermost-connector-design-2026-09.md personal/projects/archive/callee-prism-contracts-2026-09-29.md personal/projects/balda/balda-mattermost-connector.md personal/projects/personal-os/personal-os-status-2026-10.md personal/tech/callee-prism.md

This commit is contained in:
Alexey Martemyanov committed 2026-10-15 18:27:36 +06:00
1 parent 0301d4408a
commit 2ad9bba688
7 files changed
+1904 -1848

No files matched your search

Executable → Regular
+7 -387
View File
@@ -1,393 +1,13 @@
# Callee + Prism — историческая справка по контрактам
# Callee / Prism — текущие контракты Broker
> [!WARNING] Версионная справка, 2026-09-29
> Контракты ниже относятся к исследованным тогда версиям. Их нельзя считать описанием установленного Broker без сверки закреплённых зависимостей, workflow и worker package.
> Проверенные версии, текущие образы, реальные прогоны и оставшаяся работа: [[personal/projects/balda/execution-broker]].
> Старый [[personal/plans/balda-callee-prism-plan|план интеграции]] сохранён как архив.
Проверено: **2026-10-15**. Работающие образы, принятые конфигурации и открытые границы: [[personal/projects/balda/execution-broker]]. План приёмки: [[personal/plans/balda-callee-prism-plan]].
> Создан 2026-09-29. Опорная заметка к [[personal/plans/balda-callee-prism-plan]].
> Назначение: справочник по контрактам callee и prism, чтобы не перечитывать
> доки апстрима при каждой правке ресурсов.
Callee исполняется внутри одноразового worker под управлением Runner. Broker закрепляет repository/revision, executor profile и SHA256 всего workflow root. Запрос модели не может подменить executable, credentials или неразрешённый workflow. Закреплённый upstream Callee — `57db8bb55733791bc01f273738dea5a095565ca0`; reviewed adapter хранится в `integrations/callee/` репозитория Broker.
---
`Role` запускает выбранный ACP executor, `Script` выполняет операторскую команду, `Sequential` задаёт порядок. Authored permissions `deny/ask/allow` проверяет Callee. У managed OpenCode read-only роли используют native `plan` с запретом shell/effect tools и Callee deny; пишущие роли — `build` с утверждённой policy. Отказ модели выполнить запись сам по себе не доказывает permission enforcement. Отдельные тесты подают ACP-запросы записи в реальный handler и проверяют отказ и отсутствие файла.
## 1. Что это
Обычное owner approval и смена окружения имеют разные контракты. Environment checkpoint — opt-in операторский continuation: остановка и credential fence, snapshot, cleanup, отдельное одобрение, новая среда и исходный deadline. Произвольный live graph не сериализуется. Runtime/task не выбирает произвольный новый стек без allowlisted tooling profile.
| Инструмент | Репозиторий | Лицензия | Что делает |
|---|---|---|---|
| **callee** | `baldaworks/callee` | MIT | воркфлоу как версионируемые Markdown/YAML-ресурсы |
| **prism** | `baldaworks/prism` | MIT | плагин в coding agent: жизненный цикл Story/Epic |
| **prism-callee** | `baldaworks/prism-callee` | MIT | те же воркфлоу через callee-агентов |
| **Beads** | `gastownhall/beads` | — | durable-состояние (`bd`), носитель состояния prism |
Broker — источник истины о run, deadline, quality и cleanup. Prism adapter проецирует события в локальный Beads и сохраняет cursor. Успех Story подтверждается quality, SHA256 скачанных артефактов и `cleanup=done`; одно завершённое модельное сообщение этого не доказывает. Отправкой ответа в исходный чат владеет Balda с native durable waits. Публикация в GitHub, UI и другие прикладные действия принадлежат вызывающему проекту.
📌 Версия callee на 2026-09-29 — **`0.22.2`** (npm `@baldaworks/callee`, meta-пакет
с optionalDependencies на платформенные бинарники: `darwin-arm64`, `darwin-x64`,
`linux-arm64`, `linux-x64`, `win32-x64`).
---
## 2. callee — контракты
### 2.1 Типы узлов (10 публичных kinds, `callee.metalagman.dev/v1alpha1`)
| Группа | Kind | Поведение |
|---|---|---|
| Листья | `Role` | промпт + один или несколько ходов в одной свежей ACP-сессии |
| | `DynamicRole` | то же, но конфиг провайдера рендерится из состояния визита |
| | `Script` | детерминированный локальный шаг через `sh`/`bash` |
| | `Human` | пауза, один непустой ответ оператора |
| Типизированные оценки | `TypeSafeJev` | нативный оценщик System One/Jev |
| | `OpenRouterDecision` | OpenRouter Decisions API |
| Композиты | `Sequential` | дети один раз, в порядке исходника |
| | `Parallel` | fan-out и join |
| | `Loop` | повтор детей в границах до эскалации |
| | `Router` | ровно один именованный или дефолтный ребёнок |
### 2.2 Исполнение
```
Markdown/YAML
→ decode + schema/semantic/template validation
→ registry + static graph resolution
⛔ отвергает: невалидные ресурсы, битые ссылки, циклы,
дубли resource ID, дубли effective ID в resolved tree
→ resolved root tree + required parameter keys
→ runner + ОДИН shared ephemeral state-объект
→ ОДИН корневой артефакт в stdout (пишется ПОСЛЕ cleanup)
```
⚠️ Диагностика идёт в **stderr**, артефакт — в **stdout**. Непустой stderr **не** означает
провал. Успех определять по **exit status**.
**Порядок визита узла:** счётчик → слияние `spec.state` со state входящего ребра →
рендер строковых листьев по **одному** pre-node снапшоту → атомарный коммит state-
модификатора → поведение по типу → `agent finished` со статусом и длительностью.
🔴 При провале рендера state: **ни одна часть модификатора не коммитится, провайдер не стартует.**
### 2.3 Провайдеры — ЗАКРЫТЫЙ список
| `type` | Команда по умолчанию | Предусловие |
|---|---|---|
| `codex` | сам callee + `bridge codex` | авторизованный Codex CLI |
| `claude` | `npx -y @zed-industries/claude-code-acp@latest` | Node + npx + креды адаптера |
| `opencode` | `opencode acp` | OpenCode CLI |
| `copilot` | `copilot --acp --stdio` | Copilot CLI |
| `grok` | `grok agent stdio` | Grok CLI или `XAI_API_KEY` |
| `cursor` | `agent acp` | Cursor CLI |
| `generic_acp` | **нет — требует непустой `cmd`** | любой ACP-исполняемый файл |
🔴 **Gemini не является валидным типом.** DeepSeek — тоже нет → только через `generic_acp`.
**Поля провайдера:**
| Поле | Контракт |
|---|---|
| `type` | обязателен, из закрытого списка |
| `cmd` | **одно имя исполняемого файла, НЕ shell-строка**; для `generic_acp` обязателен |
| `model` / `reasoning` / `mode` | опциональны; пустые → дефолт бэкенда |
| `extraArgs` | упорядоченный argv-список; каждый элемент непустой |
| `timeout` | Go duration, **по умолчанию `15m`** |
⚠️ Конфиг провайдера обязан быть **вложенным** — плоские поля в `spec` не поддержаны.
**Таймаут независим для:** старта процесса, создания и подготовки сессии, **каждого**
хода. Корневой прогон целиком он **не** ограничивает.
**Переиспользование процесса:** одинаковые effective `type` + полностью резолвленный `cmd`
→ один процесс. `model`/`mode`/`reasoning` на идентичность процесса **не влияют**.
Сессия всегда создаётся свежая.
### 2.4 DynamicRole — провайдер из состояния
Все поля провайдера — шаблоны: `type`, `cmd`, `model`, `reasoning`, `mode`, `timeout`,
**каждый** элемент `extraArgs`.
| Факт | Значение |
|---|---|
| Источники | `.Prompt`, текущий `.Input`, **один** согласованный post-node снапшот `.State`, `.Params` |
| Недоступно | `.Output`, env, файловая система, сеть, часы, рандом, криптография, мутаторы |
| Рендер | ровно **один раз на визит**; валидируется **до** поиска процесса |
| Периметр | конфиг фиксирован на весь визит, включая REPL-ходы |
| Повтор | новый визит Loop рендерит заново из актуального состояния |
| Ошибка | рендер или валидация падают → визит падает, ничего не стартует |
📌 В `callee agent view` DynamicRole помечается `provider=runtime`; JSON-view сохраняет
авторские шаблоны. Обычный `callee doctor` валидирует ресурс, шаблон и граф, но готовность
провайдера **откладывает** — исполняемый файл проверяется только при достижении узла.
📌 После рендера в lifecycle-событиях появляются `role_model` и `role_reasoning`.
**До** успешного рендера эти поля опускаются — их появление = рендер прошёл.
🔴 **Безопасность:** state-driven `cmd` и `extraArgs` могут выбирать исполняемый код.
Не подавать туда недоверенный вывод модели без ограничения авторской шаблонной логикой.
`cmd` остаётся одним исполняемым файлом, `extraArgs` — argv-списком; shell не участвует.
### 2.5 Script и Human
**Script:** результат пишется в `State.scripts[effectiveId]` — `status` (`passed`/`failed`),
`exitCode` (`-1` при таймауте), `stdout`, `stderr`, `timedOut`.
🔴 **Таймаут всегда валит воркфлоу.** Ненулевой exit валит, если не задан `onNonZero: continue`.
**Human:** рендерит тело, показывает на **управляющем терминале**, просит один непустой
ответ. Пишется в `State[responseKey]` и дублируется в `State.outputs[effectiveId]`.
> 🔴 **Requires interactive mode** and an interactor backed by a controlling terminal.
> Explicit non-interactive mode **rejects a Human anywhere in the resolved tree during
> preflight, including beneath an unselected Router branch**.
**Следствие:** воркфлоу с `Human` **не запускается** в non-interactive режиме вообще —
и проверка идёт до всякой логики, по всему resolved tree. Запуск через `docker exec`
без `-t` даёт non-interactive → `Human` невозможен.
### 2.6 Эскалация
Принадлежит **рёбрам**, а не определениям ресурсов. `Role` может эскалировать в ближайший
включающий `Loop` **только если каждое** ребро от Loop к этому вхождению имеет
`canEscalate: true` (опущенное = `false`). Вложенный `Loop` открывает **новую границу
авторизации** — потомки не наследуют полномочия внешнего.
### 2.7 Команды
```bash
npx --yes @baldaworks/callee@latest setup <codex|claude|grok|copilot|opencode|cursor>
callee agent list
callee agent view <id> [--json]
callee agent run <id> --message "..." [--repl-timeout D]
callee agent import <owner/repo> --path <pack> --prefix <pfx> [--force]
callee doctor
```
---
## 3. Prism — таск-трекинг
### 3.1 Жизненные циклы
**Story:** Specify → Design → Breakdown → **Human approval** → Apply → Verify.
Фазы: `phase:story:specify` → `design` → `breakdown` → `human` → `apply` → `verify`.
**Epic:** Frame → Architecture → Roadmap → **approval** → Deliver Stories → Validation.
Фазы: `phase:epic:frame` → `architecture` → `roadmap` → `approval` → `delivery` → `validation`.
### 3.2 Правила
| Правило | Значение |
|---|---|
| Дети Epic | только **Stories** |
| Дети Story | только **Tasks** |
| Вложенные Epic, Task напрямую в Epic | **невалидно** |
| Фиксированный диапазон числа детей | **нет** — оценка качественная (покрытие, связность, ревьюимость, верифицируемость, ацикличность) |
| `Apply` | 🔴 **каждый переход требует явной авторизации человека** |
| Одобрение Epic → Story | **не переносится** |
| Критерии приёмки | показываются **только** по явному запросу оператора |
При паузах prism останавливается только на: необходимом уточнении, одобрении перед
реализацией, блокерах.
### 3.3 Установка и импорт
**В coding agent** (независимая установка, например Codex):
```bash
codex plugin marketplace add baldaworks/prism
codex plugin add prism@prism
# обновление: codex plugin marketplace upgrade prism → повторить add → новый thread
```
**Callee-агенты** (отдельный репозиторий, требует `callee >= 0.19.0` + `bd`):
```bash
callee agent import baldaworks/prism-callee \
--path pack/callee/prism \
--prefix prism
# обновление/починка: тот же вызов + --force
```
**Проверка:**
```bash
callee agent list | grep '^prism/'
callee agent view prism/lifecycle --json # 🔴 kind ДОЛЖЕН быть Router
callee agent view prism/story --json
callee agent view prism/epic --json
bd where
```
🔴 **`Sequential` вместо `Router` = устаревший импорт** → обновлять `--force`.
`--agent-root pack/callee` — только для поддержки самого репозитория.
> «Each repository installs and validates independently — installing one plugin does
> not install the other.»
### 3.4 Штатный и прямой запуск
| Способ | Команда | Что даёт |
|---|---|---|
| Штатный (рекомендован) | `$prism-callee:lifecycle <запрос>` | резолвит Beads-цель, **персистит фазовые метки**, поддерживает resume |
| Прямой Story | `callee agent run prism/story --message "..."` | исполняет граф, но **не** резолвит Beads-элемент и не персистит метки |
| Прямой Epic | `callee agent run prism/epic --message "..."` | то же ограничение |
📌 «The coding agent owns durable state and approval. Callee executes the selected graph;
**approval never transfers from an Epic to a Story.**»
---
## 3.5 Роли: что это и как ими управляют
**Роль — 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/` |
🔴 **Ролей в рантайме не выдают.** Роль = наличие файла в каталоге. Рантайм-механизма
«выдать роль на задачу» **не существует**.
### Что динамическое, а что нет
| Сущность | Динамически? | Чем регулируется |
|---|---|---|
| Роль (файл графа) | ❌ статично | наличие файла в каталоге |
| Провайдер (`type`, `cmd`) | ❌ или из `State` через `DynamicRole` | автор графа |
| Файлы | ❌ | монтирование + рабочая копия |
| SSH-ключ | ❌ | наличие файла у процесса |
| **Разрешение модели внутри ACP-сессии** | ⚠️ **да** | `allow_all` / `ask` / `deny_all` |
🔴 **Единственное, что реально запрашивается в рантайме — разрешение модели внутри
ACP-сессии** (`ask` → вопрос в чат).
---
## 4. Права: формула и границы
**`права = права процесса × смонтированное × разрешённое в графе`**
| Уровень | Кто задаёт |
|---|---|
| Чем запущен процесс | вы, при старте (контейнер / хост-юзер / отдельный юзер) |
| Что смонтировано | compose |
| Что разрешено в графе | автор воркфлоу |
🔴 **`permissions.mode: allow_all` — это про права модели ВНУТРИ ACP-сессии** (править
файлы, запускать команды). Оно **не** расширяет, что процесс видит в файловой системе.
Два разных ограничения.
### Где callee — там и движки, и Script'ы
🔴 **ACP-провайдер — дочерний процесс callee.** Движок на хосте + callee в контейнере
технически невозможны. Следствие: «callee на хосте» = **всё исполнение на хосте целиком**.
### Гейт `Human` защищает действие, но не возможность
Доступ у роли есть **всегда** — он определён тем, чем запущен callee и что смонтировано.
Апрув **не выдаёт доступ**, он разрешает выполнить операцию, которая уже технически
возможна.
```
Роль лежит в каталоге → доступ ЕСТЬ
▼
Узел Human («собрать Xcode в VM macos-27?») → пауза
├─ апрув → Script исполняется (права у процесса УЖЕ были)
└─ отказ → граф останавливается
```
🔴 **Изоляцию даёт только то, чем процесс запущен и что смонтировано — не граф.**
### Динамических маунтов не существует
На macOS Docker маунты задаются **при создании контейнера**; live `mount --bind`
контейнер не видит; симлинк на внешний путь битый. **«Как модель запросит доступ к папке»
не решается через маунты.**
| Подход | Оценка |
|---|---|
| A. Смонтировать всё заранее (`~/Developer:ro`) | запись нельзя |
| B. Пересоздание контейнера (`up -d`, ~3 с) | нужен механизм вне контейнера |
| C. Хост-агент ставит права сам | полный доступ, ноль изоляции |
| **D. Проект = рабочая копия** ✅ | модель работает в `~/agents/<проект>`, результат в проект через git |
**Решение: D** — доступ задаётся наличием репозитория, а не маунтом.
### Доступ выбирается маршрутом, а не запросом
Модель **не просит** прав и **не может эскалировать**: нет доступа — задача падает.
```
Роутер в графе
├─ задача требует ~/agents → роль в контейнере
├─ задача требует Xcode/lume → роль на хост-исполнителе
└─ задача требует хоста по ssh → роль со ssh-ключом (command=)
```
**Разграничение = статический роутинг по типам задач**, не динамические права.
---
## 5. DeepSeek как ACP-провайдер
Провайдер callee — `generic_acp`, `cmd: dsh-acp`, пакет
**`@openma/deepseek-harness-acp@0.4.33`** (Apache-2.0, Node `>= 22.15`,
`bin: dsh-acp` → `dist/bin.js`). Обёртка вокруг официального DeepSeek Harness.
| Способ передачи кред | Значение |
|---|---|
| Флаг | `api-key`, либо `api-key:<provider>` при нескольких провайдерах |
| Env | **`DEEPSEEK_API_KEY`** |
| Env | **`DEEPSEEK_BASE_URL`** — OpenAI-совместимый endpoint override |
Приоритет: **флаги → переменные окружения → дефолты**.
📌 `DEEPSEEK_BASE_URL` — OpenAI-совместимый override → смена провайдера сводится
к смене одной переменной, без правки графа.
📌 Официальный `@deepseek-ai/dsh-acp` **не подошёл**: pre-release (последние версии
сплошь `-rc`/`-alpha`), `bin: null` — библиотека, а не запускаемый сервер.
---
## 6. Питфоллы
1. 🔴 **`Human` требует TTY** — non-interactive режим отвергает граф целиком, включая
невыбранные ветки Router. Запуск через `docker exec` без `-t` = non-interactive.
2. 🔴 **`cmd` — одно имя файла.** Аргументы только в `extraArgs`, иначе провайдер не стартует.
3. 🔴 **Не подавать вывод модели в `cmd`/`extraArgs`** — исполнение произвольного кода.
4. 🔴 **ACP-провайдер — дочерний процесс callee** — движок и callee не могут быть на
разных хостах.
5. 🔴 **Гейт `Human` не выдаёт доступ** — только разрешает уже возможное действие.
6. ⚠️ **`prism/lifecycle` обязан быть `Router`** — иначе устаревший импорт.
7. ⚠️ **Непустой stderr ≠ провал** — судить по exit status.
8. ⚠️ **Таймаут Script валит весь воркфлоу** — не «продолжает с ошибкой».
9. ⚠️ **Concurrency группы `Parallel`** используют один живой shared state.
10. ⚠️ **Поиск npm по `scope:normahq` / `scope:baldaworks` даёт пусто** — эти scoped-пакеты
через search-API не индексируются. Искать прямыми URL
`registry.npmjs.org/<scope>%2f<name>`.
11. ⚠️ **Динамических маунтов на macOS Docker нет** — только пересоздание контейнера.
---
## Связанные заметки
- [[personal/plans/balda-callee-prism-plan]] — план внедрения (топология: стек на Mac)
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план Фаз 1-5
- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (мост, приёмка)
- [[personal/tech/agents-bridge]] — мост :8790 (демонтируется в новой топологии)
- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования (демонтируется)
- [[personal/tech/macos-docker-virtiofs-mount-limits]] — почему нет динамических маунтов
Команды и схемы: `docs/protocol.md`, `docs/executors.md`, `docs/environment-changes.md`, `docs/operations.md` Broker. Версионная справка сентября сохранена в [[personal/projects/archive/callee-prism-contracts-2026-09-29]] и не используется как актуальный API reference.