Overview
Lorsqu’une session traite activement un tour, les messages entrants peuvent être remis dans l’un des deux modes via le mode champ sur MessageOptions:
| Mode | Behavior | Cas d’utilisation |
|---|---|---|
"immediate" (direction) | Injecté dans le tour LLM actuel | « En fait, ne créez pas ce fichier , utilisez une approche différente » |
"enqueue" (mise en file d’attente) | Mis en file d’attente et traité une fois le tour actuel terminé | « Après cela, corrigez également les tests » |

Direction (mode immédiat)
Le pilotage envoie un message qui est directement injecté dans le tour en cours de l'agent. Il est utile pour corriger la trajectoire sans interrompre l'échange ; l'agent voit le message en temps réel et ajuste sa réponse en conséquence.
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();
}
Fonctionnement interne de la direction
- Le message est ajouté à la file d’attente du
ImmediatePromptProcessorruntime - Avant la requête LLM suivante au sein du tour actuel, le processeur injecte le message dans la conversation
- L’agent voit le message de direction en tant que nouveau message utilisateur et ajuste sa réponse
- Si le tour se termine avant le traitement du message de direction, il est automatiquement déplacé vers la file d’attente normale pour le prochain tour
Remarque
Les messages de direction sont les meilleurs efforts au sein du tour actuel. Si l'agent s’est déjà engagé dans un appel logiciel, l'orientation prend effet une fois cet appel terminé, mais toujours au sein du même cycle.
Mise en file d’attente (mode file d’attente)
La mise en file d'attente tamponne les messages pour qu'ils soient traités séquentiellement une fois le tour actuel terminé. Chaque message mis en file d’attente démarre son propre tour complet. Il s’agit du mode par défaut : si vous omettez mode, le Kit de développement logiciel (SDK) utilise "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
}
Fonctionnement interne de la mise en file d’attente
- Le message est ajouté à la session
itemQueueen tant queQueuedItem - Lorsque le tour actuel se termine et que la session devient inactive,
processQueuedItems()s’exécute - Les éléments sont mis en file d’attente dans l’ordre FIFO : chaque message déclenche un tour agentique complet
- Si un message de direction était en attente lorsque le tour s’est terminé, il est déplacé vers l’avant de la file d’attente
- Le traitement se poursuit jusqu’à ce que la file d’attente soit vide, puis la session émet un événement inactif
Combinaison du pilotage et de la file
Vous pouvez utiliser les deux modèles ensemble dans une seule session. La direction affecte le tour actuel pendant que les messages mis en file d’attente attendent leur propre tour :
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",
)
Choix entre la direction et la file d’attente
| Scénario | Pattern | Pourquoi |
|---|---|---|
| L’agent prend le mauvais chemin | ||
| Direction | Redirige le tour actuel sans perdre de progression | |
| Vous avez pensé à quelque chose que l’agent devrait également faire | ||
| Mise en file d’attente | Ne perturbe pas le travail actuel ; s’exécute par la suite | |
| L’agent est sur le point de faire une erreur | ||
| Direction | Intervient avant que l’erreur ne soit commise | |
| Vous souhaitez chaîner plusieurs tâches | ||
| Mise en file d’attente | L’ordre FIFO garantit l’exécution prévisible | |
| Vous souhaitez ajouter un contexte à la tâche actuelle | ||
| Direction | Agent l’intègre dans son raisonnement actuel | |
| Vous souhaitez traiter des demandes non liées en lot | ||
| Mise en file d’attente | Chacun obtient son propre tour complet avec un contexte propre |
Création d’une interface utilisateur avec pilotage et mise en file d’attente
Voici un modèle permettant de créer une interface utilisateur interactive qui prend en charge les deux modes :
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
}
}
Référence d’API
Options de message
| Language | Champ | Catégorie | Par défaut | Description |
|---|---|---|---|---|
| Node.js | mode | "enqueue" | "immediate" | "enqueue" | Mode de remise des messages |
| Python | mode | Literal["enqueue", "immediate"] | "enqueue" | Mode de remise des messages |
| Allez | Mode | string | "enqueue" | Mode de remise des messages |
| .NET | Mode | string? | "enqueue" | Mode de remise des messages |
Modes de livraison
| Mode | Résultat | Pendant le tour actif | Pendant l’inactivité |
|---|---|---|---|
"enqueue" | File d’attente pour le prochain tour | Attente dans la file FIFO | Démarre immédiatement un nouveau tour |
"immediate" | Injecter dans le tour en cours | Injecté avant l’appel LLM suivant | Démarre immédiatement un nouveau tour |
Remarque
Lorsque la session est inactive (pas de traitement), les deux modes se comportent de la même façon : le message démarre immédiatement un nouveau tour.
Bonnes pratiques
-
Mise en file d’attente par défaut : utilisez
"enqueue"(ou omettezmode) pour la plupart des messages. Il est prévisible et ne risque pas de perturber les travaux en cours. -
Réservez la direction pour les corrections : utilisez
"immediate"quand l’agent fait activement la mauvaise chose et que vous devez le rediriger avant d’aller plus loin. -
Gardez les messages clairs et concis : l’agent doit rapidement comprendre la correction du cap. Les messages de direction longs et complexes peuvent confondre le contexte actuel.
-
Évitez de surdiriger : plusieurs messages de direction rapides peuvent dégrader la qualité du virage. Si vous devez changer de direction de manière significative, envisagez d’arrêter le tour et de commencer frais.
-
Afficher l’état de la file d’attente dans votre interface utilisateur : affichez le nombre de messages mis en file d’attente afin que les utilisateurs sachent ce qui est en attente. Surveillez les événements inactifs pour effacer l'affichage.
-
Gérer la bascule de la direction vers la file d'attente : si un message de pilotage arrive une fois le processus terminé, il est automatiquement déplacé vers la file d'attente. Concevez votre interface utilisateur pour refléter cette transition.
Voir aussi
- Créez votre première application avec Copilot : Configurer une session et envoyer des messages
- Agents personnalisés et orchestration de sous-agents : Définir des agents spécialisés avec des outils délimités
- Points d'ancrage de session: Réagir aux événements du cycle de vie de la session
- Reprise de session et persistance: Reprendre les sessions après redémarrage