# カスタム エージェントとサブエージェント オーケストレーション

スコープ指定されたツールとプロンプトを使用して特殊なエージェントを定義し、Copilot 1 つのセッション内でサブエージェントとして調整できるようにします。 複数のサブエージェントを並列にディスパッチする場合は、 フリート モード を参照してください。

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

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

## Overview

カスタム エージェントは、セッションにアタッチする軽量のエージェント定義です。 各エージェントには、独自のシステム プロンプト、ツールの制限、およびオプションの MCP サーバーがあります。 ユーザーの要求がエージェントの専門知識と一致すると、Copilot ランタイムは自動的にそのエージェントに **sub-agent** として委任し、ライフサイクル イベントを親セッションにストリーミングしながら分離されたコンテキストで実行します。

![図: 説明されたプロセスを示すフローチャート。](/assets/images/help/copilot/copilot-sdk/features-custom-agents-diagram-0.png)

| 概念              | Description                                  |
| --------------- | -------------------------------------------- |
| **カスタム エージェント** | 独自のプロンプトとツール セットを含む名前付きエージェント構成              |
| **サブエージェント**    | タスクの一部を処理するためにランタイムによって呼び出されるカスタム エージェント     |
| **推論**          | ユーザーの意図に基づいてエージェントを自動選択するランタイムの機能            |
| **親セッション**      | サブエージェントを生成したセッション。は、すべてのライフサイクル イベントを受信します。 |

## カスタム エージェントの定義

