TypeScript Agent SDK V2廃止の理由と移行先
TypeScript Agent SDK 0.3.142でV2セッションAPIが廃止されました。何ができなくなったのか、query()への書き換え方を実装コードで示します。
TypeScript Agent SDK V2セッションAPIとは何だったか
TypeScript Agent SDK 0.3.142は、unstable_v2_createSession・unstable_v2_resumeSession・unstable_v2_promptと、SDKSession・SDKSessionOptionsの各型を削除しました。パッケージのバージョンは0.2.x系から0.3.142へ直接ジャンプしており、0.2.xがV2インターフェースを含む最後のバージョンです。廃止後にV2のコードをそのまま動かしたい場合は、npm install @anthropic-ai/claude-agent-sdk@0.2でメジャー・マイナーを指定してインストールする以外に手段がありません。
V2は、非同期ジェネレータとyieldの手動調整を不要にする実験的なセッションAPIでした。Agent SDK入門で扱っているとおり、V1のquery()が1本の非同期ジェネレータで入出力の両方を扱うのに対し、V2はsession.send()でメッセージを送りsession.stream()で応答を受け取る、ターンごとに独立したsend/streamサイクルへAPI面を分割しました。会話の継続はcreateSession()、以前の会話への復帰はresumeSession()という、3つの概念だけで完結する設計です。
V2が解こうとしていた課題は具体的です。V1でマルチターンの会話を組むには、会話全体を1つの非同期ジェネレータ関数として先に書き、その中で複数回yieldする必要があります。次に送るメッセージの内容が、外部からのユーザー入力や前のターンの応答結果に依存する場合、そのジェネレータの中でawaitしながらyieldのタイミングを自分で調整しなければなりません。V2は、この調整をsend()とstream()という2つの独立した呼び出しに分解し、ターンごとに好きなタイミングで送信できるようにすることで、この手動調整を消そうとしていました。
V2の主要概念とV1での対応
| V2の概念 | 役割 | V1での対応 |
|---|---|---|
unstable_v2_createSession() | 役割セッションを開始 | V1での対応query()(初回呼び出し) |
unstable_v2_resumeSession(id, opts) | 役割保存済みセッションを継続 | V1での対応query({ options: { resume: id } }) |
unstable_v2_prompt(text, opts) | 役割単発プロンプトの簡易実行 | V1での対応query() + resultサブタイプの判定 |
session.send(msg) | 役割メッセージを送信 | V1での対応入力ジェネレータのyield |
session.stream() | 役割応答を受信 | V1での対応query()のfor awaitループそのもの |
session.close() / await using | 役割セッションを終了 | V1での対応対応なし(ジェネレータの消費完了が終了) |
forkSessionオプション | 役割元のセッションを分岐 | V1での対応V1のみ。V2には最後まで実装されなかった |
なぜ廃止されたのか
公式ドキュメントは廃止の経緯そのものを説明していません。ただしV2自身のドキュメントに残された記述から、廃止に至った理由は読み取れます。
第一に、V2は関数名がすべてunstable_接頭辞のまま最後まで残りました。安定版への昇格を一度も経ないまま削除された、という事実がまずあります。
第二に、V2のドキュメントは「Feature availability」という節で、V1にあってV2にない機能を自ら列挙していました。セッションフォーク(forkSessionオプション)と高度なストリーミング入力パターンの2つです。セッションフォークは、Agent SDKのセッション管理で扱っているとおり、元のセッションを壊さず別のアプローチを試すための中核機能で、SDK overviewの機能一覧でもSessionsの説明に明記されている主要な能力です。send/stream方式に分割したことでAPIの見通しは良くなった一方、この主要機能を最後まで実装できなかった事実は、V2が実験の域を出て安定運用に耐える完成度まで届かなかったことを示しています。
移行先として指定されたのは、V2ではなくV1のquery()です。AsyncIterable<SDKUserMessage>を渡せば複数ターンの会話を組め、options.resumeを渡せば保存済みのセッションを継続できます。2つの独立したセッションAPIを並行して保守するより、機能が揃っている側へ一本化する判断は自然です。
この一件は、SDKの実験的な機能を本番コードへ組み込む際のリスクを具体的に示す事例でもあります。パッケージのバージョン番号だけを見ると、0.2.x系から0.3.142への更新はメジャーバージョンをまたがない普通のマイナー更新に見えます。実際にはこの1回の更新で、公開APIが3つとエクスポート型が2つ、まとめて削除されました。接頭辞に実験段階を示す語を持つ機能は、正式な廃止予告の期間を経ずに次のリリースで姿を消すことがあります。バージョン番号の見た目だけで破壊的変更の有無を判断せず、そうした機能を使うときはリリースの変更履歴を個別に追う運用が要ります。
V1のquery()にどう書き直すか
V2の3つの使い方は、それぞれV1のquery()に1対1で対応します。
単発プロンプト
V2のunstable_v2_prompt()は、V1ではquery()を呼び、resultサブタイプのメッセージだけを拾う形になります。
// V2(廃止)
import { unstable_v2_prompt } from "@anthropic-ai/claude-agent-sdk";
const result = await unstable_v2_prompt("What is 2 + 2?", {
model: "claude-opus-4-7"
});
if (result.subtype === "success") {
console.log(result.result);
}// V1(移行先)
import { query } from "@anthropic-ai/claude-agent-sdk";
const q = query({
prompt: "What is 2 + 2?",
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "result" && msg.subtype === "success") {
console.log(msg.result);
}
}単一セッションでの応答受信
V2のcreateSession() + send() + stream()は、V1ではquery()に文字列を渡すだけの1本のループになります。
// V1
import { query } from "@anthropic-ai/claude-agent-sdk";
const q = query({
prompt: "Hello!",
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}マルチターンの会話
V2はsession.send()を呼ぶたびに新しいターンを送れましたが、V1では入力そのものを非同期イテラブルにしてpromptへ渡します。V2のようにターンの間で任意のロジックを挟みたい場合は、ジェネレータ関数の中でyieldするタイミングを自分で制御することになります。
import { query } from "@anthropic-ai/claude-agent-sdk";
async function* createInputStream() {
yield {
type: "user",
session_id: "",
message: { role: "user", content: [{ type: "text", text: "What is 5 + 3?" }] },
parent_tool_use_id: null
};
yield {
type: "user",
session_id: "",
message: { role: "user", content: [{ type: "text", text: "Multiply by 2" }] },
parent_tool_use_id: null
};
}
const q = query({
prompt: createInputStream(),
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}V2の「送信してから処理を挟み、また送信する」という書き方に慣れていた場合、V1へ移行すると入力全体を1つのジェネレータへ事前に組み込む発想へ切り替える必要があります。ターンごとに外部のイベント(ユーザー入力やAPI応答)を待ってから次のメッセージを組み立てる設計では、このジェネレータの中でawaitを使い外部イベントを待機する形に書き直します。
セッションの再開
V2のresumeSession(sessionId, options)は、V1ではquery()のoptions.resumeにセッションIDを渡す形になります。セッションIDの取得方法自体はV2と変わらず、受信したメッセージのsession_idフィールドから読みます。
// V1
const resumedQuery = query({
prompt: "What number did I ask you to remember?",
options: {
model: "claude-opus-4-7",
resume: sessionId
}
});
for await (const msg of resumedQuery) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}resume・continue・forkの3方式の使い分けと、Python版での実装差分は前段で触れたセッション管理の記事にまとめています。V2にはなかったforkは、V1だけの機能です。
移行時に見落としやすい点
V2のawait using session = ...によるスコープベースの自動クローズは、V1のquery()には対応する概念がありません。V1では非同期ジェネレータが最後まで消費されるか、呼び出し側が明示的にループを抜けることでリソースが解放されます。V2のコードでsession.close()を呼んでいた箇所は、V1移行時にそのまま持ち込む先がないため、単に削除して問題ありません。
もう1つ意識しておきたいのが、セッションという概念の持ち方そのものの違いです。V2ではSDKSessionという手元のオブジェクトがセッションの実体を表し、sessionIdはそのオブジェクトの1属性でした。V1にはセッションを表すクライアント側のオブジェクトは存在せず、セッションはsession_idという文字列だけで表現されます。会話を続けたければ、その文字列を次のquery()呼び出しのoptions.resumeに渡すだけです。手元にオブジェクトを保持し続ける設計から、IDだけを保存しておいて必要なときにquery()へ渡す設計へと、状態の持ち方の発想を切り替える必要があります。セッションIDは初回のquery()が返す最初のメッセージのsession_idフィールドから取得する点はV1・V2で共通です。オブジェクトを介さずID文字列だけでやり取りする設計は、プロセスをまたいでセッションを引き継ぐ用途とも相性がよく、Webアプリのようにリクエストごとにプロセスが変わる環境でも扱いやすくなります。
V2のAPIリファレンスはunstable_v2_createSession()・unstable_v2_resumeSession()・unstable_v2_prompt()のいずれも、optionsにmodel以外の追加オプションを受け付けると説明していましたが、詳細な型は公開されていませんでした。V1のquery()はClaudeAgentOptionsとして全オプションが型定義されているため、V2で暗黙的に渡していた設定がある場合は、V1移行時に対応するオプション名をClaudeAgentOptionsの型から確認し直す必要があります。
「高度なストリーミング入力パターン」についても、V2のドキュメントはこの一文だけを残し、具体的にどのパターンを指すかは明記していません。マルチターンの入力ジェネレータで複雑な分岐制御をしている実装ほど、書き換え時に個別の動作確認が必要になると見ておくのが安全です。移行作業を始める前に、対象コードのターン構成をいったん図か箇条書きで書き出しておくと、抜け漏れのある書き換えを防げます。
0.2.x系に留まる場合の制約
npm install @anthropic-ai/claude-agent-sdk@0.2でV2ごと固定する選択肢は残っていますが、この方法には実務上の制約があります。0.2.xは「V2インターフェースを含む最後のバージョン」であり、それ以降のAgent SDKに入った機能追加やバグ修正は0.3.142以降の系列にしか入りません。組み込みツールやフック、サブエージェント、MCP接続、パーミッション制御、プラグインの読み込みといったAgent SDK本体の能力も、Claude Codeが積み上げる更新をそのまま受け取る形なので、0.2.xに固定した瞬間からこれらすべてが当時の実装で足止めされます。セッションAPIだけを理由に固定する前に、他の機能で新しいリリースが必要になっていないかを確認します。
参考として、V2が最後まで公開していたSDKSessionインターフェースの全体は次のとおりです。0.2.x系のコードを保守する間だけ参照する価値があります。
interface SDKSession {
readonly sessionId: string;
send(message: string | SDKUserMessage): Promise<void>;
stream(): AsyncGenerator<SDKMessage, void>;
close(): void;
}よくある質問
手元のコードがV2を使っているかはどう確認すればよいか
unstable_v2_という接頭辞を持つ関数(unstable_v2_createSession・unstable_v2_resumeSession・unstable_v2_prompt)のインポート箇所を検索します。この接頭辞が付いた識別子は、TypeScript Agent SDK 0.3.142以降には存在しないため、見つかった箇所すべてが移行対象です。
Python版のAgent SDKにも同じ廃止の影響があるか
いいえ。V2セッションAPIはTypeScript Agent SDK限定の実験機能でした。Pythonでマルチターンの会話を続ける方法は、もともとClaudeSDKClientとcontinue_conversationオプションで、V2のようなunstable_接頭辞のセッションAPIを経由したことがありません。Python側のコードは今回の廃止による書き換えが不要です。
まとめ
V2セッションAPIは、unstable_接頭辞を外せないまま、セッションフォークという主要機能を欠いた状態で廃止されました。TypeScript Agent SDK 0.3.142以降でこのAPIを使い続ける手段はなく、npm install @anthropic-ai/claude-agent-sdk@0.2で古いバージョンに固定するか、query()とAsyncIterable<SDKUserMessage>・options.resumeへの書き換えのどちらかを選ぶことになります。単発プロンプト・単一セッション・マルチターン・セッション再開の4パターンはいずれもV1へ1対1で対応しており、書き換え自体の難度は高くありません。V2だけにしか無かったawait usingの自動クローズは、単に削除して構いません。