Freeze and Restore Agents

Freeze finished agents to free their processes while keeping their output readable in place, then bring them back by restore or re-pick

A working day with aitasks ends with more agents than you need. Ten or twenty panes, each holding a code agent that has finished its task — and what you actually want from most of them is the last screen: the workflow summary, the list of tasks it spawned, the analysis it produced. The process behind that screen is still running, still holding its context, still costing you memory and a share of the machine.

Freezing ends the agent and keeps the screen. The agent’s process — and the context it was holding — goes away; its pane is taken over by the frozen-agent viewer, which replays the captured output in place. What you trade a code agent for is that viewer: a small terminal app that reads a file. The record of the agent — its task, its session, its window — is kept so you can bring it back later.

When to freeze

Three states an agent can be in, and they differ in what is running:

LiveParkedFrozen
Processrunningrunningended
Outputlive in its panelive in its panecaptured and replayed by the viewer
Costs youmemory, context, a share of the machinethe same — parking changes nothing about the processthe agent process and its context are released; the viewer holds the pane, and the capture holds disk under ~/.config/aitasks/frozen/
In the monitorsa normal cardP, hidden by the P filter, counted as N parkedF, hidden by the same filter, counted as N frozen
How you come backit is already therepress Space to unparkrestore the session, or re-pick the task

Parking is a signal to you: the agent keeps working and the monitors stop showing it. Freezing is a decision about the agent: you are done with it as a process, and you want its results readable. An agent you are still waiting on gets parked; an agent that has delivered gets frozen.

The daily loop

Freeze what has finished. Press f in ait monitor on the focused card, or in ait minimonitor on the followed agent. A dialog confirms (Freeze this agent?) and explains the trade. Nothing about your layout changes: the viewer takes over the same pane in the same window, keeping the window’s name, so an agent pane with a minimonitor companion beside it still looks exactly like that.

Read it whenever you get to it. The pane is the viewer, so there is nothing to launch — press G for the closing summary, r for plain text, m to render the capture as Markdown. Search with / and n, select with shift+↑ / shift+↓ or the mouse, and copy with y or ctrl+c, which reaches your system clipboard even from a pane you cannot currently see. The how-to walks through pulling a spawned-task list out of a transcript.

Come back by the cheaper route. Two ways back, and the obvious one is usually not the cheaper one:

  • R — restore relaunches the agent with its recorded session. Worth it when the agent’s context is what you want back: a long investigation, a half-finished conversation.
  • P — re-pick starts the task over with a fresh agent, which reads the task file instead of replaying a long transcript. That is cheaper in tokens and in time, and for a task whose file already says everything that matters it loses nothing.

Both keys belong to the viewer. From a monitor, move into the frozen agent’s pane first — s in ait monitor switches to its window, Tab in ait minimonitor moves to the pane beside it — and press them there. The monitors’ own keys keep their usual meaning on a frozen agent.

Each route needs something the other does not, and a record may carry only one: re-pick needs a task id, restore needs a recorded session id. An agent whose CLI cannot resume a session — OpenCode today — can only be re-picked. Restore also needs to know which CLI the session belongs to, and it relaunches only that CLI. For an agent you start by hand, outside ait, the restore uses the agent and model the record names when freezing could recover them (the agent was started with a model the framework knows). Without them it would use the project’s default agent for raw sessions, and resumes only when that default runs the same CLI. When the CLI could not be told, or it differs, the restore is refused and the agent stays frozen; re-picking its task is still available (see the session hook). So can a Codex agent started by hand that Codex runs through its shared app-server daemon: nothing can yet tell which conversation belongs to its pane, so no session id is recorded for it (see the session hook). A Codex agent you type at a shell prompt that runs in its own process (started with -c tui.alternate_screen=never) is read through that shell, but only while it is the shell’s foreground job; a suspended or background Codex is never taken for the pane’s agent. Its conversation is restorable only when the framework can name its model: from the launch’s -m, when that is a model the framework knows, or else from a Codex model already on the agent’s record. Without either, the agent is re-picked.

A successful, verified restore or re-pick deletes the capture. Once the resumed agent identifies itself the transcript has served its purpose and the record drops it. Choosing restore or re-pick is therefore not a way to keep the output: copy what you want with y (or ctrl+c) first.

