セッションのしくみ
セッションを作成すると、Copilot CLI は会話履歴、ツールの状態、および計画コンテキストを保持します。 既定では、この状態はメモリ内に存在し、セッションが終了すると消えます。 永続化を有効にすると、再起動、コンテナーの移行、または異なるクライアント インスタンス間でセッションを再開できます。

| State | 何が起きるか |
|---|---|
| Create | session_id が割り当てられた |
| アクティブ | プロンプト、ツール呼び出し、応答を送信する |
| 一時停止 | ディスクに保存された状態 |
| Resume | ディスクから読み込まれた状態 |
クイック スタート: 再開可能なセッションの作成
再開可能なセッションの鍵は、独自の session_idを提供することです。 1 つがないと、SDK によってランダムな ID が生成され、後でセッションを再開することはできません。
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
// Create a session with a meaningful ID
const session = await client.createSession({
sessionId: "user-123-task-456",
model: "gpt-5.2-codex",
});
// Do some work...
await session.sendAndWait({ prompt: "Analyze my codebase" });
// Session state is automatically persisted
// You can safely close the client
Python
from copilot import CopilotClient
from copilot.session import PermissionHandler
client = CopilotClient()
await client.start()
# Create a session with a meaningful ID
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.2-codex", session_id="user-123-task-456")
# Do some work...
await session.send_and_wait("Analyze my codebase")
# Session state is automatically persisted
Go
ctx := context.Background()
client := copilot.NewClient(nil)
// Create a session with a meaningful ID
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
SessionID: "user-123-task-456",
Model: "gpt-5.2-codex",
})
// Do some work...
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Analyze my codebase"})
// Session state is automatically persisted
C# (.NET)
using GitHub.Copilot;
var client = new CopilotClient();
// Create a session with a meaningful ID
var session = await client.CreateSessionAsync(new SessionConfig
{
SessionId = "user-123-task-456",
Model = "gpt-5.2-codex",
});
// Do some work...
await session.SendAndWaitAsync(new MessageOptions { Prompt = "Analyze my codebase" });
// Session state is automatically persisted
セッションの再開
その後 (分、時間、または数日) に、中断した場所からセッションを再開できます。

