Skip to content

v0.4.0

Choose a tag to compare

@libraz libraz released this 30 Aug 20:57
· 122 commits to main since this release

The first release since 0.3.0 that a caller can feel. Nothing here requires action on the orchestrator side, but four things behave differently enough to be worth reading before upgrading.

A command batch is decoded element by element

commandRequestSchema and streamMessageSchema parse each element on its own instead of accepting or rejecting the batch whole. An element a peer does not know now costs that element and not the request around it; a body with nothing known still fails with 400 no command, and a stream frame stays valid even when none of it is recognised, because the stream has to keep flowing. parseCommandRequest and parseStreamMessage hand the rejected elements back, so a caller that wants to report them can.

Which commands spend id on their own payload is named once in PAYLOAD_ID_COMMAND_NAMES, so the server cannot drift into stamping a correlation id onto one of them.

The CLI gains voice

yarn ctl voice <preset> and yarn ctl voice --bypass send the existing voice command. The detailed dsp fields stay on the API alone: a processing chain is judged by ear against a render, so the CLI carries the choice a person makes and not the knobs behind it.

point now fails on a third numeric positional, naming the extras, rather than dropping them silently.

A take and the line on air belong to a renderer

Several renderers at once is the normal case here — the panel's preview, the stage window, whatever OBS has open — so ownership is now something the server holds rather than something inferred from one open connection. A renderer names itself on /api/stream and on every recording chunk; a second renderer posting into an owned session is answered with a conflict and its bytes never interleave. A renderer that disappears mid-take closes the file on a bounded watchdog instead of leaving it open for the life of the process, and a sink failure is retained and reported on the recording instead of reading as a take in progress with no bytes.

On the viewer side the stream is tied to the page rather than to the loaded model, so an avatar swap or a StrictMode remount is still one renderer, and a slow or failed load is a connected renderer saying what went wrong.

One path guard for every file-backed surface

The BGM library, the deck and motion rosters, the script loader and the static server share src/files.ts. Every path component is walked with lstat rather than stat, so a symlink or a non-directory component is refused instead of followed, and two normalization-equivalent directory entries are an error rather than a race decided by directory order.

That change also shipped a regression in which /, /panel/ and /monitor/ were answered with 404 — a directory has no extension, and the name check demanded one before the directory-to-index branch could run. It is fixed in this release, and the prefixless document root is now covered by the suite.

Validation tightened where a caller can feel it

  • tune is strict at the command and at every group, since a misspelled knob that strips to an empty patch reads as a successful no-op.
  • say.reading must be non-empty.
  • A 2xx answer from the speech sidecar whose media type is not audio/* is refused rather than retained as a line and read back by every renderer asking for that sentence.
  • A BGM track is taken through bgmTrackIdSchema, so a name that is a path or carries no .mp3/.flac extension is refused where the caller can still read why.

Also in this release

The speech sidecar hardens its socket — a stale path is removed only after a connection is refused and the node re-checked, and the socket's parent directory is created at 0700 or validated rather than chmod-ed. The panel reads take output and avatar phase from the server instead of from its own controls, and no longer discards a line when Escape ends an IME conversion. The MCP tool inputs are rebuilt from the schemas they have to match. The guides are back in step with the runtime in both languages.