22 KiB
Executable File
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 Команды
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):
codex plugin marketplace add baldaworks/prism
codex plugin add prism@prism
# обновление: codex plugin marketplace upgrade prism → повторить add → новый thread
Callee-агенты (отдельный репозиторий, требует callee >= 0.19.0 + bd):
callee agent import baldaworks/prism-callee \
--path pack/callee/prism \
--prefix prism
# обновление/починка: тот же вызов + --force
Проверка:
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.»
3.5 Роли: что это и как ими управляют
Роль — YAML/MD-файл в каталоге, не право и не токен. Описание листа графа: провайдер, промпт, параметры, таймаут.
apiVersion: callee.metalagman.dev/v1alpha1
kind: Role
spec:
description: Собирает Xcode-проект
provider:
type: generic_acp
cmd: dsh-acp
model: deepseek-chat
timeout: 20m
---
Собери проект {{ .Params.project }} и верни отчёт об ошибках.
| Источник ролей | Что даёт |
|---|---|
| Каталог по умолчанию | 6 стартовых ресурсов (callee setup) |
| Импорт из git | callee agent import baldaworks/prism-callee --prefix prism |
| Собственные | файлы в каталоге проекта или ~/.callee/ |
🔴 Ролей в рантайме не выдают. Роль = наличие файла в каталоге. Рантайм-механизма «выдать роль на задачу» не существует.
Что динамическое, а что нет
| Сущность | Динамически? | Чем регулируется |
|---|---|---|
| Роль (файл графа) | ❌ статично | наличие файла в каталоге |
Провайдер (type, cmd) |
❌ или из State через DynamicRole |
автор графа |
| Файлы | ❌ | монтирование + рабочая копия |
| SSH-ключ | ❌ | наличие файла у процесса |
| Разрешение модели внутри ACP-сессии | ⚠️ да | allow_all / ask / deny_all |
🔴 Единственное, что реально запрашивается в рантайме — разрешение модели внутри
ACP-сессии (ask → вопрос в чат).
4. Права: формула и границы
права = права процесса × смонтированное × разрешённое в графе
| Уровень | Кто задаёт |
|---|---|
| Чем запущен процесс | вы, при старте (контейнер / хост-юзер / отдельный юзер) |
| Что смонтировано | compose |
| Что разрешено в графе | автор воркфлоу |
🔴 permissions.mode: allow_all — это про права модели ВНУТРИ ACP-сессии (править
файлы, запускать команды). Оно не расширяет, что процесс видит в файловой системе.
Два разных ограничения.
Где callee — там и движки, и Script'ы
🔴 ACP-провайдер — дочерний процесс callee. Движок на хосте + callee в контейнере технически невозможны. Следствие: «callee на хосте» = всё исполнение на хосте целиком.
Гейт Human защищает действие, но не возможность
Доступ у роли есть всегда — он определён тем, чем запущен callee и что смонтировано. Апрув не выдаёт доступ, он разрешает выполнить операцию, которая уже технически возможна.
Роль лежит в каталоге → доступ ЕСТЬ
▼
Узел Human («собрать Xcode в VM macos-27?») → пауза
├─ апрув → Script исполняется (права у процесса УЖЕ были)
└─ отказ → граф останавливается
🔴 Изоляцию даёт только то, чем процесс запущен и что смонтировано — не граф.
Динамических маунтов не существует
На macOS Docker маунты задаются при создании контейнера; live mount --bind
контейнер не видит; симлинк на внешний путь битый. «Как модель запросит доступ к папке»
не решается через маунты.
| Подход | Оценка |
|---|---|
A. Смонтировать всё заранее (~/Developer:ro) |
запись нельзя |
B. Пересоздание контейнера (up -d, ~3 с) |
нужен механизм вне контейнера |
| C. Хост-агент ставит права сам | полный доступ, ноль изоляции |
| D. Проект = рабочая копия ✅ | модель работает в ~/agents/<проект>, результат в проект через git |
Решение: D — доступ задаётся наличием репозитория, а не маунтом.
Доступ выбирается маршрутом, а не запросом
Модель не просит прав и не может эскалировать: нет доступа — задача падает.
Роутер в графе
├─ задача требует ~/agents → роль в контейнере
├─ задача требует Xcode/lume → роль на хост-исполнителе
└─ задача требует хоста по ssh → роль со ssh-ключом (command=)
Разграничение = статический роутинг по типам задач, не динамические права.
5. 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 — библиотека, а не запускаемый сервер.
6. Питфоллы
- 🔴
Humanтребует TTY — non-interactive режим отвергает граф целиком, включая невыбранные ветки Router. Запуск черезdocker execбез-t= non-interactive. - 🔴
cmd— одно имя файла. Аргументы только вextraArgs, иначе провайдер не стартует. - 🔴 Не подавать вывод модели в
cmd/extraArgs— исполнение произвольного кода. - 🔴 ACP-провайдер — дочерний процесс callee — движок и callee не могут быть на разных хостах.
- 🔴 Гейт
Humanне выдаёт доступ — только разрешает уже возможное действие. - ⚠️
prism/lifecycleобязан бытьRouter— иначе устаревший импорт. - ⚠️ Непустой stderr ≠ провал — судить по exit status.
- ⚠️ Таймаут Script валит весь воркфлоу — не «продолжает с ошибкой».
- ⚠️ Concurrency группы
Parallelиспользуют один живой shared state. - ⚠️ Поиск npm по
scope:normahq/scope:baldaworksдаёт пусто — эти scoped-пакеты через search-API не индексируются. Искать прямыми URLregistry.npmjs.org/<scope>%2f<name>. - ⚠️ Динамических маунтов на macOS Docker нет — только пересоздание контейнера.
Связанные заметки
- personal/plans/balda-callee-prism-plan — план внедрения (топология: стек на Mac)
- personal/projects/balda/balda-mm-delegation-plan — общий план Фаз 1-5
- personal/plans/balda-mm-phase4-agents — Фаза 4 (мост, приёмка)
- personal/tech/agents-bridge — мост :8790 (демонтируется в новой топологии)
- personal/tech/balda-executor-mcp — MCP-сервер делегирования (демонтируется)
- personal/tech/macos-docker-virtiofs-mount-limits — почему нет динамических маунтов