Skip to main content

Hook exécuté après l’utilisation d’un outil

Le hook onPostToolUse est appelé après qu’un outil s’est exécuté avec succès. Utilisez-le pour :

Dans cet article

  • Transformer ou filtrer les résultats de l’outil
  • Exécution de l’outil de journalisation pour l’audit
  • Ajouter un contexte en fonction des résultats
  • Supprimer les résultats de la conversation
          **Variante d’échec** : `onPostToolUse` se déclenche uniquement pour les exécutions réussies des outils. Pour observer les appels d’outils **échoués**, enregistrez `onPostToolUseFailure` (`on_post_tool_use_failure` en Python, `OnPostToolUseFailure` en Go/.NET, `on_post_tool_use_failure` en Rust). Le gestionnaire reçoit `{ sessionId, toolName, toolArgs, error, timestamp, workingDirectory }` ( le `error` champ est une chaîne extraite du résultat d’échec de l’outil) et peut revenir `{ additionalContext: string }` pour injecter des conseils supplémentaires pour le modèle (par exemple, des indicateurs de nouvelle tentative). Consultez [l’AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/hooks-overview) pour obtenir la liste complète.

Signature du hook

Langages de code navigation

TypeScript
import type {
  PostToolUseHookInput,
  HookInvocation,
  PostToolUseHookOutput,
} from "@github/copilot-sdk";
type PostToolUseHandler = (
  input: PostToolUseHookInput,
  invocation: HookInvocation,
) => Promise<PostToolUseHookOutput | null | undefined>;
type PostToolUseHandler = (
  input: PostToolUseHookInput,
  invocation: HookInvocation,
) => Promise<PostToolUseHookOutput | null | undefined>;

Input

ChampCatégorieDescription
timestampType d’horodatage du Kit de développement logiciel (SDK)Lorsque le crochet a été déclenché
workingDirectorystringRépertoire de travail actuel
toolNamestringNom de l’outil appelé
toolArgsObjetArguments passés à l’outil
toolResultObjetRésultat retourné par l’outil

Sortie

Retournez null ou undefined pour laisser le résultat inchangé. Sinon, retournez un objet avec l’un de ces champs :

ChampCatégorieDescription
modifiedResultObjetRésultat modifié à utiliser au lieu d’origine
additionalContextstringContexte supplémentaire injecté dans la conversation
suppressOutputbooléenSi la valeur est true, le résultat n’apparaît pas dans la conversation

Exemples

Journaliser tous les résultats de l’outil

Langages de code navigation

TypeScript
const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input, invocation) => {
      console.log(`[${invocation.sessionId}] Tool: ${input.toolName}`);
      console.log(`  Args: ${JSON.stringify(input.toolArgs)}`);
      console.log(`  Result: ${JSON.stringify(input.toolResult)}`);
      return null; // Pass through unchanged
    },
  },
});

Réactez les données sensibles

const SENSITIVE_PATTERNS = [
  /api[_-]?key["\s:=]+["']?[\w-]+["']?/gi,
  /password["\s:=]+["']?[\w-]+["']?/gi,
  /secret["\s:=]+["']?[\w-]+["']?/gi,
];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (typeof input.toolResult === "string") {
        let redacted = input.toolResult;
        for (const pattern of SENSITIVE_PATTERNS) {
          redacted = redacted.replace(pattern, "[REDACTED]");
        }

        if (redacted !== input.toolResult) {
          return { modifiedResult: redacted };
        }
      }
      return null;
    },
  },
});

Tronquer les résultats volumineux

const MAX_RESULT_LENGTH = 10000;

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      const resultStr = JSON.stringify(input.toolResult);

      if (resultStr.length > MAX_RESULT_LENGTH) {
        return {
          modifiedResult: {
            truncated: true,
            originalLength: resultStr.length,
            content: resultStr.substring(0, MAX_RESULT_LENGTH) + "...",
          },
          additionalContext: `Note: Result was truncated from ${resultStr.length} to ${MAX_RESULT_LENGTH} characters.`,
        };
      }
      return null;
    },
  },
});

Ajouter un contexte en fonction des résultats

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      // If a file read returned an error, add helpful context
      if (input.toolName === "read_file" && input.toolResult?.error) {
        return {
          additionalContext:
            "Tip: If the file doesn't exist, consider creating it or checking the path.",
        };
      }

      // If shell command failed, add debugging hint
      if (input.toolName === "shell" && input.toolResult?.exitCode !== 0) {
        return {
          additionalContext:
            "The command failed. Check if required dependencies are installed.",
        };
      }

      return null;
    },
  },
});

Filtrer les traces de pile d’erreurs

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (input.toolResult?.error && input.toolResult?.stack) {
        // Remove internal stack trace details
        return {
          modifiedResult: {
            error: input.toolResult.error,
            // Keep only first 3 lines of stack
            stack: input.toolResult.stack.split("\n").slice(0, 3).join("\n"),
          },
        };
      }
      return null;
    },
  },
});

Piste d’audit pour la conformité

interface AuditEntry {
  timestamp: Date;
  sessionId: string;
  toolName: string;
  args: unknown;
  result: unknown;
  success: boolean;
}

const auditLog: AuditEntry[] = [];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input, invocation) => {
      auditLog.push({
        timestamp: input.timestamp,
        sessionId: invocation.sessionId,
        toolName: input.toolName,
        args: input.toolArgs,
        result: input.toolResult,
        success: !input.toolResult?.error,
      });

      // Optionally persist to database/file
      await saveAuditLog(auditLog);

      return null;
    },
  },
});

Supprimer les résultats bruyants

const NOISY_TOOLS = ["list_directory", "search_codebase"];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (NOISY_TOOLS.includes(input.toolName)) {
        // Summarize instead of showing full result
        const items = Array.isArray(input.toolResult)
          ? input.toolResult
          : input.toolResult?.items || [];

        return {
          modifiedResult: {
            summary: `Found ${items.length} items`,
            firstFew: items.slice(0, 5),
          },
        };
      }
      return null;
    },
  },
});

Bonnes pratiques

  1. Retourne null quand aucune modification n’est nécessaire : cela est plus efficace que de renvoyer un objet vide ou le même résultat.

  2. Soyez prudent avec la modification des résultats : la modification des résultats peut affecter la façon dont le modèle interprète la sortie de l’outil. Modifiez uniquement si nécessaire.

  3. Utiliser additionalContext pour les indicateurs : au lieu de modifier les résultats, ajoutez un contexte pour aider le modèle à les interpréter.

  4. Prenez en compte la confidentialité lors de la journalisation : les résultats de l’outil peuvent contenir des données sensibles. Tronquer les résultats volumineux.

  5.           **Veillez à ce que les hooks restent rapides** : les hooks post-outil s’exécutent de manière synchrone. Le traitement lourd doit être effectué de manière asynchrone ou par lot.
    

Voir aussi