# ステアリングとキューイング

エージェントが既に動作している間、ユーザーは 2 つの対話パターンでメッセージを送信できます。 ステアリング は途中ターンでエージェントをリダイレクトし、現在のターンが完了した後に順次処理するためにメッセージ をキューに入 れます。

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

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

## Overview

セッションがターンをアクティブに処理している場合、受信メッセージは、`mode`の `MessageOptions` フィールドを使用して、次の 2 つのモードのいずれかで配信できます。

| モード                    | Behavior                        | 利用シーン                                     |
| ---------------------- | ------------------------------- | ----------------------------------------- |
| `"immediate"` (ステアリング) | LLMの**現在の**ターンに挿入               | "実際には、そのファイルを作成しないでください。別のアプローチを使用してください" |
| `"enqueue"` (キュー)      | 現在のターンが終了した **後** にキューに入れ、処理される | "この後、テストも修正します"                           |

![図: 説明されたプロセスを示すシーケンス図。](/assets/images/help/copilot/copilot-sdk/features-steering-and-queueing-diagram-0.png)

## ステアリング (イミディエイト モード)

ステアリングは、エージェントの現在のターンに直接挿入されるメッセージを送信します。 エージェントはリアルタイムでメッセージを確認し、それに応じて応答を調整します。ターンを中止せずにコースの修正に役立ちます。

<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",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Start a long-running task
const msgId = await session.send({
    prompt: "Refactor the authentication module to use sessions",
});

// While the agent is working, steer it
await session.send({
    prompt: "Actually, use JWT tokens instead of sessions",
    mode: "immediate",
});
```

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

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
        model="gpt-5.4",
    )

    # Start a long-running task
    msg_id = await session.send(
        "Refactor the authentication module to use sessions",
    )

    # While the agent is working, steer it
    await session.send(
        "Actually, use JWT tokens instead of sessions",
        mode="immediate",
    )

    await client.stop()
```

</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
package main

import (
    "context"
    "log"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    if err := client.Start(ctx); err != nil {
        log.Fatal(err)
    }
    defer client.Stop()

    session, err := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-5.4",
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    // Start a long-running task
    _, err = session.Send(ctx, copilot.MessageOptions{
        Prompt: "Refactor the authentication module to use sessions",
    })
    if err != nil {
        log.Fatal(err)
    }

    // While the agent is working, steer it
    _, err = session.Send(ctx, copilot.MessageOptions{
        Prompt: "Actually, use JWT tokens instead of sessions",
        Mode:   "immediate",
    })
    if err != nil {
        log.Fatal(err)
    }
}
```

</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",
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});

// Start a long-running task
var msgId = await session.SendAsync(new MessageOptions
{
    Prompt = "Refactor the authentication module to use sessions"
});

// While the agent is working, steer it
await session.SendAsync(new MessageOptions
{
    Prompt = "Actually, use JWT tokens instead of sessions",
    Mode = "immediate"
});
```

</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.*;

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

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-5.4")
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();

    // Start a long-running task
    session.send(new MessageOptions()
        .setPrompt("Refactor the authentication module to use sessions")
    ).get();

    // While the agent is working, steer it
    session.send(new MessageOptions()
        .setPrompt("Actually, use JWT tokens instead of sessions")
        .setMode("immediate")
    ).get();
}
```

</div>

</div>

### ステアリングの内部構造

1. メッセージがランタイムの `ImmediatePromptProcessor` キューに追加されます
2. 現在のターン内の次の LLM 要求の前に、プロセッサはメッセージを会話に挿入します
3. エージェントは、ステアリング メッセージを新しいユーザー メッセージとして認識し、その応答を調整します
4. ステアリング メッセージが処理される前にターンが完了すると、次のターンの通常のキューに自動的に移動されます。

> \[!NOTE]
> メッセージの転送は、現在のターン内で可能な限りベストを尽くします。 エージェントが既にツール呼び出しにコミットしている場合、その呼び出しが完了した後でも同じターン内でステアリングが有効になります。

## キューイング (エンキュー モード)

メッセージをキューに登録すると、現在のターンが終了した後に順に処理されます。 キューに登録された各メッセージは、独自のフル ターンを開始します。 これは既定のモードです。 `mode`を省略すると、SDK は `"enqueue"` を使用します。

<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",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Send an initial task
await session.send({ prompt: "Set up the project structure" });

// Queue follow-up tasks while the agent is busy
await session.send({
    prompt: "Add unit tests for the auth module",
    mode: "enqueue",
});

await session.send({
    prompt: "Update the README with setup instructions",
    mode: "enqueue",
});

// Messages are processed in FIFO order after each turn completes
```

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

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
        model="gpt-5.4",
    )

    # Send an initial task
    await session.send("Set up the project structure")

    # Queue follow-up tasks while the agent is busy
    await session.send(
        "Add unit tests for the auth module",
        mode="enqueue",
    )

    await session.send(
        "Update the README with setup instructions",
        mode="enqueue",
    )

    # Messages are processed in FIFO order after each turn completes
    await client.stop()
