Skip to main content

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

Utilisez le crochet onPostToolUse pour transformer les résultats de l'outil, enregistrer l'exécution de l'outil et ajouter un contexte après qu'un outil s'exécute dans Kit de développement logiciel (SDK) Copilot.

Qui peut utiliser cette fonctionnalité ?

SDK GitHub Copilot est disponible dans tous les forfaits Copilot.

Remarque

Kit de développement logiciel (SDK) Copilot est actuellement en préversion technique. Les fonctionnalités et la disponibilité sont susceptibles de changer.

Le onPostToolUse hook est appelé après l’exécution d’un outil. Utilisez-le pour :

  • 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

Signature de point d'interception

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

Pour obtenir les signatures de hook dans Python, Go et .NET, consultez le github/copilot-sdk référentiel.

Input

ChampTypeDescription
timestampnombreHorodatage Unix lorsque le hook a été déclenché
cwdficelleRépertoire de travail actuel
toolNameficelleNom 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 des champs suivants.

ChampTypeDescription
modifiedResultobjetRésultat modifié à utiliser au lieu d’origine
additionalContextficelleContexte 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

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
    },
  },
});

Pour obtenir des exemples dans Python, Go et .NET, consultez le github/copilot-sdk référentiel.

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 importants

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: number;
  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

  •         **Retournez `null` quand aucune modification n’est nécessaire.** Cela est plus efficace que de retourner un objet vide ou le même résultat.
    
  •         **Soyez prudent avec la modification du résultat.** 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.
    
  •         **Utiliser `additionalContext` pour les indicateurs.** Au lieu de modifier les résultats, ajoutez un contexte pour aider le modèle à les interpréter.
    
  •         **Prenez en compte la confidentialité lors de la journalisation.** Les résultats de l’outil peuvent contenir des données sensibles. Appliquez la rédaction avant la journalisation.
    
  •         **Gardez les crochets bien serrés.** Les crochets post-outils s’exécutent de manière synchrone. Le traitement lourd doit être effectué de manière asynchrone ou par lot.
    

Lectures complémentaires

  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/quickstart)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/pre-tool-use)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/error-handling)