Skip to content

Aperçu des canaux

Ongrid parle aux humains à travers des canaux. Un canal est soit :

  • un canal de notification — Ongrid pousse les alertes vers une surface de chat ; l'humain lit, mais le canal lui-même ne porte jamais de réponse en retour vers l'agent. C'est notify_channels dans la DB et les Senders internal/pkg/notify/webhook.go.
  • un canal IM — Ongrid s'installe sur la surface de chat d'un workspace comme bot, lit les messages entrants et exécute le même raisonnement d'agent que l'UI web exécute, streamant la réponse en retour comme des edits à un message placeholder. C'est im_apps dans la DB et internal/manager/biz/imbridge/provider/*.

Les deux sont indépendants. Câbler les alertes Telegram (sendMessage sortant) ne laisse pas les utilisateurs chatter avec l'agent sur Telegram, et inversement.

Table différente, credential différent, même UI

Les deux surfaces sont configurées dans Settings → Channels dans l'UI web. Les canaux de notification vont à l'onglet Notify ; les canaux IM vont à l'onglet IM bridge.

Lequel je veux ?

ObjectifUtilisezFichier
Pousser les alertes parties vers un canal SlackWebhook entrant Slack (Notify)internal/pkg/notify/webhook.go NewSlackSender
Pousser les alertes parties vers groupe Feishu/LarkBot personnalisé Feishu (Notify)NewFeishuSender
Pousser les alertes parties vers groupe DingTalkBot personnalisé DingTalk (Notify)NewDingTalkSender
Pousser les alertes parties vers groupe WeComBot de groupe WeCom (Notify)NewWeComSender
POST JSON générique vers votre propre serviceWebhook (Notify)NewGenericWebhookSender
Alertes Telegram (direction unique)Bot Telegram sendMessage (Notify)NewTelegramSender
Parler à l'agent depuis Slack comme botApp Slack Socket Mode (pont IM)imbridge/provider/slack/
Parler à l'agent depuis TelegramBot Telegram getUpdates (pont IM)imbridge/provider/telegram/
Parler à l'agent depuis Feishu/LarksuiteLong-connection Feishu (pont IM)imbridge/provider/feishu/

Un workspace peut exécuter les deux : par ex. un webhook entrant Slack pour les alertes plus une app Slack Socket Mode séparée pour la conversation. Ils ne partagent pas d'état.

Canaux de notification

Un canal de notification est un Sender Send(ctx, Message) sans état. Il est appelé par le pipeline d'alerte chaque fois qu'une alerte part (ou récupère, ou la fenêtre de dampening expire). Tous les types de canaux rendent la même forme canonique notify.Message ; seul le format de payload et le protocole de signature diffèrent par provider.

Champs communs qu'un Sender reçoit

go
type Message struct {
    Severity   Severity            // critical | warning | info
    Subject    string              // alert rule name + target
    Body       string              // human-readable detail
    Source     string              // "alert" | "test" | ...
    Labels     map[string]string   // rule, incident_id, device_id, ...
    DedupeKey  string              // pipeline:rule:label-set
    OccurredAt time.Time
}

Le sender Slack rend ceci dans le format attachments avec un rail de couleur teinté par sévérité, des champs structurés (Severity, Source, Rule, Incident, Device, Dedupe), et un footer ts. Les senders Feishu / DingTalk / WeCom l'aplatissent vers un payload texte [SEV] subject\nbody\nsource:…\ndedupe:… parce que leurs APIs de bot ne livrent que du texte brut en v1.

Modèles de signature en un coup d'œil

ProviderComment il authentifie
SlackL'URL de webhook elle-même est le secret. Pas de signature en plus.
FeishuHMAC-SHA256(ts\nsecret), placé dans le corps JSON comme sign.
DingTalkHMAC-SHA256(ts\nsecret), placé dans la query URL comme sign.
WeComClé de bot dans la query URL. Pas de signature en plus.
TelegramToken de bot dans le path (/bot<TOKEN>/sendMessage).
WebhookOptionnel X-Ongrid-Signature: sha256=<HMAC> sur le corps.

Les implémentations exactes vivent dans internal/pkg/notify/webhook.go : signFeishu, signDingTalkURL, signGenericWebhook.

Slack drop le champ secret silencieusement

L'URL de webhook entrant Slack est elle-même le credential. Si vous remplissez le champ Secret pour un canal Slack, le constructeur de canal le drop avant de construire le Sender. C'est intentionnel — le protocole Slack n'a pas de surface de signature séparée pour les webhooks entrants.

