Deputy turns an ellmer chat into a governed R Agent. Give it tools, a workspace, and explicit limits; get back an observable run you can inspect, stream into Shiny, save, or delegate.
Why Deputy?
ellmer gives R a provider-independent chat interface and tools. Deputy adds the runtime around that chat when a tool-using conversation becomes a real job:
| Start with ellmer | Add Deputy when you need |
|---|---|
| Chats and provider APIs | Explicit tool permissions and run limits |
| Tool registration | Hooks, semantic events, and inspectable results |
| Streaming model output | A stable stream for terminals and Shiny hosts |
| Conversation state | Explicit session persistence and file checkpoints |
| One tool-using chat | Correlated delegation to specialist Agents |
The core object model stays small:
ellmer Chat + Tools + Permissions + Limits
|
Deputy Agent
|
AgentResult + Events + Checkpoints
Installation
Deputy currently follows development versions of ellmer:
For the optional Shiny host, also install shinychat:
pak::pak("posit-dev/shinychat")A safe first run
Start with a small, read-only toolset and a bounded run. This setup is provider-independent and is executed whenever the README is rendered:
library(deputy)
workspace <- normalizePath(getwd(), winslash = "/")
first_tools <- tools_preset("minimal")
first_permissions <- permissions_readonly()
first_limits <- UsageLimits(max_requests = 6, max_tool_calls = 8)
stopifnot(
length(first_tools) == 3L,
identical(first_permissions$mode, "readonly"),
identical(first_limits$max_requests, 6L)
)Then choose any provider supported by ellmer and run a task. The model call is not executed while building the documentation:
chat <- ellmer::chat("openai")
agent <- Agent$new(
chat = chat,
tools = first_tools,
permissions = first_permissions,
usage_limits = first_limits,
working_dir = workspace
)
result <- agent$run_sync(
"Explain what this R package does. Support the answer with file paths."
)run_sync() returns an AgentResult, not just text:
cat(result$response)
result$stop_reason
result$usage
result$tool_calls()
result$tool_results()Read Getting Started to turn on a workspace-scoped write permission deliberately, create a file checkpoint, and recover from common failures.
The safety boundary
Deputy enforces application-level policy at tool boundaries. It does not turn model-generated code into untrusted code you can execute safely:
-
permissions_readonly()denies Deputy’s write, shell, R execution, web, and package-install capabilities. - A directory-valued
file_writepermission confines Deputy’s native file writes to that canonical root. File reads remain limited by the R process, not by an operating-system sandbox. -
run_r_codeuses a separate R process; that process separation is not an OS security sandbox.run_bashexecutes with the current user’s privileges. - File checkpoints cover Deputy’s native write, edit, and multi-edit tools. They are recovery machinery, not a substitute for version control or backups.
Start read-only, grant the narrowest capability that completes the job, and use a real isolation boundary for untrusted code.
Choose your path
| Goal | Read next |
|---|---|
| Understand tools and presets | Tools |
| Design a least-authority policy | Permissions and Safety |
| Observe or intervene in a run | Hooks |
| Embed an Agent in an app | Shiny Chat |
| Delegate to specialist Agents | Multi-Agent Orchestration |
| Build a concrete workflow | Recipes |
Terminal use
Deputy also ships a Rapp package executable. Run it without a global Deputy installation, or install a persistent launcher:
Status
Deputy is experimental. Its contracts are being sharpened through real package, CLI, and Shiny integrations. Please report problems and design gaps in GitHub Issues.