```

</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
// Send an initial task
session.Send(ctx, copilot.MessageOptions{
    Prompt: "Set up the project structure",
})

// Queue follow-up tasks while the agent is busy
session.Send(ctx, copilot.MessageOptions{
    Prompt: "Add unit tests for the auth module",
    Mode:   "enqueue",
})

session.Send(ctx, copilot.MessageOptions{
    Prompt: "Update the README with setup instructions",
    Mode:   "enqueue",
})

// Messages are processed in FIFO order after each turn completes
```

</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
// Send an initial task
await session.SendAsync(new MessageOptions
{
    Prompt = "Set up the project structure"
});

// Queue follow-up tasks while the agent is busy
await session.SendAsync(new MessageOptions
{
    Prompt = "Add unit tests for the auth module",
    Mode = "enqueue"
});

await session.SendAsync(new MessageOptions
{
    Prompt = "Update the README with setup instructions",
    Mode = "enqueue"
});

// Messages are processed in FIFO order after each turn completes
```

</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.*;

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

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-5.4")
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();

    // Send an initial task
    session.send(new MessageOptions().setPrompt("Set up the project structure")).get();

    // Queue follow-up tasks while the agent is busy
    session.send(new MessageOptions()
        .setPrompt("Add unit tests for the auth module")
        .setMode("enqueue")
    ).get();

    session.send(new MessageOptions()
        .setPrompt("Update the README with setup instructions")
        .setMode("enqueue")
    ).get();

    // Messages are processed in FIFO order after each turn completes
}
```

</div>

</div>

### キューイングの内部での動作

1. メッセージはセッションの `itemQueue` に追加されます。 `QueuedItem`
2. 現在のターンが完了し、セッションがアイドル状態になると、 `processQueuedItems()` 実行されます。
3. 項目は FIFO 順にデキューされます。各メッセージはエージェントのターン全体をトリガーします
4. ターンが終了したときにステアリング メッセージが保留中であった場合は、キューの先頭に移動されます。
5. キューが空になるまで処理が続行され、セッションによってアイドル 状態のイベントが生成されます

## ステアリングとキューイングの組み合わせ

両方のパターンを 1 つのセッションで一緒に使用できます。 ステアリングは、キューに入ったメッセージが自分のターンを待機している間、現在のターンに影響します。

