- Rename TTS backend "qwen3" -> "local" across tts.py and README; the
code is a generic /v1/audio/speech client, not qwen-specific, and
config.yaml.example already used the local: key.
- Guard multicast_listener against non-UTF8 and empty packets so a
single bad announcement packet can't cancel the pipeline via gather.
- Fix credentials.h.template comments to reference flash.sh (not the
old flash_firmware.sh name).
- Drop stray test.wav arg from serial_monitor.py usage example in
README; the script takes an optional serial port, not an audio file.
- git mv test_client, test_mic, test_speaker into tests/
- Add tests/test_stall.py (benchmarks the Gemini stall classifier against
conversational/fetch/capture/act/follow-up queries)
- Add tests/test_stream.py (raw SSE chunk inspection against the agentic
gateway)
- Update config path resolution in the new tests to climb one level
- Update README Testing section with new tests/ paths
- SSE sentence-level streaming: consume agent deltas, split on sentence
boundaries (handles no-space chunk joins), synthesize+send each sentence
as it forms; intermediate sends keep mic_timeout=0
- Gemini-backed stall classifier for agentic mode only: narrow to
retrieval-only, pass prev user/assistant for context awareness, avoid
action promises the stall can't honor, sub-second latency via
reasoning_effort=none
- Rename backends: local -> conversational, managed -> agentic
(files, classes, config keys)
- PTT interrupt fix: set device.interrupted when button-press frames
arrive mid-response and keep buffering so the next utterance captures
cleanly instead of being dropped
- Startup summary log showing ASR, LLM, STALL, and TTS config at a glance
- run.sh launcher with Homebrew libopus path for macOS
- voice_prompt config for per-turn agentic reminders; inline continuity
note injection so the agent knows what the stall just said aloud
- README section on streaming, stalls, and the first-turn OpenClaw caveat
- Rewrite README with v2 features (OpenClaw, M5 Echo, Opus, pluggable backends),
fold ARCHITECTURE.md and PIPELINE.md content inline
- Remove dev-only test scripts (streaming TTS, UDP recv, qwen3 bench, etc.)
- Remove redundant m5_echo/flash.sh and terminal.py (root scripts handle both)
- Consolidate credentials to .template naming, remove .example
- Embed parakeet-mlx ASR server as optional dependency (pipeline/services/asr_server.py)
- Default LLM to Claude Haiku 4.5 via OpenRouter, local example uses Gemma 4 E4B
- Update pyproject.toml with metadata, bump to 2.0.0
- Clean up .gitignore
Combine flash_firmware.sh and m5_echo/flash.sh into a single flash.sh
that takes a target arg (onjuino default, m5_echo). Auto-installs
required Arduino libraries (Adafruit NeoPixel, esp32_opus). Typing a
number at the WiFi SSID prompt now selects the corresponding network.
Added section explaining how to use flash_firmware.sh for:
- Compile-only mode (no ESP32 needed)
- Auto-detect and flash
- Flash to specific port
Emphasized using compile-only mode to verify code before committing.
🤖 Generated with Claude Code (https://claude.com/claude-code)