セッションの作成時に `customAgents` 渡します。 各エージェントには、少なくとも `name` と `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>

## 構成のリファレンス

| 財産                                                                                | タイプ        | 必須 | Description       |
| --------------------------------------------------------------------------------- | ---------- | -- | ----------------- |
| `name`                                                                            | `string`   | ✅  | エージェントのユニークID     |
| `displayName`                                                                     | `string`   |    |                   |
| イベントに表示される人間が判読できる名前                                                              |            |    |                   |
| `description`                                                                     | `string`   |    |                   |
| エージェントが実行すること - ランタイムがエージェントを選択するのを支援します                                          |            |    |                   |
| `tools`                                                                           |            |    |                   |
| `string[]` または `null`                                                             |            |    |                   |
| エージェントが使用できるツール名。                                                                 |            |    |                   |
| `null` または省略 = すべてのツール                                                            |            |    |                   |
| `prompt`                                                                          | `string`   | ✅  | エージェントのシステム プロンプト |
| `mcpServers`                                                                      | `object`   |    |                   |
| このエージェントに固有の MCP サーバー構成                                                           |            |    |                   |
| `infer`                                                                           | `boolean`  |    |                   |
| ランタイムがこのエージェントを自動選択できるかどうか (既定値: `true`)                                          |            |    |                   |
| `skills`                                                                          | `string[]` |    |                   |
| 起動時にエージェントのコンテキストにプリロードするスキル名                                                     |            |    |                   |
| `model`                                                                           | `string`   |    |                   |
| このエージェントの実行中に使用するモデル識別子                                                           |            |    |                   |
| `reasoningEffort`                                                                 | `string`   |    |                   |
| このエージェントの実行中に使用する推論作業。 省略すると、SDK はエージェントごとのオーバーライドを送信せず、ランタイムは作業を解決します (下記の注を参照)。 |            |    |                   |

> \[!TIP]
> 適切な `description` は、ランタイムがユーザーの意図を適切なエージェントと照合するのに役立ちます。 エージェントの専門知識と機能について具体的に説明します。

カスタム エージェントの実行中に親セッションのモデル設定をオーバーライドするには、 `model` と `reasoningEffort` を設定します。
`reasoningEffort`を省略すると、SDK はエージェントごとのオーバーライドを送信せず、ランタイムは、呼び出しごとのクライアント オプション、解決されたモデルの既定値、またはエージェント定義がすべて優先されます。それ以外の場合、サブエージェントが親と同じモデルを実行する場合にのみ、ランタイムは親セッションの作業を継承します。 サブエージェントが別のモデルに解決されると、親の作業を継承するのではなく、そのモデルの既定値にフォールバックします。
`reasoning_effort`を使用Python、.NETは`ReasoningEffort`を使用し、Go は`ReasoningEffort`を使用し、Javaは`setReasoningEffort`を使用し、Rust は`with_reasoning_effort`を使用します。

上記のエージェントごとの構成に加えて、`agent`自体の\*\*\*\* を設定して、セッションの開始時にアクティブなカスタム エージェントを事前に選択できます。 以下の [「セッション作成時のエージェントの選択](#selecting-an-agent-at-session-creation) 」を参照してください。

| セッション構成プロパティ                       | タイプ      | Description                      |
| ---------------------------------- | -------- | -------------------------------- |
| `agent`                            | `string` | セッションの作成時に事前に選択するカスタム エージェントの名前。 |
| `name`の`customAgents`と一致する必要があります。 |          |                                  |

## エージェントごとのスキル

`skills` プロパティを使用して、エージェントのコンテキストにスキルを事前に読み込むことができます。 指定すると、リストされている各スキルの **完全なコンテンツ** が起動時にエージェントのコンテキストに熱心に挿入されます。エージェントはスキル ツールを呼び出す必要はありません。手順は既に存在します。 スキルは **オプトイン**です。エージェントは既定でスキルを受け取らず、サブエージェントは親からスキルを継承しません。 スキル名は、セッション レベルの `skillDirectories`から解決されます。

```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" }),
});
```

この例では、 `security-auditor` は `security-scan` で始まり、 `dependency-check` はコンテキストに既に挿入されていますが、 `docs-writer` は `markdown-lint`で始まります。
`skills`フィールドを持たないエージェントは、スキルコンテンツを受け取らない。

## セッション作成時のエージェントの選択

セッション構成で `agent` を渡して、セッションの開始時にアクティブにするカスタム エージェントを事前に選択できます。 この値は、`name`で定義されているいずれかのエージェントの`customAgents`と一致する必要があります。

これは、作成後に `session.rpc.agent.select()` を呼び出すことと同じですが、追加の API 呼び出しを回避し、最初のプロンプトからエージェントがアクティブになっていることを確認します。

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

## サブエージェントの委任のしくみ

カスタム エージェントとのセッションにプロンプトを送信すると、ランタイムはサブエージェントに委任するかどうかを評価します。

1. **意図の照合** - ランタイムは、ユーザーのプロンプトを各エージェントの`name`および`description`と比較し分析します。
2. **エージェントの選択** - 一致するものが見つかり、 `infer` が `false`されていない場合、ランタイムはエージェントを選択します。
3. **分離実行** - サブエージェントは、独自のプロンプトと制限付きツール セットを使用して実行されます
4. **イベント ストリーミング** - ライフサイクル イベント (`subagent.started`、 `subagent.completed`など) が親セッションにストリームバックされます
5. **結果の統合** - サブエージェントの出力が親エージェントの応答に組み込まれます

### 推論の制御

既定では、すべてのカスタム エージェントを自動選択 (`infer: true`) で使用できます。
`infer: false`を設定して、ランタイムがエージェントを自動選択できないようにします。明示的なユーザー要求によってのみ呼び出すエージェントに役立ちます。

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

## サブエージェント イベントのリッスン

サブエージェントを実行すると、親セッションはライフサイクル イベントを生成します。 これらのイベントをサブスクライブして、エージェント アクティビティを視覚化する UI を構築します。

サブエージェントから発信されたセッション イベントは、親セッション ストリームを共有し、エンベロープ レベルの `agentId`を含めます。 ルート/メイン エージェント イベントとセッション レベルのイベントでは `agentId`が省略されるため、レンダラーはイベント エンベロープを確認することで、親の応答をサブエージェント トレースから分離できます。

### イベントの種類

| イベント                                                                                                               | 次の場合に生成されます                 | データ |
| ------------------------------------------------------------------------------------------------------------------ | --------------------------- | --- |
| `subagent.selected`                                                                                                | ランタイムがタスクのエージェントを選択する       |     |
| `agentName`、`agentDisplayName`、`tools`                                                                             |                             |     |
| `subagent.started`                                                                                                 | サブエージェントが実行を開始する            |     |
| `toolCallId`、`agentName`、`agentDisplayName`、`agentDescription`、`model?`                                            |                             |     |
| `subagent.completed`                                                                                               | サブエージェントが正常に終了する            |     |
| `toolCallId`､`agentName`、`agentDisplayName`、`model?`、`durationMs?`、`totalTokens?`、`totalToolCalls?`                |                             |     |
| `subagent.failed`                                                                                                  | サブエージェントでエラーが発生する           |     |
| `toolCallId`、 `agentName`、 `agentDisplayName`、 `error`、 `model?`、 `durationMs?`、 `totalTokens?`、 `totalToolCalls?` |                             |     |
| `subagent.deselected`                                                                                              | ランタイムがサブエージェントから他のプロセスに移行する | —   |

### イベントに登録する

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

## エージェント ツリー UI の構築

サブエージェント イベントには、実行ツリーを再構築できる `toolCallId` フィールドが含まれます。 エージェント アクティビティを追跡するためのパターンを次に示します。

```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);
});
```

## エージェントごとのツールの範囲設定

`tools` プロパティを使用して、エージェントがアクセスできるツールを制限します。 これは、セキュリティとエージェントの集中を維持するために不可欠です。

```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]
> `tools`が`null`または省略されると、エージェントはセッションで構成されているすべてのツールへのアクセスを継承します。 明示的なツール リストを使用して、最小限の特権の原則を適用します。