Drop when you are done. k deletes the record and its capture and closes the stand-in pane. It confirms first, because that is the one action that leaves you with neither the agent nor its output.

Before shutting down

Z freezes every agent on the machine — every project, parked agents included, not just the ones in the list you are looking at. The confirmation names the real count, which is why that number is usually larger than what is on screen. It is the key for shutting the machine down with your work recoverable.

When the machine is back up, run ait ide in any project as usual. Frozen agents whose viewer is gone — the tmux server died with the machine, so every viewer did — are listed before ait ide attaches, for every project, one group per project with the current one first: window name, task, when it was frozen, and whether it can be restored or re-picked. Then ait ide asks one question per project it can act on:

AnswerWhat happens
V (default)Each viewer comes back in a new window under its recorded name. The agents stay frozen; restore or drop them from the viewer as usual.
RThe viewers come back, then every agent that can resume is restored inside its viewer. The rest stay as viewers.
PThe same, but each agent that has a task is re-picked instead of resumed.
SNothing changes. The records stay listed in ait frozenagent.

All the questions come first; then everything runs.

Where they land. The current project’s agents go into the session ait ide is opening, including one you chose with --session. Every other project’s agents go into that project’s own tmux session, never into the one being opened: the first session that belongs to the project, or a new one created the way ait ide would create it, under the project’s tmux.default_session name. When ait ide is done it names those sessions; switch to one with j from any TUI, or run ait ide in that project. If a project’s session cannot be created (its configured name is already taken by another project, for example), that project’s records are reported as failed and stay frozen; the other projects are not affected.

Which projects are acted on. The current project, and any other project that is registered (see ait projects list; ait ide registers a project the first time it runs there) and whose directory still exists. Any other project’s group is listed with the reason and not touched. Nothing is created for it, and no question is asked:

  • project directory gone: the records belong to a directory that no longer holds a project. Drop them in ait frozenagent.
  • not a registered project: run ait ide in that project, or register it with ait projects add <path>.
  • project registry unreadable: the registry file could not be read, so no other project is acted on until it can be.

Companions. A viewer brought back this way gets its minimonitor beside it, as any agent window does, and so does an agent restored inside its viewer. ait ide also gives every viewer that is already open but lost its minimonitor a new one, across all projects. A window that already has a minimonitor, even one that is still starting, does not get a second.

Every frozen record also stays listed in ait frozenagent. A record marked viewer open, untracked has a viewer window the framework lost track of (an earlier attempt that did not finish): it is picked up rather than duplicated. R and P skip such a record, and say so, if picking it up fails. A record marked an earlier restore's agent is still running, untracked is different. A restore into a new window did not finish, and the agent it started is still running in a window the framework does not track. It is never mistaken for a viewer. Restore, re-pick and drop all refuse that record and name the window, because going ahead would start a second agent on the same session or delete the record while that agent runs. Close that window, then try again.

Without a terminal — a script, or ait ide piped into something — nothing is asked: a one-line note says how many frozen agents are waiting in this project and in the others. Pass --no-frozen-check to skip the check, and the minimonitor top-up, entirely.

To do the same without ait ide:

./.aitask-scripts/aitask_frozen.sh gone --root "$PWD"     # list this project's
./.aitask-scripts/aitask_frozen.sh gone --all-roots       # list every project's, grouped
./.aitask-scripts/aitask_frozen.sh reopen --root "$PWD"   # bring the viewers back
./.aitask-scripts/aitask_frozen.sh companions --all-roots # give open viewers their minimonitor

You can also bring agents back one at a time from ait frozenagent list mode — it lists every frozen record across all projects, and R / P act on the highlighted row — or all at once:

./.aitask-scripts/aitask_frozen.sh restore --all           # resume each session
./.aitask-scripts/aitask_frozen.sh restore --all --repick  # re-pick each task instead

Restore-All works through the records sequentially, and one record’s failure never stops the batch: with three frozen agents and one missing binary, the other two come back. A record with no recorded session id, one whose CLI cannot resume, or one whose CLI is unknown or is not the one the restore would launch, is reported as a failure by the first form and keeps its capture — those are the ones to bring back by re-picking.

Agents whose windows survived come back in them. Agents whose windows are gone — after a tmux restart, say — come back in new windows under their recorded names; if a name is already taken, the next one gets a -2 suffix. A project with no tmux session at all gets one created the way ait ide would. The full set of landing places is in Where the agent comes back.

What “unverified” means

A restore succeeds in one of two ways, and they are not the same outcome:

OutcomeWhat happenedThe capture
restoredthe session that came back was confirmed to be the one asked for — by the resumed agent’s session hook, or, for a Codex agent, from the transcript the resumed process holds opendeleted
restored, unverified — capture keptthe agent is running, nothing confirmed it is the same session, and the framework confirmed it on the process alonekept

Both are successes. The second is not a fault, and it is the safer of the two: precisely because nobody confirmed the session, the transcript is kept rather than deleted.

You will see it whenever nothing confirms the session within the grace. The session hook was declined at ait setup, the CLI reports no session at startup, or the agent is a Codex resume whose session cannot be read from its process (for example a Codex started by hand whose TUI is routed through the shared app-server daemon, which holds the transcript instead).

Codex fires the hook only at a session’s first turn, not at launch (measured on Codex 0.163.0; Codex 0.153.4 fires it only under codex exec). So a resumed Codex agent never confirms itself through the hook within the grace, which is why the transcript check exists. When its hook does fire later, at your first message, an unverified restore is upgraded to restored and the capture is deleted.

frozen.restore_ack_grace sets how long the restore waits for that acknowledgement. It is a waiting period, not an outcome lever: an agent that never acknowledges ends unverified however high you set it. See the reference.

When something goes wrong

A failed restore costs you nothing. The viewer comes back in its pane and the capture is intact. The header reports what happened — restore failed: <reason> — capture kept — and the viewer shows the store’s memory of it the next time you open the record.

A record that cannot be resumed is refused before anything starts. The viewer names the reason — no session id to resume, an agent that does not support resuming, or a session whose CLI is unknown or is not the one the restore would launch — as cannot restore: <reason>, and leaves the record untouched. Re-pick is the way forward.

restore did not start is the other shape of failure. Nothing was dispatched, for a reason only the coordinator could see — for example, tmux could not be queried to find out where the agent should land.

A stand-in that died is respawned. So is a freeze or a restore whose coordinator was killed part-way: records in a transitional state are settled from what tmux can actually be seen to hold. An agent that came back but still shows as frozen, because its frozen marker could not be removed, is unmarked the same way, and can then be frozen again. That settling — reconcile — runs after every freeze and restore, and on the maintenance tick of any open ait monitor or ait minimonitor. To run it yourself:

./.aitask-scripts/aitask_frozen.sh reconcile

There is no ait frozen command; the viewer is ait frozenagent, and the engine is the script above.

A session name held by another project stops a restore that would have had to create that session, rather than borrowing it. That case and the closed-window routes are covered in Where the agent comes back.

Marks and frozen agents

Freezing does not disturb the mark you put on an agent. A frozen card shows its ★ or P plus a cyan F, so the mark column never shifts, and Space still cycles the mark of a frozen agent.

The P filter hides parked and frozen agents together — it is one filter over both kinds of set-aside agent, not two. The counters stay separate: N parked and N frozen (Np and Nf in minimonitor’s narrow header), both reported whether or not the rows are hidden, and an agent that is both frozen and marked parked is counted once, as frozen.

Limits

  • Frozen records do not expire. There is no age-based cleanup: a record lives until you drop it or a verified restore consumes it. A record whose capture file has gone missing is retired, since there is nothing left to read or restore from.
  • Disk. Each record keeps capture.ansi and capture.txt under ~/.config/aitasks/frozen/<id>/. ait frozenagent with no arguments lists every record on the machine, which is also the way to find what is still taking up space.
  • The capture is the tail of the scrollback. frozen.capture_max_lines (50000 by default) is a depth, not a total: the freeze reaches that many lines back through the pane’s history and runs to the bottom of the visible pane, so a stored capture holds roughly the cap plus a pane height. A pane with more history than the cap has already lost its earliest output before the freeze runs.

See also

  • Frozen Agent — the viewer TUI, its keys and its messages.
  • Framework session — the record behind all of this: what identifies an agent, what the states mean, and where the store lives.
  • Session hooks — what ait setup installs to make a verified restore possible.
  • Crash Recovery — what to do when an agent died instead of being frozen.