{"meta":{"title":"Agentes personalizados y orquestación de subagentes","intro":"Defina agentes especializados con herramientas e instrucciones de alcance limitado y, a continuación, deje que Copilot los orqueste como subagentes dentro de una única sesión. Para enviar varios subagentes en paralelo, consulte Modo flota.","product":"GitHub Copilot","breadcrumbs":[{"href":"/es/copilot","title":"GitHub Copilot"},{"href":"/es/copilot/how-tos","title":"Procedimientos"},{"href":"/es/copilot/how-tos/copilot-sdk","title":"SDK de Copilot"},{"href":"/es/copilot/how-tos/copilot-sdk/features","title":"Características"},{"href":"/es/copilot/how-tos/copilot-sdk/features/custom-agents","title":"Agentes personalizados"}],"documentType":"article"},"body":"# Agentes personalizados y orquestación de subagentes\n\nDefina agentes especializados con herramientas e instrucciones de alcance limitado y, a continuación, deje que Copilot los orqueste como subagentes dentro de una única sesión. Para enviar varios subagentes en paralelo, consulte Modo flota.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Visión general\n\nLos agentes personalizados son definiciones de agente ligeras que se adjuntan a una sesión. Cada agente tiene su propia solicitud del sistema, restricciones de herramientas y servidores MCP opcionales. Cuando la solicitud de un usuario coincide con la experiencia de un agente, el tiempo de ejecución de Copilot se delega automáticamente en ese agente como un **sub-agent**, ejecutándolo en un contexto aislado mientras se vuelven a transmitir eventos de ciclo de vida a la sesión primaria.\n\n![Diagrama: Diagrama de flujo que muestra el proceso descrito.](/assets/images/help/copilot/copilot-sdk/features-custom-agents-diagram-0.png)\n\n| Concepto                 | Description                                                                                                            |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| **Agente personalizado** | Configuración de agente nombrada con su propio indicador y conjunto de herramientas                                    |\n| **Subagente**            | Un agente personalizado invocado por el tiempo de ejecución para controlar parte de una tarea                          |\n| **Inferencia**           | La capacidad del tiempo de ejecución para seleccionar automáticamente un agente en función de la intención del usuario |\n| **Sesión principal**     | Sesión que generó el subagente; recibe todos los eventos del ciclo de vida.                                            |\n\n## Definición de agentes personalizados\n\nPase `customAgents` al crear una sesión. Cada agente necesita como mínimo un `name` y `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## Referencia de configuración\n\n| Propiedad                                                                                                                                                                                                        | Tipo       | Obligatorio | Description                           |\n| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------- | ------------------------------------- |\n| `name`                                                                                                                                                                                                           | `string`   | ✅           | Identificador único del agente        |\n| `displayName`                                                                                                                                                                                                    | `string`   |             |                                       |\n| Nombre comprensible que se muestra en eventos                                                                                                                                                                    |            |             |                                       |\n| `description`                                                                                                                                                                                                    | `string`   |             |                                       |\n| Lo que hace el agente: ayuda al tiempo de ejecución a seleccionarlo.                                                                                                                                             |            |             |                                       |\n| `tools`                                                                                                                                                                                                          |            |             |                                       |\n| `string[]` o `null`                                                                                                                                                                                              |            |             |                                       |\n| Nombres de herramienta que el agente puede usar.                                                                                                                                                                 |            |             |                                       |\n| `null` o se omite = todas las herramientas                                                                                                                                                                       |            |             |                                       |\n| `prompt`                                                                                                                                                                                                         | `string`   | ✅           | Indicativo del sistema para el agente |\n| `mcpServers`                                                                                                                                                                                                     | `object`   |             |                                       |\n| Configuraciones de servidor MCP específicas de este agente                                                                                                                                                       |            |             |                                       |\n| `infer`                                                                                                                                                                                                          | `boolean`  |             |                                       |\n| Si el entorno de ejecución puede seleccionar automáticamente este agente (valor predeterminado: `true`)                                                                                                          |            |             |                                       |\n| `skills`                                                                                                                                                                                                         | `string[]` |             |                                       |\n| Nombres de las aptitudes que se cargarán previamente en el contexto del agente al iniciarse                                                                                                                      |            |             |                                       |\n| `model`                                                                                                                                                                                                          | `string`   |             |                                       |\n| Identificador de modelo que se va a usar mientras se ejecuta este agente                                                                                                                                         |            |             |                                       |\n| `reasoningEffort`                                                                                                                                                                                                | `string`   |             |                                       |\n| Esfuerzo de razonamiento que se debe utilizar mientras se ejecuta este agente. Si se omite, el SDK no envía ninguna anulación por agente y el tiempo de ejecución resuelve el esfuerzo (véase la nota más abajo) |            |             |                                       |\n\n> \\[!TIP]\n> Una buena `description` ayuda al entorno de ejecución a coincidir la intención del usuario con el agente correcto. Sea específico sobre la experiencia y las funcionalidades del agente.\n\nEstablezca `model` y `reasoningEffort` para invalidar la configuración del modelo de la sesión primaria mientras se ejecuta un agente personalizado. Cuando se omite `reasoningEffort`, el SDK no envía ninguna anulación por agente y el entorno de ejecución determina el esfuerzo según su propio orden de precedencia: una opción de cliente para esa llamada, el valor predeterminado del modelo resuelto o la definición del agente tienen prioridad; de lo contrario, el entorno de ejecución hereda el esfuerzo de la sesión principal solo cuando el subagente ejecuta el mismo modelo que el principal. Cuando el subagente se resuelve en un modelo diferente, recurre al valor predeterminado de ese modelo en lugar de heredar el esfuerzo del padre. Python usa `reasoning_effort`, .NET usa `ReasoningEffort`, Go usa `ReasoningEffort`, Java usa `setReasoningEffort`y Rust usa `with_reasoning_effort`.\n\nAdemás de la configuración por agente anterior, puede establecer `agent` en la **propia configuración de sesión** para seleccionar previamente qué agente personalizado está activo cuando se inicia la sesión. Consulte [Selección de un agente en la creación de la sesión](#selecting-an-agent-at-session-creation) a continuación.\n\n| Propiedad de configuración de sesión | Tipo     | Description                                                                                                                                      |\n| ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `agent`                              | `string` | Nombre del agente personalizado que se va a seleccionar previamente en la creación de la sesión. Debe coincidir con un `name` en `customAgents`. |\n\n## Aptitudes por agente\n\nPuede cargar previamente aptitudes en el contexto de un agente mediante la propiedad `skills`. Cuando se especifica, el **contenido completo** de cada aptitud enumerada se inserta diligentemente en el contexto del agente al iniciarse; el agente no necesita invocar una herramienta de aptitud; las instrucciones ya están presentes. Las aptitudes son **opcionales**: los agentes no reciben aptitudes de forma predeterminada y los subagentes no heredan las aptitudes del elemento primario. Los nombres de las aptitudes se resuelven a partir de `skillDirectories` de nivel de sesión.\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\nEn este ejemplo, `security-auditor` comienza con `security-scan` y `dependency-check` ya insertado en su contexto, mientras que `docs-writer` comienza con `markdown-lint`. Un agente sin un `skills` campo no recibe contenido de aptitudes.\n\n## Selección de un agente al crear la sesión\n\nPuede introducir `agent` en la configuración de la sesión para preseleccionar qué agente personalizado estará activo cuando la sesión comience. El valor debe coincidir con el `name` de uno de los agentes definidos en `customAgents`.\n\nEsto equivale a llamar `session.rpc.agent.select()` después de la creación, pero evita la llamada API adicional y garantiza que el agente está activo desde el primer aviso.\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## Funcionamiento de la delegación de subagentes\n\nCuando se envía una solicitud a una sesión con agentes personalizados, el tiempo de ejecución evalúa si se va a delegar en un subagente:\n\n1. **Coincidencia de intenciones**: el tiempo de ejecución analiza el mensaje del usuario con respecto a los agentes `name` y `description`\n2. **Selección del agente**: si se encuentra una coincidencia y `infer` no es `false`, el tiempo de ejecución selecciona al agente.\n3. **Ejecución aislada**: el subagente se ejecuta con su propio símbolo del sistema y un conjunto de herramientas restringido\n4. **Transmisión de eventos**: los eventos de ciclo de vida (`subagent.started`, `subagent.completed`, etc.) vuelven a la sesión principal.\n5. **Integración de resultados**: la salida del subagente se incorpora a la respuesta del agente principal.\n\n### Control de la inferencia\n\nDe forma predeterminada, todos los agentes personalizados están disponibles para la selección automática (`infer: true`). Establézcalo `infer: false` para impedir que el tiempo de ejecución seleccione automáticamente un agente, útil para los agentes que solo desea invocar a través de solicitudes de usuario explícitas:\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## Escuchando eventos del subagente\n\nCuando se ejecuta un subagente, la sesión primaria emite eventos de ciclo de vida. Suscríbase a estos eventos para crear interfaces de usuario que visualicen la actividad del agente.\n\nLos eventos de sesión originados por subagentes comparten el flujo de sesión del padre e incluyen `agentId` en el nivel de sobre. Los eventos del agente raíz/principal y los eventos de nivel de sesión omiten `agentId`, por lo que los renderizadores pueden mantener la respuesta del agente padre separada de las trazas del subagente verificando el sobre del evento.\n\n### Tipos de evento\n\n| Event                                                                                                              | Emitido cuando                              | Data |\n| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | ---- |\n| `subagent.selected`                                                                                                | Runtime selecciona un agente para la tarea. |      |\n| `agentName`, , `agentDisplayName`, `tools`                                                                         |                                             |      |\n| `subagent.started`                                                                                                 | El subagente inicia la ejecución            |      |\n| `toolCallId`, `agentName`, `agentDisplayName`, , `agentDescription`, `model?`                                      |                                             |      |\n| `subagent.completed`                                                                                               | El subagente finaliza correctamente         |      |\n| `toolCallId`, `agentName`, `agentDisplayName`, `model?`, `durationMs?`, , `totalTokens?``totalToolCalls?`          |                                             |      |\n| `subagent.failed`                                                                                                  | Subagente encuentra un error                |      |\n| `toolCallId`, `agentName`, `agentDisplayName`, `error`, `model?`, `durationMs?`, , `totalTokens?``totalToolCalls?` |                                             |      |\n| `subagent.deselected`                                                                                              | Tiempo de ejecución se aleja del subagente  | —    |\n\n### Suscribirse a eventos\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## Creación de una interfaz de usuario de árbol de agente\n\nLos eventos de subagente incluyen `toolCallId` campos que permiten reconstruir el árbol de ejecución. Este es un patrón para realizar el seguimiento de la actividad del agente:\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## Herramientas de definición por agente\n\nUse la `tools` propiedad para restringir las herramientas a las que puede acceder un agente. Esto es esencial para la seguridad y para mantener los agentes centrados:\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> Cuando `tools` es `null` o se omite, el agente hereda el acceso a todas las herramientas configuradas en la sesión. Use listas de herramientas explícitas para aplicar el principio de privilegios mínimos.\n\n## Herramientas exclusivas del agente\n\nUtilice la propiedad `defaultAgent` en la configuración de la sesión para ocultar determinadas herramientas al agente predeterminado (el agente integrado que gestiona los turnos cuando no se selecciona ningún agente personalizado). Esto obliga al agente principal a delegar a los subagentes cuando se necesitan las funcionalidades de esas herramientas, manteniendo limpio el contexto del agente principal.\n\nResulta útil cuando:\n\n* Algunas herramientas generan grandes cantidades de contexto que sobrecargarían al agente principal.\n* Quiere que el agente principal actúe como orquestador y delega trabajos pesados a subagentes especializados.\n* Necesita una separación estricta entre orquestación y ejecución.\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### Cómo funciona\n\nHerramientas enumeradas en `defaultAgent.excludedTools`:\n\n1. **Están registrados**: sus controladores están disponibles para su ejecución.\n2. **Están ocultas** en la lista de herramientas del agente principal; el LLM no las verá ni las invocará directamente.\n3. **Permanezca disponible** para cualquier subagente personalizado que los incluya en su `tools` matriz.\n\n### Interacción con otros filtros de herramientas\n\n`defaultAgent.excludedTools` es ortogonal al nivel `availableTools` de sesión y `excludedTools`:\n\n| Filtro                       | Ámbito                | Efecto                                                                                            |\n| ---------------------------- | --------------------- | ------------------------------------------------------------------------------------------------- |\n| `availableTools`             | Toda la sesión        | Lista de permitidos: solo existen estas herramientas para cualquier persona                       |\n| `excludedTools`              | Toda la sesión        | Lista de bloqueados: estas herramientas están bloqueadas para todos los usuarios                  |\n| `defaultAgent.excludedTools` | Solo agente principal | Estas herramientas están ocultas del agente principal, pero están disponibles para los subagentes |\n\nPrecedencia:\n\n1. El nivel de sesión `availableTools`/`excludedTools` se aplica primero (globalmente)\n2. `defaultAgent.excludedTools` se aplica encima, lo que restringe aún más solo al agente principal\n\n> \\[!NOTE]\n> Si una herramienta está en `excludedTools` (nivel de sesión) y `defaultAgent.excludedTools`, la exclusión de nivel de sesión tiene prioridad, la herramienta no está disponible para todos.\n\n## Adjuntar servidores MCP a agentes\n\nCada agente personalizado puede tener sus propios servidores MCP (Protocolo de contexto de modelo), lo que le proporciona acceso a orígenes de datos especializados:\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## Patrones y procedimientos recomendados\n\n### Emparejar un investigador con un editor\n\nUn patrón común es definir un agente investigador de solo lectura y un agente editor capaz de escritura. El tiempo de ejecución delega las tareas de exploración al investigador y las tareas de modificación al editor:\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### Mantener las descripciones del agente específicas\n\nEl tiempo de ejecución usa el `description` para que coincida con la intención del usuario. Las descripciones vagas conducen a una delegación deficiente:\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### Gestiona los fallos de manera elegante\n\nLos subagentes pueden fallar. Escuche siempre los eventos de `subagent.failed` y manéjelos en su aplicación.\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```"}