Skip to content

feat(shell): self-report shell identity via OSC 9001;ShellType#345

Merged
vanzue merged 6 commits into
mainfrom
dev/vanzue/what-is-shell
Jun 24, 2026
Merged

feat(shell): self-report shell identity via OSC 9001;ShellType#345
vanzue merged 6 commits into
mainfrom
dev/vanzue/what-is-shell

Conversation

@vanzue

@vanzue vanzue commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

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 derived shell from the pane's pid -> process image name, which inside a wsl.exe / pwsh.exe host 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

  • Emit: PowerShellShellIntegration.h reports pwsh / powershell + version; BashShellIntegration.h reports bash or wsl:<distro> + version (WSL reuses BashFlavor, so one edit covers Git Bash + every distro).
  • Plumb: replicates the WorkingDirectory data flow end to end — adaptDispatch DoWTAction -> ITerminalApi::SetShellType -> Terminal -> ControlCore -> ICoreState -> TermControl -> PaneInfo -> COM _toJson (shell / shell_version). wtcli list-panes / get-active-pane now expose the live shell per pane.
  • Consume: shell_from_active (autofix) now prefers the OSC-reported shell over the pid lookup; pid stays as the fallback for panes without shell integration.
  • Deploy: bumps the shell-integration script kVersion (1 -> 2) so existing users actually receive the new ShellType line — the install orchestrator only rewrites the managed block when the versioned script filename changes.
  • Cleanup: removes the dead OSC 9001 WtaReq / WtaRes in-band handler (superseded by the out-of-band COM IProtocolServer channel).

Test

  • UnitTests_TerminalCore TerminalApiTest::SetShellType — feeds the OSC, asserts GetShellName() / GetShellVersion() (name+version, name-only, empty, last-writer-wins, no interference with CmdNotFound).
  • ShellIntegrationTests *_UpgradesWhenBlockReferencesOlderScriptVersion — the kVersion bump reaches existing users (block referencing an older script version is rewritten; new script contains 9001).
  • Rust shell_from_active_prefers_osc_reported_shell + existing shell_from_active_resolves_pid — OSC field wins over pid; empty/whitespace falls back.
  • WSL end-to-end integration tests (Feature.AutofixPane.Tests.ps1, gated on a runnable WSL distro + the dev package): WSL pane self-reports wsl:<distro> through the protocol, and autofix suggests bash (not PowerShell) syntax.
  • Full bcz no_clean build: 0 errors. Manually verified on the dev package: a WSL pane reports wsl:<distro> and autofix suggests bash-syntax fixes.
image

