- speak_tool.go: the speak tool itself — rune-derived timeout budget, structured
failure codes, persona/emotion stamped on the synthesis ctx, audio persisted
through the store seam, structured result for the model.
- voice_deps.go: the wiring seam (VoiceSynth / VoiceStore + VoiceStoreFunc
adapter), voiceEnabled, withSpeakSpec (appended after every schema shrink),
and captureVoice (success beats failure, last one wins).
- options.go / loop.go: VoiceSynth, VoiceStore, SpeakBudget injection points.
- request.go: advertise speak on both toolSchemas return paths.
- tools.go / tools_invoke.go: carry the batch voice outcome; split invokeOne /
emitToolExecution / spanStatusForToolPhase / invokeParallel into their own
file (tools.go was at the 500-line ceiling) and let speak run without a
dispatcher.
- run.go: keep the voice outcome across tool batches (a later batch without
speak must not erase it).
Known, deliberate: speak bypasses the capacity allow-set and skill-round
narrowing (it belongs to no built-in toolset), so it is also advertised in
quiet/awake; execution still passes the tool security gate, where speak is not
registered and non-owner actors are denied fail-closed.