Migrations
effect-uai is pre-1.0; minor releases (0.x) can include breaking changes.
Each release that contains breaking changes ships a migration page on this
site with the full “old → new” picture: renames, removals, behavior
changes, and recommended migration order.
The CHANGELOG covers the why (one entry per PR). These pages cover the how (one page per release, written for a reader doing the upgrade in front of their editor).
Versions
- Migrating to 0.10: mostly additive. One small
breaking change to the tool layer: a
Toolnow carries a typed errorE(added beforeR, so only hand-written fullTool<...>annotations need editing), andToolkit.runpropagates tool failures typed rather than silently showing them to the model (fail with astring/Tool.fail, or wrap the toolkit inToolkit.describeFailures, to keep a failure model-visible). Additive: aWebReadcapability (URL to clean markdown, providers@effect-uai/firecrawl,@effect-uai/exa,@effect-uai/tavily,@effect-uai/jina, pluswebReadTool) and aBrowsercapability (drive a real browser over CDP via@effect-uai/browser, withbrowserToolkitand the verb tools). - Migrating to 0.9: a tool-layer refactor (breaking
but mechanical) plus an additive Mistral provider. A
Toolkitis now a name-indexed record built withToolkit.makeand passed straight tostreamTurn/Toolkit.run(noTool.toDescriptorsat the call site); plain and streaming tools unify into oneTool.makewhoserun(input, emit)returns anEffect(Tool.streaming/finalizeremoved); control and provider tools get honest kinds (Tool.signal,Tool.interaction,Tool.provider); independent toolkits combine withToolkit.compose. Additive:@effect-uai/mistral(Mistral LLM plus Voxtral STT/TTS), enough to run an all-Mistral voice loop. - Migrating to 0.8: purely additive. A new
WebSearchcapability in@effect-uai/core(a generic search service plus a ready-madewebSearchToolfor grounding an LLM), three search providers (@effect-uai/perplexity,@effect-uai/exa,@effect-uai/tavily), and two recipes (grounded answer, deep research). No breaking changes; bump dependencies and run typecheck. - Migrating to 0.7: a capability-honesty pass
across audio and embeddings.
AudioBlob.durationSeconds: numberbecomesduration: Duration.Duration(flowing through STT, TTS, and music). STT:GeminiTranscriberremoved (use OpenAI / ElevenLabs / Inworld),promptsplits intoprompt+biasingTerms,TranscriptResult.durationSeconds → duration. TTS:PhoneticEncodingandCustomPronunciation.encodingremoved (IPA-only), pronunciations now failUnsupportedon providers without an IPA path,DialogueTurntrims to{ voiceId, text }. Embeddings:EmbedEncodingtrimmed tofloat32 | int8 | binary(sparse / multivector move toJinaEncoding), mismatched encoding / image / multi-part now failUnsupportedinstead of degrading silently. Music:prompts → prompt,bpm/scale/instrumentaldropped,MusicResultcomposesAudioBlob(result.bytes → result.audio.bytes),generatereturnsGenerateResult(primary+variants[]),streamGenerationFromyieldsMusicStreamEvent. LLM (no rewrites): GeminitoolChoicenow mapped, Gemini URL images nowUnsupported, Lyria clip reports mp3 honestly. Additive (no migration needed):@effect-uai/elevenlabs/ElevenLabsMusicGenerator,@effect-uai/core/Capabilitieswarn-and-drop helper, ElevenLabspronunciationDictionaryLocators, multi-provider recipe runner via--provider=. - Migrating to 0.6: the consistent-naming sweep
plus additive speech features. Breaking but mechanical: “function
call” → “tool call” terminology (
Item→HistoryItem,FunctionCall→ToolCall), modulesOutcome→ToolResultandResolvers→Approval,Toolkit.executeAll→run,Tool.AnyKindTool→AnyTool,ToolEvent.Intermediate→Progress, and a trimmedLoopsurface (loopFrom→loopOver,stop()/stop(state),nextAfter/stopAfterremoved); the wire format is unchanged. Additive (no migration needed): multi-speaker dialogue and custom pronunciations onSpeechSynthesizer. - Migrating to 0.5:
TurnEventmigrated toData.TaggedEnum(type→_tag, snake_case → PascalCase),Encoding→EmbedEncoding, genericEmbedResponse<E>,Toolkit.outputEvent/outputEventsremoved, Gemini tool calling, newLoop.stopWith/loopFrom,LanguageModel.turn/retry,Tool.fromStandardSchema. - Migrating to 0.4: purely additive. New speech
(
Transcriber,SpeechSynthesizer) and music (MusicGenerator) services, sharedAudio/Transcript/Musicdomain, and three new provider packages (@effect-uai/openai,@effect-uai/elevenlabs,@effect-uai/inworld). No breaking changes. - Migrating to 0.3:
streamUntilComplete→onTurnComplete,nextStateFrom→continueWith(now pipe-friendly),Matchmodule removed, tool requirements flow throughR, newloopWithState, new embedding subsystem.
Versioning policy
0.x.y: minor (x) bumps may break source compatibility; patch (y) bumps don’t. Migration pages live at this level.- Post-1.0: semver. Breaking changes only on majors; each major gets a migration page.
Using Claude to migrate
The effect-uai-migrate skill
encodes per-version rewrite rules in operator form: “if you see X,
write Y.” Invoke it from Claude Code:
/skill effect-uai-migrateThe skill is one source of truth shared between the migration pages here and the assistant. New release? Update both in the same PR (see release process below).
Release process
For maintainers: every release that contains a breaking change MUST ship:
- A new
docs/migrations/v{X.Y}.mdpage following the template of the most recent migration page. - A new “X.(Y-1) → X.Y” section in
skills/effect-uai-migrate/SKILL.md. - A sidebar entry in
webpage/astro.config.mjslinking the new page. - CHANGELOG entries cross-linked to the migration page.
Treat these like CHANGELOG bumps: required in the same PR, not “I’ll do it later.” Stale skill content actively misleads users (and Claude) into recommending APIs that no longer exist.