Skip to content

TypeScript V2 session API (removed)

Reference for the experimental unstable_v2 session API removed in TypeScript Agent SDK 0.3.142, and how to migrate code that still uses it.

The V2 session API was an experimental, simpler way to hold multi-turn conversations in the TypeScript Agent SDK. Instead of feeding query() an async generator, you created a session object, called send() for each turn and stream() to read the reply.

Warning: V2 is gone. TypeScript Agent SDK 0.3.142 removed unstable_v2_createSession, unstable_v2_resumeSession, unstable_v2_prompt and the SDKSession and SDKSessionOptions types. New code should use query() with an AsyncIterable<SDKUserMessage> for multi-turn work, or options.resume to continue a saved session. This page exists for anyone maintaining code pinned to 0.2.x or earlier.

Version boundary

The package went straight from 0.2.x to 0.3.142, so "0.2 is the last with V2" and "0.3.142 removed it" describe the same line. To stay on V2:

npm install @anthropic-ai/claude-agent-sdk@0.2

As with current versions, a native Claude Code binary for your platform ships as an optional dependency, so most installs need nothing else. The quickstart lists the exceptions.

The V2 surface

Function or typeSignaturePurpose
unstable_v2_prompt(prompt: string, options: { model: string, ... }) => Promise<SDKResultMessage>One-shot question, returns the result message
unstable_v2_createSession(options: { model: string, ... }) => SDKSessionStart a conversation
unstable_v2_resumeSession(sessionId: string, options: { model: string, ... }) => SDKSessionContinue a stored conversation
SDKSessioninterfacesessionId, send(), stream(), close()
interface SDKSession {
  readonly sessionId: string;
  send(message: string | SDKUserMessage): Promise<void>;
  stream(): AsyncGenerator<SDKMessage, void>;
  close(): void;
}

model was required in every options object; other options were also accepted.

How V2 code looked

One-shot

import { unstable_v2_prompt } from "@anthropic-ai/claude-agent-sdk";

const res = await unstable_v2_prompt("Which HTTP status means 'too many requests'?", { model: "claude-sonnet-4-5" });
if (res.subtype === "success") console.log(res.result);

A conversation

Splitting send() from stream() made it easy to run your own logic between turns:

import { unstable_v2_createSession, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";

const textOf = (m: SDKMessage) =>
  m.type === "assistant"
    ? m.message.content.filter(b => b.type === "text").map(b => b.text).join("")
    : null;

await using chat = unstable_v2_createSession({ model: "claude-sonnet-4-5" });

await chat.send("Suggest three names for a bookkeeping app.");
for await (const m of chat.stream()) { const t = textOf(m); if (t) console.log(t); }

await chat.send("Make the second one sound less corporate.");
for await (const m of chat.stream()) { const t = textOf(m); if (t) console.log(t); }

await using (TypeScript 5.2+) closed the session automatically at the end of the block. On older compilers you called chat.close() yourself.

Resuming

Every streamed message carried session_id. Store it, close the session, and later:

await using again = unstable_v2_resumeSession(savedId, { model: "claude-sonnet-4-5" });
await again.send("Remind me which name we settled on.");
for await (const m of again.stream()) { /* ... */ }

What V2 never supported

  • Session forking (the forkSession option).
  • Some advanced streaming input patterns.

Both always needed the standard query() API.

Migrating to query()

V2Current equivalent
unstable_v2_prompt(p, opts)query({ prompt: p, options: opts }), then read the result message
createSession + repeated send()query({ prompt: asyncIterableOfUserMessages, options })
resumeSession(id, opts)query({ prompt, options: { ...opts, resume: id } })
session.close()Finish iterating, or call close() / interrupt() on the query object
session.sessionIdsession_id on any message

One-shot

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const m of query({ prompt: "Which HTTP status means 'too many requests'?", options: { model: "claude-sonnet-4-5" } })) {
  if (m.type === "result" && m.subtype === "success") console.log(m.result);
}

Multi-turn with an input stream

The main change is that turns now come from an async iterable you control. A small queue makes it feel much like V2:

import { query, type SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";

function userTurn(text: string): SDKUserMessage {
  return { type: "user", session_id: "", parent_tool_use_id: null,
           message: { role: "user", content: [{ type: "text", text }] } };
}

async function* turns() {
  yield userTurn("Suggest three names for a bookkeeping app.");
  // In a real app, await the next message from your UI here.
  yield userTurn("Make the second one sound less corporate.");
}

for await (const m of query({ prompt: turns(), options: { model: "claude-sonnet-4-5" } })) {
  if (m.type === "assistant") {
    console.log(m.message.content.filter(b => b.type === "text").map(b => b.text).join(""));
  }
}

Streaming vs single mode explains input streams in depth, and streamInput() on the query object adds turns to a running query.

Resume

for await (const m of query({ prompt: "Remind me which name we settled on.", options: { resume: savedId } })) { /* ... */ }

See sessions for resume, continue and fork.