Skip to content

Allow viewer apps to run in Console sessions#12545

Open
lionel- wants to merge 21 commits intomainfrom
feature/shiny-session
Open

Allow viewer apps to run in Console sessions#12545
lionel- wants to merge 21 commits intomainfrom
feature/shiny-session

Conversation

@lionel-
Copy link
Contributor

@lionel- lionel- commented Mar 17, 2026

Addresses #12556

Refactored positron-run-app extension to add a new runApplicationInConsole() entry point. The internals are shared between the terminal and console paths (in particular I extracted AppUrlDetector for URL detection in app output), and now handle errors a bit more consistently.

  • Sessions are reused and restarted when app is started again. To make the implementation easier, positron.runtime.restartSession() now returns a boolean indicating whether the session restart was cancelled by the user. When cancelled, the app restart is cancelled too. When the session is already restarting, we wait for the restart to finish (new behaviour) and return true. These changes make the behaviour of await restartSession() clearer for callers: true result means the session is ready, false means the restart was cancelled, error means the restart couldn't be completed.

  • If there are breakpoints and app runner specified a debugger type, we wait for the configuration of that debugger to be done before starting the app. The configurationDone event only fires after initial setBreakpoints requests ( one per file) are successful, and this is exactly what we need to ensure that Ark's DAP has fully started up and knows about breakpoints before sourcing the Shiny app files.

  • The viewer's stop button gains support for console sessions. It sends console interrupts via the console service.

Once posit-dev/shiny-vscode#108 is merged and released, we can bump the bundled version and merge this PR.

Release Notes

New Features

Bug Fixes

  • N/A

QA Notes

  • R Shiny apps run in console sessions, not terminals
  • Other apps (Streamlit etc.) still use terminals
  • Stop button in viewer works for console apps (interrupt) and disappears when idle (including when debugging the app, which is admitedly weird)
  • No breakpoints: fast startup. With breakpoints: slower but breakpoints hit
  • Re-running reuses the same "Shiny" console and restarts it
  • Re-running also reuses sessions after extension host restart or window reload
  • Re-running while debugging: debugger exits, session restarts
  • When re-running, declining the interrupt prompt cancels the restart
  • Closed/exited session: fresh one created on next run
  • Dirty document auto-saved before execution
  • Timeout error if URL not detected in ~25s
  • Rapid "Run App" clicks: "already starting" message, no duplicates
Screen.Recording.2026-03-17.at.18.38.44.mov

@:apps

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:apps

readme  valid tags

@lionel- lionel- force-pushed the feature/shiny-session branch from cb587f7 to 92c5ba6 Compare March 17, 2026 16:36
@lionel- lionel- force-pushed the feature/shiny-session branch 3 times, most recently from e751e43 to e51c0f6 Compare March 18, 2026 10:56
@lionel- lionel- marked this pull request as ready for review March 18, 2026 11:00
@lionel- lionel- requested review from jmcphers and seeM March 18, 2026 11:00
private readonly _runApplicationSequencerByName = new SequencerByKey<string>();
private readonly _runApplicationDisposableByName = new Map<string, vscode.Disposable>();
private readonly _appServers = new Map<string, { terminalPid: number | undefined; proxyUri: vscode.Uri }>();
private readonly _consoleSessionByName = new Map<string, { session: positron.LanguageRuntimeSession; listener: vscode.Disposable }>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This needs to be stored somewhere so that we maintain the mapping between consoles and running applications when the window reloads/extension host restarts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! I opted for a simple approach where we prune stale sessions from the persisting memento on each lookup. Should be fine since there should only be a handful of known sessions at any given time.

@lionel- lionel- force-pushed the feature/shiny-session branch from 8151db1 to 7337a79 Compare March 19, 2026 08:32
@lionel- lionel- requested a review from jmcphers March 19, 2026 08:36
}
}
if (pruned) {
await this._globalState.update(PositronRunAppApiImpl.CONSOLE_SESSIONS_KEY, persisted);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here and elsewhere: the set of console sessions should be stored in workspace state rather than global state. If they're in global state then two Positron windows opened at once will continually delete each others' entries.

https://code.visualstudio.com/api/references/vscode-api#ExtensionContext.workspaceState

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Thanks for walking me through all these pitfalls, first time I'm using persistent storage.

There's another failure mode though: if two different windows open the same workspace (e.g. via the duplicate command), we'd still get corruption.

It seems like the best setup for this sort of use cases would be to have storage space whose lifecycle exactly matches the console sessions (and is not persisted on disk). I've experimented with this in #12636.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you observe the corruption? It is not possible AFAIK to have the same workspace open in two windows. When you use the duplicate workspace command, what you get is a second (untitled) workspace with its own distinct workspace storage.

However I've been meaning to add a way for extensions to access ephemeral storage for months, and as you note it is an even better match than workspace storage since ephemeral state goes away when you close Positron. Thanks for adding that!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't! Should have checked.

So that's what "duplicate" means in that command. Makes a ton of sense.

@lionel- lionel- force-pushed the feature/shiny-session branch 2 times, most recently from 22fe806 to ae751a8 Compare March 20, 2026 09:29
@lionel- lionel- requested a review from jmcphers March 20, 2026 15:55
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