diff --git a/personal/plans/balda-callee-prism-plan.md b/personal/plans/balda-callee-prism-plan.md index 8aa1e323..a9d46788 100755 --- a/personal/plans/balda-callee-prism-plan.md +++ b/personal/plans/balda-callee-prism-plan.md @@ -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:` когда провайдеров несколько | -| 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 --message ` + │ запускает `callee agent run --message ` ▼ 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/%2f`. +Установка (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:` при нескольких провайдерах | +| 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/%2f` и +`/-/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-коннектор diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index 78cb7bd7..7c8e69c9 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -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/*`. --- diff --git a/personal/tech/agents-bridge.md b/personal/tech/agents-bridge.md index e645e372..084205f9 100755 --- a/personal/tech/agents-bridge.md +++ b/personal/tech/agents-bridge.md @@ -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 ` | -| `codex` | `codex exec --skip-git-repo-check ` | +| Провайдер | argv | Статус | +|---|---|---| +| `claude` | `claude -p --output-format text ` | ⛔ **отменяется** — требует ручного логина в контейнере | +| `codex` | `codex exec --skip-git-repo-check ` | ⛔ по той же причине | +| `callee` | `callee agent run prism/lifecycle --message ` | 🔜 **целевой** (план [[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` рвёт передачу. --- diff --git a/personal/tech/callee-prism.md b/personal/tech/callee-prism.md new file mode 100644 index 00000000..0b0d7e4b --- /dev/null +++ b/personal/tech/callee-prism.md @@ -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 +callee agent list +callee agent view [--json] +callee agent run --message "..." [--repl-timeout D] +callee agent import --path --prefix [--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:` при нескольких провайдерах | +| 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/%2f`. + +--- + +## Связанные заметки + +- [[personal/plans/balda-callee-prism-plan]] — план внедрения +- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (мост, приёмка) +- [[personal/tech/agents-bridge]] — мост :8790 +- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования