Skip to content

Latest commit

 

History

History

sess

Folders and files

README.md

sess — A high-performance IPC bridge for R sessions

R-universe version

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.

Installation

Bundled install for VS Code

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.

Manual install

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"))
)

Usage

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:

  1. The SESS_ENDPOINT environment variable.
  2. The endpoint field of the JSON file named by SESS_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.

Discovery schema and extensions

{"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.

What sess changes in your R session

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.

Graphics devices

For displaying R plots, sess chooses a graphics device in this order when plot_backend = "auto":

  1. jgd, if JGD_SOCKET is set and the jgd package is installed.
  2. httpgd, if the httpgd package is installed.
  3. 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.

Options and environment variables

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.

Protocol reference

This section is for developers writing or debugging a client.

Transport and framing

  • Transport: Unix domain socket (macOS/Linux) or named pipe (Windows). sess is 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 (with id) and responses (result or error). Unknown request methods receive error -32601 (Method not found).
  • Coordinates: row and column positions in rstudioapi/* messages are 1-indexed, as in R.

Handshake

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.

Notifications from R to client

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.

Requests from R to client

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.

Requests from client to R

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"}]}