You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Preserve disconnected terminal output and add recovery actions - #20726
Keep the last terminal screen visible when a workload exits or the connection drops, rather than clearing the output. Retained output includes text and Sixel/Kitty graphics, with a disconnect banner explaining what happened and how to recover.
Resource terminals: restart/start the owning resource through its advertised command, with existing confirmation and read-only checks. Include the exit code when the resource protocol reports one. Successful recovery reconnects the view even after automatic retries have been exhausted.
Docked terminals: close the owning tab without losing control of adjacent tabs.
Show terminal: collapse the banner into an inset top-right disconnect icon to uncover the retained screen. Click the icon to reopen recovery actions. The redundant bottom-right disconnected popup is suppressed; unrelated errors remain visible.
Uses stable Hex1b 0.173.0 across NuGet, npm, the vendored browser assets, and tool/MCP pins. NuGet packages are available through the approved internal feed; no temporary external restore source or Windows-helper copy workaround remains.
Screenshots / Recordings
Resource terminal — disconnected output and graphics remain visible, with Restart resource and Show terminal actions:
Resource terminal — Show terminal collapses recovery to the padded floating icon:
Redis REPL — Ctrl+C disconnects the docked terminal, offering Close tab and Show terminal:
Redis REPL — collapsed recovery leaves the final PING/PONG output unobstructed:
Shell prompt identities are redacted. These captures illustrate the disconnect/recovery controls; resource messages additionally show a reported exit code when available.
User-facing usage
Run playground/Terminals, exit the shell resource, and use Restart resource to reconnect. Open Redis's REPL in the dock and send Ctrl+C, then use Show terminal to uncover its final output or Close tab to dismiss it.
Implementation
The browser retains its existing renderer through transport retries until a replacement receives its first frame. The backend drains the final browser-mirror frame before closing and gates recycled-terminal handshakes on producer reconnection, preventing an empty relay from replacing retained output. Explicit resource recovery resets the retry budget without weakening ordinary terminal-completion handling.
Regression coverage includes completion-before-mount races, restart after retry exhaustion, command outcomes, resource updates during command execution, and package pairing. Tool/MCP manifest changes explicitly select the Infrastructure.Tests consistency checks.
From src/Aspire.Dashboard, npm run update-terminal-assets — passed; regenerated and verified stable 0.173.0 assets.
MSBUILDTERMINALLOGGER=false dotnet build src/Aspire.Managed/Aspire.Managed.csproj -v:minimal — passed with stable 0.173.0, zero warnings/errors and normal Windows helper copying.
MSBUILDTERMINALLOGGER=false dotnet restore src/Aspire.Managed/Aspire.Managed.csproj --force --no-http-cache -v:quiet — passed after removing the temporary external source. All three 0.173.0 package archives were separately downloaded and verified directly from dotnet-public.
Earlier live Chromium verification with the preceding alpha covered shell exit with retained text/Kitty/Sixel graphics, failed reconnect retention, resource restart, watch-driven dock closure, collapse/focus behavior, and Redis Ctrl+C. The later exit-code and review fixes have automated coverage above, but were not reverified live.
Known limitations
Hex1b 0.173.0 does not support offline selection/copy. Collapsing the banner uncovers retained output but does not enable disconnected selection; upstream support is a separate follow-up.
Docked-terminal completion does not currently expose a workload exit code through the pinned adapter/protocol. Missing codes remain unspecified; the last shell-command status is not substituted for process completion.
Security considerations
Retained output can contain sensitive workload data and remains visible until the view is closed or replaced. Existing dashboard authentication and resource-command confirmation/read-only checks remain in force. This change does not add a network endpoint or bypass command authorization. NuGet dependencies use approved internal feeds. No threat model or security review has been completed for this change.
Fixes # (issue)
Checklist
Is this feature complete?
Yes. Ready to ship.
No. Follow-up changes expected.
Are you including unit tests for the changes and scenario tests if relevant?
Yes
No
Did you add public API?
Yes
If yes, did you have an API Review for it?
Yes
No
Did you add <remarks /> and <code /> elements on your triple slash comments?
Yes
No
No
Does the change make any security assumptions or guarantees?
Yes
If yes, have you done a threat model and had a security review?
These reads add runtime inputs to DashboardTerminalScriptTests.TerminalLifecycleAndPackageContract (tests/Infrastructure.Tests/WorkflowScripts/DashboardTerminalScriptTests.cs:11–15). However, eng/github-ci/ci-skip-entirely-patterns.txt:58 still excludes .config/dotnet-tools.json, so a tools-only version change skips CI and never runs the new pairing assertion. Remove that skip entry and route the manifest to test:Infrastructure.Tests alongside the terminal inputs in eng/github-ci/test-trigger-map.yml:137–148. Also route .mcp.json explicitly rather than relying on the unmapped-file full-matrix fallback. Add focused selector regression coverage for these inputs.
If a connected view loses transport and its retry receives a 4000 close before any frame, the previous renderer stays in retainedClient while state.ended becomes true. A subsequent fatal renderer error sets mount-failed here, hiding the recovery banner and its Close tab action. The remaining Reconnect button does nothing because same-endpoint reconnects reject ended views. Use renderer-failed when the retained view has ended, and add coverage for transport loss → frameless 4000 retry → renderer failure.
If a reconnect receives close code 4000 before its first frame (for example, from CloseEndedAsync), the previous screen remains in retainedClient with state.ended true. A later fatal error in that renderer sets mount-failed here, hiding the recovery banner and its Close tab action. The replacement Reconnect button does nothing because same-URL reconnects reject ended views. Use renderer-failed when already ended, matching the completed-client branch, and add a regression covering transport loss → pre-frame completion on retry → retained-renderer failure.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Cross-process teardown and renderer recovery require final human validation against the stable runtime, alongside resolution of the retained correctness issue.
If the AppHost terminal ends before its first frame, CloseEndedAsync sends code 4000 without a projection. Neither state.client nor state.retainedClient exists, but clearing connectionError here makes the banner claim output was preserved and offer “Show terminal” over an empty view. The subsequent mount rejection cannot correct this because connectionFailed ignores ended terminals. Mark output unavailable when neither projection exists; an already-queued successful mount clears that error when it adopts its frame. Add a frameless-completion regression that also verifies the endpoint remains ended.
Signals fired: new_public_type (new TerminalDisconnectedBanner component), pr_body_has_user_facing_section (PR's "User-facing usage" section describing Restart resource / Close tab / Show terminal), and pr_body_has_cli_flag_mention (validation command mentions, a weaker signal but counted). Added a "Recover from a disconnected terminal" section to src/frontend/src/content/docs/dashboard/terminals.mdx covering: retained output (text + Sixel/Kitty graphics) on disconnect, the disconnect banner's recovery actions (Restart resource for resource terminals, Close tab for docked terminals, Show terminal to collapse/expand the banner), and the Hex1b 0.173.0 limitation that offline selection/copy isn't supported.
Note
This draft PR needs human review before merging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Keep the last terminal screen visible when a workload exits or the connection drops, rather than clearing the output. Retained output includes text and Sixel/Kitty graphics, with a disconnect banner explaining what happened and how to recover.
Uses stable Hex1b 0.173.0 across NuGet, npm, the vendored browser assets, and tool/MCP pins. NuGet packages are available through the approved internal feed; no temporary external restore source or Windows-helper copy workaround remains.
Screenshots / Recordings
Resource terminal — disconnected output and graphics remain visible, with Restart resource and Show terminal actions:
Resource terminal — Show terminal collapses recovery to the padded floating icon:
Redis REPL — Ctrl+C disconnects the docked terminal, offering Close tab and Show terminal:
Redis REPL — collapsed recovery leaves the final PING/PONG output unobstructed:
Shell prompt identities are redacted. These captures illustrate the disconnect/recovery controls; resource messages additionally show a reported exit code when available.
User-facing usage
Run
playground/Terminals, exit theshellresource, and use Restart resource to reconnect. Open Redis's REPL in the dock and send Ctrl+C, then use Show terminal to uncover its final output or Close tab to dismiss it.Implementation
The browser retains its existing renderer through transport retries until a replacement receives its first frame. The backend drains the final browser-mirror frame before closing and gates recycled-terminal handshakes on producer reconnection, preventing an empty relay from replacing retained output. Explicit resource recovery resets the retry budget without weakening ordinary terminal-completion handling.
Regression coverage includes completion-before-mount races, restart after retry exhaustion, command outcomes, resource updates during command execution, and package pairing. Tool/MCP manifest changes explicitly select the Infrastructure.Tests consistency checks.
Validation
node --test tests/Aspire.Dashboard.Components.Tests/JavaScript/TerminalView.test.mjs— passed, 136 tests.MSBUILDTERMINALLOGGER=false dotnet test --project tests/Aspire.Dashboard.Components.Tests/Aspire.Dashboard.Components.Tests.csproj --no-launch-profile -- --filter-class '*Terminal*' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true'— passed, 269 tests.MSBUILDTERMINALLOGGER=false dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj --no-launch-profile -- --filter-class '*.DashboardCommandExecutorTests' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true'— passed, 8 tests.MSBUILDTERMINALLOGGER=false dotnet test --project tests/Infrastructure.Tests/Infrastructure.Tests.csproj --no-launch-profile -- --filter-class 'Infrastructure.Tests.TestTriggerMap.*' --filter-class '*.DashboardTerminalScriptTests' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true'— passed, 298 tests.src/Aspire.Dashboard,npm run update-terminal-assets— passed; regenerated and verified stable 0.173.0 assets.MSBUILDTERMINALLOGGER=false dotnet build src/Aspire.Managed/Aspire.Managed.csproj -v:minimal— passed with stable 0.173.0, zero warnings/errors and normal Windows helper copying.MSBUILDTERMINALLOGGER=false dotnet restore src/Aspire.Managed/Aspire.Managed.csproj --force --no-http-cache -v:quiet— passed after removing the temporary external source. All three 0.173.0 package archives were separately downloaded and verified directly from dotnet-public.MSBUILDTERMINALLOGGER=false dotnet build /t:UpdateXlf src/Aspire.Dashboard/Aspire.Dashboard.csproj— passed; localization files regenerated.MSBUILDTERMINALLOGGER=false dotnet test --project tests/Aspire.TerminalHost.Tests/Aspire.TerminalHost.Tests.csproj --no-launch-profile -- --filter-class '*.TerminalHostAppTests' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true'— passed, 25 tests;MSBUILDTERMINALLOGGER=false dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj --no-launch-profile -- --filter-namespace 'Aspire.Dashboard.Tests.Terminal' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true'— passed, 112 tests.Known limitations
Security considerations
Retained output can contain sensitive workload data and remains visible until the view is closed or replaced. Existing dashboard authentication and resource-command confirmation/read-only checks remain in force. This change does not add a network endpoint or bypass command authorization. NuGet dependencies use approved internal feeds. No threat model or security review has been completed for this change.
Fixes # (issue)
Checklist
<remarks />and<code />elements on your triple slash comments?