Snapshots and tags
Every completed solve freezes the project workspace into an immutable snapshot, so the state that produced a given result stays available after the next turn changes things.
Listing history
Section titled “Listing history”Newest first, and each row carries what it came from.
const snapshots = await aura.listSnapshots(project.id);
for (const snapshot of snapshots) { console.log(snapshot.id, snapshot.kind, snapshot.label ?? "—", snapshot.solution_summary);}snapshots = await aura.list_snapshots(project.id)
for snapshot in snapshots: print(snapshot.id, snapshot.kind, snapshot.label or "—", snapshot.solution_summary)kind is "solve" for the automatic ones and "manual" for a snapshot someone captured on
demand. solution_path names the file inside the snapshot that holds the solution;
execution_id links back to the execution that produced it. Exactly one
row has is_current set: the snapshot the live workspace sits on, which is the newest one after a
solve and whichever you checked out otherwise.
Narrowing the list
Section titled “Narrowing the list”Filters are applied server-side and stack, so “give me the solutions” is one request rather than a full history you sift through.
const solutions = await aura.listSnapshots(project.id, { kind: "solve", hasSolution: true });const pinned = await aura.listSnapshots(project.id, { hasLabel: true });const fromThread = await aura.listSnapshots(project.id, { conversationId: conversation.id });from aura_sdk.models import SnapshotKind
solutions = await aura.list_snapshots(project.id, kind=SnapshotKind.SOLVE, has_solution=True)pinned = await aura.list_snapshots(project.id, has_label=True)from_thread = await aura.list_snapshots(project.id, conversation_id=conversation.id)hasSolution / has_solution and hasLabel / has_label are tri-state. Leaving one out does not
filter; passing false asks for the rows where the field is unset — the solves that produced no
solution file, or the snapshots nobody has named.
Fetching one, by id or tag
Section titled “Fetching one, by id or tag”getSnapshot takes either — a raw id, or a tag name defined in the same project.
const byId = await aura.getSnapshot(project.id, "3f1c…");const byTag = await aura.getSnapshot(project.id, "best-known");
console.log(byTag.file_count, byTag.total_size_bytes);for (const file of byTag.files) console.log(file.path, file.size);by_id = await aura.get_snapshot(project.id, "3f1c…")by_tag = await aura.get_snapshot(project.id, "best-known")
print(by_tag.file_count, by_tag.total_size_bytes)for file in by_tag.files: print(file.path, file.size)The detail response includes files; the list response does not — list is a summary.
Naming a good run
Section titled “Naming a good run”A tag is a movable name for a snapshot inside a project — its address, accepted anywhere a
snapshot id is. Point it at a new snapshot and the name follows. (A snapshot’s label is only its
display title; to fetch a snapshot by name, tag it.)
const [latest] = await aura.listSnapshots(project.id);await aura.setTag(project.id, "best-known", latest.id);
const tags = await aura.listTags(project.id);latest = (await aura.list_snapshots(project.id))[0]await aura.set_tag(project.id, "best-known", latest.id)
tags = await aura.list_tags(project.id)A nightly job can re-solve, compare, and move best-known only when it improves; everything
downstream asks for the name rather than tracking ids.
A label is different: a free-text display title, not a ref — it’s what a person sees in the
Run tab’s snapshot list, not something you fetch a snapshot by. Set or clear one with
renameSnapshot / rename_snapshot:
await aura.renameSnapshot(project.id, latest.id, "before the capacity change");
// clear itawait aura.renameSnapshot(project.id, latest.id, null);await aura.rename_snapshot(project.id, latest.id, "before the capacity change")
# clear itawait aura.rename_snapshot(project.id, latest.id, None)Loading one back — checkout
Section titled “Loading one back — checkout”Checkout replaces a project’s live workspace with the files from a source. source can be:
source |
Resolves to |
|---|---|
3f1c… |
A snapshot id in this project |
best-known |
A tag in this project |
acme/vrp |
A deployment handle — reset to the deployed starting point |
acme/vrp-demo/best-known |
A tag on another project in your company |
// go back to the best run so farconst basis = await aura.checkout(project.id, "best-known", conversationId);console.log(basis.kind, basis.file_count); // "snapshot", 42
// or start over from the deploymentawait aura.checkout(project.id, "acme/vrp", conversationId);basis = await aura.checkout(project.id, "best-known", conversation_id=conversation_id)print(basis.kind, basis.file_count) # "snapshot", 42
await aura.checkout(project.id, "acme/vrp", conversation_id=conversation_id)Pass the conversation id to attribute the checkout to that conversation, so the agent sees the workspace change in context.
One checkout at a time
Section titled “One checkout at a time”A checkout clears the workspace before it copies, so the server runs one per project and refuses the
rest with a 409 — a second checkout, and a snapshot taken while one is running, both wait their
turn rather than freezing or copying a half-written tree. A checkout is also refused while an agent
turn is in flight.
Read the workspace to see whether one is running before you start another, or after a request whose answer you never got:
const workspace = await aura.getWorkspace(project.id);if (!workspace.checkout_in_flight) { await aura.checkout(project.id, "best-known");}workspace = await aura.get_workspace(project.id)if not workspace.checkout_in_flight: await aura.checkout(project.id, "best-known")checkout_in_flight is read from the running checkout itself, so it answers for every caller — your
other tab, a teammate, a script — not just this client. It is a check, not a reservation: two callers
that read it at the same moment can still both try, and the loser gets the 409.
Capturing a snapshot on demand, and reading or deleting a tag by name, are REST endpoints you can call directly.