Skip to main content
Skip to content

セッションの再開と永続化

このガイドでは、SDK のセッション永続化機能について説明します。作業を一時停止し、後で再開し、運用環境でセッションを管理する方法について説明します。

セッションのしくみ

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

図: 説明されたプロセスを示すフローチャート。

State何が起きるか
Createsession_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特定のツールを無効にする
providerBYOK 資格情報を再入力する (BYOK セッションに必要)
capi.autoTier永続化された自動ルーティング設定をオーバーライドする
capi.enableWebSocketResponses再開されたセッションの Responses API トランスポートを選択する
reasoningEffort推論作業レベルを調整する
streamingストリーミング応答を有効または無効にする
workingDirectory作業ディレクトリを変更する
configDir構成ディレクトリをオーバーライドする
mcpServersMCP サーバーの構成
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.jssession.setAutoTier("balance")session.setAutoTier(null)
Pythonsession.set_auto_tier("balance")session.set_auto_tier(None)
Gosession.SetAutoTier(ctx, &tier)session.SetAutoTier(ctx, nil)
.NETsession.SetAutoTierAsync(AutoTier.Balance)session.SetAutoTierAsync(null)
Rustsession.set_auto_tier(Some(AutoTier::Balance))session.set_auto_tier(None)
Javasession.setAutoTier(AutoTier.BALANCE)session.setAutoTier(null)

auto モデルとそのルーティング設定を 1 回の呼び出しで選択するには、代わりにモデル スイッチで階層をステージングします。 モデルが auto以外の場合、ランタイムはこのオプションを拒否します。

SDKスイッチを使用して階層をステージングするプロバイダーの既定のルーティングにリセットする
Node.jssetModel("auto", { autoTier: "balance" })setModel("auto", { autoTier: null })
Pythonset_model("auto", auto_tier="balance")set_model("auto", auto_tier=None)
GoSetModelOptions{AutoTier: &tier}SetModelOptions{ResetAutoTier: true}
.NETnew SetModelOptions { AutoTier = AutoTier.Balance }new SetModelOptions { ResetAutoTier = true }
RustSetModelOptions::default().with_auto_tier(AutoTier::Balance)SetModelOptions::default().with_reset_auto_tier()
Javanew SetModelOptions().setModel("auto").setAutoTier(AutoTier.BALANCE)new SetModelOptions().setModel("auto").setResetAutoTier(true)

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 を選択します。 これにより、監査とクリーンアップがはるかに簡単になります。

PatternExampleユースケース(事例)
❌abc123ランダム ID監査が難しく、所有権情報がない
✅user-{userId}-{taskId}user-alice-pr-review-42マルチユーザー アプリ
✅tenant-{tenantId}-{workflow}tenant-acme-onboardingマルチテナント SaaS
✅{userId}-{taskId}-{timestamp}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 には、慣用的な自動クリーンアップ パターンも用意されています。

LanguagePatternExample
TypeScriptSymbol.asyncDisposeawait using session = await client.createSession(config);
Pythonasync with コンテキストマネージャーasync with await client.create_session(on_permission_request=handler) as session:
C#IAsyncDisposableawait using var session = await client.CreateSessionAsync(config);
Godeferdefer 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`);
});

デプロイ パターン

最適な用途: 強力な分離、マルチテナント環境、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 キーが永続化されないキーをシークレット マネージャーに保存し、再開時に提供する
書き込み可能ストレージ~/.copilot/session-state/ 書き込み可能である必要がありますコンテナーに永続ボリュームをマウントする
セッションロックなし同じセッションへの同時アクセスは未定義ですアプリケーション レベルのロックまたはキューを実装する
ツールの状態が保持されないメモリ内ツールの状態が失われるステートレスであるか、独自の状態を保持するようにツールを設計する

同時実行アクセスの処理

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.resumeSession(sessionId)
BYOK の再開provider コンフィグを指定し直す
セッションの一覧client.listSessions(filter?)
アクティブなセッションから切断するsession.disconnect()—メモリ内リソースを解放します。ディスク上のセッション データは再開のために保持されます
セッションを完全に削除するclient.deleteSession(sessionId)—ディスクからすべてのセッション データを完全に削除します。再開できません
コンテナー化されたデプロイ永続的ストレージに ~/.copilot/session-state/ をマウントする

次のステップ