Closes #352 (context awareness: agent pane & autofix recognize the active WSL/bash shell). Part of #200.

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>
Copilot AI review requested due to automatic review settings June 23, 2026 14:30
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ShellType handling in the VT action dispatcher and a new ITerminalApi::SetShellType plumbing path to store shell name/version in Terminal and expose it through ControlCore/TermControl into protocol PaneInfo and COM JSON (shell, shell_version).
  • Updates PowerShell and bash/WSL shell integration snippets to emit OSC 9001;ShellType each prompt.
  • Updates WTA’s pane JSON parsing to prefer the OSC-reported shell field (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.

Comment thread tools/wta/src/protocol/acp/client.rs Outdated
Comment thread src/terminal/adapter/adaptDispatch.cpp Outdated
Comment thread src/cascadia/inc/BashShellIntegration.h Outdated
Comment thread src/cascadia/TerminalCore/TerminalApi.cpp Outdated
- 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>
@github-actions

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>
Copilot AI review requested due to automatic review settings June 23, 2026 15:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 1 comment.

Comment thread src/cascadia/TerminalCore/TerminalApi.cpp
@github-actions

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>
@github-actions

Copy link
Copy Markdown

@check-spelling-bot Report

⚠️ Dictionary not found

Problems were encountered retrieving check dictionaries (cspell:cpp/src/template-strings.txt cspell:rust/dict/rust.txt cspell:ruby/dict/ruby.txt cspell:elixir/dict/elixir.txt cspell:golang/dict/go.txt cspell:fullstack/dict/fullstack.txt cspell:k8s/dict/k8s.txt cspell:cpp/src/people.txt cspell:cpp/src/stdlib-cerrno.txt cspell:cpp/src/compiler-gcc.txt cspell:python/src/additional_words.txt cspell:java/src/java.txt cspell:r/src/r.txt cspell:typescript/dict/typescript.txt cspell:powershell/dict/powershell.txt cspell:public-licenses/src/generated/public-licenses.txt cspell:public-licenses/src/additional-licenses.txt cspell:cpp/src/stdlib-cpp.txt cspell:npm/dict/npm.txt cspell:cpp/src/stdlib-cmath.txt cspell:php/dict/php.txt cspell:dart/src/dart.txt cspell:monkeyc/src/monkeyc_keywords.txt cspell:html/dict/html.txt cspell:dotnet/dict/dotnet.txt cspell:software-terms/dict/softwareTerms.txt cspell:swift/src/swift.txt cspell:java/src/java-terms.txt cspell:cpp/src/lang-jargon.txt cspell:cpp/src/ecosystem.txt cspell:sql/src/sql.txt cspell:sql/src/tsql.txt cspell:cpp/src/compiler-clang-attributes.txt cspell:docker/src/docker-words.txt cspell:software-terms/dict/webServices.txt cspell:python/src/python/python.txt cspell:node/dict/node.txt cspell:django/dict/django.txt cspell:scala/dict/scala.txt cspell:ada/dict/ada.txt cspell:gaming-terms/dict/gaming-terms.txt cspell:python/src/common/extra.txt cspell:svelte/dict/svelte.txt cspell:haskell/dict/haskell.txt cspell:cpp/src/lang-keywords.txt cspell:lua/dict/lua.txt cspell:latex/dict/latex.txt cspell:python/src/python/python-lib.txt cspell:clojure/src/clojure.txt cspell:cpp/src/compiler-msvc.txt cspell:css/dict/css.txt cspell:shell/dict/shell-all-words.txt cspell:cpp/src/stdlib-c.txt cspell:redis/dict/redis.txt).

⚠️ For more information, see check-dictionary-not-found.

🔴 Please review

See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.

Unrecognized words (1)

cands

These words are not needed and should be removed Backgrounder CANTCALLOUT Ccc cplusplus ctl Debian dotnet drv endptr EOFs evt Fullwidth gitlab hdr idl IME inbox ININPUTSYNCCALL intelligentterminal Ioctl KVM lbl lld lsb NODEFAULT NONINFRINGEMENT notif oss outdir Podcast pri prioritization rcv segfault SND sourced SWP Tbl testname transitioning unk unparseable unregisters Virt VMs webpage websites WINVER xsi

To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the git@github.com:microsoft/intelligent-terminal.git repository
on the dev/vanzue/what-is-shell branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/intelligent-terminal/actions/runs/28037796049/attempts/1' &&
git commit -m 'Update check-spelling metadata'
Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionary

This includes both expected items (2064) from .github/actions/spelling/expect/alphabet.txt .github/actions/spelling/expect/expect.txt .github/actions/spelling/expect/web.txt and unrecognized words (1)

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.

⚠️ Warnings Count
⚠️ check-dictionary-not-found 54

See ⚠️ Event descriptions for more information.

✏️ 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.txt file 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 the patterns.txt file.

    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>
Copilot AI review requested due to automatic review settings June 24, 2026 01:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated 1 comment.

Comment thread src/cascadia/TerminalCore/TerminalApi.cpp
…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>
@vanzue vanzue merged commit 3c45712 into main Jun 24, 2026
7 of 9 checks passed
@vanzue vanzue deleted the dev/vanzue/what-is-shell branch June 24, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Context awareness: agent pane & autofix recognize the active WSL/bash shell (OSC 9001;ShellType)

3 participants