Packages

@tanstack/ai

0.71.0 · 21 Oct 2026MIT593.9k / weekRepositoryHomepagenpmAtom feed

103 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.

  1. 0.71.x · latest line
  2. 0.71.0

    21 Oct 2026
    • Add computer use that runs the same code on OpenAI and on Anthropic. A driver controls the machine. The Playwright and sandbox drivers give each thread its own machine.

      New packages:

      Changes in existing packages:

      const computerUse = createComputerUse({
        driver: playwrightComputer({ startUrl: 'https://tanstack.com' }),
      })
      
      chat({
        adapter,
        messages,
        threadId,
        tools: computerUse.tools,
        middleware: [computerUse.middleware],
        interrupts: [computerUse.interrupt],
      })
      
      await computerUse.close(threadId) // one thread
      await computerUse.close() // every thread, and the browser

      The ai-core agent skill routes computer use to @tanstack/ai-computer-use and its three drivers.

  3. 0.70.x
  4. 0.70.0

    19 Oct 2026
    • Subagent limits, and provider keys for agents.

    • A subagent can now return a plain value and call any activity.

    • Check a raw JSON Schema tool input before the tool runs. This covers plain JSON Schema tools and every MCP tool.

      Behavior change:

      Migration: if a JSON Schema tool must accept input that its schema rejects, widen the schema.

  5. 0.69.x
  6. 0.69.0

    13 Oct 2026
    • Keep the order of Claude's thinking, text, and tool calls in one answer.

    • chat() can now retry a rate-limited model call by itself. Pass retry: { maxRetries, maxWaitMs? }. When the provider sends a wait time (retryAfterMs) with an error, chat() waits that long, then calls the model again.

      retry is off by default.

      chat() now also sends RUN_STARTED before a RUN_ERROR when the adapter failed before it sent a start.

    • Text adapters now set one stable code on RUN_ERROR when the input does not fit in the model's context window: code: 'context_overflow'. @tanstack/ai exports it as CONTEXT_OVERFLOW_CODE. The provider's own text stays in message.

      if (chunk.type === 'RUN_ERROR' && chunk.code === CONTEXT_OVERFLOW_CODE) {
        // shorten the messages, then call chat() again
      }

      These adapters set it: OpenAI and the other OpenAI-compatible adapters (from code: 'context_length_exceeded'), Anthropic, Gemini, Bedrock (Converse, Chat Completions, and Responses), OpenRouter, Grok, Cloudflare Workers AI, and BytePlus.

      Changes to code that reads the old values:

  7. 0.68.x
  8. 0.68.0

    9 Oct 2026
    • fakeText() takes an input option, for example fakeText({ input: ['text', 'image'] }). It sets the fake's inputModalities, so you can test code that checks what a model accepts. It also sets the adapter's input modality type.

    • Fix a server tool call that the next turn sent twice when you use withPersistence. When a model call starts with a tool call and no text (often after thinking), StreamProcessor gave that message the id of the next model call. The next turn then replaced the stored answer with a copy that also held the tool call, so the provider got the same tool_use twice. The client now keeps the id of the message the tool call belongs to.

  9. 0.67.x
  10. 0.67.0

    9 Oct 2026
    • Text adapters can now give inputModalities at run time: the input kinds that the model reads, for example ['text', 'image', 'document']. undefined means that the adapter does not know.

    • A middleware onAfterToolCall hook can now replace the result of a tool call. Return { type: 'replaceResult', result } from the hook.

    • Add fakeText() at the new @tanstack/ai/testing subpath. It is a text adapter that answers from a script. Use it to test chat(), tools, and middleware with no network and no API key.

  11. 0.66.x
  12. 0.66.0

    8 Oct 2026
    • breaking

      chat({ stream: false }) and streamToText() now return a ChatResult with text and chunks, not a string. The chunks keep the tool calls and interrupt outcomes that the string lost. To migrate, change const text = await chat({ stream: false }) to const { text } = await chat({ stream: false }).

      Add a JSON transport for hosts that cannot stream a response. On the server, toJsonResponse() sends a run as one JSON body, and resumeJsonResponse() sends the rest of a durable run. On the client, the fetchJson() connection adapter reads that body and asks again until the run is done. Each framework package re-exports fetchJson.

    • Keep the internal subagent run id out of synthetic tool result content.

    • Merge assistant segments that share a stable UI message ID after conversion.

  13. 0.65.x
  14. 0.65.1

    7 Oct 2026
    • StructuredOutputPart.data is no longer optional when status is 'complete'. After you check part.status === 'complete', TypeScript knows part.data is set.

      Migration: if you validate messages with a schema typed as UIMessage (for example z.ZodType<UIMessage>), split the structured-output part by status. Give 'complete' a required data field.

      Add isHydrating to useChat (and injectChat, createChat) and an onHydratingChange callback plus getIsHydrating() to ChatClient. It is true while the chat is rebuilt from persistence (the server hydrate with persistence: true, or an async storage adapter). It turns false when the transcript is in place and any in-flight run is re-joined, or when the load fails.

  15. 0.65.0

    6 Oct 2026

    Only updates dependencies, nothing else changes.

  16. 0.64.x
  17. 0.64.1

    5 Oct 2026
    • Stream durable responses live again. With durability set, a chunk waited in the append batch until 32 chunks arrived or the run finished, so a short reply showed up all at once at the end. Now the batch also flushes when the model sends no new chunk for 50ms. You do not need batch: 1 for live text any more. batch stays the largest number of chunks in one append.

      Set the wait with the new batchWaitMs option on durability (toServerSentEventsResponse, toHttpResponse) and on toWebSocketStream / toWebSocketResponse. A higher value means fewer writes to the log but slower live text. 0 appends every chunk on its own.

  18. 0.64.0

    2 Oct 2026
    • Run the server tools of one model turn at the same time. chat() used to run them one after another, so a turn with several slow tools took as long as all of them together.

    • snapDuration and snapToDurationOption accept seconds (6), a numeric string ("6"), a seconds template ("6s"), or a keyword the model lists ("auto"). durationToSeconds reads the numeric forms. Sora (sora-2, sora-2-pro) accepts 4 | 8 | 12, "4" | "8" | "12", or "4s" | "8s" | "12s" and sends "4" | "8" | "12". Lovable Veo accepts the same three spellings for 4, 6, and 8 seconds.

    • Send Claude's thinking and tool errors back the way Claude sent them.

  19. 0.63.x
  20. 0.63.0

    27 Sep 2026
    • Add an optional inputSchema to defineAgent. The agent's tool shows the schema to the model, so the parent model writes the child's input, such as a short brief. run gets the checked input as a typed ctx.input. A bad input goes back to the model as a tool error, and the child does not start. inputSchema needs tool mode: chat() throws when subagents.router meets an agent that has one. Agents without inputSchema do not change.

    • Preserve structured outcomes for cancelled and denied tool results. A tool-result part now carries outcome: 'cancelled' | 'denied' (new ToolResultOutcome type and isToolResultOutcome guard) across tool execution, streaming, persistence, and UI restoration, so callers can tell a user or middleware decision from an ordinary tool failure without matching error text. state stays 'error' for these results. The @tanstack/ai-client ToolResultPart type (used by useChat messages) now has the same outcome field.

    • @tanstack/ai-mcp can create an MCP server with createMCPServer and serveMCPStdio. The client package moves from @modelcontextprotocol/sdk to @modelcontextprotocol/client and @modelcontextprotocol/server. The client tries spec 2026-07-28 first, then the 2025 handshake. When an MCP server asks for input, chat() pauses with an mcp_input interrupt. Answer it with resolveInterrupt or cancel(), and the tool runs again with the answer in ctx.inputResponse. A tool can set execution: 'task' for a spec 2025 task handle. A tool reads requestInput and sample on ctx.context, typed by MCPToolContext. The server auth option takes the MCP SDK OAuthTokenVerifier shape. jwtVerifier and introspectionVerifier cover JWT and opaque tokens, and a tool reads the token as ctx.context.authInfo. createMCPClient<typeof server>({ transport }) types a remote client from a createMCPServer server, and createMCPClient({ server }) calls a server in the same process. server.handle(request, { authInfo, context }) takes a token your own middleware verified and values for ctx.context, typed with MCPToolContext<{ db: Db }>. A tool's metadata.title and metadata.annotations reach the host as the MCP title and annotations. A tool with no outputSchema can return a CallToolResult as is. sessions: 'reject' turns spec 2025 sessions off for hosts with many instances.

  21. 0.61.x
  22. 0.61.0

    24 Sep 2026
    • Add @tanstack/ai-worldlabs with worldlabsWorld() for Marble world generation through generateWorld(). World Labs jobs return a viewer URL and splat/mesh assets. WorldGenerationResult now has optional url, worldId, operationId, and assets so job adapters can omit a live-session token.

    • Keep Anthropic's signed thinking order when a provider-executed tool (web_search / web_fetch) runs inside the same response as thinking, and stop provider-executed calls from being classified as client tool interrupts.

      Without this, the turn after such a response fails with Anthropic's thinking or redacted_thinking blocks in the latest assistant message cannot be modified.

    • Forward summarize maxLength through OpenAI-compatible adapters using the token-limit key for their API, regardless of the summarize wrapper name. Keep explicit caller limits unchanged.

  23. 0.59.x
  24. 0.59.0

    24 Sep 2026
    • Add first-class subagents. chat({ subagents }) starts named child agents (router spawn, or a synthetic tool when there is no router). The stream emits AG-UI SUBAGENT_* events with subagentRunId. The client stores nested type: 'subagent' parts. useChat().subagents and part.subagent are the same live handle, including stop().

      In the React chat UI kit, pass the same agents to options.subagents that you pass to chat(). A subagent card can style its own child's parts: <Parts partsComponents={...} toolsComponents={...} />. Each entry replaces the root entry of the same key for that card and its nested children. Keys you do not set use the root widgets. The card's tool names, tool input and output, and approvals are typed from that agent's tools. The root interruptsComponents also accepts the children's approval tools and interrupts.

      Pass the same defineAgent list to useChat({ subagents }) when you are not using the chat UI factory. part.subagent.name narrows to those names, and that child's message parts use the agent's tools.

      A child card keeps all of the child's work: text, reasoning, tool calls, tool results, approvals, and nested children. A child can stop for an approval or a client tool. Its SUBAGENT_FINISHED has outcome: { type: 'suspended' }, and the parent run ends with that interrupt. The resume continues the same child. Pass parentRunId: ctx.parentRunId and resume: ctx.resume to the child chat().

      The AI devtools Conversation tab shows each subagent as a card of steps, drawn like the parent's steps. The steps are the child's server iterations when server events reach the devtools, else they come from the browser messages. The User view shows the child's text and tool outputs. Nested children show the same way. The child's card updates while it streams, and a later turn keeps the earlier turns.

      Child token usage is added to the parent RUN_FINISHED.usage. Child messages travel on the AG-UI wire as their own messages, tagged with subagentRunId.

      @tanstack/ai now depends on @ag-ui/core 1.0.0. Subagent events come from that package.

      AG-UI { type: 'file' } content sources now cross the wire as ContentPartFileSource. No adapter reads them yet, so chat() throws before it calls the adapter.

      RUN_FINISHED.usage now carries cacheWriteInputTokens. metadata.tanstack.usage still carries promptTokensDetails.cacheWriteTokens, so older readers see the same usage as before.

      chat({ subagentRunId }) puts that id on the middleware context as ctx.subagentRunId. A child chat() passes subagentRunId: ctx.subagentRunId, so a middleware inside the child knows it runs as a subagent and which card it belongs to. The field is absent on a top-level run.

      fromSpecTokenUsage adds every entry of RUN_FINISHED.usage. Before, it read only the first entry. A run with more than one usage entry now reports the total.

    • Carry the failing subagent's own error message and code on the parent RUN_ERROR instead of a generic "A subagent failed", and keep the children's token usage on that terminal so a failed turn still reports what it spent. Also settle the routed-subagent persistence record when a run is stopped while the router is still deciding, which previously left the record running forever and made reconstructChat hand the client a run to tail that never emits.

    • A subagent run record stores parentRunId, subagentRunId, and name. Each child keeps its full transcript: text, reasoning, tool calls, and tool results. reconstructChat uses listByParentRun to put each child card back, with nested children and a child that waits for an approval. A later message keeps that link, so the next agent still sees the child text.

      Subagent support in a store is optional. runPersistenceConformance checks the three link fields and listByParentRun only when the store has listByParentRun. A store without it passes with no change and no skipMethods entry.

  25. 0.58.x
  26. 0.58.0

    21 Sep 2026
    • Flush CUSTOM events through the durability layer as soon as they are emitted, so progress events such as compaction:started reach the client at emit time. High-volume events (process.stdout, process.stderr, sandbox.file, sandbox.file.diff) still batch. Pass { batch: true } on emitCustomEvent to opt an event into the batch.

  27. 0.57.x
  28. 0.57.0

    21 Sep 2026
    • Add decide() and TypeSafe Jev evaluate adapters.

      Callers await one decide({ adapter, state, questions }) call.
      Questions use choice(), score(), and boolean(). Answers sit on the result (value, probability, confidence). Usage sits on result.meta.

      Jev transports:

    • Add a generateVoice() activity for creating a voice, and refresh the ElevenLabs model lists.

      generateVoice() creates a reusable voice and returns voice ids that generateSpeech({ voice }) accepts. Providers create voices two ways, and the activity covers both: design one from a text prompt, or derive one from referenceAudio. Pass a name to keep the voice in the provider's library, and read saved on each returned voice to see what happened. Every returned voice also carries status, which is 'ready' on every adapter today. Comes with VoiceAdapter / BaseVoiceAdapter for adapter authors, the voice:request:* and voice:usage devtools events, and a voice_generation OpenTelemetry operation name. See Voice Creation.

      listVoices() reads an account voice catalog back, for when you did not store the id generateVoice() returned. It filters by origin (premade, generated, cloned, professional). listVoices is an optional method on TTSAdapter rather than VoiceAdapter, because voice is a generateSpeech() option and that is where the id gets used. Only providers with a per-account catalog implement it; where the voice list is fixed the package publishes it directly (GeminiTTSVoices from @tanstack/ai-gemini, the OpenAITTSVoice union from @tanstack/ai-openai), which beats a network call, and calling listVoices() on such an adapter throws and points at those exports. elevenlabsSpeech implements it against GET /v1/voices.

      elevenlabsVoiceDesign() implements voice creation on eleven_ttv_v3 and eleven_multilingual_ttv_v2. ElevenLabs' design-then-create two-step is hidden inside the adapter. Only eleven_ttv_v3 accepts referenceAudio, and it is a design reference rather than a straight clone: a prompt is still required, and modelOptions.promptStrength balances the description against the recording.

      ElevenLabs models. @elevenlabs/elevenlabs-js moves to ^2.68.0 and @elevenlabs/client to ^1.25.0. The bump is what makes the new music and transcription ids type-check: 2.44 pinned the music request to modelId?: 'music_v1' and typed speech-to-text as a two-member 'scribe_v2' | 'scribe_v1' union. Adds eleven_v3_conversational (TTS), scribe_v2_medical (Scribe), and music_v2_5 and music_v2 (music). eleven_turbo_v2, eleven_turbo_v2_5, eleven_monolingual_v1, scribe_v1, and music_v1 are still accepted and are commented as deprecated in model-meta.ts. ELEVENLABS_AUDIO_MODELS is now pinned to the SDK's own MusicModelId | SfxModelId with satisfies, so an id the SDK drops fails the build instead of the request.

      Breaking: eleven_text_to_sound_v1 is removed from ELEVENLABS_AUDIO_MODELS. ElevenLabs dropped it from the catalog and from the SDK's SfxModelId, so the adapter can no longer send it. Use eleven_text_to_sound_v2.

  29. 0.55.x
  30. 0.55.0

    17 Sep 2026
    • Page long chat threads on hydrate. Pass history: { pageSize } with persistence: true. Then call loadOlderMessages() to prepend older turns. withPersistence merges incoming messages by id so a short client list keeps stored extras. loadThread accepts optional limit / before and can return a MessagePage.

    • chat: keep ui-resource parts emitted during the current run on the interrupt MESSAGES_SNAPSHOT. Server tools emitting ui:// widgets via ctx.emitCustomEvent('ui-resource', ...) now have the resource recorded on the tool-call anchor ModelMessage, so the MESSAGES_SNAPSHOT emitted when the run pauses on a client tool no longer drops the widget from client state (#1397).

  31. 0.54.x
  32. 0.54.0

    10 Sep 2026
    • Add generateWorld() and generateLiveVideo() for prompt-steerable sessions, plus a first-party Reactor adapter (reactorWorld, reactorVideo) and fal falLiveVideo() for H3 Max Director. Reactor returns a session JWT. falLiveVideo returns the WMA app id on result.model so the browser can call wma(live.model). generateVideo() stays the job path that polls for a file URL.

    • Emit the native structured result before RUN_FINISHED so clients receive the final object before the run closes. Emit only RUN_ERROR if parsing fails.

      Wait for the active subscriber to process all events before resolving send, including streams that take more than 32 timer ticks to process.

  33. 0.53.x
  34. 0.53.0

    3 Sep 2026
    • Add @tanstack/ai-cloudflare: a Cloudflare adapter for Workers AI chat, summarization, embeddings, image generation, text-to-speech, and transcription over the env.AI binding or the REST API, with AI Gateway routing (gateway option and cloudflareGateway() helper for other providers). @tanstack/ai learns the cloudflare max-tokens key for summarize, lets defineByokProvider declare companion credentials with with, and adds getByokKeys(request, { name: provider }) to @tanstack/ai/byok/server. @tanstack/ai-client's defineByok takes providers: a send for a provider with companions (Cloudflare token plus account id) carries every x-byok-* header and prompts for each missing value.

  35. 0.52.x
  36. 0.52.3

    3 Sep 2026
    • Replay OpenAI Responses reasoning items with function_call on the next tool turn. Default include: ['reasoning.encrypted_content'] only on reasoning models.

  37. 0.52.2

    2 Sep 2026
    • createChatOptions now preserves the runtime-context requirement on its return type. When tools or middleware declare a required context, the input already enforces context via RuntimeContextOption, but the return type declared it optional — so spreading the result into chat() failed to typecheck (Type 'undefined' is not assignable to type '...'). The return type now applies the same RuntimeContextOption conditional as the parameter, so the documented spread pattern compiles with context-typed tools. Type-level only; runtime is unchanged.

  38. 0.52.1

    1 Sep 2026
    • Keep the first text delta when a message speaks after a tool call and the text
      arrives in more than one chunk.

  39. 0.52.0

    28 Aug 2026
    • Keep canonical chat history separate from compacted provider context. Reuse
      validated compaction checkpoints through an optional persistence metadata store.

    • Add ctx.emitCustomEvent on chat middleware context. The engine yields
      CUSTOM chunks while hooks such as onConfig are still running, so a long
      middleware step can send progress before it finishes. Compaction uses this
      to emit compaction:started before the strategy returns.

    • Preserve draft-07 tuple items arrays during structured-output conversion.

      Keep a single widening map for homogeneous arrays so undoNullWidening applies it to every element.

  40. 0.51.x
  41. 0.51.0

    27 Aug 2026
    • Add @tanstack/ai-skills: portable Agent Skills (SKILL.md) as a first-class chat() middleware.

      withSkills(sources, options?) renders a skill catalog and a load_skill tool so any tool-calling model can load skills on demand, on any provider, with no server sandbox. Skills come from inlineSkill, skillDirectory (/node), or a build-time staticSkills bundle, and compose via aggregate/dedupe/filter/cache. createResourceTool exposes a skill's bundled files through read_skill_resource, and runSkillSourceConformance (/testing) validates custom SkillSource adapters. The catalog renders as <available_skills> XML for Anthropic models and markdown for others; portable and hosted (native) skills refuse to combine in one call.

      Core @tanstack/ai now exports SkillLimitError. The native factories throw it (or add validation): codeExecutionTool (@tanstack/ai-anthropic) frames its 8-skill cap, and shellTool (@tanstack/openai-base) now validates skill_id format instead of nothing. @tanstack/ai-sandbox reuses the shared skill-directory walk from @tanstack/ai-skills.

      withSkills sends a skills:state CUSTOM chunk so TanStack AI DevTools can show the catalog and which skills the model loaded.

    • Keep append() pending until the HTTP response is fully processed, including later RUN_FINISHED events in the same agent loop.

    • Fix StreamProcessor dropping the first TEXT_MESSAGE_CONTENT delta when a TOOL_CALL_START event's parentMessageId precedes that message's TEXT_MESSAGE_START — the normal AG-UI shape for "call a tool, then explain the result" as one assistant turn (#1247).

  42. 0.50.x
  43. 0.50.0

    27 Aug 2026
    • Preserve tool-result identity, metadata, multimodal content, and timestamps across message conversions and hydration.

      Correct the public WireMessage type. System and user messages now require content, and the union no longer includes outbound activity messages.

      Keep structured multimodal content compatible with the AI devtools message store.

    • Preserve AG-UI message fields and UI state across serialization, snapshots, and persistence.

  44. 0.49.x
  45. 0.49.1

    24 Aug 2026
    • Allow cancelling a tool interrupt when its runtime tool is unavailable or its schema has changed

  46. 0.48.x
  47. 0.48.0

    22 Aug 2026
    • Put AG-UI extras under metadata.tanstack. SSE/HTTP wire events are spec-only.

      sendMessage({ content, metadata }) stamps user metadata on the user message.
      In-process chat() still yields toolName, TOOL_CALL_END.input, and TanStack TokenUsage.
      Thinking signatures round-trip on REASONING_ENCRYPTED_VALUE.
      Wire messages use content / toolCalls / fan-out roles, not parts.

  48. 0.47.x
  49. 0.47.3

    21 Aug 2026
    • Reject non-streaming text calls when their event stream ends with a RUN_ERROR.

    • Preserve signed thinking blocks relative to tool calls when converting assistant UI messages. A thinking block that follows a provider-executed tool now starts a new assistant segment instead of being replayed before that tool.

    • Fix tool-call input corruption when a TEXT_MESSAGE_CONTENT event arrives between TOOL_CALL_ARGS events. Text events no longer force-complete in-flight tool calls, and input is only set when a strict JSON.parse of the accumulated arguments succeeds.

  50. 0.47.2

    20 Aug 2026
    • Route adapter-emitted RUN_ERROR events through middleware onError hooks and preserve provider error codes in persisted run failures.

  51. 0.47.1

    20 Aug 2026
    • Persist completed structured outputs as structured-output message parts and restore them during chat hydration.

  52. 0.46.x
  53. 0.46.0

    19 Aug 2026
    • Add a self-describing billed field to TokenUsage so billed quantities carry the unit they are counted in (#816). usage.billed is { quantity, unit } with a BillingUnit union ('seconds', 'units', 'images', 'tokens', ... open-ended). The deprecated unitsBilled / durationSeconds counts are still populated for backward compatibility. The fal adapters report { quantity, unit: 'units' }, Grok video { quantity, unit: 'seconds' }, the OpenAI/Grok/BytePlus duration-billed transcription paths { quantity, unit: 'seconds' }, BytePlus Seedream images { quantity, unit: 'images' }, BytePlus Seedance video { quantity, unit: 'tokens' }, and Cohere/OpenRouter rerank { quantity, unit: 'units' } (search units). Persistence sums billed when both reports use the same unit. otelMiddleware emits the pair as tanstack.ai.usage.billed_quantity / tanstack.ai.usage.billed_unit span attributes.

    • WebSocket transport: a full-duplex, resumable third transport alongside SSE and
      NDJSON, reusing the same delivery-durability seam.

      On the server, @tanstack/ai adds toWebSocketStream(socket, request, { onRun, durability, batch, heartbeatMs, idleTimeoutMs, debug }) — a portable core that
      pumps a conversation over an already-accepted WHATWG WebSocketLike server
      socket (Node via ws, Bun, etc.), and toWebSocketResponse(request, { onRun, … }), a thin wrapper that upgrades via WebSocketPair and returns a 101
      Response on Cloudflare Workers/Durable Objects (it throws elsewhere, pointing
      you at toWebSocketStream). Because one socket outlives many chat() turns
      (client-tool resubmits, follow-up user messages), you pass an onRun(ctx) => AsyncIterable<StreamChunk> factory instead of a prebuilt stream — the helper
      calls it per inbound RunAgentInput frame. The socket is conversation-scoped:
      it stays open across turns and closes on client close or the idle timeout
      (which never fires while a turn is still streaming), with a periodic
      { type: 'ping' } heartbeat. An { type: 'abort', runId } control frame
      aborts only that turn, leaving the socket open. A turn that fails is surfaced
      to the client as a live RUN_ERROR frame, mirroring the HTTP transports. Durability is keyed per turn and reuses
      the existing durableStreamSource, so server→client frames carry the same
      { id, chunk } envelope as NDJSON. resumeWebSocketStream(socket, { adapter })
      and resumeWebSocketResponse({ adapter }) replay a run read-only from the
      durability log (no model call).

      On the client, webSocket(url, options) (in @tanstack/ai-client, re-exported
      from @tanstack/ai-react, -solid, -vue, -svelte, and -angular) is a
      full-duplex subscribe + send connection adapter for useChat. send()
      writes a RunAgentInput frame; subscribe() yields inbound chunks, ignores
      heartbeats, unwraps durable envelopes, and auto-reconnects a dropped durable run
      by reopening with ?runId=&offset= (browsers can't set a Last-Event-ID
      handshake header, so the offset rides in the URL). The reconnect bookkeeping
      (offset de-dupe, no-progress ceiling → StreamReconnectLimitError) is shared
      with the HTTP adapters via the new createReconnectTracker, and a fatal drop
      surfaces to the consumer (StreamReadError / StreamReconnectLimitError)
      instead of hanging. Aborting a run (stop() in useChat) sends the
      { type: 'abort', runId } frame so the server cancels the turn instead of
      generating to completion, and joinRun() opens its own replay socket so a
      rejoin never collides with the live conversation socket.

    • Preserve reasoning in server chat message history and interrupt snapshots.

  54. 0.45.x
  55. 0.45.1

    18 Aug 2026
    • Return recoverable Groq tool_use_failed responses to the model as non-executable tool errors so the agent loop can repair them.

  56. 0.45.0

    18 Aug 2026
    • Harness adapters honor chat({ outputSchema }) on the same turn.

      Claude Code and Codex pass a native schema flag. OpenCode, Grok Build, and acpCompatible parse JSON from the final assistant text. The engine reads a structured-output.complete event so harness prose is not parsed as JSON.

    • Rename Code Mode "skills" to "snippets" to disambiguate them from agent skills (the SKILL.md packaging system).

      Breaking — package rename. @tanstack/ai-code-mode-skills is now published as @tanstack/ai-code-mode-snippets. Update your dependency and imports. The /storage subpath is unchanged.

      Breaking — API rename. Every Skill/skill identifier in the package public API becomes Snippet/snippet, for example:

      Breaking — sandbox hook (@tanstack/ai-code-mode). The createCodeModeTool config option getSkillBindings is renamed to getSnippetBindings (same signature — an optional () => Promise<Record<string, ToolBinding>> returning dynamic bindings merged at execution time).

      Breaking — wire contract (@tanstack/ai). The Code Mode custom events are renamed: code_mode:skill_call / _result / _error → code_mode:snippet_* (payload field skill → snippet), and skill:registered → snippet:registered. The exported event types CodeModeSkillCallEvent / CodeModeSkillResultEvent / CodeModeSkillErrorEvent / SkillRegisteredEvent are renamed to their Snippet equivalents.

    • Preserve existing message IDs on interrupt MESSAGES_SNAPSHOT events.

  57. 0.44.x
  58. 0.44.1

    14 Aug 2026
    • Preserve stable IDs and creation timestamps for server-generated assistant messages across persistence and hydration.

  59. 0.44.0

    10 Aug 2026
    • ai: add timeout and abortSignal to media generation activities

      Media activities (generateImage, generateAudio, generateVideo, generateSpeech, generateTranscription, and summarize) now accept optional timeout and abortSignal. Core composes them into a request-specific effective signal, races the adapter call so hung providers reject, clears timeout resources on settle, and routes aborts to middleware onAbort (not onError).

      @tanstack/ai-fal forwards the signal to fal.subscribe() / fal.queue.submit() per request — never via global fal.config() — so concurrent generations stay isolated.

    • Add a multimodal embed() activity. A single primitive covers one input or a batch — input accepts a string, a text part, an image part, or a fused text+image item written as a nested Array<ContentPart> ([textPart, imagePart], the same shape chat messages use), one vector per item, with the accepted item types narrowed per model at compile time. Top-level dimensions requests Matryoshka output sizes where supported. Results carry embeddings: [{ vector, index }] plus usage when the provider reports it, and embed() participates in generation middleware, debug logging, OTel (gen_ai.operation.name: embeddings), and devtools events like every other activity.

      Provider adapters: openaiEmbedding (text-embedding-3-small/large), geminiEmbedding (gemini-embedding-001), mistralEmbedding (mistral-embed, codestral-embed), ollamaEmbedding (nomic-embed-text and any local model), bedrockEmbedding (Titan Text V2, Titan Multimodal G1 with fused text+image, Cohere Embed v3 on Bedrock), and @tanstack/ai-cohere's cohereEmbedding (embed-v4.0, multimodal text+image with required inputType).

    • add rerank() activity for reordering documents by relevance to a query

      Adds a provider-agnostic rerank() activity (with createRerankOptions, the RerankAdapter interface, and BaseRerankAdapter). Documents may be strings or JSON-serializable objects — object documents are serialized for the provider and the original element is returned in the result, fully typed. Supports topN, per-request cancellation via abortSignal, and the standard observe-only GenerationMiddleware (onStart/onUsage/onFinish/onAbort/ onError) plus rerank:* devtools events. Rerank bills in provider-defined search units, surfaced on usage.unitsBilled.

      The first adapter ships in the new @tanstack/ai-cohere package as cohereRerank / createCohereRerank.

  60. 0.43.x
  61. 0.43.1

    7 Aug 2026
    • otelMiddleware: open an iteration span for structured-output finalization

      No-tools + outputSchema calls skip the agent loop and only run the structured-output finalization request. That path previously emitted only a root chat span — no generation record, and captureContent was a silent no-op. onConfig now also opens an iteration span when phase === 'structuredOutput', so each provider model call is observable. Native-combined mode is unaffected (it never fires that phase).

  62. 0.43.0

    4 Aug 2026
    • breaking

      RunStatus widened to include 'aborted' (previously 'running' | 'completed' | 'failed' | 'interrupted'). The union appears in a read position (get(): Promise<RunRecord | null>), so an exhaustive switch over record.status with a never default in your code stops compiling until it handles 'aborted'.

    • breaking

      Run types are re-exported from @tanstack/ai rather than declared here, so the runs store is typed against core's RunStore directly. MemoryRunStore implements both optional list methods, and the shared conformance testkit covers them.

    • breaking

      runPersistenceConformance accepts skipMethods. An optional method that is missing and not declared in skipMethods now throws instead of silently passing, so an existing backend running the suite may see a new failure telling it to implement the method or declare the omission.

  63. 0.42.x
  64. 0.42.0

    20 Jul 2026

    Reading the note…

  65. 0.41.x
  66. 0.41.0

    16 Jul 2026

    Reading the note…

  67. 0.40.x
  68. 0.40.0

    6 Jul 2026

    Reading the note…

  69. 0.39.x
  70. 0.39.1

    3 Jul 2026

    Reading the note…

  71. 0.39.0

    30 Jun 2026

    Reading the note…

  72. 0.38.x
  73. 0.38.0

    26 Jun 2026

    Reading the note…

  74. 0.37.x
  75. 0.37.0

    25 Jun 2026

    Reading the note…

  76. 0.36.x
  77. 0.36.0

    25 Jun 2026

    Reading the note…

  78. 0.35.x
  79. 0.35.0

    24 Jun 2026

    Reading the note…

  80. 0.34.x
  81. 0.34.1

    24 Jun 2026

    Reading the note…

  82. 0.34.0

    23 Jun 2026

    Reading the note…

  83. 0.33.x
  84. 0.33.0

    22 Jun 2026

    Reading the note…

  85. 0.32.x
  86. 0.32.0

    18 Jun 2026

    Reading the note…

  87. 0.31.x
  88. 0.31.0

    16 Jun 2026

    Reading the note…