The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Backends: vscode, Copilot, Claude

hal speaks to three different agent transports behind a common R interface: the Positron vscode.lm API (via the bundled hal-bridge extension), GitHub Copilot CLI, and Anthropic Claude Code. You pick per session; everything else – pipe verbs, custom tools, use_env, governance – works the same.

The default is resolved at call time: vscode if you’re inside Positron (POSITRON_VERSION is set), copilot otherwise. Set options(hal.backend = "...") to override globally.

vscode Copilot Claude
Transport Localhost HTTP to hal-bridge extension ACP server (long-lived, JSON-RPC) claude -p per turn, resumes via uuid
Auth Your Positron Copilot sign-in (reused) GitHub Copilot subscription Claude.ai subscription (OAuth)
Install hal_install_bridge() (bundled VSIX, no download) hal_setup() installs Copilot CLI Download Claude Code, run claude once
Models Whatever vscode.lm exposes to your session 17 (GPT, Claude, Gemini) 3 (Haiku, Sonnet, Opus)
Mid-session model switch Yes (via vscode.lm model id) Yes, preserves context No (must reset)
Session modes Agent only Agent / Plan / Autopilot Agent only (others warn)
Quota visibility – (inherits Copilot) – (flat subscription) hal_quota() (5-hour window)
eval_r transport In-process (no MCP subprocess) MCP stdio subprocess MCP stdio subprocess
Where it runs Positron only Anywhere (RStudio, VS Code, terminal R) Anywhere
Typical cost Flat (subscription) Flat monthly ~$0.005–0.03 per call (Haiku)

Pick a backend

library(hal)

hal_configure(backend = "vscode")    # default in Positron
hal_configure(backend = "copilot")   # default elsewhere
hal_configure(backend = "claude")

hal_config()$backend

The setting takes effect on the next session init – call hal_reset() if you already have an active session and want to swap backends immediately.

Everything after that point is identical:

hal("explain this error")
mtcars |> hal_ask("summarize in 3 bullets")
iris |> hal_do("add petal_area = Petal.Length * Petal.Width")

vscode: when to pick it

Setup:

library(hal)
hal_install_bridge()                  # one-shot: installs bundled VSIX
# Fully quit Positron and reopen -- "Reload Window" is not enough on a
# fresh install; the extension host only loads new extensions cold.
hal_bridge_status()                   # confirms the extension is live
hal_available()                       # TRUE when port file + bridge are up

hal_install_bridge() requires only the Positron CLI on PATH (or POSITRON_BIN env var). No GitHub auth, no network call.

Models surface whatever vscode.lm exposes to your Positron Copilot session:

hal_models()                          # lists what vscode.lm has registered
hal("explain this error", model = "claude-3-5-sonnet")

If models you expect are missing, that’s a Positron / Copilot extension state issue – restart Positron or sign in again from the Copilot pane.

Copilot: when to pick it

Setup:

library(hal)
hal_setup()                           # installs Copilot CLI + guides login
hal_available()                       # TRUE when ready

Claude: when to pick it

Setup:

# Install Claude Code from https://claude.ai/download
claude               # run once interactively to complete OAuth
hal_configure(backend = "claude")
hal_available()                       # TRUE when `claude` is on PATH
hal("hello")                          # Haiku 4.5 by default

Per-entry-point model defaults

The Claude backend tunes its default model per entry point:

Entry point Default Why
hal() claude-sonnet-4-5-20250929 Multi-turn amortizes cost
hal_ask(), hal_do() claude-haiku-4-5-20251001 Disposable, cost-sensitive

Override globally or per call:

hal_configure(default_model = "claude-opus-4-5-20250902")

hal("deep reasoning", model = "claude-opus-4-5-20250902")

Session lifecycle

The Claude backend spawns a fresh claude -p subprocess per turn. The first call uses --session-id <uuid> to create a session; every subsequent call in the same R session uses --resume <uuid>. hal stores the uuid for you.

Cost tiers we measured on Haiku 4.5:

Tier When Cost
Cold Empty account cache $0.05–0.07
Warm Recent Claude Code activity $0.02–0.03
Resumed Successive calls in the same session $0.005

Extrapolated per-model (first call / resumed call):

Model First Resumed
Haiku 4.5 $0.02–0.03 $0.005
Sonnet 4.6 $0.05–0.07 $0.01–0.015
Opus 4.7 $0.12–0.17 $0.03–0.04

Note: total_cost_usd is notional on subscription auth – you pay in tokens against the 5-hour window, not dollars. It’s still a useful burn-rate proxy.

Quota: hal_quota()

Every Claude response stream carries a rate_limit_event with the 5-hour window status. hal_quota() surfaces it:

hal("hello")
hal_quota()
#> -- hal quota (claude) ----------------------------------------
#> i Window: "five_hour"
#> v Status: "allowed"
#> i Resets: 2026-04-23 17:30:00 PDT
#> i Overage: "allowed"

hal_quota() returns NULL on the Copilot backend (flat subscription – there’s nothing to show).

Fields returned as a hal_quota list:

What Claude doesn’t support

hal’s Claude client stubs these with a warning rather than pretending:

chat <- hal_chat()

chat$switch_model("claude-opus-4-5-20250902")
#> ! Mid-session model switching not available on Claude backend.
#> i Start a new session with `hal_reset()` and a different default model.

chat$set_mode("plan")
#> ! Session modes (plan/autopilot) not available on Claude backend.

To switch models on Claude, reset the session:

hal_configure(default_model = "claude-opus-4-5-20250902")
hal_reset()

Mock CLI for offline testing

The Copilot and Claude backends each ship with a mock CLI under inst/mock-cli/ so CI and local unit tests don’t need network, login, or credits:

The vscode backend has no mock equivalent – its transport is a real localhost HTTP server inside Positron. vscode-backend tests stub the bridge at the R level (port file + handler shim) rather than running a fake extension.

Scenarios are passed as the first positional arg (basic, echo, thinking, tool_use, tool_roundtrip, multi_turn, rate_limit, error, slow). See tests/testthat/helper-mock-client.R for the harness pattern.

See also

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.