Migrating to 0.11
0.11 is additive with one required action: bump your Effect beta. The bulk of the internal diff is the mechanical ripple of that bump; the public surface is otherwise source-compatible.
Everything else is new: a DeepResearch capability with four providers, native
grounding through provider-hosted tools on OpenAI / Gemini / Anthropic, and a
@effect-uai/ai-sdk package that serves a Vercel AI SDK useChat frontend from
an effect-uai loop.
If you hit a 0.11 compile error that looks like an older rename (tool arrays,
Tool.streaming, durationSeconds, executeAll, Outcome), you are crossing
an earlier breaking release. Apply the 0.10 page and the
ones before it first.
Required: bump Effect
The effect peer dependency moves from a pin to a range:
| Before | After |
|---|---|
4.0.0-beta.57 | >=4.0.0-beta.94 <5.0.0 |
Upgrade your app to effect@4.0.0-beta.94 or newer:
pnpm add effect@">=4.0.0-beta.94 <5.0.0"That is the whole migration. effect-uai’s own API did not rename anything; run
pnpm typecheck after the bump and fix any Effect-level beta changes in your own
code (the effect betas move fast between .57 and .94).
What’s new: DeepResearch (additive)
Submit a question, a provider runs a long-running background research job (many web searches over minutes), and you collect one cited report. It is the provider-hosted counterpart to the hand-rolled deep-research agent.
import { researchStream } from "@effect-uai/core/DeepResearch"import * as Items from "@effect-uai/core/Items"import * as Turn from "@effect-uai/core/Turn"
researchStream({ history: [Items.userText(question)] }).pipe( Stream.runForEach((event) => Match.value(event).pipe( Match.tag("WebSearchCall", (e) => Console.log(` · ${e.status}`)), Match.tag("TextDelta", (e) => write(e.text)), Match.tag("TurnComplete", (e) => printSources(Turn.citations(e.turn))), Match.orElse(() => Effect.void), ), ),)The stream terminates in TurnComplete, whose turn is a plain Turn (project
it with Turn.assistantText / Turn.citations / Turn.decodeStructured); it is
the same value the blocking research(request) and the detached collect(ref)
return. Every provider is modeled as a background job, so the surface is uniform:
research(request)submits and polls to the terminalTurn.researchStream(request)submits and forwards liveTurnEvents.submit/status/collect/streamFrom/cancelare the detached path. AResearchJobRefis serializable{ _tag, provider, id }data, so you can submit now and collect from a later process.
Four providers register the generic DeepResearch tag plus a provider-typed tag
for the narrowed model / knobs:
@effect-uai/responses/OpenAIDeepResearch(o3-deep-research, real streaming).@effect-uai/google/GoogleDeepResearch(Gemini Interactions, real streaming).@effect-uai/perplexity/PerplexityDeepResearch(sonar-deep-research, poll-only with a synthesized progress stream).@effect-uai/exa/ExaDeepResearch(exa-research, poll-only, with a provider-typedoutputSchemafor structured JSON output).
@effect-uai/core/Job (the generic poll-driven job primitive) and
@effect-uai/core/Citation (the provider-agnostic source / span model) ship
alongside. See native deep research.
What’s new: native grounding (additive)
Provider-hosted tools now render end to end. Put a provider tool in a Toolkit
next to your function tools and the adapter maps it to the model’s native tools
entry, so the model can search the web, ground against Google Search, run code,
or read files with no extra loop wiring.
import * as Toolkit from "@effect-uai/core/Toolkit"import { webSearchTool } from "@effect-uai/responses/ResponsesTools"
const toolkit = Toolkit.make(myFunctionTool, webSearchTool())// pass `toolkit` to streamTurn as usual; OpenAI runs the search itselfEach provider ships its hosted tools:
@effect-uai/responses/ResponsesTools:webSearchTool,codeInterpreterTool,fileSearchTool.@effect-uai/google/GeminiTools:googleSearchTool,urlContextTool,codeExecutionTool.@effect-uai/anthropic/AnthropicTools:webSearchTool,codeExecutionTool.
A provider tool the target adapter cannot render (a foreign provider, or an
unrecognized config) fails a typed AiError.Unsupported rather than being
silently dropped. Tool.isProviderTool / Tool.providerToolsOf are the new
core helpers that partition provider tools out of a toolkit. See
native grounding.
What’s new: AI SDK compatibility (additive)
@effect-uai/ai-sdk bridges a Vercel AI SDK frontend to an effect-uai loop.
Keep your @ai-sdk/react useChat client as it is and serve it from your loop
instead of streamText:
import * as Messages from "@effect-uai/ai-sdk/Messages"import * as UIMessageStream from "@effect-uai/ai-sdk/UIMessageStream"
const history = Messages.decodeMessages(messages) // UIMessage[] -> HistoryItem[]const sse = UIMessageStream.toUIMessageStream(agent(history)) // InteractionEvent -> SSE.EventdecodeMessages decodes the posted UIMessage[] into HistoryItems;
toUIMessageStream projects the loop’s InteractionEvent stream onto the AI SDK
UI Message Stream protocol (v1) as SSE.Events. The package owns no HTTP
layer, so it drops into any server. See examples/ai-sdk-next for a full Next.js
chat route.
This package was published at 0.10.0 outside the release train; from 0.11 it is fixed to the shared version like every other package.
Behavior changes (no rewrite)
Items.UrlCitation(and theAnnotationview) widened to the provider-agnostic citation shape:start_index/end_indexare now optional andcited_text/markerwere added. Reads stay compatible (numberbecomesnumber | undefined); a provider populates whichever anchor it has.Tool.makeandTool.provideraccept an EffectSchemadirectly asinputSchema(adapted internally,Inputstill inferred). Wrapping inTool.fromEffectSchemastill works and is unchanged.Toolkit.namespacenow preserves a tool’s typed errorEand requirementRthrough the prefixing rewrite.@effect-uai/core/SSEand@effect-uai/core/JSONLare now backed by Effect’sunstable/encodingprimitives; framing across chunk boundaries is unchanged in contract, more correct in edge cases.- Mistral no longer synthesizes a
TurnCompletefor a truncated or failed stream, so a halted turn surfaces as a failure instead of a bogus completion.
Migration order
- Bump your app to
effect@>=4.0.0-beta.94. - Run
pnpm typecheckand fix any Effect-beta changes in your own code.
Everything under “What’s new” is additive: add the layer (or the tool, or the package) and your capability-tag code resolves.