Skip to contents

hal 0.1.4

  • Plot vision: the model can now see your plots. When eval_r code draws a plot – base graphics or a returned ggplot/lattice object – hal captures it as PNG and attaches it to the tool result as an image, so the model can describe, critique, and iterate on actual visual output (“make this plot publication-ready” now works on what the plot looks like). Supported on the vscode backend (requires the bundled hal-bridge 0.1.4) and the claude backend (MCP image content blocks). The copilot backend stays text-only until its CLI’s image forwarding is verified. Disable with hal_configure(plot_vision = FALSE). Notes: returned ggplot objects are also printed to your device (they now appear in the Positron plots pane); render errors surface to the model as a ## Plot render error section; plots written to file devices the code opens itself are not echoed; one image per eval (the final page); images larger than 5 MB are skipped.

  • hal-bridge 0.1.4 (bundled): tool results can carry an image part (LanguageModelDataPart.image); if the selected model rejects image input at runtime, the bridge strips images and retries once text-only; request bodies are capped at 20 MB (413 beyond); minimum Positron/VS Code engine raised to 1.103 (first stable release with the image API). The vscode client also prunes all but the newest 2 images from the resent history so long sessions don’t re-bill every old plot.

  • hal_do() now verifies its transforms. After a successful pipe-mode transform, hal compares input and output and prints a one-line structural report – row deltas, columns added/removed, class changes, introduced NAs (hal_do: 32 -> 14 rows | +1 col (kpl)). The full report is attached as attr(result, "hal_verify") (print it for details). Suspicious patterns – output identical to input, or 0-row output – raise a classed hal_do_warning. Report-only by design: deltas are often intentional (“filter mpg > 20” should shrink the data), so verification never triggers retries or changes values. Disable with .verify = FALSE or hal_configure(verify = FALSE).

  • Fixed: the default model on the copilot and vscode backends no longer exists. Both defaulted to "claude-sonnet-4.6", which GitHub Copilot and vscode.lm have since dropped from their catalogs — neither backend serves that id any more. Both now default to "claude-sonnet-5", verified present in both live catalogs. The claude backend was unaffected: it passes the "sonnet" alias, which the CLI resolves server-side and which cannot go stale this way.

  • Fixed: stale model metadata. The Claude backend’s alias descriptions still named Sonnet 4.6 and Opus 4.8 as the current tiers (now Sonnet 5 and Opus 5), and reported a 200K context window for all three aliases — Sonnet and Opus are 1M natively, and only Haiku is 200K. Context windows for the newer Copilot/vscode.lm ids (claude-opus-4.8, claude-opus-5, claude-sonnet-5) were missing and reported as NA. Documentation examples naming retired ids were updated.

  • Missing Copilot? hal now points you at Claude if you have it. When hal falls back to the copilot backend and can’t find the Copilot CLI, but Claude Code is installed, the error, hal_status(), and hal_setup() now say so and give the one command that gets you working: hal_configure(backend = "claude"). The install instructions are still shown. Default backends are unchanged — this only changes the message in a case that already failed. Detection checks the PATH and CLAUDE_CLI_PATH only; it never runs a subprocess.

  • Fixed: slow eval_r calls could abort a healthy Claude turn. Tool calls execute synchronously inside the Claude client’s response loop, but the prompt deadline only reset when the CLI produced output – so time spent running your R code counted as the model stalling. A single slow eval (eval_timeout defaults to 30s, roughly double that when plot vision re-renders) could exhaust the 60s prompt_timeout and abort a turn that was progressing normally. Time spent servicing tool calls is now credited back to the deadline.

  • Fixed: a failed Claude turn wedged the session. The client marked a session as created only after a successful turn. If a turn timed out or the CLI died mid-stream, the session already existed in Claude Code’s store but hal did not know it – so every subsequent prompt re-sent --session-id with a live uuid and was rejected, until hal_reset(). The session is now latched as soon as the CLI announces it, so a failed turn is simply retried with --resume. Timeout messages say so.

  • Fixed: hal_setup(backend = "claude") installed the Copilot CLI. The documented "claude" value had no branch in hal_setup(), so it fell through to the Copilot path and either mislabelled Claude as Copilot or ran npm install -g @github/copilot. There is now a real Claude branch that verifies the claude binary, reports its version, and points at the download plus one-time OAuth step. It deliberately does not auto-install: Claude Code’s sign-in is interactive, and an npm install produces exactly the .cmd shim whose stdio processx drops on Windows.

  • Fixed: MCP subprocess crash on R 4.1–4.3. The generated MCP server script used %||% without defining it; base R only gained %||% in 4.4.0. The script now defines it, matching the package’s declared R >= 4.1 support.

  • Fixed: blank destination in the first-run data notice on the vscode backend. The one-time “hal sends prompts and tool results to the …” notice had no vscode branch and rendered an empty slot; it now names the model behind your Positron Copilot sign-in.

  • CRAN preparation.

    • hal() now asks before creating hal.md. Previously it wrote the project memory file into your working directory on first use and told you afterwards. It now offers — “Create hal.md?”, default no — and writes only on an explicit yes. It never asks in a non-interactive session, asks at most once per folder per R session, and a decline is not undone by hal_reset(). Turn the offer off with options(hal.memory_prompt = FALSE). An existing hal.md is still read in any context, whatever you answered.
    • Removed the never-shipped hal_query_map surface: the hal_configure(max_spawn =) parameter and hal.max_spawn option documented a function that does not exist.
    • Mock-CLI tests (which spawn local Rscript subprocesses) are skipped on CRAN; the rest of the suite runs there.
    • DESCRIPTION rewritten per CRAN conventions; stale LICENSE copyright holder fixed; .vscode/ excluded from the build.

