Skip to main content

Gancho de uso pós-ferramenta

O onPostToolUse gancho é chamado depois que uma ferramenta é executada com êxito. Use-o para:

  • Transformar ou filtrar resultados da ferramenta
  • Registro da execução da ferramenta para auditoria
  • Adicionar contexto com base nos resultados
  • Suprimir resultados da conversa
          **Variante de falha** – `onPostToolUse` só é acionada para execuções de ferramenta bem-sucedidas. Para observar chamadas de ferramenta **com falha**, registre `onPostToolUseFailure` (`on_post_tool_use_failure` no Python, `OnPostToolUseFailure` em Go/.NET, `on_post_tool_use_failure` no Rust). O manipulador recebe `{ sessionId, toolName, toolArgs, error, timestamp, workingDirectory }` — o campo `error` é uma string extraída do resultado de falha da ferramenta — e pode retornar `{ additionalContext: string }` para injetar orientações adicionais para o modelo (por exemplo, dicas para nova tentativa). Consulte o [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/hooks-overview) para obter a lista completa.

Assinatura de Hook

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>;
Python
from copilot.session import PostToolUseHookInput, PostToolUseHookOutput
from typing import Callable, Awaitable

PostToolUseHandler = Callable[
    [PostToolUseHookInput, dict[str, str]],
    Awaitable[PostToolUseHookOutput | None]
]
PostToolUseHandler = Callable[
    [PostToolUseHookInput, dict[str, str]],
    Awaitable[PostToolUseHookOutput | None]
]
Go
package main

import copilot "github.com/github/copilot-sdk/go"

type PostToolUseHandler func(
    input copilot.PostToolUseHookInput,
    invocation copilot.HookInvocation,
) (*copilot.PostToolUseHookOutput, error)

func main() {}
type PostToolUseHandler func(
    input PostToolUseHookInput,
    invocation HookInvocation,
) (*PostToolUseHookOutput, error)
.NET
using GitHub.Copilot;

public delegate Task<PostToolUseHookOutput?> PostToolUseHandler(
    PostToolUseHookInput input,
    HookInvocation invocation);
public delegate Task<PostToolUseHookOutput?> PostToolUseHandler(
    PostToolUseHookInput input,
    HookInvocation invocation);
Java
import com.github.copilot.sdk.json.*;

PostToolUseHandler postToolUseHandler;

Entrada

CampoTipoDescription
timestampTipo de carimbo de data/hora do SDKQuando o gancho foi acionado
workingDirectorycadeiaDiretório de trabalho atual
toolNamecadeiaNome da ferramenta que foi chamada
toolArgsobjetoArgumentos que foram passados para a ferramenta
toolResultobjetoResultado retornado pela ferramenta

Saída

Retorne null ou undefined para passar o resultado inalterado. Caso contrário, retorne um objeto com qualquer um desses campos:

CampoTipoDescription
modifiedResultobjetoResultado modificado a ser usado em vez de original
additionalContextcadeiaContexto extra injetado na conversa
suppressOutputbooleanSe verdadeiro, o resultado não aparecerá na conversa

Exemplos

Registrar todos os resultados da ferramenta

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
    },
  },
});
Python
from copilot.session import PermissionHandler

async def on_post_tool_use(input_data, invocation):
    print(f"[{invocation['session_id']}] Tool: {input_data['toolName']}")
    print(f"  Args: {input_data['toolArgs']}")
    print(f"  Result: {input_data['toolResult']}")
    return None  # Pass through unchanged

session = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={"on_post_tool_use": on_post_tool_use})
Go
package main

import (
    "context"
    "fmt"
    copilot "github.com/github/copilot-sdk/go"
)

func main() {
    client := copilot.NewClient(nil)
    session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
        OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
        Hooks: &copilot.SessionHooks{
            OnPostToolUse: func(input copilot.PostToolUseHookInput, inv copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) {
                fmt.Printf("[%s] Tool: %s\n", inv.SessionID, input.ToolName)
                fmt.Printf("  Args: %v\n", input.ToolArgs)
                fmt.Printf("  Result: %v\n", input.ToolResult)
                return nil, nil
            },
        },
    })
    _ = session
}
session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
    Hooks: &copilot.SessionHooks{
        OnPostToolUse: func(input copilot.PostToolUseHookInput, inv copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) {
            fmt.Printf("[%s] Tool: %s\n", inv.SessionID, input.ToolName)
            fmt.Printf("  Args: %v\n", input.ToolArgs)
            fmt.Printf("  Result: %v\n", input.ToolResult)
            return nil, nil
        },
    },
})
.NET
using GitHub.Copilot;

public static class PostToolUseExample
{
    public static async Task Main()
    {
        await using var client = new CopilotClient();
        var session = await client.CreateSessionAsync(new SessionConfig
        {
            Hooks = new SessionHooks
            {
                OnPostToolUse = (input, invocation) =>
                {
                    Console.WriteLine($"[{invocation.SessionId}] Tool: {input.ToolName}");
                    Console.WriteLine($"  Args: {input.ToolArgs}");
                    Console.WriteLine($"  Result: {input.ToolResult}");
                    return Task.FromResult<PostToolUseHookOutput?>(null);
                },
            },
        });
    }
}
var session = await client.CreateSessionAsync(new SessionConfig
{
    Hooks = new SessionHooks
    {
        OnPostToolUse = (input, invocation) =>
        {
            Console.WriteLine($"[{invocation.SessionId}] Tool: {input.ToolName}");
            Console.WriteLine($"  Args: {input.ToolArgs}");
            Console.WriteLine($"  Result: {input.ToolResult}");
            return Task.FromResult<PostToolUseHookOutput?>(null);
        },
    },
});
Java
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.CompletableFuture;

var hooks = new SessionHooks()
    .setOnPostToolUse((input, invocation) -> {
        System.out.println("[" + invocation.getSessionId() + "] Tool: " + input.getToolName());
        System.out.println("  Args: " + input.getToolArgs());
        System.out.println("  Result: " + input.getToolResult());
        return CompletableFuture.completedFuture(null);
    });

var session = client.createSession(
    new SessionConfig()
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
        .setHooks(hooks)
).get();

Redigir dados confidenciais

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

Truncar resultados grandes

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

Adicionar contexto com base nos resultados

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

Filtrar rastreamentos de pilha de erros

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

Trilha de auditoria para conformidade

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

Suprimir resultados ruidosos

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

Práticas recomendadas

  1. Retornar null quando nenhuma alteração for necessária – isso é mais eficiente do que retornar um objeto vazio ou o mesmo resultado.

  2. Tenha cuidado com a modificação do resultado – a alteração dos resultados pode afetar a forma como o modelo interpreta a saída da ferramenta. Somente modifique quando necessário.

  3. Use additionalContext para dicas – em vez de modificar resultados, adicione contexto para ajudar o modelo a interpretá-los.

  4. Considere a privacidade ao gerar logs - Os resultados da ferramenta podem conter dados sensíveis. Aplique a edição antes do log.

  5. Mantenha os hooks rápidos - os hooks executados após a ferramenta são executados de forma síncrona. O processamento pesado deve ser feito de forma assíncrona ou em lote.

Consulte também