Checkpointing
Creating checkpoints
Agency can pause and serialize its execution state at any point. The core API has three functions:
checkpoint()— snapshot current state, returns a numeric IDgetCheckpoint(id)— retrieve the full checkpoint object for a given ID (eg to save to disk)restore(idOrCheckpoint, options)— roll back to a checkpoint (accepts either a numeric ID or a Checkpoint object)
This lets you roll back to a previous point in execution. Here's a simple example:
node main() {
const cp = checkpoint()
const result:number = llm("Generate a random integer between 1 and 6")
if (result < 3) {
print("Bad roll, rolling back...")
restore(cp)
}
print("Good roll:", result)
}Note that this is not like a while loop that keeps retrying. This is actually restoring to a previous execution state. For example, suppose we want to count how many times we have rolled back. We try inserting a counter:
node main() {
let counter = 1
const cp = checkpoint()
const result:number = llm("Generate a random integer between 1 and 6")
if (result < 3) {
print("Bad roll, rolling back...")
counter++
restore(cp)
}
print("Good roll:", result, "after", counter, "rolls")
}This will not work. It will always print "...after one rolls", because if it's a bad roll, we increment the counter but then immediately roll back, which restores the counter back to one:
counter++
restore(cp)You can take this one step further by getting the checkpoint and saving it to a file, which lets you restore back to that state at any point in the future, as long as the code hasn't changed.
const id = checkpoint()
const cp = getCheckpoint(id)
printJSON(cp)Copy the printed JSON object into checkpoint.json. Do not redirect the whole output of agency run into that file: the run banner and any other program output would make it invalid JSON.
Resume it against the same source file:
agency resume checkpoint.json program.agencyYou can change values in the current checkpoint frame while resuming:
agency resume checkpoint.json program.agency \
--local-var mood='"happy"' \
--arg input='{"retry":true}' \
--global-var attempts=8Values are parsed as JSON when possible and otherwise treated as strings. Use repeated --program-arg <value> flags if the resumed program reads std::args; process arguments are not stored in checkpoints.
This is kind of like saving your progress in a video game and coming back to it later.
restore options
When you restore, you can optionally provide overrides for any of the variables in that checkpoint. This allows you to retry a section of code with different inputs.
You can override function arguments:
restore(result.checkpoint, { args: { input: "good" } })Global variables:
restore(result.checkpoint, { globals: { attempts: 8 } })Or local variables:
restore(result.checkpoint, { locals: { x: 8 } })You can also limit the number of times you restore a checkpoint using maxRestores:
restore(result.checkpoint, { maxRestores: 3 })Inspecting checkpoints
You can inspect a checkpoint saved to disk using the debugger, like so:
agency debugger foo.agency --checkpoint <checkpoint-file>Note that you have to additionally give the source file.
Verifying checkpoint integrity
You can verify that a checkpoint has not been tampered with. If you set the AGENCY_CHECKPOINT_KEY environment variable, Agency will embed a checksum into every checkpoint.
export AGENCY_CHECKPOINT_KEY=$(openssl rand -hex 32)You can verify the checkpoint like this:
import { verifyCheckpointChecksum } from "agency-lang";
if (!verifyCheckpointChecksum(checkpoint)) {
// oops! Someone has edited this checkpoint. Refuse to resume
}Signing is opt-in. Hosts using the JavaScript API call verifyCheckpointChecksum themselves. agency resume checks a signature when one is present and refuses an invalid or unverifiable signature unless you pass --force.