Version: LifeOS 7.40.4 (ce046f2)
What
LIFEOS_CONFIG.toml is where the DA's voice is declared ([da.voices.main], required by LifeosConfig.ts), and hooks/lib/identity.ts already reads identity through loadLifeosConfig(). The VoiceServer never does: it loads voices from ~/.claude/settings.json daidentity.voices, a section the setup flow no longer writes. Result on a configured install:
- hook-driven lines (the "DA says" notifications) use the configured voice;
- every skill's
curl -X POST /notify (hundreds per day) resolves no voice, logs Voice settings: fallback defaults (no config found for main) and speaks with the hard-coded default voice;
- the user hears two different voices for one assistant.
Where
LIFEOS/PULSE/VoiceServer/voice.ts
- header comment lines 9–10: "look up in settings.json daidentity.voices … use settings.json daidentity.voices.main as default";
loadVoiceConfigFromSettings (line 209): join(homedir(), ".claude", "settings.json") (line 210), settings.daidentity || {} (line 220), daidentity.voices || {} (line 221); no read of LIFEOS_CONFIG.toml;
- line 566:
log("warn", \Voice settings: fallback defaults (no config found for ${voice})`)`.
LIFEOS/TOOLS/LifeosConfig.ts lines 166–168 define and require [da.voices.main].voice_id; hooks/lib/identity.ts line 37 loads through it.
Reproduce
- Set
[da.voices.main] voice_id = "<any ElevenLabs id>" in LIFEOS_CONFIG.toml; leave settings.json without a daidentity block (the current setup output).
- Start Pulse;
curl -s -X POST localhost:31337/notify -H 'Content-Type: application/json' -d '{"message":"test"}'.
- Read the VoiceServer log:
fallback defaults (no config found for main); the audio plays in the default voice, not the configured one. Trigger a hook notification: the configured voice plays.
Expected
One source of truth for voices: the VoiceServer resolves main and named voices from LIFEOS_CONFIG.toml through LifeosConfig.ts, the same way hooks/lib/identity.ts does, with settings.json at most a legacy fallback; no "fallback defaults" warning on a configured install.
Fix shape
voice.ts builds LoadedVoiceConfig from loadLifeosConfig().da.voices (voice id, name, model, stability per entry; main as the default), keeping the settings.json path only when the TOML has no voices. One function replaced; a PR can follow.
Version: LifeOS 7.40.4 (
ce046f2)What
LIFEOS_CONFIG.tomlis where the DA's voice is declared ([da.voices.main], required byLifeosConfig.ts), andhooks/lib/identity.tsalready reads identity throughloadLifeosConfig(). The VoiceServer never does: it loads voices from~/.claude/settings.jsondaidentity.voices, a section the setup flow no longer writes. Result on a configured install:curl -X POST /notify(hundreds per day) resolves no voice, logsVoice settings: fallback defaults (no config found for main)and speaks with the hard-coded default voice;Where
LIFEOS/PULSE/VoiceServer/voice.tsloadVoiceConfigFromSettings(line 209):join(homedir(), ".claude", "settings.json")(line 210),settings.daidentity || {}(line 220),daidentity.voices || {}(line 221); no read ofLIFEOS_CONFIG.toml;log("warn", \Voice settings: fallback defaults (no config found for ${voice})`)`.LIFEOS/TOOLS/LifeosConfig.tslines 166–168 define and require[da.voices.main].voice_id;hooks/lib/identity.tsline 37 loads through it.Reproduce
[da.voices.main] voice_id = "<any ElevenLabs id>"inLIFEOS_CONFIG.toml; leavesettings.jsonwithout adaidentityblock (the current setup output).curl -s -X POST localhost:31337/notify -H 'Content-Type: application/json' -d '{"message":"test"}'.fallback defaults (no config found for main); the audio plays in the default voice, not the configured one. Trigger a hook notification: the configured voice plays.Expected
One source of truth for voices: the VoiceServer resolves
mainand named voices fromLIFEOS_CONFIG.tomlthroughLifeosConfig.ts, the same wayhooks/lib/identity.tsdoes, withsettings.jsonat most a legacy fallback; no "fallback defaults" warning on a configured install.Fix shape
voice.tsbuildsLoadedVoiceConfigfromloadLifeosConfig().da.voices(voice id, name, model, stability per entry;mainas the default), keeping the settings.json path only when the TOML has no voices. One function replaced; a PR can follow.