29 KiB
Executable File
Балда → Callee + Prism: план перестройки делегирования
Создан 2026-09-28. Статус: ПЛАН, работа не начата. Заменяет собой схему «мост :8790 →
claude -pпо подписке» из personal/plans/balda-mm-phase4-agents (Фаза 4). Причина: подписочный CLI требует ручного логина в контейнере (Not logged in, проверено 2026-09-28) — блокер снимается переходом на документированную архитектуру callee, где провайдер рендерится из состояния, а не логинится.
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 — механизм, который снимает наш блокер
Все поля провайдера — шаблоны, рендерятся из состояния в момент визита:
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. Целевая архитектура
Mattermost (Alex)
│ «добавь CSV-экспорт в отчёт»
▼
Балда-оркестратор (Kraken, DeepSeek, balda-mm:0.1.13)
│ верхний уровень: приём сообщения, чтение 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 CLI (контейнер balda-executor, Mac)
│ компилирует граф → валидирует ЦЕЛИКОМ до запуска
│ общий state-объект через весь прогон → ОДИН артефакт в stdout
├→ Role / DynamicRole → ACP-процесс → DeepSeek (generic_acp + адаптер)
├→ Script → локальные детерминированные проверки
└→ Human → точка одобрения
▼
Beads (`bd`) — durable-состояние Story/Epic, переживает сессии
▼
артефакт → Балда → Mattermost
Разделение расходов
| Роль | Где | Провайдер | Стоимость |
|---|---|---|---|
| Оркестратор верхнего уровня | Kraken | DeepSeek deepseek-chat |
дёшево |
| Исполнитель-роли callee | Mac, контейнер | DeepSeek (через адаптер) | дёшево |
| Позже | — | Codex (подписка) | решается после |
📌 Формулировка «DeepSeek с обоих концов» = обе стороны на DeepSeek, подписочный
Codex подключается позже заменой State.provider в DynamicRole — без правки графа.
3. Фазы работ
Фаза C0 — ✅ ЗАКРЫТА 2026-09-28 (разведка npm, ~20 мин)
Результат: готовый 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 выбранного пакета.
⛔ Не сработало: npm search по scope scope:normahq и scope:baldaworks
вернул пусто — эти scoped-пакеты через search-API не индексируются.
Искать только по прямым URL registry.npmjs.org/<scope>%2f<name>.
Гейт пройден: решение «адаптер готов» зафиксировано в §1.6.
Фаза C1 — установка на Mac (1-2 ч)
Всё ставится в контейнер balda-executor (там Node 24 — родная среда callee).
Проверено сейчас: ни 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 "..." |
📌 Персистентность: установки в контейнере живут в его слое. Если понадобится
переживать пересборку — вынести npm-глобаль в том (решение принять на C1, п.1).
Фаза C2 — провайдер DeepSeek для callee (главная работа, 4-8 ч)
Два варианта, выбор по результату C0:
Вариант C2-A — готовый адаптер найден. Описать Role со spec.provider.type: generic_acp, cmd: <адаптер>, extraArgs с endpoint DeepSeek. Проверить один
Role-визит вживую.
Вариант 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 ч)
- Бэкап
~/Developer/agents-bridge/executor.py. - Замена argv-таблицы
PROVIDERS:
PROVIDERS = {
# было: "claude": ["claude", "-p", "--output-format", "text"],
# стало:
"callee": ["callee", "agent", "run", "prism/lifecycle", "--message"],
}
- Промпт передаётся последним аргументом (как сейчас).
- 🔴
EXECUTOR_PROVIDERв~/Docker/balda-executor/.envменяется наcallee. - Перезапуск моста:
launchctl bootout+bootstrap(неkickstart— см. питфолл §6). - Прогон задачи через мост (
POST /task+Idempotency-Key), проверкаexit_code.
Фаза C4 — таск-трекинг prism/Beads (1-2 ч)
bdинициализируется в рабочей области/agents.- Прогон
prism:storyна реальной задаче: ожидаемый потокspecify → design → breakdown → human → apply → verify. - 🔴 Проверить, что пауза на
phase:story:humanдоходит до Mattermost — иначе точка одобрения недостижима и воркфлоу встанет намертво. Это ограничение известно:askв Балде поддержан только для Telegram и Slackagent, для Mattermost фейлится закрыто. См. §6 «Питфолл 1» иdocs/architecture/interactive-questions.md. - Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.
Фаза C5 — сквозная приёмка (1 ч)
По правилам приёмки этого проекта — от учётки Alex, не подставным аккаунтом:
@balda добавь CSV-экспорт в отчёт
→ Specify (вопросы, если нужны)
→ Design → Breakdown
→ Human approval ← 🔴 Alex подтверждает
→ Apply → Verify
→ ответ в Mattermost + артефакт
Доказательство — лента канала qentra:balda-test, автор поста = cbwsorzwoigbtrdnteugnx4aqo.
Метод чтения ленты и постинга от владельца — §17 в
personal/plans/balda-mm-phase4-agents.
4. Обоснование моих решений (Alex делегировал выбор)
Почему 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.
Почему не «вторая Балда»: дублирование оркестратора даёт два источника истины по состоянию сессии и конфликты локаторов. Документация к такому не призывает.
5. Что снимается этим планом
| Дефект | Как снимается |
|---|---|
🔴 Not logged in · Please run /login в контейнере-исполнителе (найден 2026-09-28) |
DeepSeek-провайдер рендерится из состояния — логина в подписочный CLI вообще нет |
| Ручной OAuth-логин Alex'а в контейнер | не нужен |
Потеря логина при docker compose up -d (.claude.json не в хост-монтировании) |
неактуально |
| Самодельная оркестрация в промптах | заменяется валидируемым графом callee |
| Нет таск-трекинга | prism + Beads |
6. Питфоллы
- 🔴
Human-узел против Mattermost. Документация callee/prism:storyтребует пауз на одобрение (phase:story:human), а в Балдеaskдля Mattermost фейлится закрыто. Пока не решено — воркфлоу может встать намертво на первой же точке одобрения. Варианты: (а) прокинуть человеческий ответ через мой мост как отдельный HTTP-вызов; (б) на первом этапе использовать воркфлоу безHuman; (в) чинитьaskв MM-адаптере (отдельный PR). Решать до C4. - 🔴
PROVIDERSв мосте — argv-список, замена «в лоб» рвёт передачу промпта. Сейчас:["docker","exec","-w","/agents","@container", *base, prompt]. Промпт должен остаться одним последним элементом. - 🔴
.envне перебивает plist.--hostжёстко вProgramArguments; смена аргументов требуетbootout+bootstrap. Проверка —lsof -nP -iTCP:8790 -sTCP:LISTEN(должно быть*:8790). - ⚠️
callee agent view prism/lifecycle --jsonдолжен вернутьRouter.Sequential= устаревший импорт, лечится--force. - ⚠️ Двойная сериализация
function.argumentsу DeepSeek — повторится в любом своём адаптере (C2-B). Готовая логика разбора есть вnorma-local. - ⚠️ Установки в контейнере не переживают пересборку образа — если callee/bd
окажутся нужны после
docker compose build, их надо либо заложить вDockerfile, либо вынести в том. - ⚠️
docker-compose(дефис) на Mac,docker compose(пробел) на Kraken — проверено, легко перепутать. - ⚠️ Provider
cmd— одно имя исполняемого файла, не shell-строка. Любые аргументы — только вextraArgs, иначе провайдер не стартует. - ⚠️ Никогда не маршрутизировать вывод модели в
cmd/extraArgs— прямой путь к исполнению произвольного кода (предупреждение документации).
7. Риски
| Риск | Оценка | Митигация |
|---|---|---|
| 🔴 ACP-адаптера к DeepSeek может не существовать | высокий | Фаза C0 решает это до основных работ. Если писать свой — 4-8 ч; опора на готовый патч norma-local |
🔴 Human-узел не доходит через Mattermost |
высокий | Решить до C4 (три варианта в §6.1) |
Импорт prism/* устаревает при обновлении |
средний | проверять kind: Router после каждого импорта |
| callee обновляется (0.22.2 → выше) и меняет контракт | средний | пиннить версию в ресурсах; следить за docs/ |
| Мост теряет работоспособность после смены argv | низкий | бэкап executor.py, откат одной правкой |
| Стоимость DeepSeek растёт (обе стороны на нём) | низкий | оркестратор уже на DeepSeek; исполнитель-роли — короткие визиты |
| Установка в контейнер не переживёт пересборку | низкий | решается томом или Dockerfile (§6.6) |
8. Порядок и что НЕ делать
Порядок: C0 → (решение) → C1 → C2 → C3 → C4 → C5. C0 и C1 независимы — можно параллелить.
НЕ трогать в ~/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.
НЕ начинать работу без явного «поехали» — правило проекта: план → апрув → работа. Ничего не удалять — образы для отката, диагностические контейнеры и старые теги не трогать без отдельного подтверждения.
9. Открытые вопросы
- 🔴 Есть ли готовый ACP-адаптер к DeepSeek — Фаза C0, блокирует C2.
- 🔴 Как проводить
Human-одобрение через Mattermost — §6.1, блокирует C4. - Нужен ли подписочный Codex в контуре вообще, или после Colex'а схема меняется целиком (Alex: «потом будем с колексом решать»).
Связанные заметки
- personal/plans/balda-mm-phase4-agents — Фаза 4 (предыдущая схема, мост :8790)
- personal/projects/balda/balda-mm-delegation-plan — общий план Фаз 1-5
- 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/tech/agents-bridge — мост :8790
- personal/tech/balda-executor-mcp — MCP-сервер делегирования
- personal/projects/balda/balda-mattermost-connector — MM-коннектор