# Session Hooks Hooks allow you to intercept and customize the behavior of Copilot sessions at key points in the conversation lifecycle. Use hooks to: - **Control tool execution** - approve, deny, or modify tool calls - **Transform results** - modify tool outputs before they're processed - **Add context** - inject additional information at session start - **Handle errors** - implement custom error handling - **Audit and log** - track all interactions for compliance ## Available Hooks | Hook | Trigger | Use Case | |------|---------|----------| | [`onPreToolUse`](./pre-tool-use.md) | Before a tool executes | Permission control, argument validation | | [`onPostToolUse`](./post-tool-use.md) | After a tool executes | Result transformation, logging | | [`onUserPromptSubmitted`](./user-prompt-submitted.md) | When user sends a message | Prompt modification, filtering | | [`onSessionStart`](./session-lifecycle.md#session-start) | Session begins | Add context, configure session | | [`onSessionEnd`](./session-lifecycle.md#session-end) | Session ends | Cleanup, analytics | | [`onErrorOccurred`](./error-handling.md) | Error happens | Custom error handling | ## Quick Start
Node.js / TypeScript ```typescript import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { console.log(`Tool called: ${input.toolName}`); // Allow all tools return { permissionDecision: "allow" }; }, onPostToolUse: async (input) => { console.log(`Tool result: ${JSON.stringify(input.toolResult)}`); return null; // No modifications }, onSessionStart: async (input) => { return { additionalContext: "User prefers concise answers." }; }, }, }); ```
Python ```python from copilot import CopilotClient async def main(): client = CopilotClient() await client.start() async def on_pre_tool_use(input_data, invocation): print(f"Tool called: {input_data['toolName']}") return {"permissionDecision": "allow"} async def on_post_tool_use(input_data, invocation): print(f"Tool result: {input_data['toolResult']}") return None async def on_session_start(input_data, invocation): return {"additionalContext": "User prefers concise answers."} session = await client.create_session({ "hooks": { "on_pre_tool_use": on_pre_tool_use, "on_post_tool_use": on_post_tool_use, "on_session_start": on_session_start, } }) ```
Go ```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{ Hooks: &copilot.SessionHooks{ OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { fmt.Printf("Tool called: %s\n", input.ToolName) return &copilot.PreToolUseHookOutput{ PermissionDecision: "allow", }, nil }, OnPostToolUse: func(input copilot.PostToolUseHookInput, inv copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { fmt.Printf("Tool result: %v\n", input.ToolResult) return nil, nil }, OnSessionStart: func(input copilot.SessionStartHookInput, inv copilot.HookInvocation) (*copilot.SessionStartHookOutput, error) { return &copilot.SessionStartHookOutput{ AdditionalContext: "User prefers concise answers.", }, nil }, }, }) _ = session } ```
.NET ```csharp using GitHub.Copilot.SDK; var client = new CopilotClient(); var session = await client.CreateSessionAsync(new SessionConfig { Hooks = new SessionHooks { OnPreToolUse = (input, invocation) => { Console.WriteLine($"Tool called: {input.ToolName}"); return Task.FromResult( new PreToolUseHookOutput { PermissionDecision = "allow" } ); }, OnPostToolUse = (input, invocation) => { Console.WriteLine($"Tool result: {input.ToolResult}"); return Task.FromResult(null); }, OnSessionStart = (input, invocation) => { return Task.FromResult( new SessionStartHookOutput { AdditionalContext = "User prefers concise answers." } ); }, }, }); ```
## Hook Invocation Context Every hook receives an `invocation` parameter with context about the current session: | Field | Type | Description | |-------|------|-------------| | `sessionId` | string | The ID of the current session | This allows hooks to maintain state or perform session-specific logic. ## Common Patterns ### Logging All Tool Calls ```typescript const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { console.log(`[${new Date().toISOString()}] Tool: ${input.toolName}, Args: ${JSON.stringify(input.toolArgs)}`); return { permissionDecision: "allow" }; }, onPostToolUse: async (input) => { console.log(`[${new Date().toISOString()}] Result: ${JSON.stringify(input.toolResult)}`); return null; }, }, }); ``` ### Blocking Dangerous Tools ```typescript const BLOCKED_TOOLS = ["shell", "bash", "exec"]; const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { if (BLOCKED_TOOLS.includes(input.toolName)) { return { permissionDecision: "deny", permissionDecisionReason: "Shell access is not permitted", }; } return { permissionDecision: "allow" }; }, }, }); ``` ### Adding User Context ```typescript const session = await client.createSession({ hooks: { onSessionStart: async () => { const userPrefs = await loadUserPreferences(); return { additionalContext: `User preferences: ${JSON.stringify(userPrefs)}`, }; }, }, }); ``` ## Hook Guides - **[Pre-Tool Use Hook](./pre-tool-use.md)** - Control tool execution permissions - **[Post-Tool Use Hook](./post-tool-use.md)** - Transform tool results - **[User Prompt Submitted Hook](./user-prompt-submitted.md)** - Modify user prompts - **[Session Lifecycle Hooks](./session-lifecycle.md)** - Session start and end - **[Error Handling Hook](./error-handling.md)** - Custom error handling ## See Also - [Getting Started Guide](../getting-started.md) - [Custom Tools](../getting-started.md#step-4-add-a-custom-tool) - [Debugging Guide](../debugging.md)