Repository navigation
v0.6.0
A minor release. The wire vocabulary only gains: fates and setupFates are new fields in two responses, and nothing a caller sends has changed shape. Three behaviours did change, and an orchestrator written against 0.5.0 should check them:
okonPOST /api/commandis false only when a command was lost. A command the server keeps for the next renderer isretained, and with no viewer attached that is stillok: true.- The control server refuses a request whose
Hostis not127.0.0.1orlocalhost, whoseOriginis not the address it was sent to, or whoseSec-Fetch-Siteiscross-siteorsame-site. A caller with no browser behind it sends noOriginand is unaffected. wearwith aslotand noitemleaves the slot alone. Onlyitem: nulltakes a garment off.
The server's queue is the queue
Every turn goes through the control server's queue, a say posted to /api/command included. POST /api/command answers with one fate per command: delivered to at least one viewer, retained by the server for whichever renderer attaches next, or lost. The setup, the queue verbs, BGM transport and the effect of a cue are all retained, so a renderer that opens mid-show is handed the shot, page, mood and music the others already have. POST /api/scripts/run reports its setup the same way as setupFates, and yarn ctl play, the panel and the native shell warn only about a setup command that was actually lost.
A caller's id on a say is honoured while no turn holds it and replaced with a fresh one otherwise; ids in the response says which. A rewind with mode: from that cuts the line on air puts that line back as well, after the lines it followed. JSON bodies are capped at 4 MiB and refused on their declared length before they are read.
Loopback, checked at the door
Binding to 127.0.0.1 kept other machines out but not a page open in the operator's own browser, which can reach loopback by naming it from another origin or by rebinding its own hostname onto it. Both arrive with a giveaway in Host, Origin or Sec-Fetch-Site, and src/server/guard.ts refuses them with 403 before any route, file or stream is touched. No CORS header is sent on the refusal or anywhere else.
The voice
HASHIDATE_TTS_MODEL picks the sidecar's checkpoint: small (Irodori-TTS v4.1-Small, the default), large (v4-Large) or mf (a MeanFlow distillation of small that samples at 4 steps instead of 16). Large is closer to the reference speaker and takes about twice as long per line, roughly the length of the line itself on an M5 Max, which a queued script hides and a caller sending one turn at a time does not. All three share the codec, so the encoded reference set serves any of them. Any other value is refused at startup.
The model now loads after the socket is up, so /health answers ready: false for the whole load instead of leaving requests hanging, and turns true only once the watermark self-test has passed on a real take. A sidecar whose model cannot load or whose self-test fails prints the error and exits. On the renderer's side, a /health reply without a boolean ready counts as no answer.
A line now waits five seconds plus its own spoken length for its voice, so a long line is no longer discarded against a short line's deadline. A line the voice answered without a take is asked for once more when the voice next succeeds, and a line that leaves the queue unsaid (interrupt, clear, replacement, teardown) has its synthesis aborted rather than finished for nobody.
Motion
- Every outgoing gesture fades at its own weight, and each gesture slot has its own elbow solution. Switching gestures quickly no longer drops one pose or re-raises another, and an arm no longer snaps between two elbow positions mid-handoff.
- A sustained pose held past its hold eases out from the moment it is released. It used to drop to rest in one frame.
- Breath depth follows its target across the end of a line instead of changing 30% in a single frame.
- The spring solver samples the body at each fixed step's own time. At 50 Hz a frame sometimes takes two steps, and both used to see the same body pose, which made hair and cloth step unevenly (1-1-1-1-2). One step per frame at 60 Hz is unchanged, and a recorded trajectory pins it.
- The tail's slower wag term keeps its own phase and no longer restarts when the main phase wraps.
- Lid droop eases in and out, an idle face hands back through the preset fade, and a rig with a single-eye blink shape blinks through it.
What a descriptor can declare
armRestsets resting arm directions for a garment that needs the arms held clear of the body, such as a full skirt. Gestures blend from it; slots left out keep the standing pose.anchoron a spring group attaches the group's root to a point skinned across bones that earlier groups already simulate, so a part of a garment follows the skin it sits on. An anchor that cannot be built, or that overlaps another group's bones, is reported inProfile.missingand the group is dropped.reflectionon a preserved PBR material sets how strongly it reflects the runtime's studio environment, which is built once and shared between rooms and avatar materials. Without it a metal sees only the scene environment, which is empty without a backdrop.
A third avatar, Yonagi Neru (neru), is registered after Yoka and Manuka, and Yoka stays the default. Its GLB comes from a separate production workspace and is not redistributable either.
Also in this release
- The MCP
statustool returns the expression, performance, gesture and per-arm strain, so a caller can see whatreactchanged and whether an aim was met. yarn ctlrefuses a malformed number rather than truncating it or reading it as zero, and--pageand--slidetake whole numbers.weartakes its item positionally or as--item, withnoneto take it off.vocablists backdrops, voice presets and wear presets.- A script validation error counts array positions from 1, as the outline numbers lines.
GET /api/motionslists a malformed motion, or a file whose name is not a usable id, undererrorsinstead of dropping the whole list.- The camera vocabulary is derived from the wire schema and can no longer drift from what the wire accepts.
- A page command that arrives while a deck is still opening is kept and applied once the page count is known.
- A stop names the take it ends, so a late stop for an earlier take leaves a newer one running. A page whose browser has not yet allowed audio still records.
- The console's stop goes through the server's interrupt, so the server queue empties with it.
- The panel polls state one read at a time, keeps the mood and look sliders under the pointer while the renderer catches up, keeps an open draft when its line goes on air or is dropped, warns about unknown gestures and expressions even when the vocabulary is empty, and names a take only after a script in
show/scripts/. - The native shell keeps a control server it started that answers just after the startup wait gave up, and stops it on quit.
make glbfails on a Blender error, a missing export marker or an empty file. A relativeHASHIDATE_TTS_SOCKETis made absolute once in the Makefile, so the sidecar and the control server read one path.make check-assetsruns on the bash 3.2 macOS ships and also measures the staged blob.- Dependencies: vitest 5, three r186, libsonare 1.8.1, Node 22.23.3 and Yarn 4.18.1. The speech sidecar's Irodori-TTS pin moved one upstream commit; on MPS the voice is unchanged.