Skip to content

May 23–30 production batch: halo/transcript, story engine, Suno SFX, voice+timeout fixes, OpenClaw 5.7#323

Merged
MCERQUA merged 10 commits into
devfrom
fix/halo-mood-contract-and-transcript-thinking
May 30, 2026
Merged

May 23–30 production batch: halo/transcript, story engine, Suno SFX, voice+timeout fixes, OpenClaw 5.7#323
MCERQUA merged 10 commits into
devfrom
fix/halo-mood-contract-and-transcript-thinking

Conversation

@MCERQUA
Copy link
Copy Markdown
Owner

@MCERQUA MCERQUA commented May 23, 2026

Summary

  • Halo-smoke orb visual stayed in dots animation after a turn ended because HaloSmokeFace never implemented the BaseFace setMood contract (only had ad-hoc setThinking)
  • Transcript thinking-bubble persisted above the rendered reply on the non-streaming response path because addMessage didn't call removeThinking the way startStreaming/finalizeStreaming do
  • BigHeadFace + EyeFace both unaffected (they implement the contract properly) — confirms the framework is sound; halo was the outlier

Changes

  • src/face/HaloSmokeFace.js — added setMood(mood) exported method mapping 'thinking' → dots, anything else → idle. setThinking(bool) side-channel kept for backwards compat with scattered direct callers in app.js
  • src/app.jsFaceModule.setMood now propagates to HaloSmokeFace the same way it already did to BigHeadFace
  • src/app.jsTranscriptPanel.addMessage calls removeThinking() for assistant messages before appending (matches what startStreaming already does)
  • src/face/manifest.json — halo-smoke's "moods": [] updated to the full VALID_MOODS vocab with a "moods_visual_collapse" note documenting the 2-state rendering

Scope

Single face brought into compliance with existing contract. No framework changes. No new abstractions. 23 lines added across 3 files.

Mike and others added 9 commits May 23, 2026 00:37
…ubble

Two related symptoms when running a turn on a non-streaming response path:

1. The thinking-dots bubble in the transcript persisted above the rendered
   reply. The streaming path (startStreaming/finalizeStreaming) already
   removes it, but the non-streaming path (data.response → addMessage)
   skipped the removal. Fixed in addMessage by calling removeThinking()
   for assistant messages before appending.

2. The halo-smoke orb face stayed in its dots animation after the response
   was rendered. Root cause: HaloSmokeFace never implemented the BaseFace
   setMood contract — it only exposed an ad-hoc setThinking(bool) side
   channel. FaceModule.setMood propagated to BigHeadFace but not to halo.
   When a turn ended and app.js called setMood('neutral'), halo never
   heard it. EyeFace and BHB were unaffected because they implement the
   contract properly.

Fix: add setMood(mood) to HaloSmokeFace that maps 'thinking' → dots and
anything else → idle, then propagate mood updates to it from FaceModule
the same way they're propagated to BigHeadFace.

manifest.json: halo-smoke's "moods": [] now lists the full VALID_MOODS
vocabulary with a "moods_visual_collapse" note describing the 2-state
rendering. Keeps the manifest honest about what setMood inputs are
accepted while documenting that the visual output collapses.

setThinking(bool) kept on HaloSmokeFace for backwards compatibility
with the scattered direct callers in app.js — those can be migrated to
setMood later, no urgency.

Affected paths: data.response (fast lane), websocket message events,
TTS finalize, abort handlers.

Tested on openvoiceui-azrim hot-patch — transcript bubble disappears
when reply renders, halo dots clear at end of turn.
POST /api/story/generate-scene generates the next story scene on demand:
- GPT-4o-mini writes scene JSON (title, image prompt, ambient, SFX, script, choices)
- FLUX.1-schnell generates scene image via HF router
- Suno V5_5 generates ambient loop + SFX clips in parallel
- Resemble Chatterbox pre-renders narration/dialogue per character voice
- Returns complete scene with resolved asset paths for the canvas player

All assets written to canvas-pages/stories/{story_id}/ with 777 permissions
so the node container process can write alongside host-created files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes sunoapi.org POST /api/v1/generate/sounds (2.5 credits, non-vocal
SFX/stingers) through the shared /api/suno wrapper as action=sfx, so any
agent/canvas can generate game sound effects without bespoke httpx code.
Previously only routes/story.py called this endpoint directly; the global
suno-music skill marked it 'not yet wrapped', so tenants like bhb (game
soundboard) couldn't reach it.

- _action_sfx: prompt (required, <=500) + optional title/loop/tempo/key →
  soundLoop/soundTempo/soundKey. model V5_5 to match the proven story.py path.
- Reuses _action_status for polling + download; added sourceAudioUrl as a
  download fallback (additive — songs/jingles still prefer audioUrl).
