Skip to main content
会話とセッション

外部セッションストレージ

qodercli はデフォルトで、実行中のマシンにセッション履歴を保存します。複数インスタンス、コンテナ、Serverless 環境では、次のリクエストが別のマシンに届き、以前のセッションを参照できないことがあります。 外部セッションストレージは、アプリケーションが管理するストレージに各セッションのミラーを保持します。ミラーは追加のコピーです。qodercli は引き続きセッションをローカルに書き込み、どのホストからでも後で session ID を使って同じセッションを継続できます。 次のような場合に外部ストレージが適しています。
  • リクエストが複数のサービスインスタンス間を移動する
  • ローカルディスクが一時的、または信頼できない
  • アクセス制御、暗号化、バックアップ、保持期間をアプリケーション側で管理したい
常に同じマシンで実行するアプリケーションでは、通常はローカル保存だけで十分です。

仕組み

最初のリクエスト
  query()
    -> qodercli がセッションをローカルに書き込む
    -> SDK が新しいセッション entry を外部ストレージへミラーする

後続のリクエスト、別のホストの場合もある
  query(resume=session_id 付き)
    -> SDK が外部ストレージからセッション履歴を読み込む
    -> qodercli がセッションを継続する
この設計から 2 つの性質が生まれます。
  • 書き込みはベストエフォート。 SDK はバックグラウンドで entry をミラーします。書き込みの失敗は通知されますが、進行中の会話を止めることはありません。詳しくは運用を参照してください。
  • entry は不透明。 ストアは SDK が定義した transcript entry をそのまま保存して返します。アプリケーションが qodercli のローカルファイル形式を理解する必要はありません。

クイックスタート

InMemorySessionStore はデータを現在のプロセス内だけに保持します。共有バックエンドへ接続する前に、これで、query がストアへ保存しストアから再開できるという配線を検証してください。プロセス終了時にデータが失われるため、ホストをまたいだ再開は示せません。それには実際のストアを実装してください。ストアの実装を参照してください。
import {
  InMemorySessionStore,
  qodercliAuth,
  query,
} from '@qoder-ai/qoder-agent-sdk';

const store = new InMemorySessionStore();
const options = {
  auth: qodercliAuth(),
  cwd: '/path/to/project',
  sessionStore: store,
};

// 最初の query:実行して session ID を記録する。
let sessionId: string | undefined;
for await (const message of query({
  prompt: '数字の 42 を覚えておいてください。',
  options,
})) {
  if (message.type === 'result') sessionId = message.session_id;
}
if (!sessionId) throw new Error('最初の query が session ID を返しませんでした。');

// 後続の query:ストアから再開すると、モデルは以前の文脈を思い出す。
for await (const message of query({
  prompt: '覚えるように頼んだ数字は何でしたか?',
  options: { ...options, resume: sessionId },
})) {
  if (message.type === 'result') console.log(message.result); // -> "42"
}
SDK が同じプロジェクトとして認識できるように、すべてのホストで同じ cwd を使用してください。

アプリケーションでの使用

セッションのミラーリングや復元が必要なクエリごとに、options で store を渡し(TypeScript は sessionStore、Python は session_store)、セッションの指定方法を選びます:
  • session ID が既知 —— resume を渡します。
  • プロジェクトの直近セッション —— continue: true(TypeScript)/ continue_conversation=True(Python)を渡します。store がセッション一覧メソッドを実装している必要があります。

セッションの管理

セッション管理関数はローカルファイルではなく store に対して操作します。TypeScript では sessionStore オプションで store を渡し、Python では各関数の第1引数に store を渡します:
import {
  listSessions,
  getSessionInfo,
  getSessionMessages,
  listSubagents,
  getSubagentMessages,
  renameSession,
  tagSession,
  forkSession,
  deleteSession,
} from '@qoder-ai/qoder-agent-sdk';

const project = { dir: '/path/to/project', sessionStore: store };
const sessions = await listSessions(project);
const messages = await getSessionMessages(sessionId, project);
await renameSession(sessionId, 'Investigation #1', project);
await deleteSession(sessionId, project);
セッション一覧の関数は store にセッション一覧メソッドの実装を要求します。サブエージェント一覧は listSubkeys / list_subkeys を、セッション削除は delete を要求します。listSubkeys / list_subkeys が実装されていれば、サブエージェントメッセージの取得関数もそれを使います。 既存のローカルセッションを store にコピーするには——たとえば外部ストレージ未使用だったマシンの移行時——インポート関数を使います。session ID が先、store が後である点に注意してください:
import { importSessionToStore } from '@qoder-ai/qoder-agent-sdk';

