sess is an R package for connecting your R sessions to an editor (client). The
VS Code R extension is our primary
target client and sess powers many of the extension's core features:
workspace, data and plot viewers, help panel, hover and completion, RStudio API
emulation, etc.
Under the hood, sess talks to the client over a local socket (Unix domain
socket on macOS/Linux, named pipe on Windows) using
JSON-RPC 2.0 messages.
Users of the
vscode-R extension
(>=v3.0.0) do not need to install sess
manually. The extension bundles its own copy and asks before installing it
when no installed copy has the exact bundled source revision. Accepted
installs and missing dependencies go into a vscode-R-managed library; existing
user, project, and package-manager libraries are left unchanged. The extension
explicitly loads the matching copy for its session watcher. Preparation and
installation use the terminal's R, platform, version, and normal libraries;
when needed, that R asks the extension for consent. Manual attach follows the
same rule for the attached R. If a different sess is already loaded, restart
R before attaching.
The bundled sess is a pure R package and needs no compiler to install from
source. Its dependencies, including processx, later, and jsonlite, contain
native code; during initial preparation, the extension installs missing Imports
from the configured CRAN repository before installing its bundled copy. The
extension does not substitute a separately published sess package if the
bundled installation fails.
Caution
Known limitation: isolated libraries.
A vscode-R-managed sess copy is reused across projects. Dependencies found in
ordinary R libraries during initial preparation are not copied into that managed
library. If an isolated project's .libPaths() cannot see them, sess may fail
to load; R still starts, but its session watcher is unavailable.
In the affected project, install the missing dependencies with
renv::install(). Then
restart R or retry manual attach. You do not need to install sess in the project.
renv's default implicit snapshot type includes packages detected in project
files, so editor-only dependencies may be omitted; see
snapshot types.
sess is not yet on CRAN. But you can install the development version from R-universe:
install.packages(
"sess",
repos = c("https://reditorsupport.r-universe.dev", getOption("repos"))
)Note
R Interactive
prepares its isolated runtime as part of starting an Interactive session; that
runtime follows its own lifecycle and does not change ordinary R libraries.
R Interactive requires arf on the R host.
Managed headless arf sessions stream console output and retain rich tables, HTML, and
plots. Attached arf sessions return notebook console output on completion; arbitrary
terminal console output has no subscription in the current arf IPC. Notebook console
input and debugger prompts are unavailable. Ordinary R terminals remain available
without arf.
When you start an R terminal from VS Code, the extension's R profile calls
sess::connect() for you. To connect a session yourself:
sess::connect(
endpoint = NULL, # socket/pipe endpoint; see below
use_rstudioapi = TRUE, # emulate rstudioapi functions
plot_backend = "auto" # or "standard", "httpgd", "jgd", "native"
)If plot_backend is omitted or set to NULL, sess::connect() uses auto.
The VS Code extension passes its configured backend explicitly.
For code migrating from earlier sess APIs, replace use_httpgd and
use_jgd with plot_backend (for example, sess::connect(plot_backend = "httpgd")).
register_hooks() has been removed; connect() initializes the runtime
integration when it connects.
If endpoint is omitted, connect() resolves it in this order:
- The
SESS_ENDPOINTenvironment variable. - The
endpointfield of the JSON file named bySESS_DISCOVERY_FILE.
The discovery file must have integer schema version 1 and a nonempty endpoint.
When discovery is needed, a missing, invalid, or unsupported file prevents connection.
An explicit endpoint or SESS_ENDPOINT takes precedence over discovery.
If the discovery file describes the connected endpoint, unexpected disconnection
starts a retry loop. It waits for a different endpoint and reconnects with the same
runtime options and process-lifetime session_id. A manual connect() cancels
pending retries. Each managed terminal has its own discovery file in extension
storage; VS Code refreshes it after reload, using extension-private terminal PID
metadata even when a wrapper's PID differs from R's. Unknown fields are ignored.
{"version":1,"endpoint":"/path/to/sess.sock","jgdSocket":"/path/to/jgd.sock"}Only version and endpoint are required. Consumers must ignore unknown fields.
New backends may add optional fields under version 1: adding a field does not
require a schema version bump. Removing or changing the meaning/type of existing
fields, or making additional fields mandatory, is a breaking change and requires
a new schema version. This discovery schema version is separate from the IPC
protocol_version.
The optional jgdSocket string describes the JGD renderer belonging to that
endpoint. When plot_backend resolves to auto or jgd, sess applies it before runtime initialization,
including automatic reconnect: a nonempty string sets JGD_SOCKET, an empty
string unsets it (renderer unavailable), and an omitted field leaves it untouched.
A present value of another type is invalid. It does not enable JGD or override
the selected backend; no arbitrary environment variables or R code are accepted. VS Code
publishes endpoint and renderer together in one atomic file replacement, with
an empty jgdSocket when its current backend does not provide JGD.
Fields belonging to other backends remain optional and are interpreted only by
implementations that support them. terminalPid is VS Code-private metadata for
finding managed terminal discovery files; sess does not use it as identity.
Once connected, sess registers runtime integrations that redirect
R's interactive features to the client:
| R feature | Behavior |
|---|---|
View() |
Data frames, matrices, Arrow tables and polars data frames open in a paged, sortable, filterable data viewer. Lists open as JSON; other objects as R code. |
browseURL(), viewer, page_viewer |
URLs and local HTML files (e.g. htmlwidgets) open in the editor. |
?topic, help.search() |
Help pages open in the editor's help panel, in the column configured by r.session.viewers.viewColumn.helpPanel. |
| Graphics device | Plots appear in the editor's plot viewer unless plot_backend = "native". |
rstudioapi |
Editor functions such as getActiveDocumentContext() and insertText() are emulated when use_rstudioapi = TRUE. |
| Top-level task callback | The client is notified after each command so it can refresh the workspace view. |
These changes are undone when the connection closes. sess removes its task
callbacks, closes its graphics devices, and restores any options, bindings, S3
methods and plot hooks it replaced (unless other code has since changed them).
A plot held only by a jgd device may not survive a disconnect or window reload.
For displaying R plots, sess chooses a graphics device in this order when
plot_backend = "auto":
- jgd, if
JGD_SOCKETis set and the jgd package is installed. - httpgd, if the httpgd package is installed.
- Standard: plots are recorded on a null device and re-rendered by the client on demand at the viewer's size (as SVG via svglite if installed, otherwise PNG).
In VS Code, this is controlled by the r.plot.backend setting.
plot_backend = "native" leaves the existing R graphics device option, plot
hooks, plot task callbacks, and devices untouched. The standard backend continues
to use the static plot viewer. When plot_backend is omitted or NULL, auto
is used.
| Name | Type | Purpose |
|---|---|---|
SESS_ENDPOINT |
env var | Socket/pipe path used by connect(). |
SESS_RSTUDIOAPI |
env var | TRUE/FALSE; passed as use_rstudioapi by the extension's R profile. |
SESS_PLOT_BACKEND |
env var | auto, standard, httpgd, jgd or native; passed as plot_backend by the extension's R profile. |
JGD_SOCKET |
env var | Socket used by the jgd device; set by the extension. |
sess.quiet |
R option | Set to TRUE to suppress the successful connection message. Connection failures remain visible. |
This section is for developers writing or debugging a client.
- Transport: Unix domain socket (macOS/Linux) or named pipe (Windows).
sessis the connecting side; the client listens. - Framing: JSON Lines. Each message is one
JSON-RPC 2.0 object followed by
\n. Receivers buffer incoming data and dispatch complete lines only. - Messages: standard JSON-RPC 2.0 notifications (no
id), requests (withid) and responses (resultorerror). Unknown request methods receive error-32601(Method not found). - Coordinates: row and column positions in
rstudioapi/*messages are 1-indexed, as in R.
On connecting, sess sends an attach notification:
{
"jsonrpc": "2.0",
"method": "attach",
"params": {
"protocol_version": 2,
"sess_version": "3.0.0",
"session_id": "sess-session-...",
"host": "compute42",
"version": "4.5.0",
"pid": 12345,
"tempdir": "/tmp/Rtmp.../sess",
"wd": "/path/to/project",
"info": {
"command": "/usr/bin/R",
"version": "R version 4.5.0 (...)",
"start_time": "2026-05-05 06:00:00"
}
}
}protocol_version versions the message contract independently of the R package
version. The extension rejects unsupported versions. session_id identifies an
R process across reconnects; PID and host are metadata, not connection identity.
The attached socket's lifetime controls cleanup. A replacement socket with the
same identity supersedes the old one; a late close cannot remove its replacement.
A fork child receives its own identity. Terminal PID association is local-only.
Protocol version 2 includes the viewer-state generation contract: dynamic table
and list dataview notifications provide state_generation, and
dataview_dispose requires it. Both the extension and sess must support version
2. The extension/webview documentGeneration field is internal to the client
and is not part of the sess IPC contract.
Sent with notify_client().
| Method | Params | Sent when |
|---|---|---|
attach |
see above | Connection is established. |
workspace_updated |
none | A top-level command completes. |
dataview |
title, source, type; view_id and state_generation for tables/lists, plus navigation for lists; file for other objects |
View() is called. |
plot_updated |
none | The standard device records a new or changed plot. |
httpgd |
url |
An httpgd device is opened. |
help |
requestPath |
A help page or help search is printed. |
browser / webview / page_viewer |
url |
The corresponding R viewer option is invoked. |
restart_r |
command, clean |
rstudioapi::restartSession() is called. |
rstudioapi/send_to_console |
code, execute, focus, animate |
rstudioapi::sendToConsole() is called. |
Sent with request_client(), which blocks until the matching response
arrives. All are used by rstudioapi emulation:
rstudioapi/active_editor_context, rstudioapi/document_context,
rstudioapi/insert_or_modify_text,
rstudioapi/replace_text_in_current_selection,
rstudioapi/set_selection_ranges, rstudioapi/navigate_to_file,
rstudioapi/document_new, rstudioapi/document_save,
rstudioapi/document_save_all, rstudioapi/document_close,
rstudioapi/get_project_path, rstudioapi/show_dialog,
rstudioapi/ask_for_password.
| Method | Params | Result |
|---|---|---|
workspace |
none | globalenv (objects with class, type, length, ...), search, loaded_namespaces |
workspace_children |
name, path, start |
children, next_start (paged expansion of lists, environments, S4/R6 objects) |
hover |
expr |
str: the str() output of the evaluated expression |
completion |
expr, trigger ($ or @) |
Array of {name, type, str}, where str is the element's class |
plot_latest |
width, height, format (svglite or png), devArgs |
format, data (base64) |
dataview_init |
view_id |
columns, totalRows |
dataview_page |
view_id, startRow, endRow, sortModel, filterModel |
rows, totalRows, totalUnfiltered, lastRow |
dataview_dispose |
view_id, state_generation |
true |
state_generation is an integer issued by sess when viewer data is registered.
It changes when data for an existing view_id is replaced. When closing a viewer,
the client sends the state_generation from its latest dataview notification.
Disposal deletes data only if the generation matches the current registration;
a missing or mismatched generation leaves the data intact and still returns
true. This prevents a delayed close request from deleting replacement data.
Example exchange:
{"jsonrpc":"2.0","id":4,"method":"completion","params":{"expr":"mtcars","trigger":"$"}}
{"jsonrpc":"2.0","id":4,"result":[{"name":"mpg","type":"double","str":"numeric"},{"name":"cyl","type":"double","str":"numeric"}]}