Skip to content

Session persistence

Kangentic persists every agent session to a local SQLite database, so a session survives an application crash, a system restart, and a deliberate pause, and resumes with its conversation intact.

What is saved, the states a session moves through, and how a crash or a pause is handled.

Every interaction between Kangentic and the agent CLI is stored in real-time:

  • Conversation history - The full prompt and response chain
  • Session metadata - Start time, status, worktree path, branch name
  • Terminal output - Raw terminal data for replay
  • Task state - Board position, description, and user notes

Data is written incrementally as the agent works, not just when the session ends. If the application crashes mid-session, the data up to that point is preserved.

StateDescription
RunningAgent is actively working. Session data is being written in real-time.
QueuedWaiting for a free slot because the max-concurrent-sessions limit is reached. Promoted to Running automatically (FIFO) when a slot frees up.
SuspendedAgent was intentionally paused (by user or system). Full state is preserved for resume.
ExitedAgent process terminated (finished, errored, or crashed). Check exit_code for details.
OrphanedSession was running when the application closed unexpectedly. Recoverable on next launch.

The Agent Monitor shows sessions from every project grouped by these states, as Idle, Active, Paused (suspended plus queued), and Recently finished.

Session states and the moves between them A session record is in one of five states: Running, Queued, Suspended, Exited, or Orphaned. Moving a task out of To Do starts a session as Running, or as Queued when the concurrent session limit is reached; a Queued session becomes Running when a slot frees, first in first out. A Running session becomes Suspended when its task moves to Done, moves to a column with auto-spawn off, is paused from the task detail window, or the app quits. A Suspended session becomes Running again when its task moves to an active column, which resumes the same conversation. A Running session becomes Exited when its task is reset by a move back to To Do, the task is deleted, or the agent process exits on its own; a Queued session becomes Exited on a reset. Exited is the end of that record: the next move out of To Do spawns a fresh session. A Running or Queued session found on the next launch after the app crashed or was killed is Orphaned; startup recovery resumes it to Running, or marks it Suspended when auto-resume is off. A column move that changes the agent, model, or effort also passes through Suspended, but only for the seconds it takes to respawn. Task leaves To Do Queued Running Suspended Exited Orphaned limit reached slot frees suspend resume reset, delete, exit reset app crashed recovered on launch auto-resume off
A session's five states. Solid edges are moves you make on the board; dashed edges are crash recovery on the next launch. A reset is a move back to To Do. A session suspends on a move to Done, a park in a column with auto-spawn off, a pause, or app quit, and resumes on a move to an active column. A settings change on a column move passes through Suspended for a few seconds and comes back as Running.

Suspended is not always a park. Moving a task to a column that changes its agent, model, or effort suspends the running session first and respawns it a couple of seconds later, as does crossing into or out of an isolated column. For that gap the session is genuinely suspended without anyone having paused anything. Since v0.41.0 the card names what is happening instead of going dark and offering a Resume button you should not press: Switching model…, Switching agent…, Applying new settings…, or Starting new session…, on both the card and the task detail window. The two moves that really are parks, into Done and into a column with auto-spawn off, clear the label and read as Paused.

When Kangentic starts, it checks for sessions that were in a “Running” state when the application last closed. These are marked as crashed and can be:

  • Resumed - Relaunch the agent with the full conversation context. The agent picks up where it left off.
  • Archived - Keep the session data but don’t resume. Useful if the task is no longer relevant.

Pausing from the header button or its kebab twin also closes the task’s detail window, on the reasoning that pausing means “I am done with this task for now”. The session is only suspended, never killed, so it resumes exactly as before when you reopen the task. This is wired to the pause gesture rather than to the suspended state, so the other routes into suspension leave your windows alone: a board move to a To Do or Done column, the Code Review column, and a restart with auto-resume disabled all suspend without closing anything.

Sessions paused manually by the user (via the pause button in the task detail window or the kebab menu) are remembered across restarts. On relaunch, user-paused sessions remain paused instead of auto-resuming. This respects user intent - if you paused an agent, it will not start back up on its own. Only system-suspended sessions (those suspended by shutdown or column moves) auto-resume. The Agent Monitor lists paused sessions under its Paused group, but its rows carry no pause or resume controls - open the task’s detail window (from the monitor or the board) to resume.

The Agent Monitor over all three projects: summary tiles for idle, active, and paused sessions, then a card per session grouped by project, each with its column, its latest output, and its model and context use. v0.46.0 The real app on a sample install. The projects and terminals are recordings, connected to nothing, so click anything.

When you resume a suspended or crashed session, Kangentic:

  1. Reads the full conversation history from the database
  2. Recreates the agent session with the stored native session ID (via each CLI’s native resume flag)
  3. Reconnects the terminal to the new process
  4. The agent continues working with full awareness of previous context

The agent sees the same conversation it had before, so it can continue its work without repeating steps or losing context.

On the board, a resumed card reads Resuming agent…, not Starting agent…, until the agent reports its model. Before that, while a Resume is still preparing, the card reads Resuming session… rather than Paused, so a slow resume does not look like a press that did nothing. A session you paused yourself reads Paused and stays that way through a relaunch.

