Normalize the observed AI SILK container marker at the shared audio transcode boundary before invoking the bundled decoder. Preserve caller-owned bytes and leave ordinary SILK, AMR, and unrelated inputs unchanged.
Add unit and packaged-addon regression coverage for MP3 conversion, payload preservation, and input immutability.
Fixes#268
Replace the feature-specific MP3 verification command with a workspace-wide native test entry point.
Keep native regression cases in package-owned test suites so future checks can expand without adding workflow steps.
Use the dedicated audio conversion path for record output so mono QQ voice records produce valid MP3 data.
Add release-target smoke coverage and preserve conversion and cleanup failures for diagnostics.
Fixes#239
#165: get_record gains an optional `out_format` (mp3/amr/wma/m4a/spx/ogg/
wav/flac, mirroring NapCat). When set, the QQ voice (SILK/AMR) is
transcoded server-side and the result is attached as `data.base64`; the
original info (file/url/file_size) is preserved. Absent ⇒ behaviour
unchanged. This spares ASR consumers from shipping a SILK decoder.
- protocol: convertAudioBytes(bytes, format, deps?) drives the bundled
SILK-capable ffmpeg addon's convertFile through temp files (always
cleaned up), with a format allowlist and an output-size cap. Pure/
injectable, unit-tested with a fake addon (6 cases).
- core: ExtrasApi.convertRecord(source, format) = loadBinarySource (64 MiB
cap) → convertAudioBytes.
- onebot: get_record wires out_format; transcode failure (incl. an addon
lacking SILK decode) degrades to ACTION_FAILED, never crashes.
Adversarially reviewed: no path traversal (format allowlisted), no SSRF
(source is the cached CDN url, not user input), cleanup covers every path,
graceful degradation confirmed. Catalog regenerated. Real-machine e2e
(does the shipped .node actually decode SILK?) is Phase 2.