# Tasks: voice-elevenlabs **Change ID**: voice-elevenlabs **Package**: `@suaveplan/voice-elevenlabs` **Target path**: `packages/audio/voice-elevenlabs` **Prerequisite packages on Verdaccio**: `@suaveplan/voice`, `@suaveplan/third-party-elevenlabs`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/streams` > Supersedes the 2026-04-24 fused-HTTP design. The prior `packages/audio/voice-elevenlabs` implementation MUST be deleted before this rebuild starts. --- ## 0. Pre-rebuild teardown - [x] Confirm `git status` shows the prior `packages/audio/voice-elevenlabs` content is committed or accounted for; if uncommitted scratch work exists, stash it explicitly with the user. - [x] `rm -rf packages/audio/voice-elevenlabs` — delete the entire prior implementation. - [x] Confirm no other package imports from `@suaveplan/voice-elevenlabs` (`grep -rln '@suaveplan/voice-elevenlabs' packages/`); the only references should be inside the to-be-deleted folder itself. --- ## 1. Scaffold - [x] Create `packages/audio/voice-elevenlabs/` with `src/`, `tests/`, `src/__benchmarks__/`. - [x] Write `package.json` at version `0.1.0`, `"type": "module"`, `"sideEffects": false`, full subpath `exports` map per feature module, deps `@suaveplan/voice`, `@suaveplan/third-party-elevenlabs`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/streams`, `zod`, `tslib` (catalogs only). - [x] Write `tsconfig.lib.json` extending `../../../tsconfig.base.json`. - [x] Write `vite.config.ts` with multi-entry build for every feature module + factory. - [x] Write `vitest.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`, coverage thresholds 100/100/100/100. - [x] Add `lint`, `lint:fix`, `format`, `check`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check` scripts. --- ## 2. Error remapping module - [x] `src/errors/remap.ts` — `mapElevenLabsError(err: unknown, context: { operation: VoiceOperation; voiceId?: string }): VoiceError` covering every `ElevenLabsApiError` subclass per the proposal §Error remapping table. Preserves the original error via `cause`. - [x] `src/errors/remap.schemas.ts` — Zod for `VoiceOperation = 'tts' | 'tts-stream' | 'stt' | 'voices' | 'sound-generation' | 'audio-isolation'`. - [x] `src/errors/remap.md` - [x] `src/errors/remap.test.ts` — every `ElevenLabsApiError` subclass produces the expected `VoiceError` code, preserves `cause`, and threads through `context.voiceId` where the table specifies. --- ## 3. Engine implementations ### 3.1 `tts` - [x] `src/tts/tts.ts` — `ElevenLabsTtsEngine` implements `VoiceSynthesizer`. - `synthesize(req)`: maps `SynthesisRequest` → ElevenLabs TTS body via `mapSynthesisRequest()`; calls `tts.synthesize(req.voiceId, body)`; maps `BinaryResponse` → `SynthesisResult` (audio bytes, format echoed from request, `charactersUsed` from `metadata.characterCost`). - `synthesizeStream(req)`: maps request, calls `tts.synthesizeStream(req.voiceId, body)`, returns `response.body` directly. - All errors flow through `mapElevenLabsError`. - [x] `src/tts/tts.schemas.ts` — Zod for `ElevenLabsTtsAdapterOptions`, `mapSynthesisRequest` input/output. - [x] `src/tts/tts.md` - [x] `src/tts/tts.test.ts` — happy-path synthesize, happy-path stream, voice-not-found, validation error, rate-limited with `retryAfterMs`, abort, `cause` chain preserved. ### 3.2 `stt` - [x] `src/stt/stt.ts` — `ElevenLabsSttEngine` implements `TranscriptionEngine`. - `transcribe(req)`: maps `TranscriptionRequest` → ElevenLabs STT multipart body (audio: `Uint8Array | ReadableStream | string` passed through to third-party `MultipartFile`); calls `stt.transcribe(body)`; maps response to `TranscriptionResult` (words, derived speakers, entities split from word-stream). - [x] `src/stt/stt.schemas.ts` - [x] `src/stt/stt.md` - [x] `src/stt/stt.test.ts` — transcribe from bytes / stream / URL, with diarization, with entity detection, error remapping. ### 3.3 `voices` - [x] `src/voices/voices.ts` — `ElevenLabsVoiceRepository` implements `VoiceRepository`. - `list(opts)`: calls `voices.list({ ...opts })`; maps each ElevenLabs voice DTO → `Voice`; `nextPageToken`/`hasMore` derived from response. - `get(id)`: calls `voices.get(id)`; remaps `THIRD_PARTY_ELEVENLABS.REQUEST.NOT_FOUND` → `null` (per `VoiceRepository.get` contract); other errors flow through `mapElevenLabsError` with `voiceId` context. - [x] `src/voices/voices.schemas.ts` - [x] `src/voices/voices.md` - [x] `src/voices/voices.test.ts` ### 3.4 `sound-effects` - [x] `src/sound-effects/sound-effects.ts` — `ElevenLabsSoundEffectEngine` implements `SoundEffectEngine`. - `generate(req)`: maps `SoundEffectRequest` → ElevenLabs sound-generation body; calls `soundGeneration.generate(body)`; maps response to `SoundEffectResult`. - [x] `src/sound-effects/sound-effects.schemas.ts` - [x] `src/sound-effects/sound-effects.md` - [x] `src/sound-effects/sound-effects.test.ts` ### 3.5 `voice-isolation` - [x] `src/voice-isolation/voice-isolation.ts` — `ElevenLabsVoiceIsolationEngine` implements `VoiceIsolationEngine`. - `isolate(req)`: maps `IsolationRequest` → ElevenLabs isolation multipart body; calls `audioIsolation.isolate(body)`; maps response to `IsolationResult`. - [x] `src/voice-isolation/voice-isolation.schemas.ts` - [x] `src/voice-isolation/voice-isolation.md` - [x] `src/voice-isolation/voice-isolation.test.ts` --- ## 4. Composite factory - [x] `src/factory/factory.ts` — `createElevenLabsVoice(options)` returns `{ client, tts, stt, voices, effects, isolation }`. - Accepts either an existing `ElevenLabsClient` or `ElevenLabsClientOptions` to construct one fresh. - Wires every engine to share the same client instance. - [x] `src/factory/factory.schemas.ts` — Zod for `CreateElevenLabsVoiceOptions`. - [x] `src/factory/factory.md` - [x] `src/factory/factory.test.ts` — factory returns five engines + client; engines share client; both option shapes (existing client vs construct fresh) work. --- ## 5. Index - [x] `src/index.ts` — barrel re-export of all five engines + factory + adapter-options types + `mapElevenLabsError` (for advanced callers). --- ## 6. Property-based tests (`tests/property.test.ts`) - [x] `AudioFormat` round-trip — every `@suaveplan/voice` `AudioFormat` value passes through to ElevenLabs `output_format` and back without modification. - [x] `SynthesisRequest` round-trip — arbitrary valid request maps to ElevenLabs body and remaps to equivalent `SynthesisResult` shape (subject to documented field mappings). - [x] Error remapping totality — every `ElevenLabsApiError` subclass produces a `VoiceError` for every documented operation; no unmapped paths. --- ## 7. Benchmarks (`src/__benchmarks__/voice-elevenlabs.bench.ts`) - [x] `ElevenLabsTtsEngine.synthesize` round-trip with mocked `fetchImpl` (no network). - [x] `ElevenLabsSttEngine.transcribe` round-trip with mocked `fetchImpl`. --- ## 8. Pre-publish gate - [x] `bunx turbo lint typecheck test --filter=@suaveplan/voice-elevenlabs` — green. - [x] `bunx turbo test:coverage --filter=@suaveplan/voice-elevenlabs` — 100/100/100/100. - [x] `bun run docs:check --filter=@suaveplan/voice-elevenlabs` — ≥ 90%. - [x] Banned-pattern scan in `src/` — zero hits. - [x] `README.md` exists at package root. - [x] Subpath exports count matches feature module count. - [x] No bare versions in `package.json`. - [x] `bunx turbo test --filter=...@suaveplan/voice-elevenlabs` — dependents pass (currently none). --- ## 9. Archive - [x] Fold spec deltas into `openspec/specs/voice-elevenlabs/spec.md`. - [x] Move `openspec/changes/voice-elevenlabs/` → `openspec/archive/2026-05-02-voice-elevenlabs/`. - [x] Publish to Verdaccio: `bun scripts/publish.ts --package @suaveplan/voice-elevenlabs`. - [x] Update `STATUS.md`. --- ## Audit Note (2026-05-02) **Status**: DEFERRED. State at audit: - The package directory `packages/audio/voice-elevenlabs/` does not exist on genesis. The teardown step (Section 0) is a no-op for the audit. - Upstream `@suaveplan/voice` is shipped (103 tests / 100% coverage / archived 2026-05-02). - Upstream `@suaveplan/third-party-elevenlabs` is **also deferred** (typecheck green after this audit's edits, but missing `src/index.ts`, factory implementation, property tests, benchmarks, README) — `voice-elevenlabs` cannot ship until that prerequisite lands. Resuming work: 1. Finish `third-party-elevenlabs` (see its audit note) so the third-party client surface is stable. 2. Then execute Sections 1–9 of this tasks file to scaffold `packages/audio/voice-elevenlabs/` and wire the five engines + factory.