gptr can keep the code an agent executed beside the prompt that
requested it. In a designated R script, the code goes below the owning
peter() statement between marker comments; in a report or
notebook, it becomes a chunk or cell. A fresh recorded block can run
again as R without another model request. Replaying still executes its R
code, with the effects and input dependencies that code has.
The history keeps code and bounded output, rather than archiving every input object. Provide those objects in the replay environment; working on data already loaded in R does not require an intermediate dataset file. See Execution model for object lifetimes and the separate costs of checkpoints, workers and snapshots.
The examples below are recipes for your configured model. Run recording and file-writing steps deliberately in a temporary project.
Write a prompt for each step. A pipe continues the same session, so the next prompt can use the objects created by the previous one. Keep prompts on separate lines and put short notes where they help explain the next step.
library(gptr)
cars = datasets::mtcars
session = peter("Fit mpg ~ wt in cars with stats::lm(); store fit.") |>
# Reuse the fitted model.
peter("Use fit to predict mpg for every row of cars; store predicted.") |>
peter("Summarize observed mpg minus predicted; store residual_summary.")
graphics::plot(cars$mpg, predicted)
session |> peter("Explain the largest residuals.")Prompts and ordinary R can alternate. A pipe adds turns to the same
mutable session; assigning the session to another name does not make a
branch. Use gptr_fork() for a branch.
peter() is an R function. Give it an environment when
results should outlive a helper call:
rank_cars = function(data, envir) {
peter("Find the three most fuel-efficient cars in data; store top.",
data = force(data), envir = envir)
}
work = list2env(list(data = mtcars))
session = rank_cars(work$data, work)
work$top
summary(session)data = force(data) supplies model context; it does not
create an R binding in work. Here work$data is
prepared before the call, so the R tool can read it. Objects attached by
name must be visible from the evaluation environment.
summary(session) reads recorded turns without a model call;
it is not a narrative or an executable workflow.
A call nested inside a function, loop or another expression does not own a document block. Running that code again may make another model request. Keep recordable prompts at top level and reusable R calculations in functions. System 1 cache hits do not make an arbitrary loop into a recorded workflow.
gptr_doc(path) binds a document and supplies explicit
consent to write blocks into it. gptr_doc() reports the
binding; gptr_doc(FALSE) removes it. It does not create a
workflow from past conversation turns.
Save this as analysis.R in a temporary directory:
library(gptr)
cars = datasets::mtcars
session = peter("Find the three most fuel-efficient cars in cars; store top.")
topThen run it from a separate driver or at the console:
gptr_doc("analysis.R")
work = new.env()
gptr_source("analysis.R", replay = "auto", envir = work)
gptr_blocks("analysis.R")
gptr_doc(FALSE)A missing block makes a model request, and the executed code is
recorded below the call. The header identifies the block, model, date
and prompt hash; the body hash identifies its recorded code.
## Decision: retains a model-supplied note, and
#> retains bounded output. A note is a model
explanation, not proof that the code is correct.
The document must be writable, and recording needs consent.
gptr_doc(path) supplies that consent explicitly;
record = "off" prevents automatic recording into discovered
documents. To opt out for one call, including a call in a bound
document, pass .opts = list(record = FALSE). Pass it to
each call in a pipe chain that should not record.
record = TRUE in .opts keeps the usual consent
checks; it does not supply consent. Without a binding, source discovery
can find a call in a document. At an interactive console, agreeing to
transcript recording creates a script under
.gptr/transcripts/; an explicit
gptr_doc(path, format = "transcript") selects an R
transcript target.
Main-session recorded code runs as ordinary document code on replay.
Keep its input objects in the document’s evaluation environment, using
peter()’s default caller environment. An arbitrary custom
envir is not restored or applied to the following recorded
chunk. For a function that works in a separate environment, opt out with
.opts = list(record = FALSE) and keep its executable steps
explicitly in the function. Forks and team children have their own
recorded overlay wrappers.
gptr_doc("analysis.R")
listing = gptr_blocks("analysis.R")
listing[, c("id", "status", "model", "date", "tokens", "cost")]
gptr_source("analysis.R", replay = "replay", envir = new.env())
gptr_cache()
gptr_doc(FALSE)gptr_source() executes .R files
only, expression by expression. It can regenerate a stale block
and skip its old code in the same run, which base source()
cannot do. Its return value is a block listing with the action taken for
each block. gptr_blocks() reads R, R Markdown, Quarto and
Jupyter formats; pending writes can be recovered before listing.
| Replay mode | Behavior |
|---|---|
"auto" |
Replay fresh blocks; run missing or stale calls through the selected model. |
"replay" |
Never call a model; a missing or stale record signals
gptr_error_not_recorded. |
"live" |
Ask the model again and rewrite recorded blocks when writing is allowed. |
"record" |
Regenerate stale blocks; fresh blocks replay. |
A call’s replay = argument wins over process options,
the GPTR_REPLAY environment variable and settings. In any R
front end, use gptr_source("analysis.R", replay = "replay")
to run a recorded script without model calls. From a Unix shell:
This checks that the requested calls can use their records; it does not certify the analysis, freeze package versions, capture external datasets or make file/network operations in the recorded R code safe to repeat. Verify data paths, dependencies, outputs and side effects.
gptr_blocks() marks a block fresh,
stale, user-edited or undone.
Changed prompts or interpolated values can invalidate replay; the
listing alone cannot evaluate runtime interpolation. Hand-edited code is
recognized by its changed hash. Undone code stays visible as inert
comments. Keep the associated answer cache if the replayed calls need
their text or values; the code block and the cache are different parts
of the record.
gptr_cache() reports System 1 and System 2 answer
caches, temporary spill files and pending sidecars.
gptr_cache("prune") removes entries no longer in use or
beyond their retention window; gptr_cache("clear", "s2")
removes System 2 answers. Clearing answers can change what is available
during replay. Pending document sidecars are not deleted by cache
cleanup; apply them with gptr_doc(path, sync = TRUE).
In .Rmd and .qmd, a recorded block becomes
a chunk labeled gptr-<id> after its calling chunk.
Bind the report, run it with its normal renderer, then inspect the
recorded blocks:
gptr_doc("analysis.Rmd")
rmarkdown::render("analysis.Rmd")
gptr_blocks("analysis.Rmd")
gptr_doc(FALSE)Use quarto render analysis.qmd for .qmd;
gptr_source() is not a renderer for either format. Arrange
the document’s setup to load gptr, prepare its input data and select a
model or replay mode. Plain R chunks still execute when the report is
rendered.
For Jupyter, bind the saved .ipynb path in the R kernel.
Blocks become agent cells. Writes are queued while the notebook is open
to avoid competing with the notebook editor; after closing it, apply the
pending blocks:
gptr_doc("analysis.ipynb")
# Run the notebook's `peter()` calls in its R kernel, then save and close the notebook.
gptr_doc("analysis.ipynb", sync = TRUE)
gptr_blocks("analysis.ipynb")
gptr_doc(FALSE)A notebook edited since a pending block was queued may need conflict resolution. Reopening or listing a file is not proof that pending blocks were applied successfully.
gptr_export() reads a synchronized .R
history and writes a new .R file without running it or
asking a model. Its default format = "r" turns
peter() calls into comments and keeps the recorded R
executable. With format = "peter", calls remain active and
the previous recorded code becomes comments, so that code is not
executed a second time. Ordinary user code and comments retain their
source order. Choose a new .R destination in an existing
directory, different from the source; existing paths are never replaced.
Export also refuses a source that changes during publication.
For example, save the following as analysis.R. With a
configured model and the usual permission checks, its first three
prompts steer one session; the fourth starts another. The ordinary plot
runs between them, using objects in the same R environment.
cars = datasets::mtcars
session = gptr::peter("Fit mpg ~ wt in cars with stats::lm(); store fit.") |>
# Reuse the fitted model.
gptr::peter("Use fit to predict mpg for every row of cars; store predicted.") |>
gptr::peter("Summarize observed mpg minus predicted; store residual_summary.")
# User code and this comment stay in the exported source.
graphics::plot(cars$mpg, predicted, xlab = "Observed mpg", ylab = "Predicted mpg")
graphics::abline(a = 0, b = 1)
session = gptr::peter("Check predicted length and finite values; store checks.")Record it deliberately, inspect the code and results, then synchronize before exporting:
work = new.env(parent = globalenv())
gptr_doc("analysis.R")
gptr_source("analysis.R", replay = "record", envir = work)
work$residual_summary
work$checks
gptr_doc("analysis.R", sync = TRUE)
gptr_export("analysis.R", "analysis-raw.R")
gptr_export("analysis.R", "analysis-peter.R", format = "peter")
gptr_doc(FALSE)The export operation creates no model request. Running the
format = "peter" export can create new requests; running
the raw export executes its recorded R and ordinary user code. Review
those effects, then validate the raw result in a fresh environment with
the intended inputs. For this example, input preparation is already in
the source. For an analysis that uses a large object loaded earlier,
supply that object in the new environment; exporting does not serialize
the dataset or make an intermediate data file.
In the format = "peter" output, recorded Decision and
Steer notes and bounded #> output follow each operand of
this separate-line pipeline. Other layouts keep feedback after the
complete statement. Notes and output are comments, not assertions that
the analysis is correct. Each block keeps at most twelve output lines
and eighty characters per output line.
Export requires fresh completed records and no pending sidecar. It
refuses computed or nested prompts, mixed peter()/ordinary
pipelines, shared-line statements, interpolated-input hashes, missing or
edited blocks and secret-marked records. Raw export also refuses later
ordinary code that depends on a removed session handle, such as
session$value; put needed results in ordinary R objects, or
retain the format = "peter" output. Earlier unrecorded
console input is not reconstructed. Preserved user code can still
require gptr or other packages, read or write files, or call the
network. Export is a source transformation, not verified replay.
Choose the deliverable explicitly. A history document, an ordinary
executable script and a narrative summary have different roles.
gptr_export() transforms an explicitly recorded R document;
it cannot recover arbitrary conversation turns into a complete
workflow.
With your configured model, continue session to request
a narrative:
session |> peter(
"Summarize completed steps, decisions, inputs, outputs and open issues.
Separate proposed work. Name saved files only when verified.",
mode = plan
)
session$textThe answer is model-generated text and adds a turn
to session. Check its claims against the workspace, files
and history before treating it as a record.
gptr_usage(session) shows the request’s usage.
For a history document, designate the target before recording top-level prompts, include ordinary R setup and checks, and inspect the blocks and replay behavior. To assemble one from earlier work, ask the model to draft it and review the draft; earlier unrecorded turns are not automatically recovered into validated blocks.
For an ordinary R script, request a separate, explicit implementation:
session |> peter(
"Write ordinary R for the verified analysis, including inputs and output saves.
Do not call Peter. Identify missing inputs or steps instead of inventing them.",
mode = plan
)
session$textThis returns model-generated output, not an exported or validated script. Save only reviewed code, then run it in a fresh environment with the intended inputs and verify its outputs.
See System 1 decisions for typed decisions and Teams and background work for orchestration and SDK control.