Skip to content

Commit fa5b7fa

Browse files
committed
Use VS Code runtime and keep Interactive entry points private
Remove the Node path setting, scope Electron Node mode to agent launches, and mark Interactive settings experimental. Verify private sess calls and full editor quit/reconnect across versions.
1 parent 1cefacd commit fa5b7fa

27 files changed

Lines changed: 325 additions & 133 deletions

‎R/interactive-worker.R‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function(library, config, support_libraries = character(), worker = TRUE) {
1919
for (package in c("jsonlite", "later", "ps", "processx", "rstudioapi")) {
2020
loadNamespace(package, lib.loc = support_paths)
2121
}
22-
loadNamespace("sess", lib.loc = c(library, support_paths))
22+
ns <- loadNamespace("sess", lib.loc = c(library, support_paths))
2323
cfg <- jsonlite::fromJSON(config)
2424
# Prefer project versions of support packages. Libraries outside an isolated
2525
# project are available only for loading the IDE bridge and its plot device.
@@ -28,5 +28,9 @@ function(library, config, support_libraries = character(), worker = TRUE) {
2828
error = function(e) {
2929
if (isTRUE(cfg$useJgd)) stop(e)
3030
})
31-
if (worker) sess::run_worker(config) else sess::interactive_start(config, mirror = TRUE)
31+
if (worker) {
32+
base::get("run_worker", ns, inherits = FALSE)(config)
33+
} else {
34+
base::get("interactive_start", ns, inherits = FALSE)(config, mirror = TRUE)
35+
}
3236
}

