Skip to content

Latest commit

 

History

History
334 lines (242 loc) · 11.6 KB

File metadata and controls

334 lines (242 loc) · 11.6 KB

Local Automation Patches

This fork of Windows Terminal exists to support trusted local automation workflows in developer-controlled environments. It is not positioned as an upstream-approved security model and it is not intended to be a general remote-control distribution of Windows Terminal.

Why This Fork Exists

Stock Windows Terminal does not provide a public command-line surface for sending text into an already running terminal window. This fork adds that missing control surface so automation callers can:

  • send text into an existing Windows Terminal window
  • optionally press Enter after the text
  • use a safe default submit gap for normal text when Enter is requested
  • target a specific existing WT window deterministically
  • identify the current WT window directly from a patched shell
  • inventory running WT windows through a file-based JSON query

What This Fork Adds

wt send-input

New command-line action:

wt.exe -w <target> send-input [--escape] [--enter] [--enter-delay-ms <ms>] [--activate] -- <text>

Added behavior:

  • send-input
  • --enter
  • --enter-delay-ms <ms>
  • --activate

Existing-window send-input stays in the background by default unless --activate is used.

Explicit typed existing-window selectors

This fork also supports explicit typed selectors for -w:

  • hwnd:0x<HWND>
  • id:<window-id>
  • name:<window-name>

These selectors are intended for automation callers that already know the exact WT target they want to address.

Explicit selectors fail closed if they do not resolve. They do not silently create a new WT window.

Patched shell env vars

Patched WT shells now expose:

  • WT_PATCHED=1
  • WT_WINDOW_SELECTOR=hwnd:0x...
  • WT_WINDOW_HWND=0x...
  • WT_WINDOW_ID=<id>
  • WT_WINDOW_NAME=<name> when the WT window has a name

These vars are intended for shell-local tooling that needs to identify the current WT window without Win32 foreground probing.

Notes:

  • WT_SESSION is still the stock WT session GUID.
  • window-scoped vars are launch-time snapshots for that shell session.
  • if a tab/window is later torn out, renamed, or moved, already-running child processes do not get refreshed env vars.

wt list-windows

New query action:

wt.exe list-windows

This writes a JSON inventory of running WT windows to stdout.

Current empty result:

{"windows":[]}

Current per-window JSON fields:

  • selector
  • hwnd
  • id
  • name
  • title
  • processId
  • isFocused
  • tabCount
  • paneCount (leaf panes across all tabs in the window)

Command Reference

Primary command shape

wt.exe -w <target> send-input [--escape] [--enter] [--enter-delay-ms <ms>] [--activate] -- <text>
wt.exe list-windows

Parameters

  • send-input
    • sends text to the active pane in the targeted command context
  • --escape
    • treats the provided text as escaped sequences
    • useful for control characters such as \u0015
  • --enter
    • sends Enter after the text
    • for normal text payloads, this uses WT's default safe submit delay
  • --enter-delay-ms <ms>
    • overrides WT's default submit delay
    • only needed when a caller wants custom timing
  • --activate
    • opts back into normal foreground activation behavior
    • existing-window send-input otherwise stays background by default
  • --
    • ends option parsing so command text such as /quit is treated as literal input
  • list-windows
    • writes the current WT window inventory as JSON to stdout

Target selectors

  • -w 0
    • follows WT's normal "current existing window" behavior
    • convenient for quick local tests, but not the preferred automation selector
  • -w last
    • WT's most recently used existing-window selector
  • -w <name>
    • target a named WT window
  • -w <id>
    • target WT's internal numeric window ID
  • -w hwnd:0x<HWND>
    • preferred automation-grade selector when a helper already knows the exact WT handle
  • -w id:<window-id>
    • explicit typed form of the WT numeric window ID
  • -w name:<window-name>
    • explicit typed form of the WT window name

Behavior notes

  • explicit typed selectors fail closed if the target does not resolve
  • explicit typed selectors are the preferred deterministic targeting surface for automation
  • current-shell discovery can now use WT_WINDOW_SELECTOR
  • external discovery can now use wt list-windows
  • For explicitly attended legacy/unmanaged Codex prompt submission, plain --enter is the preferred terminal-input path.
  • --enter-delay-ms <ms> is an advanced override for callers that want custom timing.
  • Attended legacy/unmanaged automation that targets Codex should not rely on terminal titles. Codex can change titles or statusline text while a turn is running, waiting for approval, compacting, or showing action-required state. Prefer the current shell's WT_WINDOW_SELECTOR or a saved hwnd:0x... selector, and re-check wt list-windows before delivery if the target may have moved.
  • send-input remains a deterministic terminal primitive for shell startup, restart/resume lifecycle, and explicit attended legacy/unmanaged workflows. It does not claim ownership of Codex semantic delivery. Callers with a native session/control API should use that API for ordinary prompts, goals, steering, interaction responses, or managed handover.

Examples

Normal shell command

wt.exe -w hwnd:0x123456 send-input --enter "echo READY"

Attended legacy/unmanaged Codex prompt submission

wt.exe -w hwnd:0x123456 send-input --enter "Please reply exactly with TEST_OK."
wt.exe -w hwnd:0x123456 send-input --enter -- "First line; still payload`nSecond line"

