Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9b7f401
feat: add managed tool library helpers
eitsupi Oct 25, 2026
373b271
test: cover managed tool library installation and loading
eitsupi Oct 25, 2026
b832d7c
fix: distinguish tool installation and update failures
eitsupi Oct 25, 2026
d87efc6
test: normalize managed tool library fixture paths
eitsupi Oct 25, 2026
64a0124
refactor: share file-based installation consent transport
eitsupi Oct 25, 2026
1b6c965
feat: request consent for managed tool installation plans
eitsupi Oct 25, 2026
b868ece
fix: normalize repository URLs and simplify tool plan validation
eitsupi Oct 25, 2026
40fae1a
fix: limit automatic tool prompts per extension host session
eitsupi Oct 25, 2026
018284b
fix: resolve consent lint and test ordering failures
eitsupi Oct 25, 2026
7727e2d
feat: prepare managed jgd before terminal session connection
eitsupi Oct 25, 2026
df36aa1
test: cover managed jgd startup and attachment
eitsupi Oct 25, 2026
bf7ffab
fix: align managed jgd consent with session startup
eitsupi Oct 25, 2026
3485d19
fix: store automatic jgd prompt dismissal in global state
eitsupi Oct 25, 2026
779aecf
docs: shorten managed jgd installation notes
eitsupi Oct 25, 2026
f670f27
fix: scope jgd prompt dismissal to the minimum version
eitsupi Oct 25, 2026
628fef2
style: group jgd version imports with local modules
eitsupi Oct 25, 2026
181a123
fix: clarify managed package recovery and consent format
eitsupi Oct 25, 2026
bb14b55
fix: preserve jgd preparation decisions through discovery reconnects
eitsupi Oct 25, 2026
15a51ed
chore: bump sess development version for allow_jgd
eitsupi Oct 25, 2026
0709010
ci: run managed jgd preparation tests after installing sess
eitsupi Oct 25, 2026
7872c96
fix: inherit child R test environments on Windows
eitsupi Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,16 @@ jobs:
- run: pnpm install --frozen-lockfile
- name: Verify sess source identity and bootstrap
run: pnpm run test:sess-source
- name: Verify managed tool library
run: pnpm run test:tool-library
- name: Verify managed tool consent
run: pnpm run test:tool-consent
- name: Install R test dependencies
run: install.packages(c("remotes", "pkgbuild", "languageserver", "lintr", "renv"))
shell: Rscript {0}
- run: pnpm run build
- name: Verify managed jgd preparation
run: pnpm run test:prepare-jgd
- name: Verify installed sess identity
run: |
source("R/sess_source.R")
Expand Down
62 changes: 60 additions & 2 deletions R/attach_sess.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# Shared terminal and manual-attach preparation. Installation requires a
# single-use grant from the extension process; .libPaths() is never changed.
vscode_r_with_jgd <- function(
plot_backend,
connect,
jgd_helper = Sys.getenv("VSCODE_R_JGD_HELPER", unset = ""),
tool_root = Sys.getenv("VSCODE_R_TOOL_ROOT", unset = ""),
tool_consent_directory = Sys.getenv("VSCODE_R_TOOL_CONSENT_DIRECTORY", unset = ""),
tool_library_helper = Sys.getenv("VSCODE_R_TOOL_LIBRARY_HELPER", unset = ""),
tool_consent_helper = Sys.getenv("VSCODE_R_TOOL_CONSENT_HELPER", unset = ""),
can_prompt = identical(Sys.getenv("VSCODE_R_JGD_CAN_PROMPT", unset = "TRUE"), "TRUE"),
minimum_version = Sys.getenv("VSCODE_R_JGD_MINIMUM_VERSION", unset = "")
) {
if (!is.function(connect)) stop("connect must be a function.", call. = FALSE)
if (!plot_backend %in% c("jgd", "auto")) {
return(connect(allow_jgd = TRUE))
}
jgd_setup <- new.env(parent = environment())
loaded <- nzchar(jgd_helper) && file.exists(jgd_helper) &&
isTRUE(tryCatch({
sys.source(jgd_helper, envir = jgd_setup)
TRUE
}, error = function(error) {
message("vscode-R could not load managed jgd setup resources: ",
conditionMessage(error))
FALSE
}))
if (loaded && is.function(jgd_setup$vscode_r_connect_with_jgd)) {
return(jgd_setup$vscode_r_connect_with_jgd(
plot_backend, connect, tool_root, tool_consent_directory,
tool_library_helper, tool_consent_helper, can_prompt, minimum_version
))
}
message("vscode-R could not load managed jgd setup resources; using the available plot fallback.")
connect(allow_jgd = FALSE)
}

