Skip to content

Focus-stealing Windows Terminal windows from Claude/Codex: measured cause and Console Host mitigation #1

Description

@cloopadoop

Summary

On Windows 11, Claude Code/Codex console launches can be delegated to Windows Terminal even when the calling application did not intentionally open a terminal UI. The brokered WindowsTerminal.exe window can briefly become the foreground window and steal keyboard focus.

A window-event trace identified the sequence reliably and a reversible switch to Windows Console Host eliminated the observed Windows Terminal foreground transitions.

Environment

  • Windows 11 Pro, build 26200
  • Windows Terminal 1.24.11911.0
  • Claude Desktop 1.25927.0.0
  • Codex Desktop 26.730.7989.0
  • Claude CLI involved in the trace: %USERPROFILE%\.local\bin\claude.exe

Measured symptom

A SetWinEventHook trace recorded three occurrences in about 18 seconds:

OBJECT_CREATE  WindowsTerminal.exe  CASCADIA_HOSTING_WINDOW_CLASS  "Windows Terminal"
FOREGROUND     WindowsTerminal.exe  CASCADIA_HOSTING_WINDOW_CLASS  "Windows Terminal"
OBJECT_SHOW    WindowsTerminal.exe  CASCADIA_HOSTING_WINDOW_CLASS
OBJECT_CREATE  claude.exe           PseudoConsoleWindow
OBJECT_SHOW    claude.exe           PseudoConsoleWindow

The same pattern repeated with new Windows Terminal top-level window handles. The window title later changed to claude or Terminal.

PowerToys “Always on Top” did not help because it controls z-order, not foreground keyboard focus.

Things that did not solve it

  • Increasing ForegroundLockTimeout
  • Hiding the window after EVENT_OBJECT_SHOW (it can still flash first)
  • Windows Terminal "windowingBehavior": "useAnyExisting"
    • The existing window was reused, but Terminal restored/activated it, so focus behavior was effectively unchanged.
  • Broad title matching such as “hide any window titled claude.exe
    • This is unsafe: it can match brokered Terminal windows, Explorer dialogs, or legitimate Electron surfaces.

Working mitigation

Back up the current values under:

HKCU\Console\%%Startup
  DelegationConsole
  DelegationTerminal

Then select Windows Console Host by setting both values to:

{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}

The original Windows Terminal values on this machine were:

DelegationConsole  = {2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}
DelegationTerminal = {E12CFF52-A866-4C77-9A90-F570A7AA2C6B}

Microsoft documents these as the supported per-user default-terminal values:
https://learn.microsoft.com/en-us/windows/terminal/group-policy#default-terminal-application

Windows Terminal remains available when opened manually.

Guard safety boundary

After switching to Console Host, the window guard should only hide genuine console surfaces:

  • ConsoleWindowClass
  • PseudoConsoleWindow

It should additionally require proven Codex/Claude/editor ancestry. It should not hide arbitrary:

  • CASCADIA_HOSTING_WINDOW_CLASS
  • Chrome_WidgetWin_*
  • Intermediate D3D Window
  • Explorer #32770 dialogs
  • windows based only on a title containing claude.exe

For launch paths that honor process-local PATH, GUI-subsystem wrappers for cmd.exe, powershell.exe, and pwsh.exe can forward to the real executables with CREATE_NO_WINDOW. The event-based guard remains a fallback for MSIX/app activation paths that bypass the wrapper environment.

The guard also needs a reliable login start mechanism. In this case it was not running after reboot, so a per-user Run entry for the GUI-subsystem guard executable was used (not a scheduled task).

Result

  • Before: 3 Windows Terminal foreground transitions in 18 seconds
  • After selecting Windows Console Host: 0 Windows Terminal foreground transitions during a 35-second observation window
  • Claude and Codex did not need to be terminated for the registry change.
  • No reboot was required for new console launches.
  • User follow-up: “So far so good.”

Suggested upstream changes

  1. Have status.ps1 report the default-terminal delegation mode prominently.
  2. Offer an explicit, reversible Console Host mitigation with exact-value backup/restore.
  3. Keep the guard console-class constrained; avoid process-name/title-only hiding.
  4. Cover direct pwsh.exe as well as powershell.exe.
  5. Provide a reliable, non-console login-start option for the native guard.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions