Обзор
Когда сессия активно обрабатывает ход, входящие сообщения могут доставляться в одном из двух режимов через mode поле на MessageOptions:
| Режим | Behavior | Сценарий использования |
|---|---|---|
"immediate" (рулевое управление) | Вводится в текущий ход LLM | «На самом деле, не создавай этот файл — используй другой подход» |
"enqueue" (очередь) | Поставлен в очередь и обработан после окончания текущего хода | «После этого тоже починьте тесты» |

Происхождение сообщений
Задайте необязательный источник при пересылке сообщения из другого агента. Определяемый источник агента сериализуется как agent-<id>. Оставьте исходный набор неустановленным для обычных пользователей, чтобы сохранить значения по умолчанию среды выполнения. Интерфейсы API отправки и ожидания поддерживают источник "enqueue" и "immediate" доставку.
| SDK | Идентифицированный источник агента |
|---|---|
| Node.js / TypeScript | source: "agent-sender-id" |
| Python | source=Agent |
| Go | Source: copilot.Message |
| .NET | Source = Message |
| Java | .set |
| Rust | .with_source(Message |
Типизированные API также поддерживают user и system. Используется system для внутреннего контекста приложения, а не в качестве замены определяемого агента. Проверка подлинности агента позволяет среде выполнения различать входные данные агента от авторизации человека при сохранении поведения управления сообщениями агента. Наследуйте идентификатор отправителя из метаданных доверенного приложения, никогда не из текста сообщения.
Источник определяет источник. Срочность запросов в режиме доставки. Ни для того, чтобы получатель не создает видимый ответ, и источник не устанавливает флаги выставления счетов. Среда выполнения применяет существующие правила планирования. Вызывающие средства Rust, использующие типизированный API RPC, также могут передаваться MessageSource в rpc::SendRequest::with_source(...).
Успешное подтверждение высокого уровня send возвращает идентификатор сообщения и подтверждает принятие, а не то, что получатель использовал сообщение. Не перенаправлять принятое сообщение автоматически, так как ответ не отображается. Отправка и ожидание может завершиться бездействующего события без сообщения помощника.
Предупреждение
Удаленные серверные серверы не обязательно сохраняют конечный конечный источник. Сеанс агента может включать источник в локальное эхо без его переноса в удаленный HTTP-запрос. Локальное исходное событие не доказывает, что удаленный рабочий сотрудник получил то же доказательство.
Рулевое управление (режим мгновенности)
Управление посылает сообщение, которое вводится непосредственно в текущий оборот агента. Агент видит сообщение в реальном времени и корректирует его ответ — полезно для коррекции курса без прерывания хода.
Языки кода navigation
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",
});
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()
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)
}
}
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"
});
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();
}
Как работает внутреннее управление
- Сообщение добавляется в очередь
ImmediatePromptProcessorвремени выполнения - Перед следующим запросом LLM в текущем ходу процессор вводит сообщение в разговор
- Агент воспринимает сообщение управления как сообщение нового пользователя и корректирует его ответ
- Если поворот завершается до обработки сообщения о рулевом управлении, он автоматически переводится в обычную очередь на следующий ход
Примечание.
Сообщения управления — это лучшее усилие в текущем ходу. Если агент уже выполнил вызов инструмента, управление вступает в силу после завершения этого вызова, но всё ещё в пределах того же хода.
Очередь (режим очереди)
Очередь буферизирует сообщения для последовательной обработки после завершения текущего хода. Каждое очередное сообщение начинает свой полный ход. Это режим по умолчанию — если пропустить mode, SDK использует "enqueue".
Языки кода navigation
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
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()
// 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
// 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.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
}
Как работает внутренняя очередь
- Сообщение добавляется в сессии
itemQueueв видеQueuedItem - Когда текущий ход заканчивается и сессия становится бездействующей,
processQueuedItems()запускается - Элементы выставляются из очереди в порядке FIFO — каждое сообщение запускает полный агентный ход
- Если на момент окончания поворота сообщение о рулевом управлении было ожидаемым, оно перемещается в начало очереди
- Обработка продолжается до тех пор, пока очередь не опустеет, после чего сессия выпускает событие простоя
Совмещение рулевого управления и очереди
Вы можете использовать оба узора вместе за одну сессию. Управление влияет на текущий ход, пока сообщения в очереди ждут своих ходов:
Языки кода navigation
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",
});
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",
)
Выбор между рулём и очередью
| Scenario | Pattern | Почему |
|---|---|---|
| Агент идёт по неправильному пути | ||
| Рулевое управление | Перенаправляет текущий ход без потери прогресса | |
| Вы придумали, что агент тоже должен сделать | ||
| Очередь | Не мешает текущей работе; Следующие сезоны | |
| Агент вот-вот совершит ошибку | ||
| Рулевое управление | Вмешивается до совершения ошибки | |
| Нужно объединять несколько задач в цепочку | ||
| Очередь | Порядок FIFO обеспечивает предсказуемое выполнение | |
| Вы хотите добавить контекст к текущей задаче | ||
| Рулевое управление | Агент включает это в свою текущую логику | |
| Вы хотите делать пакетные несвязанные запросы | ||
| Очередь | Каждый из них получает свой полный ход с чистым контекстом |
Создание интерфейса с управлением и очередями
Вот схема создания интерактивного интерфейса, поддерживающего оба режима:
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
MessageOptions
| Язык | Поле | Тип | По умолчанию | 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"тогда, когда агент активно делает что-то не так, и вам нужно перенаправить его, прежде чем он пойдёт дальше. -
Держите сообщения управления лаконичными — агенту нужно быстро понять корректировку курса. Длинные, сложные сообщения управления могут запутать текущий контекст.
-
Не переусердствуйте — несколько сообщений о быстром рулевом управлении могут ухудшить качество поворота. Если нужно сильно изменить направление, подумайте о том, чтобы прервать поворот и начать с чистого листа.
-
Покажите состояние очереди в интерфейсе — откажите количество поставленных в очередь сообщений, чтобы пользователи знали, что ожидается. Прислушивайтесь к событиям простоя, чтобы очистить дисплей.
-
Обрабатывайте запасной вариант руления в очередь — если после завершения поворота приходит сообщение о рулевом управлении, оно автоматически переносится в очередь. Спроектируйте свой интерфейс так, чтобы отражать этот переход.
См. также
- Build your first Copilot-powered app: Настройте сессию и отправьте сообщения
- Пользовательские агенты и оркестровка субагентов: Определите специализированные агенты с помощью инструментов с ограниченной областью действия
- Сессионные хуки: Реагировать на события жизненного цикла сессии
- Возобновление сессии и сохранение: Возобновить сессии после перезапуска