Canaux IM

Un canal IM est une goroutine longue durée qui :

  1. Se connecte en sortant vers le provider (websocket pour Feishu/Slack, long poll pour Telegram). Aucun port entrant n'est ouvert sur le manager.
  2. Reçoit les messages utilisateur entrants, les filtre via allow_from, et passe le texte à bizbridge.Bridge.HandleInbound.
  3. HandleInbound poste une réponse placeholder, exécute le graphe d'agent complet (le même que l'UI web utilise), et stream les edits en retour vers l'id de message placeholder.

Le même kernel d'agent, skills et registre de personas alimentent à la fois l'UI web et les canaux IM. Il n'y a pas d'« agent spécifique à l'IM » — le coordinator est le même que vous voyez sur /chat.

L'entrant est spécifique au provider, le sortant est uniforme

Les différences de provider (ack envelope_id Slack, offset d'update Telegram, encrypt_key Feishu) vivent dans imbridge/provider/*/stream.go. Une fois un message à l'intérieur de bizbridge.HandleInbound, tout en aval est provider-agnostique.

default_locale

Les lignes IM et Notify portent toutes les deux un default_locale optionnel. Le validateur n'accepte que la chaîne vide (auto), en, ou zhen-US / zh-CN sont pliés vers leur subtag principal, et EN-us / une locale avec typo est rejetée d'emblée dans AppInput.validate.

ValeurComportement
"" (auto)Pas de directive. Le LLM mirror la langue de l'utilisateur. Défaut legacy.
enAjoute une directive Respond in English au system prompt.
zhAjoute une directive 「请用中文回复」 au system prompt.

Ceci est indépendant de la locale UI (variable d'env ONGRID_DEFAULT_LOCALE ou langue du navigateur). Un canal IM gagne toujours pour les messages reçus à travers lui. Voir le feedback sur la locale de sortie IA pour la règle de fallback auto-trigger sur les sorties initiées par le manager.

allow_from

L'allowlist d'expéditeurs. Requise pour Telegram et Slack ; optionnelle pour Feishu/DingTalk. Elle est parsée exactement une fois par ParseAllowFrom, partagée entre le validateur et la boucle de poll/stream de chaque provider pour que la règle de parse ait une définition unique.

Syntaxe. Virgule, espace, nouvelle ligne, tabulation, point-virgule — toute combinaison sépare les tokens. L'ordre est préservé, les duplicatas sont droppés. Les préfixes telegram: et tg: sont retirés silencieusement (compat OpenClaw).

Format par provider.

  • Telegram. IDs utilisateur numériques seulement. Les tokens non-numériques et les valeurs négatives (IDs de chat de groupe) sont droppés au validate time. Au moins un ID est requis — le bot est publiquement découvrable par username, donc une allowlist vide laisserait n'importe quel utilisateur Telegram commander un agent équipé d'outils. Voir ADR-031.
  • Slack. IDs utilisateur commençant par U (ou W pour les invités Enterprise). Au moins un est requis. Trouvez le vôtre en cliquant sur le profil de workspace → Copy member ID.
  • Feishu / DingTalk. Optionnel. Ces plateformes sont gatées par appartenance au tenant entreprise ; seuls les membres de l'organisation atteignent le bot.

Une allowlist vide pour Telegram ou Slack est rejetée

Le validateur renvoie telegram requires allow_from / slack requires allow_from quand la liste parsée est vide. Il n'y a pas de mode « deny par défaut mais autoriser plus tard le setup ». Le bot est publiquement joignable ; l'opérateur doit consciemment ouvrir la porte.

Le mode d'échec est le drop silencieux. Un message d'un expéditeur non-allowlisté est loggué à WARN avec l'user_id de l'expéditeur, puis droppé. Pas de réponse, pas de placeholder, pas d'exécution d'agent. Le bot ne confirme même pas qu'il existe — la même forme que dmPolicy: allowFrom d'OpenClaw.

À quoi ressemble une livraison d'alerte partie

text
alert evaluator         → produces notify.Message

notify.Sender (per channel)   ↓ buildBody(msg) → JSON payload
                              ↓ signTarget(endpoint, secret, body) → headers / URL params
                              ↓ POST endpoint
                              ↓ resp.StatusCode in [200, 299] → success

                          chat surface

Pour plus de détails, voir les pages par canal :