Use -- before arbitrary prompt text, especially multiline text or text that contains semicolons. After send-input --, semicolons are payload characters, not WT command separators.

These examples exercise the terminal primitive; they are not the supported semantic path for a natively managed Codex session. For an explicitly attended legacy/unmanaged run, get the target from WT_WINDOW_SELECTOR or wt.exe list-windows first. Avoid matching on a visible title because action state, approval prompts, compaction, and dynamic status text can change it between discovery and delivery.

Restart or explicit legacy/unmanaged Codex quit

wt.exe -w hwnd:0x123456 send-input --enter -- "/quit"

Do not infer managed-session retirement semantics from this terminal example; use the owning session/control API when one exists.

Window-name targeting

wt.exe -w name:codexdev send-input --enter "echo READY"

Inspect current patched-shell variables

Get-ChildItem Env:WT_* | Sort-Object Name
$env:WT_WINDOW_SELECTOR

Window inventory to JSON

wt.exe list-windows

Discover, choose, then target

$windows = (wt.exe list-windows | ConvertFrom-Json).windows
$focused = $windows | Where-Object isFocused | Select-Object -First 1
wt.exe -w $focused.selector send-input --enter "echo READY"

Current patched shell window selector

$env:WT_WINDOW_SELECTOR

Window inventory to JSON

wt.exe list-windows

Intended Use

This fork is intended for:

  • trusted local automation
  • developer-controlled workflows
  • callers that already identify the intended WT window explicitly

It is not intended as a blanket answer to upstream terminal automation for arbitrary local applications.

Upstream Status

An upstream PR was opened to demonstrate the feature shape and implementation approach:

That PR was closed without merge. The maintainer concern was the security model of allowing general input injection into running Windows Terminal instances.

This fork remains valuable locally because:

  • it proves the workflow
  • it provides a concrete command surface
  • it supports real automation use cases in a trusted local environment

Building This Fork

This repo does not ship binaries from the fork itself. Build locally.

Prerequisites

Use the normal upstream Windows Terminal prerequisites described in README.md, especially:

  • Visual Studio with the required workloads/components
  • the Windows SDK expected by the repo
  • any repo bootstrap prerequisites documented upstream

Build the Windows Terminal package

Example from this fork's current working flow:

& 'C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe' `
  '<repo-root>\OpenConsole.slnx' `
  /p:Configuration=Release `
  /p:Platform=x64 `
  /p:WindowsTerminalBranding=Dev `
  /p:AppxSymbolPackageEnabled=false `
  /t:Terminal\CascadiaPackage `
  /m `
  /nodeReuse:false

Register the locally built package

powershell.exe -NoProfile -Command "Add-AppxPackage -ForceApplicationShutdown -ForceUpdateFromAnyVersion -Register '<repo-root>\src\cascadia\CascadiaPackage\bin\x64\Release\AppxManifest.xml'"

This fork's maintained local install shape is Dev-branded, which exposes wtd.exe. Preview/Release-branded packages typically expose wt.exe. Do not switch branding or alias shape unless that is a deliberate test.

After registration, verify the generated manifest and execution aliases before testing:

Get-Command wtd.exe -ErrorAction SilentlyContinue
Get-Command wt.exe -ErrorAction SilentlyContinue
wtd.exe list-windows

The expected steady state for the Dev-branded install is that wtd.exe resolves and wt.exe does not, unless a stock Windows Terminal package has intentionally been installed side by side.

Upstream Merge Verification

For routine upstream merges, prefer focused verification that covers this fork's automation surfaces without running the full visible app-host suite:

  • build the Dev-branded package
  • register or refresh the built package from src\cascadia\CascadiaPackage\bin\x64\Release\AppxManifest.xml
  • confirm wtd.exe list-windows works
  • run the targeted localTerminalApp command-line tests, for example Invoke-OpenConsoleTests -Test localTerminalApp -Platform x64 -Configuration Debug -TaefArgs @('/name:TerminalAppLocalTests::CommandlineTest::*')
  • run unitSettingsModel
  • run unitControl
  • run a live wtd.exe send-input --enter -- <payload> smoke against a disposable terminal window

The full localTerminalApp suite launches the TAEF/UWP TestHostApp repeatedly. Visible TestHostApp windows and desktop flicker are expected during that suite, and do not indicate that stock Windows Terminal was installed or that the fork is launching unexpectedly.

Only run the full localTerminalApp suite when the change touches app lifecycle, UI hosting, windowing, settings UI, or behavior that specifically needs broad app-level coverage. For send-input, selector, alias, and package-refresh checks, use the targeted command-line tests and live smoke instead.

Notes For Maintainers Of This Fork

  • The preferred automation-grade target identity is hwnd:0x....
  • The preferred current-shell identity is WT_WINDOW_SELECTOR.
  • The preferred external inventory path is wt list-windows.
  • Codex-oriented prompt flows should normally use plain --enter.
  • Keep --enter-delay-ms <ms> for custom timing, diagnostics, or non-default app behavior.
  • Keep tracked examples generic. Do not add credentials, private paths, machine/user names, or local-only Codex workflow details to this public fork.
  • Track Codex remote-control separately as an experimental alternative for future managed prompt delivery. Do not remove patched WT shell/handover coverage until remote-control proves parity for startup, attach, /quit, resume, and interruption flows.