Réveil des agents — nudges Module

Vaks PM · Guide d'intégration · Orchestration des agents · Août 2026

Ce que couvre ce guide. Les agents IA de Vaks PM travaillent en « pull » : ils tirent leur travail eux-mêmes. Un nudge ajoute un chemin événementiel par-dessus : dès qu'une tâche devient pertinente pour un agent, Vaks PM appelle une URL de callback que sa plateforme a enregistrée, pour le réveiller. C'est la pièce qui rend un agent réactif sans le faire tourner en boucle. Cette page décrit le mécanisme de bout en bout — événements, contrat d'abonnement, en-têtes, signature, contrôle de capacité. Pour le brancher concrètement sur une plateforme, voir Agent autonome · Copilot Studio.

Pourquoi un réveil, alors que les agents tirent déjà

Le modèle des agents est délibérément en pull : un agent demande sa prochaine tâche, la réclame de façon atomique, la traite, livre. Rien ne le pousse — c'est ce qui garde le cycle de vie gouverné côté serveur et empêche deux agents de détenir la même tâche. Mais le pull seul laisse une question ouverte : quand l'agent redemande-t-il ? Le faire interroger l'API toutes les quelques secondes, c'est du gaspillage et de la latence ; le faire trop rarement, c'est un agent qui répond une heure après qu'on lui a confié une tâche.

Le nudge résout exactement ça, sans rien changer au modèle de sécurité. C'est une sonnette, pas une clé : Vaks PM signale simplement « il y a peut-être du travail pour toi », et l'agent, réveillé, fait ce qu'il aurait fait de toute façon — il tire et réclame avec son propre jeton. Le nudge ne porte aucune autorité et ne peut rien débloquer que l'agent ne pouvait déjà faire.

Provider-agnostique. Vaks PM ne connaît que l'URL de callback. Ce qui la reçoit — un flow Power Automate derrière un agent Copilot Studio, un workflow n8n, un runner maison — lui est indifférent. Le contrat ci-dessous est celui du trigger « HTTP Webhook » de Logic Apps (donc directement consommable par Copilot Studio / Power Automate), mais rien n'y est spécifique à Microsoft.

Le modèle en un coup d'œil

TempsCe qui se passe
1. Abonnement (une fois)La plateforme de l'agent enregistre une URL de callback auprès de Vaks PM (POST /me/webhook-subscriptions), authentifiée par le jeton de l'agent. Vaks PM renvoie un secret HMAC, affiché une seule fois.
2. ÉvénementUne tâche devient pertinente pour l'agent — on la lui assigne, une revue demande des corrections, un commentaire arrive sur une tâche qu'il tient.
3. NudgeVaks PM POST un petit corps JSON signé vers l'URL de callback. Il contient de quoi identifier la tâche et l'agent — jamais de contenu sensible, jamais de jeton.
4. PullLa plateforme réveille l'agent. Celui-ci vérifie la signature, puis tire et réclame atomiquement avec son propre PAT (vaks_claim_task / POST /me/next-task/claim). À partir de là, c'est la boucle de travail normale.
Le nudge n'est pas une commande d'action. Il ne réclame pas la tâche à la place de l'agent, ne lui accorde aucun droit et ne réserve rien. Un nudge perdu n'a aucune conséquence : la tâche reste dans la file, et l'agent la retrouvera au prochain pull — le réveil n'est qu'une optimisation de latence par-dessus un modèle qui fonctionne sans lui.

Quand un nudge part

Trois événements déclenchent un réveil. Ils couvrent l'entrée dans la boucle et son bouclage après une revue :

ÉvénementDéclencheurAgent réveillé
task.assignedUn humain assigne une tâche à l'agent (ou la capacité se libère et une tâche éligible lui est proposée — voir capacité).L'agent assigné.
task.changes_requestedUne revue renvoie un livrable avec une demande de changements.L'agent qui tient la tâche (son claimant).
comment.createdUn commentaire est ajouté à une tâche qu'un agent tient — typiquement une réponse humaine à une question de l'agent.Les agents assignés / claimant de la tâche.

L'émission est implicite : il n'y a pas d'interrupteur d'organisation à activer. Un nudge part dès lors que (1) le projet est ouvert aux agents (agentsEnabled) et (2) une souscription active correspond à l'agent concerné. Un agent sans abonnement ne reçoit simplement rien — et continue de fonctionner en pull pur.

S'abonner — le contrat

L'abonnement est self-service et self-only : le sujet de la souscription est toujours dérivé du porteur du jeton, jamais d'un identifiant passé dans le corps. Un agent ne peut abonner que lui-même.

POST /api/v1/me/webhook-subscriptions
X-Api-Key: vaks_pat_<jeton de l'agent>
Content-Type: application/json

{ "callbackUrl": "https://<votre-plateforme>/callback", "events": ["task.assigned"] }

→ 201 Created
  Location: https://<votre-domaine>/api/v1/me/webhook-subscriptions/<id>
  { "id": "…", "callbackUrl": "…", "events": [...],
    "secret": "…",              ← montré UNE SEULE FOIS (secret HMAC)
    "unsubscribeUrl": "https://…/me/webhook-subscriptions/<id>" }
Authentification par X-Api-Key, pas Authorization. Les connecteurs Power Platform / Copilot Studio réservent l'en-tête Authorization et le retirent avant que la requête ne parte. Un PAT placé en Authorization: Bearer n'arriverait jamais. Vaks PM lit donc le jeton, en repli, dans l'en-tête non réservé X-Api-Key (valeur = le PAT brut). C'est le même repli que celui utilisé par le connecteur MCP d'un agent dédié.

L'opération est un upsert : une seule souscription active par sujet. Réappeler POST remplace l'URL et régénère le secret. GET /me/webhook-subscriptions renvoie la souscription courante (sans le secret), ou null.

Deux modes d'abonnement

Qui porte le jeton d'abonnement décide de la portée de la souscription :

Jeton utiliséPortéeQuand l'utiliser
PAT d'agent (vaks_pat_…)Ciblée sur cet agent. Le callback ne reçoit que les nudges de l'agent porteur du jeton.Un flow (ou runner) par agent. Le chemin le plus simple quand vous n'avez qu'un ou deux agents.
Clé d'organisation (vaks_org_…) scopée nudge:manageOrg-level. Un seul callback reçoit les nudges de tous les agents de l'organisation ; chacun est identifié dans le corps et les en-têtes du nudge.« Un seul flow pour tous les agents » : un flow Copilot/Power Automate qui aiguille sur X-Vaks-Agent-Email et réveille le bon agent.
La clé org de nudge ne lit rien. Le scope nudge:manage est volontairement minimal : il permet uniquement de gérer l'abonnement de réveil — il n'autorise aucune lecture de tâches, de projets ou quoi que ce soit d'autre. Générez-la en un clic depuis la console admin (voir plus bas), sans avoir à composer les scopes à la main. Un jeton de session humain, lui, est toujours refusé (403) : l'abonnement est réservé aux identités machine.

Se désabonner

Trois formes, toutes idempotentes (200 même si la souscription est déjà absente ou inactive) :

AppelEffet
DELETE /me/webhook-subscriptions (sans id)Désactive la souscription courante du porteur. URI statique — c'est la forme qu'exige le trigger « HTTP Webhook », qui a besoin d'une URL de désinscription fixe au moment du design, avant de connaître l'id.
DELETE /me/webhook-subscriptions/<id>Désactive par identifiant — celui renvoyé dans l'en-tête Location à l'abonnement.
POST /me/webhook-subscriptions/<id>/unsubscribeAlias en POST — certains flux Logic Apps émettent un POST plutôt qu'un DELETE sur l'URL de désinscription. Même effet.

Le nudge sortant — en-têtes & corps

Vaks PM POST vers votre callback un corps JSON compact et un jeu d'en-têtes calqués sur ceux des webhooks génériques :

POST <votre callbackUrl>
Content-Type: application/json
User-Agent: Vaks-PM-Nudge/1.0
X-Vaks-Event: task.assigned                    ← nom de l'événement
X-Vaks-Delivery: <id de livraison>              ← idempotence côté receveur
X-Vaks-Signature: sha256=<hex>                  ← HMAC-SHA256(secret, corps brut)
X-Vaks-Timestamp: 1723200000                   ← epoch en secondes
X-Vaks-Agent-Id: <id de l'agent>
X-Vaks-Agent-Email: agent-…@agents.invalid
X-Vaks-Agent-Name: Vaks%20Doc%20Agent          ← URL-encodé

{
  "event":      "task.assigned",
  "taskId":     "…",
  "projectId":  "…",
  "agentId":    "…",
  "agentEmail": "agent-…@agents.invalid",
  "agentName":  "Vaks Doc Agent"
}

L'identité de l'agent apparaît à la fois dans le corps et dans les en-têtes, exprès : en mode org-level, un flow peut aiguiller sur l'en-tête X-Vaks-Agent-Email (un simple Switch) sans avoir à parser le corps. Le nom est URL-encodé dans l'en-tête (les en-têtes HTTP ne tolèrent pas les caractères arbitraires) ; le corps le porte en clair.

Ce que le nudge ne contient jamais. Pas de titre de tâche, pas de description, pas de contenu de commentaire, pas de jeton, pas de donnée client. Seulement des identifiants opaques et de quoi router. Tout le reste, l'agent va le chercher lui-même avec son propre jeton une fois réveillé — ce qui garantit qu'il ne voit jamais, via le nudge, plus que ce que ses droits l'autorisent à lire.

Vérifier la signature

Chaque nudge est signé en HMAC-SHA256 sur le corps brut, avec le secret rendu à l'abonnement. Le receveur recalcule et compare — c'est ce qui prouve que le nudge vient bien de Vaks PM et n'a pas été altéré.

// pseudo-code côté receveur
const attendu = "sha256=" + hmacSha256(secret, corpsBrut).hex();
if (!timingSafeEqual(attendu, header["X-Vaks-Signature"])) rejeter(401);

Ce que l'agent fait ensuite

Réveillé, l'agent revient dans la boucle normale — le nudge ne remplace aucune de ses étapes :

  1. Il tire et réclame avec son propre PAT : vaks_claim_task pour la tâche précise que le nudge désigne, ou vaks_claim_next_task pour prendre la prochaine tâche éligible du projet.
  2. Il lit le contexte de travail (vaks_get_work_context) — y compris, sur un task.changes_requested, le verdict de revue le plus récent.
  3. Il travaille, livre, déclare son coût, resoumet en revue — exactement la boucle de travail décrite ailleurs.
Le nudge ne réserve pas la tâche. Un task.assigned rend la tâche visible et réveille l'agent, mais ne la lui verrouille pas : c'est le claim atomique qui l'attribue. Si un autre agent du projet tire cette tâche entre-temps, il l'obtient — conforme au modèle pull. L'assignation exprime une intention, pas une réservation.

Capacité & backpressure

Réveiller un agent qui n'a pas la capacité de prendre le travail ne sert à rien — pire, cela lui fait payer une conversation pour se heurter ensuite à un refus. Un contrôle de capacité (réglable, sous Réveils d'agents) filtre les nudges à l'émission selon deux seuils indépendants :

SeuilPortéeCe qu'il limite
ConcurrenceUn agent, tous projets confondus.Le nombre de tâches qu'un même agent peut mener en parallèle. Au plafond, on ne le réveille pas pour une tâche de plus.
Backpressure de revueUn projet.Le nombre de livrables d'agents en attente de revue humaine dans le projet. Au plafond, les agents cessent d'être réveillés pour ne pas prendre de l'avance sur les réviseurs. Ne compte que les tâches d'agents — une file de revue humaine ne bloque jamais les agents.

La réservation d'un créneau est atomique (pour éviter que trois approbations en rafale ne réveillent neuf agents pour trois places) et n'est consommée qu'au claim. Quand la file de revue d'un projet est saturée, une alerte review_queue_full part une seule fois pour le projet — pas une par agent bloqué. Quand un créneau se libère (un livrable est approuvé), l'agent est réveillé et draine une tâche ; si la file repasse au-dessus du plafond, elle se re-sature aussitôt, comme prévu.

Le parallélisme est un choix, pas un défaut à corriger. Plusieurs runs d'agents concurrents, c'est le but — assigner trois tâches d'un coup réveille trois exécutions simultanées. Le réglage de parallélisme est un curseur de coût : le baisser sérialise le travail (moins de conversations d'agents en vol à la fois), le monter le parallélise davantage. À vous de le placer selon votre budget.

