Skip to content

Sessions, resume & compaction

Every conversation is saved to disk as it happens, so you can quit and pick up where you left off, roll back a mistake, or trim a long context. This page covers the journal, resuming, rewinding, and compaction.

The journal

Each session is an append-only JSONL file. The first line is a meta record (provider, model, workdir, created time); every line after it is either a message (one entry of the provider-native history, appended at each turn boundary) or a mode change.

~/.kin/projects/<workdir-slug>/<session_id>.jsonl

The <workdir-slug> is the project's absolute path with separators flattened to dashes, so every project keeps its own set of sessions. The id looks like s_<unix-time>_<hex>.

Append-only means crash-safe: a torn final line is skipped on load, not fatal, and each turn appends only its new messages. Provider-native message shapes are stored verbatim — OpenAI flat dicts and Anthropic content-block lists (thinking signatures included) — so a resumed session is faithful on the same backend.

The projects root can be relocated. KIN_HOME moves all of ~/.kin; the KIN_SESSION_DIR environment variable or the session_dir global setting points the whole projects tree somewhere else. See Environment variables and Settings (TOML).

Resume & continue

Action How
Resume a specific session at launch kin --resume <id>
Resume the newest session for this project kin --continue
Switch sessions mid-run /resume (picker) or /resume <id>

Headless runs (kin -p) write the same journal, so a cron run that exited 2 (needs-human) can be picked up interactively with kin --resume <id> — the id is printed on stderr and in the --output report.

At launch, resume reuses the saved provider and model from the journal's meta unless you override a backend flag. If the saved provider doesn't match the backend you launch with, resume fails closed (it refuses rather than replaying into the wrong wire) and kin starts a fresh session instead. A bare --resume (no --mode flag and no KIN_MODE) also keeps the session's saved permission mode rather than re-applying your global default, so a strict-mode session you resume doesn't silently reopen in auto.

In-session, bare /resume opens a picker of this project's sessions (newest first, with a one-line title, age, turn count, and model); /resume <id> switches directly. The switch is non-destructive — the current session's journal is left intact on disk — so it needs no confirmation. You can't switch while a turn or a compaction is running, or while another modal is open.

Forking

/fork branches the current session: it copies the on-disk journal to a fresh session id — everything after the meta line copied byte-for-byte — then switches you onto the copy via the exact same path /resume uses. The fork becomes the live session; the source is left exactly as it was at the moment you forked, untouched and resumable with /resume <old-id> later. This is the reversible way to explore a branch without losing the original: fork, go try something, and if it doesn't pan out, /resume back to where you started.

There's no separate "fork back" verb — a fork is just a session like any other, and /resume already gets you to any session by id. /fork refuses (with a notify) while a turn or a compaction is running, the same as /export.

Subagent journals are hidden

Every subagent spawned by task writes its own journal next to the root session's (same project dir), with the spawning session's id recorded as parent_session_id in its meta. These child journals are deliberately hidden from every root-session enumeration surface/resume, --continue, and /sessions list only root sessions, so a busy session that spawned a dozen subagents doesn't drown the picker and --continue always resumes the root, not whichever subagent happened to finish last. An explicit kin --resume <child-id> still loads one (a deliberate act; the id isn't advertised). Subagent transcripts are read live through the agent panel (Ctrl+O) and the agent_output tool, not through resume.

Session titles

Each session's picker row starts with a default title — its first user message. After a session sits idle for ~120 seconds with no new turn, kin generates a short (3–6 word) title from the conversation via a one-off model side-call (the same pattern compaction uses — it doesn't touch your history or emit turn events) and saves it to the journal. The timer resets on every new turn, so an active session is never interrupted, and a resumed historic session picks up a title after the same idle gap. The generated title is a title record in the journal — persisted like mode, so it survives /compact, /clear, /rewind, and a /providers swap — and is what the /resume picker prefers over the raw first message.

See Slash commands for the full command list and CLI reference for the launch flags.

Rewind & retry

/rewind [n] [conversation|files|both] rolls back the last n completed user turns (default 1) — the conversation, the on-disk files, or both. A bare /rewind or /rewind <n> behaves exactly as before: it rolls back the conversation and repaints the transcript from the truncated history. The cut is always taken at a real user-turn boundary, so a tool round is never split. The journal is rewritten to match, so a resumed session reflects the rewound state.

  • /rewind files restores on-disk file state to a checkpoint taken just before the rewound turn's first edit — without touching the conversation. If those turns made no edits (or the workdir isn't a git repo, or the snapshot safety net was off), it's a clean no-op: "no file changes to rewind."
  • /rewind both restores files first, then rolls back the conversation to match — only if the file restore succeeds. A failed restore leaves the conversation untouched (files-first, so you never lose the record of what happened without also getting your files back).

files and both always ask for confirmation, even for a single turn — restoring the worktree is destructive, and a protective snapshot of the CURRENT state is taken automatically before either one runs, so a restore you didn't mean to do is itself reversible.

/retry re-sends the last user message — useful after an error or an interrupt. It still works after a /rewind, because the last user turn is recomputed from the truncated history.

When a turn ends in a backend hard-error (the model endpoint was unreachable, or rejected the request), the error note in the transcript is followed by a focusable ↻ retry button — click it, or Tab to it and press Enter. Unlike /retry, this first removes the failed turn from history, so the model sees exactly one fresh copy of your message (no duplicate). The note's guidance distinguishes a recoverable failure (a dead/unreachable endpoint — worth retrying as-is) from a rejected request (a bad api key / model id / base url — fix it first, then retry).

Neither /rewind nor /retry (nor the retry button) runs while a turn or a compaction is in flight.

Double-Esc rewind picker

