# Session hooks

> Hooks allow you to intercept and customize the behavior of Copilot sessions at key points in the conversation lifecycle. Use hooks to:

- 网址：https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/hooks-overview/
- 来源：GitHub Copilot 官方文档原文（英文），CC-BY-4.0 许可，同步于 2026-10-11
- 官方原文：https://docs.github.com/en/copilot/how-tos/copilot-sdk/hooks/hooks-overview

---
<!-- markdownlint-disable GHD046 GHD005 -->
<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

* **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 |
|------|---------|----------|
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/pre-tool-use/) | Before a tool executes | Permission control, argument validation |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/post-tool-use/) | After a tool executes (success only) | Result transformation, logging |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/post-tool-use/#failure-variant) | After a tool execution whose result was a failure | Inject retry guidance, log failures |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted/) | When user sends a message | Prompt modification, filtering |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/user-prompt-transformed/) | After runtime prompt transformation | Inspect or replace model-facing content |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/session-lifecycle/#session-start-hook) | Session begins | Add context, configure session |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/session-lifecycle/#session-end-hook) | Session ends | Cleanup, analytics |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/error-handling/) | Error happens | Custom error handling |
| [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/session-lifecycle/#agent-stop-hook) | Top-level agent naturally stops | Validate completion or request another turn |

## Quick start

**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
from copilot.session import PermissionHandler

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(on_permission_request=PermissionHandler.approve_all, hooks={
            "on_pre_tool_use": on_pre_tool_use,
            "on_post_tool_use": on_post_tool_use,
            "on_session_start": on_session_start,
        })
```

**go**

```golang
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
}
```

**dotnet**

```csharp
using GitHub.Copilot;

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<PreToolUseHookOutput?>(
                new PreToolUseHookOutput { PermissionDecision = "allow" }
            );
        },
        OnPostToolUse = (input, invocation) =>
        {
            Console.WriteLine($"Tool result: {input.ToolResult}");
            return Task.FromResult<PostToolUseHookOutput?>(null);
        },
        OnSessionStart = (input, invocation) =>
        {
            return Task.FromResult<SessionStartHookOutput?>(
                new SessionStartHookOutput { AdditionalContext = "User prefers concise answers." }
            );
        },
    },
});
```

**java**

```java
import com.github.copilot.*;
import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;

try (var client = new CopilotClient()) {
    client.start().get();

    var hooks = new SessionHooks()
        .setOnPreToolUse((input, invocation) -> {
            System.out.println("Tool called: " + input.getToolName());
            return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
        })
        .setOnPostToolUse((input, invocation) -> {
            System.out.println("Tool result: " + input.getToolResult());
            return CompletableFuture.completedFuture(null);
        })
        .setOnSessionStart((input, invocation) -> {
            return CompletableFuture.completedFuture(
                new SessionStartHookOutput("User prefers concise answers.", null)
            );
        });

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

## 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

* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/pre-tool-use/)** - Control tool execution permissions
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/post-tool-use/)** - Transform tool results
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted/)** - Modify user prompts
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/user-prompt-transformed/)** - Replace model-facing prompts
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/session-lifecycle/)** - Session start and end
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/session-lifecycle/#agent-stop-hook)** - Validate completion before the agent stops
* **[AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/hooks/error-handling/)** - Custom error handling

## See also

* [AUTOTITLE](https://funcoding.ai/agents/github-copilot/get-started/sdk-quickstart/)
* [AUTOTITLE](https://funcoding.ai/agents/github-copilot/get-started/sdk-quickstart/#step-4-add-a-custom-tool)
* [AUTOTITLE](https://funcoding.ai/agents/github-copilot/how-tos/copilot-sdk/troubleshooting/debugging/)
