Overview
セッションがターンをアクティブに処理している場合、受信メッセージは、modeの MessageOptions フィールドを使用して、次の 2 つのモードのいずれかで配信できます。
| モード | Behavior | 利用シーン |
|---|---|---|
"immediate" (ステアリング) | LLMの現在のターンに挿入 | "実際には、そのファイルを作成しないでください。別のアプローチを使用してください" |
"enqueue" (キューイング) | 現在のターンが終了した 後 にキューに入れ、処理される | "この後、テストも修正します" |

ステアリング (イミディエイト モード)
ステアリングは、エージェントの現在のターンに直接挿入されるメッセージを送信します。 エージェントはリアルタイムでメッセージを確認し、それに応じて応答を調整します。ターンを中止せずにコースの修正に役立ちます。
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-4.1",
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",
});
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-4.1",
)
# 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()
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-4.1",
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)
}
}
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
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"
});
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.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();
}
ステアリングの内部構造
- メッセージがランタイムの
ImmediatePromptProcessorキューに追加されます - 現在のターン内の次の LLM 要求の前に、プロセッサはメッセージを会話に挿入します
- エージェントは、ステアリング メッセージを新しいユーザー メッセージとして認識し、その応答を調整します
- ステアリング メッセージが処理される前にターンが完了すると、次のターンの通常のキューに自動的に移動されます。
メモ
メッセージの転送は、現在のターン内で可能な限りベストを尽くします。 エージェントが既にツール呼び出しにコミットしている場合、その呼び出しが完了した後でも同じターン内でステアリングが有効になります。
キューイング (エンキュー モード)
メッセージをキューに登録すると、現在のターンが終了した後に順に処理されます。 キューに登録された各メッセージは、独自のフル ターンを開始します。 これは既定のモードです。 modeを省略すると、SDK は "enqueue" を使用します。
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-4.1",
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
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-4.1",
)
# 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()
package main
import (
"context"
copilot "github.com/github/copilot-sdk/go"
"github.com/github/copilot-sdk/go/rpc"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
client.Start(ctx)
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
return &rpc.PermissionDecisionApproveOnce{}, nil
},
})
session.Send(ctx, copilot.MessageOptions{
Prompt: "Set up the project structure",
})
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",
})
}
// 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
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
public static class QueueingExample
{
public static async Task Main()
{
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = (req, inv) =>
Task.FromResult(PermissionDecision.ApproveOnce()),
});
await session.SendAsync(new MessageOptions
{
Prompt = "Set up the project structure"
});
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"
});
}
}
// 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
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.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
}
キューイングの内部での動作
- メッセージはセッションの
itemQueueに追加されます。QueuedItem - 現在のターンが完了し、セッションがアイドル状態になると、
processQueuedItems()実行されます。 - 項目は FIFO 順にデキューされます。各メッセージはエージェントのターン全体をトリガーします
- ターンが終了したときにステアリング メッセージが保留中であった場合は、キューの先頭に移動されます。
- キューが空になるまで処理が続行され、セッションによってアイドル 状態のイベントが生成されます
ステアリングとキューイングの組み合わせ
両方のパターンを 1 つのセッションで一緒に使用できます。 ステアリングは、キューに入ったメッセージが自分のターンを待機している間、現在のターンに影響します。
const session = await client.createSession({
model: "gpt-4.1",
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",
});
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
model="gpt-4.1",
)
# 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",
)
ステアリングとキューイングの選択
| シナリオ | Pattern | なぜでしょうか |
|---|---|---|
| エージェントが間違った方向に進んでいる | ||
| ステアリング | 進行状況を失うことなく現在のターンをリダイレクトします | |
| あなたは、エージェントが行うべきことを思いついた。 | ||
| 待ち行列 | 現在の作業を中断しません。次に実行 | |
| エージェントが間違いを犯しようとしています | ||
| ステアリング | 間違いが犯される前に介入する | |
| 複数のタスクを連結する | ||
| 待ち行列 | FIFO の順序付けにより、予測可能な実行が保証されます | |
| 現在のタスクにコンテキストを追加する | ||
| ステアリング | エージェントが自身の現在の推論に組み込む | |
| 関連のない要求をバッチ処理する場合 | ||
| 待ち行列 | それぞれが明確なコンテキストで完全なターンを行います |
ステアリングとキューを使用して UI を構築する
両方のモードをサポートする対話型 UI を構築するためのパターンを次に示します。
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 呼び出しの前に挿入 | 新しいターンをすぐに開始する |
メモ
セッションがアイドル状態 (未処理)、両方のモードが同じように動作します。メッセージはすぐに新しいターンを開始します。
ベスト プラクティス
-
既定ではキューに登録されます。ほとんどのメッセージに対して
"enqueue"(または省略mode) を使用します。 これは予測可能であり、進行中の作業を中断するリスクはありません。 -
修正のためにステアリングを予約する - エージェントが間違ったことを積極的に行っており、先に進む前にリダイレクトする必要がある場合は、
"immediate"を使用します。 -
指示メッセージを簡潔に保つ - エージェントはコースの修正をすばやく理解する必要があります。 長く複雑なステアリング メッセージは、現在のコンテキストを混乱させる可能性があります。
-
オーバーステアを行わない - 複数の迅速なステアリング メッセージによってターン品質が低下する可能性があります。 方向を大幅に変更する必要がある場合は、ターンを中止して新たに開始することを検討してください。
-
UI にキューの状態を表示する - ユーザーが保留中の内容を把握できるように、キューに登録されたメッセージの数を表示します。 アイドル 状態のイベントをリッスンして、表示をクリアします。
-
ステアリングからキューへのフォールバックを処理する - ターンが完了した後にステアリング メッセージが到着すると、自動的にキューに移動されます。 この遷移を反映するように UI を設計します。
こちらも参照ください
- 初めてのCopilot搭載アプリを構築する: セッションを設定してメッセージを送信する
- カスタム エージェントとサブエージェント オーケストレーション: スコープ付きツールを使用して特殊なエージェントを定義する
- セッション フック: セッション ライフサイクル イベントに対応する
- セッションの再開と永続化: 再起動の間にセッションを再開する