Skip to main content

ツール利用後のフック

          `onPostToolUse`フックを使用して、ツールの結果を変換し、ツールの実行をログに記録し、ツールがCopilot SDKで実行された後にコンテキストを追加します。

この機能を使用できるユーザーについて

GitHub Copilot SDK は、すべての Copilot プランで使用できます。

メモ

Copilot SDK は現在 テクニカル プレビューです。 機能と可用性は変更される場合があります。

          `onPostToolUse` フックは、ツールの実行**後**に呼び出されます。 これは次の目的で使用されます。
  • 変換またはフィルターツールの結果
  • 監査のためのログ ツールの実行
  • 結果に基づいてコンテキストを追加する
  • 会話からの結果を抑制する

フック署名

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

Python、Go、.NET のフック署名については、 github/copilot-sdk リポジトリを参照してください。

入力

フィールドタイプ説明
timestamp数値フックがトリガーされたときの Unix タイムスタンプ
cwd文字列現在の作業ディレクトリ
toolName文字列呼び出されたツールの名前
toolArgsオブジェクトツールに渡された引数
toolResultオブジェクトツールによって返される結果

アウトプット

結果を変更せずに渡す null または undefined を返します。 それ以外の場合は、次のいずれかのフィールドを持つオブジェクトを返します。

フィールドタイプ説明
modifiedResultオブジェクト元の結果ではなく使用するように変更された結果
additionalContext文字列会話に挿入された追加のコンテキスト
suppressOutputブーリアンtrue、結果は会話に表示されません

例示

すべてのツールの結果をログに記録する

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、Go、.NET の例については、 github/copilot-sdk リポジトリを参照してください。

機密データを編集する

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

大きな結果を切り捨てる

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

結果に基づいてコンテキストを追加する

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

エラースタックトレースをフィルタリングする

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

コンプライアンスの監査証跡

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

ノイズの多い結果を抑制する

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

ベスト プラクティス

  •         **変更が必要ない場合は、 `null` を返します。** これは、空のオブジェクトまたは同じ結果を返すよりも効率的です。
    
  •         **結果の変更には注意してください。** 結果を変更すると、モデルがツールの出力を解釈する方法に影響する可能性があります。 必要な場合にのみ変更します。
    
  •         **ヒントには `additionalContext` を使用します。** 結果を変更する代わりに、モデルが結果を解釈するのに役立つコンテキストを追加します。
    
  •         **ログ記録時のプライバシーを考慮してください。** ツールの結果に機密データが含まれている場合があります。 ログ記録の前に編集を適用します。
    
  •         **フックをしっかりと固定してください。** ツール後フックは同期的に実行されます。 大量の処理は、非同期またはバッチ処理で行う必要があります。
    

詳細については、次を参照してください。

  •         [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)