chore(i18n): refresh uk translations

This commit is contained in:
openclaw-docs-i18n[bot] 2026-04-30 00:45:15 +00:00
parent cfc114e1f0
commit 5b55128fff
3 changed files with 842 additions and 432 deletions

View File

@ -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
```
<Note>
Teams CLI зараз перебуває в preview. Команди та прапорці можуть змінюватися між випусками.
Teams CLI наразі перебуває в preview. Команди й прапорці можуть змінюватися між випусками.
</Note>
**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
```
<Note>
`--allow-anonymous` потрібен, бо Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK.
`--allow-anonymous` потрібен, оскільки Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK.
</Note>
Альтернативи: `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 <teamsAppId> --install-link
@ -123,23 +119,23 @@ teams app get <teamsAppId> --install-link
teams app doctor <teamsAppId>
```
Це запускає діагностику реєстрації бота, конфігурації застосунку 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.
<Note>
Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якому учаснику (з вимогою згадки).
Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якого учасника (з обмеженням за згадкою).
</Note>
## Цілі
- Спілкуватися з 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 <teamsAppId>
**Доступ до 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 <teamsAppId>
**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 <teamsAppId>
<details>
<summary><strong>Ручне налаштування (без Teams CLI)</strong></summary>
Якщо ви не можете використовувати Teams CLI, ви можете налаштувати бота вручну через Azure Portal.
Якщо ви не можете використовувати Teams CLI, можете налаштувати бота вручну через Azure Portal.
### Як це працює
@ -217,25 +213,25 @@ teams app doctor <teamsAppId>
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** |
<Warning>
Створення нових мультитенантних ботів застаріло після 2025-07-31. Використовуйте **Single Tenant** для нових ботів.
Створення нових multi-tenant ботів було застарілим після 2025-07-31. Використовуйте **Single Tenant** для нових ботів.
</Warning>
3. Натисніть **Review + create****Create** (зачекайте ~1-2 хвилини)
@ -253,7 +249,7 @@ teams app doctor <teamsAppId>
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 <teamsAppId>
### Крок 5: Зберіть маніфест застосунку Teams
- Додайте запис `bot` з `botId = <App ID>`.
- Додайте запис `bot` із `botId = <App ID>`.
- 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 <teamsAppId>
</details>
## Федеративна автентифікація (сертифікат плюс керована ідентичність)
## Федеративна автентифікація (сертифікат плюс 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 <teamsAppId>
}
```
**Змінні середовища:**
**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 <teamsAppId>
}
```
**Конфігурація (керована ідентичність, призначена користувачем):**
**Конфігурація (призначена користувачем керована ідентичність):**
```json5
{
@ -389,12 +385,12 @@ teams app doctor <teamsAppId>
- `MSTEAMS_USE_MANAGED_IDENTITY=true`
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>` (лише для призначеної користувачем)
### Налаштування AKS Workload Identity
### Налаштування ідентичності робочого навантаження AKS
Для розгортань AKS, що використовують ідентичність робочого навантаження:
1. **Увімкніть ідентичність робочого навантаження** у вашому кластері AKS.
2. **Створіть облікові дані федеративної ідентичності** для реєстрації застосунку Entra ID:
2. **Створіть облікові дані федеративної ідентичності** у реєстрації застосунку Entra ID:
```bash
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
@ -405,7 +401,7 @@ teams app doctor <teamsAppId>
}'
```
3. **Додайте анотацію до сервісного облікового запису Kubernetes** з клієнтським ID застосунку:
3. **Додайте анотацію до сервісного облікового запису Kubernetes** з ID клієнта застосунку:
```yaml
apiVersion: v1
@ -416,7 +412,7 @@ teams app doctor <teamsAppId>
azure.workload.identity/client-id: "<APP_CLIENT_ID>"
```
4. **Додайте мітку до pod** для ін’єкції ідентичності робочого навантаження:
4. **Додайте мітку до пода** для інʼєкції ідентичності робочого навантаження:
```yaml
metadata:
@ -424,17 +420,17 @@ teams app doctor <teamsAppId>
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 <teamsAppId> --endpoint "https://<new-url>/api/messages"
@ -470,49 +466,49 @@ teams app doctor <teamsAppId>
**Надішліть тестове повідомлення:**
1. Установіть застосунок Teams (скористайтеся посиланням для встановлення з `teams app get <id> --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["<user_id>"].historyLimit`.
- `channels.msteams.historyLimit` керує тим, скільки останніх повідомлень каналу/групи загортається в запит.
- Має резервне значення `messages.groupChat.historyLimit`. Установіть `0`, щоб вимкнути (за замовчуванням 50).
- Отримана історія треду фільтрується за списками дозволених відправників (`allowFrom` / `groupAllowFrom`), тому початкове наповнення контексту треду містить лише повідомлення від дозволених відправників.
- Контекст цитованих вкладень (отриманий із HTML-відповіді Teams `ReplyTo*`) наразі передається як отримано.
- Іншими словами, списки дозволених визначають, хто може запускати агента; сьогодні фільтруються лише окремі шляхи додаткового контексту.
- Історію DM можна обмежити за допомогою `channels.msteams.dmHistoryLimit` (ходи користувача). Перевизначення для окремих користувачів: `channels.msteams.dms["<user_id>"].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 <teamsAppId> ChannelMessage.Read.Group --type Application
## Приклад маніфесту Teams (відредаговано)
Мінімальний дійсний приклад із потрібними полями. Замініть ID та URL-адреси.
Мінімальний, чинний приклад із потрібними полями. Замініть ID та URL-адреси.
```json5
{
@ -580,17 +576,17 @@ teams app rsc add <teamsAppId> 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 <teamsAppId>
Після оновлення перевстановіть застосунок у кожній команді, щоб нові дозволи набули чинності, і **повністю закрийте та перезапустіть Teams** (не просто закрийте вікно), щоб очистити кешовані метадані застосунку.
<details>
<summary>Оновлення маніфесту вручну (без CLI)</summary>
<summary>Ручне оновлення маніфесту (без CLI)</summary>
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 <teamsAppId>
- Читання **текстового** вмісту повідомлень каналу.
- Надсилання **текстового** вмісту повідомлень каналу.
- Отримання файлових вкладень у **особистих (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.<teamId>.replyStyle`: перевизначення для окремої команди.
- `channels.msteams.teams.<teamId>.requireMention`: перевизначення для окремої команди.
- `channels.msteams.teams.<teamId>.tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), які використовуються, коли перевизначення каналу відсутнє.
- `channels.msteams.teams.<teamId>.tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), що використовуються, коли немає перевизначення для каналу.
- `channels.msteams.teams.<teamId>.toolsBySender`: стандартні перевизначення політики інструментів для окремої команди й окремого відправника (підтримується wildcard `"*"`).
- `channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle`: перевизначення для окремого каналу.
- `channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention`: перевизначення для окремого каналу.
@ -709,35 +705,35 @@ Markdown у Teams обмеженіший, ніж у Slack або Discord:
- `channels.msteams.teams.<teamId>.channels.<conversationId>.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:<agentId>:<mainKey>`).
- Ключі сесій відповідають стандартному формату агента (див. [/concepts/session](/uk/concepts/session)):
- Прямі повідомлення спільно використовують основну сесію (`agent:<agentId>:<mainKey>`).
- Повідомлення каналів/груп використовують conversation id:
- `agent:<agentId>:msteams:channel:<conversationId>`
- `agent:<agentId>:msteams:group:<conversationId>`
## Стиль відповіді: гілки проти дописів
## Стиль відповіді: треди проти дописів
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:<id> ...`
- Голоси записуються 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:<aad-object-id>` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |
| Користувач (за іменем) | `user:<display-name>` | `user:John Smith` (потрібен Graph API) |
@ -934,24 +930,24 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
```
<Note>
Без префікса `user:` імена за замовчуванням розпізнаються як групи або команди. Завжди використовуйте `user:`, коли націлюєтеся на людей за відображуваним іменем.
Без префікса `user:` імена за замовчуванням обробляються як групи або команди. Завжди використовуйте `user:`, коли вказуєте людей за відображуваним іменем.
</Note>
## Проактивний обмін повідомленнями
## Проактивні повідомлення
- Проактивні повідомлення можливі лише **після** взаємодії користувача, оскільки в цей момент ми зберігаємо посилання на розмову.
- Див. `/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) — модель доступу та посилення захисту