hal 0.1.3

  • excelR() is now hal_excel(). Clean rename (no alias) to match the hal_ prefix convention before anyone depends on it. The result class is hal_excel_code and the verification report attribute is attr(x, "hal_excel"). Functionality is unchanged.

  • New hal_status(): one traffic-light diagnostic for the whole stack. Reports the resolved backend (and why), whether its transport is reachable, session state, and — when something is wrong — the single next step to fix it. Returns a structured list invisibly for programmatic use. This is the first thing to run when hal misbehaves, and the thing to paste into a bug report.

  • Positron setup no longer requires the GitHub CLI. hal_setup() was still gating the vscode path on gh being installed and gh auth status passing — a leftover from before the bridge VSIX was bundled (0.1.1). The gate is gone; the only external requirement (a Copilot sign-in inside Positron itself) is now surfaced as a reminder in the next-step bullets, since hal cannot verify it from R.

  • hal_do() now aborts on failure in non-interactive contexts. Previously a failed generation warned and passed .data through unchanged everywhere — in a script or R Markdown pipeline that means silently continuing with untransformed data. Interactive sessions keep the forgiving warn-and-passthrough; scripts get an error. Override either way with hal_configure(do_on_fail = "warn"|"abort").

  • Default hal_do() retries raised from 1 to 2 (hal.do_retries), matching the default-on edit-in-place behavior: more self-correction before giving up. hal_excel() inherits the same default.

  • hal failures now signal classed conditions. hal() transport failures abort with hal_transport_error in non-interactive contexts; hal_do() signals hal_do_error / hal_do_warning; hal_ask() signals hal_ask_warning. All inherit from hal_error / hal_warning, so programmatic callers can tryCatch(..., hal_error = ...) instead of string-matching messages.

  • Environment auto-detect is harder to false-positive. hal()’s use_env = NULL auto-detection no longer injects your environment when a prompt merely contains English function words that collide with object names (“show me all the columns on that table” with objects all/on). Backtick-quoting, $/[ subsetting, or calling the object still triggers injection deliberately. Data-science nouns (data, model, fit, …) still match by name — missing real context costs more than a small extra snapshot.

  • Website URL fixed. DESCRIPTION, _pkgdown.yml, and the README pointed at d-m4rk.github.io/hal (404); the site deploys at arclite-red.github.io/hal.

hal 0.1.2

  • hal-bridge discovery file moved out of the system temp dir (bridge 0.1.2). The bridge previously wrote its port + token to %TEMP%\hal-bridge.port, which the OS garbage-collects (e.g. Windows Storage Sense). After a day or two the file was swept while the bridge was still listening, so hal_bridge_status() reported the bridge as gone and users reinstalled needlessly. The discovery file now lives in a durable per-user app-data dir — %LOCALAPPDATA%\hal-bridge\port.json on Windows, $XDG_RUNTIME_DIR/hal-bridge/port.json (else ~/.cache/hal-bridge/port.json) on POSIX. Requires the bundled bridge 0.1.2; older bridges write the old location and won’t be found — reinstall with hal_install_bridge(force = TRUE) and cold-restart Positron.

  • hal_config()$backend now reports the resolved backend, not the raw option. Previously it read getOption("hal.backend", "copilot") with a hardcoded literal fallback, so in Positron it lied and said "copilot" even though hal() would actually route to "vscode". Now uses .hal_backend() — the same resolver hal() uses — so the reporter and the router agree.

  • Bridge install messaging now tells users to fully quit Positron, not just “Reload Window”. On a fresh extension install the extension host only loads new extensions on a cold start; “Reload Window” is insufficient and leaves users stuck at hal_bridge_status() reporting the bridge as not installed. Updated in: hal_install_bridge() success message, .hal_bridge_discover() not-found error, hal_setup() next- step bullets, and the backends vignette install snippet. Runtime reload prompts (crashed bridge, 401 token rotation) still say “reload Positron” since Reload Window is sufficient there.

hal 0.1.1

  • hal_install_bridge() now installs from a bundled VSIX. The hal-bridge extension (7 KB) ships in inst/extdata/ and is installed directly into Positron — no GitHub download, no auth token, no SHA pin. Removes the install-time gh auth login / GITHUB_PAT requirement that broke fresh installs on machines without a token. local_path = "..." still works for testing dev builds; version and verify arguments are gone (no longer meaningful). To bump the bridge, ship a new hal release with the updated VSIX in inst/extdata/.

  • Internal: deleted .hal_download_bridge_vsix, .hal_github_token, .hal_github_api_get, .hal_github_download_asset, .hal_sha256_file, and the BRIDGE_SHA256 / BRIDGE_REPO constants. Dropped digest and openssl from Suggests.

hal 0.1.0

Initial public release.

  • New vscode backend – talks to Positron’s built-in vscode.lm API via a small localhost HTTP bridge (the hal-bridge Positron extension). Skips the Copilot CLI entirely: no Node.js, no @github/copilot package, no --additional-mcp-config plumbing. Tool calls round-trip directly through R, so eval_r runs without an MCP subprocess.

    • hal_install_bridge() – downloads the pinned VSIX from the hal-bridge private GitHub release and installs it into Positron. GitHub auth comes from gh auth token, git credential helper, or GITHUB_PAT (in that order); no separate token setup needed for most users.
    • hal_bridge_status() – ping/version/port diagnostics.
    • hal_setup() now detects Positron and walks through the bridge install path automatically; pass backend = "copilot" to force the legacy CLI flow.
    • hal_models() queries the bridge’s /models endpoint and returns whatever vscode.lm exposes to your Copilot session.
    • hal_available() is backend-aware: takes backend = "..." to probe a specific transport without changing the session default.
    • permission_policy is now honored on the vscode backend (previously accepted for API parity but silently always auto-allowed). Supports "auto-allow" (default), "auto-deny", "ask" (interactive prompt; denies in non-interactive sessions), or a function receiving list(backend = "vscode", tool_name, input) and returning a string containing "allow" or "deny". Denied calls are surfaced to the model as a tool error and recorded with status = "denied".
  • Smart-default backend – when hal.backend is unset, hal now resolves to "vscode" inside Positron (POSITRON_VERSION set) and "copilot" everywhere else. Existing users with options(hal.backend = "...") are unaffected.

  • BREAKING: Removed the blackboard API (hal_bb_put(), hal_bb_get(), hal_bb_list(), hal_bb_rm(), hal_bb_clear(), hal_bb()). Replaced with use_env parameter on hal() that auto-injects the caller’s environment snapshot so the model uses eval_r directly on your objects. No manual staging needed — hal("analyze df", use_env = TRUE) just works.

  • BREAKING: Removed allow_eval parameter from hal() and hal_ask(). eval_r tool is now always registered at session init. It is inert without the use_env system prompt nudge. write_r_script removed (redundant with the SDK’s built-in create tool).

  • Bidirectional IPC: eval_r tool calls execute in the user’s live R session via file-based message passing between the MCP subprocess and parent R session. Model can inspect and modify caller environment objects.

  • hal_do() retry mechanism: automatic retry on code parse/execution failure, model self-corrects within the same disposable session. Configurable via hal_configure(do_retries = N).

  • Enhanced environment descriptions in hal_do() standalone mode: data frame column names/types, function bodies for small functions.

  • hal_configure() now accepts permission_policy and session_quiet for controlling the global session without using the R6 API.

  • hal_chat() and hal_client() wrappers now accept permission_policy and quiet directly.

  • hal_setup() – guided CLI installation and authentication helper. Checks for Node.js, installs the Copilot CLI via npm, and walks through copilot login.

  • Full functional API: hal(), hal_ask(), hal_do(), hal_history(), hal_reset().

  • Pipe-friendly verbs: hal_ask() for data-aware analysis, hal_do() for code generation (pipe and standalone modes).

  • Edit-in-place: hal_do() replaces itself in the editor with generated code when running from an IDE script.

  • Configuration via hal_configure() / hal_config() with hal.* options.

  • Custom tool calling via MCP: hal_tool(), hal_register_tool(), hal_register_package_tools(), hal_register_tool_specs().

  • Governance layer: eval_r denylist (AST walker), credential scanner, eval timeout, spawn cap.

  • ANSI-colored output formatting with optional typewriter streaming.

  • High-level chat API via HalChat R6 class, mirroring the ellmer::Chat interface.

  • Low-level ACP transport via HalClient R6 class (NDJSON over stdio).

  • S3 data objects: hal_response, hal_turn, hal_tool_call with print/format methods.

  • Mid-session model switching via $switch_model().

  • Session modes: $set_mode("agent") or $set_mode("plan").

  • Cancel support: Ctrl+C interrupt + $cancel() method.

  • Streaming callbacks: on_text, on_tool_call, on_thought.

  • Permission handling for built-in tool calls ("auto-allow", "auto-deny", or custom function).

  • hal_available() checks for a working Copilot CLI installation.

  • hal_models() lists available models and usage multipliers.

  • CLI discovery via standalone copilot binary, gh copilot --, or COPILOT_CLI_PATH env var.

  • Tool registration compatible with ellmer::ToolDef.