‎R/sess-package-install.R‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ sess_verify_package <- function(library, required, interactive) {
6363
"stopifnot(utils::compareVersion(as.character(utils::packageVersion('sess',lib.loc=a[1])),a[2]) >= 0);",
6464
"stopifnot(all(c('connect','notify_client','register_hooks','request_client') %in% getNamespaceExports(ns)));",
6565
"if (a[3] == 'TRUE') {",
66-
"stopifnot(all(c('interactive_start','interactive_execute','interactive_stop','run_worker','display')",
67-
"%in% getNamespaceExports(ns)));",
66+
"stopifnot(all(c('interactive_stop','display') %in% getNamespaceExports(ns)));",
67+
"stopifnot(all(vapply(c('interactive_start','interactive_execute','run_worker'),",
68+
"function(n) exists(n, ns, mode='function', inherits=FALSE), FALSE)));",
6869
"r <- getDLLRegisteredRoutines(getLoadedDLLs()[['sess']])[['.Call']];",
6970
"n <- c(sess_bridge_start=3L,sess_bridge_send=1L,sess_bridge_context=1L,sess_bridge_stop=0L);",
7071
"stopifnot(all(names(n) %in% names(r)));",

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,4 @@ Call `View(x)` to open a table. The viewer loads rows on demand and applies colu
137137

138138
## Persistent R Interactive
139139

140-
Use native VS Code Interactive windows with independent plain-R or arf sessions, including adoption of existing arf sessions over Remote SSH. See the [setup and usage guide](docs/r-interactive.md) for session switching, persistence, rich outputs, and platform requirements.
140+
This experimental feature provides native VS Code Interactive windows with independent plain-R or arf sessions, including adoption of existing arf sessions over Remote SSH. See the [setup and usage guide](docs/r-interactive.md) for session switching, persistence, rich outputs, and platform requirements.

‎docs/r-interactive-backend-plan.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The current coupling extends beyond execution:
1515
| `src/interactive/manager.ts:518`–`558`, `1323`–`1355` | provider preflight and unconditional sess installation during creation and restart |
1616
| `src/interactive/launcher.ts:88`–`142` | one cache/install operation currently bundles the agent, sess, bootstrap scripts and graphics resources |
1717

18-
Plain R currently calls `interactive_execute` over sess RPC. The arf path uses visible arf `evaluate` to call `sess::interactive_execute()`. Both still depend on the sess console bridge and use sess for inspection. Implementing two independent backends by copying that shared code would make subsequent fixes harder to maintain.
18+
Plain R currently calls `interactive_execute` over sess RPC. The arf path uses visible arf `evaluate` to call the internal sess `interactive_execute()` entry point. Both still depend on the sess console bridge and use sess for inspection. Implementing two independent backends by copying that shared code would make subsequent fixes harder to maintain.
1919

2020
## Responsibility boundary
2121

‎docs/r-interactive-live-testing.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ A subsequent picker polish uses only **R** and **arf**, each with its executable
6464

6565
## Remote startup and CI follow-up
6666

67-
The Remote SSH `spawn node ENOENT` report came from the shared agent launcher used by both R and arf. The default now resolves Node on PATH, then falls back to the standalone runtime running VS Code Server. Explicit invalid settings still produce an actionable `r.interactive.nodePath` error. A runtime/version check runs before private runtime installation and before stopping R for restart. Desktop Electron helpers are excluded.
67+
The Remote SSH `spawn node ENOENT` report came from the shared agent launcher used by both R and arf. The launcher now uses the extension host's own runtime, including VS Code Server remotely and Electron in Node mode on desktop. No Node PATH lookup or runtime setting is required. A runtime/version check runs before private runtime installation and before stopping R for restart.
6868

6969
The Node regressions include a real standalone subprocess with PATH containing no Node executable, custom-path precedence, missing/removed executables, unsupported version output, no session storage on failed creation, and preservation of the running process and transcript on failed restart preflight. The full local suite passed **384 tests** on VS Code **1.140.0** with tmux enabled; the real arf runtime matrix passed **24 tests**. Actual Remote SSH transport was not exercised in this pass.
7070

@@ -153,3 +153,15 @@ Production/TypeScript builds and lint pass, with **0 errors and 70 existing Type
153153
- Unsubmitted native input drafts were preserved when creating another session, but were not restored after full application exit in the 1.140.0 pass. Persistent R objects and executed history survived. Save unfinished code in a source file before quitting.
154154
- Renaming a session updates its kernel/status/picker presentation, but an already-open native Interactive tab can retain its original name. Widget frames currently use a fixed height, which can leave whitespace for small widgets.
155155
- Existing agents retain their original HTTP policy and R runtime. Reloading updates editor-side diagnostics and export behavior; start a new session or explicitly restart a managed session to use the widget policy and shortened error traces. Restart loses in-memory R objects, so a new session is preferable while old work is still needed.
156+
157+
## Bundled editor runtime and private sess entry points (2026-10-04)
158+
159+
The session agent now uses the current extension host executable automatically. Desktop launches set `ELECTRON_RUN_AS_NODE=1`; remote launches use VS Code Server's Node. The flag reaches detached launchers and is passed explicitly through tmux/systemd, then removed before the agent starts R. `r.interactive.nodePath` has been removed. Availability and Node version checks still run before private runtime preparation or stopping a session for restart.
160+
161+
The bootstrap and arf adapter call the internal sess `interactive_start`, `interactive_execute`, and `run_worker` functions through namespace lookups. Public `display` and `interactive_stop` remain exported. Compatibility checks verify the internal functions without requiring their export. Qualified base lookups prevent user-defined `get` or `asNamespace` helpers from breaking arf dispatch. All remaining `r.interactive.*` settings carry VS Code's experimental tag.
162+
163+
Local macOS validation passed 99 backend/runtime tests, 47 arf tests, 106 editor/runtime/supervision/terminal tests, and 634 sess checks. TypeScript compilation and R lint passed; ESLint reported no errors and the existing warnings. The combined editor suite exposed a terminal fixture that assumed no editor was focused; it now explicitly isolates that state.
164+
165+
The repeatable `src/test/examples/runtime-lifecycle.cjs` fixture launched a session from the actual VS Code 1.110.0 extension host with no standalone Node on PATH, exited the full application, and verified that R remained reachable. VS Code 1.140.0 then reconnected to the same R PID and retained object, and launched a second session using its own extension-host executable. Both sessions survived application exit, and R did not inherit `ELECTRON_RUN_AS_NODE`.
166+
167+
This checks quit/reopen and switching editor versions on macOS. It does not exercise the editor's updater or establish that every OS/update mechanism preserves running agents. New launches resolve the current host each time; a removed runtime reports a reload/repair instruction, and reconnection to a live agent does not need to relaunch its executable. Linux supervisor coverage remains in CI; the local supervisor fixtures verify propagation of the Electron flag through both command forms.

‎docs/r-interactive-plan.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ Route synchronous `rstudioapi` requests and stdin prompts to the controlling cli
298298

299299
### Plain background R
300300

301-
Add a managed worker entry point, for example the proposed `sess::run_worker()`, launched by the session agent. The worker owns the evaluation loop and `.GlobalEnv`; `sess` continues to provide editor hooks.
301+
Add a managed worker entry point, for example the internal sess `run_worker()`, launched by the session agent. The worker owns the evaluation loop and `.GlobalEnv`; `sess` continues to provide editor hooks.
302302

303303
Use a dedicated execution driver that receives structured submissions at safe points. Prototype R-level evaluation with `evaluate` and output handlers, or an equivalent carefully tested expression driver. This is a new dependency/implementation choice to settle in the execution spike. It must preserve visible-value printing, assignments, multiple expressions, warnings/messages, errors, source locations, working directory, options, and random-number state as expected for interactive use.
304304

‎docs/r-interactive.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
R Interactive runs R independently of the VS Code extension host. Each session has its own Interactive window, execution queue, environment, retained transcript, plot service, and private agent. Reloading VS Code, closing a tab, or losing an SSH connection does not terminate R.
44

5+
R Interactive and its `r.interactive.*` settings are experimental. Configuration and behavior may change as the design evolves.
6+
57
## Remote SSH setup
68

79
Install this extension **on the remote host**. The initial implementation supports Linux and macOS; its native console bridge does not support Windows.
810

911
The remote host needs:
1012

1113
- R, plus either compatible pre-built sess/dependency packages or a C compiler capable of building R packages (`r-base-dev` and `build-essential` on Debian/Ubuntu).
12-
- Node.js 18 or newer. The default searches the extension host's PATH, then uses VS Code Server's own standalone Node runtime in Remote SSH. A separate Node installation is normally unnecessary on the remote host. Local desktop sessions require standalone Node on PATH or an explicit `r.interactive.nodePath`; the Electron helper is not used.
14+
- The session agent uses the extension host's bundled runtime: VS Code's Electron runtime in Node mode on desktop, or VS Code Server's Node runtime remotely. No separate Node.js installation or runtime setting is needed.
1315
- `tmux` is optional. On Linux, the default supervisor uses tmux when available and otherwise starts an independent detached process. `systemd --user` is another configurable option.
1416
- R packages `processx`, `later`, `jsonlite`, and `rstudioapi`. Install `languageserver` for language features, `jgd` and `systemfonts` for JGD graphics, `svglite` for the static fallback, and `htmlwidgets`/`htmltools` for HTML output.
1517
- `arf` is optional. Creating a headless arf session requires an executable on the R host; plain R requires no arf installation. Connecting to an already-running arf uses its socket and does not require arf on the extension host's PATH. The provider contract was exercised with arf 0.5.1.
@@ -23,13 +25,13 @@ install.packages(c(
2325
))
2426
```
2527

26-
If a configured Node path is missing or unusable, set **R › Interactive: Node Path** to an executable on the R host. Paths support `~/`, `${userHome}`, and `${workspaceFolder}`. Runtime availability and version are checked before installation or restart, so a failed check leaves the current R process running.
28+
Runtime availability and version (Node.js 18+) are checked before installation or restart, so a failed check leaves the current R process running. If an editor update removes the previous runtime, reload VS Code to select its current executable. Reconnecting to an existing agent does not launch or replace its runtime; new and restarted sessions use the current extension host's runtime.
2729

2830
The extension installs its bundled `sess` into a private, content-addressed library. It does not replace your installed `sess` package. If the source build fails, including when build tools are missing, it tries a compiler-free package from [R-universe](https://reditorsupport.r-universe.dev/sess). Installation diagnostics appear in the **R Interactive** output channel.
2931

3032
The fallback uses R's matching macOS/Windows binary repository, or an Ubuntu repository matching the host's codename, architecture and R version. It never assumes that an Ubuntu binary is compatible with a different Linux distribution. Where no binary target exists, a published pure-R package can also be used without compilation, provided its dependencies are already installed or available without compilation. Dependencies for a binary fallback come from the matching R-universe repositories. No global repository settings are changed.
3133

32-
Interactive requires the published package's compatibility marker (`Config/vscode-R/Interactive: 1`), the expected exports, and the registered native console routines. A matching package version alone is insufficient. **The public sess 3.0.1 build checked on 2026-10-02 predates this PR, reports `NeedsCompilation: no`, and cannot run these Interactive sessions.** A build containing this branch's native bridge must be published before that fallback can replace local compilation for Interactive. Ordinary terminal sess installation can already use the existing public build. Missing/incompatible binaries produce an actionable installation error rather than a partially initialized session.
34+
Interactive requires the published package's compatibility marker (`Config/vscode-R/Interactive: 1`), the expected public exports and internal entry points, and the registered native console routines. A matching package version alone is insufficient. **The public sess 3.0.1 build checked on 2026-10-02 predates this PR, reports `NeedsCompilation: no`, and cannot run these Interactive sessions.** A build containing this branch's native bridge must be published before that fallback can replace local compilation for Interactive. Ordinary terminal sess installation can already use the existing public build. Missing/incompatible binaries produce an actionable installation error rather than a partially initialized session.
3335

3436
That private library is used only to load the bridge; it is not added to your session's `.libPaths()` or `R_LIBS`. Package installation follows ordinary R behavior: `install.packages()` defaults to the first library in `.libPaths()`, usually your user library. Startup files can customize that order, and an explicit `lib=` still takes precedence. The extension does not force a user-library destination over a project library.
3537

@@ -41,7 +43,6 @@ Typical remote settings:
4143

4244
```json
4345
{
44-
"r.interactive.nodePath": "/usr/bin/node",
4546
"r.interactive.arfPath": "/home/me/.cargo/bin/arf",
4647
"r.interactive.supervision": "auto",
4748
"r.interactive.restore": true,

0 commit comments

Comments
 (0)