Skip to contents

codeagent is an R-native coding agent: it wraps any ellmer chat with a full agent harness (tools, permissions, compaction, hooks, skills) and drives it from the R console, a terminal REPL, or a Shiny app. This vignette walks from a first query to configuration and sandboxing.

All code chunks below require a configured model endpoint and are therefore not evaluated when the vignette is built.

How it works

codeagent_client(chat)   builds the harness once:
  - injects tools (built mode="bypass") + system prompt
  - register_midloop_compaction()  -> chat$on_tool_result
  - .install_permission_gate()     -> chat$on_tool_request   (sole authority)

One user turn = agent_loop(input, client, iteration)

  iteration > max_turns? ---- yes ---> stop "max_turns"
    | no
  SessionStart hook (first turn)
  inject system-reminder into the USER message (preserves prompt cache)
  budget check ---- over ---> stop "budget_exceeded"
  TURN-BOUNDARY compaction   (if tokens >= window - margin)
  UserMessage hook
    |
    v
  chat$chat(input)  -- ellmer runs the multi-round tool loop in-process --+
    |   per tool round:                                                   |
    |     on_tool_request -> PERMISSION GATE -> allow / deny / ask         |
    |     tool executes                                                    |
    |     on_tool_result  -> MID-LOOP compaction + PostToolUse hook        |
    +-- loops until the model stops requesting tools -------------------->+
    |
    v
  final assistant turn
  finish_reason == "length"? -> append truncation note
  AssistantMessage hook
  verify_fn?  -- fail --> re-enter chat$chat(re-prompt) once
  save_session()
  Stop hook "completed"  ->  return {response, session_id, stop_reason}

Installation

# Install from GitHub (pak handles all dependencies including Rapp)
pak::pak(c("tidyverse/ellmer", "kaipingyang/codeagent"))

The recommended way to configure codeagent is a user-level settings file. Create it with:

codeagent::use_codeagent_settings()   # creates ~/.codeagent/settings.json from template

Edit the file to add your endpoint and credentials:

{
  "model": "main",
  "provider": "openai_compatible",
  "env": {
    "CODEAGENT_BASE_URL": "https://YOUR-WORKSPACE.cloud.databricks.net/serving-endpoints",
    "CODEAGENT_MODEL": "your-main-endpoint",
    "CODEAGENT_HEAVY_MODEL": "your-heavy-endpoint",
    "CODEAGENT_FAST_MODEL": "your-fast-endpoint",
    "CODEAGENT_API_KEY": "your-token"
  },
  "permissions": {
    "defaultMode": "default"
  }
}

Note: CODEAGENT_API_KEY in the env block is loaded at startup, so you do not need to set it separately in .Renviron.

Alternative: ~/.Renviron

Set the minimum required variables and restart R:

CODEAGENT_BASE_URL=https://YOUR-WORKSPACE.cloud.databricks.net/serving-endpoints
CODEAGENT_MODEL=your-main-endpoint
CODEAGENT_API_KEY=your-token

All CODEAGENT_* variables in ~/.Renviron are loaded automatically, even when running under --vanilla (e.g. via the CLI).

CLI

Installing codeagent does not automatically put a codeagent command on your PATH. Run this once after installation:

Then use from any terminal:

codeagent           # interactive REPL (default permission mode)
codeagent -y        # bypass mode (skip all permission prompts)
codeagent "query"   # one-shot query
codeagent -p "query"  # one-shot, non-interactive output

1. Build a client

A codeagent client wraps an ellmer::Chat. With settings.json configured, the simplest form is:

library(codeagent)

# Auto-build from ~/.codeagent/settings.json
client <- codeagent_client()

Or provide an explicit ellmer::Chat:

chat <- ellmer::chat_openai_compatible(
  base_url    = Sys.getenv("CODEAGENT_BASE_URL"),
  model       = Sys.getenv("CODEAGENT_MODEL"),
  credentials = function() Sys.getenv("CODEAGENT_API_KEY")
)

client <- codeagent_client(chat)

codeagent_client() injects the tool set (Bash, Read, Write, Edit, Glob, Grep, LS, plus btw tool groups) and builds the system prompt. The permission_mode controls how tool calls are gated – see section 5.

2. One-shot queries

codeagent(client, "List the .R files in R/ and summarise what each does")

The agent plans, calls tools, and returns a final answer. History accumulates on the client, so follow-up calls keep context.

3. Interactive REPL

From R:

Or from a terminal (after running install_codeagent_cli() once):

codeagent           # interactive REPL (default permission mode)
codeagent -y        # bypass mode (skip all permission prompts)
codeagent "query"   # one-shot query

Inside the REPL, slash commands include /model, /compact, /clear, /rewind, /sessions, and /budget; /name invokes a skill.

4. Shiny app

codeagent_app(client, theme = "default")

The app streams output, renders tool cards, and provides session management and a searchable skill panel.

5. Permissions

Permission mode decides whether a tool call is allowed, denied, or requires confirmation:

Mode Behaviour
default reads auto-allow; writes and shell require confirmation
plan read-only; all mutating tools denied
accept_edits file edits auto-allow; Bash still asks
bypass everything allowed
dont_ask mutating tools auto-denied (CI)
bubble sub-agent mode; decisions bubble to the parent

Fine-grained rules match on tool arguments – for example, allow only a specific command:

client <- codeagent_client(
  chat,
  permission_mode = "default",
  rules = list(PermissionRule("Bash", "allow", rule_content = "R CMD build *"))
)

6. Configuration with settings.json

Scaffold a settings file:

use_codeagent_settings(scope = "user")

It mirrors the schema of command-line coding agents (model tiers, an env block, permissions, hooks, MCP servers, sandbox, effort level). Precedence, low to high: package defaults, then ~/.codeagent/settings.json, then .codeagent/settings.json, then environment variables.

Keep API keys in .Renviron (as CODEAGENT_API_KEY), never in settings.json.

7. Sandboxed R execution

The RunR tool executes R code behind the permission gate. Enable sandboxing to run each call in an isolated callr subprocess with a scrubbed environment (API keys are not visible), no .Renviron reload, and a wall-clock timeout:

{ "sandbox": { "enabled": true, "allow_network": false } }

Sandboxing is opt-in because spawning a subprocess has a small cost. Turn it on when executing less-trusted code; leave it off for fast, local, trusted use.

8. Multi-agent teams

Run independent tasks in parallel, capped to the container’s CPU quota:

# Fixed fan-out
team_run(c("review R/a.R", "review R/b.R"))

# Work-stealing over a shared board (balances uneven task sizes)
team_coordinate(c("task 1", "task 2", "task 3", "task 4"))

Where to go next

  • ?codeagent_client – all client options (tool groups, verification, MCP).
  • ?codeagent_app – Shiny app themes and panels.
  • README – feature overview and provider reference.