Skip to main content

Session Management

How Daintree saves and restores workspace state: scroll buffer serialization, agent session history, hibernation, sleep and wake, and project switching.

Updated
Reviewed

What Gets Persisted

Daintree saves your workspace state to disk and restores it the next time you open the app. This happens automatically.

Terminal State

  • Scroll buffer: terminal content is serialized to disk, so previous output is there after a restart. See Scroll Buffer Limits for how much history is kept.
  • Panel arrangement: which panels are open, where they sit in the grid, and their sizes
  • Active worktree: which worktree was selected
  • Focus state: which terminal had focus in each worktree

Each terminal's scroll buffer is serialized with xterm's built-in serializer and written to a .restore file on disk. Snapshots are debounced: Daintree waits five seconds after terminal output settles before writing. Terminals with fewer than 1,000 lines are serialized synchronously. Larger buffers use an async path that yields to the event loop, so the PTY host stays responsive during active agent runs.

When you reopen a project or switch back to it, each terminal's .restore file is replayed into the terminal. A dim separator line marks the top of the restored content:

  • ─── Session restored · <date and time> ─── for a normal restore
  • ─── Session hibernated · <date and time> · auto-suspended to save resources ─── if the project was hibernated (no scroll content is restored in this case, just the banner)
  • ─── Restored · <date and time> · previous session was in a full-screen app ─── if the terminal was running a full-screen app like vim or htop

For large buffers (over 256 KB of serialized data), Daintree restores content in 32 KB chunks and yields between each chunk. The UI stays responsive while the scroll history streams in. Your scroll position from the previous session is restored once the replay completes.

App State

  • Sidebar width: your custom sidebar size
  • Window bounds: position, size, and maximized or fullscreen state
  • Portal state: tabs, links, default target, and width
  • Terminal sizes: per-panel resize ratios
  • Focus mode: whether focus mode was on

Resuming agent conversations is separate from workspace state. See Agent Session Resume.

This persistence covers clean restarts and project switches. If Daintree detects a crash loop on startup (three unclean exits inside a 30-minute sliding window), it boots in safe mode and quarantines the panels that look like the culprits, which breaks the loop. The window decays on its own: once the most recent unclean exit ages out, the next launch comes up normally. For how Daintree handles unclean exits, see Crash Recovery.

Scroll Buffer Limits

Scrollback line limits

The scrollback setting controls how many lines of history each terminal keeps in memory. It is also the upper bound on what gets serialized to disk. Set the base value in Settings > Terminal (Scrollback section). Presets are 500, 1,000 (default), 2,500, and 5,000 lines.

Different terminal types apply a multiplier to the base setting. Agent run logs are usually worth more than shell command output, so they get a larger budget:

Terminal typeMultiplierEffective range (clamped)
Agent (Claude, Gemini, Codex, OpenCode)1.5× base500 to 5,000 lines
Standard terminal / shell0.3× base200 to 2,000 lines
Performance mode (all types)Flat cap100 lines

At the default base of 1,000 lines, agent terminals keep up to 1,500 lines and standard terminals keep up to 300 lines. If you need more history, raise the base setting to 2,500 or 5,000 in Settings.

Serialization size cap

Daintree keeps up to 5 MB of serialized scroll buffer per terminal. At roughly 250 bytes per line (including ANSI color codes), 5 MB holds around 20,000 lines, well above the 5,000-line maximum any terminal type can reach. The cap is rarely hit.

It can matter for terminals with unusually dense ANSI output, such as heavily color-coded diffs or verbose logging with escape sequences. That output pushes the per-line byte count well above 250 bytes, which brings a 5,000-line agent buffer closer to the 5 MB threshold.

Note
If a terminal's serialized scroll buffer exceeds 5 MB, Daintree skips saving it. No error is shown. If a terminal isn't restoring its previous output, dense ANSI output from the prior session is the most likely cause. Lowering the scrollback setting in Settings > Terminal reduces the amount of data serialized.

Storage and Eviction