<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
const session = await client.createSession({
    model: "gpt-5.4",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Start a task
await session.send({ prompt: "Refactor the database layer" });

// Steer the current work
await session.send({
    prompt: "Make sure to keep backwards compatibility with the v1 API",
    mode: "immediate",
});

// Queue a follow-up for after this turn
await session.send({
    prompt: "Now add migration scripts for the schema changes",
    mode: "enqueue",
});
```

</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
session = await client.create_session(
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
    model="gpt-5.4",
)

# Start a task
await session.send("Refactor the database layer")

# Steer the current work
await session.send(
    "Make sure to keep backwards compatibility with the v1 API",
    mode="immediate",
)

# Queue a follow-up for after this turn
await session.send(
    "Now add migration scripts for the schema changes",
    mode="enqueue",
)
```

</div>

</div>

## ステアリングとキューイングの選択

| シナリオ                      | Pattern                      | なぜでしょうか |
| ------------------------- | ---------------------------- | ------- |
| エージェントが間違った方向に進んでいる       |                              |         |
| **ステアリング**                | 進行状況を失うことなく現在のターンをリダイレクトします  |         |
| あなたは、エージェントが行うべきことを思いついた。 |                              |         |
| **待ち行列**                  | 現在の作業を中断しません。次に実行            |         |
| エージェントが間違いを犯しようとしています     |                              |         |
| **ステアリング**                | 間違いが犯される前に介入する               |         |
| 複数のタスクを連結する               |                              |         |
| **待ち行列**                  | FIFO の順序付けにより、予測可能な実行が保証されます |         |
| 現在のタスクにコンテキストを追加する        |                              |         |
| **ステアリング**                | エージェントが自身の現在の推論に組み込む         |         |
| 関連のない要求をバッチ処理する場合         |                              |         |
| **待ち行列**                  | それぞれが明確なコンテキストで完全なターンを行います   |         |

## ステアリングとキューを使用して UI を構築する

両方のモードをサポートする対話型 UI を構築するためのパターンを次に示します。

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

interface PendingMessage {
    prompt: string;
    mode: "immediate" | "enqueue";
    sentAt: Date;
}

class InteractiveChat {
    private session: CopilotSession;
    private isProcessing = false;
    private pendingMessages: PendingMessage[] = [];

    constructor(session: CopilotSession) {
        this.session = session;

        session.on((event) => {
            if (event.type === "session.idle") {
                this.isProcessing = false;
                this.onIdle();
            }
            if (event.type === "assistant.message") {
                this.renderMessage(event);
            }
        });
    }

    async sendMessage(prompt: string): Promise<void> {
        if (!this.isProcessing) {
            this.isProcessing = true;
            await this.session.send({ prompt });
            return;
        }

        // Session is busy — let the user choose how to deliver
        // Your UI would present this choice (e.g., buttons, keyboard shortcuts)
    }

    async steer(prompt: string): Promise<void> {
        this.pendingMessages.push({
            prompt,
            mode: "immediate",
            sentAt: new Date(),
        });
        await this.session.send({ prompt, mode: "immediate" });
    }

    async enqueue(prompt: string): Promise<void> {
        this.pendingMessages.push({
            prompt,
            mode: "enqueue",
            sentAt: new Date(),
        });
        await this.session.send({ prompt, mode: "enqueue" });
    }

    private onIdle(): void {
        this.pendingMessages = [];
        // Update UI to show session is ready for new input
    }

    private renderMessage(event: unknown): void {
        // Render assistant message in your UI
    }
}
```

## API リファレンス

### メッセージオプション

| Language | フィールド  | タイプ                               | デフォルト       | Description |
| -------- | ------ | --------------------------------- | ----------- | ----------- |
| Node.js  | `mode` | `"enqueue" \| "immediate"`        | `"enqueue"` | メッセージ配信モード  |
| Python   | `mode` | `Literal["enqueue", "immediate"]` | `"enqueue"` | メッセージ配信モード  |
| Go       | `Mode` | `string`                          | `"enqueue"` | メッセージ配信モード  |
| .NET     | `Mode` | `string?`                         | `"enqueue"` | メッセージ配信モード  |

### 配送モード

| モード           | 影響          | アクティブターン中        | 待機中            |
| ------------- | ----------- | ---------------- | -------------- |
| `"enqueue"`   | 次のターンの順番待ち  | FIFO キューでの待機     | 新しいターンをすぐに開始する |
| `"immediate"` | 現在のターンに挿入する | 次の LLM 呼び出しの前に挿入 | 新しいターンをすぐに開始する |

> \[!NOTE]
> セッションがアイドル状態 (未処理)、両方のモードが同じように動作します。メッセージはすぐに新しいターンを開始します。

## ベスト プラクティス

1. **既定ではキューに登録**されます。ほとんどのメッセージに対して `"enqueue"` (または省略 `mode`) を使用します。 これは予測可能であり、進行中の作業を中断するリスクはありません。

2. **修正のためにステアリングを予約**する - エージェントが間違ったことを積極的に行っており、先に進む前にリダイレクトする必要がある場合は、 `"immediate"` を使用します。

3. **指示メッセージを簡潔に保つ** - エージェントはコースの修正をすばやく理解する必要があります。 長く複雑なステアリング メッセージは、現在のコンテキストを混乱させる可能性があります。

4. **オーバーステアを行わない** - 複数の迅速なステアリング メッセージによってターン品質が低下する可能性があります。 方向を大幅に変更する必要がある場合は、ターンを中止して新たに開始することを検討してください。

5. **UI にキューの状態を表示**する - ユーザーが保留中の内容を把握できるように、キューに登録されたメッセージの数を表示します。 アイドル 状態のイベントをリッスンして、表示をクリアします。

6. **ステアリングからキューへのフォールバックを処理**する - ターンが完了した後にステアリング メッセージが到着すると、自動的にキューに移動されます。 この遷移を反映するように UI を設計します。

## こちらも参照ください

* [初めてのCopilot搭載アプリを構築する](/ja/copilot/how-tos/copilot-sdk/getting-started): セッションを設定してメッセージを送信する
* [カスタム エージェントとサブエージェント オーケストレーション](/ja/copilot/how-tos/copilot-sdk/features/custom-agents): スコープ付きツールを使用して特殊なエージェントを定義する
* [セッション フック](/ja/copilot/how-tos/copilot-sdk/hooks/hooks-overview): セッション ライフサイクル イベントに対応する
* [セッションの再開と永続化](/ja/copilot/how-tos/copilot-sdk/features/session-persistence): 再起動の間にセッションを再開する