# Changelog

All notable changes to `nexo-plugin-voice` are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
the project adheres to [Semantic Versioning](https://semver.org)
once `v1.0.0` is tagged.

## [Unreleased]

## [0.3.0] — 2026-05-16

### Added

- **Stage 1 — pairing adapter contract completeness** (Phase
  81.20.x follow-up). Manifest now declares `[plugin.pairing]` +
  `[plugin.pairing.adapter]` + 3 broker subscribe topics
  (`plugin.voice.pairing.{normalize_sender,send_reply,send_qr_image}`).
  Voice is transform-only (TTS / STT) with no operator-facing
  pairing flow — the section is declared so the daemon's
  `GenericBrokerPairingAdapter` constructs uniformly across every
  canonical plugin; each handler returns a typed `not_supported`
  so a stray daemon-side `PairingChannelAdapter` trait call
  degrades gracefully. With this change voice declares 5/5
  manifest sections (pairing + http + admin + metrics + dashboard)
  plus the entrypoint + tools + config + credentials + supervisor
  + sandbox sections.

### Changed

- Bump version 0.2.0 → 0.3.0.

## [0.2.0] — 2026-05-16

### Added

- **Multi-instance config shape** (Wave A). `[plugin.config_schema] shape = "array"`.
  Operators may now declare an array of per-tenant voice config blocks; each
  block carries an optional `instance` label and `allow_agents` whitelist.
  Tool invocations route by `agent_id` to the matching tenant via the new
  `instance_registry::resolve` helper. Single-object YAML still parses via
  `VoicePluginShape::Single` and becomes one implicit `default` tenant for
  back-compat with 0.1.x deployments.
- **`[plugin.dashboard]` manifest section.** `workspace_walk` layout with
  `subdir = "voice"`, `auth_check.candidates = ["voice.db", ".nexo-paired"]`.
  Setup wizard surfaces a row per declared tenant.
- **`print_manifest_if_requested(MANIFEST)` in `main()`.** Honours the
  daemon's plugin auto-discovery flag — invoking the binary with
  `--print-manifest` dumps the embedded TOML to stdout and exits 0, so the
  daemon's discovery walker (Phase 81.33 Stage 8) can register the plugin
  without spawning the full subprocess wire.
- **`boot::apply_configure`** — sanitises tenant labels, diffs vs registry,
  rebuilds each declared tenant's `VoiceToolContext` via async factory, and
  records the `agent_id → tenant` routing table. Catch-all tenant marked
  automatically (first declaration with empty `allow_agents`, or first
  registered if none).
- **`instance_registry`** — process-wide `DashMap<label, Arc<VoiceToolContext>>`
  with `resolve(agent_id)` lookup + catch-all fallback. Mirrors the
  email plugin's shape so the daemon's
  `register_instance_subprocess_factories` helper targets voice too.

### Changed

- Broker subscribers (HTTP / admin / metrics) now lookup the live catch-all
  context per request instead of capturing a single one at boot. Per-tenant
  HTTP / admin routing is a Wave B follow-up.
- `VoicePluginConfig` gains `instance: Option<String>` and `allow_agents: Vec<String>`
  fields (default `None` / empty). YAML written for 0.1.x continues to load
  via `serde(default)`.

### Migration

`0.1.x` operators: no action required. The existing single-object
`voice:` YAML keeps loading as the implicit `default` tenant.

Multi-tenant deployments: change `voice:` to a YAML array. Each element is a
config block with an optional `instance` label and `allow_agents` list:

```yaml
voice:
  - instance: acme
    allow_agents: [agent-acme-main]
    default_voice: en-US-AriaNeural
  - instance: globex
    allow_agents: [agent-globex-main, agent-globex-billing]
    default_voice: es-CO-SalomeNeural
```

## [0.1.0]

### Added

- **Initial scaffold** (Phase voice-plugin-extract Fase B).
  Out-of-tree subprocess plugin under
  `/home/familia/chat/nexo-rs-plugin-voice/`. Mirrors the
  layout used by `nexo-rs-plugin-email` + `nexo-rs-plugin-telegram`.
  Cargo.toml with feature flags for 7 TTS providers (Edge default,
  ElevenLabs / Azure / OpenAI / Cartesia / Piper / Kokoro opt-in)
  plus `ffmpeg-transcode` (default) / `pure-rust-transcode`.
  Manifest TOML declares `[plugin.tools]`, `[plugin.http]`,
  `[plugin.admin]`, `[plugin.metrics]`, `[plugin.config_schema]`,
  `[plugin.credentials_schema]`, `[plugin.sandbox]`,
  `[plugin.capabilities.broker]`.

