gptr 1.0.0 is a complete rewrite: it runs language model agents in an existing R session, where generated code can inspect and modify objects already in memory.
get_response() and dataframe_to_text(), is removed with no aliases.get_response(), use
session = peter("your prompt", model = "openai/gpt-6-sol"), which returns a session object;
its answer is session$text, and session |> peter("next prompt") continues the conversation.
The key comes from OPENAI_API_KEY (or a .env file read with gptr_env()) instead of the
api_key argument, instructions that went into system_specification go into the prompt or
the project instructions file .gptr/vignette.Rmd, and answers stream to the console when a
person is present instead of through print_response.dataframe_to_text(), pass the data frame to peter() as context, for example
peter("Which variables are correlated?", mtcars): gptr describes the object compactly and
the model computes on it in your session.model argument or with gptr_config().context = "none".
That setting still omits workspace and attached-object descriptions, while retaining project
instructions and environment details. Unattended runs need saved provider consent; offline
providers and local loopback routes remain exempt, with Ollama exempt only while its
local-only inference is on.inst/COPYRIGHTS.peter() is the single entry point. Without a prompt it opens a chat in the console; with a
prompt it runs the agent loop and returns the session. The pipe steers one session:
peter("...") |> peter("...") adds turns to the same object. gptr_fork() is the only way to
branch. Sessions are saved in the project's .gptr/ workspace (without one, until R exits)
and resumed with gptr_sessions(), gptr_resume() and gptr_last().gptr_providers(check_login = TRUE) shows CLI sign-in status.peter$grep(), peter$find() and peter$ls(); shell, Python, SQL and knitr engines
are reached through peter$sh(), peter$py(), peter$sql() and peter$knit().api = "bedrock-converse") and cost accounting where the
model supports them. The Claude plan (through the claude command-line tool, experimental)
and the ChatGPT plan (through codex) are supported without keys. See gptr_providers()
and gptr_models().peter() returns logical, choice or score vectors with
their probabilities (gptr_prob()) that work inside if, for and while, one element per
input. A generative model can emulate them; emulated answers are marked as uncalibrated.manual (default), edits, auto and plan, rules with
gptr_permissions(), and an advisory risk classifier, gptr_risk(). Deny and ask rules also
match read-only shell commands, such as r(sh:cat *). A permission question without a person
present stops the run with a classed condition. In auto and yolo mode, system(),
system2(), shell() and shell.exec() with a command the code computes (system2()'s env
included) are refused unless an allow rule such as r(fn:system) names the function; the model
is pointed to peter$sh(), whose command the rules check when it runs.auto mode too, before a Sys.setenv(), Sys.unsetenv(),
withr::local_envvar() or withr::with_envvar() call, or an os.environ write in
peter$py() or peter$knit("python", code) code, that sets or unsets R_USER_CONFIG_DIR,
XDG_CONFIG_HOME, HOME, APPDATA or USERPROFILE (the last two matter on Windows; gptr asks
on every system), which move its configuration folder or your home, as it does for a provider key.
It also asks before such an R call with a name it cannot read, and before readRenviron() and
dotenv::load_dot_env(); ?gptr_security lists the changes it does not read.peter()
call in .R, .Rmd, .qmd and .ipynb documents, also what a call ran before it failed,
and replays without model calls (gptr_doc(), gptr_source(), gptr_blocks(),
gptr_cache()); gptr_source(model =) asks a recorded analysis again with another model. A
console transcript also records a run that was aborted or stopped by an error, marked
## Run: <status>.@file and the
notes of earlier ! lines, as #< lines) and, after /model or /mode, the model and mode
a turn ran with, so gptr_source(replay = "live") sends each turn again with what it was sent
with and asks it on that model and in that mode (a turn that names none is asked with those its
session has from the turns before, else with the model and mode settings of the session
that re-asks); code the model wrote is never sent again as your context or your steer. A !
line that uses an object of an undone turn is turned off (#~ ) and back on after /redo, so
source() does not stop there, and /retry sends a turn's ! notes again when that turn is
the latest message sent.
gptr_export() turns such a transcript into an ordinary R script.gptr_init() creates a .gptr/ project workspace with the project instructions file
.gptr/vignette.Rmd; gptr_config() and gptr_trust() manage settings and trust.gptr_rewind(), gptr_checkpoints() and /undo restore objects, files and the
conversation.agents =, parallel =, gptr_parallel()), across
providers.gptr_register(), gptr_spec() and the spec constructors).gptr_tailor() makes a harness: a function you call exactly like peter() that carries
peter() arguments you fix (such as mode, model, tools or extensions), your own hard
rules, and coaching: a note added to peter's instructions, skills peter can open, a review that
asks peter to check its work before it stops, and rules. It calls no model. Share a harness
with saveRDS() and load one with gptr_tailor(readRDS(file)); dir = writes it out as
files to read.gptr_coach() improves a harness's coaching by running your task: the harness does the task,
two coaches propose new coaching, each proposal does the task, a third coach combines them, and
the result does the task once more. Code keeps the new coaching only when that last run ended
normally, delivered what the first one did, took no riskier kind of action and passed your own
check. k (1 to 5) asks for more coaching: with strategy = "parallel" it runs k of each
side by side, and when more than one result passes, a blind judge picks one; with
"sequential" it runs k rounds, each building on the one before. Every session runs
unattended in its own R process with copies of your objects. It works on API keys and on
Claude Code and Codex plans (on a plan, at most two sessions at once, and it stops at a plan
limit). A call such as h = gptr_coach(...) in a script or notebook is recorded as the
coaching it produced and replays without a model, a key or any process.gptr_spec("skill", name, description = ..., text = ...)
needs no SKILL.md file. A skill registered for a call, by an extension or a harness, stays in
that session.gptr_tailor(peter, rules = list(never = ..., require = ...)) and in
coaching: a never rule, such as r(category:install), refuses every matching call in every
mode, sub-agents included; a require rule names a call that must happen before peter
finishes, such as saving results with write.csv(), and gptr reminds peter at most twice and
reports a requirement still missing. Rules read the code before it runs, so a name or path the
code computes can slip past them.extensions = also applies to every
session the run starts (a peter() call from the agent's code, team members, fan-out children
and workers). A nested peter$sh(), peter$write() or peter$edit() whose command or path
the agent's code computes meets every deny and ask rule, and the strictest answer wins.claude-cli or codex route signals gptr_error_plan_limit, with the
time the limit resets when the CLI says it..opts$returns works on Claude Code (claude-cli): gptr asks for the reply's shape at
the end of the prompt and checks the reply itself.gptr_key attribute (a string) loads once per session, so
passing it again when you continue the session does not load it twice.gptr_mcp(), gptr_mcp_add()) that exposes server
tools as R functions, and gptr_mcp_serve(), which serves the live session to other agents.
A project's own servers, also those in the project files of Claude Code, Codex, Cursor,
VS Code and Pi, start only in a trusted project.gptr_artifacts()).gptr_login() signs in to
providers and MCP servers, gptr_env() loads .env files (including the jev-key alias) and
gptr_scrub() cleans files that captured a key. Model code that reads a registered key, or
hands one to a child process through the env of peter$sh() or peter$script(), asks in
auto mode too.gptr_usage(), gptr_prompt(), gptr_describe() and the
budget argument.background = TRUE) are experimental.?gptr_security and ?gptr_egress for what gptr does on your behalf and what it sends to
model providers, and ?gptr_options for every option.gptr_message_billing_env condition with the removed names, never their values. Handlers
use muffleMessage; the notice respects gptr.quiet. Actual billing refusals remain errors.yolo mode (mode = "yolo", /mode yolo) runs actions without approval, including
package installs, writes anywhere and gptr's own configuration functions. A person is still
asked before deleting files outside the project or protected files, moving the working
directory out of the project, rm(list = ls()), options(gptr.*) and sending a secret over
the network; a one-time notice says so. setwd() or withr::local_dir() to a directory the
code computes, such as setwd(here::here(d)), counts as such a move, in auto too;
here::here() of literal parts and a computed directory R moves back from, as with
withr::with_dir(), do not. The classifier reads code; it is not a sandbox. In
every mode, model code that would end or hang the R session, such as q() or readline(), is
refused without a question.type_ahead swaps the two); Up takes back a message not yet delivered, and
/command and !code lines wait for the next prompt. A paste is one draft, however busy the
session, in a terminal that supports bracketed paste. A question waits for typing to pause and
keeps the draft. On the CLI routes a steer is sent after the CLI's answer. In RStudio and R.app
(Esc) and Positron (Ctrl-C) the pause menu steers.peter> prompt is sent
as one message, pasted fences and """ blocks are read at once, a block pasted at an approval
or a choice is kept in the console history, not read as the answer, and one pasted at the
pause menu's steer or follow-up question is the whole message; a last pasted line without a
newline waits for Enter at the next prompt, so a two-line paste without a final newline answers
a question with its first line. Elsewhere a block pasted at a prompt or a question arrives one
line at a time, as typed lines do.@, !, !!, / and peter$, never in plain
text, and never offers a name that holds a line break or another control character.gptr_parallel()
print one line when each sub-agent starts and ends.peter() call made by the agent's own code without envir = works in an overlay of the
caller's environment; its objects are in res$envir.object.size(),
code the agent runs keeps no parse data, and values returned by sub-agents stay small./model and model = accept a provider name alone, such as claude-cli or codex-cli
(its first model); /model checks the name before the first prompt. A model declared without
tool calls says once that the session's tools are not sent.api = "bedrock-converse" signs requests
with Signature Version 4 in R. Region and endpoint are provider-record data
(compat = list(region = ...), base_url), so a SigV4 gateway is configuration; see
?gptr_provider. AWS_ACCESS_KEY_ID is handled as a secret./cost lists each agent and one total that keeps
the known cost when a request's cost is unknown, and /context says the CLI keeps its own
transcript./undo and gptr_rewind() restore what the agent's served calls changed,
and a rewind or compaction starts a fresh CLI conversation. Automatic compaction does not run
there, since the CLI keeps its own context. /undo reports each item once, by its outcome.par(mfrow = c(1, 2)) keep every panel and knitr chunks keep their
figures; plots of calls served to the CLI routes are drawn on your device..x; parallel = with .opts$context = "none" is an
error. A peter() call made by the agent's code without model = uses the running session's
model, and the egress question names the sub-agent that asks. Assignments in a sub-agent's
overlay are not reported as overwriting your objects.p = normalizePath("../x.txt"); file.remove(p).a and p only when it has a rule to suggest (typed anyway, they say why, such as an ask rule
that asks every time); result lines and errors wrap in the margin, and a
failed r call still lists its objects; sub-agent failures give their reason and count, and
repeated nested prompts are numbered; command output and long prompts stay in the margin;
Codex commands show without their shell wrapper and Codex messages without a held last word; a
cut code block says so inside its fence; wide pipe tables stream verbatim; /model and
/status name the model behind a CLI's default.gptr_models(refresh = TRUE) reads each models.dev price tier once, so models with a
200k-token price tier resolve and a 272k tier gains no premium from 200k tokens.model = "gemini" (Google's newest Gemini Pro) asks for gptr_models(refresh = TRUE) or a
provider/id.gptr_models(refresh = TRUE), for example MiniMax-M3 and gpt-oss-120b) are not refused as
too small: gptr reserves and requests at most half the window for output. A compact_at too
small for the preset is named as the cause, and the hint reads tools = "minimal".gptr_models(provider = "ollama", refresh = TRUE) as the remedy, not the internal
model_prepare().peter(model = "ollama/<model>") asks the Ollama server about that model before its first
request in each R session, as System 1 calls do, so a saved default Ollama model works in a
new session.provider/id on a route the catalog lists no models for yet (before a refresh,
for example OpenRouter, Groq, xAI or Azure) says that gptr_models(refresh = TRUE) downloads
them from models.dev.gptr_provider() and gptr_spec("model") accept a model's prices as a list of price records
as well as a data frame, as gptr_config(providers =) does, and check the table when the spec
is built, naming the field.azure/ before AZURE_OPENAI_ENDPOINT is set, names
the variable that sets it (or providers$<id>$base_url in gptr_config()).models set with
gptr_config(.scope = "session") or options(gptr.providers =), replaces the one in a lower
layer (for example the user file); named lists merge.gptr_config() warns when it saves a setting to a project that is not trusted and so does not
apply it (for example providers or model), naming gptr_trust() and .scope = "session";
settings that only tighten, such as mode = plan or deny rules, stay silent (a permissions
write names only its allow rules).gptr_config(model = opuss) (or small_model, system1) warns when the name is not a model
gptr knows in this session, as the console's /model does, with the reason and how
to undo it; the value is still saved, since a provider registered later or a catalog refresh
can make it valid.gptr_init() writes no mode or record into .gptr/settings.json, so a user-level
mode = auto or record = "auto" applies in every initialized project. In a project made by
a development build from GitHub, delete "mode": "manual" and "record": "ask" from that file
to let your user settings apply.mode applies, so gptr_config(mode = auto) in a trusted project takes
effect; an untrusted project only tightens the mode, and context and record only tighten in
any project. A project cannot select yolo:
gptr_config(mode = yolo) at project scope is refused, naming .scope = "user" and "session".api = "bedrock-converse" send thinking levels
(.opts = list(thinking =) or a :high suffix) to a Claude model whose record declares
capabilities = list(adaptive_thinking = TRUE, effort = TRUE) (Claude 4.6 and later; see
?gptr_provider). Any other model, an undeclared Claude record included, runs without
thinking and says so once..opts = list(thinking = "ultra"),
is reported as such ("the string given is not one of them", with "Did you mean" when it differs
from a choice only in case)."opus:off" runs at low, and thinking = "max"
on a model whose levels end at high runs at high. ?gptr_provider documents
thinking_levels; a reasoning model declared without it gets off to high.model, a tool for tools): after
opus = gptr_fake_provider(...), model = opus uses the alias and suggests !!opus.choices, levels, threshold, min_confidence and uncertain given to a conversational
model, also with agents = or parallel =, are refused, naming the argument. Use a decision
model such as jev; after gptr_config(system1 = "emulate:<ref>"), model = jev runs it
through a chat model. A decision model refuses agents = and parallel =: it answers every
input in one call.uncertain = "stop" error names the uncertain inputs (the first ten) and carries their
positions ($index), $names and $confidence, the confidence the band tests, every answer
($value) and $prob.as.data.frame() of a System 1 score names its probability columns by level (p_<level>), as
gptr_prob(x, "probabilities") and choices do.choices given as a factor, or .opts = list(output = "factor"), return a plain factor without
probabilities, as ?peter, ?gptr_prob and the System 1 vignette say. When elements fail,
the factor keeps attr(x, "meta")$errors, which the failure warning points to.get_response() and dataframe_to_text() for the
OpenAI 'ChatGPT' API (released 2025-04-05, after 0.5.0 and 0.6.0 in May 2024).