fix(coding-agent): preserved btw codex websocket routing

- Preserved the session websocket preference for ephemeral /btw side-channel turns so Codex websocket-only models do not fall back to SSE.
- Prioritized active /btw and /omfg panels in Esc handling before loop, maintenance, and main-turn interrupts.
- Added regression coverage for side-channel websocket options and /btw Escape priority.

Fixes #5213
This commit is contained in:
roboomp
2026-07-11 17:31:37 +00:00
parent c6b83c1d96
commit 2c161d2a8f
5 changed files with 43 additions and 9 deletions
@@ -266,6 +266,8 @@ export class InputController {
});
}
this.ctx.editor.onEscape = () => {
// Side-channel panels are the topmost view. Esc dismisses them before
// touching loop mode, maintenance, or the underlying main turn.
// Active context maintenance owns Esc: auto/manual compaction,
// handoff generation, and auto-retry backoff all advertise
// "(esc to cancel)". Dispatch on live session state instead of
@@ -281,6 +283,13 @@ export class InputController {
// (see EventController). Main-session maintenance still owns Esc and
// stays cancellable from the main view (focused submit gates /compact
// and handoff, so manual maintenance is main-only anyway).
if (this.ctx.hasActiveBtw() && this.ctx.handleBtwEscape()) {
return;
}
if (this.ctx.hasActiveOmfg() && this.ctx.handleOmfgEscape()) {
return;
}
if (!this.ctx.focusedAgentId) {
const viewSession = this.ctx.viewSession;
let aborted = false;
@@ -308,12 +317,6 @@ export class InputController {
}
return;
}
if (this.ctx.hasActiveBtw() && this.ctx.handleBtwEscape()) {
return;
}
if (this.ctx.hasActiveOmfg() && this.ctx.handleOmfgEscape()) {
return;
}
if (this.ctx.focusedAgentId) {
// Esc never interrupts the focused agent's turn: clear typed text,
// else return the view to the main session. Interrupt via empty
@@ -14706,7 +14706,7 @@ export class AgentSession {
// stable, but give provider routing a unique request lineage.
sessionId: `${cacheSessionId}:side:${Snowflake.next()}`,
promptCacheKey: cacheSessionId,
preferWebsockets: false,
preferWebsockets: this.#preferWebsockets,
reasoning: toReasoningEffort(this.thinkingLevel),
disableReasoning: shouldDisableReasoning(this.thinkingLevel),
hideThinkingSummary: this.agent.hideThinkingSummary,