[2026-09-29] eagle: personal/plans/balda-callee-prism-plan.md personal/plans/balda-mm-phase4-agents.md personal/tech/agents-bridge.md personal/tech/callee-prism.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-29 01:02:57 +06:00
1 parent d91cb21f9c
commit 4aff6eec6a
4 files changed
+544 -358

No files matched your search

+242 -351
View File
@@ -1,216 +1,30 @@
# Балда → Callee + Prism: план перестройки делегирования
# Балда → Callee + Prism: делегирование
> Создан 2026-09-28. Статус: **ПЛАН, работа не начата.**
> Заменяет собой схему «мост :8790 → `claude -p` по подписке» из
> [[personal/plans/balda-mm-phase4-agents]] (Фаза 4).
> Причина: подписочный CLI требует ручного логина в контейнере (`Not logged in`,
> проверено 2026-09-28) — блокер снимается переходом на документированную
> архитектуру callee, где провайдер **рендерится из состояния**, а не логинится.
> Обновлён 2026-09-29. Статус: **C0 закрыта, C1-C5 не начаты.**
> Часть [[personal/projects/balda/balda-mm-delegation-plan]].
---
## 0. Что решено (и на чём основано)
Alex 2026-09-28: «Давай пока на deepseek с обоих концов соберём, потом будем с
колексом решать» + «надо приколбасить prism для таск-трекинга» + «Ты скажи план».
| # | Решение | Источник |
|---|---|---|
| 1 | Оба конца — DeepSeek | команда Alex |
| 2 | Оркестрация — **callee**, не самодельная | документация callee (см. §1) |
| 3 | Таск-трекинг — **prism** + Beads (`bd`) | задание Alex |
| 4 | Мост `:8790` — сохранить, перевести с `claude -p` на `callee agent run` | мой выбор, обоснован в §4 |
| 5 | Host coding agent — **`callee` CLI в контейнере `balda-executor`** | мой выбор, обоснован в §4 |
---
## 1. Что говорит документация (факты, проверено 2026-09-28)
Источники: `baldaworks/callee` (README + `docs/concepts/architecture.md`,
`docs/guides/acp-providers.md`, `docs/agent-kinds/dynamic-role.md`),
`baldaworks/prism`, `baldaworks/prism-callee`. Версия callee — **0.22.2** (npm).
### 1.1 Кто чем владеет — разделение жёсткое
```
Markdown/YAML ресурсы
→ decode + schema/semantic/template validation
→ registry + static graph resolution ← отвергает циклы, дубли ID, битые ссылки
→ resolved root tree + required parameter keys
→ workflow runner + ОДИН общий ephemeral state-объект
├→ Role / DynamicRole → Norma Runtime → ACP-процесс → свежая сессия
├→ Script → локальный shell
├→ Human → управляющий терминал
└→ TypeSafeJev / OpenRouterDecision → HTTP API
→ ОДИН корневой артефакт в stdout
```
| Слой | Владелец | Цитата |
|---|---|---|
| Граф воркфлоу, валидация, state | **callee** | «validates the whole graph before execution, carries one explicit state object, returns one final artifact» |
| Durable-состояние + одобрение | **coding agent** | «The coding agent owns durable state and approval» (prism-callee) |
| Исполнение модели | ACP-процесс | «Role/DynamicRole → Norma Runtime → ACP provider process → fresh session» |
### 1.2 🔴 DeepSeek в списке ACP-провайдеров ОТСУТСТВУЕТ
Поддерживаемые типы — **закрытый список**:
| `type` | Команда по умолчанию | Предусловие |
|---|---|---|
| `codex` | сам callee + `bridge codex` | установленный и авторизованный Codex CLI |
| `claude` | `npx -y @zed-industries/claude-code-acp@latest` | Node + npx + креды адаптера |
| `opencode` | `opencode acp` | установленный OpenCode |
| `copilot` | `copilot --acp --stdio` | установленный Copilot CLI |
| `grok` | `grok agent stdio` | Grok CLI или `XAI_API_KEY` |
| `cursor` | `agent acp` | установленный Cursor CLI |
| `generic_acp` | **нет — требует непустой `cmd`** | любой ACP-исполняемый файл |
> «The supported type list is closed. **Gemini is not a valid provider type.**»
**Следствие:** «DeepSeek с обоих концов» напрямую через ACP-тип **не выражается**.
Рабочий путь — **`generic_acp` + свой ACP-адаптер к DeepSeek** в `cmd`.
Это ключевая развилка плана и главный риск (§7).
### 1.3 DynamicRole — механизм, который снимает наш блокер
Все поля провайдера — **шаблоны**, рендерятся из состояния в момент визита:
```yaml
kind: DynamicRole
spec:
provider:
type: '{{ .State.provider }}'
model: '{{ .State.model }}'
reasoning: '{{ .State.reasoning }}'
timeout: '{{ .State.timeout }}'
state:
provider: codex
model: gpt-5.6-sol
reasoning: high
timeout: 20m
```
| Факт | Значение |
|---|---|
| Что рендерится | `type`, `cmd`, `model`, `reasoning`, `mode`, `timeout`, каждый `extraArgs` |
| Из чего | `.Prompt`, текущий `.Input`, **один** согласованный post-node снапшот `.State`, `.Params` |
| Чего **нельзя** | `.Output`, env, файловая система, сеть, часы, рандом, криптография, мутаторы |
| Когда валидируется | до поиска и старта процесса; ошибка → визит падает, провайдер не стартует |
| Периметр визита | конфиг фиксирован на весь визит, включая REPL-ходы; новый визит Loop рендерит заново |
| Переиспользование процесса | одинаковые effective type+cmd → один процесс; **сессия всегда новая** |
🔴 **Безопасность (цитата):** «State-driven `cmd` and `extraArgs` can select executable
code. Do not copy untrusted model output into them.» — `cmd` это **одно имя исполняемого
файла**, `extraArgs` — argv-список; shell не участвует.
### 1.4 Callee и prism — НЕЗАВИСИМЫЕ установки
> «Each repository installs and validates independently — installing one plugin does
> not install the other.»
| | `baldaworks/prism` | `baldaworks/prism-callee` |
|---|---|---|
| Что это | плагин в coding agent (Codex/Claude/Grok/Copilot/Cursor) | callee-агенты для тех же воркфлоу |
| Требования | coding agent + `bd` (Beads) | coding agent + `bd` + **callee ≥ 0.19.0** |
| Команды | `$prism:story`, `$prism:epic`, `$prism:lifecycle` | `$prism-callee:lifecycle` |
| Состояние | **Beads (`bd`)** | **Beads (`bd`)** |
| Импорт агентов | — | `callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix prism` |
**Проверка валидности импорта:** `callee agent view prism/lifecycle --json` должен
вернуть **kind `Router`**. Если `Sequential` — импорт устарел, обновлять `--force`.
### 1.5 Prism — жизненный цикл Story/Epic (это и есть таск-трекинг)
**Story:** Change intent → Specify → (clarify/ready) → Design → Breakdown →
**Human approval** → Apply and review → Verify → Verified change.
Фазы: `phase:story:specify` → `design` → `breakdown` → `human` → `apply` → `verify`.
**Epic:** Frame → Architecture → Roadmap → **approval** → Deliver Stories → Validation.
Фазы: `phase:epic:frame` → `architecture` → `roadmap` → `approval` → `delivery` → `validation`.
| Правило | Значение |
|---|---|
| Дети Epic | только **Stories** |
| Дети Story | только **Tasks** |
| Вложенные Epic / Task напрямую в Epic | **невалидно** |
| Фиксированного диапазона числа детей | **нет** — оценка качественная |
| `Apply` | 🔴 **каждый переход требует явной авторизации человека** |
| Одобрение Epic → Story | **не переносится** («approval never transfers from an Epic to a Story») |
### 1.6 ✅ C0 ЗАКРЫТ (2026-09-28): ACP-адаптер к DeepSeek СУЩЕСТВУЕТ
Разведка npm-реестра (§3 Фаза C0). **Свой адаптер писать НЕ надо** — найдено два
готовых, выбран один.
| | `@deepseek-ai/dsh-acp` | **`@openma/deepseek-harness-acp`** ✅ ВЫБРАН |
|---|---|---|
| Версия | `0.0.1-rc.1` (`latest`) | **`0.4.33`** |
| dist-tags | `latest: 0.0.1-rc.1`, `alpha: 0.1.7-alpha.2`, `next: 0.1.7-rc.2` | — |
| Родословная | официальный DeepSeek | обёртка вокруг **официального** DeepSeek Harness |
| `bin` | **`null`** — это библиотека | **`dsh-acp` → `dist/bin.js`** — запускаемый сервер |
| Родословная/зрелость | pre-release: последние 8 версий сплошь `-rc`/`-alpha`, стабильных нет | 33 патча, обкатан |
| Зависимости | `@agentclientprotocol/sdk 0.25.1`, `@deepseek-ai/schemastery` | `@agentclientprotocol/sdk 1.4.0`, `tar`, `zod` |
| Node | — | `>= 22.15` (в контейнере 24.21.0 ✅) |
| Лицензия | — | Apache-2.0 |
**Почему официальный пакет не подошёл:** `@deepseek-ai/dsh-acp` в статусе pre-release
(стабильных версий нет), `bin: null` → это библиотека для сборки, а не запускаемый
ACP-сервер. `@openma`-версия даёт готовый исполняемый файл.
**Как передаются креды (проверено по README пакета):** приоритет
**флаги → переменные окружения → дефолты**. Поддержаны:
| Способ | Значение |
|---|---|
| Флаг | `api-key`, либо `api-key:<provider>` когда провайдеров несколько |
| Env | **`DEEPSEEK_API_KEY`** |
| Env | **`DEEPSEEK_BASE_URL`** — «OpenAI-compatible endpoint override» |
📌 **Следствие для Colex:** `DEEPSEEK_BASE_URL` — это OpenAI-совместимый override,
значит смена провайдера на Colex сводится к смене одной переменной, без правки графа.
**Ключ у нас есть:** `DEEPSEEK_API_KEY` лежит в `~/.hermes/hermes-whale/.env`
(длина 35 символов, проверено; значение в чат не выводилось).
**Два способа установки (из README пакета):**
| | A · dsh profile plugin (рекомендуемый) | B · Standalone server |
|---|---|---|
| Для чего | обычная установка и апгрейды | подключение ACP-клиента без управления профилем |
| Установка | `dsh plugin --profile acp add @openma/deepseek-harness-acp@latest` | `npm i -g @openma/deepseek-harness-acp` |
| Запуск | `dsh --profile acp` | `dsh-acp` |
📌 Для callee (`generic_acp` + непустой `cmd`) подходит **вариант B**: `cmd: dsh-acp`.
📌 Креды не попадают в конфиг клиента: адаптер переиспользует ключ из credential store
(`dsh web` или `dsh-acp login`), либо берёт `DEEPSEEK_API_KEY` из окружения процесса.
🔴 **Node-требование:** пакет требует Node `>= 22.15` — учитывать при сборке образа.
В текущем `balda-executor:0.1.0` стоит Node **24.21.0** ✅.
---
## 2. Целевая архитектура
## 1. Целевая архитектура
```
Mattermost (Alex)
│ «добавь CSV-экспорт в отчёт»
▼
Балда-оркестратор (Kraken, DeepSeek, balda-mm:0.1.13)
│ верхний уровень: приём сообщения, чтение Obsidian, контекст, ответ в чат
│ приём сообщения, чтение Obsidian, контекст, ответ в чат
│ MCP-инструмент executor_run
▼
MCP-сервер balda-executor-mcp (Kraken, :8890, сеть mattermost_mm-net)
│ HTTP + X-Bridge-Secret
▼
Мост agents-bridge (Mac, :8790, launchd com.agents.bridge)
│ 🔴 МЕНЯЕТСЯ: было `claude -p --output-format text`
│ стало `callee agent run <resource> --message <prompt>`
│ запускает `callee agent run <resource> --message <prompt>`
▼
callee CLI (контейнер balda-executor, Mac)
│ компилирует граф → валидирует ЦЕЛИКОМ до запуска
│ общий state-объект через весь прогон → ОДИН артефакт в stdout
├→ Role / DynamicRole → ACP-процесс → DeepSeek (generic_acp + адаптер)
│ общий state-объект через прогон → ОДИН артефакт в stdout
├→ Role / DynamicRole → ACP-процесс → DeepSeek (generic_acp)
├→ Script → локальные детерминированные проверки
└→ Human → точка одобрения
▼
@@ -219,232 +33,309 @@ Beads (`bd`) — durable-состояние Story/Epic, переживает с
артефакт → Балда → Mattermost
```
### Разделение ролей
| Слой | Владелец |
|---|---|
| Граф воркфлоу, валидация, state | **callee** |
| Durable-состояние + одобрение | **coding agent** |
| Исполнение модели | ACP-процесс |
### Разделение расходов
| Роль | Где | Провайдер | Стоимость |
|---|---|---|---|
| Оркестратор верхнего уровня | Kraken | DeepSeek `deepseek-chat` | дёшево |
| Исполнитель-роли callee | Mac, контейнер | DeepSeek (через адаптер) | дёшево |
| Позже | — | Codex (подписка) | решается после |
| Роль | Где | Провайдер |
|---|---|---|
| Оркестратор верхнего уровня | Kraken | DeepSeek `deepseek-chat` |
| Исполнитель-роли callee | Mac, контейнер | DeepSeek |
📌 Формулировка «DeepSeek с обоих концов» = **обе стороны на DeepSeek**, подписочный
Codex подключается **позже** заменой `State.provider` в DynamicRole — без правки графа.
📌 «DeepSeek с обоих концов» = обе стороны на DeepSeek. Подписочный Codex подключается
позже заменой `State.provider` в `DynamicRole` — без правки графа.
---
## 3. Фазы работ
## 2. Провайдер DeepSeek для callee
### Фаза C0 — ✅ ЗАКРЫТА 2026-09-28 (разведка npm, ~20 мин)
### 2.1 ACP-адаптер — выбран `@openma/deepseek-harness-acp@0.4.33`
**Результат: готовый ACP-адаптер к DeepSeek существует.** Выбран
`@openma/deepseek-harness-acp@0.4.33` (детали и сравнение — §1.6). Свой адаптер
(C2-B) **не нужен**, опора на патч `norma-local` не потребуется.
Свой адаптер писать не надо.
Что проверялось: npm-поиск по `acp agent`, `deepseek acp agent`,
`acp openai compatible agent`; метаданные и `bin`/`dependencies`/`engines`
кандидатов; README выбранного пакета.
| | Значение |
|---|---|
| Пакет | `@openma/deepseek-harness-acp` |
| Версия | **`0.4.33`** |
| `bin` | **`dsh-acp`** → `dist/bin.js` — запускаемый ACP-сервер |
| Родословная | обёртка вокруг официального DeepSeek Harness |
| Node | **`>= 22.15`** (в контейнере 24.21.0 ✅) |
| Лицензия | Apache-2.0 |
| Зависимости | `@agentclientprotocol/sdk 1.4.0`, `tar`, `zod` |
⛔ **Не сработало:** `npm search` по scope `scope:normahq` и `scope:baldaworks`
вернул **пусто** — эти scoped-пакеты через search-API не индексируются.
Искать только по прямым URL `registry.npmjs.org/<scope>%2f<name>`.
Установка (standalone-вариант, подходит для `generic_acp`):
**Гейт пройден:** решение «адаптер готов» зафиксировано в §1.6.
```bash
npm i -g @openma/deepseek-harness-acp
# запуск: dsh-acp
```
### Фаза C1 — установка на Mac (1-2 ч)
### 2.2 Креды
Всё ставится **в контейнер `balda-executor`** (там Node 24 — родная среда callee).
Проверено сейчас: ни `callee`, ни `bd` нет ни в контейнере, ни на хосте.
Приоритет: **флаги → переменные окружения → дефолты**.
| Способ | Значение |
|---|---|
| Флаг | `api-key`, либо `api-key:<provider>` при нескольких провайдерах |
| Env | **`DEEPSEEK_API_KEY`** |
| Env | **`DEEPSEEK_BASE_URL`** — OpenAI-совместимый endpoint override |
🔴 `DEEPSEEK_BASE_URL` — это OpenAI-совместимый override, значит переезд на Colex
сводится к смене одной переменной, без правки графа.
📌 Ключ `DEEPSEEK_API_KEY` лежит в `~/.hermes/hermes-whale/.env` (длина 35).
📌 Креды не попадают в конфиг клиента: адаптер переиспользует ключ из credential store
(`dsh web` / `dsh-acp login`), либо берёт `DEEPSEEK_API_KEY` из окружения процесса.
### 2.3 Конфигурация Role
Тип провайдера — **`generic_acp`**, потому что список поддерживаемых типов закрытый:
`codex`, `claude`, `opencode`, `copilot`, `grok`, `cursor`, `generic_acp`.
DeepSeek в нём отсутствует.
```yaml
provider:
type: generic_acp
cmd: dsh-acp # одно имя исполняемого файла, НЕ shell-строка
model: deepseek-chat
timeout: 20m
```
| Поле | Контракт |
|---|---|
| `cmd` | одно имя исполняемого файла; для `generic_acp` обязателен |
| `extraArgs` | упорядоченный argv-список; каждый элемент непустой |
| `model` / `reasoning` / `mode` | опциональны; пустые → дефолт бэкенда |
| `timeout` | Go duration, **по умолчанию `15m`** |
| Конфиг провайдера | обязан быть **вложенным** (`spec.provider.*`), плоские поля не поддержаны |
### 2.4 DynamicRole — провайдер из состояния
Все поля провайдера — шаблоны, рендерятся из состояния в момент визита:
```yaml
kind: DynamicRole
spec:
provider:
type: '{{ .State.provider }}'
model: '{{ .State.model }}'
timeout: '{{ .State.timeout }}'
state:
provider: generic_acp
model: deepseek-chat
timeout: 20m
```
| Факт | Значение |
|---|---|
| Что рендерится | `type`, `cmd`, `model`, `reasoning`, `mode`, `timeout`, каждый `extraArgs` |
| Из чего | `.Prompt`, текущий `.Input`, один согласованный post-node снапшот `.State`, `.Params` |
| Чего нельзя | `.Output`, env, файловая система, сеть, часы, рандом, криптография, мутаторы |
| Когда валидируется | до поиска и старта процесса; ошибка → визит падает, провайдер не стартует |
| Периметр визита | конфиг фиксирован на весь визит, включая REPL-ходы; новый визит Loop рендерит заново |
| Переиспользование процесса | одинаковые effective type+cmd → один процесс; сессия всегда новая |
🔴 **Безопасность:** state-driven `cmd` и `extraArgs` могут выбирать исполняемый код.
Не подавать туда вывод модели без ограничения через авторскую шаблонную логику.
---
## 3. Prism — таск-трекинг
### 3.1 Callee и prism — независимые установки
Установка одного плагина не ставит другой.
| | `baldaworks/prism` | `baldaworks/prism-callee` |
|---|---|---|
| Что это | плагин в coding agent (Codex/Claude/Grok/Copilot/Cursor) | callee-агенты для тех же воркфлоу |
| Требования | coding agent + `bd` (Beads) | coding agent + `bd` + callee ≥ 0.19.0 |
| Команды | `$prism:story`, `$prism:epic`, `$prism:lifecycle` | `$prism-callee:lifecycle` |
| Состояние | Beads (`bd`) | Beads (`bd`) |
Импорт callee-агентов:
```bash
callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix prism
```
🔴 **Проверка импорта:** `callee agent view prism/lifecycle --json` → **kind `Router`**.
`Sequential` = устаревший импорт, обновлять `--force`.
### 3.2 Жизненный цикл
**Story:** Specify → Design → Breakdown → **Human approval** → Apply → Verify.
Фазы: `phase:story:specify` → `design` → `breakdown` → `human` → `apply` → `verify`.
**Epic:** Frame → Architecture → Roadmap → **approval** → Deliver Stories → Validation.
| Правило | Значение |
|---|---|
| Дети Epic | только Stories |
| Дети Story | только Tasks |
| Вложенные Epic / Task напрямую в Epic | невалидно |
| Фиксированного диапазона числа детей | нет — оценка качественная |
| Apply | каждый переход требует явной авторизации человека |
| Одобрение Epic → Story | не переносится |
---
## 4. Одобрения в Mattermost — работают
Одобрение проходит **ответом в треде**, как в Slack.
| Механизм | Статус |
|---|---|
| Вопрос с опциями (markdown-нумерация) | ✅ `questionfmt` + `questioncmd` + `deliveryfmt`, покрыто тестами |
| Приём ответа реплаем | ✅ `BuildInboundReply` через `RootID` (`channel/mattermost/inbound.go`) |
| `ask` как режим разрешений | ✅ `mattermost` в списке каналов (`permissions/service.go`) |
📌 `ask` — это режим политики разрешений агента; механизм вопросов — отдельный слой.
Оба в MM работают.
⛔ **Инлайн-кнопок и реакций в MM нет** — только Telegram. Событие `reaction`
сознательно отбрасывается в `channel/mattermost/ingress.go`.
---
## 5. Фазы
### C0 — ✅ ЗАКРЫТА 2026-09-29
Разведка npm: готовый ACP-адаптер к DeepSeek существует (§2.1). Свой адаптер не нужен.
**Метод поиска:** прямые URL `registry.npmjs.org/<scope>%2f<name>` и
`/-/v1/search?text=...`. ⛔ Поиск по `scope:normahq` и `scope:baldaworks` даёт пусто —
scoped-пакеты так не индексируются.
### C1 — установка в контейнер `balda-executor` (1-2 ч)
Ни `callee`, ни `bd` нет ни в контейнере, ни на хосте.
| # | Шаг | Проверка |
|---|---|---|
| 1 | `npm i -g @baldaworks/callee` в контейнере | `callee --version` |
| 2 | `callee setup codex` (или иной host agent) → 6 стартовых ресурсов | `callee agent list` содержит `workflows/investigate` |
| 3 | Установить Beads `bd` | `bd where` |
| 4 | Импорт prism-пака | `callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix prism` |
| 5 | 🔴 Проверить импорт | `callee agent view prism/lifecycle --json` → **kind `Router`** (не `Sequential`!) |
| 6 | Прогнать стартовый `investigate` | `callee agent run workflows/investigate --message "..."` |
| 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 |
📌 Персистентность: установки в контейнере живут в его слое. Если понадобится
переживать пересборку — вынести `npm`-глобаль в том (решение принять на C1, п.1).
📌 Персистентность: установки живут в слое контейнера. Чтобы переживали пересборку —
том или `Dockerfile`.
### Фаза C2 — провайдер DeepSeek для callee (главная работа, 4-8 ч)
### C2 — Role к DeepSeek (1 ч)
Два варианта, выбор по результату C0:
Описать `Role` по §2.3 и проверить один визит вживую.
**Вариант C2-A — готовый адаптер найден.** Описать `Role` со `spec.provider.type:
generic_acp`, `cmd: <адаптер>`, `extraArgs` с endpoint DeepSeek. Проверить один
`Role`-визит вживую.
**Гейт:** в логе видны `role_model` и `role_reasoning`. До успешного рендера провайдера
эти поля опускаются — их появление означает, что рендер прошёл.
**Вариант C2-B — пишем адаптер.** Минимальный ACP-процесс, транслирующий ACP-сессии
в OpenAI-совместимый `chat/completions` DeepSeek. Опорный материал — наш прошлый
опыт: патч `tool_calls` в `norma-local` (`pkg/runtime/hostedagent/openai.go`,
коммит `91c2c0a`) и PR `normahq/runtime#4`. 🔴 **Там же грабли:** DeepSeek отдаёт
`function.arguments` **двойной сериализацией** (JSON-строкой внутри JSON-строки).
См. [[personal/projects/balda/balda-valera]].
**Гейт C2:** один `Role`-визит к DeepSeek отрабатывает вживую, в логе виден
`role_model`, `role_reasoning` (документация: до успешного рендера эти поля
опускаются — значит их появление = рендер прошёл).
### Фаза C3 — подключение воркфлоу к мосту (2-3 ч)
### C3 — подключение к мосту (2-3 ч)
1. Бэкап `~/Developer/agents-bridge/executor.py`.
2. Замена argv-таблицы `PROVIDERS`:
```python
PROVIDERS = {
# было: "claude": ["claude", "-p", "--output-format", "text"],
# стало:
"callee": ["callee", "agent", "run", "prism/lifecycle", "--message"],
}
```
3. Промпт передаётся последним аргументом (как сейчас).
4. 🔴 `EXECUTOR_PROVIDER` в `~/Docker/balda-executor/.env` меняется на `callee`.
5. Перезапуск моста: `launchctl bootout` + `bootstrap` (не `kickstart` — см. питфолл §6).
6. Прогон задачи через мост (`POST /task` + `Idempotency-Key`), проверка `exit_code`.
3. `EXECUTOR_PROVIDER=callee` в `~/Docker/balda-executor/.env`.
4. Перезапуск моста: `launchctl bootout` + `bootstrap`.
5. Прогон задачи через мост, проверка `exit_code`.
### Фаза C4 — таск-трекинг prism/Beads (1-2 ч)
### C4 — таск-трекинг (1-2 ч)
1. `bd` инициализируется в рабочей области `/agents`.
2. Прогон `prism:story` на реальной задаче: ожидаемый поток
`specify → design → breakdown → human → apply → verify`.
3. 🔴 Проверить, что пауза на `phase:story:human` **доходит до Mattermost** —
иначе точка одобрения недостижима и воркфлоу встанет намертво. Это ограничение
известно: `ask` в Балде поддержан **только** для Telegram и Slackagent, для
Mattermost фейлится закрыто. См. §6 «Питфолл 1» и
`docs/architecture/interactive-questions.md`.
4. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.
1. `bd` инициализируется в `/agents`.
2. Прогон `prism:story` на реальной задаче.
3. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.
### Фаза C5 — сквозная приёмка (1 ч)
### C5 — сквозная приёмка (1 ч)
По правилам приёмки этого проекта — **от учётки Alex**, не подставным аккаунтом:
От **учётки Alex**, не подставным аккаунтом:
```
@balda добавь CSV-экспорт в отчёт
→ Specify (вопросы, если нужны)
→ Design → Breakdown
→ Human approval ← 🔴 Alex подтверждает
→ Apply → Verify
→ Specify → Design → Breakdown → Human approval → Apply → Verify
→ ответ в Mattermost + артефакт
```
Доказательство — лента канала `qentra:balda-test`, автор поста = `cbwsorzwoigbtrdnteugnx4aqo`.
Метод чтения ленты и постинга от владельца — §17 в
[[personal/plans/balda-mm-phase4-agents]].
Доказательство — лента `qentra:balda-test`, автор поста `cbwsorzwoigbtrdnteugnx4aqo`.
---
## 4. Обоснование моих решений (Alex делегировал выбор)
## 6. Решения
**Почему host coding agent — callee CLI в контейнере `balda-executor`:**
там уже Node 24 (родная среда callee), там же монтируется `~/agents` (рабочая
область), и оттуда мост уже умеет запускать команды через `docker exec`. Ставить
callee на хост Mac — значит плодить вторую среду и разбираться с PATH в launchd.
**Почему мост сохраняем, а не выбрасываем:**
документация callee не требует HTTP-прослойки — Балда могла бы вызывать `callee
agent run` и через свой MCP. Но MCP-сервер `balda-executor-mcp` уже работает и
принят (§19.5), туннель `agents-bridge.qentra.top` уже заведён, идемпотентность,
таймауты и лимит в 1 задачу уже реализованы и проверены (6 проверок). Выбросить их
во имя «чище по доке» — значит потерять работающий слой ради эстетики. Мост
остаётся транспортом, меняется **одна таблица argv**.
**Почему не «вторая Балда»:**
дублирование оркестратора даёт два источника истины по состоянию сессии и
конфликты локаторов. Документация к такому не призывает.
| # | Вопрос | Решение | Почему |
|---|---|---|---|
| 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 задача уже проверены; выбрасывать работающий слой ради эстетики незачем |
| 6 | Тип провайдера | `generic_acp` | список типов закрытый, DeepSeek в нём нет |
---
## 5. Что снимается этим планом
## 7. Питфоллы
| Дефект | Как снимается |
|---|---|
| 🔴 `Not logged in · Please run /login` в контейнере-исполнителе (найден 2026-09-28) | DeepSeek-провайдер рендерится из состояния — логина в подписочный CLI вообще нет |
| Ручной OAuth-логин Alex'а в контейнер | не нужен |
| Потеря логина при `docker compose up -d` (`.claude.json` не в хост-монтировании) | неактуально |
| Самодельная оркестрация в промптах | заменяется валидируемым графом callee |
| Нет таск-трекинга | prism + Beads |
1. ⚠️ **`PROVIDERS` в мосте — argv-список.** Промпт должен остаться **одним**
последним элементом, иначе передача рвётся.
2. ⚠️ **`.env` не перебивает plist.** `--host` жёстко в `ProgramArguments`; смена
аргументов требует `bootout` + `bootstrap`. Проверка:
`lsof -nP -iTCP:8790 -sTCP:LISTEN` → должно быть `*:8790`.
3. ⚠️ **`prism/lifecycle` должен быть `Router`** — `Sequential` означает устаревший
импорт, лечится `--force`.
4. ⚠️ **Provider `cmd` — одно имя файла, не shell-строка.** Аргументы — только
в `extraArgs`.
5. ⚠️ **Никогда не маршрутизировать вывод модели в `cmd`/`extraArgs`.**
6. ⚠️ **Установки в контейнере не переживают пересборку** — том или `Dockerfile`.
7. ⚠️ **`docker-compose` (дефис) на Mac, `docker compose` (пробел) на Kraken.**
8. ⚠️ **Node `>= 22.15`** требуется адаптером (в образе 24.21.0).
---
## 6. Питфоллы
1. 🔴 **`Human`-узел против Mattermost.** Документация callee/`prism:story` требует
пауз на одобрение (`phase:story:human`), а в Балде `ask` для Mattermost фейлится
закрыто. Пока не решено — воркфлоу может встать намертво на первой же точке
одобрения. Варианты: (а) прокинуть человеческий ответ через мой мост как
отдельный HTTP-вызов; (б) на первом этапе использовать воркфлоу без `Human`;
(в) чинить `ask` в MM-адаптере (отдельный PR). **Решать до C4.**
2. 🔴 **`PROVIDERS` в мосте — argv-список, замена «в лоб» рвёт передачу промпта.**
Сейчас: `["docker","exec","-w","/agents","@container", *base, prompt]`. Промпт
должен остаться **одним** последним элементом.
3. 🔴 **`.env` не перебивает plist.** `--host` жёстко в `ProgramArguments`; смена
аргументов требует `bootout` + `bootstrap`. Проверка — `lsof -nP -iTCP:8790
-sTCP:LISTEN` (должно быть `*:8790`).
4. ⚠️ **`callee agent view prism/lifecycle --json` должен вернуть `Router`.**
`Sequential` = устаревший импорт, лечится `--force`.
5. ⚠️ **Двойная сериализация `function.arguments` у DeepSeek** — повторится в любом
своём адаптере (C2-B). Готовая логика разбора есть в `norma-local`.
6. ⚠️ **Установки в контейнере не переживают пересборку образа** — если callee/bd
окажутся нужны после `docker compose build`, их надо либо заложить в `Dockerfile`,
либо вынести в том.
7. ⚠️ **`docker-compose` (дефис) на Mac, `docker compose` (пробел) на Kraken** —
проверено, легко перепутать.
8. ⚠️ **Provider `cmd` — одно имя исполняемого файла, не shell-строка.** Любые
аргументы — только в `extraArgs`, иначе провайдер не стартует.
9. ⚠️ **Никогда не маршрутизировать вывод модели в `cmd`/`extraArgs`** — прямой
путь к исполнению произвольного кода (предупреждение документации).
---
## 7. Риски
## 8. Риски
| Риск | Оценка | Митигация |
|---|---|---|
| 🔴 **ACP-адаптера к DeepSeek может не существовать** | **высокий** | Фаза C0 решает это до основных работ. Если писать свой — 4-8 ч; опора на готовый патч `norma-local` |
| 🔴 `Human`-узел не доходит через Mattermost | **высокий** | Решить до C4 (три варианта в §6.1) |
| Импорт `prism/*` устаревает при обновлении | средний | проверять `kind: Router` после каждого импорта |
| callee обновляется (0.22.2 → выше) и меняет контракт | средний | пиннить версию в ресурсах; следить за `docs/` |
| callee обновляется и меняет контракт | средний | пиннить версию в ресурсах |
| Мост теряет работоспособность после смены argv | низкий | бэкап `executor.py`, откат одной правкой |
| Стоимость DeepSeek растёт (обе стороны на нём) | низкий | оркестратор уже на DeepSeek; исполнитель-роли — короткие визиты |
| Установка в контейнер не переживёт пересборку | низкий | решается томом или `Dockerfile` (§6.6) |
| Стоимость DeepSeek растёт | низкий | исполнитель-роли — короткие визиты |
| Установка в контейнер не переживёт пересборку | низкий | том или `Dockerfile` |
---
## 8. Порядок и что НЕ делать
## 9. Порядок и запреты
**Порядок:** C0 → (решение) → C1 → C2 → C3 → C4 → C5.
C0 и C1 независимы — можно параллелить.
**Порядок:** C1 → C2 → C3 → C4 → C5.
**НЕ трогать в `~/Developer/balda`:**
`go.mod`, `Dockerfile.release`, `go.sum`, `internal/apps/balda/app.go`,
`internal/apps/balda/preflight.go` — это вне PR-дерева, нужны для сборки стенда
Kraken. `git checkout -- .` / `stash` / `reset --hard` их снесёт. См.
[[personal/plans/balda-mm-pr-plan]] §5.
**НЕ трогать в `~/Developer/balda`:** `go.mod`, `Dockerfile.release`, `go.sum`,
`internal/apps/balda/app.go`, `internal/apps/balda/preflight.go` — вне PR-дерева,
нужны для сборки стенда. `git checkout -- .` / `stash` / `reset --hard` их снесёт.
См. [[personal/plans/balda-mm-pr-plan]] §5.
**НЕ начинать работу** без явного «поехали» — правило проекта: план → апрув → работа.
**Ничего не удалять** — образы для отката, диагностические контейнеры и старые теги
не трогать без отдельного подтверждения.
---
## 9. Открытые вопросы
1. 🔴 **Есть ли готовый ACP-адаптер к DeepSeek** — Фаза C0, блокирует C2.
2. 🔴 **Как проводить `Human`-одобрение через Mattermost** — §6.1, блокирует C4.
3. Нужен ли подписочный Codex в контуре вообще, или после Colex'а схема меняется
целиком (Alex: «потом будем с колексом решать»).
**НЕ начинать работу** без явного «поехали».
**Ничего не удалять** без отдельного подтверждения.
---
## Связанные заметки
- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (предыдущая схема, мост :8790)
- [[personal/tech/callee-prism]] — **справочник по контрактам callee и prism**
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план Фаз 1-5
- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (мост :8790, приёмка)
- [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8)
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅ бот отвечает)
- [[personal/projects/balda/balda-valera]] — патч `tool_calls` для DeepSeek (опора для C2-B)
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3
- [[personal/tech/agents-bridge]] — мост :8790
- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования
- [[personal/projects/balda/balda-mattermost-connector]] — MM-коннектор
+15 -1
View File
@@ -25,7 +25,21 @@
| 6g | A/B-эксперимент откатом на `0.1.11` | ✅ **доведён 2026-09-27** — `0.1.11` падает С ТЕМ ЖЕ `duplicate tool`. Гипотеза §16.2 («форк обнажил») **ОПРОВЕРГНУТА**: баг был всегда, независимо от раунда. См. §16.4 |
| 6h | Фикс дублирующегося тулсета | ✅ **ИСПРАВЛЕНО 2026-09-27, `balda-mm:0.1.13` задеплоен** — дедуп по эндпоинту в `hostedToolsets` (`runtime-local/agentfactory/agentfactory.go`) + 6 тестов. `go build`/`go vet`/`go test ./...` ✅. Приёмка от владельца: `@balda вызови executor_health` → ответ `**executor_health:** ok`, `turn_complete: true`, `duplicate tool:` = **0**, deadletter = **0**. См. §19.3-19.5 |
| 7 | Приёмка от аккаунта Alex | ✅ **ПРОЙДЕНА 2026-09-27** — пост владельца `cbwsorzwoigbtrdnteugnx4aqo` → ответ бота `edxrgupoxiyig5gzmr79tjypbme`: `**executor_health:** ok`. `settlement_stage: accepted`, `turn_complete: true`, инструмент исполнился. См. §17, §19.5 |
| 8 | 🔴 **CLI исполнителя НЕ залогинен → реальная задача не проходит** | 🔴 **Открыто 2026-09-28** — весь конвейер живой, упирается ТОЛЬКО в логин CLI. `POST /task` через мост → `exit_code: 1`, `duration 0.8 c`, `stdout: "Not logged in · Please run /login"`. Диагноз, доказательства и два пути фикса — **§20**. Ждёт решения Alex |
| 8 | ~~CLI исполнителя НЕ залогинен~~ | ⛔ **СНЯТО 2026-09-29 — неактуально.** Схема `claude -p` по подписке **отменена**: провайдер обеих сторон переводится на **DeepSeek**, блокер логина исчезает (нечего логинить). Новая схема — **callee + prism**, план: [[personal/plans/balda-callee-prism-plan]]. Разбор дефекта оставлен в **§20** (история). |
🔴 **2026-09-29 — смена схемы Фазы 4.** Alex: «Давай пока на deepseek с обоих концов
соберём» + «надо приколбасить prism для таск-трекинга». Оркестрация передаётся
**callee** (документированная архитектура вместо самодельной), таск-трекинг — **prism +
Beads**, обе стороны на DeepSeek. Мост `:8790` **сохраняется**, меняется только argv-таблица.
**Актуальный план: [[personal/plans/balda-callee-prism-plan]].** Разделы §20 и «Два пути
фикса» ниже — история, к работе не применять.
**Найдено попутно (2026-09-29): одобрения в Mattermost РАБОТАЮТ.** `ask` для MM не
«fails closed», как утверждает `docs/architecture/interactive-questions.md:275` — в коде
`permissions/service.go` канал `mattermost` **есть в списке поддержанных**. Ответ на вопрос
приходит **реплаем в тред** (`BuildInboundReply` через `RootID`,
`channel/mattermost/inbound.go`). Реакции и инлайн-кнопки в MM не поддержаны — только Telegram.
🔴 **Документ апстрима устарел** — поправить в ветке `local/*`.
---
+13 -6
View File
@@ -3,6 +3,8 @@
> Создан 2026-09-26. Компонент Фазы 4 [[personal/plans/balda-mm-phase4-agents]] (шаг 2 ✅).
> Назначение: принять задачу от оркестратора Балды (Kraken) по HTTP, запустить
> CLI-исполнитель в контейнере `balda-executor`, вернуть вывод.
> 🔴 2026-09-29: провайдер меняется с `claude` (подписка) на **`callee`** (DeepSeek) —
> см. §Провайдеры и [[personal/plans/balda-callee-prism-plan]].
---
@@ -59,13 +61,18 @@ GET /tasks?limit=N → последние задачи (для дашбор
`executor.PROVIDERS` — argv для headless-запуска:
| Провайдер | argv |
|---|---|
| `claude` | `claude -p --output-format text <prompt>` |
| `codex` | `codex exec --skip-git-repo-check <prompt>` |
| Провайдер | argv | Статус |
|---|---|---|
| `claude` | `claude -p --output-format text <prompt>` | ⛔ **отменяется** — требует ручного логина в контейнере |
| `codex` | `codex exec --skip-git-repo-check <prompt>` | ⛔ по той же причине |
| `callee` | `callee agent run prism/lifecycle --message <prompt>` | 🔜 **целевой** (план [[personal/plans/balda-callee-prism-plan]], фаза C3) |
Дефолт — `EXECUTOR_PROVIDER=claude`. Команда собирается **списком аргументов** (без
`shell=True`), `WORKDIR=/agents` внутри контейнера — наружу не выйти.
Дефолт — `EXECUTOR_PROVIDER=claude` → **меняется на `callee`**. Команда собирается
**списком аргументов** (без `shell=True`), `WORKDIR=/agents` внутри контейнера — наружу не выйти.
⚠️ **При смене argv промпт обязан остаться ОДНИМ последним элементом списка** —
`["docker","exec","-w","/agents","@container", *base, prompt]`. Вставка аргументов после
`prompt` рвёт передачу.
---
+274
View File
@@ -0,0 +1,274 @@
# Callee + Prism — инструменты оркестрации и таск-трекинга
> Создан 2026-09-29. Опорная заметка к [[personal/plans/balda-callee-prism-plan]].
> Назначение: справочник по контрактам callee и prism, чтобы не перечитывать
> доки апстрима при каждой правке ресурсов.
---
## 1. Что это
| Инструмент | Репозиторий | Лицензия | Что делает |
|---|---|---|---|
| **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 |
📌 Версия 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.**»
---
## 4. 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` — библиотека, а не запускаемый сервер.
---
## 5. Питфоллы
1. 🔴 **`Human` требует TTY** — non-interactive режим отвергает граф целиком, включая
невыбранные ветки Router. Запуск через `docker exec` без `-t` = non-interactive.
2. 🔴 **`cmd` — одно имя файла.** Аргументы только в `extraArgs`, иначе провайдер не стартует.
3. 🔴 **Не подавать вывод модели в `cmd`/`extraArgs`** — исполнение произвольного кода.
4. ⚠️ **`prism/lifecycle` обязан быть `Router`** — иначе устаревший импорт.
5. ⚠️ **Непустой stderr ≠ провал** — судить по exit status.
6. ⚠️ **Таймаут Script валит весь воркфлоу** — не «продолжает с ошибкой».
7. ⚠️ **Concurrency группы `Parallel`** используют один живой shared state.
8. ⚠️ **Поиск npm по `scope:normahq` / `scope:baldaworks` даёт пусто** — эти scoped-пакеты
через search-API не индексируются. Искать прямыми URL
`registry.npmjs.org/<scope>%2f<name>`.
---
## Связанные заметки
- [[personal/plans/balda-callee-prism-plan]] — план внедрения
- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (мост, приёмка)
- [[personal/tech/agents-bridge]] — мост :8790
- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования