SpyBara
Go Premium

agent-sdk/hooks.md 2026-05-13 23:01 UTC to 2026-05-14 17:02 UTC

11 added, 5 removed.

2026
Sun 31 06:39 Sat 30 06:23 Fri 29 06:38 Thu 28 06:37 Wed 27 06:42 Tue 26 06:33 Sun 24 06:25 Sat 23 06:18 Fri 22 06:33 Thu 21 06:36 Wed 20 06:35 Tue 19 06:34 Mon 18 23:59 Sun 17 01:01 Fri 15 22:58 Thu 14 17:02 Wed 13 23:01 Tue 12 22:57 Mon 11 23:00 Sun 10 23:03 Sat 9 04:57 Fri 8 22:00 Thu 7 22:59 Tue 5 23:00 Mon 4 22:58 Sat 2 18:14 Fri 1 18:19

Intercepter et contrôler le comportement des agents avec des hooks

Interceptez et personnalisez le comportement des agents aux points d'exécution clés avec des hooks

Les hooks sont des fonctions de rappel qui exécutent votre code en réponse à des événements d'agent, comme un outil appelé, une session démarrée ou l'exécution arrêtée. Avec les hooks, vous pouvez :

  • Bloquer les opérations dangereuses avant leur exécution, comme les commandes shell destructrices ou l'accès non autorisé aux fichiers
  • Enregistrer et auditer chaque appel d'outil pour la conformité, le débogage ou l'analyse
  • Transformer les entrées et les sorties pour nettoyer les données, injecter des identifiants ou rediriger les chemins de fichiers
  • Exiger une approbation humaine pour les actions sensibles comme les écritures de base de données ou les appels API
  • Suivre le cycle de vie de la session pour gérer l'état, nettoyer les ressources ou envoyer des notifications

Ce guide couvre le fonctionnement des hooks, comment les configurer et fournit des exemples pour les modèles courants comme bloquer les outils, modifier les entrées et transférer les notifications.

Fonctionnement des hooks

1

Un événement se déclenche

Quelque chose se produit lors de l'exécution de l'agent et le SDK déclenche un événement : un outil est sur le point d'être appelé (PreToolUse), un outil a renvoyé un résultat (PostToolUse), un sous-agent a démarré ou s'est arrêté, l'agent est inactif ou l'exécution s'est terminée. Consultez la liste complète des événements.

2

Le SDK collecte les hooks enregistrés

Le SDK vérifie les hooks enregistrés pour ce type d'événement. Cela inclut les hooks de rappel que vous transmettez dans options.hooks et les hooks de commande shell à partir des fichiers de paramètres lorsque l'entrée settingSources ou setting_sources correspondante est activée, ce qui est le cas pour les options query() par défaut.

3

Les matchers filtrent les hooks qui s'exécutent

Si un hook a un motif matcher (comme "Write|Edit"), le SDK le teste par rapport à la cible de l'événement (par exemple, le nom de l'outil). Les hooks sans matcher s'exécutent pour chaque événement de ce type.

4

Les fonctions de rappel s'exécutent

Chaque fonction de rappel du hook correspondant reçoit des informations sur ce qui se passe : le nom de l'outil, ses arguments, l'ID de session et d'autres détails spécifiques à l'événement.

5

Votre rappel retourne une décision

Après avoir effectué toute opération (enregistrement, appels API, validation), votre rappel retourne un objet de sortie qui indique à l'agent quoi faire : autoriser l'opération, la bloquer, modifier l'entrée ou injecter du contexte dans la conversation.

L'exemple suivant réunit ces étapes. Il enregistre un hook PreToolUse (étape 1) avec un matcher "Write|Edit" (étape 3) afin que le rappel ne se déclenche que pour les outils d'écriture de fichiers. Lorsqu'il est déclenché, le rappel reçoit l'entrée de l'outil (étape 4), vérifie si le chemin du fichier cible un fichier .env et retourne permissionDecision: "deny" pour bloquer l'opération (étape 5) :

import asyncio
from claude_agent_sdk import (
AssistantMessage,
ClaudeSDKClient,
ClaudeAgentOptions,
HookMatcher,
ResultMessage,
)


# Define a hook callback that receives tool call details
async def protect_env_files(input_data, tool_use_id, context):
# Extract the file path from the tool's input arguments
file_path = input_data["tool_input"].get("file_path", "")
file_name = file_path.split("/")[-1]

# Block the operation if targeting a .env file
if file_name == ".env":
return {
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "deny",
"permissionDecisionReason": "Cannot modify .env files",
}
}

# Return empty object to allow the operation
return {}


async def main():
options = ClaudeAgentOptions(
hooks={
# Register the hook for PreToolUse events
# The matcher filters to only Write and Edit tool calls
"PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]
}
)

async with ClaudeSDKClient(options=options) as client:
await client.query("Update the database configuration")
async for message in client.receive_response():
# Filter for assistant and result messages
if isinstance(message, (AssistantMessage, ResultMessage)):
print(message)


asyncio.run(main())

Hooks disponibles

Le SDK fournit des hooks pour différentes étapes de l'exécution de l'agent. Certains hooks sont disponibles dans les deux SDK, tandis que d'autres sont réservés à TypeScript.

Événement Hook SDK Python SDK TypeScript Ce qui le déclenche Cas d'usage exemple
PreToolUse Oui Oui Demande d'appel d'outil (peut bloquer ou modifier) Bloquer les commandes shell dangereuses
PostToolUse Oui Oui Résultat de l'exécution de l'outil Enregistrer tous les changements de fichiers dans la piste d'audit
PostToolUseFailure Oui Oui Échec de l'exécution de l'outil Gérer ou enregistrer les erreurs d'outil
PostToolBatch Non Oui Un lot complet d'appels d'outil se résout, une fois par lot avant l'appel du modèle suivant Injecter des conventions une fois pour tout le lot
UserPromptSubmit Oui Oui Soumission d'invite utilisateur Injecter du contexte supplémentaire dans les invites
Stop Oui Oui Arrêt de l'exécution de l'agent Enregistrer l'état de la session avant la sortie
SubagentStart Oui Oui Initialisation du sous-agent Suivre le lancement des tâches parallèles
SubagentStop Oui Oui Achèvement du sous-agent Agréger les résultats des tâches parallèles
PreCompact Oui Oui Demande de compaction de conversation Archiver la transcription complète avant le résumé
PermissionRequest Oui Oui La boîte de dialogue de permission s'afficherait Gestion des permissions personnalisée
SessionStart Non Oui Initialisation de la session Initialiser la journalisation et la télémétrie
SessionEnd Non Oui Arrêt de la session Nettoyer les ressources temporaires
Notification Oui Oui Messages d'état de l'agent Envoyer les mises à jour d'état de l'agent à Slack ou PagerDuty
Setup Non Oui Configuration/maintenance de la session Exécuter les tâches d'initialisation
TeammateIdle Non Oui Le coéquipier devient inactif Réassigner le travail ou notifier
TaskCompleted Non Oui La tâche de fond se termine Agréger les résultats des tâches parallèles
ConfigChange Non Oui Le fichier de configuration change Recharger les paramètres dynamiquement
WorktreeCreate Non Oui Git worktree créé Suivre les espaces de travail isolés
WorktreeRemove Non Oui Git worktree supprimé Nettoyer les ressources de l'espace de travail

Configurer les hooks

Pour configurer un hook, transmettez-le dans le champ hooks de vos options d'agent (ClaudeAgentOptions en Python, l'objet options en TypeScript) :

options = ClaudeAgentOptions(
hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[my_callback])]}
)

async with ClaudeSDKClient(options=options) as client:
await client.query("Your prompt")
async for message in client.receive_response():
print(message)

L'option hooks est un dictionnaire (Python) ou un objet (TypeScript) où :

Matchers

Utilisez les matchers pour filtrer quand vos rappels se déclenchent. Le champ matcher est une chaîne regex qui correspond à une valeur différente selon le type d'événement hook. Par exemple, les hooks basés sur les outils correspondent au nom de l'outil, tandis que les hooks Notification correspondent au type de notification. Consultez la référence des hooks Claude Code pour la liste complète des valeurs de matcher pour chaque type d'événement.

Option Type Par défaut Description
matcher string undefined Motif regex mis en correspondance avec le champ de filtre de l'événement. Pour les hooks d'outils, c'est le nom de l'outil. Les outils intégrés incluent Bash, Read, Write, Edit, Glob, Grep, WebFetch, Agent et d'autres (consultez Types d'entrée d'outil pour la liste complète). Les outils MCP utilisent le motif mcp__<server>__<action>.
hooks HookCallback[] - Requis. Tableau de fonctions de rappel à exécuter lorsque le motif correspond
timeout number 60 Délai d'expiration en secondes

Utilisez le motif matcher pour cibler des outils spécifiques chaque fois que possible. Un matcher avec 'Bash' s'exécute uniquement pour les commandes Bash, tandis que l'omission du motif exécute vos rappels pour chaque occurrence de l'événement. Notez que pour les hooks basés sur les outils, les matchers ne filtrent que par nom d'outil, pas par chemins de fichiers ou d'autres arguments. Pour filtrer par chemin de fichier, vérifiez tool_input.file_path à l'intérieur de votre rappel.

Fonctions de rappel

Entrées

Chaque rappel hook reçoit trois arguments :

  • Données d'entrée : un objet typé contenant les détails de l'événement. Chaque type de hook a sa propre forme d'entrée (par exemple, PreToolUseHookInput inclut tool_name et tool_input, tandis que NotificationHookInput inclut message). Consultez les définitions de type complètes dans les références du SDK TypeScript et Python.
    • Toutes les entrées de hook partagent session_id, cwd et hook_event_name.
    • agent_id et agent_type sont remplis lorsque le hook se déclenche à l'intérieur d'un sous-agent. En TypeScript, ceux-ci se trouvent sur l'entrée de hook de base et sont disponibles pour tous les types de hook. En Python, ils se trouvent uniquement sur PreToolUse, PostToolUse et PostToolUseFailure.
  • ID d'utilisation d'outil (str | None / string | undefined) : met en corrélation les événements PreToolUse et PostToolUse pour le même appel d'outil.
  • Contexte : en TypeScript, contient une propriété signal (AbortSignal) pour l'annulation. En Python, cet argument est réservé pour une utilisation future.

Sorties

Votre rappel retourne un objet avec deux catégories de champs :

  • Champs de niveau supérieur fonctionnent de la même manière sur chaque événement : systemMessage affiche un message à l'utilisateur, et continue (continue_ en Python) détermine si l'agent continue à s'exécuter après ce hook.
  • hookSpecificOutput contrôle l'opération actuelle. Les champs à l'intérieur dépendent du type d'événement hook. Pour les hooks PreToolUse, c'est là que vous définissez permissionDecision ("allow", "deny", "ask" ou "defer"), permissionDecisionReason et updatedInput. Retourner "defer" termine la requête pour que vous puissiez la reprendre plus tard. Pour les hooks PostToolUse, vous pouvez définir additionalContext pour ajouter des informations au résultat de l'outil, ou updatedToolOutput pour remplacer entièrement la sortie de l'outil avant que Claude ne la voie.

Retournez {} pour autoriser l'opération sans modifications. Les hooks de rappel du SDK utilisent le même format de sortie JSON que les hooks de commande shell Claude Code, qui documente chaque champ et option spécifique à l'événement. Pour les définitions de type du SDK, consultez les références du SDK TypeScript et Python.

Sortie asynchrone

Par défaut, l'agent attend que votre hook retourne avant de continuer. Si votre hook effectue un effet secondaire (enregistrement, envoi d'un webhook) et n'a pas besoin d'influencer le comportement de l'agent, vous pouvez retourner une sortie asynchrone à la place. Cela indique à l'agent de continuer immédiatement sans attendre la fin du hook :

async def async_hook(input_data, tool_use_id, context):
# Start a background task, then return immediately
asyncio.create_task(send_to_logging_service(input_data))
return {"async_": True, "asyncTimeout": 30000}
Champ Type Description
async true Signale le mode asynchrone. L'agent continue sans attendre. En Python, utilisez async_ pour éviter le mot-clé réservé.
asyncTimeout number Délai d'expiration optionnel en millisecondes pour l'opération de fond