Réessais & auto-désactivation. Une livraison qui échoue est réessayée (backoff exponentiel), avec un délai de 10 s par tentative. Une souscription qui accumule 5 échecs terminaux est automatiquement désactivée — un callback mort ne fait pas retenter indéfiniment. Elle réapparaît en « auto-désactivée » dans la console admin, où on peut la réactiver après avoir corrigé la cause.

Callbacks vers une IP privée (on-premise)

Par défaut, Vaks PM applique une protection anti-SSRF stricte sur toute URL sortante : les adresses privées, loopback et métadonnées sont refusées. C'est le bon défaut en SaaS mutualisé. Mais un déploiement on-premise a souvent son orchestrateur (n8n, un runner) sur le même réseau privé — le callback vise alors une IP LAN, légitimement.

Ce cas est autorisé sans lever la protection générale, via un double verrou :

Toujours bloqués, quoi qu'il arrive. Le loopback (127.0.0.0/8, ::1) et les adresses de métadonnées cloud (169.254.0.0/16) restent refusés même déclarés dans un CIDR — ce sont les cibles classiques d'une SSRF, et aucun cas légitime n'a besoin de les viser. Par ailleurs, les ports autorisés restent 80, 443, 8080 et 8443 : un callback LAN doit écouter sur l'un d'eux. Le chemin des webhooks génériques, lui, reste strict sans exception.

Console admin & journal des livraisons

Tout se pilote sous Admin → Gestion des agents IA → Réveils d'agents (droit agent:manage — administrateurs, ou porteurs de l'octroi Administrateur IA) :

Récapitulatif des endpoints

EndpointQui l'appelleRôle
POST /me/webhook-subscriptionsAgent (PAT) ou clé org nudge:manageS'abonner (upsert). Renvoie le secret + Location.
GET /me/webhook-subscriptionsIdemSouscription courante (sans secret).
DELETE /me/webhook-subscriptionsIdemDésabonner la souscription courante (URI statique).
DELETE /me/webhook-subscriptions/:idIdemDésabonner par id.
POST /me/webhook-subscriptions/:id/unsubscribeIdemAlias POST de désinscription.
GET /admin/agent-webhook-subscriptionsAdmin (agent:manage)Lister les souscriptions.
POST /admin/agent-webhook-subscriptions/connector-keyAdminGénérer la clé org de connexion.
GET /admin/agent-webhook-subscriptions/deliveriesAdminJournal des livraisons.
GET · PUT /admin/agent-webhook-subscriptions/internal-cidrsAdminMode on-premise & CIDR internes.

Dépannage

SymptômeCause & correction
L'abonnement renvoie 403 « Réservé aux jetons… »Vous appelez avec un jeton de session humain. Utilisez un PAT d'agent, ou une clé org scopée nudge:manage.
La clé org renvoie 403 « doit être scopée nudge:manage »La clé n'a pas le bon scope. Régénérez-la via Générer la clé de connexion, qui pose le scope automatiquement.
Le jeton « n'arrive pas » (-32001 / 401)Vous l'avez mis en Authorization: Bearer depuis un connecteur Power Platform, qui le strippe. Utilisez l'en-tête X-Api-Key = PAT brut.
Aucun nudge n'arrive alors qu'on assigne une tâcheVérifiez que le projet est ouvert aux agents, que l'agent en est membre, et que la souscription est active. En capacité saturée, le nudge est volontairement retenu — regardez le seuil de concurrence / backpressure.
Le callback est appelé mais rejeté (4xx dans le journal)Le journal des livraisons montre le code et un extrait de la réponse. Souvent une signature mal recalculée (re-sérialisation du corps) ou un domaine de callback non autorisé par la gouvernance des webhooks.
Callback LAN refusé (400)Le mode on-premise est fermé, ou la plage n'est pas déclarée, ou le port n'est pas dans 80/443/8080/8443. Voir callbacks LAN.
La souscription est passée « auto-désactivée »5 échecs terminaux d'affilée. Corrigez le callback puis réactivez-la dans la console.

À lire aussi : Agent autonome · Copilot Studio — brancher un agent dédié (outil MCP + trigger de réveil) · Agents IA — la boucle de travail, la gouvernance, les budgets · Authentification des agents — comment un agent obtient son jeton · Webhooks — les rappels HTTP génériques · toutes les intégrations.