The contoso-web board in Planning: the Fix WebSocket reconnection card shows its agent's last message and reads Resuming agent..., the Onboarding empty states card below it reads Paused, and the agents in Executing are working. v0.46.0 The real app on a sample install. The projects and terminals are recordings, connected to nothing, so click anything.

Three states have no in-place Resume (since v0.36.0), each with a message naming the way forward instead of an error:

Task isMessage
In a To Do columnCannot resume a session for a task in the To Do column
In DoneThis task is complete. Move it out of Done to continue working on it.
Archived in any other columnThis task is archived. Restore it to the board to continue working on it.

The gate is the column’s role, not its auto-spawn setting, so a custom column you have turned auto-spawn off for still offers Resume.

A completed task lives in Done with its worktree deleted and its session suspended. Resuming it in place would recreate that worktree and put a live agent on a task with no board card, burning quota with nothing on screen to notice it. The designed route back is to move the task out of Done, which unarchives it first and then spawns through the normal path. Moving a task out of Done also unarchives it immediately rather than waiting for the spawn to land, and the card shows the restore as in progress rather than as Paused, so a slow worktree rebuild does not read as a stall.

Kangentic will not resume a conversation that does not exist. A session that ends before its first turn leaves a resumable-looking record pointing at a transcript the agent CLI never wrote, and re-entering it used to leave you on a bare shell with the agent gone.

That is now detected and the spawn is downgraded to a fresh one. The check is deliberately narrow, and all three conditions must hold: the transcript path is the one the agent itself reported, that same report must independently say the conversation had no turns (no tokens, no cost), and any missing or unreadable evidence leaves the resume exactly as it was. A session that had real turns is never downgraded, even if its transcript has moved.

Claude Code deletes transcripts older than its cleanupPeriodDays setting, 30 days by default, so any task left idle that long used to resume onto a bare shell and a “No conversation found” message. Since v0.46.0 Kangentic checks before the spawn. When Claude Code has deleted the conversation, the same spawn starts a new one, with a recap of the task as its prompt: what happened and when, the task and its summary, the old conversation’s last turns, the branch’s commits and uncommitted changes, and a pointer to kangentic_get_transcript for the rest. The old session record is kept, and nothing is deleted.

On the task’s main session the recap is the whole prompt, and it asks the agent to say where the work stands and then wait. The column’s message, its task template, and the task’s own command are not sent, because a conversation old enough to expire belongs to a stale task, and the run log records those message rows as skipped, “Not sent”. The turn that answers the recap runs no On agent turn end rows and no workflow step. A column that keeps its own conversation in an isolated session starts a normal fresh pass instead, with the recap added to its prompt as context. A toast names each task: “Fix login redirect” started a new conversation because the one from Sep 3 expired. A notice for a project in the background shows when you open that project.

The check needs proof, so it only ever costs a resume, never data. It reads only transcript paths Claude Code itself reported, and it starts over only when the file is gone from every one of Claude’s project folders. Anything it cannot rule out, such as a permission error or a path on another filesystem, resumes exactly as before. Only Claude Code is checked today. A Claude conversation the check cannot prove gone, such as one a moved project folder hides, still ends at startup, and Kangentic reads Claude Code’s own account of why and raises an “Agent did not start” notice naming it, as it has since v0.42.0.

Kangentic also keeps its own copy of each conversation, so the conversation viewer can still show one after the agent deletes its file.

An agent can stop without anyone asking: it finishes and exits, or a resume fails to read its transcript. Since v0.42.0 every move, resume, and settings change first checks the task’s session against the live registry rather than trusting the stored pointer. A pointer at a session that has already ended is cleared, so the move resumes the record instead of “keeping alive” an agent that is not there, which is what used to happen on every move of such a task; and a live agent the pointer had lost is re-linked, so the move continues it instead of spawning a second one.

Resume matrix:

AgentResumeMechanism
Claude Code✓Caller-controlled session ID via --resume for deterministic re-entry
Codex CLI✓codex resume <id> with the CLI-generated rollout ID
Gemini CLI✓--resume <id> against the auto-generated chat session file
Cursor CLI✓cursor-agent --resume=<id> using the session ID captured from the init event on Cursor’s stream-JSON output
GitHub Copilot CLI✓Caller-controlled UUID via --resume <uuid> (same semantics as Claude)
Grok Build✓Caller-owned session IDs: --session-id <uuid> on first launch, --resume <uuid> to re-enter (Claude-identical flag semantics)
Antigravity CLI✓agy --conversation <uuid> with the conversation ID captured from hook payloads or the shutdown summary; resumes cross-directory
Goose CLI✓Caller-supplied session name: Kangentic passes -n <id> at spawn and resumes with -r -n <id>; resumes cross-directory (state is keyed by name, not path)
OpenCode✓Native session IDs (ses_<id>) captured from the plugin’s session.created event
Aider-Edit-first and relaunched fresh. Prior edits are still on the worktree, but conversation history is not carried over
Qwen Code✓Caller-owned session IDs: --session-id <uuid> on first launch, --resume <uuid> to re-enter (Gemini-style chat session file)
Kimi Code✓Caller-owned session IDs via --session <uuid>; --continue resumes the latest
Factory Droid✓droid --cwd <cwd> --resume <uuid> with the UUID from ~/.factory/sessions/<cwd-slug>/<id>.jsonl
Warp (Oz CLI)-oz does not expose a session-resume mechanism. Re-entry starts a new session in the preserved worktree
Ollama-Local single-turn chat: each ollama run is one turn with no CLI-level session resume. Re-entry starts a fresh run in the preserved worktree

