Files
obsidian-vault/personal/tech/callee-prism.md
T

23 KiB
Executable File
Raw Blame History

Callee + Prism — историческая справка по контрактам

[!WARNING] Версионная справка, 2026-09-29 Контракты ниже относятся к исследованным тогда версиям. Их нельзя считать описанием установленного Broker без сверки закреплённых зависимостей, workflow и worker package. Проверенные версии, текущие образы, реальные прогоны и оставшаяся работа: personal/projects/balda/execution-broker. Старый personal/plans/balda-callee-prism-plan сохранён как архив.

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

  1. 🔴 Human требует TTY — non-interactive режим отвергает граф целиком, включая невыбранные ветки Router. Запуск через docker exec без -t = non-interactive.
  2. 🔴 cmd — одно имя файла. Аргументы только в extraArgs, иначе провайдер не стартует.
  3. 🔴 Не подавать вывод модели в cmd/extraArgs — исполнение произвольного кода.
  4. 🔴 ACP-провайдер — дочерний процесс callee — движок и callee не могут быть на разных хостах.
  5. 🔴 Гейт Human не выдаёт доступ — только разрешает уже возможное действие.
  6. ⚠️ prism/lifecycle обязан быть Router — иначе устаревший импорт.
  7. ⚠️ Непустой stderr ≠ провал — судить по exit status.
  8. ⚠️ Таймаут Script валит весь воркфлоу — не «продолжает с ошибкой».
  9. ⚠️ Concurrency группы Parallel используют один живой shared state.
  10. ⚠️ Поиск npm по scope:normahq / scope:baldaworks даёт пусто — эти scoped-пакеты через search-API не индексируются. Искать прямыми URL registry.npmjs.org/<scope>%2f<name>.
  11. ⚠️ Динамических маунтов на macOS Docker нет — только пересоздание контейнера.

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