Overview
세션이 턴을 적극적으로 처리하는 경우 들어오는 메시지는 다음의 필드를 mode통해 MessageOptions 두 가지 모드 중 하나로 배달될 수 있습니다.
| 모드 | 동작 | 사용 사례 |
|---|---|---|
"immediate" (스티어링) | 현재 LLM 턴에 삽입됨 | "실제로 해당 파일을 만들지 마세요. 다른 방법을 사용하세요." |
"enqueue" (대기 중) | 현재 턴이 완료된 후에 대기열 지정 및 처리됨 | "이 후에 테스트도 수정합니다." |

메시지 출처
다른 에이전트에서 메시지를 전달할 때 선택적 원본을 설정합니다. 식별된 에이전트 소스는 agent-<id>로 직렬화됩니다. 런타임 기본값을 유지하기 위해 일반 사람이 보내는 원본을 설정하지 않은 상태로 둡니다. send API와 send-and-wait API는 모두 "enqueue" 및 "immediate" 전송이 포함된 소스를 지원합니다.
| SDK | 식별된 에이전트 원본 |
|---|---|
| Node.js/ TypeScript | source: "agent-sender-id" |
| Python | source=Agent |
| Go | Source: copilot.Message |
| .NET | Source = Message |
| Java | .set |
| 러스트 | .with_source(Message |
형식화된 API도 user 및 system를 지원합니다. 식별된 에이전트를 대체하는 것이 아니라 내부 애플리케이션 컨텍스트에 사용합니다 system . 에이전트 출처를 통해 런타임은 에이전트-메시지 조향 동작을 유지하면서 에이전트 입력을 사용자 권한 부여와 구분할 수 있습니다. 보낸 사람 ID는 메시지 텍스트가 아니라 신뢰할 수 있는 애플리케이션 메타데이터에서만 가져오세요.
소스는 원본을 식별합니다. 배달 모드는 긴급성을 요청합니다. 둘 다 받는 사람이 눈에 보이는 회신을 작성할 필요가 없으며, 원본 메시지는 청구 플래그를 설정하지 않습니다. 런타임은 기존 일정 규칙을 적용합니다. 형식화된 RPC API를 사용하는 Rust 호출자도 MessageSource를 rpc::SendRequest::with_source(...)에 전달할 수 있습니다.
성공적인 상위 수준 send 확인 응답은 메시지 ID를 반환하고, 수신자가 메시지를 소비했다는 것이 아니라 메시지가 수락되었음을 확인합니다. 회신이 표시되지 않으므로 수락된 메시지를 자동으로 다시 보내지 마세요. 도우미 메시지 없이도 유휴 이벤트 시 전송 후 대기를 완료할 수 있습니다.
경고
원격 백엔드가 소스를 처음부터 끝까지 반드시 보존하는 것은 아닙니다. 에이전트 세션은 원격 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 순서 지정은 예측 가능한 실행을 보장합니다. | |
| 현재 작업에 컨텍스트를 추가하려고 합니다. | ||
| 스티어링 | 에이전트가 그것을 현재의 추론에 통합합니다. | |
| 관련 없는 요청을 일괄 처리하려고 합니다. | ||
| 대기열 지정 | 각각은 깔끔한 문맥 속에서 자신만의 전체 턴을 갖습니다. |
스티어링 및 대기열 지정을 사용하여 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 참조
메시지 옵션
| 언어 | Field | Type | Default | 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를 디자인합니다.
참고하십시오
- Build your first Copilot-powered app: 세션 설정 및 메시지 보내기
- 사용자 정의 에이전트 및 하위 에이전트 오케스트레이션: 범위가 지정된 도구를 사용하여 특수 에이전트 정의
- 세션 후크: 세션 수명 주기 이벤트에 반응
- 세션 다시 시작 및 지속성: 다시 시작 후에도 세션 재개