🇬🇧 English version — каноническая версия, она может быть новее.
Плагины
Плагин - это то, как Ива растёт: папка, которую вы приносите откуда угодно, ставите одной командой и снимаете другой. Внутри скиллы, код и MCP-серверы, поэтому новая способность не стоит ни правки поставляемого дерева, ни форка (ADR-0008). Формат - Agent Plugins 1.0.0, та же папка, которую читают Codex, Cursor и Kiro. Своего формата Ива не придумывала.
Плагин ставит только владелец и только из терминала. Ни команды в Telegram, ни инструмента у модели нет намеренно: код плагина работает внутри процесса агента, а в окружении процесса лежат ключи этой инсталляции, поэтому подсунутое сообщение не должно уметь поставить плагин (ADR-0009). Что это значит для вас - в конце, в разделе Чем вы рискуете.
Поставить плагин
iva plugin add trace # имя из Marketplace
iva plugin add smixs/iva-plugins/trace # owner/repo[/подпапка][@ref]
iva plugin add smixs/iva-plugins/[email protected] # запинить тег, ветку или полный sha
iva plugin add https://github.com/you/my-plugin.git
iva plugin add ./my-plugin # папка на этой машине
add печатает состав папки до того, как что-то встанет на место:
trace 0.1.0 — skills: trace; code: sh.iva; mcp: files
Перед самой первой установкой один раз печатается принятый риск. Если плагин несёт stdio
MCP-серверы или свои сервисы, команда покажет их команды запуска и спросит
Start these processes on this machine? [y/N]. Флаг --trust отвечает «да» без вопроса, а
неинтерактивная оболочка без --trust отвечает «нет».
Что будет дальше, зависит от состава:
- 📋 Только скиллы - работают со следующего хода. Без сборки и рестарта.
- ⚙️ eve Extension в
sh.iva/или MCP-серверы, которым вы доверяете - их код и их connections живут внутри версии, поэтому Ива собирает новую с плагином внутри и перезапускает агента, по тем же рельсам, чтоiva update. Сборка не удалась - установка откатывается, работающая версия не тронута: поставить плагин не может сломать коробку (ADR-0003). - 🔌 Только сервисы - папка
sh.iva/сservices/и безpackage.jsonв версию не попадает вообще. Ничего не пересобирается и ничего не рестартует:trustпишет юниты и поднимает их, вся установка на этом кончается.
Команды
| Команда | Что делает |
|---|---|
iva plugin add <имя|источник> [--trust] | Поставить по имени из Marketplace или из папки, owner/repo[/подпапка][@ref], https://…, git@… |
iva plugin list | Что стоит: версия, sha, enabled/disabled, trusted/untrusted, состав, источник |
iva plugin list --available | Что предлагают ваши маркетплейсы и что из этого уже стоит |
iva plugin update [имя] [--force] | Подтянуть отслеживаемый ref, печатая old → new; без имени - все плагины |
iva plugin enable <имя> | Включить плагин обратно, скиллы вернутся со следующего хода |
iva plugin disable <имя> | Выключить, не удаляя |
iva plugin trust <имя> | Разрешить его MCP-серверам и сервисам работать процессами на этой машине |
iva plugin untrust <имя> | Погасить эти процессы и снять их юниты; скиллы продолжают работать |
iva plugin remove <имя> | Удалить плагин; его данные в data/plugin-data/<имя>/ остаются |
iva plugin sync | Починка: пересобрать plugins.json по папкам и доставить недостающее |
iva plugin marketplace add|remove|list | Ваши списки плагинов, которые ставятся по имени |
iva plugin без аргументов печатает тот же список. Все команды, меняющие состояние, берут тот
же лок, что iva update: работает одна за раз, вторая посреди обновления скажет об этом, а не
полезет в те же каталоги.
Где живёт плагин
Всё лежит в Custom layer или рядом с ним, поэтому iva update этого не касается:
| Путь | Что это |
|---|---|
data/custom/plugins/<имя>/ | сам плагин: git-checkout, запиненный на один sha, или копия вашей папки |
data/custom/plugins/<имя>.config.json | ваши настройки для кода плагина, читаются на старте и в сборку не зашиваются |
data/custom/plugins/<имя>.env | окружение MCP-серверов плагина, по одной строке KEY=value |
data/plugin-data/<имя>/ | данные самого плагина (PLUGIN_DATA), переживают его обновление и даже удаление |
data/custom/plugins.json | один файл состояния: name, source, ref, sha, digest, enabled, trusted, installedAt, плюс ваши маркетплейсы и выданные порты |
data/marketplace-cache/ | списки маркетплейсов в том виде, в каком их прочитали последний раз |
plugins.json - источник истины. Снесли папки, переехали на другой сервер, развернули старый
бэкап - iva plugin sync вернёт каждый плагин на записанный sha. Папку без записи он примет
обратно в файл, запись без папки доставит заново. Если восстановленный плагин несёт код, sync
об этом скажет: код попадёт в работающую версию следующим iva update.
Файлы конфига и env лежат рядом с папкой плагина, а не внутри: папку обновление перезаписывает, а ваши значения обязаны это пережить.
enabled и trusted
Два независимых тумблера, и list печатает оба:
- enabled - скиллы и код плагина загружены.
iva plugin disableвыключает их, оставляя всё на диске. - trusted - MCP-серверам и долгоживущим сервисам плагина разрешено работать процессами на вашей машине. Свежая установка недоверенная: текст в промпте и процесс на хосте - это не один тумблер.
Недоверенный плагин работает, просто тише: скиллы грузятся, stdio MCP-серверы и сервисы не
поднимаются. iva plugin trust <имя> их запускает, а untrust, disable и remove гасят и
снимают юниты. Доверие плагину с MCP-серверами ещё и пересобирает версию: его connections
генерируются именно туда. Плагину с одними сервисами хватает юнитов.
Имена скиллов общие на всю Иву, поэтому add отказывает двум плагинам с одноимённым скиллом:
один из них молча погас бы. Ваш собственный скилл в data/custom/agent/skills/ побеждает
одноимённый скилл плагина, а скилл плагина побеждает ядровой.
Что делает с плагинами iva update
Каждый включённый плагин с кодом собирается заново тем eve, который запустит новая версия.
Поэтому плагин не коммитит dist/ и не отстаёт от бампа eve: папка, конфиг, данные и флаг
доверия остаются как были, переделывается только сборка.
Плагин, чей код не собрался с новым eve, обновление не роняет. Версия собирается без него, плагин выключается, а один Alert называет виновника:
iva plugin update <имя>
iva plugin enable <имя>
iva plugin update идёт по ref, записанному при установке: ветка, тег или sha, - и печатает
old → new. Папку, которую вы правили руками, он не трогает (digest в plugins.json
показывает, что содержимое разошлось), пока не передадите --force. Так локальные правки не
затираются молча. Записи, которую Marketplace запинил на коммит, идти некуда: чтобы её
сдвинуть, сделайте remove и add заново.
Если обновление сменило содержимое плагина, его юниты - сервисы и MCP proxy - тут же
перезапускаются на новый код. Поэтому плагину с одними сервисами iva update для новой
версии его кода не нужен.
Marketplace
Marketplace - это список плагинов в git-репозитории: имя и источник, больше ничего. Ни центра,
ни модерации, ни чьего-либо разрешения на публикацию. Ива читает
.agents/plugins/marketplace.json по конвенции Codex, поэтому тот же список работает и для его
пользователей.
iva plugin list --available # что предлагают
iva plugin marketplace add you/your-plugins
iva plugin marketplace list
iva plugin marketplace remove your-plugins
Список по умолчанию - smixs/iva-plugins, и свой добавляется рядом, не вместо него. Списки
кэшируются в data/marketplace-cache/; без сети берётся кэш с пометкой, что он может быть
устаревшим. Имя, которое предлагают два списка, отклоняется с подсказкой add <имя>@<список>. А запись,
у которой манифест называет плагин иначе, не ставится вообще: список - это файл, который
никто не проверял.
Написать свой
Минимальный плагин - манифест и один скилл:
my-plugin/
plugin.json
skills/
weather/
SKILL.md
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "my-plugin",
"version": "0.1.0",
"description": "Что он делает, одной строкой."
}
Обязательных полей два: $schema и name. Имя - от 1 до 64 знаков, строчные буквы, цифры,
дефисы и точки, первый и последний знак буква или цифра, без -- и без ... Манифест
проверяется локально, схема из сети никогда не тянется.
Два ограничения стоит знать до упаковки: симлинки отвергаются целиком, а на плагин действуют
потолки 2000 файлов, 50 МБ и 16 уровней вложенности. Битая компонента не роняет остальные:
плагин с непригодным mcp.json всё равно отдаст свои скиллы, а iva plugin list напечатает
диагностику.
Пока пишете, ставьте саму папку: iva plugin add ./my-plugin. Она копируется в стор, а не
линкуется, и iva plugin update my-plugin копирует её снова из того же пути.
Скиллы
skills/<имя>/SKILL.md, ровно один уровень вложенности, формат
Agent Skills с name и description во frontmatter. Описание
пишется как условие срабатывания («Use when…»): до загрузки скилла модель видит только эту
строку. Всё остальное про скиллы - в extending.md: скилл плагина это обычный
скилл, который приехал в папке.
Код: eve Extension в sh.iva/
sh.iva - namespace Ивы, и внутри него живут две разные вещи: eve Extension, то есть
sh.iva/package.json, и сервисы, то есть sh.iva/services/<svc>/service.json. Любая из них
годится по отдельности. Namespace обязан быть объявлен в манифесте в обоих случаях: папку
sh.iva/, на которую манифест не заявился, Ива игнорирует и пишет об этом диагностику в
iva plugin list.
Для кода объявите ключ и положите в папку настоящий пакет eve Extension:
{ "extensions": { "sh.iva": {} } }
my-plugin/
sh.iva/
package.json # eve.extension.{source,dist}, пишется командой: eve extension init
tsconfig.json # обязателен: eve эмитит декларации через tsc и без него отказывается
extension/extension.ts
dist/ не коммитьте. Ива собирает его внутри версии: копия плагина, npm ci без dev- и
peer-зависимостей, eve extension build тем самым eve, который его запустит, сгенерированный
mount agent/extensions/<ns>.ts, дальше обычная сборка, проверка старта, переключение и
рестарт. <ns> - имя плагина, где . и - свёрнуты в _; оно же становится префиксом каждого
тула вашего расширения: my_plugin__forecast.
Настройки владельца приходят из data/custom/plugins/<имя>.config.json. Объявите их форму и
читайте значения через handle: сгенерированный mount передаёт ему содержимое файла на старте,
поэтому изменённая настройка стоит рестарта, а не пересборки.
// sh.iva/extension/extension.ts
import { defineExtension } from "eve/extension";
import { z } from "zod";
export default defineExtension({ config: z.object({ city: z.string() }) });
Расширение не может объявить agent.ts, sandbox, schedules и вложенные extensions: это
принадлежит агенту, который его монтирует. Своих расписаний у плагина нет по той же причине -
eve их запрещает.
MCP-серверы: mcp.json
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"files": {
"type": "stdio",
"command": "node",
"args": ["server.mjs", "--root", "${PLUGIN_DATA}"],
"env": { "FILES_LOG": "${PLUGIN_DATA}/log" }
},
"search": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer ${SEARCH_TOKEN}" }
}
}
}
Работают оба транспорта, и обоим нужен trusted:
streamable-httpиsseпревращаются в сгенерированный connection с именемmcp-<имя>--<server>: файлagent/connections/mcp-<имя>--<server>.tsвнутри версии. В имени connection eve принимает только строчные буквы, цифры и дефисы, поэтому обе половины сворачиваются в этот алфавит: серверTrace.Viewerстановитсяtrace-viewer. Тулы модель находит черезconnection_search, как у любого другого подключения, и зовёт ихconnection__mcp-trace--viewer__<tool>.${VAR}в заголовке подставляется в рантайме изdata/custom/plugins/<имя>.env, поэтому токен в сборку не попадает. Адрес обязан быть абсолютнымhttps://(простойhttp://только на loopback), без user info и без фрагмента.stdioработает systemd-юнитомiva-mcp-<имя>-<server>.service, а перед ним стоит MCP proxy: сервис Ивы держит ваш сервер на его stdio и отдаёт агенту по streamable-http на127.0.0.1:<порт>/mcpза bearer. Токен лежит вdata/plugin-data/<имя>/mcp-<server>.tokenс режимом0600;GET /healthотвечает без него.Restart=on-failure, поэтому упавший сервер поднимается снова.
command - один токен: голое имя исполняемого файла или ./-путь внутри плагина, но никогда
шелл-строка. ${PLUGIN_ROOT} и ${PLUGIN_DATA} раскрываются один проход в args, env и
cwd; рабочий каталог по умолчанию - PLUGIN_ROOT.
Что stdio-сервер видит в своём окружении - ради этого прокси и стоит: PATH, HOME,
PLUGIN_ROOT, PLUGIN_DATA, объявленное в mcp.json и то, что вы написали в <имя>.env.
Больше из окружения агента ему не достаётся ничего: ни токена Telegram, ни ключа провайдера.
Порты выдаются при trust, от 8730 вверх, записываются в plugins.json и держатся до
удаления плагина.
Долгоживущий сервис
Плагин, которому нужен свой процесс - вьюер, воркер, что угодно, что должно пережить рестарт
агента, - объявляет его в sh.iva/ рядом с тем же ключом extensions и без всякого
package.json:
my-plugin/
sh.iva/
services/
viewer/
service.json
server.mjs
{ "command": "node", "args": ["server.mjs"], "port": 8726 }
Ива пишет юнит iva-plugin-<имя>-<svc>.service и поднимает его только пока плагин включён и
доверен, оба условия сразу, с Restart=on-failure. Рабочий каталог - папка самого сервиса, а в окружении
ровно четыре переменные:
| Переменная | Что в ней |
|---|---|
IVA_SERVICE_PORT | порт, который надо слушать; слушать только на 127.0.0.1 |
IVA_DATA_DIR | абсолютный путь каталога данных Ивы, чтобы читать оттуда, например, trace/ |
PLUGIN_ROOT | папка плагина в сторе |
PLUGIN_DATA | data/plugin-data/<имя>/, ваш каталог для записи |
Плагины, которые Ива привозит из smixs/iva-plugins, написаны на TypeScript и запускаются
прямо через Node - "command": "node", "args": ["server.ts"], без всякой сборки, - а ваш
собственный сервис может быть любой командой: контракт - это только service.json и те же
четыре переменные.
port в service.json - это пожелание: Ива берёт его, если свободен, иначе выдаёт следующий
свободный. Поэтому номер приезжает через IVA_SERVICE_PORT, а не константой в вашем коде.
Плагину с сервисами и без расширения сборка версии не нужна, ему нужны только юниты, а
iva plugin update перезапускает их на код, который только что привёз.
Юниты работают на Linux. На машине без systemd команды печатают no systemd и идут дальше:
состояние всё равно записано, поэтому та же инсталляция на сервере поднимет процессы.
Проверить
iva plugin list # по каждому плагину: тумблеры, состав, источник, диагностика
iva doctor # раздел Plugins
iva plugin sync # то, что doctor велит запустить, когда они расходятся
iva doctor читает манифесты, сверяет plugins.json с data/custom/plugins/, говорит, правда
ли код каждого плагина собран в работающую версию, и проверяет ожидаемые юниты: is-active для
каждого плюс GET /health для каждого MCP proxy.
Чем вы рискуете
Прочитайте один раз, дальше решайте по каждому плагину отдельно.
Код плагина работает внутри процесса агента, а bash внутри его скилла - с окружением агента.
Значит оба видят все ключи этой инсталляции: токен Telegram, провайдера модели, всё остальное
из .env. Поставить чужой плагин - отдать ему эти ключи. Проверка манифеста от этого не
спасает: plugin.json описывает состав, а не поведение.
Это принятый риск, а не недосмотр (ADR-0008): изоляция секретов от шелла - отдельная отложенная работа, и плагины её не приближают. Что рельсы делают вместо этого - оставляют решение вам и показывают его прямо:
- ставит только владелец и только из терминала, никогда модель и никогда из Telegram;
trusted- второе, отдельное «да» перед тем, как процесс плагина запустится на машине;- MCP-сервер получает меньше агента: своё окружение,
PLUGIN_ROOTиPLUGIN_DATA, и ничего сверх; - плагин, который не собрался, до работающей версии не доходит.
Свой плагин вы видите насквозь, чужой - нет. Читайте код до установки или пиньте плагин на тот sha, который прочитали.
Остальная картина по безопасности - в security.md.
Исходный Markdown ↗