vscode_r_prepare_sess <- function(pkg_path, managed_root, consent_dir,
source_helper, installer_helper,
timeout_seconds = 180,
Expand Down Expand Up @@ -185,7 +220,22 @@ vscode_r_attach_sess <- function(endpoint, pkg_path, managed_root, consent_dir,
setup_timeout_seconds = 300,
use_rstudioapi = TRUE,
attachment_id = NULL,
auto_activate = TRUE) {
auto_activate = TRUE,
tool_root = Sys.getenv("VSCODE_R_TOOL_ROOT", unset = ""),
tool_consent_directory = Sys.getenv(
"VSCODE_R_TOOL_CONSENT_DIRECTORY", unset = ""
),
tool_library_helper = Sys.getenv(
"VSCODE_R_TOOL_LIBRARY_HELPER", unset = ""
),
tool_consent_helper = Sys.getenv(
"VSCODE_R_TOOL_CONSENT_HELPER", unset = ""
),
jgd_helper = Sys.getenv("VSCODE_R_JGD_HELPER", unset = ""),
jgd_can_prompt = TRUE,
jgd_minimum_version = Sys.getenv(
"VSCODE_R_JGD_MINIMUM_VERSION", unset = ""
)) {
if (!is.null(attachment_id) &&
!(is.character(attachment_id) && length(attachment_id) == 1L &&
!is.na(attachment_id) &&
Expand Down Expand Up @@ -241,7 +291,15 @@ vscode_r_attach_sess <- function(endpoint, pkg_path, managed_root, consent_dir,
args$attachment_id <- attachment_id
args$auto_activate <- auto_activate
}
do.call(connect, args)
vscode_r_with_jgd(
plot_backend,
function(allow_jgd = TRUE) {
args$allow_jgd <- allow_jgd
do.call(connect, args)
},
jgd_helper, tool_root, tool_consent_directory,
tool_library_helper, tool_consent_helper, jgd_can_prompt, jgd_minimum_version
)
}

if (profile_process) {
Expand Down
87 changes: 87 additions & 0 deletions R/prepare-jgd.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Keep optional jgd preparation and its result shared by terminal connections.
vscode_r_connect_with_jgd <- function(
plot_backend,
connect,
tool_root = Sys.getenv("VSCODE_R_TOOL_ROOT", unset = ""),
consent_directory = Sys.getenv("VSCODE_R_TOOL_CONSENT_DIRECTORY", unset = ""),
library_helper = Sys.getenv("VSCODE_R_TOOL_LIBRARY_HELPER", unset = ""),
consent_helper = Sys.getenv("VSCODE_R_TOOL_CONSENT_HELPER", unset = ""),
can_prompt = identical(Sys.getenv("VSCODE_R_JGD_CAN_PROMPT", unset = "TRUE"), "TRUE"),
minimum_version = Sys.getenv("VSCODE_R_JGD_MINIMUM_VERSION", unset = "")
) {
if (!is.function(connect)) stop("connect must be a function.", call. = FALSE)
if (!plot_backend %in% c("jgd", "auto")) {
return(connect(allow_jgd = TRUE))
}
ready <- isTRUE(vscode_r_prepare_jgd(
plot_backend, tool_root, consent_directory,
library_helper, consent_helper, can_prompt, minimum_version
))
connect(allow_jgd = ready)
}

# Prepare optional plot support without making sess startup depend on CRAN access
# or user consent. The managed library is loaded before sess checks the package.
vscode_r_prepare_jgd <- function(
plot_backend,
tool_root = Sys.getenv("VSCODE_R_TOOL_ROOT", unset = ""),
consent_directory = Sys.getenv("VSCODE_R_TOOL_CONSENT_DIRECTORY", unset = ""),
library_helper = Sys.getenv("VSCODE_R_TOOL_LIBRARY_HELPER", unset = ""),
consent_helper = Sys.getenv("VSCODE_R_TOOL_CONSENT_HELPER", unset = ""),
can_prompt = identical(Sys.getenv("VSCODE_R_JGD_CAN_PROMPT", unset = "TRUE"), "TRUE"),
minimum_version = Sys.getenv("VSCODE_R_JGD_MINIMUM_VERSION", unset = "")
) {
if (!is.character(plot_backend) || length(plot_backend) != 1L ||
is.na(plot_backend) || !plot_backend %in% c("jgd", "auto")) {
return(FALSE)
}
if (!(is.logical(can_prompt) && length(can_prompt) == 1L && !is.na(can_prompt))) {
stop("can_prompt must be a single non-NA logical value.", call. = FALSE)
}
helpers <- c(library_helper, consent_helper)
if (any(!nzchar(helpers)) || any(!file.exists(helpers)) || !nzchar(tool_root)) {
message("vscode-R could not locate managed jgd setup resources; using the available plot fallback.")
return(FALSE)
}

tryCatch({
tool_environment <- new.env(parent = environment(vscode_r_prepare_jgd))
for (helper in helpers) {
sys.source(helper, envir = tool_environment)
}
if (!can_prompt) {
# A compatible ordinary or managed copy remains usable without resolving a new plan.
return(!is.null(tool_environment$tool_library_load(
"jgd", minimum_version, tool_root
)))
}
prepared <- tool_environment$tool_library_prepare(
"jgd",
minimum_version,
tool_root,
consent_directory,
plot_backend,
consent_timeout_seconds = 180,
install_timeout_seconds = 300,
resolution_timeout_seconds = 30
)
if (is.null(prepared)) {
message("vscode-R optional jgd installation was not approved; using the available plot fallback.")
return(FALSE)
}
namespace <- tool_environment$tool_library_load(
"jgd", minimum_version, tool_root
)
if (is.null(namespace)) {
message("vscode-R could not load a compatible jgd package; using the available plot fallback.")
return(FALSE)
}
TRUE
}, error = function(error) {
message("vscode-R could not prepare optional jgd plot support: ", conditionMessage(error))
FALSE
}, interrupt = function(error) {
message("vscode-R jgd preparation was interrupted; using the available plot fallback.")
FALSE
})
}
5 changes: 4 additions & 1 deletion R/profile.R
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ invisible(local({
plot_backend <- Sys.getenv("SESS_PLOT_BACKEND", "auto")
connect <- get("connect", envir = ns, inherits = FALSE)
use_rstudioapi <- as.logical(Sys.getenv("SESS_RSTUDIOAPI", "TRUE"))
result <- connect(use_rstudioapi = use_rstudioapi, plot_backend = plot_backend)
result <- vscode_r_with_jgd(plot_backend, function(allow_jgd = TRUE) {
connect(use_rstudioapi = use_rstudioapi, plot_backend = plot_backend,
allow_jgd = allow_jgd)
})
isTRUE(result)
}

Expand Down
4 changes: 2 additions & 2 deletions R/tests/attach_sess.R
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ writeLines(c(
" stopifnot(identical(result, TRUE), same_paths(.libPaths(), ordinary))",
" stopifnot(identical(getOption('vscodeR.test.use_rstudioapi'), FALSE))",
" stopifnot(identical(getOption('vscodeR.test.connect.args'),",
" list(attachment_id='console_1', auto_activate=FALSE)))",
" list(attachment_id='console_1', auto_activate=FALSE, allow_jgd=TRUE)))",
" stopifnot(identical(sess_loaded_source_revision(), revision))",
" actual <- getNamespaceInfo(asNamespace('sess'), 'path')",
" stopifnot(same_paths(actual, file.path(expected, 'sess')))",
Expand All @@ -211,7 +211,7 @@ writeLines(c(
" file.path(getwd(), 'R', 'sess-package-install.R'), 'standard', 0)",
" stopifnot(identical(legacy_result, TRUE),",
" identical(getOption('vscodeR.test.use_rstudioapi'), TRUE))",
" stopifnot(identical(getOption('vscodeR.test.connect.args'), list()))",
" stopifnot(identical(getOption('vscodeR.test.connect.args'), list(allow_jgd=TRUE)))",
" blocked_file <- file.path(root, 'missing-directory', 'startup')",
" context <- vscode_r_startup_register(blocked_file, strrep('c', 32), 'endpoint')",
" called <- FALSE",
Expand Down
Loading
Loading