## エージェント専用ツール

セッション構成の `defaultAgent` プロパティを使用して、既定のエージェント (カスタム エージェントが選択されていないときにターンを処理する組み込みエージェント) から特定のツールを非表示にします。 これにより、これらのツールの機能が必要になったときにメイン エージェントがサブエージェントに委任され、メイン エージェントのコンテキストがクリーンな状態が維持されます。

これは、次の場合に役立ちます。

* 特定のツールは、メイン エージェントを圧倒する大量のコンテキストを生成します
* メイン エージェントをオーケストレーターとして機能させ、特殊なサブエージェントに重い作業を委任する
* オーケストレーションと実行を厳密に分離する必要がある

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

### どのように機能するのか

`defaultAgent.excludedTools`に一覧表示されているツール:

1. **登録済み**であり、ハンドラーを実行できます
2. メイン エージェントのツール リストから**非表示**になります。LLM はそれらを直接表示または呼び出しません
3. それらを\*\*\*\* 配列に含む任意のカスタム サブエージェントで`tools`

### 他のツールフィルターとの連携

`defaultAgent.excludedTools` はセッション レベルの `availableTools` と `excludedTools`に直交します。

| Filter                       | Scope        | 影響                                           |
| ---------------------------- | ------------ | -------------------------------------------- |
| `availableTools`             | セッション全体      | Allowlist - すべてのユーザーに対してこれらのツールのみが存在します      |
| `excludedTools`              | セッション全体      | ブロックリスト - すべてのユーザーに対してこれらのツールがブロックされます       |
| `defaultAgent.excludedTools` | メイン エージェントのみ | これらのツールはメイン エージェントには表示されませんが、サブエージェントで使用できます |

優先順位：

1. セッション レベルの `availableTools`/`excludedTools` が最初に適用されます (グローバルに)
2. `defaultAgent.excludedTools` が上に適用され、メイン エージェントのみがさらに制限されます

> \[!NOTE]
> ツールが `excludedTools` (セッション レベル) と `defaultAgent.excludedTools`の両方にある場合、セッション レベルの除外が優先されます。ツールはすべてのユーザーが使用できません。

## エージェントへの MCP サーバーのアタッチ

各カスタム エージェントは独自の MCP (モデル コンテキスト プロトコル) サーバーを持つ可能性があり、特殊なデータ ソースにアクセスできます。

```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"],
                },
            },
        },
    ],
});
```

## パターンとベスト プラクティス

### 研究者と編集者のペアリング

一般的なパターンは、読み取り専用の研究者エージェントと書き込み可能なエディター エージェントを定義することです。 ランタイムは探索タスクを研究者に委任し、変更タスクをエディターに委任します。

```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.",
    },
]
```

### エージェントの説明を固有に保つ

ランタイムは、 `description` を使用してユーザーの意図と一致します。 あいまいな説明は、委任が不適切になります。

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

### エラーを適切に処理する

サブエージェントは失敗する可能性があります。 常に `subagent.failed` イベントをリッスンし、アプリケーションで処理します。

```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
    }
});
```