{"meta":{"title":"Agents personnalisés et orchestration de sous-agents","intro":"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.","product":"GitHub Copilot","breadcrumbs":[{"href":"/fr/copilot","title":"GitHub Copilot"},{"href":"/fr/copilot/how-tos","title":"Procédures"},{"href":"/fr/copilot/how-tos/copilot-sdk","title":"Kit de développement logiciel (SDK) Copilot"},{"href":"/fr/copilot/how-tos/copilot-sdk/features","title":"Fonctionnalités"},{"href":"/fr/copilot/how-tos/copilot-sdk/features/custom-agents","title":"Agents personnalisés"}],"documentType":"article"},"body":"# Agents personnalisés et orchestration de sous-agents\n\nDé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.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Overview\n\nLes 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.\n\n![Diagramme : Organigramme montrant le processus décrit.](/assets/images/help/copilot/copilot-sdk/features-custom-agents-diagram-0.png)\n\n| Concept                | Description                                                                                             |\n| ---------------------- | ------------------------------------------------------------------------------------------------------- |\n| **Agent personnalisé** | Configuration d’assistant nommé avec sa propre demande et son jeu d’outils                              |\n| **Sous-agent**         | Agent personnalisé appelé par le runtime pour gérer une partie d’une tâche                              |\n| **Inférence**          | Capacité du runtime à sélectionner automatiquement un agent en fonction de l’intention de l’utilisateur |\n| **Session parente**    | Session qui a généré le sous-agent ; reçoit tous les événements de cycle de vie                         |\n\n## Définition d’agents personnalisés\n\nIndiquez `customAgents` lors de la création d'une session. Chaque agent a besoin au minimum d’un `name` et `prompt`.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nawait client.start();\n\nconst session = await client.createSession({\n    model: \"gpt-5.4\",\n    customAgents: [\n        {\n            name: \"researcher\",\n            displayName: \"Research Agent\",\n            description: \"Explores codebases and answers questions using read-only tools\",\n            tools: [\"grep\", \"glob\", \"view\"],\n            prompt: \"You are a research assistant. Analyze code and answer questions. Do not modify any files.\",\n        },\n        {\n            name: \"editor\",\n            displayName: \"Editor Agent\",\n            description: \"Makes targeted code changes\",\n            tools: [\"view\", \"edit\", \"bash\"],\n            prompt: \"You are a code editor. Make minimal, surgical changes to files as requested.\",\n        },\n    ],\n    onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotClient, PermissionDecisionApproveOnce\n\nclient = CopilotClient()\nawait client.start()\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    model=\"gpt-5.4\",\n    custom_agents=[\n        {\n            \"name\": \"researcher\",\n            \"display_name\": \"Research Agent\",\n            \"description\": \"Explores codebases and answers questions using read-only tools\",\n            \"tools\": [\"grep\", \"glob\", \"view\"],\n            \"prompt\": \"You are a research assistant. Analyze code and answer questions. Do not modify any files.\",\n        },\n        {\n            \"name\": \"editor\",\n            \"display_name\": \"Editor Agent\",\n            \"description\": \"Makes targeted code changes\",\n            \"tools\": [\"view\", \"edit\", \"bash\"],\n            \"prompt\": \"You are a code editor. Make minimal, surgical changes to files as requested.\",\n        },\n    ],\n)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\nctx := context.Background()\nclient := copilot.NewClient(nil)\nclient.Start(ctx)\n\nsession, _ := client.CreateSession(ctx, &copilot.SessionConfig{\n    Model: \"gpt-5.4\",\n    CustomAgents: []copilot.CustomAgentConfig{\n        {\n            Name:        \"researcher\",\n            DisplayName: \"Research Agent\",\n            Description: \"Explores codebases and answers questions using read-only tools\",\n            Tools:       []string{\"grep\", \"glob\", \"view\"},\n            Prompt:      \"You are a research assistant. Analyze code and answer questions. Do not modify any files.\",\n        },\n        {\n            Name:        \"editor\",\n            DisplayName: \"Editor Agent\",\n            Description: \"Makes targeted code changes\",\n            Tools:       []string{\"view\", \"edit\", \"bash\"},\n            Prompt:      \"You are a code editor. Make minimal, surgical changes to files as requested.\",\n        },\n    },\n    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {\n        return &rpc.PermissionDecisionApproveOnce{}, nil\n    },\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\nusing GitHub.Copilot.Rpc;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"gpt-5.4\",\n    CustomAgents = new List<CustomAgentConfig>\n    {\n        new()\n        {\n            Name = \"researcher\",\n            DisplayName = \"Research Agent\",\n            Description = \"Explores codebases and answers questions using read-only tools\",\n            Tools = new List<string> { \"grep\", \"glob\", \"view\" },\n            Prompt = \"You are a research assistant. Analyze code and answer questions. Do not modify any files.\",\n        },\n        new()\n        {\n            Name = \"editor\",\n            DisplayName = \"Editor Agent\",\n            Description = \"Makes targeted code changes\",\n            Tools = new List<string> { \"view\", \"edit\", \"bash\" },\n            Prompt = \"You are a code editor. Make minimal, surgical changes to files as requested.\",\n        },\n    },\n    OnPermissionRequest = (req, inv) =>\n        Task.FromResult(PermissionDecision.ApproveOnce()),\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\nimport java.util.List;\n\ntry (var client = new CopilotClient()) {\n    client.start().get();\n\n    var session = client.createSession(\n        new SessionConfig()\n            .setModel(\"gpt-5.4\")\n            .setCustomAgents(List.of(\n                new CustomAgentConfig()\n                    .setName(\"researcher\")\n                    .setDisplayName(\"Research Agent\")\n                    .setDescription(\"Explores codebases and answers questions using read-only tools\")\n                    .setTools(List.of(\"grep\", \"glob\", \"view\"))\n                    .setPrompt(\"You are a research assistant. Analyze code and answer questions. Do not modify any files.\"),\n                new CustomAgentConfig()\n                    .setName(\"editor\")\n                    .setDisplayName(\"Editor Agent\")\n                    .setDescription(\"Makes targeted code changes\")\n                    .setTools(List.of(\"view\", \"edit\", \"bash\"))\n                    .setPrompt(\"You are a code editor. Make minimal, surgical changes to files as requested.\")\n            ))\n            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n    ).get();\n}\n```\n\n</div>\n\n</div>\n\n## Référence de configuration\n\n| Propriété                                                                                                                                                                                                                        | Catégorie  | Obligatoire | Description                                    |\n| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | ---------------------------------------------- |\n| `name`                                                                                                                                                                                                                           | `string`   | ✅           | Identificateur unique de l’agent               |\n| `displayName`                                                                                                                                                                                                                    | `string`   |             |                                                |\n| Nom lisible par l’homme affiché dans les événements                                                                                                                                                                              |            |             |                                                |\n| `description`                                                                                                                                                                                                                    | `string`   |             |                                                |\n| Ce que fait l’agent : aide le runtime à le sélectionner                                                                                                                                                                          |            |             |                                                |\n| `tools`                                                                                                                                                                                                                          |            |             |                                                |\n| `string[]` ou `null`                                                                                                                                                                                                             |            |             |                                                |\n| Les noms d’outils que l’agent peut utiliser.                                                                                                                                                                                     |            |             |                                                |\n| `null` ou omis = tous les outils                                                                                                                                                                                                 |            |             |                                                |\n| `prompt`                                                                                                                                                                                                                         | `string`   | ✅           | Invite de commande du système pour l’assistant |\n| `mcpServers`                                                                                                                                                                                                                     | `object`   |             |                                                |\n| Configurations de serveur MCP spécifiques à cet agent                                                                                                                                                                            |            |             |                                                |\n| `infer`                                                                                                                                                                                                                          | `boolean`  |             |                                                |\n| Indique si le runtime peut sélectionner automatiquement cet agent (par défaut : `true`)                                                                                                                                          |            |             |                                                |\n| `skills`                                                                                                                                                                                                                         | `string[]` |             |                                                |\n| Noms de compétences à précharger dans le contexte de l’agent au démarrage                                                                                                                                                        |            |             |                                                |\n| `model`                                                                                                                                                                                                                          | `string`   |             |                                                |\n| Identificateur de modèle à utiliser pendant l’exécution de cet agent                                                                                                                                                             |            |             |                                                |\n| `reasoningEffort`                                                                                                                                                                                                                | `string`   |             |                                                |\n| 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) |            |             |                                                |\n\n> \\[!TIP]\n> 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.\n\nDé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`.\n\nEn 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.\n\n| Propriété de configuration de session | Catégorie | Description                                                                                                                        |\n| ------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| `agent`                               | `string`  | Nom de l’agent personnalisé à préélectionnez lors de la création de la session. Doit correspondre à un `name` dans `customAgents`. |\n\n## Compétences par agent\n\nVous 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.\n\n```typescript\nconst session = await client.createSession({\n    skillDirectories: [\"./skills\"],\n    customAgents: [\n        {\n            name: \"security-auditor\",\n            description: \"Security-focused code reviewer\",\n            prompt: \"Focus on OWASP Top 10 vulnerabilities\",\n            skills: [\"security-scan\", \"dependency-check\"],\n        },\n        {\n            name: \"docs-writer\",\n            description: \"Technical documentation writer\",\n            prompt: \"Write clear, concise documentation\",\n            skills: [\"markdown-lint\"],\n        },\n    ],\n    onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\nDans 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.\n\n## Sélection d’un agent lors de la création de session\n\nVous 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`.\n\nCela é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.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n<!-- docs-validate: skip -->\n\n```typescript\nconst session = await client.createSession({\n    customAgents: [\n        {\n            name: \"researcher\",\n            prompt: \"You are a research assistant. Analyze code and answer questions.\",\n        },\n        {\n            name: \"editor\",\n            prompt: \"You are a code editor. Make minimal, surgical changes.\",\n        },\n    ],\n    agent: \"researcher\", // Pre-select the researcher agent\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n<!-- docs-validate: skip -->\n\n```python\nsession = await client.create_session(\n    on_permission_request=PermissionHandler.approve_all,\n    custom_agents=[\n        {\n            \"name\": \"researcher\",\n            \"prompt\": \"You are a research assistant. Analyze code and answer questions.\",\n        },\n        {\n            \"name\": \"editor\",\n            \"prompt\": \"You are a code editor. Make minimal, surgical changes.\",\n        },\n    ],\n    agent=\"researcher\",  # Pre-select the researcher agent\n)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n<!-- docs-validate: skip -->\n\n```golang\nsession, _ := client.CreateSession(ctx, &copilot.SessionConfig{\n    CustomAgents: []copilot.CustomAgentConfig{\n        {\n            Name:   \"researcher\",\n            Prompt: \"You are a research assistant. Analyze code and answer questions.\",\n        },\n        {\n            Name:   \"editor\",\n            Prompt: \"You are a code editor. Make minimal, surgical changes.\",\n        },\n    },\n    Agent: \"researcher\", // Pre-select the researcher agent\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n<!-- docs-validate: skip -->\n\n```csharp\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    CustomAgents = new List<CustomAgentConfig>\n    {\n        new() { Name = \"researcher\", Prompt = \"You are a research assistant. Analyze code and answer questions.\" },\n        new() { Name = \"editor\", Prompt = \"You are a code editor. Make minimal, surgical changes.\" },\n    },\n    Agent = \"researcher\", // Pre-select the researcher agent\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.rpc.*;\nimport java.util.List;\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setCustomAgents(List.of(\n            new CustomAgentConfig()\n                .setName(\"researcher\")\n                .setPrompt(\"You are a research assistant. Analyze code and answer questions.\"),\n            new CustomAgentConfig()\n                .setName(\"editor\")\n                .setPrompt(\"You are a code editor. Make minimal, surgical changes.\")\n        ))\n        .setAgent(\"researcher\") // Pre-select the researcher agent\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n).get();\n```\n\n</div>\n\n</div>\n\n## Fonctionnement de la délégation de sous-agent\n\nLorsque vous envoyez une invite à une session avec des agents personnalisés, le runtime évalue s’il faut déléguer à un sous-agent :\n\n1. **Correspondance d’intention**—L'environnement d'exécution analyse l’invite de l’utilisateur par rapport à chaque agent `name` et `description`\n2. **Sélection de l’agent** : si une correspondance est trouvée et `infer` non `false`, le runtime sélectionne l’agent\n3. **Exécution isolée** : le sous-assistant s’exécute avec son propre invite et un jeu d’outils restreint\n4. **Streaming d’événements** : les événements de cycle de vie (`subagent.started`, `subagent.completed`, etc.) retournent vers la session parente.\n5. **Intégration des résultats** : la sortie du sous-agent est incorporée dans la réponse de l’agent parent\n\n### Contrôle de l’inférence\n\nPar 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 :\n\n```typescript\n{\n    name: \"dangerous-cleanup\",\n    description: \"Deletes unused files and dead code\",\n    tools: [\"bash\", \"edit\", \"view\"],\n    prompt: \"You clean up codebases by removing dead code and unused files.\",\n    infer: false, // Only invoked when user explicitly asks for this agent\n}\n```\n\n## Écoute des événements de sous-assistant\n\nLorsqu’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.\n\nLes é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.\n\n### Types d’événements\n\n| Event                                                                                                              | Émis quand                                    | Data |\n| ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- | ---- |\n| `subagent.selected`                                                                                                | Le runtime sélectionne un agent pour la tâche |      |\n| `agentName`, `agentDisplayName`, `tools`                                                                           |                                               |      |\n| `subagent.started`                                                                                                 | Le sous-agent commence l’exécution            |      |\n| `toolCallId`, , `agentName``agentDisplayName`, , `agentDescription``model?`                                        |                                               |      |\n| `subagent.completed`                                                                                               | Le sous-assistant se termine avec succès      |      |\n| `toolCallId`, `agentName`, `model?`, `agentDisplayName`, `durationMs?`, `totalTokens?`, `totalToolCalls?`          |                                               |      |\n| `subagent.failed`                                                                                                  | Un sous-agent rencontre une erreur            |      |\n| `toolCallId`, `agentName`, `error`, `agentDisplayName`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` |                                               |      |\n| `subagent.deselected`                                                                                              | Le runtime s’éloigne du sous-agent            | —    |\n\n### Abonnement aux événements\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nsession.on((event) => {\n    switch (event.type) {\n        case \"subagent.started\":\n            console.log(`▶ Sub-agent started: ${event.data.agentDisplayName}`);\n            console.log(`  Description: ${event.data.agentDescription}`);\n            console.log(`  Tool call ID: ${event.data.toolCallId}`);\n            break;\n\n        case \"subagent.completed\":\n            console.log(`✅ Sub-agent completed: ${event.data.agentDisplayName}`);\n            if (event.data.durationMs !== undefined) console.log(`  Duration: ${event.data.durationMs}ms`);\n            if (event.data.totalTokens !== undefined) console.log(`  Tokens: ${event.data.totalTokens}`);\n            if (event.data.totalToolCalls !== undefined) console.log(`  Tool calls: ${event.data.totalToolCalls}`);\n            break;\n\n        case \"subagent.failed\":\n            console.log(`❌ Sub-agent failed: ${event.data.agentDisplayName}`);\n            console.log(`  Error: ${event.data.error}`);\n            if (event.data.durationMs !== undefined) console.log(`  Duration: ${event.data.durationMs}ms`);\n            break;\n\n        case \"subagent.selected\":\n            console.log(`🎯 Agent selected: ${event.data.agentDisplayName}`);\n            console.log(`  Tools: ${event.data.tools?.join(\", \") ?? \"all\"}`);\n            break;\n\n        case \"subagent.deselected\":\n            console.log(\"↩ Agent deselected, returning to parent\");\n            break;\n    }\n});\n\nconst response = await session.sendAndWait({\n    prompt: \"Research how authentication works in this codebase\",\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\ndef handle_event(event):\n    if event.type == \"subagent.started\":\n        print(f\"▶ Sub-agent started: {event.data.agent_display_name}\")\n        print(f\"  Description: {event.data.agent_description}\")\n    elif event.type == \"subagent.completed\":\n        print(f\"✅ Sub-agent completed: {event.data.agent_display_name}\")\n    elif event.type == \"subagent.failed\":\n        print(f\"❌ Sub-agent failed: {event.data.agent_display_name}\")\n        print(f\"  Error: {event.data.error}\")\n    elif event.type == \"subagent.selected\":\n        tools = event.data.tools or \"all\"\n        print(f\"🎯 Agent selected: {event.data.agent_display_name} (tools: {tools})\")\n\nunsubscribe = session.on(handle_event)\n\nresponse = await session.send_and_wait(\"Research how authentication works in this codebase\")\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\nsession.On(func(event copilot.SessionEvent) {\n    switch d := event.Data.(type) {\n    case *copilot.SubagentStartedData:\n        fmt.Printf(\"▶ Sub-agent started: %s\\n\", d.AgentDisplayName)\n        fmt.Printf(\"  Description: %s\\n\", d.AgentDescription)\n        fmt.Printf(\"  Tool call ID: %s\\n\", d.ToolCallID)\n    case *copilot.SubagentCompletedData:\n        fmt.Printf(\"✅ Sub-agent completed: %s\\n\", d.AgentDisplayName)\n    case *copilot.SubagentFailedData:\n        fmt.Printf(\"❌ Sub-agent failed: %s — %v\\n\", d.AgentDisplayName, d.Error)\n    case *copilot.SubagentSelectedData:\n        fmt.Printf(\"🎯 Agent selected: %s\\n\", d.AgentDisplayName)\n    }\n})\n\n_, err := session.SendAndWait(ctx, copilot.MessageOptions{\n    Prompt: \"Research how authentication works in this codebase\",\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing var subscription = session.On<SessionEvent>(evt =>\n{\n    switch (evt)\n    {\n        case SubagentStartedEvent started:\n            Console.WriteLine($\"▶ Sub-agent started: {started.Data.AgentDisplayName}\");\n            Console.WriteLine($\"  Description: {started.Data.AgentDescription}\");\n            Console.WriteLine($\"  Tool call ID: {started.Data.ToolCallId}\");\n            break;\n        case SubagentCompletedEvent completed:\n            Console.WriteLine($\"✅ Sub-agent completed: {completed.Data.AgentDisplayName}\");\n            break;\n        case SubagentFailedEvent failed:\n            Console.WriteLine($\"❌ Sub-agent failed: {failed.Data.AgentDisplayName} — {failed.Data.Error}\");\n            break;\n        case SubagentSelectedEvent selected:\n            Console.WriteLine($\"🎯 Agent selected: {selected.Data.AgentDisplayName}\");\n            break;\n    }\n});\n\nawait session.SendAndWaitAsync(new MessageOptions\n{\n    Prompt = \"Research how authentication works in this codebase\"\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\nsession.on(event -> {\n    if (event instanceof SubagentStartedEvent e) {\n        System.out.println(\"▶ Sub-agent started: \" + e.getData().agentDisplayName());\n        System.out.println(\"  Description: \" + e.getData().agentDescription());\n        System.out.println(\"  Tool call ID: \" + e.getData().toolCallId());\n    } else if (event instanceof SubagentCompletedEvent e) {\n        System.out.println(\"✅ Sub-agent completed: \" + e.getData().agentName());\n    } else if (event instanceof SubagentFailedEvent e) {\n        System.out.println(\"❌ Sub-agent failed: \" + e.getData().agentName());\n        System.out.println(\"  Error: \" + e.getData().error());\n    } else if (event instanceof SubagentSelectedEvent e) {\n        System.out.println(\"🎯 Agent selected: \" + e.getData().agentDisplayName());\n    } else if (event instanceof SubagentDeselectedEvent e) {\n        System.out.println(\"↩ Agent deselected, returning to parent\");\n    }\n});\n\nvar response = session.sendAndWait(\n    new MessageOptions().setPrompt(\"Research how authentication works in this codebase\")\n).get();\n```\n\n</div>\n\n</div>\n\n## Création d’une interface utilisateur pour l’arborescence des agents\n\nLes é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 :\n\n```typescript\ninterface AgentNode {\n    toolCallId: string;\n    name: string;\n    displayName: string;\n    status: \"running\" | \"completed\" | \"failed\";\n    error?: string;\n    startedAt: Date;\n    completedAt?: Date;\n}\n\nconst agentTree = new Map<string, AgentNode>();\n\nsession.on((event) => {\n    if (event.type === \"subagent.started\") {\n        agentTree.set(event.data.toolCallId, {\n            toolCallId: event.data.toolCallId,\n            name: event.data.agentName,\n            displayName: event.data.agentDisplayName,\n            status: \"running\",\n            startedAt: new Date(event.timestamp),\n        });\n    }\n\n    if (event.type === \"subagent.completed\") {\n        const node = agentTree.get(event.data.toolCallId);\n        if (node) {\n            node.status = \"completed\";\n            node.completedAt = new Date(event.timestamp);\n        }\n    }\n\n    if (event.type === \"subagent.failed\") {\n        const node = agentTree.get(event.data.toolCallId);\n        if (node) {\n            node.status = \"failed\";\n            node.error = event.data.error;\n            node.completedAt = new Date(event.timestamp);\n        }\n    }\n\n    // Render your UI with the updated tree\n    renderAgentTree(agentTree);\n});\n```\n\n## Outils de portée par assistant\n\nUtilisez 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 :\n\n```typescript\nconst session = await client.createSession({\n    customAgents: [\n        {\n            name: \"reader\",\n            description: \"Read-only exploration of the codebase\",\n            tools: [\"grep\", \"glob\", \"view\"],  // No write access\n            prompt: \"You explore and analyze code. Never suggest modifications directly.\",\n        },\n        {\n            name: \"writer\",\n            description: \"Makes code changes\",\n            tools: [\"view\", \"edit\", \"bash\"],   // Write access\n            prompt: \"You make precise code changes as instructed.\",\n        },\n        {\n            name: \"unrestricted\",\n            description: \"Full access agent for complex tasks\",\n            tools: null,                        // All tools available\n            prompt: \"You handle complex multi-step tasks using any available tools.\",\n        },\n    ],\n});\n```\n\n> \\[!NOTE]\n> 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.\n\n## Outils réservés aux agents\n\nUtilisez 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.\n\nCela est utile quand :\n\n* Certains outils génèrent de grandes quantités de contexte qui surchargeraient l’agent principal\n* Vous souhaitez que l’agent principal agisse en tant qu’orchestrateur, en déléguant le travail lourd aux sous-agents spécialisés\n* Vous avez besoin d’une séparation stricte entre l’orchestration et l’exécution\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient, defineTool, approveAll } from \"@github/copilot-sdk\";\nimport { z } from \"zod\";\n\nconst heavyContextTool = defineTool(\"analyze-codebase\", {\n    description: \"Performs deep analysis of the codebase, generating extensive context\",\n    parameters: z.object({ query: z.string() }),\n    handler: async ({ query }) => {\n        // ... expensive analysis that returns lots of data\n        return { analysis: \"...\" };\n    },\n});\n\nconst session = await client.createSession({\n    tools: [heavyContextTool],\n    defaultAgent: {\n        excludedTools: [\"analyze-codebase\"],\n    },\n    customAgents: [\n        {\n            name: \"researcher\",\n            description: \"Deep codebase analysis agent with access to heavy-context tools\",\n            tools: [\"analyze-codebase\"],\n            prompt: \"You perform thorough codebase analysis using the analyze-codebase tool.\",\n        },\n    ],\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotClient\nfrom copilot.tools import Tool\n\nheavy_tool = Tool(\n    name=\"analyze-codebase\",\n    description=\"Performs deep analysis of the codebase\",\n    handler=analyze_handler,\n    parameters={\"type\": \"object\", \"properties\": {\"query\": {\"type\": \"string\"}}},\n)\n\nsession = await client.create_session(\n    tools=[heavy_tool],\n    default_agent={\"excluded_tools\": [\"analyze-codebase\"]},\n    custom_agents=[\n        {\n            \"name\": \"researcher\",\n            \"description\": \"Deep codebase analysis agent\",\n            \"tools\": [\"analyze-codebase\"],\n            \"prompt\": \"You perform thorough codebase analysis.\",\n        },\n    ],\n    on_permission_request=approve_all,\n)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n<!-- docs-validate: skip -->\n\n```golang\nsession, err := client.CreateSession(ctx, &copilot.SessionConfig{\n    Tools: []copilot.Tool{heavyTool},\n    DefaultAgent: &copilot.DefaultAgentConfig{\n        ExcludedTools: []string{\"analyze-codebase\"},\n    },\n    CustomAgents: []copilot.CustomAgentConfig{\n        {\n            Name:        \"researcher\",\n            Description: \"Deep codebase analysis agent\",\n            Tools:       []string{\"analyze-codebase\"},\n            Prompt:      \"You perform thorough codebase analysis.\",\n        },\n    },\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"csharp\" data-label=\"C#\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">C#</div>\n\n<!-- docs-validate: skip -->\n\n```csharp\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Tools = [analyzeCodebaseTool],\n    DefaultAgent = new DefaultAgentConfig\n    {\n        ExcludedTools = [\"analyze-codebase\"],\n    },\n    CustomAgents =\n    [\n        new CustomAgentConfig\n        {\n            Name = \"researcher\",\n            Description = \"Deep codebase analysis agent\",\n            Tools = [\"analyze-codebase\"],\n            Prompt = \"You perform thorough codebase analysis.\",\n        },\n    ],\n});\n```\n\n</div>\n\n</div>\n\n### Fonctionnement\n\nOutils répertoriés dans `defaultAgent.excludedTools`:\n\n1. **Sont inscrits** : leurs gestionnaires sont disponibles pour l’exécution\n2. **Sont masqués** de la liste d’outils de l’agent principal — le LLM ne les verra pas et ne les appellera pas directement\n3. **Restez disponible** pour n’importe quel sous-agent personnalisé qui les inclut dans son `tools` tableau\n\n### Interaction avec d’autres filtres d’outils\n\n`defaultAgent.excludedTools` est orthogonal aux `availableTools` et `excludedTools` au niveau de la session :\n\n| Filter                       | Scope                      | Résultat                                                                          |\n| ---------------------------- | -------------------------- | --------------------------------------------------------------------------------- |\n| `availableTools`             | À l’échelle de la session  | Liste d’autorisation : seuls ces outils existent pour n’importe qui               |\n| `excludedTools`              | À l’échelle de la session  | Liste de blocage : ces outils sont bloqués pour tout le monde                     |\n| `defaultAgent.excludedTools` | Agent principal uniquement | Ces outils sont cachés à l’agent principal, mais disponibles pour les sous-agents |\n\nPréséance:\n\n1. Le niveau `availableTools`/`excludedTools` de session est appliqué en premier (globalement)\n2. `defaultAgent.excludedTools` est appliqué en haut, limitant davantage l’agent principal uniquement\n\n> \\[!NOTE]\n> 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.\n\n## Attachement de serveurs MCP à des agents\n\nChaque 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 :\n\n```typescript\nconst session = await client.createSession({\n    customAgents: [\n        {\n            name: \"db-analyst\",\n            description: \"Analyzes database schemas and queries\",\n            prompt: \"You are a database expert. Use the database MCP server to analyze schemas.\",\n            mcpServers: {\n                \"database\": {\n                    command: \"npx\",\n                    args: [\"-y\", \"@modelcontextprotocol/server-postgres\", \"postgresql://localhost/mydb\"],\n                },\n            },\n        },\n    ],\n});\n```\n\n## Modèles et bonnes pratiques\n\n### Associer un chercheur à un éditeur\n\nUn 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.\n\n```typescript\ncustomAgents: [\n    {\n        name: \"researcher\",\n        description: \"Analyzes code structure, finds patterns, and answers questions\",\n        tools: [\"grep\", \"glob\", \"view\"],\n        prompt: \"You are a code analyst. Thoroughly explore the codebase to answer questions.\",\n    },\n    {\n        name: \"implementer\",\n        description: \"Implements code changes based on analysis\",\n        tools: [\"view\", \"edit\", \"bash\"],\n        prompt: \"You make minimal, targeted code changes. Always verify changes compile.\",\n    },\n]\n```\n\n### Conserver des descriptions spécifiques de l’agent\n\nLe runtime utilise `description` pour répondre à l'intention de l’utilisateur. Les descriptions vagues mènent à une délégation médiocre :\n\n```typescript\n// ❌ Too vague — runtime can't distinguish from other agents\n{ description: \"Helps with code\" }\n\n// ✅ Specific — runtime knows when to delegate\n{ description: \"Analyzes Python test coverage and identifies untested code paths\" }\n```\n\n### Gérer correctement les défaillances\n\nLes sous-agents peuvent échouer. Écoutez toujours les événements `subagent.failed` et gérez-les dans votre application :\n\n```typescript\nsession.on((event) => {\n    if (event.type === \"subagent.failed\") {\n        logger.error(`Agent ${event.data.agentName} failed: ${event.data.error}`);\n        // Show error in UI, retry, or fall back to parent agent\n    }\n});\n```"}