- Job registered with kind=sfx; clip saved to generated_music/ (server-side,
  never in-memory only).

Verified live in openvoiceui-bhb: V5 and V5_5 both generate + download end to
end (coin-blip + mallet-thwack mp3s saved to disk).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…L on failure

The 'Report an issue' button 500'd with a PermissionError writing to
/app/runtime/issue-reports (root:root 755 in the image — the dir was missing
from the Dockerfile mkdir list so it never got the chown -R appuser). Flask's
HTML 500 page then broke the frontend's res.json() with the cryptic
"Unexpected token '<', \"<!doctype\"" error.

- Dockerfile: add runtime/issue-reports to the mkdir before chown -R appuser
  (durable fix — created appuser-owned in the image).
- report_issue.py: wrap local save in try/except; on OSError still accept the
  report (forward to feedback service) and return clean JSON, never an HTML 500.
- app.js: guard res.json() so any non-JSON body yields a readable message
  instead of the JSON-parse error.

Live containers hotfixed via chown; source fixes bake in at next image rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The transcript showed '🔀 Redirected.' / 'Task redirected by user' on EVERY
aborted agentic request — including the 60s inactivity timeout that fires when
the agent goes silent during long work (subagent spawns, batch file ops). Users
saw 'redirected by user' when they did nothing.

Tag each abort with its cause (_abortReason: user | inactivity | stop) and
branch the label: only an explicit user interrupt says 'redirected by user';
inactivity shows a timeout notice; unknown/stop close the stream quietly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…run budget

The client aborted the stream after 60s of no data, but the openclaw gateway
run budget is 300s — so legitimate long silent work (subagent spawns, batch
file ops) got cut at the client well before the server finished, surfacing as
a (mislabeled) timeout/redirect. Match the client backstop to the server's
300s. Heartbeats (every 5-10s) still reset it in the normal case; 300s is the
hard ceiling. Deployed to all live OVU containers + baked in source so image
rebuilds keep it (it had silently drifted back to 60s everywhere).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…c library

action=sfx previously saved into generated_music/ and registered in the music
metadata + completed-songs queue, so generated sounds polluted the music
player. Now SFX:
- save to a dedicated generated_music/sfx/ subdir (served at /generated_music/sfx/,
  no mount/compose change — still under the mounted tree)
- skip _add_song_to_metadata + completed_songs_queue (music-only)
- omit callBackUrl so they complete via polling and never hit the webhook path
  that registers results as music

Music/song/jingle behavior is unchanged. _action_list already skips the subdir
(non-recursive iterdir + audio-suffix filter).
…start greetings

Captures work that has been LIVE on the fleet since 2026-05-23 but was only
hotpatched into containers, never committed — so a rebuild would silently
revert it (the same drift documented in the inactivity-timeout incident).
Verified live on dsf, test-dev, bhb, src, mike before committing.

Two changes:

1. Slow-empty Z.AI-direct fallback. The fast-empty retry only covered <5s
   empties and the double-empty branch only covered post-_retried empties,
   leaving a 5-30s gap where a single non-retried slow empty fell straight to
   "No response from agent after recovery" (observed on bhb: 16882ms + 17370ms
   empties both fell through). Threshold lowered 30000ms -> 5000ms; on a
   non-session-start slow empty we now try Z.AI direct (bypasses the gateway
   and any poisoned openclaw session) before the spoken apology.

2. Remove the 5 hardcoded session-start fallback greetings. They masked
   LLM-empty failures on __session_start__ with canned text, making the broken
   state invisible. Now: if the LLM returns empty and there is no
   profile-defined greeting, surface silence + a warning log so the failure is
   visible. A tenant-owned profile conversation.greeting still wins — that's
   config, not hardcoded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ller paths

Captures the OpenClaw version bump that is already LIVE in containers
(verified: openclaw-test-dev and openclaw-dsf both run "OpenClaw 2026.5.7")
but was never committed to source — so a rebuild would revert the pin.

Keeps the three pinned installer paths in sync per the bump-script rule:
  - deploy/openclaw/Dockerfile (ARG OPENCLAW_VERSION)
  - docker-compose.yml (OPENCLAW_VERSION build arg default)
  - setup-sudo.sh (OPENCLAW_TESTED_VERSION)
Plus services/gateways/compat.py OPENCLAW_TESTED_VERSION (warning-log baseline).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MCERQUA MCERQUA changed the title fix(face,transcript): halo-smoke stuck on thinking + duplicate dots bubble May 23–30 production batch: halo/transcript, story engine, Suno SFX, voice+timeout fixes, OpenClaw 5.7 May 30, 2026
@MCERQUA MCERQUA changed the base branch from main to dev May 30, 2026 03:42
@MCERQUA MCERQUA merged commit 96bed8c into dev May 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant