# Agents personnalisés et orchestration de sous-agents

Définissez des agents spécialisés avec des outils et des invites délimités, puis laissez Copilot les orchestrer en tant que sous-agents au sein d’une même session. Pour distribuer plusieurs sous-agents en parallèle, consultez Mode flotte.

<!-- markdownlint-disable GHD046 GHD005 -->

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

## Overview

Les agents personnalisés sont des définitions légères d’agents que vous associez à une session. Chaque agent possède son propre invite de commande système, ses restrictions d’outil et ses serveurs MCP facultatifs. Lorsque la demande d’un utilisateur correspond à l’expertise d’un agent, l’environnement d’exécution Copilot délègue automatiquement la tâche à cet agent en tant que **sous-agent** — en l’exécutant dans un contexte isolé tout en retransmettant en continu les événements du cycle de vie à la session parente.

![Diagramme : Organigramme montrant le processus décrit.](/assets/images/help/copilot/copilot-sdk/features-custom-agents-diagram-0.png)

| Concept                | Description                                                                                             |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| **Agent personnalisé** | Configuration d’assistant nommé avec sa propre demande et son jeu d’outils                              |
| **Sous-agent**         | Agent personnalisé appelé par le runtime pour gérer une partie d’une tâche                              |
| **Inférence**          | Capacité du runtime à sélectionner automatiquement un agent en fonction de l’intention de l’utilisateur |
| **Session parente**    | Session qui a généré le sous-agent ; reçoit tous les événements de cycle de vie                         |

## Définition d’agents personnalisés

