configuration - fswolf/luna-local-agent GitHub Wiki
Configuration
Two files, two jobs.
| File | Holds | You edit it when |
|---|---|---|
agent/agent.json |
Who she is: name, personality, tone, traits, rules | You want her to behave differently |
config.json |
How the machine runs: voice, models, thresholds, timeouts, theme | You want it to work differently |
They used to be one file, which meant tuning a VAD threshold and rewriting her personality were the same edit, and you couldn't share either one without handing over the other.
Where both define a key, config.json wins. agent.json is still read
as a fallback, so an older install that never split them keeps working
untouched.
Every block in config.json is optional and every value has a default
in the config/ package (one file per area: voice.py, listening.py,
moods.py...), so a missing block means "use the defaults" rather than
an error. The file that ships has them written out explicitly, because
you can't turn a dial that isn't there.
{
"voice": "af_bella",
"generation": { "max_tokens": 4800, "reasoning": "low" },
"stt": { ... },
"tts": { ... },
"vision": { ... },
"desktop": { ... },
"mood": { ... },
"files": { ... },
"plugins": { ... },
"wake_word": { ... },
"tools": { ... },
"web_search": { ... },
"reminders": { ... },
"alarms": { ... },
"history": { ... },
"long_term_memory": { ... },
"thoughts": { ... },
"adaptive": { ... },
"monitor": { ... },
"self": { ... }
}
A syntax error in either file is reported and skipped rather than being fatal: hand-editing them is the whole point of their being JSON.
Changing settings without leaving the terminal
/set lists everything you can change, with its current value:
> /set
[stt]
stt.barge_in_margin 2.5
stt.vad_threshold 0.4
stt.model small (needs a restart)
[tts]
tts.speed 1.0
> /set stt.barge_in_margin 3.5
stt.barge_in_margin = 3.5 - saved
Changes are written to config.json immediately, so they survive a
restart. Most take effect at once; the few that don't (a Whisper model
size, a wake word file) say so rather than pretending.
That split is real, not cosmetic. Settings are read once at import and copied into constants, which is why they can't normally change at runtime. The ones worth tuning by ear are read from the config module at the point of use instead, because tuning is a loop of change it, say something, listen, change it again and a restart each time around makes it useless.
/mode saves too: a mode you picked and then lost on restart is just
an annoyance.
Logging
Writes to ~/.cache/ai-voice/ai-voice.log, rotating so it can't grow
without bound. /log tails it without leaving the app.
It records decisions, not just errors. "TTS failed" is a line anyone would write; the one that earns its keep looks like this:
[barge-in] fired | level=0.0412 baseline=0.0040 needed>0.0100
speech=0.96 threshold=0.65 held=0.35s
A baseline sitting exactly on the floor means calibration ran during silence, which is a bug that otherwise costs a screenshot and an hour to find.
"logging": { "enabled": true, "level": "info", "max_kb": 1024, "keep": 3 }
debug adds every VAD decision and tool argument, which is a lot;
info keeps what went wrong and the reasoning behind it.