Press Esc twice within about a second — on an empty composer, with no modal open, and no turn running — to open a picker instead of typing /rewind by hand: it lists your recent turns (newest first, with a short preview of each), then asks for a scope (conversation / files / both) once you pick one. Up / Down navigate, Enter selects, Esc backs out with no action at either stage. The picker dispatches through the exact same /rewind <n> <scope> path described above, so files/both still confirm before touching your worktree.

A single Esc keeps its normal meaning everywhere (interrupting a running turn, closing a menu, exiting cursor mode) — the picker only appears on a second tap while kin is otherwise idle.

Export

/export [path] dumps the current session's saved transcript to a markdown file — a keepsake, not a second live copy. It reads the ON-DISK journal (the same one resume/rewind read), so it always reflects what's actually durable, and renders ## You / ## Kin sections with tool calls as fenced blocks and tool results elided to a one-line summary (a full tool result — a read_file dump, a shell command's stdout — would make the export unreadable). Bare /export writes kin-export-<session-id>.md into the workdir; an explicit path wins, and overwrite is allowed with no confirm (unlike /clear//rewind, this is additive, not destructive). Refuses with a notify while a turn or a compaction is running. The exported file carries your conversation verbatim, with no sanitization — it's a file you asked for, not something rendered back at you.

File snapshots

In a git workspace, kin takes a lightweight checkpoint of your files before the first edit of every turn — a git commit-tree recipe under refs/kin-snapshots/, invisible to your real index, HEAD, or worktree (it never stages, commits, or moves a branch you'd see in git status or git log). This is what /rewind files//rewind both restore from. The checkpoint is taken once per turn (a turn that edits three files gets one checkpoint, not three), and a session keeps its 20 most recent checkpoints (older ones are pruned automatically).

The safety net is on by default (KIN_SNAPSHOTS=0 or snapshots = false in settings.toml turns it off) and degrades to a silent no-op outside a git repo — no checkpoint, no error, /rewind files just reports nothing to restore.

What it does not cover: a shell command or run_code script that writes a file directly (bypassing kin's own write_file/edit_file tools) is invisible to this safety net — the checkpoint only fires on kin's own EDIT-kind tool calls. For those, git status/git diff (or your own snapshot habits) are still the tool.

Terminal notifications

kin gives your terminal a walk-away signal: a bare bell (\x07) always, plus — on a terminal it recognizes as capable (Ghostty, iTerm2, WezTerm, or kitty) — an OSC 9 escape sequence that surfaces a native desktop notification (dock bounce / banner) even when the tab isn't focused.

It fires on two moments:

  • A turn finishes that ran 15 seconds or longer (a fast turn doesn't need a walk-away signal). Pressing esc to interrupt never notifies — you were right there.
  • kin needs your input — an approval prompt, a question, a plan or settings-proposal review, or a sandbox-override confirmation.

Repeated notifications are debounced to at most one every 10 seconds. If your terminal reports focus events, the bell is suppressed while the app is actually focused (a terminal that never reports focus never goes silent — kin only suppresses once it has proven the terminal tells it who has focus).

The message text is always one of a fixed set of static strings (kin — turn complete, kin — needs your input, …) — it never includes any model or server output. On by default; KIN_NOTIFY=0 or notify = false in settings.toml turns it off. See kin -p for the headless-run equivalent — the same bell fires to stderr at process completion when stderr is a real terminal.

Compaction

As history grows toward the context window, compaction replaces the older portion with one synthetic summary message and keeps the most recent turns verbatim. Run it manually with /compact; add a focus to bias the summary:

/compact the auth refactor and the failing tests

The summarizer flattens the old history to plain text and produces a structured summary (task overview, current state, discoveries, next steps, context to preserve). The result is a single <summary>…</summary> user message followed by the verbatim recent turns — the cut is taken at a user-turn boundary so no tool round is split and retained thinking-signature blocks are untouched. After a compact, the journal is rewritten and the context meter re-baselines.

Auto-compaction is off by default. It runs only when you set a threshold — KIN_COMPACT_THRESHOLD (env) or compact_threshold (settings), a fraction of the window clamped to 0.1–0.98. When set, kin checks at the end of each turn and compacts once usage crosses the threshold. The number of recent turns kept verbatim defaults to 4 and is tunable via KIN_COMPACT_KEEP_TURNS / compact_keep_turns. Subagents never auto-compact.

Context usage

/tokens shows where your context is going. On the Anthropic wire it breaks the total into system prompt, tool definitions, and history (real counts via the wire's token counter); on the OpenAI-compat wire, which doesn't expose a counter, it reports the live metered total only. The component figures are approximate — each includes per-request framing, so they sum to slightly more than the measured total. The top bar shows live usage continuously as you work.

Ephemeral runs

Launch with --no-save to run without writing a journal. Nothing is persisted, so the session can't be resumed, rewound, or listed — useful for a throwaway question you don't want recorded.

Suppressing empty sessions

Sessions that never receive a user submission — you launch kin and quit without typing anything — leave no journal file on disk. The journal + tasks-DAG sidecar pair is created in memory, but Session.aclose deletes them before tearing down the bg subsystems when the per-session turn counter is still zero. A resumed session that doesn't get a new turn keeps its file (its history preceded this launch — there's something to resume). A mode-only change (/mode strict then quit) is also suppressed — no user submission, nothing worth recording.

--no-save / KIN_NO_SAVE continue to skip persistence entirely; the suppression only applies on the default save path.

Old tool names in old journals

The tool registry keeps dispatch-only aliases for view_fileread_file, view_notebookread_notebook, and exit_plan (the pre-plan-lifecycle tool) so journals written before these renames still resume — the replay sees the old name, the alias routes it to the current tool, and the model on the next turn only ever sees the new names. The aliases are not advertised to the model; they're a backwards-compat seam only. See Tools for the full tool surface.