Exemples

Modifier l'entrée de l'outil

Cet exemple intercepte les appels d'outil Write et réécrit l'argument file_path pour ajouter /sandbox, redirigeant toutes les écritures de fichiers vers un répertoire en sandbox. Le rappel retourne updatedInput avec le chemin modifié et permissionDecision: 'allow' pour approuver automatiquement l'opération réécrite :

async def redirect_to_sandbox(input_data, tool_use_id, context):
if input_data["hook_event_name"] != "PreToolUse":
return {}

if input_data["tool_name"] == "Write":
original_path = input_data["tool_input"].get("file_path", "")
return {
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "allow",
"updatedInput": {
**input_data["tool_input"],
"file_path": f"/sandbox{original_path}",
},
}
}
return {}

Ajouter du contexte et bloquer un outil

Cet exemple bloque les écritures dans le répertoire /etc et explique pourquoi au modèle et à l'utilisateur :

  • permissionDecision: 'deny' arrête l'appel d'outil.
  • permissionDecisionReason indique au modèle pourquoi, afin qu'il évite de réessayer.
  • systemMessage montre à l'utilisateur ce qui s'est passé.
async def block_etc_writes(input_data, tool_use_id, context):
file_path = input_data["tool_input"].get("file_path", "")

if file_path.startswith("/etc"):
return {
# Top-level field: message shown to the user
"systemMessage": "Remember: system directories like /etc are protected.",
# hookSpecificOutput: block the operation
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "deny",
"permissionDecisionReason": "Writing to /etc is not allowed",
},
}
return {}

Approuver automatiquement des outils spécifiques

Par défaut, l'agent peut demander une permission avant d'utiliser certains outils. Cet exemple approuve automatiquement les outils de système de fichiers en lecture seule (Read, Glob, Grep) en retournant permissionDecision: 'allow', les laissant s'exécuter sans confirmation de l'utilisateur tout en laissant tous les autres outils soumis aux vérifications de permission normales :

async def auto_approve_read_only(input_data, tool_use_id, context):
if input_data["hook_event_name"] != "PreToolUse":
return {}

read_only_tools = ["Read", "Glob", "Grep"]
if input_data["tool_name"] in read_only_tools:
return {
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "allow",
"permissionDecisionReason": "Read-only tool auto-approved",
}
}
return {}

Enregistrer plusieurs hooks

Quand un événement se déclenche, tous les hooks correspondants s'exécutent en parallèle. Pour les décisions de permission, le résultat le plus restrictif gagne : un seul deny bloque l'appel d'outil indépendamment de ce que les autres hooks retournent. Parce que l'ordre d'exécution est non-déterministe, écrivez chaque hook pour agir indépendamment plutôt que de compter sur l'exécution préalable d'un autre hook.

L'exemple ci-dessous enregistre trois vérifications indépendantes pour chaque appel d'outil :

options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(hooks=[authorization_check]),
HookMatcher(hooks=[input_validator]),
HookMatcher(hooks=[audit_logger]),
]
}
)

Filtrer avec des matchers regex

Utilisez des motifs regex pour correspondre à plusieurs outils. Cet exemple enregistre trois matchers avec des portées différentes : le premier déclenche file_security_hook uniquement pour les outils de modification de fichiers, le second déclenche mcp_audit_hook pour tout outil MCP (outils dont les noms commencent par mcp__), et le troisième déclenche global_logger pour chaque appel d'outil indépendamment du nom :

options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
# Match file modification tools
HookMatcher(matcher="Write|Edit|Delete", hooks=[file_security_hook]),
# Match all MCP tools
HookMatcher(matcher="^mcp__", hooks=[mcp_audit_hook]),
# Match everything (no matcher)
HookMatcher(hooks=[global_logger]),
]
}
)

Suivre l'activité des sous-agents

Utilisez les hooks SubagentStop pour surveiller quand les sous-agents terminent leur travail. Consultez le type d'entrée complet dans les références du SDK TypeScript et Python. Cet exemple enregistre un résumé chaque fois qu'un sous-agent se termine :

async def subagent_tracker(input_data, tool_use_id, context):
# Log subagent details when it finishes
print(f"[SUBAGENT] Completed: {input_data['agent_id']}")
print(f"  Transcript: {input_data['agent_transcript_path']}")
print(f"  Tool use ID: {tool_use_id}")
print(f"  Stop hook active: {input_data.get('stop_hook_active')}")
return {}


options = ClaudeAgentOptions(
hooks={"SubagentStop": [HookMatcher(hooks=[subagent_tracker])]}
)

Effectuer des requêtes HTTP à partir des hooks

Les hooks peuvent effectuer des opérations asynchrones comme les requêtes HTTP. Capturez les erreurs à l'intérieur de votre hook au lieu de les laisser se propager, car une exception non gérée peut interrompre l'agent.

Cet exemple envoie un webhook après chaque exécution d'outil, enregistrant quel outil a été exécuté et quand. Le hook capture les erreurs afin qu'un webhook échoué n'interrompe pas l'agent :

import asyncio
import json
import urllib.request
from datetime import datetime


def _send_webhook(tool_name):
"""Synchronous helper that POSTs tool usage data to an external webhook."""
data = json.dumps(
{
"tool": tool_name,
"timestamp": datetime.now().isoformat(),
}
).encode()
req = urllib.request.Request(
"https://api.example.com/webhook",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req)


async def webhook_notifier(input_data, tool_use_id, context):
# Only fire after a tool completes (PostToolUse), not before
if input_data["hook_event_name"] != "PostToolUse":
return {}

try:
# Run the blocking HTTP call in a thread to avoid blocking the event loop
await asyncio.to_thread(_send_webhook, input_data["tool_name"])
except Exception as e:
# Log the error but don't raise. A failed webhook shouldn't stop the agent
print(f"Webhook request failed: {e}")

return {}

Transférer les notifications à Slack

Utilisez les hooks Notification pour recevoir les notifications système de l'agent et les transférer vers des services externes. Les notifications se déclenchent pour des types d'événements spécifiques : permission_prompt (Claude a besoin d'une permission), idle_prompt (Claude attend une entrée), auth_success (authentification terminée), elicitation_dialog (Claude invite l'utilisateur), elicitation_response (l'utilisateur a répondu à une élicitation), et elicitation_complete (une élicitation s'est fermée). Chaque notification inclut un champ message avec une description lisible par l'homme et optionnellement un title.

Cet exemple transfère chaque notification à un canal Slack. Il nécessite une URL de webhook entrant Slack, que vous créez en ajoutant une application à votre espace de travail Slack et en activant les webhooks entrants :

import asyncio
import json
import urllib.request

from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, HookMatcher


def _send_slack_notification(message):
"""Synchronous helper that sends a message to Slack via incoming webhook."""
data = json.dumps({"text": f"Agent status: {message}"}).encode()
req = urllib.request.Request(
"https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req)


async def notification_handler(input_data, tool_use_id, context):
try:
# Run the blocking HTTP call in a thread to avoid blocking the event loop
await asyncio.to_thread(_send_slack_notification, input_data.get("message", ""))
except Exception as e:
print(f"Failed to send notification: {e}")

# Return empty object. Notification hooks don't modify agent behavior
return {}


async def main():
options = ClaudeAgentOptions(
hooks={
# Register the hook for Notification events (no matcher needed)
"Notification": [HookMatcher(hooks=[notification_handler])],
},
)

async with ClaudeSDKClient(options=options) as client:
await client.query("Analyze this codebase")
async for message in client.receive_response():
print(message)


asyncio.run(main())

Corriger les problèmes courants

Hook ne se déclenche pas

  • Vérifiez que le nom de l'événement hook est correct et sensible à la casse (PreToolUse, pas preToolUse)
  • Vérifiez que votre motif matcher correspond exactement au nom de l'outil
  • Assurez-vous que le hook se trouve sous le type d'événement correct dans options.hooks
  • Pour les hooks non basés sur les outils comme Stop et SubagentStop, les matchers correspondent à des champs différents (consultez motifs matcher)
  • Les hooks peuvent ne pas se déclencher lorsque l'agent atteint la limite max_turns car la session se termine avant que les hooks puissent s'exécuter

Matcher ne filtre pas comme prévu

Les matchers ne correspondent qu'aux noms d'outils, pas aux chemins de fichiers ou à d'autres arguments. Pour filtrer par chemin de fichier, vérifiez tool_input.file_path à l'intérieur de votre hook :

const myHook: HookCallback = async (input, toolUseID, { signal }) => {
  const preInput = input as PreToolUseHookInput;
  const toolInput = preInput.tool_input as Record<string, unknown>;
  const filePath = toolInput?.file_path as string;
  if (!filePath?.endsWith(".md")) return {}; // Skip non-markdown files
  // Process markdown files...
  return {};
};

Délai d'expiration du hook

  • Augmentez la valeur timeout dans la configuration HookMatcher
  • Utilisez le AbortSignal du troisième argument de rappel pour gérer l'annulation correctement en TypeScript

Outil bloqué de manière inattendue

  • Vérifiez tous les hooks PreToolUse pour les retours permissionDecision: 'deny'
  • Ajoutez la journalisation à vos hooks pour voir quel permissionDecisionReason ils retournent
  • Vérifiez que les motifs matcher ne sont pas trop larges (un matcher vide correspond à tous les outils)

Entrée modifiée non appliquée

  • Assurez-vous que updatedInput se trouve à l'intérieur de hookSpecificOutput, pas au niveau supérieur :

    return {
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "allow",
        updatedInput: { command: "new command" }
      }
    };
    
  • Vous devez également retourner permissionDecision: 'allow' ou 'ask' pour que la modification d'entrée prenne effet

  • Incluez hookEventName dans hookSpecificOutput pour identifier le type de hook pour lequel la sortie est destinée

Hooks de session non disponibles en Python

SessionStart et SessionEnd peuvent être enregistrés en tant que hooks de rappel du SDK en TypeScript, mais ne sont pas disponibles dans le SDK Python (HookEvent les omet). En Python, ils ne sont disponibles que comme hooks de commande shell définis dans les fichiers de paramètres (par exemple, .claude/settings.json). Pour charger les hooks de commande shell à partir de votre application SDK, incluez la source de paramètre appropriée avec setting_sources ou settingSources :

options = ClaudeAgentOptions(
setting_sources=["project"],  # Loads .claude/settings.json including hooks
)

Pour exécuter la logique d'initialisation en tant que rappel du SDK Python à la place, utilisez le premier message de client.receive_response() comme déclencheur.

Les invites de permission des sous-agents se multiplient

Lors du lancement de plusieurs sous-agents, chacun peut demander des permissions séparément. Les sous-agents n'héritent pas automatiquement des permissions de l'agent parent. Pour éviter les invites répétées, utilisez les hooks PreToolUse pour approuver automatiquement des outils spécifiques, ou configurez des règles de permission qui s'appliquent aux sessions de sous-agent.

Boucles de hook récursives avec des sous-agents

Un hook UserPromptSubmit qui lance des sous-agents peut créer des boucles infinies si ces sous-agents déclenchent le même hook. Pour éviter cela :

  • Vérifiez un indicateur de sous-agent dans l'entrée du hook avant de lancer
  • Utilisez une variable partagée ou un état de session pour suivre si vous êtes déjà à l'intérieur d'un sous-agent
  • Limitez les hooks pour qu'ils s'exécutent uniquement pour la session d'agent de niveau supérieur

systemMessage n'apparaît pas dans la sortie

Le champ systemMessage affiche un message à l'utilisateur, pas au modèle. Par défaut, le SDK ne fait pas apparaître la sortie du hook dans le flux de messages, donc le message peut ne pas apparaître à moins que vous définissiez includeHookEvents (include_hook_events en Python). Pour transmettre du contexte au modèle à la place, retournez additionalContext.

Si vous avez besoin de faire apparaître les décisions de hook à votre application de manière fiable, enregistrez-les séparément ou utilisez un canal de sortie dédié.

Ressources connexes