From 5b55128fffd9c1fd69f97ef474bffc42e22c01f9 Mon Sep 17 00:00:00 2001 From: "openclaw-docs-i18n[bot]" Date: Thu, 30 Apr 2026 00:45:15 +0000 Subject: [PATCH] chore(i18n): refresh uk translations --- docs/uk/channels/msteams.md | 432 ++++++++++++------------ docs/uk/plugins/sdk-channel-plugins.md | 442 +++++++++++++------------ docs/uk/plugins/sdk-channel-turn.md | 400 ++++++++++++++++++++++ 3 files changed, 842 insertions(+), 432 deletions(-) create mode 100644 docs/uk/plugins/sdk-channel-turn.md diff --git a/docs/uk/channels/msteams.md b/docs/uk/channels/msteams.md index 88762e6af..13ddbaaf9 100644 --- a/docs/uk/channels/msteams.md +++ b/docs/uk/channels/msteams.md @@ -1,34 +1,30 @@ --- read_when: - Робота над функціями каналу Microsoft Teams -summary: Статус підтримки, можливості та конфігурація бота Microsoft Teams +summary: Стан підтримки, можливості та налаштування бота Microsoft Teams title: Microsoft Teams x-i18n: - generated_at: "2026-04-29T07:51:49Z" + generated_at: "2026-04-30T00:42:15Z" model: gpt-5.5 provider: openai - source_hash: 535bd7f9713f221572a99ae3a7a39d7acdd5a1e41c2d79a43d4caf9c2ce2b159 + source_hash: c2c8cd13a72941a18d609b1f7263d9b9ed3284873f9b1483975ca1356b543979 source_path: channels/msteams.md workflow: 16 --- -Статус: підтримуються текст + вкладення в DM; надсилання файлів у канали/групи потребує `sharePointSiteId` + дозволів Graph (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)). Опитування надсилаються через Adaptive Cards. Дії повідомлень надають явний `upload-file` для надсилання, де файл є основним. +Стан: підтримуються текст і вкладення в DM; надсилання файлів у канал/групу потребує `sharePointSiteId` + дозволів Graph (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)). Опитування надсилаються через Adaptive Cards. Дії з повідомленнями надають явний `upload-file` для надсилань, де файл є першим. ## Вбудований Plugin -Microsoft Teams постачається як вбудований Plugin у поточних випусках OpenClaw, тому -окреме встановлення у звичайній пакетній збірці не потрібне. +Microsoft Teams постачається як вбудований Plugin у поточних випусках OpenClaw, тому в звичайній пакетованій збірці окреме встановлення не потрібне. -Якщо ви використовуєте старішу збірку або власне встановлення, що виключає вбудований Teams, -встановіть поточний npm-пакет, коли його буде опубліковано: +Якщо ви використовуєте старішу збірку або власне встановлення, яке виключає вбудований Teams, встановіть поточний npm-пакет, коли його буде опубліковано: ```bash openclaw plugins install @openclaw/msteams ``` -Якщо npm повідомляє, що пакет, яким володіє OpenClaw, застарілий, використовуйте поточну пакетну -збірку OpenClaw або локальний шлях checkout, доки не буде -опубліковано новіший npm-пакет. +Якщо npm повідомляє, що пакет, який належить OpenClaw, застарілий, використовуйте поточну пакетовану збірку OpenClaw або шлях до локального checkout, доки не буде опубліковано новіший npm-пакет. Локальний checkout (під час запуску з git-репозиторію): @@ -36,7 +32,7 @@ openclaw plugins install @openclaw/msteams openclaw plugins install ./path/to/local/msteams-plugin ``` -Докладніше: [Plugins](/uk/tools/plugin) +Докладно: [Plugins](/uk/tools/plugin) ## Швидке налаштування @@ -51,12 +47,12 @@ teams status # verify you're logged in and see your tenant info ``` -Teams CLI зараз перебуває в preview. Команди та прапорці можуть змінюватися між випусками. +Teams CLI наразі перебуває в preview. Команди й прапорці можуть змінюватися між випусками. -**2. Запустіть тунель** (Teams не може дістатися до localhost) +**2. Запустіть тунель** (Teams не може дістатися localhost) -Встановіть і автентифікуйте devtunnel CLI, якщо ще цього не зробили ([посібник із початку роботи](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started)). +Встановіть і автентифікуйте devtunnel CLI, якщо ви ще цього не зробили ([посібник із початку роботи](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started)). ```bash # One-time setup (persistent URL across sessions): @@ -69,10 +65,10 @@ devtunnel host my-openclaw-bot ``` -`--allow-anonymous` потрібен, бо Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK. +`--allow-anonymous` потрібен, оскільки Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK. -Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (але вони можуть змінювати URL у кожній сесії). +Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (але вони можуть змінювати URL кожної сесії). **3. Створіть застосунок** @@ -85,9 +81,9 @@ teams app create \ Ця одна команда: - Створює застосунок Entra ID (Azure AD) -- Генерує клієнтський секрет -- Збирає та завантажує маніфест застосунку Teams (з іконками) -- Реєструє бота (типово керований Teams — передплата Azure не потрібна) +- Генерує client secret +- Збирає й завантажує маніфест застосунку Teams (з іконками) +- Реєструє бота (типово керується Teams — підписка Azure не потрібна) Вивід покаже `CLIENT_ID`, `CLIENT_SECRET`, `TENANT_ID` і **Teams App ID** — занотуйте їх для наступних кроків. Він також запропонує встановити застосунок безпосередньо в Teams. @@ -111,7 +107,7 @@ teams app create \ **5. Встановіть застосунок у Teams** -`teams app create` запропонує встановити застосунок — виберіть "Встановити в Teams". Якщо ви пропустили це, посилання можна отримати пізніше: +`teams app create` запропонує встановити застосунок — виберіть "Install in Teams". Якщо ви пропустили це, можете отримати посилання пізніше: ```bash teams app get --install-link @@ -123,23 +119,23 @@ teams app get --install-link teams app doctor ``` -Це запускає діагностику реєстрації бота, конфігурації застосунку AAD, чинності маніфесту та налаштування SSO. +Це запускає діагностику реєстрації бота, конфігурації застосунку AAD, чинності маніфесту й налаштування SSO. -Для production-розгортань розгляньте використання [федеративної автентифікації](/uk/channels/msteams#federated-authentication-certificate-plus-managed-identity) (сертифікат або керована ідентичність) замість клієнтських секретів. +Для production-розгортань розгляньте використання [федеративної автентифікації](/uk/channels/msteams#federated-authentication-certificate-plus-managed-identity) (сертифікат або managed identity) замість client secrets. -Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якому учаснику (з вимогою згадки). +Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якого учасника (з обмеженням за згадкою). ## Цілі - Спілкуватися з OpenClaw через DM, групові чати або канали Teams. -- Зберігати детерміновану маршрутизацію: відповіді завжди повертаються до каналу, з якого вони надійшли. +- Зберігати детермінізм маршрутизації: відповіді завжди повертаються в канал, з якого надійшли. - Типово використовувати безпечну поведінку каналів (згадки обов’язкові, якщо не налаштовано інакше). ## Записи конфігурації -Типово Microsoft Teams дозволено записувати оновлення конфігурації, спричинені `/config set|unset` (потребує `commands.config: true`). +Типово Microsoft Teams дозволено записувати оновлення конфігурації, ініційовані `/config set|unset` (потребує `commands.config: true`). Вимкніть за допомогою: @@ -153,16 +149,16 @@ teams app doctor **Доступ до DM** -- Типово: `channels.msteams.dmPolicy = "pairing"`. Невідомі відправники ігноруються, доки їх не схвалять. -- `channels.msteams.allowFrom` має використовувати стабільні object ID AAD. -- Не покладайтеся на зіставлення UPN/відображуваного імені для allowlist — вони можуть змінюватися. OpenClaw типово вимикає пряме зіставлення імен; увімкніть його явно через `channels.msteams.dangerouslyAllowNameMatching: true`. -- Майстер може зіставляти імена з ID через Microsoft Graph, коли облікові дані це дозволяють. +- Типово: `channels.msteams.dmPolicy = "pairing"`. Невідомі відправники ігноруються, доки їх не схвалено. +- `channels.msteams.allowFrom` має використовувати стабільні object IDs AAD. +- Не покладайтеся на зіставлення UPN/display-name для allowlists — вони можуть змінюватися. OpenClaw типово вимикає пряме зіставлення імен; увімкніть його явно через `channels.msteams.dangerouslyAllowNameMatching: true`. +- Майстер може зіставляти імена з IDs через Microsoft Graph, коли облікові дані це дозволяють. -**Доступ до груп** +**Доступ груп** -- Типово: `channels.msteams.groupPolicy = "allowlist"` (заблоковано, якщо ви не додасте `groupAllowFrom`). Використайте `channels.defaults.groupPolicy`, щоб перевизначити типове значення, коли воно не задане. -- `channels.msteams.groupAllowFrom` контролює, які відправники можуть запускати обробку в групових чатах/каналах (із fallback до `channels.msteams.allowFrom`). -- Задайте `groupPolicy: "open"`, щоб дозволити будь-якому учаснику (типово все одно з вимогою згадки). +- Типово: `channels.msteams.groupPolicy = "allowlist"` (заблоковано, якщо ви не додасте `groupAllowFrom`). Використовуйте `channels.defaults.groupPolicy`, щоб перевизначити типове значення, коли воно не задане. +- `channels.msteams.groupAllowFrom` керує тим, які відправники можуть запускати дії в групових чатах/каналах (із fallback до `channels.msteams.allowFrom`). +- Задайте `groupPolicy: "open"`, щоб дозволити будь-якого учасника (типово все одно з обмеженням за згадкою). - Щоб не дозволяти **жодних каналів**, задайте `channels.msteams.groupPolicy: "disabled"`. Приклад: @@ -180,12 +176,12 @@ teams app doctor **Teams + allowlist каналів** -- Обмежуйте відповіді в групах/каналах, перелічуючи команди та канали в `channels.msteams.teams`. -- Ключі мають використовувати стабільні ID команд і conversation ID каналів. -- Коли `groupPolicy="allowlist"` і наявний allowlist Teams, приймаються лише перелічені команди/канали (з вимогою згадки). +- Обмежуйте відповіді в групах/каналах, перелічуючи команди й канали в `channels.msteams.teams`. +- Ключі мають використовувати стабільні Teams conversation IDs із посилань Teams, а не змінні відображувані назви. +- Коли `groupPolicy="allowlist"` і наявний allowlist команд, приймаються лише перелічені команди/канали (з обмеженням за згадкою). - Майстер налаштування приймає записи `Team/Channel` і зберігає їх для вас. -- Під час запуску OpenClaw зіставляє імена команд/каналів та імена allowlist користувачів з ID (коли дозволи Graph це дозволяють) - і записує зіставлення в журнал; нерозв’язані імена команд/каналів зберігаються як введені, але типово ігноруються для маршрутизації, якщо не ввімкнено `channels.msteams.dangerouslyAllowNameMatching: true`. +- Під час запуску OpenClaw зіставляє назви team/channel і user allowlist з IDs (коли дозволи Graph це дозволяють) + і логує мапінг; нерозв’язані назви team/channel зберігаються так, як введені, але типово ігноруються для маршрутизації, якщо не ввімкнено `channels.msteams.dangerouslyAllowNameMatching: true`. Приклад: @@ -209,7 +205,7 @@ teams app doctor
Ручне налаштування (без Teams CLI) -Якщо ви не можете використовувати Teams CLI, ви можете налаштувати бота вручну через Azure Portal. +Якщо ви не можете використовувати Teams CLI, можете налаштувати бота вручну через Azure Portal. ### Як це працює @@ -217,25 +213,25 @@ teams app doctor 2. Створіть **Azure Bot** (App ID + secret + tenant ID). 3. Зберіть **пакет застосунку Teams**, який посилається на бота й містить дозволи RSC нижче. 4. Завантажте/встановіть застосунок Teams у команду (або personal scope для DM). -5. Налаштуйте `msteams` у `~/.openclaw/openclaw.json` (або змінних середовища) і запустіть Gateway. -6. Gateway типово слухає трафік Webhook Bot Framework на `/api/messages`. +5. Налаштуйте `msteams` у `~/.openclaw/openclaw.json` (або env vars) і запустіть Gateway. +6. Gateway типово слухає трафік Bot Framework Webhook на `/api/messages`. ### Крок 1: Створіть Azure Bot -1. Перейдіть до [Створити Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot) -2. Заповніть вкладку **Основи**: +1. Перейдіть до [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot) +2. Заповніть вкладку **Basics**: | Поле | Значення | | ------------------ | -------------------------------------------------------- | - | **Дескриптор бота** | Ім’я вашого бота, напр., `openclaw-msteams` (має бути унікальним) | - | **Передплата** | Виберіть вашу передплату Azure | - | **Група ресурсів** | Створіть нову або використайте наявну | - | **Ціновий рівень** | **Free** для розробки/тестування | - | **Тип застосунку** | **Single Tenant** (рекомендовано - див. примітку нижче) | - | **Тип створення** | **Create new Microsoft App ID** | + | **Bot handle** | Назва вашого бота, напр., `openclaw-msteams` (має бути унікальною) | + | **Subscription** | Виберіть вашу підписку Azure | + | **Resource group** | Створіть нову або використайте наявну | + | **Pricing tier** | **Free** для розробки/тестування | + | **Type of App** | **Single Tenant** (рекомендовано - див. примітку нижче) | + | **Creation type** | **Create new Microsoft App ID** | -Створення нових мультитенантних ботів застаріло після 2025-07-31. Використовуйте **Single Tenant** для нових ботів. +Створення нових multi-tenant ботів було застарілим після 2025-07-31. Використовуйте **Single Tenant** для нових ботів. 3. Натисніть **Review + create** → **Create** (зачекайте ~1-2 хвилини) @@ -253,7 +249,7 @@ teams app doctor 1. В Azure Bot → **Configuration** 2. Задайте **Messaging endpoint** як URL вашого Webhook: - Production: `https://your-domain.com/api/messages` - - Локальна розробка: використайте тунель (див. [Локальна розробка](#local-development-tunneling) нижче) + - Локальна розробка: використовуйте тунель (див. [Локальна розробка](#local-development-tunneling) нижче) ### Крок 4: Увімкніть канал Teams @@ -263,12 +259,12 @@ teams app doctor ### Крок 5: Зберіть маніфест застосунку Teams -- Додайте запис `bot` з `botId = `. +- Додайте запис `bot` із `botId = `. - Scopes: `personal`, `team`, `groupChat`. - `supportsFiles: true` (потрібно для обробки файлів у personal scope). - Додайте дозволи RSC (див. [Дозволи RSC](#current-teams-rsc-permissions-manifest)). - Створіть іконки: `outline.png` (32x32) і `color.png` (192x192). -- Заархівуйте всі три файли разом: `manifest.json`, `outline.png`, `color.png`. +- Запакуйте всі три файли разом у zip: `manifest.json`, `outline.png`, `color.png`. ### Крок 6: Налаштуйте OpenClaw @@ -294,19 +290,19 @@ teams app doctor
-## Федеративна автентифікація (сертифікат плюс керована ідентичність) +## Федеративна автентифікація (сертифікат плюс managed identity) > Додано у 2026.4.11 -Для production-розгортань OpenClaw підтримує **федеративну автентифікацію** як безпечнішу альтернативу клієнтським секретам. Доступні два методи: +Для production-розгортань OpenClaw підтримує **федеративну автентифікацію** як безпечнішу альтернативу client secrets. Доступні два методи: ### Варіант A: Автентифікація на основі сертифіката -Використайте PEM-сертифікат, зареєстрований у вашій реєстрації застосунку Entra ID. +Використовуйте PEM-сертифікат, зареєстрований у вашій реєстрації застосунку Entra ID. **Налаштування:** -1. Згенеруйте або отримайте сертифікат (PEM-формат із приватним ключем). +1. Згенеруйте або отримайте сертифікат (формат PEM із приватним ключем). 2. В Entra ID → App Registration → **Certificates & secrets** → **Certificates** → завантажте публічний сертифікат. **Конфігурація:** @@ -326,26 +322,26 @@ teams app doctor } ``` -**Змінні середовища:** +**Env vars:** - `MSTEAMS_AUTH_TYPE=federated` - `MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem` ### Варіант B: Azure Managed Identity -Використовуйте Azure Managed Identity для автентифікації без пароля. Це ідеально для розгортань в інфраструктурі Azure (AKS, App Service, Azure VMs), де доступна керована ідентичність. +Використовуйте Azure Managed Identity для автентифікації без пароля. Це ідеально для розгортань в інфраструктурі Azure (AKS, App Service, Azure VMs), де доступна managed identity. **Як це працює:** -1. Pod/VM бота має керовану ідентичність (system-assigned або user-assigned). -2. **Федеративні облікові дані ідентичності** зв’язують керовану ідентичність із реєстрацією застосунку Entra ID. -3. Під час виконання OpenClaw використовує `@azure/identity`, щоб отримувати токени з endpoint Azure IMDS (`169.254.169.254`). -4. Токен передається до Teams SDK для автентифікації бота. +1. Pod/VM бота має managed identity (system-assigned або user-assigned). +2. **Federated identity credential** пов’язує managed identity із реєстрацією застосунку Entra ID. +3. Під час виконання OpenClaw використовує `@azure/identity`, щоб отримувати токени з Azure IMDS endpoint (`169.254.169.254`). +4. Токен передається в Teams SDK для автентифікації бота. **Передумови:** -- Інфраструктура Azure з увімкненою керованою ідентичністю (AKS workload identity, App Service, VM) -- Федеративні облікові дані ідентичності, створені в реєстрації застосунку Entra ID +- Інфраструктура Azure з увімкненою managed identity (AKS workload identity, App Service, VM) +- Federated identity credential, створений у реєстрації застосунку Entra ID - Мережевий доступ до IMDS (`169.254.169.254:80`) з pod/VM **Конфігурація (system-assigned managed identity):** @@ -365,7 +361,7 @@ teams app doctor } ``` -**Конфігурація (керована ідентичність, призначена користувачем):** +**Конфігурація (призначена користувачем керована ідентичність):** ```json5 { @@ -389,12 +385,12 @@ teams app doctor - `MSTEAMS_USE_MANAGED_IDENTITY=true` - `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=` (лише для призначеної користувачем) -### Налаштування AKS Workload Identity +### Налаштування ідентичності робочого навантаження AKS Для розгортань AKS, що використовують ідентичність робочого навантаження: 1. **Увімкніть ідентичність робочого навантаження** у вашому кластері AKS. -2. **Створіть облікові дані федеративної ідентичності** для реєстрації застосунку Entra ID: +2. **Створіть облікові дані федеративної ідентичності** у реєстрації застосунку Entra ID: ```bash az ad app federated-credential create --id --parameters '{ @@ -405,7 +401,7 @@ teams app doctor }' ``` -3. **Додайте анотацію до сервісного облікового запису Kubernetes** з клієнтським ID застосунку: +3. **Додайте анотацію до сервісного облікового запису Kubernetes** з ID клієнта застосунку: ```yaml apiVersion: v1 @@ -416,7 +412,7 @@ teams app doctor azure.workload.identity/client-id: "" ``` -4. **Додайте мітку до pod** для ін’єкції ідентичності робочого навантаження: +4. **Додайте мітку до пода** для інʼєкції ідентичності робочого навантаження: ```yaml metadata: @@ -424,17 +420,17 @@ teams app doctor azure.workload.identity/use: "true" ``` -5. **Забезпечте мережевий доступ** до IMDS (`169.254.169.254`) — якщо використовуєте NetworkPolicy, додайте правило вихідного трафіку, що дозволяє трафік до `169.254.169.254/32` на порту 80. +5. **Забезпечте мережевий доступ** до IMDS (`169.254.169.254`) — якщо використовується NetworkPolicy, додайте правило вихідного трафіку, яке дозволяє трафік до `169.254.169.254/32` на порті 80. ### Порівняння типів автентифікації -| Метод | Конфігурація | Переваги | Недоліки | -| ---------------------- | --------------------------------------------- | ------------------------------------- | ----------------------------------------------- | -| **Клієнтський секрет** | `appPassword` | Просте налаштування | Потрібна ротація секрету, менш безпечно | -| **Сертифікат** | `authType: "federated"` + `certificatePath` | Немає спільного секрету через мережу | Додаткове керування сертифікатами | -| **Керована ідентичність** | `authType: "federated"` + `useManagedIdentity` | Без паролів, не потрібно керувати секретами | Потрібна інфраструктура Azure | +| Метод | Конфігурація | Переваги | Недоліки | +| ------------------------ | --------------------------------------------- | ------------------------------------- | --------------------------------------------- | +| **Секрет клієнта** | `appPassword` | Просте налаштування | Потрібна ротація секрету, менш безпечно | +| **Сертифікат** | `authType: "federated"` + `certificatePath` | Немає спільного секрету через мережу | Додаткове керування сертифікатами | +| **Керована ідентичність** | `authType: "federated"` + `useManagedIdentity` | Без пароля, не потрібно керувати секретами | Потрібна інфраструктура Azure | -**Поведінка за замовчуванням:** Якщо `authType` не задано, OpenClaw за замовчуванням використовує автентифікацію через клієнтський секрет. Наявні конфігурації продовжують працювати без змін. +**Поведінка за замовчуванням:** Коли `authType` не задано, OpenClaw за замовчуванням використовує автентифікацію через секрет клієнта. Наявні конфігурації продовжують працювати без змін. ## Локальна розробка (тунелювання) @@ -451,7 +447,7 @@ devtunnel host my-openclaw-bot Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (URL-адреси можуть змінюватися в кожному сеансі). -Якщо URL-адреса вашого тунелю змінилася, оновіть endpoint: +Якщо URL-адреса вашого тунелю зміниться, оновіть кінцеву точку: ```bash teams app update --endpoint "https:///api/messages" @@ -470,49 +466,49 @@ teams app doctor **Надішліть тестове повідомлення:** 1. Установіть застосунок Teams (скористайтеся посиланням для встановлення з `teams app get --install-link`) -2. Знайдіть бота в Teams і надішліть приватне повідомлення +2. Знайдіть бота в Teams і надішліть йому DM 3. Перевірте журнали Gateway на наявність вхідної активності ## Змінні середовища -Усі ключі конфігурації можна натомість задати через змінні середовища: +Усі ключі конфігурації натомість можна задавати через змінні середовища: - `MSTEAMS_APP_ID` - `MSTEAMS_APP_PASSWORD` - `MSTEAMS_TENANT_ID` -- `MSTEAMS_AUTH_TYPE` (необов’язково: `"secret"` або `"federated"`) -- `MSTEAMS_CERTIFICATE_PATH` (федеративна + сертифікат) -- `MSTEAMS_CERTIFICATE_THUMBPRINT` (необов’язково, не потрібне для автентифікації) -- `MSTEAMS_USE_MANAGED_IDENTITY` (федеративна + керована ідентичність) -- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID` (лише MI, призначена користувачем) +- `MSTEAMS_AUTH_TYPE` (необовʼязково: `"secret"` або `"federated"`) +- `MSTEAMS_CERTIFICATE_PATH` (федеративна автентифікація + сертифікат) +- `MSTEAMS_CERTIFICATE_THUMBPRINT` (необовʼязково, не потрібно для автентифікації) +- `MSTEAMS_USE_MANAGED_IDENTITY` (федеративна автентифікація + керована ідентичність) +- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID` (лише призначена користувачем керована ідентичність) ## Дія з інформацією про учасника -OpenClaw надає дію `member-info` на основі Graph для Microsoft Teams, щоб агенти й автоматизації могли отримувати відомості про учасників каналу (відображуване ім’я, електронну пошту, роль) безпосередньо з Microsoft Graph. +OpenClaw надає дію `member-info` на основі Graph для Microsoft Teams, щоб агенти й автоматизації могли отримувати відомості про учасників каналу (відображуване імʼя, електронна пошта, роль) безпосередньо з Microsoft Graph. Вимоги: - Дозвіл RSC `Member.Read.Group` (уже є в рекомендованому маніфесті) -- Для пошуків між командами: дозвіл Graph Application `User.Read.All` зі згодою адміністратора +- Для пошуку між командами: дозвіл Graph Application `User.Read.All` зі згодою адміністратора -Дію обмежує `channels.msteams.actions.memberInfo` (за замовчуванням: увімкнено, коли доступні облікові дані Graph). +Дія керується параметром `channels.msteams.actions.memberInfo` (за замовчуванням: увімкнено, коли доступні облікові дані Graph). ## Контекст історії -- `channels.msteams.historyLimit` керує тим, скільки останніх повідомлень каналу/групи загортаються в prompt. -- Якщо не задано, використовується `messages.groupChat.historyLimit`. Установіть `0`, щоб вимкнути (за замовчуванням 50). -- Отримана історія thread фільтрується за списками дозволених відправників (`allowFrom` / `groupAllowFrom`), тому ініціалізація контексту thread включає лише повідомлення від дозволених відправників. -- Контекст цитованих вкладень (`ReplyTo*`, отриманий із HTML-відповіді Teams) наразі передається як отримано. -- Іншими словами, списки дозволених відправників визначають, хто може запускати агента; сьогодні фільтруються лише окремі додаткові шляхи контексту. -- Історію приватних повідомлень можна обмежити за допомогою `channels.msteams.dmHistoryLimit` (ходи користувача). Перевизначення для окремих користувачів: `channels.msteams.dms[""].historyLimit`. +- `channels.msteams.historyLimit` керує тим, скільки останніх повідомлень каналу/групи загортається в запит. +- Має резервне значення `messages.groupChat.historyLimit`. Установіть `0`, щоб вимкнути (за замовчуванням 50). +- Отримана історія треду фільтрується за списками дозволених відправників (`allowFrom` / `groupAllowFrom`), тому початкове наповнення контексту треду містить лише повідомлення від дозволених відправників. +- Контекст цитованих вкладень (отриманий із HTML-відповіді Teams `ReplyTo*`) наразі передається як отримано. +- Іншими словами, списки дозволених визначають, хто може запускати агента; сьогодні фільтруються лише окремі шляхи додаткового контексту. +- Історію DM можна обмежити за допомогою `channels.msteams.dmHistoryLimit` (ходи користувача). Перевизначення для окремих користувачів: `channels.msteams.dms[""].historyLimit`. -## Поточні дозволи Teams RSC (маніфест) +## Поточні дозволи RSC Teams (маніфест) -Це **наявні resourceSpecific дозволи** у нашому маніфесті застосунку Teams. Вони застосовуються лише всередині команди/чату, де встановлено застосунок. +Це **наявні ресурсно-специфічні дозволи** в маніфесті нашого застосунку Teams. Вони застосовуються лише всередині команди/чату, де встановлено застосунок. **Для каналів (область команди):** -- `ChannelMessage.Read.Group` (Application) - отримувати всі повідомлення каналу без @mention +- `ChannelMessage.Read.Group` (Application) - отримувати всі повідомлення каналу без @згадки - `ChannelMessage.Send.Group` (Application) - `Member.Read.Group` (Application) - `Owner.Read.Group` (Application) @@ -522,7 +518,7 @@ OpenClaw надає дію `member-info` на основі Graph для Microsof **Для групових чатів:** -- `ChatMessage.Read.Chat` (Application) - отримувати всі повідомлення групового чату без @mention +- `ChatMessage.Read.Chat` (Application) - отримувати всі повідомлення групового чату без @згадки Щоб додати дозволи RSC через Teams CLI: @@ -532,7 +528,7 @@ teams app rsc add ChannelMessage.Read.Group --type Application ## Приклад маніфесту Teams (відредаговано) -Мінімальний дійсний приклад із потрібними полями. Замініть ID та URL-адреси. +Мінімальний, чинний приклад із потрібними полями. Замініть ID та URL-адреси. ```json5 { @@ -580,17 +576,17 @@ teams app rsc add ChannelMessage.Read.Group --type Application } ``` -### Застереження щодо маніфесту (обов’язкові поля) +### Застереження щодо маніфесту (обовʼязкові поля) - `bots[].botId` **має** збігатися з Azure Bot App ID. - `webApplicationInfo.id` **має** збігатися з Azure Bot App ID. - `bots[].scopes` має включати поверхні, які ви плануєте використовувати (`personal`, `team`, `groupChat`). -- `bots[].supportsFiles: true` потрібне для обробки файлів в особистій області. -- `authorization.permissions.resourceSpecific` має включати читання/надсилання каналів, якщо вам потрібен трафік каналів. +- `bots[].supportsFiles: true` потрібен для обробки файлів в особистій області. +- `authorization.permissions.resourceSpecific` має включати читання/надсилання для каналів, якщо вам потрібен трафік каналів. ### Оновлення наявного застосунку -Щоб оновити вже встановлений застосунок Teams (наприклад, додати дозволи RSC): +Щоб оновити вже встановлений застосунок Teams (наприклад, щоб додати дозволи RSC): ```bash # Download, edit, and re-upload the manifest @@ -603,11 +599,11 @@ teams app manifest upload manifest.json Після оновлення перевстановіть застосунок у кожній команді, щоб нові дозволи набули чинності, і **повністю закрийте та перезапустіть Teams** (не просто закрийте вікно), щоб очистити кешовані метадані застосунку.
-Оновлення маніфесту вручну (без CLI) +Ручне оновлення маніфесту (без CLI) 1. Оновіть ваш `manifest.json` новими налаштуваннями 2. **Збільште поле `version`** (наприклад, `1.0.0` → `1.1.0`) -3. **Повторно запакуйте в zip** маніфест з іконками (`manifest.json`, `outline.png`, `color.png`) +3. **Заново запакуйте в zip** маніфест з іконками (`manifest.json`, `outline.png`, `color.png`) 4. Завантажте новий zip: - **Teams Admin Center:** застосунки Teams → Керування застосунками → знайдіть ваш застосунок → Завантажити нову версію - **Sideload:** у Teams → Застосунки → Керування вашими застосунками → Завантажити власний застосунок @@ -622,57 +618,57 @@ teams app manifest upload manifest.json - Читання **текстового** вмісту повідомлень каналу. - Надсилання **текстового** вмісту повідомлень каналу. -- Отримання файлових вкладень у **особистих (DM)** повідомленнях. +- Отримання файлових вкладень **особистих (DM)** повідомлень. Не працює: -- **Вміст зображень або файлів** у каналах/групах (payload містить лише HTML-заглушку). +- **Вміст зображень або файлів** у каналі/групі (корисне навантаження містить лише HTML-заглушку). - Завантаження вкладень, збережених у SharePoint/OneDrive. -- Читання історії повідомлень (поза live-подією Webhook). +- Читання історії повідомлень (поза межами події live Webhook). ### З **Teams RSC + дозволами Microsoft Graph Application** Додає: -- Завантаження hosted contents (зображень, вставлених у повідомлення). +- Завантаження розміщеного вмісту (зображень, вставлених у повідомлення). - Завантаження файлових вкладень, збережених у SharePoint/OneDrive. -- Читання історії повідомлень каналів/чатів через Graph. +- Читання історії повідомлень каналу/чату через Graph. ### RSC проти Graph API -| Можливість | Дозволи RSC | Graph API | -| ----------------------- | -------------------- | -------------------------------------- | -| **Повідомлення в реальному часі** | Так (через Webhook) | Ні (лише опитування) | -| **Історичні повідомлення** | Ні | Так (можна запитувати історію) | -| **Складність налаштування** | Лише маніфест застосунку | Потрібна згода адміністратора + token flow | -| **Працює офлайн** | Ні (має бути запущено) | Так (запит у будь-який час) | +| Можливість | Дозволи RSC | Graph API | +| --------------------------- | ------------------- | ------------------------------------------ | +| **Повідомлення в реальному часі** | Так (через Webhook) | Ні (лише опитування) | +| **Історичні повідомлення** | Ні | Так (можна запитувати історію) | +| **Складність налаштування** | Лише маніфест застосунку | Потрібна згода адміністратора + потік токенів | +| **Працює офлайн** | Ні (має бути запущено) | Так (запит у будь-який час) | -**Підсумок:** RSC призначений для прослуховування в реальному часі; Graph API — для доступу до історії. Щоб наздогнати пропущені повідомлення під час офлайну, потрібен Graph API з `ChannelMessage.Read.All` (потрібна згода адміністратора). +**Підсумок:** RSC призначено для прослуховування в реальному часі; Graph API — для доступу до історії. Щоб наздоганяти пропущені повідомлення під час офлайну, потрібен Graph API з `ChannelMessage.Read.All` (потрібна згода адміністратора). -## Медіа й історія з увімкненим Graph (потрібно для каналів) +## Медіа та історія з увімкненим Graph (потрібно для каналів) Якщо вам потрібні зображення/файли в **каналах** або ви хочете отримувати **історію повідомлень**, потрібно ввімкнути дозволи Microsoft Graph і надати згоду адміністратора. -1. У **реєстрації застосунку** Entra ID (Azure AD) додайте **дозволи Application** Microsoft Graph: - - `ChannelMessage.Read.All` (вкладення каналів + історія) +1. В **реєстрації застосунку** Entra ID (Azure AD) додайте **дозволи Application** Microsoft Graph: + - `ChannelMessage.Read.All` (вкладення каналу + історія) - `Chat.Read.All` або `ChatMessage.Read.All` (групові чати) -2. **Надайте згоду адміністратора** для tenant. -3. Збільште **версію маніфесту** застосунку Teams, повторно завантажте його та **перевстановіть застосунок у Teams**. +2. **Надайте згоду адміністратора** для клієнта. +3. Підвищте **версію маніфесту** застосунку Teams, повторно завантажте його та **перевстановіть застосунок у Teams**. 4. **Повністю закрийте та перезапустіть Teams**, щоб очистити кешовані метадані застосунку. -**Додатковий дозвіл для згадок користувачів:** User @mentions працюють одразу для користувачів у розмові. Однак, якщо ви хочете динамічно шукати й згадувати користувачів, яких **немає в поточній розмові**, додайте дозвіл `User.Read.All` (Application) і надайте згоду адміністратора. +**Додатковий дозвіл для згадок користувачів:** @згадки користувачів працюють одразу для користувачів у розмові. Однак, якщо ви хочете динамічно шукати й згадувати користувачів, яких **немає в поточній розмові**, додайте дозвіл `User.Read.All` (Application) і надайте згоду адміністратора. ## Відомі обмеження ### Тайм-аути Webhook -Teams доставляє повідомлення через HTTP Webhook. Якщо обробка триває занадто довго (наприклад, повільні відповіді LLM), ви можете побачити: +Teams доставляє повідомлення через HTTP Webhook. Якщо обробка триває надто довго (наприклад, повільні відповіді LLM), ви можете побачити: - Тайм-аути Gateway -- Teams повторно надсилає повідомлення (що спричиняє дублікати) +- Повторні спроби Teams надіслати повідомлення (що спричиняє дублікати) - Відкинуті відповіді -OpenClaw обробляє це, швидко повертаючи відповідь і проактивно надсилаючи відповіді, але дуже повільні відповіді все одно можуть спричиняти проблеми. +OpenClaw обробляє це, швидко повертаючи відповідь і проактивно надсилаючи повідомлення, але дуже повільні відповіді все одно можуть спричиняти проблеми. ### Форматування @@ -680,28 +676,28 @@ Markdown у Teams обмеженіший, ніж у Slack або Discord: - Базове форматування працює: **жирний**, _курсив_, `code`, посилання - Складний markdown (таблиці, вкладені списки) може відображатися некоректно -- Adaptive Cards підтримуються для опитувань і надсилання семантичних презентацій (див. нижче) +- Adaptive Cards підтримуються для опитувань і семантичних presentation-відправлень (див. нижче) ## Конфігурація -Ключові налаштування (спільні шаблони каналів див. у `/gateway/configuration`): +Ключові налаштування (див. `/gateway/configuration` для спільних патернів каналів): - `channels.msteams.enabled`: увімкнути/вимкнути канал. - `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: облікові дані бота. -- `channels.msteams.webhook.port` (за замовчуванням `3978`) -- `channels.msteams.webhook.path` (за замовчуванням `/api/messages`) -- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (за замовчуванням: pairing) -- `channels.msteams.allowFrom`: allowlist для особистих повідомлень (рекомендовано AAD object IDs). Майстер під час налаштування зіставляє імена з ID, коли доступний Graph. -- `channels.msteams.dangerouslyAllowNameMatching`: аварійний перемикач для повторного ввімкнення змінного зіставлення за UPN/відображуваним іменем і прямої маршрутизації за назвою команди/каналу. +- `channels.msteams.webhook.port` (типово `3978`) +- `channels.msteams.webhook.path` (типово `/api/messages`) +- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (типово: pairing) +- `channels.msteams.allowFrom`: allowlist для DM (рекомендовано AAD object IDs). Майстер зіставляє імена з ID під час налаштування, коли доступний доступ до Graph. +- `channels.msteams.dangerouslyAllowNameMatching`: аварійний перемикач для повторного ввімкнення зіставлення за змінними UPN/display-name і прямої маршрутизації за назвою команди/каналу. - `channels.msteams.textChunkLimit`: розмір фрагмента вихідного тексту. -- `channels.msteams.chunkMode`: `length` (за замовчуванням) або `newline`, щоб розділяти за порожніми рядками (межами абзаців) перед поділом за довжиною. -- `channels.msteams.mediaAllowHosts`: allowlist для хостів вхідних вкладень (за замовчуванням домени Microsoft/Teams). -- `channels.msteams.mediaAuthAllowHosts`: allowlist для додавання заголовків Authorization під час повторних спроб медіа (за замовчуванням хости Graph + Bot Framework). -- `channels.msteams.requireMention`: вимагати @mention у каналах/групах (за замовчуванням true). +- `channels.msteams.chunkMode`: `length` (типово) або `newline`, щоб ділити за порожніми рядками (межами абзаців) перед поділом за довжиною. +- `channels.msteams.mediaAllowHosts`: allowlist для хостів вхідних вкладень (типово домени Microsoft/Teams). +- `channels.msteams.mediaAuthAllowHosts`: allowlist для додавання заголовків Authorization під час повторних спроб роботи з медіа (типово хости Graph + Bot Framework). +- `channels.msteams.requireMention`: вимагати @mention у каналах/групах (типово true). - `channels.msteams.replyStyle`: `thread | top-level` (див. [Стиль відповіді](#reply-style-threads-vs-posts)). - `channels.msteams.teams..replyStyle`: перевизначення для окремої команди. - `channels.msteams.teams..requireMention`: перевизначення для окремої команди. -- `channels.msteams.teams..tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), які використовуються, коли перевизначення каналу відсутнє. +- `channels.msteams.teams..tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), що використовуються, коли немає перевизначення для каналу. - `channels.msteams.teams..toolsBySender`: стандартні перевизначення політики інструментів для окремої команди й окремого відправника (підтримується wildcard `"*"`). - `channels.msteams.teams..channels..replyStyle`: перевизначення для окремого каналу. - `channels.msteams.teams..channels..requireMention`: перевизначення для окремого каналу. @@ -709,35 +705,35 @@ Markdown у Teams обмеженіший, ніж у Slack або Discord: - `channels.msteams.teams..channels..toolsBySender`: перевизначення політики інструментів для окремого каналу й окремого відправника (підтримується wildcard `"*"`). - Ключі `toolsBySender` мають використовувати явні префікси: `id:`, `e164:`, `username:`, `name:` (застарілі ключі без префікса все ще зіставляються лише з `id:`). -- `channels.msteams.actions.memberInfo`: увімкнути або вимкнути дію інформації про учасника на основі Graph (за замовчуванням: увімкнено, коли доступні облікові дані Graph). -- `channels.msteams.authType`: тип автентифікації — `"secret"` (за замовчуванням) або `"federated"`. -- `channels.msteams.certificatePath`: шлях до файлу PEM-сертифіката (федеративна автентифікація + автентифікація за сертифікатом). -- `channels.msteams.certificateThumbprint`: відбиток сертифіката (необов’язково, не потрібен для автентифікації). -- `channels.msteams.useManagedIdentity`: увімкнути автентифікацію через керовану ідентичність (федеративний режим). -- `channels.msteams.managedIdentityClientId`: client ID для user-assigned managed identity. +- `channels.msteams.actions.memberInfo`: увімкнути або вимкнути дію отримання інформації про учасника на основі Graph (типово: увімкнено, коли доступні облікові дані Graph). +- `channels.msteams.authType`: тип автентифікації — `"secret"` (типово) або `"federated"`. +- `channels.msteams.certificatePath`: шлях до PEM-файлу сертифіката (федеративна автентифікація + автентифікація сертифікатом). +- `channels.msteams.certificateThumbprint`: відбиток сертифіката (необов’язково, не потрібно для автентифікації). +- `channels.msteams.useManagedIdentity`: увімкнути автентифікацію managed identity (федеративний режим). +- `channels.msteams.managedIdentityClientId`: client ID для призначеної користувачем managed identity. - `channels.msteams.sharePointSiteId`: SharePoint site ID для завантаження файлів у групових чатах/каналах (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)). -## Маршрутизація та сеанси +## Маршрутизація та сесії -- Ключі сеансів відповідають стандартному формату агента (див. [/concepts/session](/uk/concepts/session)): - - Особисті повідомлення спільно використовують головний сеанс (`agent::`). +- Ключі сесій відповідають стандартному формату агента (див. [/concepts/session](/uk/concepts/session)): + - Прямі повідомлення спільно використовують основну сесію (`agent::`). - Повідомлення каналів/груп використовують conversation id: - `agent::msteams:channel:` - `agent::msteams:group:` -## Стиль відповіді: гілки проти дописів +## Стиль відповіді: треди проти дописів -Teams нещодавно запровадив два стилі інтерфейсу каналів поверх тієї самої базової моделі даних: +Teams нещодавно запровадив два стилі UI каналу поверх тієї самої базової моделі даних: | Стиль | Опис | Рекомендований `replyStyle` | | ------------------------ | --------------------------------------------------------- | ------------------------ | -| **Posts** (класичний) | Повідомлення відображаються як картки з гілковими відповідями під ними | `thread` (за замовчуванням) | +| **Posts** (класичний) | Повідомлення відображаються як картки з тредовими відповідями під ними | `thread` (типово) | | **Threads** (як у Slack) | Повідомлення йдуть лінійно, більше схоже на Slack | `top-level` | -**Проблема:** Teams API не показує, який стиль інтерфейсу використовує канал. Якщо використати неправильний `replyStyle`: +**Проблема:** Teams API не показує, який стиль UI використовує канал. Якщо використати неправильний `replyStyle`: -- `thread` у каналі зі стилем Threads → відповіді виглядають незручно вкладеними -- `top-level` у каналі зі стилем Posts → відповіді відображаються як окремі дописи верхнього рівня, а не в гілці +- `thread` у каналі стилю Threads → відповіді виглядають незручно вкладеними +- `top-level` у каналі стилю Posts → відповіді відображаються як окремі дописи верхнього рівня, а не всередині треду **Рішення:** Налаштуйте `replyStyle` для кожного каналу залежно від того, як налаштовано канал: @@ -764,33 +760,33 @@ Teams нещодавно запровадив два стилі інтерфей **Поточні обмеження:** -- **Особисті повідомлення:** Зображення та файлові вкладення працюють через файлові API бота Teams. -- **Канали/групи:** Вкладення зберігаються в сховищі M365 (SharePoint/OneDrive). Корисне навантаження Webhook містить лише HTML-заглушку, а не фактичні байти файлу. **Для завантаження вкладень каналів потрібні дозволи Graph API**. -- Для явного надсилання, де файл є основним вмістом, використовуйте `action=upload-file` з `media` / `filePath` / `path`; необов’язковий `message` стає супровідним текстом/коментарем, а `filename` перевизначає назву завантаженого файлу. +- **DM:** Зображення й файлові вкладення працюють через файлові API бота Teams. +- **Канали/групи:** Вкладення зберігаються в сховищі M365 (SharePoint/OneDrive). Webhook payload містить лише HTML-заглушку, а не фактичні байти файлу. **Для завантаження вкладень каналів потрібні дозволи Graph API**. +- Для явних надсилань, де файл є основним вмістом, використовуйте `action=upload-file` з `media` / `filePath` / `path`; необов’язковий `message` стає супровідним текстом/коментарем, а `filename` перевизначає назву завантаженого файлу. -Без дозволів Graph повідомлення каналу із зображеннями будуть отримані лише як текст (вміст зображення недоступний боту). -За замовчуванням OpenClaw завантажує медіа лише з імен хостів Microsoft/Teams. Перевизначте через `channels.msteams.mediaAllowHosts` (використовуйте `["*"]`, щоб дозволити будь-який хост). -Заголовки Authorization додаються лише для хостів у `channels.msteams.mediaAuthAllowHosts` (за замовчуванням хости Graph + Bot Framework). Тримайте цей список суворим (уникайте суфіксів для кількох tenant). +Без дозволів Graph повідомлення каналів із зображеннями надходитимуть лише як текст (вміст зображення недоступний боту). +Типово OpenClaw завантажує медіа лише з імен хостів Microsoft/Teams. Перевизначте це за допомогою `channels.msteams.mediaAllowHosts` (використовуйте `["*"]`, щоб дозволити будь-який хост). +Заголовки Authorization додаються лише для хостів у `channels.msteams.mediaAuthAllowHosts` (типово хости Graph + Bot Framework). Тримайте цей список суворим (уникайте багатотенантних суфіксів). ## Надсилання файлів у групових чатах -Боти можуть надсилати файли в особистих повідомленнях за допомогою потоку FileConsentCard (вбудовано). Однак **надсилання файлів у групових чатах/каналах** потребує додаткового налаштування: +Боти можуть надсилати файли в DM за допомогою потоку FileConsentCard (вбудовано). Однак **надсилання файлів у групових чатах/каналах** потребує додаткового налаштування: | Контекст | Як надсилаються файли | Потрібне налаштування | | ------------------------ | -------------------------------------------- | ----------------------------------------------- | -| **Особисті повідомлення** | FileConsentCard → користувач приймає → бот завантажує | Працює одразу | +| **DM** | FileConsentCard → користувач приймає → бот завантажує | Працює одразу | | **Групові чати/канали** | Завантаження в SharePoint → поширення посилання | Потрібні `sharePointSiteId` + дозволи Graph | -| **Зображення (будь-який контекст)** | Inline у кодуванні Base64 | Працює одразу | +| **Зображення (будь-який контекст)** | Вбудовано з кодуванням Base64 | Працює одразу | ### Чому груповим чатам потрібен SharePoint -Боти не мають персонального диска OneDrive (endpoint Graph API `/me/drive` не працює для application identities). Щоб надсилати файли в групових чатах/каналах, бот завантажує їх на **сайт SharePoint** і створює посилання для спільного доступу. +Боти не мають особистого диска OneDrive (endpoint Graph API `/me/drive` не працює для ідентичностей застосунків). Щоб надсилати файли в групові чати/канали, бот завантажує їх на **сайт SharePoint** і створює посилання для поширення. ### Налаштування 1. **Додайте дозволи Graph API** в Entra ID (Azure AD) → App Registration: - `Sites.ReadWrite.All` (Application) - завантаження файлів у SharePoint - - `Chat.Read.All` (Application) - необов’язково, вмикає посилання для спільного доступу на рівні користувача + - `Chat.Read.All` (Application) - необов’язково, вмикає посилання для поширення для окремих користувачів 2. **Надайте згоду адміністратора** для tenant. @@ -821,40 +817,40 @@ Teams нещодавно запровадив два стилі інтерфей } ``` -### Поведінка спільного доступу +### Поведінка поширення -| Дозвіл | Поведінка спільного доступу | +| Дозвіл | Поведінка поширення | | --------------------------------------- | --------------------------------------------------------- | -| Лише `Sites.ReadWrite.All` | Посилання для спільного доступу в межах організації (доступ має будь-хто в організації) | -| `Sites.ReadWrite.All` + `Chat.Read.All` | Посилання для спільного доступу на рівні користувача (доступ мають лише учасники чату) | +| лише `Sites.ReadWrite.All` | Посилання для поширення на всю організацію (доступ має будь-хто в організації) | +| `Sites.ReadWrite.All` + `Chat.Read.All` | Посилання для поширення для окремих користувачів (доступ мають лише учасники чату) | -Спільний доступ на рівні користувача безпечніший, оскільки лише учасники чату можуть отримати доступ до файлу. Якщо дозволу `Chat.Read.All` немає, бот повертається до спільного доступу в межах організації. +Поширення для окремих користувачів безпечніше, оскільки доступ до файлу мають лише учасники чату. Якщо дозволу `Chat.Read.All` бракує, бот повертається до поширення на всю організацію. ### Резервна поведінка | Сценарій | Результат | | ------------------------------------------------- | -------------------------------------------------- | -| Груповий чат + файл + налаштований `sharePointSiteId` | Завантаження в SharePoint, надсилання посилання для спільного доступу | -| Груповий чат + файл + без `sharePointSiteId` | Спроба завантаження в OneDrive (може завершитися помилкою), надсилання лише тексту | +| Груповий чат + файл + налаштовано `sharePointSiteId` | Завантаження в SharePoint, надсилання посилання для поширення | +| Груповий чат + файл + немає `sharePointSiteId` | Спроба завантаження в OneDrive (може завершитися помилкою), надсилання лише тексту | | Особистий чат + файл | Потік FileConsentCard (працює без SharePoint) | -| Будь-який контекст + зображення | Inline у кодуванні Base64 (працює без SharePoint) | +| Будь-який контекст + зображення | Вбудовано з кодуванням Base64 (працює без SharePoint) | -### Місце зберігання файлів +### Розташування збережених файлів Завантажені файли зберігаються в папці `/OpenClawShared/` у стандартній бібліотеці документів налаштованого сайту SharePoint. ## Опитування (Adaptive Cards) -OpenClaw надсилає опитування Teams як Adaptive Cards (нативного Teams poll API немає). +OpenClaw надсилає опитування Teams як Adaptive Cards (нативного API опитувань Teams немає). - CLI: `openclaw message poll --channel msteams --target conversation: ...` - Голоси записуються Gateway у `~/.openclaw/msteams-polls.json`. - Gateway має залишатися онлайн, щоб записувати голоси. -- Опитування поки що не публікують підсумки результатів автоматично (за потреби перегляньте файл сховища). +- Опитування поки що не публікують автоматично підсумки результатів (за потреби перегляньте файл сховища). -## Картки презентацій +## Presentation-картки -Надсилайте семантичні презентаційні payload користувачам або розмовам Teams за допомогою інструмента `message` або CLI. OpenClaw відтворює їх як Teams Adaptive Cards із загального контракту презентацій. +Надсилайте семантичні presentation payloads користувачам або conversations Teams за допомогою інструмента `message` або CLI. OpenClaw рендерить їх як Teams Adaptive Cards із загального presentation-контракту. Параметр `presentation` приймає семантичні блоки. Коли надано `presentation`, текст повідомлення необов’язковий. @@ -880,13 +876,13 @@ openclaw message send --channel msteams \ --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}' ``` -Докладніше про формат цілі див. у [Формати цілей](#target-formats) нижче. +Докладніше про формат target див. у розділі [Формати target](#target-formats) нижче. -## Формати цілей +## Формати target -Цілі MSTeams використовують префікси, щоб розрізняти користувачів і розмови: +Targets MSTeams використовують префікси, щоб розрізняти користувачів і conversations: -| Тип цілі | Формат | Приклад | +| Тип target | Формат | Приклад | | ------------------- | -------------------------------- | --------------------------------------------------- | | Користувач (за ID) | `user:` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` | | Користувач (за іменем) | `user:` | `user:John Smith` (потрібен Graph API) | @@ -934,24 +930,24 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread. ``` -Без префікса `user:` імена за замовчуванням розпізнаються як групи або команди. Завжди використовуйте `user:`, коли націлюєтеся на людей за відображуваним іменем. +Без префікса `user:` імена за замовчуванням обробляються як групи або команди. Завжди використовуйте `user:`, коли вказуєте людей за відображуваним іменем. -## Проактивний обмін повідомленнями +## Проактивні повідомлення - Проактивні повідомлення можливі лише **після** взаємодії користувача, оскільки в цей момент ми зберігаємо посилання на розмову. -- Див. `/gateway/configuration` щодо `dmPolicy` і керування списком дозволених. +- Див. `/gateway/configuration` для `dmPolicy` і керування allowlist. -## Ідентифікатори команд і каналів (поширена пастка) +## ID команди та каналу (поширена помилка) -Параметр запиту `groupId` в URL Teams **НЕ** є ідентифікатором команди, який використовується для конфігурації. Натомість витягайте ідентифікатори зі шляху URL: +Параметр запиту `groupId` в URL Teams **НЕ** є ID команди, що використовується для конфігурації. Натомість витягуйте ID зі шляху URL: **URL команди:** ``` https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... └────────────────────────────┘ - Team ID (URL-decode this) + ID розмови команди (URL-декодуйте це) ``` **URL каналу:** @@ -959,46 +955,46 @@ https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?gro ``` https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... └─────────────────────────┘ - Channel ID (URL-decode this) + ID каналу (URL-декодуйте це) ``` **Для конфігурації:** -- Ідентифікатор команди = сегмент шляху після `/team/` (URL-декодований, наприклад, `19:Bk4j...@thread.tacv2`) -- Ідентифікатор каналу = сегмент шляху після `/channel/` (URL-декодований) -- **Ігноруйте** параметр запиту `groupId` +- Ключ команди = сегмент шляху після `/team/` (URL-декодований, напр., `19:Bk4j...@thread.tacv2`; старіші тенанти можуть показувати `@thread.skype`, що також є чинним) +- Ключ каналу = сегмент шляху після `/channel/` (URL-декодований) +- **Ігноруйте** параметр запиту `groupId` для маршрутизації OpenClaw. Це ID групи Microsoft Entra, а не ID розмови Bot Framework, що використовується у вхідних активностях Teams. ## Приватні канали Боти мають обмежену підтримку в приватних каналах: -| Функція | Стандартні канали | Приватні канали | -| ---------------------------- | ----------------- | ------------------------------- | -| Встановлення бота | Так | Обмежено | -| Повідомлення в реальному часі (webhook) | Так | Може не працювати | -| Дозволи RSC | Так | Можуть поводитися інакше | -| @згадки | Так | Якщо бот доступний | -| Історія Graph API | Так | Так (за наявності дозволів) | +| Функція | Стандартні канали | Приватні канали | +| ----------------------------- | ----------------- | ---------------------------- | +| Встановлення бота | Так | Обмежено | +| Повідомлення в реальному часі (Webhook) | Так | Може не працювати | +| Дозволи RSC | Так | Можуть поводитися інакше | +| @mentions | Так | Якщо бот доступний | +| Історія Graph API | Так | Так (з дозволами) | -**Обхідні рішення, якщо приватні канали не працюють:** +**Обхідні варіанти, якщо приватні канали не працюють:** 1. Використовуйте стандартні канали для взаємодії з ботом -2. Використовуйте DM - користувачі завжди можуть написати боту напряму +2. Використовуйте приватні повідомлення - користувачі завжди можуть написати боту напряму 3. Використовуйте Graph API для доступу до історії (потрібен `ChannelMessage.Read.All`) -## Усунення несправностей +## Усунення неполадок ### Поширені проблеми -- **Зображення не відображаються в каналах:** відсутні дозволи Graph або згода адміністратора. Перевстановіть застосунок Teams і повністю закрийте/знову відкрийте Teams. -- **Немає відповідей у каналі:** за замовчуванням потрібні згадки; встановіть `channels.msteams.requireMention=false` або налаштуйте окремо для команди/каналу. -- **Невідповідність версії (Teams усе ще показує старий маніфест):** видаліть і повторно додайте застосунок, а потім повністю закрийте Teams, щоб оновити дані. -- **401 Unauthorized від webhook:** очікувано під час ручного тестування без Azure JWT - це означає, що кінцева точка доступна, але автентифікація не вдалася. Використовуйте Azure Web Chat для коректного тестування. +- **Зображення не відображаються в каналах:** відсутні дозволи Graph або згода адміністратора. Перевстановіть застосунок Teams і повністю закрийте та знову відкрийте Teams. +- **Немає відповідей у каналі:** згадки потрібні за замовчуванням; задайте `channels.msteams.requireMention=false` або налаштуйте для конкретної команди/каналу. +- **Невідповідність версії (Teams досі показує старий маніфест):** видаліть і повторно додайте застосунок, а також повністю закрийте Teams для оновлення. +- **401 Unauthorized від Webhook:** очікувано під час ручного тестування без Azure JWT - це означає, що endpoint доступний, але автентифікація не пройшла. Використовуйте Azure Web Chat для коректного тестування. ### Помилки завантаження маніфесту -- **"Icon file cannot be empty":** маніфест посилається на файли піктограм розміром 0 байтів. Створіть коректні PNG-піктограми (32x32 для `outline.png`, 192x192 для `color.png`). -- **"webApplicationInfo.Id already in use":** застосунок усе ще встановлено в іншій команді/чаті. Спочатку знайдіть і видаліть його або зачекайте 5-10 хвилин на поширення змін. +- **"Icon file cannot be empty":** маніфест посилається на файли іконок розміром 0 байтів. Створіть коректні PNG-іконки (32x32 для `outline.png`, 192x192 для `color.png`). +- **"webApplicationInfo.Id already in use":** застосунок досі встановлений в іншій команді/чаті. Спершу знайдіть і видаліть його або зачекайте 5-10 хвилин на поширення змін. - **"Something went wrong" під час завантаження:** натомість завантажте через [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com), відкрийте DevTools браузера (F12) → вкладку Network і перевірте тіло відповіді на фактичну помилку. - **Sideload не вдається:** спробуйте "Upload an app to your org's app catalog" замість "Upload a custom app" - це часто обходить обмеження sideload. @@ -1007,23 +1003,23 @@ https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?gr 1. Переконайтеся, що `webApplicationInfo.id` точно збігається з App ID вашого бота 2. Повторно завантажте застосунок і перевстановіть його в команді/чаті 3. Перевірте, чи адміністратор вашої організації не заблокував дозволи RSC -4. Підтвердьте, що використовуєте правильну область: `ChannelMessage.Read.Group` для команд, `ChatMessage.Read.Chat` для групових чатів +4. Підтвердьте, що використовуєте правильний scope: `ChannelMessage.Read.Group` для команд, `ChatMessage.Read.Chat` для групових чатів ## Посилання -- [Створити Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - посібник із налаштування Azure Bot -- [Портал розробника Teams](https://dev.teams.microsoft.com/apps) - створення й керування застосунками Teams +- [Створення Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - посібник із налаштування Azure Bot +- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - створення й керування застосунками Teams - [Схема маніфесту застосунку Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) -- [Отримання повідомлень каналу за допомогою RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) +- [Отримання повідомлень каналу з RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) - [Довідник дозволів RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent) -- [Обробка файлів ботом Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (канал/група потребує Graph) -- [Проактивний обмін повідомленнями](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) -- [@microsoft/teams.cli](https://www.npmjs.com/package/@microsoft/teams.cli) - Teams CLI для керування ботом +- [Обробка файлів ботом Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (для каналу/групи потрібен Graph) +- [Проактивні повідомлення](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) +- [@microsoft/teams.cli](https://www.npmjs.com/package/@microsoft/teams.cli) - Teams CLI для керування ботами ## Пов’язане - [Огляд каналів](/uk/channels) — усі підтримувані канали -- [Сполучення](/uk/channels/pairing) — автентифікація DM і процес сполучення +- [Сполучення](/uk/channels/pairing) — автентифікація в приватних повідомленнях і потік сполучення - [Групи](/uk/channels/groups) — поведінка групового чату та керування згадками -- [Маршрутизація каналів](/uk/channels/channel-routing) — маршрутизація сеансів для повідомлень -- [Безпека](/uk/gateway/security) — модель доступу та зміцнення безпеки +- [Маршрутизація каналів](/uk/channels/channel-routing) — маршрутизація сесій для повідомлень +- [Безпека](/uk/gateway/security) — модель доступу та посилення захисту diff --git a/docs/uk/plugins/sdk-channel-plugins.md b/docs/uk/plugins/sdk-channel-plugins.md index 6d3ec6da2..c23405eaf 100644 --- a/docs/uk/plugins/sdk-channel-plugins.md +++ b/docs/uk/plugins/sdk-channel-plugins.md @@ -2,129 +2,129 @@ read_when: - Ви створюєте новий Plugin каналу обміну повідомленнями - Ви хочете підключити OpenClaw до платформи обміну повідомленнями - - Потрібно зрозуміти поверхню адаптера ChannelPlugin + - Потрібно розуміти інтерфейс адаптера ChannelPlugin sidebarTitle: Channel Plugins summary: Покроковий посібник зі створення Plugin каналу обміну повідомленнями для OpenClaw -title: Створення Plugin-ів каналів +title: Створення Plugin для каналів x-i18n: - generated_at: "2026-04-29T15:39:41Z" + generated_at: "2026-04-30T00:42:36Z" model: gpt-5.5 provider: openai - source_hash: 03384057a4316b87c6088d3859d16ed4546c803f7c64639cd12be293f4841258 + source_hash: 068cd797f7761efa54f4fdeb7cb4aa784ceace959f1af12bc549c16ed2776b72 source_path: plugins/sdk-channel-plugins.md workflow: 16 --- -Цей посібник покроково показує, як створити Plugin каналу, що підключає OpenClaw до +Цей посібник показує, як створити Plugin каналу, який підключає OpenClaw до платформи обміну повідомленнями. Наприкінці ви матимете робочий канал із безпекою DM, -спарюванням, ланцюжками відповідей і вихідними повідомленнями. +сполученням, гілкуванням відповідей і вихідними повідомленнями. - Якщо ви ще не створювали жодного Plugin OpenClaw, спершу прочитайте - [Початок роботи](/uk/plugins/building-plugins), щоб дізнатися про базову структуру - пакета та налаштування маніфесту. + Якщо ви раніше не створювали жодного Plugin OpenClaw, спершу прочитайте + [Початок роботи](/uk/plugins/building-plugins), щоб дізнатися базову структуру + пакета й налаштування маніфесту. -## Як працюють Plugins каналів +## Як працюють Plugin каналів -Plugins каналів не потребують власних інструментів надсилання/редагування/реакцій. OpenClaw тримає один +Plugin для каналів не потребують власних інструментів надсилання, редагування чи реакцій. OpenClaw тримає один спільний інструмент `message` у ядрі. Ваш Plugin відповідає за: -- **Конфігурація** — визначення акаунта та майстер налаштування -- **Безпека** — політика DM та списки дозволених -- **Спарювання** — потік підтвердження через DM -- **Граматика сесії** — як ідентифікатори розмов конкретного провайдера відображаються на базові чати, ідентифікатори ланцюжків і батьківські резервні варіанти -- **Вихідні повідомлення** — надсилання тексту, медіа та опитувань на платформу -- **Ланцюжки** — як групуються відповіді -- **Heartbeat typing** — необов'язкові сигнали набору/зайнятості для цілей доставки Heartbeat +- **Конфігурація** — визначення акаунта й майстер налаштування +- **Безпека** — політика DM і списки дозволених +- **Сполучення** — потік схвалення через DM +- **Граматика сеансів** — як специфічні для провайдера ідентифікатори розмов зіставляються з базовими чатами, ідентифікаторами гілок і батьківськими резервними варіантами +- **Вихідні повідомлення** — надсилання тексту, медіа й опитувань на платформу +- **Гілкування** — як відповіді потрапляють у гілки +- **Набір для Heartbeat** — необов'язкові сигнали набору/зайнятості для цілей доставки Heartbeat -Ядро відповідає за спільний інструмент повідомлень, підключення промптів, зовнішню форму ключа сесії, +Ядро відповідає за спільний інструмент повідомлень, підключення промптів, зовнішню форму ключа сеансу, загальний облік `:thread:` і диспетчеризацію. Якщо ваш канал підтримує індикатори набору поза вхідними відповідями, експонуйте `heartbeat.sendTyping(...)` у Plugin каналу. Ядро викликає його з -визначеною ціллю доставки Heartbeat перед початком запуску моделі Heartbeat і -використовує спільний життєвий цикл підтримання/очищення індикатора набору. Додайте `heartbeat.clearTyping(...)`, +розв'язаною ціллю доставки Heartbeat перед початком запуску моделі Heartbeat і +використовує спільний життєвий цикл підтримання та очищення набору. Додайте `heartbeat.clearTyping(...)`, коли платформі потрібен явний сигнал зупинки. -Якщо ваш канал додає параметри інструмента повідомлень, що несуть джерела медіа, експонуйте ці +Якщо ваш канал додає параметри інструмента повідомлень, які несуть джерела медіа, експонуйте ці назви параметрів через `describeMessageTool(...).mediaSourceParams`. Ядро використовує -цей явний список для нормалізації шляхів sandbox і політики доступу до вихідних медіа, -тож Plugins не потребують спеціальних випадків у спільному ядрі для параметрів аватарів, -вкладень або обкладинок, специфічних для провайдера. -Бажано повертати мапу за ключами дій, наприклад +цей явний список для нормалізації шляхів пісочниці й політики доступу до вихідних медіа, +тому Plugin не потребують спеціальних випадків у спільному ядрі для специфічних для провайдера +параметрів аватара, вкладення або зображення обкладинки. +Надавайте перевагу поверненню мапи з ключами за діями, наприклад `{ "set-profile": ["avatarUrl", "avatarPath"] }`, щоб непов'язані дії не успадковували медіааргументи іншої дії. Плоский масив також працює для параметрів, які -навмисно спільні для всіх експонованих дій. +навмисно спільні для кожної експонованої дії. -Якщо ваша платформа зберігає додатковий scope всередині ідентифікаторів розмов, тримайте цей парсинг +Якщо ваша платформа зберігає додаткову область усередині ідентифікаторів розмов, тримайте цей розбір у Plugin через `messaging.resolveSessionConversation(...)`. Це -канонічний хук для відображення `rawId` на базовий ідентифікатор розмови, необов'язковий ідентифікатор ланцюжка, -явний `baseConversationId` і будь-які `parentConversationCandidates`. -Коли повертаєте `parentConversationCandidates`, тримайте їх упорядкованими від -найвужчого батьківського елемента до найширшої/базової розмови. +канонічний хук для зіставлення `rawId` з базовим ідентифікатором розмови, необов'язковим ідентифікатором гілки, +явним `baseConversationId` і будь-якими `parentConversationCandidates`. +Коли ви повертаєте `parentConversationCandidates`, зберігайте їх упорядкованими від +найвужчого батька до найширшої/базової розмови. Використовуйте `openclaw/plugin-sdk/channel-route`, коли коду Plugin потрібно нормалізувати -поля, схожі на маршрути, порівняти дочірній ланцюжок із його батьківським маршрутом або побудувати -стабільний ключ дедуплікації з `{ channel, to, accountId, threadId }`. Помічник -нормалізує числові ідентифікатори ланцюжків так само, як це робить ядро, тому Plugins мають віддавати -йому перевагу над ситуативними порівняннями `String(threadId)`. -Plugins із граматикою цілей, специфічною для провайдера, можуть інжектувати свій парсер у -`resolveChannelRouteTargetWithParser(...)` і все одно отримувати ту саму форму цілі маршруту -та семантику резервного ланцюжка, яку використовує ядро. +поля, схожі на маршрути, порівняти дочірню гілку з її батьківським маршрутом або побудувати +стабільний ключ дедуплікації з `{ channel, to, accountId, threadId }`. Допоміжний засіб +нормалізує числові ідентифікатори гілок так само, як це робить ядро, тому Plugin мають надавати +йому перевагу перед спеціальними порівняннями `String(threadId)`. +Plugin із специфічною для провайдера граматикою цілей можуть ін'єктувати свій парсер у +`resolveChannelRouteTargetWithParser(...)` і все одно отримати ту саму форму цілі маршруту +та семантику резервних гілок, яку використовує ядро. -Вбудовані Plugins, яким потрібен той самий парсинг до запуску реєстру каналів, -також можуть експонувати файл верхнього рівня `session-key-api.ts` з відповідним -експортом `resolveSessionConversation(...)`. Ядро використовує цю безпечну для bootstrap поверхню -лише тоді, коли runtime-реєстр Plugin ще недоступний. +Вбудовані Plugin, яким потрібен той самий розбір до завантаження реєстру каналів, +також можуть експонувати файл верхнього рівня `session-key-api.ts` із відповідним +експортом `resolveSessionConversation(...)`. Ядро використовує цю безпечну для початкового завантаження поверхню +лише тоді, коли реєстр Plugin часу виконання ще недоступний. `messaging.resolveParentConversationCandidates(...)` залишається доступним як -застарілий резерв сумісності, коли Plugin потребує лише батьківських резервних варіантів поверх -загального/raw ідентифікатора. Якщо існують обидва хуки, ядро спершу використовує -`resolveSessionConversation(...).parentConversationCandidates` і лише +застарілий резервний механізм сумісності, коли Plugin потребує лише батьківських резервних варіантів поверх +загального/сирого ідентифікатора. Якщо існують обидва хуки, ядро спочатку використовує +`resolveSessionConversation(...).parentConversationCandidates` і лише потім переходить до `resolveParentConversationCandidates(...)`, коли канонічний хук -їх пропускає. +їх не надає. -## Підтвердження та можливості каналу +## Схвалення та можливості каналів -Більшості Plugins каналів не потрібен код, специфічний для підтверджень. +Більшість Plugin для каналів не потребують коду, специфічного для схвалень. -- Ядро відповідає за same-chat `/approve`, спільні payload підтверджувальних кнопок і загальну резервну доставку. -- Віддавайте перевагу одному об'єкту `approvalCapability` у Plugin каналу, коли каналу потрібна поведінка, специфічна для підтверджень. -- `ChannelPlugin.approvals` видалено. Розміщуйте факти доставки/native/render/auth для підтверджень у `approvalCapability`. -- `plugin.auth` призначений лише для входу/виходу; ядро більше не читає хуки auth підтверджень із цього об'єкта. -- `approvalCapability.authorizeActorAction` і `approvalCapability.getActionAvailabilityState` є канонічним seam auth підтверджень. -- Використовуйте `approvalCapability.getActionAvailabilityState` для доступності auth same-chat підтверджень. -- Якщо ваш канал експонує native exec підтвердження, використовуйте `approvalCapability.getExecInitiatingSurfaceState` для стану initiating-surface/native-client, коли він відрізняється від auth same-chat підтверджень. Ядро використовує цей exec-специфічний хук, щоб розрізняти `enabled` і `disabled`, вирішувати, чи підтримує початковий канал native exec підтвердження, і включати канал у fallback-підказки native-client. `createApproverRestrictedNativeApprovalCapability(...)` заповнює це для типового випадку. -- Використовуйте `outbound.shouldSuppressLocalPayloadPrompt` або `outbound.beforeDeliverPayload` для поведінки життєвого циклу payload, специфічної для каналу, наприклад приховування дубльованих локальних промптів підтвердження або надсилання індикаторів набору перед доставкою. -- Використовуйте `approvalCapability.delivery` лише для маршрутизації native підтверджень або пригнічення fallback. -- Використовуйте `approvalCapability.nativeRuntime` для фактів native підтверджень, якими володіє канал. Тримайте його lazy на гарячих entrypoints каналу за допомогою `createLazyChannelApprovalNativeRuntimeAdapter(...)`, який може імпортувати ваш runtime-модуль на вимогу, водночас дозволяючи ядру зібрати життєвий цикл підтвердження. -- Використовуйте `approvalCapability.render` лише тоді, коли каналу справді потрібні власні payload підтвердження замість спільного renderer. -- Використовуйте `approvalCapability.describeExecApprovalSetup`, коли канал хоче, щоб відповідь disabled-path пояснювала точні перемикачі конфігурації, потрібні для ввімкнення native exec підтверджень. Хук отримує `{ channel, channelLabel, accountId }`; канали з іменованими акаунтами мають рендерити шляхи в межах акаунта, як-от `channels..accounts..execApprovals.*`, замість top-level defaults. -- Якщо канал може вивести стабільні owner-like DM ідентичності з наявної конфігурації, використовуйте `createResolvedApproverActionAuthAdapter` з `openclaw/plugin-sdk/approval-runtime`, щоб обмежити same-chat `/approve` без додавання логіки ядра, специфічної для підтверджень. -- Якщо каналу потрібна доставка native підтверджень, зосередьте код каналу на нормалізації цілі та фактах транспорту/представлення. Використовуйте `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver` і `createApproverRestrictedNativeApprovalCapability` з `openclaw/plugin-sdk/approval-runtime`. Розміщуйте специфічні для каналу факти за `approvalCapability.nativeRuntime`, бажано через `createChannelApprovalNativeRuntimeAdapter(...)` або `createLazyChannelApprovalNativeRuntimeAdapter(...)`, щоб ядро могло зібрати handler і відповідати за фільтрацію запитів, маршрутизацію, дедуплікацію, завершення терміну дії, підписку Gateway і сповіщення про маршрутизацію в інше місце. `nativeRuntime` розділено на кілька менших seam: -- `createChannelNativeOriginTargetResolver` за замовчуванням використовує спільний matcher channel-route для цілей `{ to, accountId, threadId }`. Передавайте `targetsMatch` лише тоді, коли канал має специфічні для провайдера правила еквівалентності, наприклад зіставлення префікса timestamp у Slack. -- Передавайте `normalizeTargetForMatch` у `createChannelNativeOriginTargetResolver`, коли каналу потрібно канонізувати ідентифікатори провайдера перед запуском стандартного route matcher або власного callback `targetsMatch`, зберігаючи оригінальну ціль для доставки. Використовуйте `normalizeTarget` лише тоді, коли саму визначену ціль доставки потрібно канонізувати. +- Ядро відповідає за `/approve` у тому самому чаті, спільні корисні навантаження кнопок схвалення й загальну резервну доставку. +- Надавайте перевагу одному об'єкту `approvalCapability` у Plugin каналу, коли каналу потрібна поведінка, специфічна для схвалень. +- `ChannelPlugin.approvals` видалено. Розміщуйте факти про доставку, нативність, рендеринг і автентифікацію схвалень у `approvalCapability`. +- `plugin.auth` призначений лише для входу/виходу; ядро більше не читає хуки автентифікації схвалень із цього об'єкта. +- `approvalCapability.authorizeActorAction` і `approvalCapability.getActionAvailabilityState` є канонічною точкою інтеграції для автентифікації схвалень. +- Використовуйте `approvalCapability.getActionAvailabilityState` для доступності автентифікації схвалень у тому самому чаті. +- Якщо ваш канал експонує нативні exec-схвалення, використовуйте `approvalCapability.getExecInitiatingSurfaceState` для стану поверхні ініціювання/нативного клієнта, коли він відрізняється від автентифікації схвалень у тому самому чаті. Ядро використовує цей специфічний для exec хук, щоб відрізняти `enabled` від `disabled`, вирішувати, чи канал ініціювання підтримує нативні exec-схвалення, і включати канал у підказки щодо резервного переходу для нативного клієнта. `createApproverRestrictedNativeApprovalCapability(...)` заповнює це для типового випадку. +- Використовуйте `outbound.shouldSuppressLocalPayloadPrompt` або `outbound.beforeDeliverPayload` для специфічної для каналу поведінки життєвого циклу корисного навантаження, наприклад приховування дубльованих локальних запитів на схвалення або надсилання індикаторів набору перед доставкою. +- Використовуйте `approvalCapability.delivery` лише для маршрутизації нативних схвалень або пригнічення резервної доставки. +- Використовуйте `approvalCapability.nativeRuntime` для належних каналу фактів про нативні схвалення. Тримайте його лінивим на гарячих точках входу каналу за допомогою `createLazyChannelApprovalNativeRuntimeAdapter(...)`, який може імпортувати ваш модуль часу виконання на вимогу й водночас дозволяє ядру зібрати життєвий цикл схвалень. +- Використовуйте `approvalCapability.render` лише тоді, коли каналу справді потрібні власні корисні навантаження схвалень замість спільного рендерера. +- Використовуйте `approvalCapability.describeExecApprovalSetup`, коли канал хоче, щоб відповідь для вимкненого шляху пояснювала точні параметри конфігурації, потрібні для ввімкнення нативних exec-схвалень. Хук отримує `{ channel, channelLabel, accountId }`; канали з іменованими акаунтами мають рендерити шляхи, обмежені акаунтом, наприклад `channels..accounts..execApprovals.*`, замість типових значень верхнього рівня. +- Якщо канал може вивести стабільні DM-ідентичності на кшталт власника з наявної конфігурації, використовуйте `createResolvedApproverActionAuthAdapter` з `openclaw/plugin-sdk/approval-runtime`, щоб обмежити `/approve` у тому самому чаті без додавання специфічної для схвалень логіки ядра. +- Якщо каналу потрібна доставка нативних схвалень, тримайте код каналу зосередженим на нормалізації цілей і фактах транспорту/представлення. Використовуйте `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver` і `createApproverRestrictedNativeApprovalCapability` з `openclaw/plugin-sdk/approval-runtime`. Розмістіть специфічні для каналу факти за `approvalCapability.nativeRuntime`, в ідеалі через `createChannelApprovalNativeRuntimeAdapter(...)` або `createLazyChannelApprovalNativeRuntimeAdapter(...)`, щоб ядро могло зібрати обробник і відповідати за фільтрацію запитів, маршрутизацію, дедуплікацію, строк дії, підписку Gateway і повідомлення про маршрутизацію в інше місце. `nativeRuntime` поділено на кілька менших точок інтеграції: +- `createChannelNativeOriginTargetResolver` типово використовує спільний зіставлювач channel-route для цілей `{ to, accountId, threadId }`. Передавайте `targetsMatch` лише тоді, коли канал має специфічні для провайдера правила еквівалентності, наприклад зіставлення префікса часової мітки Slack. +- Передавайте `normalizeTargetForMatch` до `createChannelNativeOriginTargetResolver`, коли каналу потрібно канонізувати ідентифікатори провайдера перед запуском типового зіставлювача маршрутів або власного callback `targetsMatch`, зберігаючи початкову ціль для доставки. Використовуйте `normalizeTarget` лише тоді, коли сама розв'язана ціль доставки має бути канонізована. - `availability` — чи налаштовано акаунт і чи потрібно обробляти запит -- `presentation` — відображає спільну view model підтвердження в pending/resolved/expired native payload або фінальні дії -- `transport` — готує цілі та надсилає/оновлює/видаляє native повідомлення підтвердження -- `interactions` — необов'язкові хуки bind/unbind/clear-action для native кнопок або реакцій +- `presentation` — зіставлення спільної моделі подання схвалення з очікуваними/вирішеними/простроченими нативними корисними навантаженнями або фінальними діями +- `transport` — підготовка цілей і надсилання/оновлення/видалення нативних повідомлень схвалення +- `interactions` — необов'язкові хуки прив'язування/відв'язування/очищення дії для нативних кнопок або реакцій - `observe` — необов'язкові хуки діагностики доставки -- Якщо каналу потрібні runtime-об'єкти, якими він володіє, наприклад client, token, Bolt app або webhook receiver, реєструйте їх через `openclaw/plugin-sdk/channel-runtime-context`. Загальний реєстр runtime-context дає ядру змогу bootstrap capability-driven handlers зі стану запуску каналу без додавання glue wrapper, специфічного для підтверджень. -- Звертайтеся до нижчорівневих `createChannelApprovalHandler` або `createChannelNativeApprovalRuntime` лише тоді, коли capability-driven seam ще недостатньо виразний. -- Канали native підтверджень мають маршрутизувати і `accountId`, і `approvalKind` через ці helpers. `accountId` утримує політику підтверджень для кількох акаунтів у межах правильного bot account, а `approvalKind` зберігає поведінку exec і plugin підтверджень доступною для каналу без hardcoded branches у ядрі. -- Тепер ядро також відповідає за сповіщення про reroute підтверджень. Plugins каналів не мають надсилати власні follow-up повідомлення "підтвердження пішло в DM / інший канал" із `createChannelNativeApprovalRuntime`; натомість експонуйте точну маршрутизацію origin + approver-DM через спільні helpers capability підтверджень і дозвольте ядру агрегувати фактичні доставки перед публікацією будь-якого сповіщення назад у початковий чат. -- Зберігайте тип доставленого ідентифікатора підтвердження end-to-end. Native clients не повинні - вгадувати або переписувати маршрутизацію exec vs plugin підтверджень зі стану, локального для каналу. -- Різні типи підтверджень можуть навмисно експонувати різні native поверхні. +- Якщо каналу потрібні об'єкти, що належать часу виконання, як-от клієнт, токен, застосунок Bolt або Webhook-приймач, реєструйте їх через `openclaw/plugin-sdk/channel-runtime-context`. Загальний реєстр контексту часу виконання дозволяє ядру початково завантажувати обробники, керовані можливостями, зі стану запуску каналу без додавання специфічного для схвалень зв'язувального коду обгортки. +- Звертайтеся до нижчорівневих `createChannelApprovalHandler` або `createChannelNativeApprovalRuntime` лише тоді, коли точка інтеграції, керована можливостями, ще недостатньо виразна. +- Канали нативних схвалень мають маршрутизувати і `accountId`, і `approvalKind` через ці допоміжні засоби. `accountId` утримує політику схвалень для кількох акаунтів у межах правильного акаунта бота, а `approvalKind` зберігає доступність поведінки exec-схвалень і схвалень Plugin для каналу без жорстко заданих розгалужень у ядрі. +- Ядро тепер також відповідає за повідомлення про перемаршрутизацію схвалень. Plugin каналів не мають надсилати власні подальші повідомлення "схвалення пішло в DM / інший канал" з `createChannelNativeApprovalRuntime`; натомість експонуйте точну маршрутизацію джерела + DM схвалювача через спільні допоміжні засоби можливостей схвалення й дозвольте ядру агрегувати фактичні доставки перед публікацією будь-якого повідомлення назад у чат ініціювання. +- Зберігайте тип доставленого ідентифікатора схвалення наскрізно. Нативні клієнти не повинні + вгадувати або переписувати маршрутизацію exec-схвалень і схвалень Plugin з локального стану каналу. +- Різні типи схвалень можуть навмисно експонувати різні нативні поверхні. Поточні вбудовані приклади: - - Slack зберігає native маршрутизацію підтверджень доступною і для exec, і для plugin ids. - - Matrix зберігає ту саму native маршрутизацію DM/каналу та UX реакцій для exec - і plugin підтверджень, водночас дозволяючи auth відрізнятися за типом підтвердження. -- `createApproverRestrictedNativeApprovalAdapter` все ще існує як wrapper сумісності, але новий код має віддавати перевагу builder capability і експонувати `approvalCapability` у Plugin. + - Slack зберігає доступність маршрутизації нативних схвалень і для exec, і для ідентифікаторів Plugin. + - Matrix зберігає ту саму нативну маршрутизацію DM/каналу й UX реакцій для exec + і схвалень Plugin, водночас дозволяючи автентифікації відрізнятися за типом схвалення. +- `createApproverRestrictedNativeApprovalAdapter` усе ще існує як обгортка сумісності, але новий код має надавати перевагу побудовувачу можливостей і експонувати `approvalCapability` у Plugin. -Для гарячих entrypoints каналу віддавайте перевагу вужчим runtime subpaths, коли вам потрібна лише -одна частина цієї родини: +Для гарячих точок входу каналу надавайте перевагу вужчим підшляхам часу виконання, коли вам потрібна лише +одна частина цього сімейства: - `openclaw/plugin-sdk/approval-auth-runtime` - `openclaw/plugin-sdk/approval-client-runtime` @@ -136,125 +136,128 @@ Plugins із граматикою цілей, специфічною для пр - `openclaw/plugin-sdk/approval-reply-runtime` - `openclaw/plugin-sdk/channel-runtime-context` -Так само віддавайте перевагу `openclaw/plugin-sdk/setup-runtime`, +Так само надавайте перевагу `openclaw/plugin-sdk/setup-runtime`, `openclaw/plugin-sdk/setup-adapter-runtime`, `openclaw/plugin-sdk/reply-runtime`, `openclaw/plugin-sdk/reply-dispatch-runtime`, `openclaw/plugin-sdk/reply-reference` і -`openclaw/plugin-sdk/reply-chunking`, коли вам не потрібна ширша umbrella +`openclaw/plugin-sdk/reply-chunking`, коли вам не потрібна ширша загальна поверхня. -Зокрема для налаштування: +Для налаштування зокрема: -- `openclaw/plugin-sdk/setup-runtime` охоплює runtime-safe helpers налаштування: - import-safe setup patch adapters (`createPatchedAccountSetupAdapter`, +- `openclaw/plugin-sdk/setup-runtime` охоплює безпечні для часу виконання допоміжні засоби налаштування: + безпечні для імпорту адаптери патчів налаштування (`createPatchedAccountSetupAdapter`, `createEnvPatchedAccountSetupAdapter`, - `createSetupInputPresenceValidator`), виведення lookup-note, + `createSetupInputPresenceValidator`), вивід lookup-note, `promptResolvedAllowFrom`, `splitSetupEntries` і делеговані - builders setup-proxy -- `openclaw/plugin-sdk/setup-adapter-runtime` — це вузький env-aware adapter - seam для `createEnvPatchedAccountSetupAdapter` -- `openclaw/plugin-sdk/channel-setup` охоплює builders налаштування optional-install - плюс кілька setup-safe primitives: + побудовувачі проксі налаштування +- `openclaw/plugin-sdk/setup-adapter-runtime` — це вузька, обізнана про змінні середовища + точка інтеграції адаптера для `createEnvPatchedAccountSetupAdapter` +- `openclaw/plugin-sdk/channel-setup` охоплює побудовувачі налаштування + необов'язкового встановлення плюс кілька безпечних для налаштування примітивів: `createOptionalChannelSetupSurface`, `createOptionalChannelSetupAdapter`, -Якщо ваш канал підтримує налаштування або auth на основі env і загальні потоки startup/config -мають знати ці env назви до завантаження runtime, оголосіть їх у -маніфесті Plugin через `channelEnvVars`. Тримайте runtime `envVars` каналу або локальні -константи лише для operator-facing copy. +Якщо ваш канал підтримує налаштування або автентифікацію, керовані змінними середовища, і загальні потоки запуску/конфігурації +мають знати ці назви змінних середовища до завантаження часу виконання, оголосіть їх у +маніфесті Plugin через `channelEnvVars`. Тримайте `envVars` часу виконання каналу або локальні +константи лише для текстів, звернених до операторів. Якщо ваш канал може з’являтися в `status`, `channels list`, `channels status` або -скануваннях SecretRef до запуску середовища виконання plugin, додайте `openclaw.setupEntry` у +скануваннях SecretRef до запуску середовища виконання Plugin, додайте `openclaw.setupEntry` у `package.json`. Ця точка входу має бути безпечною для імпорту в шляхах команд -лише для читання й має повертати метадані каналу, безпечний для налаштування адаптер конфігурації, адаптер статусу -та метадані цілі секретів каналу, потрібні для цих зведень. Не -запускайте клієнти, слухачі або транспортні середовища виконання з точки входу налаштування. +лише для читання і має повертати метадані каналу, безпечний для налаштування +адаптер конфігурації, адаптер статусу та метадані цілі секретів каналу, потрібні +для цих зведень. Не запускайте клієнти, слухачі або транспортні середовища +виконання з точки входу налаштування. -Також тримайте шлях імпорту основної точки входу каналу вузьким. Виявлення може оцінити -точку входу й модуль channel plugin, щоб зареєструвати можливості без активації -каналу. Файли на кшталт `channel-plugin-api.ts` мають експортувати об’єкт channel -plugin без імпорту майстрів налаштування, транспортних клієнтів, socket -listeners, засобів запуску підпроцесів або модулів запуску сервісу. Розміщуйте ці runtime -частини в модулях, завантажених із `registerFull(...)`, runtime setters або lazy -capability adapters. +Шлях імпорту головної точки входу каналу також має бути вузьким. Виявлення може +оцінювати точку входу й модуль Plugin каналу, щоб зареєструвати можливості без +активації каналу. Файли на кшталт `channel-plugin-api.ts` мають експортувати +об’єкт Plugin каналу без імпорту майстрів налаштування, транспортних клієнтів, +слухачів сокетів, запускувачів підпроцесів або модулів запуску сервісів. +Розміщуйте ці частини середовища виконання в модулях, що завантажуються з +`registerFull(...)`, сетерах середовища виконання або лінивих адаптерах +можливостей. `createOptionalChannelSetupWizard`, `DEFAULT_ACCOUNT_ID`, -`createTopLevelChannelDmPolicy`, `setSetupChannelEnabled`, і +`createTopLevelChannelDmPolicy`, `setSetupChannelEnabled` і `splitSetupEntries` -- використовуйте ширший seam `openclaw/plugin-sdk/setup` лише тоді, коли вам також потрібні - важчі спільні помічники налаштування/конфігурації, такі як +- використовуйте ширший шов `openclaw/plugin-sdk/setup` лише тоді, коли вам + також потрібні важчі спільні помічники налаштування/конфігурації, як-от `moveSingleAccountChannelSectionToDefaultAccount(...)` -Якщо ваш канал хоче лише показувати "спершу встановіть цей plugin" на поверхнях -налаштування, віддайте перевагу `createOptionalChannelSetupSurface(...)`. Згенерований -адаптер/майстер fail closed під час записів конфігурації та фіналізації, і вони повторно використовують -те саме повідомлення про обов’язкове встановлення у валідації, фіналізації та тексті -посилання на документацію. +Якщо ваш канал лише хоче показувати «спершу встановіть цей Plugin» у поверхнях +налаштування, надайте перевагу `createOptionalChannelSetupSurface(...)`. +Згенерований адаптер/майстер безпечно відмовляється від записів конфігурації та +фіналізації, і вони повторно використовують одне й те саме повідомлення про +потрібне встановлення у валідації, фіналізації та тексті посилання на документацію. -Для інших гарячих шляхів каналів віддавайте перевагу вузьким помічникам замість ширших застарілих -поверхонь: +Для інших гарячих шляхів каналу надавайте перевагу вузьким помічникам над +ширшими застарілими поверхнями: - `openclaw/plugin-sdk/account-core`, `openclaw/plugin-sdk/account-id`, - `openclaw/plugin-sdk/account-resolution`, і - `openclaw/plugin-sdk/account-helpers` для багаторахункової конфігурації та - fallback для стандартного акаунта + `openclaw/plugin-sdk/account-resolution` і + `openclaw/plugin-sdk/account-helpers` для конфігурації кількох облікових + записів і резервного переходу до стандартного облікового запису - `openclaw/plugin-sdk/inbound-envelope` і - `openclaw/plugin-sdk/inbound-reply-dispatch` для inbound route/envelope та - зв’язування record-and-dispatch + `openclaw/plugin-sdk/inbound-reply-dispatch` для вхідного маршруту/конверта та + зв’язування запису й диспетчеризації - `openclaw/plugin-sdk/messaging-targets` для розбору/зіставлення цілей - `openclaw/plugin-sdk/outbound-media` і - `openclaw/plugin-sdk/outbound-runtime` для завантаження медіа плюс outbound - identity/send delegates і планування payload + `openclaw/plugin-sdk/outbound-runtime` для завантаження медіа, а також + вихідних делегатів ідентичності/надсилання та планування корисного + навантаження - `buildThreadAwareOutboundSessionRoute(...)` з - `openclaw/plugin-sdk/channel-core`, коли outbound route має зберігати явний - `replyToId`/`threadId` або відновлювати поточну сесію `:thread:` - після того, як базовий ключ сесії все ще збігається. Provider plugins можуть перевизначати - пріоритет, поведінку суфікса та нормалізацію thread id, коли їхня платформа - має нативну семантику доставки в thread. -- `openclaw/plugin-sdk/thread-bindings-runtime` для життєвого циклу thread-binding - і реєстрації адаптера -- `openclaw/plugin-sdk/agent-media-payload` лише тоді, коли все ще потрібна застаріла розкладка полів agent/media - payload -- `openclaw/plugin-sdk/telegram-command-config` для нормалізації користувацьких команд Telegram, - валідації дублікатів/конфліктів і fallback-stable контракту конфігурації - команд + `openclaw/plugin-sdk/channel-core`, коли вихідний маршрут має зберігати явний + `replyToId`/`threadId` або відновлювати поточну сесію `:thread:` після того, + як базовий ключ сесії все ще збігається. Plugin провайдера можуть + перевизначати пріоритет, поведінку суфіксів і нормалізацію ідентифікаторів + гілок, коли їхня платформа має нативну семантику доставки в гілки. +- `openclaw/plugin-sdk/thread-bindings-runtime` для життєвого циклу прив’язок + гілок і реєстрації адаптерів +- `openclaw/plugin-sdk/agent-media-payload` лише тоді, коли все ще потрібна + застаріла схема полів корисного навантаження агента/медіа +- `openclaw/plugin-sdk/telegram-command-config` для нормалізації користувацьких + команд Telegram, валідації дублікатів/конфліктів і стабільного за резервним + переходом контракту конфігурації команд -Канали лише з автентифікацією зазвичай можуть зупинитися на стандартному шляху: core обробляє approvals, а plugin лише відкриває outbound/auth можливості. Нативні канали approval, такі як Matrix, Slack, Telegram і користувацькі chat transports, мають використовувати спільні нативні помічники замість написання власного життєвого циклу approval. +Канали лише для автентифікації зазвичай можуть зупинитися на стандартному шляху: ядро обробляє схвалення, а Plugin лише надає вихідні/автентифікаційні можливості. Нативні канали схвалення, як-от Matrix, Slack, Telegram і користувацькі чат-транспорти, мають використовувати спільні нативні помічники замість власноручної реалізації життєвого циклу схвалень. -## Політика inbound згадок +## Політика вхідних згадок -Тримайте обробку inbound згадок розділеною на два шари: +Тримайте обробку вхідних згадок розділеною на два рівні: -- збирання доказів, що належить plugin +- збирання доказів, яким володіє Plugin - оцінювання спільної політики -Використовуйте `openclaw/plugin-sdk/channel-mention-gating` для рішень mention-policy. -Використовуйте `openclaw/plugin-sdk/channel-inbound` лише тоді, коли вам потрібен ширший inbound -barrel помічників. +Використовуйте `openclaw/plugin-sdk/channel-mention-gating` для рішень політики +згадок. Використовуйте `openclaw/plugin-sdk/channel-inbound` лише тоді, коли вам +потрібен ширший barrel вхідних помічників. -Добре підходить для plugin-local логіки: +Добре підходить для локальної логіки Plugin: -- виявлення reply-to-bot -- виявлення quoted-bot -- перевірки thread-participation -- виключення service/system-message -- platform-native кеші, потрібні для доведення участі bot +- виявлення відповіді боту +- виявлення процитованого бота +- перевірки участі в гілці +- виключення сервісних/системних повідомлень +- нативні для платформи кеші, потрібні для підтвердження участі бота Добре підходить для спільного помічника: - `requireMention` -- явний результат згадки -- allowlist неявних згадок +- результат явної згадки +- список дозволених неявних згадок - обхід команд -- остаточне рішення про пропуск +- остаточне рішення пропустити -Бажаний потік: +Рекомендований потік: -1. Обчисліть локальні факти згадок. +1. Обчисліть локальні факти згадки. 2. Передайте ці факти в `resolveInboundMentionDecision({ facts, policy })`. -3. Використовуйте `decision.effectiveWasMentioned`, `decision.shouldBypassMention` і `decision.shouldSkip` у вашому inbound gate. +3. Використовуйте `decision.effectiveWasMentioned`, `decision.shouldBypassMention` і `decision.shouldSkip` у вашому вхідному шлюзі. ```typescript import { @@ -293,8 +296,8 @@ const decision = resolveInboundMentionDecision({ if (decision.shouldSkip) return; ``` -`api.runtime.channel.mentions` відкриває ті самі спільні помічники згадок для -вбудованих channel plugins, які вже залежать від runtime injection: +`api.runtime.channel.mentions` надає ті самі спільні помічники згадок для +вбудованих Plugin каналів, які вже залежать від ін’єкції середовища виконання: - `buildMentionRegexes` - `matchesMentionPatterns` @@ -304,21 +307,21 @@ if (decision.shouldSkip) return; Якщо вам потрібні лише `implicitMentionKindWhen` і `resolveInboundMentionDecision`, імпортуйте з -`openclaw/plugin-sdk/channel-mention-gating`, щоб уникнути завантаження не пов’язаних inbound -runtime helpers. +`openclaw/plugin-sdk/channel-mention-gating`, щоб уникнути завантаження +непов’язаних вхідних помічників середовища виконання. Старіші помічники `resolveMentionGating*` залишаються в -`openclaw/plugin-sdk/channel-inbound` лише як експорти сумісності. Новий код -має використовувати `resolveInboundMentionDecision({ facts, policy })`. +`openclaw/plugin-sdk/channel-inbound` лише як експорти сумісності. Новий код має +використовувати `resolveInboundMentionDecision({ facts, policy })`. ## Покроковий огляд - Створіть стандартні файли plugin. Поле `channel` у `package.json` — це - те, що робить це channel plugin. Повну поверхню package-metadata див. - у [Налаштування й конфігурація Plugin](/uk/plugins/sdk-setup#openclaw-channel): + Створіть стандартні файли Plugin. Поле `channel` у `package.json` — це те, + що робить його Plugin каналу. Повну поверхню метаданих пакета див. у + [Налаштування та конфігурація Plugin](/uk/plugins/sdk-setup#openclaw-channel): ```json package.json @@ -375,16 +378,17 @@ runtime helpers. ``` - `configSchema` валідує `plugins.entries.acme-chat.config`. Використовуйте його для - налаштувань, що належать plugin і не є конфігурацією акаунта каналу. `channelConfigs` - валідує `channels.acme-chat` і є cold-path джерелом, яке використовують schema конфігурації, - налаштування та UI поверхні до завантаження runtime plugin. + `configSchema` валідує `plugins.entries.acme-chat.config`. Використовуйте її + для налаштувань, якими володіє Plugin і які не є конфігурацією облікового + запису каналу. `channelConfigs` валідує `channels.acme-chat` і є джерелом + холодного шляху, яке використовують схема конфігурації, налаштування та + UI-поверхні до завантаження середовища виконання Plugin. - - Інтерфейс `ChannelPlugin` має багато необов’язкових поверхонь адаптерів. Почніть із - мінімуму — `id` і `setup` — і додавайте адаптери за потреби. + + Інтерфейс `ChannelPlugin` має багато необов’язкових поверхонь адаптерів. + Почніть із мінімуму — `id` і `setup` — і додавайте адаптери за потреби. Створіть `src/channel.ts`: @@ -479,35 +483,42 @@ runtime helpers. }); ``` - Для каналів, які приймають і канонічні top-level DM keys, і застарілі вкладені ключі, використовуйте помічники з `plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`, `resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` і `normalizeChannelDmPolicy` тримають account-local значення попереду успадкованих root значень. Поєднайте той самий resolver із doctor repair через `normalizeLegacyDmAliases`, щоб runtime і migration читали той самий контракт. + Для каналів, які приймають і канонічні DM-ключі верхнього рівня, і + застарілі вкладені ключі, використовуйте помічники з + `plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`, + `resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` і + `normalizeChannelDmPolicy` зберігають локальні для облікового запису + значення перед успадкованими кореневими значеннями. Поєднайте той самий + резолвер із ремонтом doctor через `normalizeLegacyDmAliases`, щоб + середовище виконання й міграція читали один і той самий контракт. - - Замість того, щоб реалізовувати низькорівневі інтерфейси адаптерів вручну, ви передаєте - декларативні параметри, а builder компонуватиме їх: + + Замість ручної реалізації низькорівневих інтерфейсів адаптерів ви передаєте + декларативні параметри, а builder компонує їх: | Параметр | Що він зв’язує | | --- | --- | - | `security.dm` | Scoped DM security resolver з полів конфігурації | - | `pairing.text` | Текстовий потік DM pairing з обміном кодом | - | `threading` | Resolver режиму reply-to (фіксований, account-scoped або користувацький) | - | `outbound.attachedResults` | Функції надсилання, які повертають result metadata (message IDs) | + | `security.dm` | Обмежений DM-резолвер безпеки з полів конфігурації | + | `pairing.text` | Текстовий потік DM-сполучення з обміном кодом | + | `threading` | Резолвер режиму відповіді (фіксований, scoped за обліковим записом або користувацький) | + | `outbound.attachedResults` | Функції надсилання, що повертають метадані результату (ідентифікатори повідомлень) | - Ви також можете передати необроблені об’єкти адаптерів замість декларативних параметрів, - якщо вам потрібен повний контроль. + Ви також можете передати сирі об’єкти адаптерів замість декларативних + параметрів, якщо вам потрібен повний контроль. - Raw outbound adapters may define a `chunker(text, limit, ctx)` function. - The optional `ctx.formatting` carries delivery-time formatting decisions - such as `maxLinesPerMessage`; apply it before sending so reply threading - and chunk boundaries are resolved once by shared outbound delivery. - Send contexts also include `replyToIdSource` (`implicit` or `explicit`) - when a native reply target was resolved, so payload helpers can preserve - explicit reply tags without consuming an implicit single-use reply slot. + Адаптери сирого вихідного доставлення можуть визначати функцію `chunker(text, limit, ctx)`. + Необов’язкове `ctx.formatting` передає рішення щодо форматування під час доставлення, + як-от `maxLinesPerMessage`; застосовуйте його перед надсиланням, щоб гілкування відповідей + і межі фрагментів один раз визначалися спільним вихідним доставленням. + Контексти надсилання також містять `replyToIdSource` (`implicit` або `explicit`), + коли було визначено нативну ціль відповіді, тож допоміжні функції корисного навантаження можуть зберігати + явні теги відповіді, не витрачаючи неявний одноразовий слот відповіді. - Create `index.ts`: + Створіть `index.ts`: ```typescript index.ts import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core"; @@ -544,12 +555,12 @@ runtime helpers. Розмістіть CLI-дескриптори, що належать каналу, у `registerCliMetadata(...)`, щоб OpenClaw міг показувати їх у кореневій довідці без активації повного runtime каналу, - тоді як звичайні повні завантаження й надалі підхоплюватимуть ті самі дескриптори для справжньої + тоді як звичайні повні завантаження все одно підхоплюватимуть ті самі дескриптори для справжньої реєстрації команд. Залиште `registerFull(...)` для роботи, потрібної лише під час runtime. Якщо `registerFull(...)` реєструє RPC-методи Gateway, використовуйте - префікс, специфічний для Plugin. Основні адміністративні простори імен (`config.*`, + префікс, специфічний для Plugin. Простори імен адміністрування ядра (`config.*`, `exec.approvals.*`, `wizard.*`, `update.*`) залишаються зарезервованими й завжди - розв’язуються як `operator.admin`. + розв’язуються в `operator.admin`. `defineChannelPluginEntry` автоматично обробляє розділення режимів реєстрації. Див. [Точки входу](/uk/plugins/sdk-entrypoints#definechannelpluginentry) для всіх параметрів. @@ -557,7 +568,7 @@ runtime helpers. - Створіть `setup-entry.ts` для легкого завантаження під час onboarding: + Створіть `setup-entry.ts` для легкого завантаження під час онбордингу: ```typescript setup-entry.ts import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core"; @@ -567,20 +578,20 @@ runtime helpers. ``` OpenClaw завантажує це замість повної точки входу, коли канал вимкнений - або не налаштований. Це дає змогу не підтягувати важкий runtime-код під час потоків налаштування. - Докладніше див. [Налаштування та конфігурація](/uk/plugins/sdk-setup#setup-entry). + або не налаштований. Це запобігає підтягуванню важкого runtime-коду під час потоків налаштування. + Докладніше див. [Налаштування й конфігурація](/uk/plugins/sdk-setup#setup-entry). - Канали bundled workspace, які виносять setup-safe експорти в sidecar + Канали з комплектного робочого простору, які виокремлюють безпечні для налаштування експорти в допоміжні модулі, можуть використовувати `defineBundledChannelSetupEntry(...)` з `openclaw/plugin-sdk/channel-entry-contract`, коли їм також потрібен - явний setter runtime під час setup. + явний runtime-сетер на етапі налаштування. - Ваш Plugin має отримувати повідомлення з платформи й пересилати їх до - OpenClaw. Типовий патерн — Webhook, який перевіряє запит і - передає його через inbound-обробник вашого каналу: + Ваш Plugin має отримувати повідомлення з платформи та пересилати їх до + OpenClaw. Типовий шаблон — це Webhook, який перевіряє запит і + передає його через вхідний обробник вашого каналу: ```typescript registerFull(api) { @@ -604,16 +615,16 @@ runtime helpers. ``` - Обробка inbound-повідомлень є специфічною для каналу. Кожен channel Plugin володіє - власним inbound pipeline. Перегляньте bundled channel Plugins - (наприклад, пакет Plugin для Microsoft Teams або Google Chat) для реальних патернів. + Обробка вхідних повідомлень залежить від каналу. Кожен канальний Plugin володіє + власним вхідним конвеєром. Перегляньте комплектні канальні Plugins + (наприклад, пакет Plugin для Microsoft Teams або Google Chat), щоб побачити реальні шаблони. -Пишіть colocated тести в `src/channel.test.ts`: +Пишіть колоковані тести в `src/channel.test.ts`: ```typescript src/channel.test.ts import { describe, it, expect } from "vitest"; @@ -651,7 +662,7 @@ runtime helpers. pnpm test -- /acme-chat/ ``` - Для спільних test helpers див. [Тестування](/uk/plugins/sdk-testing). + Про спільні допоміжні засоби тестування див. [Тестування](/uk/plugins/sdk-testing). @@ -677,7 +688,7 @@ runtime helpers. - Фіксовані, account-scoped або користувацькі режими відповіді + Фіксовані, прив’язані до облікового запису або користувацькі режими відповіді describeMessageTool і виявлення дій @@ -686,26 +697,29 @@ runtime helpers. inferTargetChatType, looksLikeId, resolveTarget - TTS, STT, медіа, subagent через api.runtime + TTS, STT, медіа, підагент через api.runtime + + + Спільний життєвий цикл вхідного ходу: приймання, розв’язання, запис, передавання, завершення -Деякі bundled helper seams досі існують для супроводу bundled-plugin і -сумісності. Вони не є рекомендованим патерном для нових channel Plugins; -віддавайте перевагу generic channel/setup/reply/runtime subpaths зі спільної поверхні SDK, -якщо ви не супроводжуєте цю сім’ю bundled Plugins безпосередньо. +Деякі комплектні допоміжні шви все ще існують для обслуговування комплектних Plugins і +сумісності. Вони не є рекомендованим шаблоном для нових канальних Plugins; +віддавайте перевагу загальним підшляхам channel/setup/reply/runtime зі спільної поверхні SDK, +якщо ви не підтримуєте цю родину комплектних Plugins напряму. ## Наступні кроки - [Provider Plugins](/uk/plugins/sdk-provider-plugins) — якщо ваш Plugin також надає моделі -- [Огляд SDK](/uk/plugins/sdk-overview) — повний довідник імпортів subpath -- [Тестування SDK](/uk/plugins/sdk-testing) — тестові утиліти й contract tests +- [Огляд SDK](/uk/plugins/sdk-overview) — повна довідка щодо імпорту підшляхів +- [Тестування SDK](/uk/plugins/sdk-testing) — тестові утиліти та контрактні тести - [Маніфест Plugin](/uk/plugins/manifest) — повна схема маніфесту ## Пов’язане - [Налаштування Plugin SDK](/uk/plugins/sdk-setup) - [Створення Plugins](/uk/plugins/building-plugins) -- [Agent harness Plugins](/uk/plugins/sdk-agent-harness) +- [Plugins агентного стенда](/uk/plugins/sdk-agent-harness) diff --git a/docs/uk/plugins/sdk-channel-turn.md b/docs/uk/plugins/sdk-channel-turn.md new file mode 100644 index 000000000..71283bede --- /dev/null +++ b/docs/uk/plugins/sdk-channel-turn.md @@ -0,0 +1,400 @@ +--- +read_when: + - Ви створюєте Plugin каналу й хочете використовувати спільний життєвий цикл вхідного кроку взаємодії + - Ви переносите монітор каналу з самописного коду-зв’язки для запису/диспетчеризації + - Потрібно розуміти етапи допуску, приймання, класифікації, попередньої перевірки, розв’язання, запису, диспетчеризації та завершення +sidebarTitle: Channel turn +summary: runtime.channel.turn -- спільне ядро вхідних ходів, яке вбудовані та сторонні плагіни каналів використовують для запису, диспетчеризації та фіналізації ходів агента +title: Ядро ходу каналу +x-i18n: + generated_at: "2026-04-30T00:42:46Z" + model: gpt-5.5 + provider: openai + source_hash: dc918da4c43f955f509aed18a93129db26efe21686c30f9328a5639f3e700984 + source_path: plugins/sdk-channel-turn.md + workflow: 16 +--- + +Ядро обробки ходу каналу — це спільна вхідна машина станів, яка перетворює нормалізовану подію платформи на хід агента. Плагіни каналів надають факти платформи й callback доставлення. Core володіє оркестрацією: приймання, класифікація, попередня перевірка, розв’язання, авторизація, складання, запис, dispatch і фіналізація. + +Використовуйте це, коли ваш плагін перебуває на гарячому шляху вхідного повідомлення. Для подій, що не є повідомленнями (slash-команди, модальні вікна, взаємодії з кнопками, події життєвого циклу, реакції, стан голосу), залишайте їх локальними для плагіна. Ядро володіє лише подіями, які можуть стати текстовим ходом агента. + + + До ядра звертаються через інʼєктований runtime плагіна як `runtime.channel.turn.*`. Тип runtime плагіна експортується з `openclaw/plugin-sdk/core`, тож сторонні нативні плагіни можуть використовувати ці точки входу так само, як це роблять bundled-плагіни каналів. + + +## Навіщо спільне ядро + +Плагіни каналів повторюють той самий вхідний потік: нормалізувати, маршрутизувати, пропустити через gates, побудувати контекст, записати метадані сесії, dispatch ходу агента, фіналізувати стан доставлення. Без спільного ядра зміну в gating згадок, видимих відповідях лише для інструментів, метаданих сесії, pending-історії або фіналізації dispatch потрібно застосовувати окремо для кожного каналу. + +Ядро навмисно тримає чотири поняття окремими: + +- `ConversationFacts`: звідки надійшло повідомлення +- `RouteFacts`: який агент і яка сесія мають його обробити +- `ReplyPlanFacts`: куди мають іти видимі відповіді +- `MessageFacts`: яке тіло й додатковий контекст має бачити агент + +Slack DM, теми Telegram, потоки Matrix і topic-сесії Feishu на практиці розрізняють усе це. Якщо трактувати їх як один ідентифікатор, з часом виникає drift. + +## Життєвий цикл етапів + +Ядро виконує той самий фіксований pipeline незалежно від каналу: + +1. `ingest` -- адаптер перетворює raw-подію платформи на `NormalizedTurnInput` +2. `classify` -- адаптер оголошує, чи може ця подія почати хід агента +3. `preflight` -- адаптер виконує dedupe, self-echo, hydration, debounce, decryption, часткове попереднє заповнення фактів +4. `resolve` -- адаптер повертає повністю зібраний хід (route, reply plan, message, delivery) +5. `authorize` -- політика DM, group, mention і command застосовується до зібраних фактів +6. `assemble` -- `FinalizedMsgContext` будується з фактів через `buildContext` +7. `record` -- зберігаються метадані вхідної сесії та останній route +8. `dispatch` -- хід агента виконується через buffered block dispatcher +9. `finalize` -- adapter `onFinalize` виконується навіть у разі помилки dispatch + +Кожен етап emits структуровану подію журналу, коли надано callback `log`. Див. [Спостережуваність](#observability). + +## Види допуску + +Ядро не кидає помилку, коли хід відхиляється gate. Воно повертає `ChannelTurnAdmission`: + +| Вид | Коли | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `dispatch` | Хід допущено. Хід агента запускається, і шлях видимої відповіді виконується. | +| `observeOnly` | Хід виконується end-to-end, але delivery-адаптер не надсилає нічого видимого. Використовується для broadcast observer-агентів та інших пасивних multi-agent потоків. | +| `handled` | Подію платформи було оброблено локально (життєвий цикл, реакція, кнопка, модальне вікно). Ядро пропускає dispatch. | +| `drop` | Шлях пропуску. Опційно `recordHistory: true` зберігає повідомлення в pending group history, щоб майбутня згадка мала контекст. | + +Допуск може надходити з `classify` (клас події сказав, що вона не може почати хід), з `preflight` (dedupe, self-echo, відсутня згадка із записом історії) або безпосередньо з `resolveTurn`. + +## Точки входу + +Runtime надає три бажані точки входу, щоб адаптери могли підключатися на рівні, який відповідає каналу. + +```typescript +runtime.channel.turn.run(...) // adapter-driven full pipeline +runtime.channel.turn.runPrepared(...) // channel owns dispatch; kernel runs record + finalize +runtime.channel.turn.buildContext(...) // pure facts to FinalizedMsgContext mapping +``` + +Два старіші runtime helpers залишаються доступними для сумісності з Plugin SDK: + +```typescript +runtime.channel.turn.runResolved(...) // deprecated compatibility alias; prefer run +runtime.channel.turn.dispatchAssembled(...) // deprecated compatibility alias; prefer run or runPrepared +``` + +### run + +Використовуйте, коли ваш канал може виразити свій вхідний потік як `ChannelTurnAdapter`. Адаптер має callbacks для `ingest`, необовʼязкового `classify`, необовʼязкового `preflight`, обовʼязкового `resolveTurn` і необовʼязкового `onFinalize`. + +```typescript +await runtime.channel.turn.run({ + channel: "tlon", + accountId, + raw: platformEvent, + adapter: { + ingest(raw) { + return { + id: raw.messageId, + timestamp: raw.timestamp, + rawText: raw.body, + textForAgent: raw.body, + }; + }, + classify(input) { + return { kind: "message", canStartAgentTurn: input.rawText.length > 0 }; + }, + async preflight(input, eventClass) { + if (await isDuplicate(input.id)) { + return { admission: { kind: "drop", reason: "dedupe" } }; + } + return {}; + }, + resolveTurn(input) { + return buildAssembledTurn(input); + }, + onFinalize(result) { + clearPendingGroupHistory(result); + }, + }, +}); +``` + +`run` — правильна форма, коли канал має невелику логіку адаптера й виграє від володіння життєвим циклом через hooks. + +### runPrepared + +Використовуйте, коли канал має складний локальний dispatcher із previews, retries, edits або thread bootstrap, які мають залишатися у власності каналу. Ядро все одно записує вхідну сесію перед dispatch і повертає уніфікований `DispatchedChannelTurnResult`. + +```typescript +const { dispatchResult } = await runtime.channel.turn.runPrepared({ + channel: "matrix", + accountId, + routeSessionKey, + storePath, + ctxPayload, + recordInboundSession, + record: { + onRecordError, + updateLastRoute, + }, + onPreDispatchFailure: async (err) => { + await stopStatusReactions(); + }, + runDispatch: async () => { + return await runMatrixOwnedDispatcher(); + }, +}); +``` + +Rich channels (Matrix, Mattermost, Microsoft Teams, Feishu, QQ Bot) використовують `runPrepared`, тому що їхній dispatcher оркеструє platform-specific поведінку, про яку ядро не повинно знати. + +### buildContext + +Чиста функція, яка відображає bundles фактів у `FinalizedMsgContext`. Використовуйте її, коли ваш канал hand-rolls частину pipeline, але хоче узгоджену форму контексту. + +```typescript +const ctxPayload = runtime.channel.turn.buildContext({ + channel: "googlechat", + accountId, + messageId, + timestamp, + from, + sender, + conversation, + route, + reply, + message, + access, + media, + supplemental, +}); +``` + +`buildContext` також корисний усередині callbacks `resolveTurn` під час складання ходу для `run`. + + + Застарілі SDK helpers, як-от `dispatchInboundReplyWithBase`, досі bridge through assembled-turn helper. Новий код плагіна має використовувати `run` або `runPrepared`. + + +## Типи фактів + +Факти, які ядро споживає з вашого адаптера, не залежать від платформи. Перекладайте обʼєкти платформи в ці форми, перш ніж передавати їх ядру. + +### NormalizedTurnInput + +| Поле | Призначення | +| ---------------- | ---------------------------------------------------------------------------- | +| `id` | Стабільний message id, що використовується для dedupe і журналів | +| `timestamp` | Необовʼязкові epoch ms | +| `rawText` | Тіло в тому вигляді, у якому його отримано від платформи | +| `textForAgent` | Необовʼязкове очищене тіло для агента (mention strip, typing trim) | +| `textForCommands` | Необовʼязкове тіло, що використовується для парсингу `/command` | +| `raw` | Необовʼязкове pass-through посилання для callbacks адаптера, яким потрібен оригінал | + +### ChannelEventClass + +| Поле | Призначення | +| ---------------------- | ---------------------------------------------------------------------- | +| `kind` | `message`, `command`, `interaction`, `reaction`, `lifecycle`, `unknown` | +| `canStartAgentTurn` | Якщо false, ядро повертає `{ kind: "handled" }` | +| `requiresImmediateAck` | Підказка для адаптерів, яким потрібно ACK перед dispatch | + +### SenderFacts + +| Поле | Призначення | +| ------------- | ---------------------------------------------------------------------- | +| `id` | Стабільний sender id платформи | +| `name` | Відображуване імʼя | +| `username` | Handle, якщо відрізняється від `name` | +| `tag` | Дискримінатор у стилі Discord або тег платформи | +| `roles` | Role ids, що використовуються для зіставлення member-role allowlist | +| `isBot` | True, коли відправник є відомим ботом (ядро використовує для dropping) | +| `isSelf` | True, коли відправник є самим налаштованим агентом | +| `displayLabel` | Попередньо відрендерений label для envelope-тексту | + +### ConversationFacts + +| Поле | Призначення | +| ----------------- | ---------------------------------------------------------------------- | +| `kind` | `direct`, `group` або `channel` | +| `id` | Conversation id, що використовується для routing | +| `label` | Людський label для envelope | +| `spaceId` | Необовʼязковий outer space identifier (Slack workspace, Matrix homeserver) | +| `parentId` | Outer conversation id, коли це thread | +| `threadId` | Thread id, коли це повідомлення всередині thread | +| `nativeChannelId` | Platform-native channel id, коли відрізняється від routing id | +| `routePeer` | Peer, що використовується для lookup `resolveAgentRoute` | + +### RouteFacts + +| Поле | Призначення | +| ----------------------- | -------------------------------------------------------------- | +| `agentId` | Агент, який має обробити цей хід | +| `accountId` | Необовʼязковий override (multi-account channels) | +| `routeSessionKey` | Session key, що використовується для routing | +| `dispatchSessionKey` | Session key, що використовується під час dispatch, коли відрізняється від route key | +| `persistedSessionKey` | Session key, що записується в persisted session metadata | +| `parentSessionKey` | Parent для branched/threaded sessions | +| `modelParentSessionKey` | Model-side parent для branched sessions | +| `mainSessionKey` | Main DM owner pin для direct conversations | +| `createIfMissing` | Дозволити етапу record створити відсутній session row | + +### ReplyPlanFacts + +| Поле | Призначення | +| ------------------------- | -------------------------------------------------------------- | +| `to` | Логічна ціль відповіді, записана в контекст `To` | +| `originatingTo` | Початкова ціль контексту (`OriginatingTo`) | +| `nativeChannelId` | Нативний для платформи ідентифікатор каналу для доставлення | +| `replyTarget` | Кінцеве місце призначення видимої відповіді, якщо воно відрізняється від `to` | +| `deliveryTarget` | Нижчорівневе перевизначення доставлення | +| `replyToId` | Ідентифікатор процитованого/прив’язаного повідомлення | +| `replyToIdFull` | Повна форма процитованого ідентифікатора, коли платформа має обидві | +| `messageThreadId` | Ідентифікатор треду на момент доставлення | +| `threadParentId` | Ідентифікатор батьківського повідомлення треду | +| `sourceReplyDeliveryMode` | `thread`, `reply`, `channel`, `direct` або `none` | + +### AccessFacts + +`AccessFacts` містить булеві значення, потрібні етапу авторизації. Зіставлення ідентичності залишається в каналі: ядро лише споживає результат. + +| Поле | Призначення | +| ---------- | ------------------------------------------------------------------------- | +| `dm` | Рішення дозволити/спарувати/заборонити DM і список `allowFrom` | +| `group` | Політика групи, дозвіл маршруту, дозвіл відправника, allowlist, вимога згадки | +| `commands` | Авторизація команд у налаштованих авторизаторах | +| `mentions` | Чи можливе виявлення згадки та чи було згадано агента | + +### MessageFacts + +| Поле | Призначення | +| ---------------- | -------------------------------------------------------------- | +| `body` | Кінцеве тіло конверта (відформатоване) | +| `rawBody` | Сире вхідне тіло | +| `bodyForAgent` | Тіло, яке бачить агент | +| `commandBody` | Тіло, використане для розбору команд | +| `envelopeFrom` | Попередньо відрендерена мітка відправника для конверта | +| `senderLabel` | Необов’язкове перевизначення для відрендереного відправника | +| `preview` | Короткий редагований попередній перегляд для журналів | +| `inboundHistory` | Останні записи вхідної історії, коли канал зберігає буфер | + +### SupplementalContextFacts + +Додатковий контекст охоплює контекст цитати, пересланого повідомлення та початкового завантаження треду. Ядро застосовує налаштовану політику `contextVisibility`. Адаптер каналу надає лише факти та прапорці `senderAllowed`, щоб політика між каналами залишалася узгодженою. + +### InboundMediaFacts + +Медіа мають форму фактів. Завантаження з платформи, авторизація, політика SSRF, правила CDN і розшифрування залишаються локальними для каналу. Ядро перетворює факти на `MediaPath`, `MediaUrl`, `MediaType`, `MediaPaths`, `MediaUrls`, `MediaTypes` і `MediaTranscribedIndexes`. + +## Контракт адаптера + +Для повного `run` форма адаптера така: + +```typescript +type ChannelTurnAdapter = { + ingest(raw: TRaw): Promise | NormalizedTurnInput | null; + classify?(input: NormalizedTurnInput): Promise | ChannelEventClass; + preflight?( + input: NormalizedTurnInput, + eventClass: ChannelEventClass, + ): Promise; + resolveTurn( + input: NormalizedTurnInput, + eventClass: ChannelEventClass, + preflight: PreflightFacts, + ): Promise | ChannelTurnResolved; + onFinalize?(result: ChannelTurnResult): Promise | void; +}; +``` + +`resolveTurn` повертає `ChannelTurnResolved`, тобто `AssembledChannelTurn` з необов’язковим типом допуску. Повернення `{ admission: { kind: "observeOnly" } }` запускає хід без створення видимого виводу. Адаптер усе ще володіє callback доставлення; для цього ходу він просто стає no-op. + +`onFinalize` виконується для кожного результату, включно з помилками диспетчеризації. Використовуйте його, щоб очищати очікувану групову історію, прибирати реакції підтвердження, зупиняти індикатори стану та скидати локальний стан. + +## Адаптер доставлення + +Ядро не викликає платформу напряму. Канал передає ядру `ChannelTurnDeliveryAdapter`: + +```typescript +type ChannelTurnDeliveryAdapter = { + deliver(payload: ReplyPayload, info: ChannelDeliveryInfo): Promise; + onError?(err: unknown, info: { kind: string }): void; +}; + +type ChannelDeliveryResult = { + messageIds?: string[]; + threadId?: string; + replyToId?: string; + visibleReplySent?: boolean; +}; +``` + +`deliver` викликається один раз для кожного буферизованого фрагмента відповіді. Повертайте ідентифікатори повідомлень платформи, коли канал їх має, щоб диспетчер міг зберігати прив’язки тредів і редагувати пізніші фрагменти. Для ходів лише зі спостереженням повертайте `{ visibleReplySent: false }` або використовуйте `createNoopChannelTurnDeliveryAdapter()`. + +## Параметри запису + +Етап запису обгортає `recordInboundSession`. Більшість каналів можуть використовувати типові значення. Перевизначайте через `record`: + +```typescript +record: { + groupResolution, + createIfMissing: true, + updateLastRoute, + onRecordError: (err) => log.warn("record failed", err), + trackSessionMetaTask: (task) => pendingTasks.push(task), +} +``` + +Диспетчер очікує завершення етапу запису. Якщо запис викидає помилку, ядро запускає `onPreDispatchFailure` (коли його передано до `runPrepared`) і повторно викидає помилку. + +## Спостережуваність + +Кожен етап випромінює структуровану подію, коли надано callback `log`: + +```typescript +await runtime.channel.turn.run({ + channel: "twitch", + accountId, + raw, + adapter, + log: (event) => { + runtime.log?.debug?.(`turn.${event.stage}:${event.event}`, { + channel: event.channel, + accountId: event.accountId, + messageId: event.messageId, + sessionKey: event.sessionKey, + admission: event.admission, + reason: event.reason, + }); + }, +}); +``` + +Етапи журналювання: `ingest`, `classify`, `preflight`, `resolve`, `authorize`, `assemble`, `record`, `dispatch`, `finalize`. Уникайте журналювання сирих тіл; використовуйте `MessageFacts.preview` для коротких редагованих попередніх переглядів. + +## Що залишається локальним для каналу + +Ядро володіє оркестрацією. Канал усе ще володіє: + +- Транспортами платформи (gateway, REST, websocket, polling, webhooks) +- Розв’язанням ідентичності та зіставленням відображуваних імен +- Нативними командами, slash-командами, автодоповненням, модальними вікнами, кнопками, голосовим станом +- Рендерингом карток, модальних вікон і adaptive-card +- Авторизацією медіа, правилами CDN, зашифрованими медіа, транскрипцією +- API редагування, реакцій, редагування/приховування та присутності +- Backfill і отриманням історії на боці платформи +- Потоками спарування, які потребують специфічної для платформи перевірки + +Якщо двом каналам починає бути потрібен той самий helper для одного з цих пунктів, винесіть спільний SDK helper замість проштовхування його в ядро. + +## Стабільність + +`runtime.channel.turn.*` є частиною публічної поверхні runtime Plugin. Типи фактів (`SenderFacts`, `ConversationFacts`, `RouteFacts`, `ReplyPlanFacts`, `AccessFacts`, `MessageFacts`, `SupplementalContextFacts`, `InboundMediaFacts`) і форми допуску (`ChannelTurnAdmission`, `ChannelEventClass`) доступні через `PluginRuntime` з `openclaw/plugin-sdk/core`. + +Застосовуються правила зворотної сумісності: нові поля фактів є додатковими, типи допуску не перейменовуються, а назви точок входу залишаються стабільними. Нові потреби каналу, які вимагають неадитивної зміни, мають проходити через процес міграції SDK Plugin. + +## Пов’язане + +- [Створення plugins каналів](/uk/plugins/sdk-channel-plugins) для ширшого контракту plugin каналу +- [Runtime helpers Plugin](/uk/plugins/sdk-runtime) для інших поверхонь `runtime.*` +- [Внутрішня архітектура Plugin](/uk/plugins/architecture-internals) для конвеєра завантаження та механіки реєстру