Indiquez `customAgents` lors de la création d'une session. Chaque agent a besoin au minimum d’un `name` et `prompt`.

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
    model: "gpt-5.4",
    customAgents: [
        {
            name: "researcher",
            displayName: "Research Agent",
            description: "Explores codebases and answers questions using read-only tools",
            tools: ["grep", "glob", "view"],
            prompt: "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        {
            name: "editor",
            displayName: "Editor Agent",
            description: "Makes targeted code changes",
            tools: ["view", "edit", "bash"],
            prompt: "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    ],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
from copilot import CopilotClient, PermissionDecisionApproveOnce

client = CopilotClient()
await client.start()

session = await client.create_session(
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
    model="gpt-5.4",
    custom_agents=[
        {
            "name": "researcher",
            "display_name": "Research Agent",
            "description": "Explores codebases and answers questions using read-only tools",
            "tools": ["grep", "glob", "view"],
            "prompt": "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        {
            "name": "editor",
            "display_name": "Editor Agent",
            "description": "Makes targeted code changes",
            "tools": ["view", "edit", "bash"],
            "prompt": "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    ],
)
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
ctx := context.Background()
client := copilot.NewClient(nil)
client.Start(ctx)

session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    Model: "gpt-5.4",
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            DisplayName: "Research Agent",
            Description: "Explores codebases and answers questions using read-only tools",
            Tools:       []string{"grep", "glob", "view"},
            Prompt:      "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        {
            Name:        "editor",
            DisplayName: "Editor Agent",
            Description: "Makes targeted code changes",
            Tools:       []string{"view", "edit", "bash"},
            Prompt:      "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    },
    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
        return &rpc.PermissionDecisionApproveOnce{}, nil
    },
})
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using GitHub.Copilot;
using GitHub.Copilot.Rpc;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-5.4",
    CustomAgents = new List<CustomAgentConfig>
    {
        new()
        {
            Name = "researcher",
            DisplayName = "Research Agent",
            Description = "Explores codebases and answers questions using read-only tools",
            Tools = new List<string> { "grep", "glob", "view" },
            Prompt = "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        new()
        {
            Name = "editor",
            DisplayName = "Editor Agent",
            Description = "Makes targeted code changes",
            Tools = new List<string> { "view", "edit", "bash" },
            Prompt = "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    },
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;
import java.util.List;

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

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-5.4")
            .setCustomAgents(List.of(
                new CustomAgentConfig()
                    .setName("researcher")
                    .setDisplayName("Research Agent")
                    .setDescription("Explores codebases and answers questions using read-only tools")
                    .setTools(List.of("grep", "glob", "view"))
                    .setPrompt("You are a research assistant. Analyze code and answer questions. Do not modify any files."),
                new CustomAgentConfig()
                    .setName("editor")
                    .setDisplayName("Editor Agent")
                    .setDescription("Makes targeted code changes")
                    .setTools(List.of("view", "edit", "bash"))
                    .setPrompt("You are a code editor. Make minimal, surgical changes to files as requested.")
            ))
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();
}
```

</div>

</div>

## Référence de configuration

| Propriété                                                                                                                                                                                                                        | Catégorie  | Obligatoire | Description                                    |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | ---------------------------------------------- |
| `name`                                                                                                                                                                                                                           | `string`   | ✅           | Identificateur unique de l’agent               |
| `displayName`                                                                                                                                                                                                                    | `string`   |             |                                                |
| Nom lisible par l’homme affiché dans les événements                                                                                                                                                                              |            |             |                                                |
| `description`                                                                                                                                                                                                                    | `string`   |             |                                                |
| Ce que fait l’agent : aide le runtime à le sélectionner                                                                                                                                                                          |            |             |                                                |
| `tools`                                                                                                                                                                                                                          |            |             |                                                |
| `string[]` ou `null`                                                                                                                                                                                                             |            |             |                                                |
| Les noms d’outils que l’agent peut utiliser.                                                                                                                                                                                     |            |             |                                                |
| `null` ou omis = tous les outils                                                                                                                                                                                                 |            |             |                                                |
| `prompt`                                                                                                                                                                                                                         | `string`   | ✅           | Invite de commande du système pour l’assistant |
| `mcpServers`                                                                                                                                                                                                                     | `object`   |             |                                                |
| Configurations de serveur MCP spécifiques à cet agent                                                                                                                                                                            |            |             |                                                |
| `infer`                                                                                                                                                                                                                          | `boolean`  |             |                                                |
| Indique si le runtime peut sélectionner automatiquement cet agent (par défaut : `true`)                                                                                                                                          |            |             |                                                |
| `skills`                                                                                                                                                                                                                         | `string[]` |             |                                                |
| Noms de compétences à précharger dans le contexte de l’agent au démarrage                                                                                                                                                        |            |             |                                                |
| `model`                                                                                                                                                                                                                          | `string`   |             |                                                |
| Identificateur de modèle à utiliser pendant l’exécution de cet agent                                                                                                                                                             |            |             |                                                |
| `reasoningEffort`                                                                                                                                                                                                                | `string`   |             |                                                |
| Effort de raisonnement à utiliser pendant l’exécution de cet agent. En cas d’omission, le Kit de développement logiciel (SDK) n’envoie aucune substitution par agent et le runtime résout l’effort (voir la remarque ci-dessous) |            |             |                                                |

> \[!TIP]
> Une bonne `description` aide le runtime à faire correspondre l’intention de l’utilisateur à l’agent approprié. Soyez spécifique sur l’expertise et les capacités de l’agent.

Définissez `model` et `reasoningEffort` pour remplacer les paramètres du modèle de la session parente pendant l’exécution d’un agent personnalisé. Lorsque `reasoningEffort` est omis, le SDK n’envoie aucune surcharge propre à l’agent, et le runtime détermine l’effort selon son propre ordre de priorité : une option client définie pour l’appel, la valeur par défaut du modèle résolu ou la définition de l’agent priment toutes ; sinon, le runtime hérite de l’effort de la session parente uniquement lorsque le sous-agent utilise le même modèle que le parent. Lorsque le sous-agent correspond à un autre modèle, il utilise alors la valeur par défaut de ce modèle au lieu d’hériter du niveau d’effort du parent. Python utilise `reasoning_effort`, .NET `ReasoningEffort`utilise , Go utilise `ReasoningEffort`, Java utilise `setReasoningEffort`, et Rust utilise `with_reasoning_effort`.

En plus de la configuration par agent ci-dessus, vous pouvez définir `agent` sur la **configuration de session** elle-même pour pré-sélectionner l’agent personnalisé actif au démarrage de la session. Voir [Sélection d’un agent lors de la création de session](#selecting-an-agent-at-session-creation) ci-dessous.

| Propriété de configuration de session | Catégorie | Description                                                                                                                        |
| ------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `agent`                               | `string`  | Nom de l’agent personnalisé à préélectionnez lors de la création de la session. Doit correspondre à un `name` dans `customAgents`. |

## Compétences par agent

Vous pouvez précharger des compétences dans le contexte d’un assistant à l’aide de la propriété `skills`. Lorsqu’il est spécifié, le **contenu complet** de chaque compétence répertoriée est injecté d’emblée dans le contexte de l’agent au démarrage — l’agent n’a pas besoin d’invoquer un outil de compétence ; les instructions sont déjà présentes. Les compétences sont **opt-in** : les agents ne reçoivent aucune compétence par défaut, et les sous-agents n’héritent pas des compétences du parent. Les noms de compétence sont résolus à partir du niveau `skillDirectories`de la session.

```typescript
const session = await client.createSession({
    skillDirectories: ["./skills"],
    customAgents: [
        {
            name: "security-auditor",
            description: "Security-focused code reviewer",
            prompt: "Focus on OWASP Top 10 vulnerabilities",
            skills: ["security-scan", "dependency-check"],
        },
        {
            name: "docs-writer",
            description: "Technical documentation writer",
            prompt: "Write clear, concise documentation",
            skills: ["markdown-lint"],
        },
    ],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});
```

Dans cet exemple, `security-auditor` démarre avec `security-scan` et `dependency-check` déjà injectés dans son contexte, tandis que `docs-writer` démarre avec `markdown-lint`. Un agent sans `skills` champ ne reçoit aucun contenu de compétence.

## Sélection d’un agent lors de la création de session

Vous pouvez passer `agent` la configuration de session pour pré-sélectionner l’agent personnalisé qui doit être actif au démarrage de la session. La valeur doit correspondre à `name` l’un des agents définis dans `customAgents`.

Cela équivaut à appeler `session.rpc.agent.select()` après la création, mais évite l’appel d’API supplémentaire et garantit que l’agent est actif à partir de la première invite.

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

<!-- docs-validate: skip -->

```typescript
const session = await client.createSession({
    customAgents: [
        {
            name: "researcher",
            prompt: "You are a research assistant. Analyze code and answer questions.",
        },
        {
            name: "editor",
            prompt: "You are a code editor. Make minimal, surgical changes.",
        },
    ],
    agent: "researcher", // Pre-select the researcher agent
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

<!-- docs-validate: skip -->

```python
session = await client.create_session(
    on_permission_request=PermissionHandler.approve_all,
    custom_agents=[
        {
            "name": "researcher",
            "prompt": "You are a research assistant. Analyze code and answer questions.",
        },
        {
            "name": "editor",
            "prompt": "You are a code editor. Make minimal, surgical changes.",
        },
    ],
    agent="researcher",  # Pre-select the researcher agent
)
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

<!-- docs-validate: skip -->

```golang
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:   "researcher",
            Prompt: "You are a research assistant. Analyze code and answer questions.",
        },
        {
            Name:   "editor",
            Prompt: "You are a code editor. Make minimal, surgical changes.",
        },
    },
    Agent: "researcher", // Pre-select the researcher agent
})
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

<!-- docs-validate: skip -->

```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
    CustomAgents = new List<CustomAgentConfig>
    {
        new() { Name = "researcher", Prompt = "You are a research assistant. Analyze code and answer questions." },
        new() { Name = "editor", Prompt = "You are a code editor. Make minimal, surgical changes." },
    },
    Agent = "researcher", // Pre-select the researcher agent
});
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

<!-- docs-validate: skip -->

```java
import com.github.copilot.rpc.*;
import java.util.List;

var session = client.createSession(
    new SessionConfig()
        .setCustomAgents(List.of(
            new CustomAgentConfig()
                .setName("researcher")
                .setPrompt("You are a research assistant. Analyze code and answer questions."),
            new CustomAgentConfig()
                .setName("editor")
                .setPrompt("You are a code editor. Make minimal, surgical changes.")
        ))
        .setAgent("researcher") // Pre-select the researcher agent
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
```

</div>

</div>

## Fonctionnement de la délégation de sous-agent

Lorsque vous envoyez une invite à une session avec des agents personnalisés, le runtime évalue s’il faut déléguer à un sous-agent :

1. **Correspondance d’intention**—L'environnement d'exécution analyse l’invite de l’utilisateur par rapport à chaque agent `name` et `description`
2. **Sélection de l’agent** : si une correspondance est trouvée et `infer` non `false`, le runtime sélectionne l’agent
3. **Exécution isolée** : le sous-assistant s’exécute avec son propre invite et un jeu d’outils restreint
4. **Streaming d’événements** : les événements de cycle de vie (`subagent.started`, `subagent.completed`, etc.) retournent vers la session parente.
5. **Intégration des résultats** : la sortie du sous-agent est incorporée dans la réponse de l’agent parent

### Contrôle de l’inférence

Par défaut, tous les agents personnalisés sont disponibles pour la sélection automatique (`infer: true`). Définissez `infer: false` pour empêcher le runtime de sélectionner automatiquement un agent, ce qui est utile pour les agents que vous souhaitez appeler uniquement par le biais de requêtes utilisateur explicites :

```typescript
{
    name: "dangerous-cleanup",
    description: "Deletes unused files and dead code",
    tools: ["bash", "edit", "view"],
    prompt: "You clean up codebases by removing dead code and unused files.",
    infer: false, // Only invoked when user explicitly asks for this agent
}
```

## Écoute des événements de sous-assistant

Lorsqu’un sous-agent s’exécute, la session parente émet des événements de cycle de vie. Abonnez-vous à ces événements pour générer des interfaces utilisateur qui visualisent l’activité de l’agent.

Les événements de session générés par un sous-agent partagent le flux de la session parente et incluent l’élément `agentId` au niveau de l’enveloppe. Les événements de l’agent racine/principal et les événements au niveau de la session omettent `agentId`, de sorte que les renderers peuvent séparer la réponse parente des traces de sous-agent en vérifiant l’enveloppe d’événement.

### Types d’événements

| Event                                                                                                              | Émis quand                                    | Data |
| ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- | ---- |
| `subagent.selected`                                                                                                | Le runtime sélectionne un agent pour la tâche |      |
| `agentName`, `agentDisplayName`, `tools`                                                                           |                                               |      |
| `subagent.started`                                                                                                 | Le sous-agent commence l’exécution            |      |
| `toolCallId`, , `agentName``agentDisplayName`, , `agentDescription``model?`                                        |                                               |      |
| `subagent.completed`                                                                                               | Le sous-assistant se termine avec succès      |      |
| `toolCallId`, `agentName`, `model?`, `agentDisplayName`, `durationMs?`, `totalTokens?`, `totalToolCalls?`          |                                               |      |
| `subagent.failed`                                                                                                  | Un sous-agent rencontre une erreur            |      |
| `toolCallId`, `agentName`, `error`, `agentDisplayName`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` |                                               |      |
| `subagent.deselected`                                                                                              | Le runtime s’éloigne du sous-agent            | —    |

### Abonnement aux événements

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
session.on((event) => {
    switch (event.type) {
        case "subagent.started":
            console.log(`▶ Sub-agent started: ${event.data.agentDisplayName}`);
            console.log(`  Description: ${event.data.agentDescription}`);
            console.log(`  Tool call ID: ${event.data.toolCallId}`);
            break;

        case "subagent.completed":
            console.log(`✅ Sub-agent completed: ${event.data.agentDisplayName}`);
            if (event.data.durationMs !== undefined) console.log(`  Duration: ${event.data.durationMs}ms`);
            if (event.data.totalTokens !== undefined) console.log(`  Tokens: ${event.data.totalTokens}`);
            if (event.data.totalToolCalls !== undefined) console.log(`  Tool calls: ${event.data.totalToolCalls}`);
            break;

        case "subagent.failed":
            console.log(`❌ Sub-agent failed: ${event.data.agentDisplayName}`);
            console.log(`  Error: ${event.data.error}`);
            if (event.data.durationMs !== undefined) console.log(`  Duration: ${event.data.durationMs}ms`);
            break;

        case "subagent.selected":
            console.log(`🎯 Agent selected: ${event.data.agentDisplayName}`);
            console.log(`  Tools: ${event.data.tools?.join(", ") ?? "all"}`);
            break;

        case "subagent.deselected":
            console.log("↩ Agent deselected, returning to parent");
            break;
    }
});

const response = await session.sendAndWait({
    prompt: "Research how authentication works in this codebase",
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
def handle_event(event):
    if event.type == "subagent.started":
        print(f"▶ Sub-agent started: {event.data.agent_display_name}")
        print(f"  Description: {event.data.agent_description}")
    elif event.type == "subagent.completed":
        print(f"✅ Sub-agent completed: {event.data.agent_display_name}")
    elif event.type == "subagent.failed":
        print(f"❌ Sub-agent failed: {event.data.agent_display_name}")
        print(f"  Error: {event.data.error}")
    elif event.type == "subagent.selected":
        tools = event.data.tools or "all"
        print(f"🎯 Agent selected: {event.data.agent_display_name} (tools: {tools})")

unsubscribe = session.on(handle_event)

response = await session.send_and_wait("Research how authentication works in this codebase")
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
session.On(func(event copilot.SessionEvent) {
    switch d := event.Data.(type) {
    case *copilot.SubagentStartedData:
        fmt.Printf("▶ Sub-agent started: %s\n", d.AgentDisplayName)
        fmt.Printf("  Description: %s\n", d.AgentDescription)
        fmt.Printf("  Tool call ID: %s\n", d.ToolCallID)
    case *copilot.SubagentCompletedData:
        fmt.Printf("✅ Sub-agent completed: %s\n", d.AgentDisplayName)
    case *copilot.SubagentFailedData:
        fmt.Printf("❌ Sub-agent failed: %s — %v\n", d.AgentDisplayName, d.Error)
    case *copilot.SubagentSelectedData:
        fmt.Printf("🎯 Agent selected: %s\n", d.AgentDisplayName)
    }
})

_, err := session.SendAndWait(ctx, copilot.MessageOptions{
    Prompt: "Research how authentication works in this codebase",
})
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using var subscription = session.On<SessionEvent>(evt =>
{
    switch (evt)
    {
        case SubagentStartedEvent started:
            Console.WriteLine($"▶ Sub-agent started: {started.Data.AgentDisplayName}");
            Console.WriteLine($"  Description: {started.Data.AgentDescription}");
            Console.WriteLine($"  Tool call ID: {started.Data.ToolCallId}");
            break;
        case SubagentCompletedEvent completed:
            Console.WriteLine($"✅ Sub-agent completed: {completed.Data.AgentDisplayName}");
            break;
        case SubagentFailedEvent failed:
            Console.WriteLine($"❌ Sub-agent failed: {failed.Data.AgentDisplayName} — {failed.Data.Error}");
            break;
        case SubagentSelectedEvent selected:
            Console.WriteLine($"🎯 Agent selected: {selected.Data.AgentDisplayName}");
            break;
    }
});

await session.SendAndWaitAsync(new MessageOptions
{
    Prompt = "Research how authentication works in this codebase"
});
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

<!-- docs-validate: skip -->

```java
session.on(event -> {
    if (event instanceof SubagentStartedEvent e) {
        System.out.println("▶ Sub-agent started: " + e.getData().agentDisplayName());
        System.out.println("  Description: " + e.getData().agentDescription());
        System.out.println("  Tool call ID: " + e.getData().toolCallId());
    } else if (event instanceof SubagentCompletedEvent e) {
        System.out.println("✅ Sub-agent completed: " + e.getData().agentName());
    } else if (event instanceof SubagentFailedEvent e) {
        System.out.println("❌ Sub-agent failed: " + e.getData().agentName());
        System.out.println("  Error: " + e.getData().error());
    } else if (event instanceof SubagentSelectedEvent e) {
        System.out.println("🎯 Agent selected: " + e.getData().agentDisplayName());
    } else if (event instanceof SubagentDeselectedEvent e) {
        System.out.println("↩ Agent deselected, returning to parent");
    }
});

var response = session.sendAndWait(
    new MessageOptions().setPrompt("Research how authentication works in this codebase")
).get();
```

</div>

</div>

## Création d’une interface utilisateur pour l’arborescence des agents

Les événements de sous-agent incluent des `toolCallId` champs qui vous permettent de reconstruire l’arborescence d’exécution. Voici un modèle pour le suivi de l’activité de l’agent :

```typescript
interface AgentNode {
    toolCallId: string;
    name: string;
    displayName: string;
    status: "running" | "completed" | "failed";
    error?: string;
    startedAt: Date;
    completedAt?: Date;
}

const agentTree = new Map<string, AgentNode>();

session.on((event) => {
    if (event.type === "subagent.started") {
        agentTree.set(event.data.toolCallId, {
            toolCallId: event.data.toolCallId,
            name: event.data.agentName,
            displayName: event.data.agentDisplayName,
            status: "running",
            startedAt: new Date(event.timestamp),
        });
    }

    if (event.type === "subagent.completed") {
        const node = agentTree.get(event.data.toolCallId);
        if (node) {
            node.status = "completed";
            node.completedAt = new Date(event.timestamp);
        }
    }

    if (event.type === "subagent.failed") {
        const node = agentTree.get(event.data.toolCallId);
        if (node) {
            node.status = "failed";
            node.error = event.data.error;
            node.completedAt = new Date(event.timestamp);
        }
    }

    // Render your UI with the updated tree
    renderAgentTree(agentTree);
});
```

## Outils de portée par assistant

Utilisez la `tools` propriété pour restreindre les outils auxquels un agent peut accéder. Ceci est essentiel pour la sécurité et pour garder les agents concentrés :

```typescript
const session = await client.createSession({
    customAgents: [
        {
            name: "reader",
            description: "Read-only exploration of the codebase",
            tools: ["grep", "glob", "view"],  // No write access
            prompt: "You explore and analyze code. Never suggest modifications directly.",
        },
        {
            name: "writer",
            description: "Makes code changes",
            tools: ["view", "edit", "bash"],   // Write access
            prompt: "You make precise code changes as instructed.",
        },
        {
            name: "unrestricted",
            description: "Full access agent for complex tasks",
            tools: null,                        // All tools available
            prompt: "You handle complex multi-step tasks using any available tools.",
        },
    ],
});
```

> \[!NOTE]
> Lorsque la balise `tools` est `null` ou omise, l’agent hérite de l'accès à tous les outils configurés sur la session. Utilisez des listes d’outils explicites pour appliquer le principe du privilège minimum.

## Outils réservés aux agents

Utilisez la `defaultAgent` propriété sur la configuration de session pour masquer des outils spécifiques de l’agent par défaut (l’agent intégré qui gère les tours lorsqu’aucun agent personnalisé n’est sélectionné). Cela force l’agent principal à déléguer aux sous-agents lorsque les fonctionnalités de ces outils sont nécessaires, en gardant le contexte de l’agent principal propre.

Cela est utile quand :

* Certains outils génèrent de grandes quantités de contexte qui surchargeraient l’agent principal
* Vous souhaitez que l’agent principal agisse en tant qu’orchestrateur, en déléguant le travail lourd aux sous-agents spécialisés
* Vous avez besoin d’une séparation stricte entre l’orchestration et l’exécution

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk";
import { z } from "zod";

const heavyContextTool = defineTool("analyze-codebase", {
    description: "Performs deep analysis of the codebase, generating extensive context",
    parameters: z.object({ query: z.string() }),
    handler: async ({ query }) => {
        // ... expensive analysis that returns lots of data
        return { analysis: "..." };
    },
});

const session = await client.createSession({
    tools: [heavyContextTool],
    defaultAgent: {
        excludedTools: ["analyze-codebase"],
    },
    customAgents: [
        {
            name: "researcher",
            description: "Deep codebase analysis agent with access to heavy-context tools",
            tools: ["analyze-codebase"],
            prompt: "You perform thorough codebase analysis using the analyze-codebase tool.",
        },
    ],
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
from copilot import CopilotClient
from copilot.tools import Tool

heavy_tool = Tool(
    name="analyze-codebase",
    description="Performs deep analysis of the codebase",
    handler=analyze_handler,
    parameters={"type": "object", "properties": {"query": {"type": "string"}}},
)

session = await client.create_session(
    tools=[heavy_tool],
    default_agent={"excluded_tools": ["analyze-codebase"]},
    custom_agents=[
        {
            "name": "researcher",
            "description": "Deep codebase analysis agent",
            "tools": ["analyze-codebase"],
            "prompt": "You perform thorough codebase analysis.",
        },
    ],
    on_permission_request=approve_all,
)
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

<!-- docs-validate: skip -->

```golang
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
    Tools: []copilot.Tool{heavyTool},
    DefaultAgent: &copilot.DefaultAgentConfig{
        ExcludedTools: []string{"analyze-codebase"},
    },
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            Description: "Deep codebase analysis agent",
            Tools:       []string{"analyze-codebase"},
            Prompt:      "You perform thorough codebase analysis.",
        },
    },
})
```

</div>

<div class="ghd-codetab" data-lang="csharp" data-label="C#"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">C#</div>

<!-- docs-validate: skip -->

```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
    Tools = [analyzeCodebaseTool],
    DefaultAgent = new DefaultAgentConfig
    {
        ExcludedTools = ["analyze-codebase"],
    },
    CustomAgents =
    [
        new CustomAgentConfig
        {
            Name = "researcher",
            Description = "Deep codebase analysis agent",
            Tools = ["analyze-codebase"],
            Prompt = "You perform thorough codebase analysis.",
        },
    ],
});
```

</div>

</div>

### Fonctionnement

Outils répertoriés dans `defaultAgent.excludedTools`:

1. **Sont inscrits** : leurs gestionnaires sont disponibles pour l’exécution
2. **Sont masqués** de la liste d’outils de l’agent principal — le LLM ne les verra pas et ne les appellera pas directement
3. **Restez disponible** pour n’importe quel sous-agent personnalisé qui les inclut dans son `tools` tableau

### Interaction avec d’autres filtres d’outils

`defaultAgent.excludedTools` est orthogonal aux `availableTools` et `excludedTools` au niveau de la session :

| Filter                       | Scope                      | Résultat                                                                          |
| ---------------------------- | -------------------------- | --------------------------------------------------------------------------------- |
| `availableTools`             | À l’échelle de la session  | Liste d’autorisation : seuls ces outils existent pour n’importe qui               |
| `excludedTools`              | À l’échelle de la session  | Liste de blocage : ces outils sont bloqués pour tout le monde                     |
| `defaultAgent.excludedTools` | Agent principal uniquement | Ces outils sont cachés à l’agent principal, mais disponibles pour les sous-agents |

Préséance:

1. Le niveau `availableTools`/`excludedTools` de session est appliqué en premier (globalement)
2. `defaultAgent.excludedTools` est appliqué en haut, limitant davantage l’agent principal uniquement

> \[!NOTE]
> Si un outil figure à la fois dans `excludedTools` (au niveau de la session) et dans `defaultAgent.excludedTools`, l’exclusion au niveau de la session est prioritaire : l’outil n’est disponible pour personne.

## Attachement de serveurs MCP à des agents

Chaque agent personnalisé peut avoir ses propres serveurs MCP (Model Context Protocol), ce qui lui donne accès à des sources de données spécialisées :

```typescript
const session = await client.createSession({
    customAgents: [
        {
            name: "db-analyst",
            description: "Analyzes database schemas and queries",
            prompt: "You are a database expert. Use the database MCP server to analyze schemas.",
            mcpServers: {
                "database": {
                    command: "npx",
                    args: ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"],
                },
            },
        },
    ],
});
```

## Modèles et bonnes pratiques

### Associer un chercheur à un éditeur

Un modèle courant consiste à définir un agent de recherche en lecture seule et un agent d’édition capable d’écrire. Le runtime délègue les tâches d'exploration au chercheur et les tâches de modification à l'éditeur.

```typescript
customAgents: [
    {
        name: "researcher",
        description: "Analyzes code structure, finds patterns, and answers questions",
        tools: ["grep", "glob", "view"],
        prompt: "You are a code analyst. Thoroughly explore the codebase to answer questions.",
    },
    {
        name: "implementer",
        description: "Implements code changes based on analysis",
        tools: ["view", "edit", "bash"],
        prompt: "You make minimal, targeted code changes. Always verify changes compile.",
    },
]
```

### Conserver des descriptions spécifiques de l’agent

Le runtime utilise `description` pour répondre à l'intention de l’utilisateur. Les descriptions vagues mènent à une délégation médiocre :

```typescript
// ❌ Too vague — runtime can't distinguish from other agents
{ description: "Helps with code" }

// ✅ Specific — runtime knows when to delegate
{ description: "Analyzes Python test coverage and identifies untested code paths" }
```

### Gérer correctement les défaillances

Les sous-agents peuvent échouer. Écoutez toujours les événements `subagent.failed` et gérez-les dans votre application :

```typescript
session.on((event) => {
    if (event.type === "subagent.failed") {
        logger.error(`Agent ${event.data.agentName} failed: ${event.data.error}`);
        // Show error in UI, retry, or fall back to parent agent
    }
});
```