Skip to content

Add PowerShell host UI streaming#48

Merged
TorreyBetts merged 14 commits into
DataficationSDK:mainfrom
eosfor:pr/powershell-host-streaming
May 18, 2026
Merged

Add PowerShell host UI streaming#48
TorreyBetts merged 14 commits into
DataficationSDK:mainfrom
eosfor:pr/powershell-host-streaming

Conversation

@eosfor
Copy link
Copy Markdown
Contributor

@eosfor eosfor commented May 6, 2026

Problem

PowerShell cells that wrote host UI output during long-running execution did not surface that output until the command finished. This made cells that called Write-Host, prompted with Read-Host, or blocked on longer work feel unresponsive in the notebook UI.

PowerShell interactive input also needed a host-to-extension path that can reply while the execution pipeline is still running, without waiting behind the same sequential request queue.

Approach

This PR adds a dedicated Verso.PowerShellHost adapter and wires it into the PowerShell runspace. The host captures PowerShell UI output and input requests, forwards streamed output through the execution context, and sends input requests to the VS Code extension through the host protocol.

The VS Code bridge handles input/request notifications by showing an input box and replying to the host with input/response.

Notable changes

  • Add src/Verso.PowerShellHost with a PSHost, PSHostUserInterface, and raw UI implementation.
  • Create the PowerShell runspace with the Verso host adapter.
  • Stream host output through IExecutionContext.WriteOutputAsync.
  • Add interactive input plumbing through IExecutionContext.RequestInputAsync.
  • Add JSON-RPC protocol names and VS Code bridge handling for input/request and input/response.
  • Add PowerShell host package metadata/README so package generation succeeds.
  • Document PowerShell host interactivity in the architecture docs.
  • Update related tests and test fakes for the expanded execution context surface.

Validation

  • npm run lint
  • dotnet build Verso.sln --no-restore

Manual checks

PowerShell host output now appears while the command is still running:

Write-Host "before $(Get-Date -Format HH:mm:ss)"
Start-Sleep -Seconds 20
Write-Host "after $(Get-Date -Format HH:mm:ss)"

Interactive input can be requested from a PowerShell cell:

$name = Read-Host "Name"
Write-Host "hello $name"

Review notes

This PR intentionally focuses on PowerShell host UI streaming and input plumbing. Stop/cancel controls are split into a separate PR.

@eosfor
Copy link
Copy Markdown
Contributor Author

eosfor commented May 7, 2026

Rebased this branch onto current main with -Xrenormalize and force-pushed. GitHub now reports it as mergeable.

@eosfor eosfor force-pushed the pr/powershell-host-streaming branch from 932d7b4 to 9dbe271 Compare May 9, 2026 07:28
@eosfor
Copy link
Copy Markdown
Contributor Author

eosfor commented May 9, 2026

Synced and rechecked the F5 manual validation.

The issue we saw locally was not the PowerShell host streaming implementation itself. The F5 extension host was able to pick up an older configured verso.hostPath, so it was running a stale host instead of the freshly built bundled vscode/host/Verso.Host.dll.

I updated development-mode host resolution so F5 prefers the bundled host, while production still honors a configured verso.hostPath. I also added tests for that behavior.

Manual F5 verification now passes:

  • Write-Host "before" appears while Start-Sleep is still running.
  • Read-Host shows the input prompt and then outputs the provided value.

I also reverted the WASM cache-buster experiment since it was not the root cause.

Screenshot 2026-05-09 011659

@eosfor eosfor force-pushed the pr/powershell-host-streaming branch from ed9af6d to 4c87c52 Compare May 11, 2026 15:46
@eosfor
Copy link
Copy Markdown
Contributor Author

eosfor commented May 11, 2026

I'm sorry, this was smth internal :)

But actually, yes, .net8 end of support is November 10, 2026. So perhaps it is time to migrate.

@eosfor eosfor force-pushed the pr/powershell-host-streaming branch from 4c87c52 to bf23744 Compare May 15, 2026 07:30
@eosfor
Copy link
Copy Markdown
Contributor Author

eosfor commented May 15, 2026

Rebased this PR onto current main (ce26f2b) and force-pushed. The branch still stays on the current upstream target frameworks; the separate local .NET 9 work is not included here.

Local validation after the rebase:

  • npm run lint
  • dotnet build Verso.sln --no-restore
  • dotnet test tests/Verso.PowerShell.Tests/Verso.PowerShell.Tests.csproj --no-restore

Do you plan to merge this PR, or is there anything else you would like changed first?

