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.
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 filesrestores 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 bothrestores 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:
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_file → read_file,
view_notebook → read_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.