참고
코필로트 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)