TypeScript
// Resume from a different client instance (or after restart)
const session = await client.resumeSession("user-123-task-456");
// Continue where you left off
await session.sendAndWait({ prompt: "What did we discuss earlier?" });
Python
# Resume from a different client instance (or after restart)
session = await client.resume_session("user-123-task-456", on_permission_request=PermissionHandler.approve_all)
# Continue where you left off
await session.send_and_wait("What did we discuss earlier?")
Go
ctx := context.Background()
// Resume from a different client instance (or after restart)
session, _ := client.ResumeSession(ctx, "user-123-task-456", nil)
// Continue where you left off
session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What did we discuss earlier?"})
C# (.NET)
// Resume from a different client instance (or after restart)
var session = await client.ResumeSessionAsync("user-123-task-456");
// Continue where you left off
await session.SendAndWaitAsync(new MessageOptions { Prompt = "What did we discuss earlier?" });
再開オプション
セッションを再開するときに、必要に応じて多くの設定を再構成できます。 これは、モデルの変更、ツール構成の更新、または動作の変更が必要な場合に便利です。
| オプション | Description |
|---|---|
model | 再開されたセッションのモデルを変更する |
systemMessage | システム プロンプトをオーバーライドまたは拡張する |
availableTools | 使用できるツールを制限する |
excludedTools | 特定のツールを無効にする |
provider | BYOK 資格情報を再入力する (BYOK セッションに必要) |
capi.autoTier | 永続化された自動ルーティング設定をオーバーライドする |
capi.enable | 再開されたセッションの Responses API トランスポートを選択する |
reasoningEffort | 推論作業レベルを調整する |
streaming | ストリーミング応答を有効または無効にする |
workingDirectory | 作業ディレクトリを変更する |
configDir | 構成ディレクトリをオーバーライドする |
mcpServers | MCP サーバーの構成 |
customAgents | カスタム エージェントを構成する |
agent | 名前でカスタム エージェントを事前に選択する |
skillDirectories | スキルを読み込むためのディレクトリ |
disabledSkills | 無効にするスキル |
infiniteSessions | 無限セッション動作を構成する |
自動階層の永続化
model: "auto"では、オプションのcapi.autoTier設定で、自動ルーティング設定 (efficiency、balance、intelligence、またはfast) が選択されます。 Python では、 capi={"auto_tier": "balance"}を使用します。 この設定は V2 自動ルーティングに適用されます。V1 自動要求は変更されません。
fastはインテグレーター専用の待機時間プリセットであり、ファースト パーティのGitHub Copilot製品の基本設定ではありません。 SDK では、高速適格性の決定、クライアント ID の検査、既定としての選択、ランタイムがサポートしていない場合の別のレベルへのフォールバックは行われません。古いランタイムは、ネイティブ エラーを変更せずに返します。
ランタイムは選択したレベルを保持するため、アプリケーションは、すべての再開時に再送信する必要はありません。
- セッションの作成時に層を省略すると、ランタイムの既定のルーティング動作が使用されます。
- コールド再開では、永続化された層が復元されます。 明示的なティアを指定すると、新しいアクティブ化の際に復元される値が上書きされます。
- ランタイムに既に常駐しているセッションを再開する場合、層を省略すると現在の選択が維持され、同じ層を指定することは no-opです。 別のレベルを指定すると、再開が成功した後にランタイムが適用される安全なスイッチが要求されます。既にフライト中のターンを変更することはできません。
- 永続化された層がない古いセッションでは、既定のルーティング動作が保持されます。
階層の選択は、ライブ モデル切り替え操作ではありません。 SDK はユーザー設定を転送します。ランタイムは永続化と検証を所有します。
session.startイベントとsession.resume イベントは、オプションの data.autoTier フィールド (Python でdata.auto_tier) で選択したレベルを公開します。 階層が選択されていない場合、フィールドは省略されます。
セッション中の自動レベルの変更
setAutoTierを呼び出して、選択したモデルを変更せずにライブ セッションのルーティング設定を変更します。
null (Python None、Go nil) を渡して、プロバイダーの既定の自動ルーティングに戻ります。
const result = await session.setAutoTier("intelligence");
if (result.status === "pending") {
// Accepted, but not yet in effect.
}
ランタイムは設定をすぐに適用しません。 これは要求を記録し、後でユーザーが auto モデルを使用して有効なモデルをプロバイダーから正常に取得した場合にのみコミットします。 したがって、 pending 状態は、要求が有効になったのではなく、受け入れられたことを確認します。 最新の要求のみが存続します。ターンがまだ要求されていない以前の要求は、新しい要求によって置き換えられます。
次のイベントを通じて結果を確認します。
session.model_change設定がコミットされたとき。session.auto_tier_switch_failedそうでない場合は。 このイベントは一時的なイベントであるため、ランタイムは再開時に永続化したり再生したりすることはありません。 そのreasonフィールドは、policy_rejected、request_failed、setup_failed、またはunsupportedのいずれかであり、以前に有効だった優先設定はアクティブなままです。
また、セッションの model.getCurrent RPC メソッドを使用して、いつでも権限のある状態を読み取ることができます。このメソッドは、コミットされた autoTier、未請求の pendingAutoTier、進行中のアクティブ化によって現在要求されている activatingAutoTier を報告します。
| SDK | レベルを変更する | プロバイダーの既定のルーティングに戻る |
|---|---|---|
| Node.js | session.set | session.set |
| Python | session.set_auto_tier("balance") | session.set_auto_tier(None) |
| Go | session.Set | session.Set |
| .NET | session.Set | session.Set |
| Rust | session.set_auto_tier(Some(Auto | session.set_auto_tier(None) |
| Java | session.set | session.set |
auto モデルとそのルーティング設定を 1 回の呼び出しで選択するには、代わりにモデル スイッチで階層をステージングします。 モデルが auto以外の場合、ランタイムはこのオプションを拒否します。
| SDK | スイッチを使用して階層をステージングする | プロバイダーの既定のルーティングにリセットする |
|---|---|---|
| Node.js | setModel("auto", { autoTier: "balance" }) | setModel("auto", { autoTier: null }) |
| Python | set_model("auto", auto_tier="balance") | set_model("auto", auto_tier=None) |
| Go | Set | Set |
| .NET | new SetModelOptions { AutoTier = AutoTier.Balance } | new SetModelOptions { ResetAutoTier = true } |
| Rust | Set | Set |
| Java | new Set | new Set |
Node.js、Python、Rust は、3 つの状態をすべて 1 つの値で表します。null/Noneは省略された引数と区別できるため、Node.js とPython、また、AutoTierPreference::Resetは同じオプションの個別のバリアントであるため Rust です。 Go、.NET、Javaには、1 つの値で "reset" と "unset" を区別する方法がないため、個別のリセット フラグが設定されます。 両方を省略すると、常に "現在の優先設定をそのまま残す" という意味になります。
再開時の応答の転送
省略可能な capi.enableWebSocketResponses 設定では、CAPI Responses API のトランスポートが選択されます。 既定では trueされるため、選択したモデルが ws:/responses エンドポイントをアドバタイズするたびに WebSocket トランスポートが使用されます。
falseに設定すると、HTTP トランスポートにフォールバックします。 Python では、 capi={"enable_web_socket_responses": False}を使用します。
必要な場合は、再開呼び出しでそれを指定します。 プロキシの背後で WebSocket 接続が失敗したとき、および再開されたセッションが webSocket トランスポートに固有の 400 input item ID does not belong to this connectionを報告する場合に設定することをお勧めします。
const session = await client.resumeSession("user-123-task-456", {
capi: { enableWebSocketResponses: false },
});
これを false に設定すると、極性が逆の COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES 環境変数に相当します。
例: 再開時のモデルの変更
// Resume with a different model
const session = await client.resumeSession("user-123-task-456", {
model: "claude-sonnet-4", // Switch to a different model
reasoningEffort: "high", // Increase reasoning effort
});
再開されたセッションでの BYOK (Bring Your Own Key) の使用
独自の API キーを使用する場合は、再開時にプロバイダー構成を再指定する必要があります。 API キーは、セキュリティ上の理由からディスクに保持されることはありません。
// Original session with BYOK
const session = await client.createSession({
sessionId: "user-123-task-456",
model: "gpt-5.2-codex",
provider: {
type: "azure",
endpoint: "https://my-resource.openai.azure.com",
apiKey: process.env.AZURE_OPENAI_KEY,
deploymentId: "my-gpt-deployment",
},
});
// When resuming, you MUST re-provide the provider config
const resumed = await client.resumeSession("user-123-task-456", {
provider: {
type: "azure",
endpoint: "https://my-resource.openai.azure.com",
apiKey: process.env.AZURE_OPENAI_KEY, // Required again
deploymentId: "my-gpt-deployment",
},
});
何が永続化されますか?
セッション状態は ~/.copilot/session-state/{sessionId}/に保存されます。
~/.copilot/session-state/
└── user-123-task-456/
├── checkpoints/ # Conversation history snapshots
│ ├── 001.json # Initial state
│ ├── 002.json # After first interaction
│ └── ... # Incremental checkpoints
├── plan.md # Agent's planning state (if any)
└── files/ # Session artifacts
├── analysis.md # Files the agent created
└── notes.txt # Working documents
| データ | 永続化されるか | メモ |
|---|---|---|
| 会話の履歴 | ||
| ✅ はい | メッセージ全体のスレッド | |
| ツール呼び出しの結果 | ||
| ✅ はい | コンテキスト用にキャッシュ | |
| エージェントの計画状態 | ||
| ✅ はい | ||
plan.md ファイル | ||
| セッション成果物 | ||
| ✅ はい | ||
files/ ディレクトリ内 | ||
| プロバイダー/API キー | ||
| ❌ いいえ | セキュリティ: 再提供する必要があります | |
| メモリ内ツールの状態 | ||
| ❌ いいえ | ツールはステートレスにする必要がある |
セッション ID のベスト プラクティス
所有権と目的をエンコードするセッション ID を選択します。 これにより、監査とクリーンアップがはるかに簡単になります。
| Pattern | Example | ユースケース(事例) |
|---|---|---|
❌abc123 | ランダム ID | 監査が難しく、所有権情報がない |
✅user-{user | user-alice-pr-review-42 | マルチユーザー アプリ |
✅tenant-{tenant | tenant-acme-onboarding | マルチテナント SaaS |
✅{user | alice-deploy-1706932800 | 時間ベースのクリーンアップ |
構造化 ID の利点:
- 監査が簡単: "ユーザー alice のすべてのセッションを表示する"
- クリーンアップが簡単: "X より前のすべてのセッションを削除する"
- 自然アクセス制御: セッション ID からユーザー ID を解析する
例: セッション ID の生成
function createSessionId(userId: string, taskType: string): string {
const timestamp = Date.now();
return `${userId}-${taskType}-${timestamp}`;
}
const sessionId = createSessionId("alice", "code-review");
// → "alice-code-review-1706932800000"
import time
def create_session_id(user_id: str, task_type: str) -> str:
timestamp = int(time.time())
return f"{user_id}-{task_type}-{timestamp}"
session_id = create_session_id("alice", "code-review")
# → "alice-code-review-1706932800"
セッション ライフサイクルの管理
アクティブなセッションの一覧表示
// List all sessions
const sessions = await client.listSessions();
console.log(`Found ${sessions.length} sessions`);
for (const session of sessions) {
console.log(`- ${session.sessionId} (created: ${session.createdAt})`);
}
// Filter sessions by repository
const repoSessions = await client.listSessions({ repository: "owner/repo" });
古いセッションのクリーンアップ
async function cleanupExpiredSessions(maxAgeMs: number) {
const sessions = await client.listSessions();
const now = Date.now();
for (const session of sessions) {
const age = now - new Date(session.createdAt).getTime();
if (age > maxAgeMs) {
await client.deleteSession(session.sessionId);
console.log(`Deleted expired session: ${session.sessionId}`);
}
}
}
// Clean up sessions older than 24 hours
await cleanupExpiredSessions(24 * 60 * 60 * 1000);
セッションからの切断 (disconnect)
タスクが完了したら、タイムアウトを待機するのではなく、セッションから明示的に切断します。 これによりメモリ内リソースは解放されますが、 ディスク上のセッション データは保持されるため、セッションは後で再開できます。
try {
// Do work...
await session.sendAndWait({ prompt: "Complete the task" });
// Task complete — release in-memory resources (session can be resumed later)
await session.disconnect();
} catch (error) {
// Clean up even on error
await session.disconnect();
throw error;
}
各 SDK には、慣用的な自動クリーンアップ パターンも用意されています。
| Language | Pattern | Example |
|---|---|---|
| TypeScript | Symbol.async | await using session = await client.create |
| Python | async with コンテキストマネージャー | async with await client.create_session(on_ |
| C# | IAsyncDisposable | await using var session = await client.Create |
| Go | defer | defer session.Disconnect() |
メモ
destroy() は非推奨であり、代わりに disconnect() が推奨されます。 destroy()を使用する既存のコードは引き続き機能しますが、移行する必要があります。
セッションの完全な削除 (deleteSession)
セッションとそのすべてのデータ (会話履歴、計画状態、成果物) を完全に削除するには、 deleteSessionを使用します。 これは元に戻せません。削除後にセッションを再開 することはできません 。
// Permanently remove session data
await client.deleteSession("user-123-task-456");
disconnect()とdeleteSession():disconnect()はメモリ内リソースを解放しますが、後で再開できるようにセッション データをディスクに保持します。deleteSession()ディスク上のファイルを含め、すべてを完全に削除します。
自動整理: 待機タイムアウト
既定では、セッションには アイドル タイムアウトがなく 、明示的に切断または削除されるまで無期限にライブ状態になります。 必要に応じて、 CopilotClientOptions.sessionIdleTimeoutSecondsを使用してサーバー全体のアイドル タイムアウトを構成できます。
const client = new CopilotClient({
sessionIdleTimeoutSeconds: 30 * 60, // 30 minutes
});
タイムアウトが構成されると、その期間のアクティビティのないセッションが自動的にクリーンアップされます。 無効にするには、0 に設定するか、省略します。
メモ
このオプションは、SDK がランタイム プロセスを生成する場合にのみ適用されます。
cliUrl経由で既存のサーバーに接続する場合、サーバー独自のタイムアウト構成が適用されます。

アクティブな作業 (実行中のコマンド、バックグラウンド エージェント) を含むセッションは、タイムアウト設定に関係なく、常にアイドル 状態のクリーンアップから保護されます。
セッションの非アクティブ状態に対応するため、アイドル状態のイベントを監視します:
session.on("session.idle", (event) => {
console.log(`Session idle for ${event.idleDurationMs}ms`);
});
デプロイ パターン
パターン 1: ユーザーごとに 1 つの CLI サーバー (推奨)
最適な用途: 強力な分離、マルチテナント環境、Azure Dynamic Sessions。

**利点:**✅ 完全な分離 | ✅ シンプル なセキュリティ | ✅ 簡単なスケーリング
パターン 2: 共有 CLI サーバー (リソース効率)
ベスト:内部ツール、信頼できる環境、リソースに制約のあるセットアップ。

要件:
- ⚠️ ユーザーごとの一意のセッション ID
- ⚠️ アプリケーション レベルのアクセス制御
- ⚠️ 操作前のセッション ID 検証
// Application-level access control for shared CLI
async function resumeSessionWithAuth(
client: CopilotClient,
sessionId: string,
currentUserId: string
): Promise<Session> {
// Parse user from session ID
const [sessionUserId] = sessionId.split("-");
if (sessionUserId !== currentUserId) {
throw new Error("Access denied: session belongs to another user");
}
return client.resumeSession(sessionId);
}
Azure の動的セッション
コンテナーを再起動または移行できるサーバーレス/コンテナーデプロイの場合:
永続ストレージをマウントする
セッション状態ディレクトリを永続ストレージにマウントする必要があります。
# Azure Container Instance example
containers:
- name: copilot-agent
image: my-agent:latest
volumeMounts:
- name: session-storage
mountPath: /home/app/.copilot/session-state
volumes:
- name: session-storage
azureFile:
shareName: copilot-sessions
storageAccountName: myaccount

セッションはコンテナーの再起動後も存続します。
実行時間の長いワークフローの無限セッション
コンテキストの制限を超える可能性があるワークフローの場合は、自動圧縮で無限セッションを有効にします。
const session = await client.createSession({
sessionId: "long-workflow-123",
infiniteSessions: {
enabled: true,
backgroundCompactionThreshold: 0.80, // Start compaction at 80% context
bufferExhaustionThreshold: 0.95, // Block at 95% if needed
},
});
メモ
しきい値は、絶対トークン数ではなく、コンテキスト使用率の比率 (0.0 から 1.0) です。 詳細については、 SDK と CLI の互換性 を参照してください。
制限事項と考慮事項
| 制限事項 | Description | 緩和策 |
|---|---|---|
| BYOK の再認証 | API キーが永続化されない | キーをシークレット マネージャーに保存し、再開時に提供する |
| 書き込み可能ストレージ | ~/ 書き込み可能である必要があります | コンテナーに永続ボリュームをマウントする |
| セッションロックなし | 同じセッションへの同時アクセスは未定義です | アプリケーション レベルのロックまたはキューを実装する |
| ツールの状態が保持されない | メモリ内ツールの状態が失われる | ステートレスであるか、独自の状態を保持するようにツールを設計する |
同時実行アクセスの処理
SDK では、組み込みのセッション ロックは提供されません。 複数のクライアントが同じセッションにアクセスする可能性がある場合:
// Option 1: Application-level locking with Redis
import Redis from "ioredis";
const redis = new Redis();
async function withSessionLock<T>(
sessionId: string,
fn: () => Promise<T>
): Promise<T> {
const lockKey = `session-lock:${sessionId}`;
const acquired = await redis.set(lockKey, "locked", "NX", "EX", 300);
if (!acquired) {
throw new Error("Session is in use by another client");
}
try {
return await fn();
} finally {
await redis.del(lockKey);
}
}
// Usage
await withSessionLock("user-123-task-456", async () => {
const session = await client.resumeSession("user-123-task-456");
await session.sendAndWait({ prompt: "Continue the task" });
});
まとめ
| 特徴 | 使い方 |
|---|---|
| 再開可能なセッションを作成する | 独自のサービスを提供する sessionId |
| セッションを再開する | client.resume |
| BYOK の再開 | provider コンフィグを指定し直す |
| セッションの一覧 | client.list |
| アクティブなセッションから切断する | session.disconnect()—メモリ内リソースを解放します。ディスク上のセッション データは再開のために保持されます |
| セッションを完全に削除する | client.delete—ディスクからすべてのセッション データを完全に削除します。再開できません |
| コンテナー化されたデプロイ | 永続的ストレージに ~/ をマウントする |
次のステップ
- セッション フック - フックを使用してセッションの動作をカスタマイズする
- SDK と CLI の互換性 - SDK と CLI の機能の比較
- デバッグ ガイド - セッションの問題のトラブルシューティング