Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
dda5d488a2
4 files changed
+544
-358
No files matched your search
@@ -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-коннектор
|
||||
@@ -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/*`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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` рвёт передачу.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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-сервер делегирования
|
||||
Reference in new issue
Block a user