feat: add experimental voice-steered design mode#11436
Conversation
Add `kilo design`, an experimental, dev-gated launch command for voice-steered live design iteration. It runs one in-process Kilo session driven by a continuous voice loop: speak (or type, in fake-voice mode), turns auto-segment on end-of-utterance and dispatch to the agent while edits stream back into a browser canvas. Escape interrupts and clears. - Pure core (protocol/reducer/metadata/orchestrator) with unit tests - Terminal voice console (voice-activity meter, rolling transcript, status) - Apple Speech sidecar (packages/kilo-voice-sidecar) with continuous listening, automatic silence-window segmentation, and a watchdog - Zero-install browser canvas fixture (packages/kilo-design-fixture) - Auto-approve session permissions; dispatch errors surfaced on the console
| if (event.properties.sessionID === session.sessionID) orchestrator.agentOpen() | ||
| }) | ||
| const offClose = yield* bus.subscribeCallback(Session.Event.TurnClose, (event) => { | ||
| if (event.properties.sessionID === session.sessionID) orchestrator.agentClose(event.properties.reason) |
There was a problem hiding this comment.
WARNING: Turn-close events can race with a new turn after Esc
TurnClose is session-scoped here, so after escape() a late "interrupted" close from the canceled turn will still call agentClose() even if the user has already started a fresh turn. Because the orchestrator does not track which turn the close belongs to, that stale close clears state.active for the new turn and lets subsequent input dispatch immediately instead of queueing. This can reorder or overlap turns whenever a new utterance arrives before the cancel fully settles.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| Effect.sync(() => { | ||
| offOpen() | ||
| offClose() | ||
| void orchestrator.stop() |
There was a problem hiding this comment.
WARNING: Async shutdown is dropped on exit
orchestrator.stop() is the only path that tells the sidecar/helper process to shut down and waits for Process.stop(...). Calling it with void inside Effect.sync means the command can finish and hit the top-level process.exit() before that promise resolves, leaving kilo-voice-sidecar or a custom --voice-helper-command running after kilo design exits.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| } | ||
|
|
||
| func setActivation(_ value: String) { | ||
| lock.async { self.activation = value } |
There was a problem hiding this comment.
WARNING: push-to-talk is advertised but never changes capture behavior
This setter only stores the mode. Nothing in beginAudio(), startTimer(), or the stdin command handlers branches on activation, so --voice-activation push-to-talk still runs the exact same always-listening loop as continuous. Since the CLI exposes this as a supported choice, users opting into push-to-talk will get continuous microphone capture with no warning.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Other Observations (not in diff)Issues found in unchanged code that cannot receive inline comments:
Files Reviewed (29 files)
Fix these issues in Kilo Cloud Reviewed by gpt-5.4-2026-03-05 · 730,293 tokens Review guidance: REVIEW.md from base branch |
Adds an experimental
kilo designcommand for voice-steered browser editing.src/kilocode/designwith a changeset.loadingvoice lifecycle state used by the sidecar before listening begins.This is an exploratory MVP for evaluating the continuous voice-to-edit loop rather than a production feature.