Merge PR #6220: fix(tts): prioritize playback interruption on Esc (@roboomp)
This commit is contained in:
@@ -166,6 +166,7 @@
|
||||
- Fixed Mnemopi consolidation re-storing cumulative session transcripts after incremental auto-retain, including after resuming a session ([#6058](https://github.com/can1357/oh-my-pi/issues/6058)).
|
||||
- Fixed turn-ending Codex rate-limit errors remaining hidden behind the fullscreen Plan Review overlay and leaving its approval promise pending ([#6086](https://github.com/can1357/oh-my-pi/issues/6086)).
|
||||
- Fixed prewalked subagents continuing to display their starting model after switching to the target model. ([#6083](https://github.com/can1357/oh-my-pi/issues/6083))
|
||||
- Fixed Esc aborting an ongoing agent turn instead of overlapping TTS playback, leaving speech uninterruptible ([#6118](https://github.com/can1357/oh-my-pi/issues/6118)).
|
||||
|
||||
## [17.0.5] - 2026-07-18
|
||||
|
||||
|
||||
@@ -308,6 +308,14 @@ export class InputController {
|
||||
if (aborted) return;
|
||||
}
|
||||
|
||||
if (vocalizer.isSpeaking()) {
|
||||
// Playback from the completed response can overlap the next agent
|
||||
// turn. Silence it before interrupting any ongoing main-turn work.
|
||||
vocalizer.clear();
|
||||
this.ctx.lastEscapeTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ctx.loopModeEnabled) {
|
||||
this.ctx.pauseLoop();
|
||||
if (this.ctx.session.isStreaming) {
|
||||
@@ -360,13 +368,6 @@ export class InputController {
|
||||
} else if (this.ctx.editor.getText().trim()) {
|
||||
// Esc must not destroy an in-progress draft.
|
||||
this.ctx.lastEscapeTime = 0;
|
||||
} else if (vocalizer.isSpeaking()) {
|
||||
// TTS buffers seconds of PCM past the streaming abort, so an Esc
|
||||
// arriving after the model stopped would otherwise fall through to
|
||||
// the double-Esc gesture while Kokoro reads on. Silence first;
|
||||
// tree/branch stays reachable via a second Esc.
|
||||
vocalizer.clear();
|
||||
this.ctx.lastEscapeTime = 0;
|
||||
} else {
|
||||
// Double-interrupt with empty editor triggers /tree, /branch, or nothing based on setting
|
||||
const action = settings.get("doubleEscapeAction");
|
||||
|
||||
@@ -669,6 +669,24 @@ describe("InputController escape behavior", () => {
|
||||
expect(ctx.showUserMessageSelector).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("silences TTS before aborting an overlapping agent turn (#6118)", () => {
|
||||
const clear = vi.spyOn(vocalizer, "clear").mockImplementation(() => {});
|
||||
vi.spyOn(vocalizer, "isSpeaking").mockReturnValue(true);
|
||||
const { ctx, editor, spies } = createContext();
|
||||
const pauseLoop = vi.fn();
|
||||
ctx.loopModeEnabled = true;
|
||||
ctx.pauseLoop = pauseLoop;
|
||||
mutableSessionState(ctx).isStreaming = true;
|
||||
const controller = new InputController(ctx);
|
||||
|
||||
controller.setupKeyHandlers();
|
||||
editor.onEscape?.();
|
||||
|
||||
expect(clear).toHaveBeenCalledTimes(1);
|
||||
expect(pauseLoop).not.toHaveBeenCalled();
|
||||
expect(spies.abort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("silences a still-audible vocalizer on Esc instead of opening the tree selector (#4521)", () => {
|
||||
const clear = vi.spyOn(vocalizer, "clear").mockImplementation(() => {});
|
||||
const isSpeaking = vi.spyOn(vocalizer, "isSpeaking").mockReturnValue(true);
|
||||
|
||||
Reference in New Issue
Block a user