View File

@ -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,
спарюванням, ланцюжками відповідей і вихідними повідомленнями.
сполученням, гілкуванням відповідей і вихідними повідомленнями.
<Info>
Якщо ви ще не створювали жодного Plugin OpenClaw, спершу прочитайте
[Початок роботи](/uk/plugins/building-plugins), щоб дізнатися про базову структуру
пакета та налаштування маніфесту.
Якщо ви раніше не створювали жодного Plugin OpenClaw, спершу прочитайте
[Початок роботи](/uk/plugins/building-plugins), щоб дізнатися базову структуру
пакета й налаштування маніфесту.
</Info>
## Як працюють 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.<channel>.accounts.<id>.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.<channel>.accounts.<id>.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 })`.
## Покроковий огляд
<Steps>
<a id="step-1-package-and-manifest"></a>
<Step title="Пакет і маніфест">
Створіть стандартні файли 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):
<CodeGroup>
```json package.json
@ -375,16 +378,17 @@ runtime helpers.
```
</CodeGroup>
`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.
</Step>
<Step title="Побудуйте об’єкт channel plugin">
Інтерфейс `ChannelPlugin` має багато необов’язкових поверхонь адаптерів. Почніть із
мінімуму — `id` і `setup`і додавайте адаптери за потреби.
<Step title="Створіть об’єкт Plugin каналу">
Інтерфейс `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`, щоб
середовище виконання й міграція читали один і той самий контракт.
<Accordion title="Що createChatChannelPlugin робить за вас">
Замість того, щоб реалізовувати низькорівневі інтерфейси адаптерів вручну, ви передаєте
декларативні параметри, а builder компонуватиме їх:
<Accordion title="Що createChatChannelPlugin робить для вас">
Замість ручної реалізації низькорівневих інтерфейсів адаптерів ви передаєте
декларативні параметри, а 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`),
коли було визначено нативну ціль відповіді, тож допоміжні функції корисного навантаження можуть зберігати
явні теги відповіді, не витрачаючи неявний одноразовий слот відповіді.
</Accordion>
</Step>
<Step title="Wire the entry point">
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.
</Step>
<Step title="Add a setup entry">
Створіть `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-сетер на етапі налаштування.
</Step>
<Step title="Handle inbound messages">
Ваш Plugin має отримувати повідомлення з платформи й пересилати їх до
OpenClaw. Типовий патерн — Webhook, який перевіряє запит і
передає його через inbound-обробник вашого каналу:
Ваш Plugin має отримувати повідомлення з платформи та пересилати їх до
OpenClaw. Типовий шаблон — це Webhook, який перевіряє запит і
передає його через вхідний обробник вашого каналу:
```typescript
registerFull(api) {
@ -604,16 +615,16 @@ runtime helpers.
```
<Note>
Обробка inbound-повідомлень є специфічною для каналу. Кожен channel Plugin володіє
власним inbound pipeline. Перегляньте bundled channel Plugins
(наприклад, пакет Plugin для Microsoft Teams або Google Chat) для реальних патернів.
Обробка вхідних повідомлень залежить від каналу. Кожен канальний Plugin володіє
власним вхідним конвеєром. Перегляньте комплектні канальні Plugins
(наприклад, пакет Plugin для Microsoft Teams або Google Chat), щоб побачити реальні шаблони.
</Note>
</Step>
<a id="step-6-test"></a>
<Step title="Test">
Пишіть 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 -- <bundled-plugin-root>/acme-chat/
```
Для спільних test helpers див. [Тестування](/uk/plugins/sdk-testing).
Про спільні допоміжні засоби тестування див. [Тестування](/uk/plugins/sdk-testing).
</Step>
</Steps>
@ -677,7 +688,7 @@ runtime helpers.
<CardGroup cols={2}>
<Card title="Threading options" icon="git-branch" href="/uk/plugins/sdk-entrypoints#registration-mode">
Фіксовані, account-scoped або користувацькі режими відповіді
Фіксовані, прив’язані до облікового запису або користувацькі режими відповіді
</Card>
<Card title="Message tool integration" icon="puzzle" href="/uk/plugins/architecture#channel-plugins-and-the-shared-message-tool">
describeMessageTool і виявлення дій
@ -686,26 +697,29 @@ runtime helpers.
inferTargetChatType, looksLikeId, resolveTarget
</Card>
<Card title="Runtime helpers" icon="settings" href="/uk/plugins/sdk-runtime">
TTS, STT, медіа, subagent через api.runtime
TTS, STT, медіа, підагент через api.runtime
</Card>
<Card title="Channel turn kernel" icon="bolt" href="/uk/plugins/sdk-channel-turn">
Спільний життєвий цикл вхідного ходу: приймання, розв’язання, запис, передавання, завершення
</Card>
</CardGroup>
<Note>
Деякі bundled helper seams досі існують для супроводу bundled-plugin і
сумісності. Вони не є рекомендованим патерном для нових channel Plugins;
віддавайте перевагу generic channel/setup/reply/runtime subpaths зі спільної поверхні SDK,
якщо ви не супроводжуєте цю сім’ю bundled Plugins безпосередньо.
Деякі комплектні допоміжні шви все ще існують для обслуговування комплектних Plugins і
сумісності. Вони не є рекомендованим шаблоном для нових канальних Plugins;
віддавайте перевагу загальним підшляхам channel/setup/reply/runtime зі спільної поверхні SDK,
якщо ви не підтримуєте цю родину комплектних Plugins напряму.
</Note>
## Наступні кроки
- [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)

View File

@ -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-команди, модальні вікна, взаємодії з кнопками, події життєвого циклу, реакції, стан голосу), залишайте їх локальними для плагіна. Ядро володіє лише подіями, які можуть стати текстовим ходом агента.
<Info>
До ядра звертаються через інʼєктований runtime плагіна як `runtime.channel.turn.*`. Тип runtime плагіна експортується з `openclaw/plugin-sdk/core`, тож сторонні нативні плагіни можуть використовувати ці точки входу так само, як це роблять bundled-плагіни каналів.
</Info>
## Навіщо спільне ядро
Плагіни каналів повторюють той самий вхідний потік: нормалізувати, маршрутизувати, пропустити через 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<TRaw>`. Адаптер має 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`.
<Note>
Застарілі SDK helpers, як-от `dispatchInboundReplyWithBase`, досі bridge through assembled-turn helper. Новий код плагіна має використовувати `run` або `runPrepared`.
</Note>
## Типи фактів
Факти, які ядро споживає з вашого адаптера, не залежать від платформи. Перекладайте обʼєкти платформи в ці форми, перш ніж передавати їх ядру.
### 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<TRaw> = {
ingest(raw: TRaw): Promise<NormalizedTurnInput | null> | NormalizedTurnInput | null;
classify?(input: NormalizedTurnInput): Promise<ChannelEventClass> | ChannelEventClass;
preflight?(
input: NormalizedTurnInput,
eventClass: ChannelEventClass,
): Promise<PreflightFacts | ChannelTurnAdmission | null | undefined>;
resolveTurn(
input: NormalizedTurnInput,
eventClass: ChannelEventClass,
preflight: PreflightFacts,
): Promise<ChannelTurnResolved> | ChannelTurnResolved;
onFinalize?(result: ChannelTurnResult): Promise<void> | void;
};
```
`resolveTurn` повертає `ChannelTurnResolved`, тобто `AssembledChannelTurn` з необов’язковим типом допуску. Повернення `{ admission: { kind: "observeOnly" } }` запускає хід без створення видимого виводу. Адаптер усе ще володіє callback доставлення; для цього ходу він просто стає no-op.
`onFinalize` виконується для кожного результату, включно з помилками диспетчеризації. Використовуйте його, щоб очищати очікувану групову історію, прибирати реакції підтвердження, зупиняти індикатори стану та скидати локальний стан.
## Адаптер доставлення
Ядро не викликає платформу напряму. Канал передає ядру `ChannelTurnDeliveryAdapter`:
```typescript
type ChannelTurnDeliveryAdapter = {
deliver(payload: ReplyPayload, info: ChannelDeliveryInfo): Promise<ChannelDeliveryResult | void>;
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) для конвеєра завантаження та механіки реєстру