await importSessionToStore(sessionId, store, { dir: '/path/to/project' });

ストアの実装

SDK は本番利用可能なストレージ実装を提供しません。アプリケーションが選んだ共有ストレージに対して SessionStore インターフェース(TypeScript)/ プロトコル(Python)を実装します。Redis と PostgreSQL の実行可能なリファレンス実装が具体的な使い方を示しています。TypeScript サンプル または Python サンプル を参照してください。これらは出発点であり、本番利用可能な実装ではありません。
type SessionKey = {
  projectKey: string; // cwd から導出。プロジェクトを識別する
  sessionId: string; // セッション UUID
  subpath?: string; // サブエージェント transcript のみ。例 "subagents/agent-<id>"
};

type SessionStoreEntry = {
  type: string;
  uuid?: string;
  timestamp?: string;
  [key: string]: unknown; // 不透明な transcript 行——そのまま保存して返す
};

interface SessionStore {
  // 必須
  append(key: SessionKey, entries: SessionStoreEntry[]): Promise<void>;
  load(key: SessionKey): Promise<SessionStoreEntry[] | null>;
  // 任意——必要な機能だけ実装する
  listSessions?(
    projectKey: string,
  ): Promise<Array<{ sessionId: string; mtime: number }>>;
  delete?(key: SessionKey): Promise<void>;
  listSubkeys?(key: Omit<SessionKey, 'subpath'>): Promise<string[]>;
}
SessionKey は1つの transcript を識別します。メインセッションには subpath がありません。各サブエージェントの transcript は同じ project key と session ID を再利用し、異なる subpath を持ちます。key と entry は不透明データとして扱い——そのまま保存してそのまま返し、メッセージ内容を解析しないでください。 必須の 2 メソッドが保存と復元を提供します。各任意メソッドが 1 つの機能を有効にします。
メソッド有効になる機能
appendloadセッションのミラーと ID による再開 —— 必須
listSessions / list_sessions直近セッションの再開。保存済みセッションの一覧
deletestore からのセッション削除
listSubkeys / list_subkeysサブエージェント transcript の完全な復元と閲覧

実装チェックリスト

  • key ごとに追加順を保持しload では履歴全体を返す。再生は順序に依存します。
  • key を分離する。 ある key の entry を別の key の下で返さないこと。
  • append を冪等にする。 SDK は失敗した書き込みを同じ entry で再試行する場合があるため、再試行で履歴が重複してはいけません。
  • セッション一覧の mtime は Unix ミリ秒タイムスタンプを返し、メインセッション(subpath を持たないもの)のみを返します。
  • 削除をカスケードする。 メインセッションの削除時は、そのサブエージェント transcript も削除します。
  • listSubkeys / list_subkeys は相対識別子のみを返します——絶対パスや . / .. を含むパスは返さないでください。これらはストレージキーになり、パストラバーサル片があると transcript が名前空間の外に出てしまいます。
  • 同時書き込みを直列化する。 同じセッションを複数プロセスが書き込む可能性がある場合は、ストレージ層で直列化します。
SDK が提供する conformance テストでこれらの汎用動作を検証し(TypeScript は SDK リポジトリに SessionStore conformance テストを同梱、Python は qoder_agent_sdk.testingrun_session_store_conformance を提供)、バックエンド固有の並行・リトライテストを追加してください。接続管理、権限、暗号化、バックアップ、移行、データ保持は引き続きアプリケーションの責務です。

運用

失敗時の処理。 外部書き込みの失敗は進行中の会話を中断しません。最後のリトライが失敗すると、SDK はミラーエラーメッセージ(TypeScript は system/mirror_error、Python は SDKMirrorErrorMessage)を発行します。ミラーの完全性が重要な場合は監視してください——書き込みが失敗しても会話は成功裏に完了することがあります。 チューニング。
  • 外部読み取りはデフォルトで最大60秒待機します。loadTimeoutMs / load_timeout_ms で調整します。
  • flush 戦略(sessionStoreFlush / session_store_flush)のデフォルトは batched です。eager にすると entry をより早くミラーしますが、ストレージリクエストが増えます。
制約。
  • 明示的に指定したセッションが store にない場合、SDK は同じ ID のローカルセッションにフォールバックできます。
  • store はファイル checkpoint、カスタム transport、実験的な Cloud Agent runtime と併用できません(TypeScript では persistSession: false とも併用不可)。TypeScript では組み込みの Process / Worker transport で、Python では組み込みの subprocess transport でサポートされます。
  • store が保持するのはセッション履歴だけです。認証状態、アプリケーション設定、ファイル checkpoint、保持ポリシーは保持しません。