Skip to main content

ツール使用後フック

onPostToolUse フックは、ツールが正常に実行された****後に呼び出されます。 これは次の目的で使用されます。

  • 変換またはフィルターツールの結果
  • 監査のためのログ ツールの実行
  • 結果に基づいてコンテキストを追加する
  • 会話からの結果を抑制する
          **失敗バリアント** — `onPostToolUse` は、ツールの実行が成功した場合にのみ発生します。 

failed ツールの呼び出しを観察するには、onPostToolUseFailure (Python では on_post_tool_use_failure、Go/.NET では OnPostToolUseFailure、Rust では on_post_tool_use_failure) を登録します。 ハンドラーは、 { sessionId, toolName, toolArgs, error, timestamp, workingDirectory } ( error フィールドはツールのエラー結果から抽出された文字列) を受け取り、 { additionalContext: string } を返してモデルの追加のガイダンス (再試行ヒントなど) を挿入する場合があります。 完全な一覧については 、AUTOTITLE を参照してください。

フック署名

コード言語 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>;

入力

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

アウトプット

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

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

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

コード言語 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
    },
  },
});

機密データを編集する

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

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

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

ベスト プラクティス

  1. 変更が不要な場合に null を返す - これは、空のオブジェクトまたは同じ結果を返すよりも効率的です。

  2. 結果の変更には注意してください 。 結果を変更すると、モデルがツールの出力を解釈する方法に影響する可能性があります。 必要な場合にのみ変更します。

  3. ヒントに additionalContext を使用 する - 結果を変更する代わりに、モデルがそれらを解釈できるようにコンテキストを追加します。

  4. ログ記録時にプライバシーを考慮する - ツールの結果に機密データが含まれている可能性があります。 ログ記録の前に編集を適用します。

  5. フックは高速に保つ - ツール実行後のフックは同期実行されます。 大量の処理は、非同期またはバッチ処理で行う必要があります。

こちらも参照ください