Разработка плагина
Третьесторонний плагин — это TypeScript плюс декларативный манифест
yttri-plugin.json. Код обращается к Yttri только через типизированный
YttriHost.
Быстрый старт
Заголовок раздела «Быстрый старт»Плагин — это папка с манифестом и одним ES-модулем. Минимальный рабочий вариант создаётся вручную:
mkdir -p my-plugin/dist && cd my-plugin{ "manifestVersion": "1", "id": "acme.hello", "name": "Hello", "version": "0.1.0", "sdkVersion": "^1.1.0", "yttriVersion": ">=0.80.0", "runtime": { "kind": "js_ts_v1", "entry": "dist/index.js" }, "permissions": [ { "domain": "notes", "access": "read", "reason": "Показать число заметок" } ], "capabilities": { "tools": [ { "name": "hello_count", "description": "Сколько заметок", "sideEffect": "read_only" } ] }}import { definePlugin } from '@yttri/plugin-api';
export default definePlugin({ async activate(host) { host.log.info('activated'); }, tools: { // Первый аргумент — host, второй — аргументы вызова. async hello_count(host) { const res = await host.invoke({ capability: 'notes', access: 'read', operation: 'notes.list', payload: { limit: 100 }, }); return { count: Array.isArray(res) ? res.length : 0 }; }, },});Импорт @yttri/plugin-api резолвится самим рантаймом: это синтетический
модуль внутри песочницы, устанавливать его не нужно. Пакет из npm понадобится
только ради типов TypeScript — когда он будет опубликован.
Установите папку через Настройки → Плагины → Папка разработчика. После правки файлов нажмите Обновить: файлы заменятся, настройки и состояние сохранятся.
Чтобы получить пакет для распространения, заархивируйте папку — формат
.yttri-plugin это обычный zip с манифестом в корне:
zip -r my-plugin-0.1.0.yttri-plugin . -x 'node_modules/*' -x '.git/*'TypeScript
Заголовок раздела «TypeScript»Писать плагин на TypeScript можно уже сейчас, но типы @yttri/plugin-api
придётся объявить самостоятельно или писать на чистом JavaScript: пакет с
типами не опубликован. Компилировать нужно в ES-модуль — рантайм исполняет
только JavaScript, TypeScript в пакете не собирается.
Точка входа
Заголовок раздела «Точка входа»import { definePlugin, type YttriHost } from '@yttri/plugin-api';
export default definePlugin({ async activate(host: YttriHost) { host.log.info('activated'); },
tools: { // Ключ совпадает с capabilities.tools[].name в манифесте. async acme_ping(host: YttriHost) { const token = await host.secrets.get('api_token'); const res = await host.network.fetch({ url: 'https://api.acme.com/ping', headers: token ? { authorization: `Bearer ${token}` } : {}, }); return { ok: res.ok, status: res.status }; }, },
jobs: { async sync(host: YttriHost) { // Фоновая задача из capabilities.jobs. }, },});YttriHost
Заголовок раздела «YttriHost»host.invoke({ capability, operation, payload })— вызовы в домены данных: notes, tasks, calendar, contacts, projects и search (чтение и создание), documents, mail и meetings (чтение). Write-операции требуют write-гранта.host.secrets.{get,set,delete,list}— секреты в изолированном пространстве плагина; значения шифруются.host.network.fetch(req)— HTTP только к хостам, объявленным в манифесте.host.settings.getAll()— настройки плагина; форму по схеме генерирует Yttri.host.accounts.*— реестр подключённых аккаунтов плагина: регистрация, список, статус, отметка синхронизации, удаление.host.log.{info,warn,error}— лог без чувствительных данных.
Ассистент (SDK 1.1)
Заголовок раздела «Ассистент (SDK 1.1)»Плагин может отдать ситуационному слою небольшой нормализованный факт и прочитать компактный статус связанных ситуаций:
import { publishObservation, lookupSituations } from '@yttri/plugin-api';
await publishObservation(host, { kind: 'source_updated', payload: { … } });const status = await lookupSituations(host, { … });Публикация требует гранта agent/write, чтение — agent/read. Плагин не
получает доступ к таблицам и не может создавать или менять ситуации, планы,
исходы и оценки — только внести факт и прочитать статус. Поля
observationKinds и outcomeCapabilities в манифесте требуют sdkVersion,
явно исключающего SDK 1.0 (например ^1.1.0).
Жизненный цикл
Заголовок раздела «Жизненный цикл»activate(host)вызывается один раз при запуске плагина. Здесь регистрируют состояние модуля и читают настройки; сеть и запись в домены на этом этапе недоступны.- Дальше плагин живёт в своём процессе: состояние модуля между вызовами сохраняется, повторный вызов инструмента не перезапускает плагин.
- Выключение останавливает процесс. Состояние в памяти теряется — всё, что должно пережить перезапуск, храните в данных Yttri или в секретах.
- Ошибка инструмента терминальна для этого вызова: Yttri не повторяет его автоматически.
Инструменты для агента
Заголовок раздела «Инструменты для агента»Инструменты включённого плагина попадают в реестр AI-агента как
plugin_<имя>. Первым аргументом функция получает host, вторым — аргументы
вызова, разобранные по схеме parameters из манифеста.
По умолчанию каждый вызов требует подтверждения пользователя. Доверенному плагину можно разрешить выполнение без подтверждений отдельным переключателем на его странице — права при этом не расширяются, гранты продолжают проверяться.
Пометьте инструмент честным sideEffect: read_only, mutating или
external. Он влияет на то, что видит агент и как вызов проходит проверки.
Фоновые задачи
Заголовок раздела «Фоновые задачи»capabilities.jobs объявляет задачи: ручные (кнопка на странице плагина) и по
cron-расписанию. Они идут через общую очередь Yttri с таймаутами и повторами и
не требуют открытого окна — плагин исполняется в фоновом процессе.
Задача не должна рассчитывать на состояние предыдущего запуска: между запусками процесс может быть остановлен.
Отладка
Заголовок раздела «Отладка»host.log.{info,warn,error}— логи плагина видны на его странице во вкладке «Логи»; чувствительные данные из них вычищаются.- Ошибки установки и включения показываются с причиной: не тот entry, несовместимая версия SDK, пакет не загружается.
- Правьте файлы и нажимайте Обновить в папке разработчика — переустановка не теряет настройки и выданные права.