feat(shell): self-report shell identity via OSC 9001;ShellType#345
Conversation
Shells now emit OSC 9001;ShellType;<name>;<version> on every prompt so the terminal always knows which shell owns a pane, even after a nested shell (pwsh -> wsl -> exit) returns. Replicates the WorkingDirectory data flow: adaptDispatch -> ITerminalApi::SetShellType -> Terminal -> ControlCore -> ICoreState -> TermControl -> PaneInfo -> COM JSON (shell/shell_version), so wtcli list-panes / get-active-pane expose the live shell per pane. PowerShell reports pwsh/powershell; bash reports bash or wsl:<distro>. Fixes autofix recommending PowerShell commands in a WSL/bash pane: the autofix Shell Context now prefers the OSC-reported shell over the pid-based process-image lookup, which can't see the real foreground shell inside a nested wsl/pwsh host process. Also removes the dead OSC 9001 WtaReq/WtaRes in-band handler (superseded by the out-of-band COM IProtocolServer channel). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adds a new shell-integration signal (OSC 9001;ShellType;<name>;<version>) that lets the foreground shell report its identity on every prompt, and propagates that information through the Terminal core, control/app layers, and the Terminal Protocol COM JSON so WTA/autofix can reliably choose shell-appropriate fixes (especially across nested shells like pwsh → wsl → exit).
Changes:
- Adds
ShellTypehandling in the VT action dispatcher and a newITerminalApi::SetShellTypeplumbing path to store shell name/version inTerminaland expose it throughControlCore/TermControlinto protocolPaneInfoand COM JSON (shell,shell_version). - Updates PowerShell and bash/WSL shell integration snippets to emit
OSC 9001;ShellTypeeach prompt. - Updates WTA’s pane JSON parsing to prefer the OSC-reported
shellfield (with tests) and fall back to pid-based resolution when absent/empty.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/protocol/acp/client.rs | Prefer OSC-reported shell in active-pane JSON; adds unit test for precedence/fallback behavior. |
| src/terminal/adapter/ut_adapter/adapterTest.cpp | Updates test mock ITerminalApi implementation with SetShellType. |
| src/terminal/adapter/ITerminalApi.hpp | Adds SetShellType(shellName, shellVersion) to the terminal API contract. |
| src/terminal/adapter/adaptDispatch.cpp | Implements OSC 9001;ShellType WT action parsing and forwards to ITerminalApi. |
| src/host/outputStream.hpp | Adds SetShellType to conhost internal API surface (no-op implementation). |
| src/host/outputStream.cpp | No-op SetShellType for conhost, documenting feature is WT-only. |
| src/cascadia/WindowsTerminal/TerminalProtocolComServer.cpp | Serializes pane shell and shell_version into protocol JSON. |
| src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp | Adds TerminalCore unit test validating ShellType parsing/state behavior. |
| src/cascadia/TerminalProtocol/TerminalProtocol.idl | Extends PaneInfo with Shell and ShellVersion. |
| src/cascadia/TerminalCore/TerminalApi.cpp | Implements Terminal::SetShellType and one-time telemetry event. |
| src/cascadia/TerminalCore/Terminal.hpp | Stores shell name/version and adds getters + ITerminalApi override. |
| src/cascadia/TerminalCore/Terminal.cpp | Implements GetShellName() / GetShellVersion() accessors. |
| src/cascadia/TerminalControl/TermControl.h | Adds ShellName() / ShellVersion() accessors to TermControl. |
| src/cascadia/TerminalControl/TermControl.cpp | Wires TermControl shell accessors through to ControlCore. |
| src/cascadia/TerminalControl/ICoreState.idl | Adds ShellName / ShellVersion properties to core state interface. |
| src/cascadia/TerminalControl/ControlCore.h | Adds ShellName() / ShellVersion() accessors to ControlCore. |
| src/cascadia/TerminalControl/ControlCore.cpp | Reads shell name/version under terminal read lock for the control layer. |
| src/cascadia/TerminalApp/TerminalPage.Protocol.cpp | Plumbs shell name/version into PaneInfo returned by protocol APIs. |
| src/cascadia/inc/PowerShellShellIntegration.h | Emits OSC 9001;ShellType each PowerShell prompt with edition + version. |
| src/cascadia/inc/BashShellIntegration.h | Emits OSC 9001;ShellType each bash prompt; reports wsl:<distro> under WSL. |
- TerminalApi.cpp: make the one-shot ShellType telemetry gate race-free (function-static logged is shared across Terminal instances holding different locks; switch to std::atomic<bool>::exchange). - Remove dangling references to the deleted doc/specs/shell-integration-and-osc9001.md spec from code comments (adaptDispatch.cpp, BashShellIntegration.h, client.rs). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Add a WSL-gated Describe to Feature.AutofixPane that exercises OSC 9001;ShellType end-to-end: Case 1 (deterministic) asserts a WSL pane self-reports shell=wsl:<distro> through the protocol (scoped by tab id, since list-panes is active-tab scoped); Case 2 (AI oracle) verifies autofix suggests Linux/bash syntax, not PowerShell, in a WSL pane. The whole Describe skips unless a dev package + copilot + winapp + a runnable WSL distro are present. Framework fix: UI-dependent suites gated only on package/copilot would throw a raw 'winapp not found' in BeforeAll when the Windows App CLI isn't installed, instead of skipping. Add a non-throwing Test-WinAppAvailable helper (exported) and fold winapp into the readiness gates of every agent-pane / FRE-overlay suite. Packaging keeps its §9 protocol Describe runnable without winapp; only the §10 UI Describe is winapp-gated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…s existing users The OSC 9001;ShellType emission was added to the v1 shell-integration scripts without bumping kVersion. The install orchestrator's block-match early-out (ShellIntegrationCommon.h:414) only rewrites the on-disk script when the \C:\Users\kaitao\OneDrive - Microsoft\Documents\PowerShell\Microsoft.PowerShell_profile.ps1/.bashrc sourcing block changes or the script file is missing. Since the block embeds the versioned filename and the filename was unchanged, existing users kept their stale v1 script (no ShellType) and the feature silently never reached them — verified live: a pwsh pane reported shell='' until the bump. Bump both PowerShell and Bash (WSL inherits via WslBashFlavor) to v2 so the block changes -> the orchestrator rewrites the script in place. Verified end-to-end: after deploy, shell-integration_v2.ps1 is written with the 9001 emission, the profile block is rewritten to reference it (with a backup), and a pwsh pane now reports shell='pwsh' 7.4.14. Add regression tests Install_/Bash_Install_UpgradesWhenBlockReferences- OlderScriptVersion: a managed block pointing at an older script version plus a stale on-disk script must upgrade (alreadyInstalled=false), and the rewritten script must contain the OSC 9001 emission. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@check-spelling-bot Report
|
| Dictionary | Entries | Covers | Uniquely |
|---|---|---|---|
| cspell:csharp/csharp.txt | 32 | 2 | 2 |
| cspell:aws/aws.txt | 232 | 2 | 2 |
| cspell:fonts/fonts.txt | 536 | 1 | 1 |
Consider adding to the extra_dictionaries array (in the .github/actions/spelling/config.json file):
"cspell:csharp/csharp.txt",
"cspell:aws/aws.txt",
"cspell:fonts/fonts.txt",
To stop checking additional dictionaries, put (in the .github/actions/spelling/config.json file):
"check_extra_dictionaries": []Warnings ⚠️ (1)
See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.
| Count | |
|---|---|
| 54 |
See
✏️ Contributor please read this
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
If the listed items are:
- ... misspelled, then please correct them instead of using the command.
- ... names, please add them to
.github/actions/spelling/allow/names.txt. - ... APIs, you can add them to a file in
.github/actions/spelling/allow/. - ... just things you're using, please add them to an appropriate file in
.github/actions/spelling/expect/. - ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in
.github/actions/spelling/patterns/.
See the README.md in each directory for more information.
🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉
If the flagged items are 🤯 false positives
If items relate to a ...
-
binary file (or some other file you wouldn't want to check at all).
Please add a file path to the
excludes.txtfile matching the containing file.File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.
^refers to the file's path from the root of the repository, so^README\.md$would exclude README.md (on whichever branch you're using). -
well-formed pattern.
If you can write a pattern that would match it,
try adding it to thepatterns.txtfile.Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
The feature and self-test Describes hardcoded -Package Store in their BeforeAll/It launch calls, so on a dev-only machine (only the sideload package installed) they could not run as-written. Route every launch through a new Get-ItTestPackage selector that honors the ITE2E_PACKAGE env var and defaults to Auto (prefer a resolvable Store install, else Dev). Start-TerminalFre's default param now uses the same selector. This lets the whole suite validate against the dev build without edits; set ITE2E_PACKAGE=Store to pin to the store build in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eout The Insert and Run autofix retry loops wrapped Wait-Autofix in try/finally WITHOUT a catch, so a single 45s Wait-Autofix timeout propagated and failed the test before the loop could try the next typo or reach the LLM-variance skip guard. The sibling card-render loop already swallows this with an empty catch. Add the same catch so an explain-only (no-card) run skips instead of hard-failing, matching the documented LLM-variance behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
What
Lets shells self-report their identity each prompt via the private
OSC 9001;ShellType;<name>;<version>sub-action, so the terminal always knows which shell owns a pane — even after a nested shell (pwsh->wsl->exit) returns, where there's no reliable "I exited" signal.Why
Autofix was recommending PowerShell commands (e.g.
Get-ChildItem) inside a WSL/bash pane. The autofix shell context derivedshellfrom the pane's pid -> process image name, which inside awsl.exe/pwsh.exehost can't see the real foreground shell drawing the prompt. The prompt is the natural "who's in charge now" signal, so the shell reports it on every prompt.How
PowerShellShellIntegration.hreportspwsh/powershell+ version;BashShellIntegration.hreportsbashorwsl:<distro>+ version (WSL reusesBashFlavor, so one edit covers Git Bash + every distro).WorkingDirectorydata flow end to end —adaptDispatch DoWTAction->ITerminalApi::SetShellType->Terminal->ControlCore->ICoreState->TermControl->PaneInfo-> COM_toJson(shell/shell_version).wtcli list-panes/get-active-panenow expose the live shell per pane.shell_from_active(autofix) now prefers the OSC-reportedshellover the pid lookup; pid stays as the fallback for panes without shell integration.kVersion(1 -> 2) so existing users actually receive the newShellTypeline — the install orchestrator only rewrites the managed block when the versioned script filename changes.WtaReq/WtaResin-band handler (superseded by the out-of-band COMIProtocolServerchannel).Test
UnitTests_TerminalCoreTerminalApiTest::SetShellType— feeds the OSC, assertsGetShellName()/GetShellVersion()(name+version, name-only, empty, last-writer-wins, no interference withCmdNotFound).ShellIntegrationTests*_UpgradesWhenBlockReferencesOlderScriptVersion— the kVersion bump reaches existing users (block referencing an older script version is rewritten; new script contains9001).shell_from_active_prefers_osc_reported_shell+ existingshell_from_active_resolves_pid— OSC field wins over pid; empty/whitespace falls back.Feature.AutofixPane.Tests.ps1, gated on a runnable WSL distro + the dev package): WSL pane self-reportswsl:<distro>through the protocol, and autofix suggests bash (not PowerShell) syntax.bcz no_cleanbuild: 0 errors. Manually verified on the dev package: a WSL pane reportswsl:<distro>and autofix suggests bash-syntax fixes.Closes #352 (context awareness: agent pane & autofix recognize the active WSL/bash shell). Part of #200.