{"meta":{"title":"Hook exécuté après l’utilisation d’un outil","intro":"Le hook onPostToolUse est appelé après qu’un outil s’est exécuté avec succès. Utilisez-le pour :","product":"GitHub Copilot","breadcrumbs":[{"href":"/fr/copilot","title":"GitHub Copilot"},{"href":"/fr/copilot/how-tos","title":"Procédures"},{"href":"/fr/copilot/how-tos/copilot-sdk","title":"Kit de développement logiciel (SDK) Copilot"},{"href":"/fr/copilot/how-tos/copilot-sdk/hooks","title":"Utiliser des crochets"},{"href":"/fr/copilot/how-tos/copilot-sdk/hooks/post-tool-use","title":"Post Tool Use"}],"documentType":"article"},"body":"# Hook exécuté après l’utilisation d’un outil\n\nLe hook onPostToolUse est appelé après qu’un outil s’est exécuté avec succès. Utilisez-le pour :\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* Transformer ou filtrer les résultats de l’outil\n* Exécution de l’outil de journalisation pour l’audit\n* Ajouter un contexte en fonction des résultats\n* Supprimer les résultats de la conversation\n\n> ```\n>           **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.\n> ```\n\n<a id=\"failure-variant\"></a>\n\n## Signature du hook\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\ntype PostToolUseHandler = (\n  input: PostToolUseHookInput,\n  invocation: HookInvocation,\n) => Promise<PostToolUseHookOutput | null | undefined>;\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nPostToolUseHandler = Callable[\n    [PostToolUseHookInput, dict[str, str]],\n    Awaitable[PostToolUseHookOutput | None]\n]\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\ntype PostToolUseHandler func(\n    input PostToolUseHookInput,\n    invocation HookInvocation,\n) (*PostToolUseHookOutput, error)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\npublic delegate Task<PostToolUseHookOutput?> PostToolUseHandler(\n    PostToolUseHookInput input,\n    HookInvocation invocation);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\n@FunctionalInterface\npublic interface PostToolUseHandler {\n    CompletableFuture<PostToolUseHookOutput> handle(\n        PostToolUseHookInput input,\n        HookInvocation invocation);\n}\n```\n\n</div>\n\n</div>\n\n## Input\n\n| Champ              | Catégorie                                                | Description                        |\n| ------------------ | -------------------------------------------------------- | ---------------------------------- |\n| `timestamp`        | Type d’horodatage du Kit de développement logiciel (SDK) | Lorsque le crochet a été déclenché |\n| `workingDirectory` | string                                                   | Répertoire de travail actuel       |\n| `toolName`         | string                                                   | Nom de l’outil appelé              |\n| `toolArgs`         | Objet                                                    | Arguments passés à l’outil         |\n| `toolResult`       | Objet                                                    | Résultat retourné par l’outil      |\n\n## Sortie\n\nRetournez `null` ou `undefined` pour laisser le résultat inchangé. Sinon, retournez un objet avec l’un de ces champs :\n\n| Champ               | Catégorie | Description                                                            |\n| ------------------- | --------- | ---------------------------------------------------------------------- |\n| `modifiedResult`    | Objet     | Résultat modifié à utiliser au lieu d’origine                          |\n| `additionalContext` | string    | Contexte supplémentaire injecté dans la conversation                   |\n| `suppressOutput`    | booléen   | Si la valeur est true, le résultat n’apparaît pas dans la conversation |\n\n## Exemples\n\n### Journaliser tous les résultats de l’outil\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input, invocation) => {\n      console.log(`[${invocation.sessionId}] Tool: ${input.toolName}`);\n      console.log(`  Args: ${JSON.stringify(input.toolArgs)}`);\n      console.log(`  Result: ${JSON.stringify(input.toolResult)}`);\n      return null; // Pass through unchanged\n    },\n  },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PermissionHandler\n\nasync def on_post_tool_use(input_data, invocation):\n    print(f\"[{invocation['session_id']}] Tool: {input_data['toolName']}\")\n    print(f\"  Args: {input_data['toolArgs']}\")\n    print(f\"  Result: {input_data['toolResult']}\")\n    return None  # Pass through unchanged\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\"on_post_tool_use\": on_post_tool_use})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\nsession, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnPostToolUse: func(input copilot.PostToolUseHookInput, inv copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) {\n            fmt.Printf(\"[%s] Tool: %s\\n\", inv.SessionID, input.ToolName)\n            fmt.Printf(\"  Args: %v\\n\", input.ToolArgs)\n            fmt.Printf(\"  Result: %v\\n\", input.ToolResult)\n            return nil, nil\n        },\n    },\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnPostToolUse = (input, invocation) =>\n        {\n            Console.WriteLine($\"[{invocation.SessionId}] Tool: {input.ToolName}\");\n            Console.WriteLine($\"  Args: {input.ToolArgs}\");\n            Console.WriteLine($\"  Result: {input.ToolResult}\");\n            return Task.FromResult<PostToolUseHookOutput?>(null);\n        },\n    },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.*;\nimport com.github.copilot.rpc.*;\nimport java.util.concurrent.CompletableFuture;\n\nvar hooks = new SessionHooks()\n    .setOnPostToolUse((input, invocation) -> {\n        System.out.println(\"[\" + invocation.getSessionId() + \"] Tool: \" + input.getToolName());\n        System.out.println(\"  Args: \" + input.getToolArgs());\n        System.out.println(\"  Result: \" + input.getToolResult());\n        return CompletableFuture.completedFuture(null);\n    });\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n        .setHooks(hooks)\n).get();\n```\n\n</div>\n\n</div>\n\n### Réactez les données sensibles\n\n```typescript\nconst SENSITIVE_PATTERNS = [\n  /api[_-]?key[\"\\s:=]+[\"']?[\\w-]+[\"']?/gi,\n  /password[\"\\s:=]+[\"']?[\\w-]+[\"']?/gi,\n  /secret[\"\\s:=]+[\"']?[\\w-]+[\"']?/gi,\n];\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      if (typeof input.toolResult === \"string\") {\n        let redacted = input.toolResult;\n        for (const pattern of SENSITIVE_PATTERNS) {\n          redacted = redacted.replace(pattern, \"[REDACTED]\");\n        }\n\n        if (redacted !== input.toolResult) {\n          return { modifiedResult: redacted };\n        }\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Tronquer les résultats volumineux\n\n```typescript\nconst MAX_RESULT_LENGTH = 10000;\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      const resultStr = JSON.stringify(input.toolResult);\n\n      if (resultStr.length > MAX_RESULT_LENGTH) {\n        return {\n          modifiedResult: {\n            truncated: true,\n            originalLength: resultStr.length,\n            content: resultStr.substring(0, MAX_RESULT_LENGTH) + \"...\",\n          },\n          additionalContext: `Note: Result was truncated from ${resultStr.length} to ${MAX_RESULT_LENGTH} characters.`,\n        };\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Ajouter un contexte en fonction des résultats\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      // If a file read returned an error, add helpful context\n      if (input.toolName === \"read_file\" && input.toolResult?.error) {\n        return {\n          additionalContext:\n            \"Tip: If the file doesn't exist, consider creating it or checking the path.\",\n        };\n      }\n\n      // If shell command failed, add debugging hint\n      if (input.toolName === \"shell\" && input.toolResult?.exitCode !== 0) {\n        return {\n          additionalContext:\n            \"The command failed. Check if required dependencies are installed.\",\n        };\n      }\n\n      return null;\n    },\n  },\n});\n```\n\n### Filtrer les traces de pile d’erreurs\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      if (input.toolResult?.error && input.toolResult?.stack) {\n        // Remove internal stack trace details\n        return {\n          modifiedResult: {\n            error: input.toolResult.error,\n            // Keep only first 3 lines of stack\n            stack: input.toolResult.stack.split(\"\\n\").slice(0, 3).join(\"\\n\"),\n          },\n        };\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Piste d’audit pour la conformité\n\n```typescript\ninterface AuditEntry {\n  timestamp: Date;\n  sessionId: string;\n  toolName: string;\n  args: unknown;\n  result: unknown;\n  success: boolean;\n}\n\nconst auditLog: AuditEntry[] = [];\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        toolName: input.toolName,\n        args: input.toolArgs,\n        result: input.toolResult,\n        success: !input.toolResult?.error,\n      });\n\n      // Optionally persist to database/file\n      await saveAuditLog(auditLog);\n\n      return null;\n    },\n  },\n});\n```\n\n### Supprimer les résultats bruyants\n\n```typescript\nconst NOISY_TOOLS = [\"list_directory\", \"search_codebase\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      if (NOISY_TOOLS.includes(input.toolName)) {\n        // Summarize instead of showing full result\n        const items = Array.isArray(input.toolResult)\n          ? input.toolResult\n          : input.toolResult?.items || [];\n\n        return {\n          modifiedResult: {\n            summary: `Found ${items.length} items`,\n            firstFew: items.slice(0, 5),\n          },\n        };\n      }\n      return null;\n    },\n  },\n});\n```\n\n## Bonnes pratiques\n\n1. **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.\n\n2. **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.\n\n3. **Utiliser `additionalContext` pour les indicateurs** : au lieu de modifier les résultats, ajoutez un contexte pour aider le modèle à les interpréter.\n\n4. **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.\n\n5. **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.\n\n## Voir aussi\n\n* [Utiliser des crochets](/fr/copilot/how-tos/copilot-sdk/hooks)\n* [Hook exécuté avant l’utilisation d’un outil](/fr/copilot/how-tos/copilot-sdk/hooks/pre-tool-use)\n* [Hook de gestion des erreurs](/fr/copilot/how-tos/copilot-sdk/hooks/error-handling)"}