Kangentic preserves terminal scrollback across resume so you still see the prior session’s output.

The less common paths: isolated sessions, the state machine, and where history and usage live.

A task normally runs on a single main session - its primary conversation, resumed as the task moves between columns. A column can instead be set to run tasks on an isolated session: a separate, independently-resumable conversation of its own, keyed by that column.

An isolated session does not inherit the main conversation, which suits work that should stay independent of it - a code review column is the canonical example. A task can hold its main session plus one isolated session per isolated column, all sharing the same worktree, so edits made in either are real and persist. Only one agent runs at a time per task.

  • Entering an isolated column suspends the main session and resumes (or freshly spawns) the column’s isolated session.
  • Leaving an isolated column for a normal column suspends the isolated session and resumes the main session.
  • The session tab badges the active track as Isolated or Main so you always know which conversation you’re watching.

Two per-column settings control this, both round-tripped to kangentic.json: sessionTarget (main or isolated) and sessionSpawnStrategy (create_or_resume or always_spawn_new). Only the first is offered in the Column Manager, as Session; switching it to Isolated carries the strategy to a fresh pass each entry (the reviewer archetype), and switching back to Main returns it to resuming for continuity. The other combination, an isolated track that resumes, is a hand edit of kangentic.json. always_spawn_new applies on column entry only: an app restart, or a pause and resume of a session already in progress, still resumes it. See Column-level settings.

Every session lifecycle transition (spawn, suspend, resume, kill, queue-promote) runs through an atomic state machine with a per-task lock, added in v0.15.0. The lock prevents two transitions from interleaving on the same task, which was the root cause of several pause/resume race conditions before v0.15.0: a rapid drag could trigger a spawn while a prior kill was still cleaning up, leaving a half-alive session. Now a move that arrives while the task is mid-transition is queued until the current transition completes, then either executes or is coalesced with the target state if it became redundant.

The state machine also coordinates with the concurrency queue: if a spawn transition finishes by placing the session in the queue (because the max-concurrent limit is reached), queue promotion runs under the same lock so a later manual suspend doesn’t race with an automatic promotion.

Killing a session used to mean killing the process outright, which gives an agent CLI about 100 milliseconds to tidy up. That is enough for an agent that has been running a while and nothing at all for one that just started, and Claude Code in particular keeps a startup record it withdraws only on a clean exit. Kill it inside that window and the next launch drops to Claude’s plain renderer instead of its fullscreen one. Do it twice and the plain renderer sticks for every Kangentic session on the machine.

Since v0.41.0, a session Kangentic considers young - it entered its fullscreen view less than 12 seconds ago, or has not drawn a frame yet and spawned under a minute ago - is sent the agent’s own exit sequence first and force-killed 1.5 seconds later, the same grace a suspend already had. A mature session is still killed at once. Stopping a Command Terminal, dragging a task back to To Do, deleting a task, and quitting the app normally all take this path; the quit extends its own shutdown wait so the grace outlives it. The exceptions are the shutdowns Kangentic does not get to pace: a Windows log-off or OS shutdown, and a kill signal, both of which exit immediately and flush every waiting terminal at once. Nothing about the grace is configurable and nothing asks you to wait for it.

Every session, finished or archived, stays in the database. Three surfaces read it:

  • The View conversation pill on a task window opens the conversation viewer, a read-only transcript of the task’s newest session.
  • The completed task view on an archived task shows when it ran, its agent runtime, cost, and changes, and on its Usage tab every run with its tokens and tool calls.
  • Conversation memory indexes the transcripts so Quick Find and the kangentic_search MCP tool can search them.

Session data lives in a per-project SQLite database under Kangentic’s data directory, at projects/<projectId>.db beside the machine-wide index.db, not inside your repository. See Configuration files for where that directory is on each platform. The repository’s own .kangentic/ folder holds the files that sit beside it: config.json, worktrees, per-session directories, the files you attach to tasks and backlog items, logs, and the MCP config. Kangentic adds it to .gitignore when it opens the project. The database grows with usage but stays small, typically a few MB.

Per-session token and cost samples are append-only in a separate usage_history table, so the Usage Stats totals for live (the trailing two hours, the default), today, week, month, and all stay accurate even after you delete a task or wipe a session. Deleting a task removes its row from sessions and tasks but leaves the historical samples intact. The dashboard’s saved range lives in usageStatsPeriod (see the Configuration reference).

Kangentic is free and open source. A star helps other people find it.

Star on GitHub