Daintree manages the terminal-sessions/ directory (inside Daintree's app data directory) on its own. You never need to clean it up by hand:

  • 30-day TTL: session files older than 30 days are deleted on startup
  • 100 MB total cap: if the directory exceeds 100 MB, the oldest files are evicted first
  • Orphan cleanup: files for terminals that no longer exist are removed on startup
  • Panel removal: deleting a terminal panel removes its .restore file. For agent panels, that happens after the 20-second trash window closes, not the moment you close the panel
  • Hibernation: hibernating a project clears its session files. The next time the project opens, terminals start fresh with a "Session hibernated" banner instead of restored scroll content

Disabling persistence

To turn off terminal session persistence entirely, set the environment variable DAINTREE_TERMINAL_SESSION_PERSISTENCE=0 before launching Daintree. This is a developer escape hatch, not a regular setting.

Agent Session History

When you trash an agent panel, Daintree saves a session record once the trash window closes. The record lets you pick the conversation back up from the Panel Palette later. Records live in agent-session-history.json inside the app data directory.

The 20-second trash window

Closing an agent panel does not kill it right away. The panel moves into the sidebar trash bin and starts a 20-second countdown. During the window you have three options:

  • Restore: click the restore button on the trashed panel to bring it back exactly as it was. No session record is written.
  • Remove now: click the X to kill the agent immediately and write the session record at once.
  • Let it expire: do nothing. After 20 seconds the PTY is killed and the session record is written.
Note
The session enters the Resume Sessions list only after the 20-second window closes, or after you click the X. While the panel is still counting down in the trash bin, no record exists yet.

Quitting Daintree with active agent panels writes their session records as part of shutdown, without a trash countdown. For the quit-confirmation dialog that protects in-progress work, see Session Resume on the agents page.

What gets saved

Each record holds enough metadata to relaunch the agent in the same state:

  • Session ID from the agent's terminal output
  • Agent type (Claude, Gemini, Codex, OpenCode)
  • Model ID, if the agent reported one
  • Worktree ID, which scopes the record to where it was started
  • Panel title: the last meaningful title the terminal emitted, or the agent's default name if nothing useful was seen. Shell binary names, absolute paths, and prompt-style strings are filtered out so the label stays readable.
  • Original launch flags (for example, --dangerously-skip-permissions for Claude), so resume keeps the same behavior
  • Timestamp of when the record was saved

Resuming from the Panel Palette

Open the Panel Palette with Cmd/Ctrl+N and scroll to the Resume Sessions section at the bottom. It shows up to five entries, scoped to the active worktree. Each entry reads Resume: {title} when a meaningful title was captured, or Resume {AgentName} otherwise. The subtitle shows the model, agent name, and a relative timestamp.

Selecting an entry launches a new agent panel with the right resume flag and the original launch arguments. You get a fresh panel that continues the same conversation. For the per-agent resume syntax, see Agent Session Resume.

Note
Sessions are scoped to the worktree they were started in. If that worktree is later deleted, its sessions do not show up in the Resume Sessions list for any other worktree. The records stay in the JSON file until the 30-day TTL prunes them, but the palette filters them out.

Storage limits

Daintree keeps up to 50 records per worktree, newest first. Anything older than 30 days is pruned on the next write. The history file is evicted along with the rest of Daintree's session data, so you never need to clean it up by hand.

Hibernation

Hibernation pauses background projects to free up system resources. When a project hibernates:

  1. All PTY processes for the project are killed
  2. Its persisted state file is cleared
  3. It stays in the project list, and starts fresh when you reopen it

Configuration

Configure hibernation in Settings > General:

  • Enable/disable: off by default
  • Threshold: how long a project must sit idle before it hibernates: 12, 24 (default), 48, or 72 hours. Configurable between 1 and 168 hours.

How It Works

  • Daintree checks for hibernation candidates every hour (the first check runs 5 seconds after startup)
  • The active project is never hibernated: only background projects qualify
  • When you switch back to a hibernated project, relaunch the terminals you need

System Sleep & Wake

Daintree hooks into your OS power management. While agents are actively working or running, it prevents the system from sleeping. If the system does sleep anyway, say you close your laptop lid, here is what happens:

On Sleep

  • PTY processes are paused
  • Background projects may be hibernated
  • Sleep metrics are recorded

On Wake

  • PTY processes are resumed
  • Worktree state refreshes (picks up any external changes)

Project Switching

Switching to another project saves the current project's state (terminal layout, active worktree, panel sizes), closes the switcher, and swaps in the new project's view. The visible swap is decoupled from loading the new project's data. Daintree shows the new view as soon as it's ready, and the saved state hydrates from disk on its own. The previous project keeps running in the background or closes fully, depending on what you chose in the close dialog.

Hover prefetch

While the project switcher is open, hovering a project row primes its data before you click. Daintree waits 150ms after the pointer settles on a row, then reads that project's saved state from disk, so it's in memory by the time you select it. The prime covers the full hydrate payload: app state, terminal snapshots, panel layout, active worktree, focus mode, terminal config, and agent settings.

Prefetch is mouse-only. Touch and pen pointers are ignored, and the active project (along with any project flagged as missing) is skipped. The primed state is cached for 30 seconds and consumed once, on the click that follows the hover. Re-hovering within 15 seconds of a successful prime won't read from disk again.

Note
Hover prefetch is best-effort, not a guarantee. If the prime hasn't landed, because you clicked faster than the 150ms delay or the cache expired, the switch falls back to reading from disk on click. Same result, not as fast. Prefetch errors are swallowed and never block a switch.

How the swap happens

Daintree keeps a small number of recently used project views cached in memory (see Cached Project Views). What you see on a switch depends on whether the target project's view is one of them:

  • Warm swap (~16ms): the view is already cached, so Daintree re-attaches it synchronously. The switch is near-instant, especially when the hover prefetch has already landed.
  • Cold start (~500ms): the view has to be rebuilt. Daintree inserts the new view behind the outgoing one and holds the old view in place until the new one paints, so you never see a blank or half-rendered frame mid-switch.

Switches are serialized. If you click through several projects quickly, they queue and run in order rather than racing each other. A cold start that stalls won't hang the UI. After a timeout, Daintree falls through to the new view instead of waiting indefinitely.

Eviction vs. freeze

When a project's view leaves the cache, Daintree drops it in one of two ways. They sound similar but behave differently.

  • Eviction: the view's Chromium renderer is destroyed and removed from the cache, freeing roughly 100 to 500 MB. Returning to the project pays the ~500ms cold load to rebuild it.
  • Freeze: the view stays in memory, but its renderer is suspended. Timers and the event loop are paused through the page lifecycle API. It still holds RAM, and resuming is instant when you switch back.

Which one happens depends on why Daintree is under pressure:

  • Eviction follows least-recently-used order, with one protection: idle views are evicted before views with an active agent. A view's memory size doesn't change its place in line.
  • Only memory pressure that actually contributed to the resource profile destroys views. When the Efficiency profile engages for other reasons, such as battery, thermal, or sustained CPU pressure, Daintree freezes the cached views instead of destroying them, so they come back instantly once the pressure clears.
  • Under extreme free-memory conditions, Daintree clamps the cache to a single view, regardless of your configured limit or current profile, until the next evaluation cycle.
Note
Freeze and eviction act on a project's view, the UI layer. They are automatic and silent. The terminals, PTYs, and agents behind the view keep running either way. Hibernation is different: it acts on the whole project, kills PTYs, and clears the project's state file on a user-configured timer measured in hours. For the resource profiles that drive freeze and eviction, see Automatic Resource Management.

Custom panel names survive project switches. If you rename a panel by double-clicking its tab title, or through the right-click context menu's Rename item, that name comes back when you return to the project. The custom name takes precedence over whatever title the terminal or agent would otherwise set.

Focus Mode

Focus mode is a per-project toggle that hides UI chrome for a distraction-free view. The state persists across sessions.