Files
obsidian-vault/personal/plans/balda-callee-prism-plan.md
T

29 KiB
Executable File
Raw Blame History

Балда → 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 ч)

  1. Бэкап ~/Developer/agents-bridge/executor.py.
  2. Замена argv-таблицы PROVIDERS:
PROVIDERS = {
    # было: "claude": ["claude", "-p", "--output-format", "text"],
    # стало:
    "callee": ["callee", "agent", "run", "prism/lifecycle", "--message"],
}
  1. Промпт передаётся последним аргументом (как сейчас).
  2. 🔴 EXECUTOR_PROVIDER в ~/Docker/balda-executor/.env меняется на callee.
  3. Перезапуск моста: launchctl bootout + bootstrap (не kickstart — см. питфолл §6).
  4. Прогон задачи через мост (POST /task + Idempotency-Key), проверка exit_code.

Фаза C4 — таск-трекинг prism/Beads (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 на месте.

Фаза 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. Питфоллы

  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. Риски

Риск Оценка Митигация
🔴 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. Открытые вопросы

  1. 🔴 Есть ли готовый ACP-адаптер к DeepSeek — Фаза C0, блокирует C2.
  2. 🔴 Как проводить Human-одобрение через Mattermost — §6.1, блокирует C4.
  3. Нужен ли подписочный Codex в контуре вообще, или после Colex'а схема меняется целиком (Alex: «потом будем с колексом решать»).

Связанные заметки