Для контрибьютора
Как устроена Unica
Агент говорит с Unica по MCP. Unica отвечает предметными инструментами, а тяжёлую работу отдаёт демону: он живёт дольше вызова, делит рабочие пространства и запускает движки.
Кто приходит и по какому протоколу
О версии протокола MCP договариваются в initialize.
Unica принимает три версии протокола и отвечает той, которую попросил агент.
Длительные операции: два способа вернуться за результатом
Индекс BSL строится минутами, запуск 1С тоже. Инструмент отвечает раньше, чем закончит работу: отдаёт квитанцию и продолжает.
На уровне протокола
Агент спрашивает о задаче сам протокол — инструменты для этого не нужны. Так умеют клиенты, объявившие расширение задач (SEP-2663): в MCP оно появилось недавно.
Инструментами Unica
Клиент расширение не объявил — Unica добавляет в список три обычных инструмента unica.task.*. Они делают ровно то же: узнать состояние, забрать результат, отменить.
Выбирать не нужно: Unica смотрит, что объявил клиент, и сама решает, каким путём отвечать.
Что происходит внутри
Процесс MCP тонкий: он переводит кадры протокола в вызовы и обратно. Состояние, длительные операции и движки принадлежат демону, который переживает отдельный вызов.
| Движок | Проект | Лицензия | Как попадает в поставку |
|---|---|---|---|
| RLM | Dach-Coin/rlm-tools-bsl | MIT | Собираем в тулчейне Unica из закреплённого тега. |
| bsl-analyzer | itrous/bsl-analyzer | LGPL-3.0-or-later | Собираем в тулчейне Unica из закреплённого тега. |
| v8-runner | IngvarConsulting/v8-runner-rust — форк alkoleft/v8-runner-rust | AGPL-3.0-only | Собираем в форке. |
Каждая версия закреплена в tools.lock.json вместе с коммитом. AGPL у v8-runner
лицензией Unica не заменяется: он запускается отдельным процессом и остаётся на своих условиях.
Почему демон, а не процесс на вызов
Индекс BSL строится минутами, а вызов агента живёт секунды. Демон переживает вызов, поэтому второй запрос попадает в готовый индекс, а длительная операция отвечает состоянием вместо обрыва.
Почему актор на рабочее пространство
Две задачи в двух worktree не должны делить кеш и ревизию. Актор изолирует состояние, а квитанции остаются адресуемыми внутри своего пространства.
Сколько живёт демон
Без работы он завершается через пятнадцать минут, а пока задача идёт — держится. Серия вызовов подряд попадает в один тёплый процесс; забытая сессия не оставляет его висеть.
Общий ли демон у Claude Code и Codex
Нет. Каталог состояния демон берёт от агента: у Codex он свой, у Claude Code свой — и процесс у каждого получается свой. Внутри одного агента демон, наоборот, один на все worktree: их разделяют акторы.
Сеть и данные
Три вопроса службы безопасности перед тем, как пустить инструмент в контур.
Что уходит с машины
- Исходники остаются на диске. Unica читает, проверяет и пишет файлы локально и никуда их не отправляет.
- С моделью говорит агент, Codex или Claude Code. Что он отправляет, задаёт его политика, а не Unica.
- В сеть ходит только поиск по документации: стандарты разработки и база знаний 1С получают текст запроса. Оба поставщика выключаются в
unica.toml.
# unica.toml в корне проекта
[network]
default = "deny"
[providers.v8std]
network = "allow"
Что качается при установке
- Ядро и движки приходят из релизов на GitHub, движки при первом вызове, которому они нужны. Других адресов у загрузчика нет.
- Каждый архив и каждый файл сверяются по SHA-256. Неполная загрузка не считается готовой и повторяется.
- Кеш лежит в каталоге данных агента и переживает обновление плагина.
- Движок v8-runner умеет докачивать YAxUnit, Vanessa Automation и client MCP из их релизов на GitHub. Unica в версии 0.13 такую операцию не публикует, но агент может позвать v8-runner напрямую, в обход Unica: тогда загрузка идёт по правилам самого раннера.
- Ядро
IngvarConsulting/unica- Движки
unica-toolchainv8-runner-rust- Откуда
- github.com, страницы релизов
- Проверка
- SHA-256 на архив и каждый файл
- По запросу
- YAxUnit, Vanessa Automation, client MCP, через v8-runner
Без сети и за прокси
- Для образа без сети ядро и движки скачиваются заранее на машине с доступом, каталог кеша переносится вместе с образом.
- Прокси с подменой сертификатов загрузчик пока не проходит: он доверяет только вшитым корням. Это открытая задача #592, до её закрытия используйте предзагрузку.
unica-bootstrap prefetch --plugin-root <каталог плагина>
Набор исходников
Набор исходников — именованный каталог с выгрузкой конфигурации или расширения. Наборов в проекте может быть несколько: рядом с основной конфигурацией лежат расширения и другие её версии.
Список наборов Unica не придумывает и не хранит у себя: она читает
v8project.yaml, файл проекта v8-runner в корне, который уже описывает
проект для запуска 1С. Одно описание работает и на запуск, и на адресацию.
Отсюда связь между адресом и диском: main:Catalog.Валюты читается
в каталоге src, тот же справочник из другого набора — в каталоге этого
набора. Инструменты работают с именем набора и никогда не получают путь.
# v8project.yaml
format: DESIGNER
source-set:
- name: main
type: CONFIGURATION
path: src
- name: ext
type: EXTENSION
path: src-ext
| Поле | Что задаёт |
|---|---|
| format | Как выгружены исходники: DESIGNER или EDT. Можно не писать, тогда действует умолчание v8-runner. |
| basePath | От чего считаются пути наборов. По умолчанию каталог самого файла. |
| name | Имя набора, оно же стоит слева от двоеточия в адресе. По умолчанию main. |
| type | CONFIGURATION или EXTENSION. Синоним поля purpose. |
| path | Каталог, в котором лежит выгрузка этого набора. |
В версии 0.13 инструменты читают, проверяют и записывают только выгрузку конфигуратора. Набор в формате EDT распознаётся при подключении проекта, но не читается и не изменяется.
Семантическая адресация
Адрес называет объект так, как о нём думает разработчик: набор исходников, вид, имя. Путь к файлу в адрес не входит.
Виды и имена чередуются, и так до нужной глубины. Последний вид может остаться без имени — тогда адрес указывает на всю ветку. Русские названия видов приводятся к английским токенам, прикладные имена остаются как написаны.
| Адрес | Что это |
|---|---|
| main:Configuration | Корень конфигурации. Configuration стоит только здесь и больше нигде. |
| main:Catalog | Сам справочник. |
| main:Catalog | Все реквизиты справочника: вид без имени — это ветка. |
| main:Catalog | Один реквизит. |
| main:Catalog | Табличная часть. |
| main:Catalog | Реквизиты этой табличной части. |
| main:Report | Макет отчёта. |
| main:Report | Поле набора данных внутри схемы компоновки. Глубина не ограничена. |
| main:Document | Реквизит формы. |
| main:Catalog | Элемент формы. |
| main:CommonModule | Метод общего модуля. |
| epf:ExternalDataProcessor | Метод модуля объекта внешней обработки. Слева другой набор исходников. |
| newer:Catalog | Тот же объект в другом наборе исходников. |
Предметные инструменты
Эту публичную поверхность Unica публикует в MCP.
| Инструмент | Что делает | Почему так удобнее агенту |
|---|---|---|
| unica.view | Показывает, что есть в проекте, и открывает объект по адресу: реквизиты, формы, макеты, код. | Агент смотрит на объект, а не на файлы: не нужно знать, где что лежит на диске. |
| unica.resolve | Переводит адрес в файл и файл в адрес. Поиска по имени здесь нет, только конвертация в обе стороны. | Путь агент получает лишь тогда, когда без него не обойтись, а остальные инструменты работают с адресом. |
| unica.search | Ищет по коду: текст или объявления процедур и функций. Можно искать внутри одного объекта. | Ищет по коду, а не по строкам файла, и область поиска сужается до нужной ветки. |
| unica.diff | Показывает, чем отличаются два объекта одного вида. | Сравнение без выгрузок во временные файлы, и ничего при этом не меняется. |
| unica.check | Без адреса проверяет, что проект принят в работу. С адресом гоняет по объекту все проверки его вида: модуль, форму, роль, СКД. | Ошибки приходят списком, поэтому агент чинит их по одной и видит, что осталось. |
| unica.apply | Вносит правку: добавить реквизит, изменить свойство, дописать код. Сначала можно посмотреть план и ничего не записывать. | План до записи совпадает с самой записью, поэтому правку видно раньше, чем она попадёт в файлы. |
| unica.run | Без аргументов отдаёт словарь из двенадцати операций запуска: подготовить рабочее пространство, создать и собрать информационную базу, выгрузить и загрузить конфигурацию или расширение, снять и восстановить полный снимок базы, собрать поставку, запустить клиент. Каждая операция, которая меняет файлы или базу, проходит через план и запись, как apply. | Словарь приходит из кода вместе с отметкой, какие операции уже реализованы, поэтому агент не выдумывает команду, которой нет, и не зовёт ту, которой ещё нет. |
| unica.docs | Ищет сразу по четырём источникам: справка проекта в рабочем пространстве, синтакс-помощник установленной платформы и два сетевых — база знаний 1С и стандарты разработки. | Локальные источники отвечают без сети, сетевые включаются политикой проекта. Результат разложен по источникам, поэтому видно, откуда взят ответ. |
| unica.test | Планируется в версии 0.14, пока недоступен. | |
| unica.feature | Планируется в версии 0.14, пока недоступен. | |
| unica.log | Планируется в версии 0.14, пока недоступен. | |
Три инструмента совместимости
Эти три Unica публикует, когда клиент не объявил поддержку задач протокола.
| Инструмент | Что делает | Почему так удобнее агенту |
|---|---|---|
| unica.task.get | Показывает, что с задачей сейчас, и отвечает сразу. | Спросить «как там» можно в любой момент: работа не начнётся заново. |
| unica.task.result | Ждёт результат ограниченное время и возвращает его или новую квитанцию. | Ожидание всегда конечно, поэтому агент не зависает и не теряет задачу. |
| unica.task.cancel | Просит отменить задачу. | Повторная отмена безопасна — можно звать, не проверяя прошлый ответ. |
Механика dryRun
Инструмент, который меняет проект, вызывают дважды: сначала за планом, потом за записью.
Это один и тот же вызов с разным dryRun.
План — dryRun: true
Ничего не пишет. Возвращает, что именно изменится, и ревизию проекта rev — состояние, по которому план посчитан.
Запись — dryRun: false
Принимает ifRev из плана. Если проект успел измениться, ревизия не сойдётся и запись не состоится.
rev и ifRev вместе дают оптимистическую блокировку: между вызовами
проект никто не держит, а записать поверх чужой правки всё равно не выйдет. Отдельной
проверки перед записью не нужно — план и запись идут одним кодом, поэтому показанное
планом и запишется.
На чём проверено
Раздел заполняется после приёмки версии 0.13.