Skip to content

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 ID
  • getCheckpoint(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:

ts
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:

ts
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:

ts
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.

ts
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:

bash
agency resume checkpoint.json program.agency

You can change values in the current checkpoint frame while resuming:

bash
agency resume checkpoint.json program.agency \
  --local-var mood='"happy"' \
  --arg input='{"retry":true}' \
  --global-var attempts=8

Values 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:

ts
restore(result.checkpoint, { args: { input: "good" } })

Global variables:

ts
restore(result.checkpoint, { globals: { attempts: 8 } })

Or local variables:

ts
restore(result.checkpoint, { locals: { x: 8 } })

You can also limit the number of times you restore a checkpoint using maxRestores:

ts
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.

bash
export AGENCY_CHECKPOINT_KEY=$(openssl rand -hex 32)

You can verify the checkpoint like this:

ts
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.