This one is important for us because it makes PowerShell notebooks usable for the workflows we are actively testing. The sample notebooks we are using include:

  • installing/importing graph modules such as PSQuickGraph and PSGraphView from PowerShell Gallery,
  • building graph objects in PowerShell and rendering them back into the notebook as SVG/PNG output,
  • running richer PowerShell notebooks such as a small micrograd-style computation graph with custom formatting and graph visualization.

For those scenarios, silent long-running cells are very hard to diagnose, and interactive host prompts are a blocker for common PowerShell flows such as Read-Host, credential/choice prompts, and auth/device-login style commands. Streaming host UI output and routing host input through the VS Code bridge are the pieces that make those notebooks feel like a real PowerShell environment instead of a batch-only runner.

@TorreyBetts
Copy link
Copy Markdown
Contributor

Thanks for the rebase and the validation runs. Before I review this one I want to finish closing out testing on the PRs that merged recently and fix anything that surfaces from that pass. Stacking more on top before that makes the next round of issues harder to attribute. I'll come back to this once that's done.

}

foreach (var info in ps.Streams.Information)
if (hostOutput is null)
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.

Write-Information output is dropped: WriteInformation is a no-op and ps.Streams.Information is skipped when hostOutput is set. Please keep the stream loop and filter records by tag: skip PSHOST (that's Write-Host, already covered by the host UI) and keep the rest. Worth a regression test with Write-Information -InformationAction Continue.

@TorreyBetts
Copy link
Copy Markdown
Contributor

TorreyBetts commented May 16, 2026

The new Scaffold.OnCellOutputUpdated and Scaffold.InputRequester are only wired in Verso.Host (the VS Code backend). ServerNotebookService subscribes to OnCellExecuted but not the new event, and nothing sets InputRequester for Blazor Server. Net effect: Read-Host in verso serve falls through to a PSError ("Interactive input is not supported") even though a UI is present. Please add equivalent wiring next to the existing OnCellExecuted subscription so live output and input prompts work there too.

Unrelated to the above, but should be addressed as well is Verso.PowerShellHost is published as its own NuGet, but it has only one consumer and no third-party scenario. Could you fold the contents into src/Verso.PowerShell/Kernel/Host/ as internal types? That preserves your intent without committing to a new package.


public override PSHostRawUserInterface RawUI => _rawUI;

public override bool SupportsVirtualTerminal => true;
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.

It's cleaner to set this as false until SGR parsing is implemented. The Emit currently strips the ANSI sequences, so it's easier to just leave them out for now.

eosfor added 6 commits May 16, 2026 12:45
Move the PowerShell PSHost adapter under src/Verso.PowerShell/Kernel/Host and switch it to the Verso.PowerShell.Kernel.Host namespace. The adapter is now an internal implementation detail of the PowerShell kernel instead of a separately published Verso.PowerShellHost package.

Remove the Verso.PowerShellHost project from the solution and drop the project reference from Verso.PowerShell. Update architecture docs so they describe the host adapter as internal to Verso.PowerShell rather than as a standalone package/project.

Validation run outside the sandbox: dotnet build src/Verso.PowerShell/Verso.PowerShell.csproj --no-restore; dotnet build Verso.sln --no-restore; dotnet publish src/Verso.PowerShell/Verso.PowerShell.csproj --no-restore -f net8.0; dotnet publish src/Verso.PowerShell/Verso.PowerShell.csproj --no-restore -f net10.0.
Set SupportsVirtualTerminal to false while host output still strips ANSI escape sequences instead of parsing SGR formatting. This avoids advertising VT support until the host can preserve or render supported ANSI sequences safely.

Validation: dotnet build src/Verso.PowerShell/Verso.PowerShell.csproj --no-restore; dotnet test tests/Verso.PowerShell.Tests/Verso.PowerShell.Tests.csproj --no-restore.
Keep processing ps.Streams.Information even when the PowerShell host output callback is active. Filter out only records tagged PSHOST so Write-Host remains covered by the host UI path without duplicating that output, while ordinary Write-Information records are still returned.

Add a regression test for Write-Information -InformationAction Continue and update the kernel comment to reflect that the remaining information stream is Write-Information output rather than Write-Host output.

Validation: dotnet test tests/Verso.PowerShell.Tests/Verso.PowerShell.Tests.csproj --no-restore; dotnet build src/Verso.PowerShell/Verso.PowerShell.csproj --no-restore.
Subscribe ServerNotebookService to Scaffold.OnCellOutputUpdated and forward those updates through the existing OnOutputUpdated service event so verso serve can repaint running cells when kernels stream host output.

Set Scaffold.InputRequester in the server service and add a pending input request flow modelled after extension consent. NotebookPage now displays a Blazor modal for PowerShell input, supports cancellation, and switches to password input when the kernel requests masked input.

Add server-flow regression tests for live output forwarding, Read-Host happy path, Read-Host cancellation, password flag propagation, and Write-Information output. The Blazor shared test project now references Verso.Blazor and Verso.PowerShell so these in-process server paths can be exercised directly.

Validation: dotnet test tests/Verso.Blazor.Shared.Tests/Verso.Blazor.Shared.Tests.csproj; dotnet test tests/Verso.Blazor.Shared.Tests/Verso.Blazor.Shared.Tests.csproj --no-restore; dotnet test tests/Verso.PowerShell.Tests/Verso.PowerShell.Tests.csproj --no-restore; dotnet build Verso.sln --no-restore.
Run cell and run-all actions now start execution from a background task instead of awaiting the full kernel execution inside the originating Blazor event handler. This keeps the Blazor Server circuit free to process the later Submit, Cancel, and close events from the PowerShell input dialog while a Read-Host cell is waiting for input.

Also mark the input dialog buttons as explicit type=button so they cannot accidentally participate in form submission semantics when embedded in future markup.

Adds bUnit coverage that the run button returns before a held execution completes, plus submit, cancel, and password-rendering coverage for NotebookInputDialog. Manual verso serve validation confirmed Read-Host, secure Read-Host, prompt cancellation, and Ctrl+C behavior.
Write-Information with -InformationAction Continue can be surfaced twice: PowerShell may render the information record through the host UI while also keeping the same non-PSHOST record in ps.Streams.Information. The previous review fix correctly preserved non-host information records, but did not account for that host-rendered copy.

Filter final InformationLines against plain-text outputs already streamed during the same execution, normalizing line endings and trailing newlines before comparison. This keeps Write-Information visible while avoiding a duplicate final output block in verso serve.

Strengthens the PowerShell regression test so Write-Information 'info message' -InformationAction Continue must appear exactly once. Manual verso serve validation confirmed the output now renders once.
@eosfor
Copy link
Copy Markdown
Contributor Author

eosfor commented May 16, 2026

Pushed the review follow-up changes to pr/powershell-host-streaming.

Summary of what changed:

  • Folded the PowerShell host adapter into Verso.PowerShell.

    • Removed the separate Verso.PowerShellHost project/package.
    • Moved the host implementation under src/Verso.PowerShell/Kernel/Host/.
    • Made the host adapter types internal.
  • Set SupportsVirtualTerminal => false.

    • The host still strips ANSI escape sequences, but does not advertise VT support until SGR parsing is implemented.
  • Fixed Write-Information.

    • Non-PSHOST information records are now preserved.
    • PSHOST records are skipped to avoid duplicating Write-Host.
    • Added regression coverage for Write-Information -InformationAction Continue.
    • Also fixed the Blazor Server case where Write-Information -InformationAction Continue could render twice because PowerShell can both host-render the information message and keep the same record in ps.Streams.Information.
  • Wired Blazor Server / verso serve to the same live output and input flow.

    • ServerNotebookService now subscribes to Scaffold.OnCellOutputUpdated.
    • ServerNotebookService now provides Scaffold.InputRequester.
    • Added a Blazor input dialog for Read-Host / secure input.
    • Fixed the Blazor Server event-flow issue where awaiting the full cell execution inside the original run-button event prevented Submit/Cancel/close clicks from resolving a pending Read-Host.

Validation run locally:

dotnet test tests/Verso.PowerShell.Tests/Verso.PowerShell.Tests.csproj --no-restore
dotnet test tests/Verso.Blazor.Shared.Tests/Verso.Blazor.Shared.Tests.csproj --no-restore
dotnet build Verso.sln --no-restore
npm run lint

Manual verso serve checks:

Write-Host "before $(Get-Date -Format HH:mm:ss)"
Start-Sleep -Seconds 5
Write-Host "after $(Get-Date -Format HH:mm:ss)"

Confirmed before appears while the cell is still running, and after appears after the sleep.

$name = Read-Host "Name"
Write-Host "hello $name"

Confirmed the input dialog appears, Submit resumes execution, and output contains the submitted value.

$secret = Read-Host "Secret" -AsSecureString
Write-Host "done"

Confirmed secure input is masked and execution resumes.

Write-Information "info message" -InformationAction Continue

Confirmed the message renders once.

@TorreyBetts TorreyBetts merged commit b47613e into DataficationSDK:main May 18, 2026
5 checks passed
@TorreyBetts
Copy link
Copy Markdown
Contributor

Thanks for the follow-up work. Merging now.

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.

2 participants