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.
Persistence
Section titled “Persistence”What is saved, the states a session moves through, and how a crash or a pause is handled.
How persistence works
Section titled “How persistence works”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.
Session states
Section titled “Session states”| State | Description |
|---|---|
| Running | Agent is actively working. Session data is being written in real-time. |
| Queued | Waiting for a free slot because the max-concurrent-sessions limit is reached. Promoted to Running automatically (FIFO) when a slot frees up. |
| Suspended | Agent was intentionally paused (by user or system). Full state is preserved for resume. |
| Exited | Agent process terminated (finished, errored, or crashed). Check exit_code for details. |
| Orphaned | Session 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.
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.
Crash recovery
Section titled “Crash recovery”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.
User-paused sessions
Section titled “User-paused sessions”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.
Resuming sessions
Section titled “Resuming sessions”When you resume a suspended or crashed session, Kangentic:
- Reads the full conversation history from the database
- Recreates the agent session with the stored native session ID (via each CLI’s native resume flag)
- Reconnects the terminal to the new process
- 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.
When resume is refused
Section titled “When resume is refused”Three states have no in-place Resume (since v0.36.0), each with a message naming the way forward instead of an error:
| Task is | Message |
|---|---|
| In a To Do column | Cannot resume a session for a task in the To Do column |
| In Done | This task is complete. Move it out of Done to continue working on it. |
| Archived in any other column | This 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.
A conversation the agent never wrote
Section titled “A conversation the agent never wrote”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.
A conversation the agent deleted
Section titled “A conversation the agent deleted”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 that ended on its own
Section titled “An agent that ended on its own”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:
| Agent | Resume | Mechanism |
|---|---|---|
| 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.
Internals and history
Section titled “Internals and history”The less common paths: isolated sessions, the state machine, and where history and usage live.
Isolated sessions
Section titled “Isolated sessions”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.
State machine and locking
Section titled “State machine and locking”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.
A grace for a young agent
Section titled “A grace for a young agent”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.
Session history
Section titled “Session history”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_searchMCP tool can search them.
Storage
Section titled “Storage”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.
Lifetime usage history
Section titled “Lifetime usage history”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).
See also
Section titled “See also”- Activity detection - how Kangentic knows when a session is idle
- Notifications - alerts when sessions need you
- Agent Monitor - every project’s sessions, grouped by these states
- Configuration reference - session-related config keys
Kangentic is free and open source. A star helps other people find it.
Star on GitHub