Session commands#
term-llm sessions
term-llm sessions list --provider anthropic
term-llm sessions search "kubernetes"
term-llm sessions show 42
term-llm sessions export 42
term-llm sessions name 42 "investigate auth flow"
term-llm sessions tag 42 bughunt auth
term-llm sessions untag 42 auth
term-llm sessions autotitle
term-llm sessions autotitle --dry-run
term-llm sessions browse
term-llm sessions share 42 --visibility unlisted
term-llm sessions export gist 42
term-llm sessions delete 42
term-llm sessions reset
term-llm chat --resume=42Sessions are numbered sequentially for convenience, so 42 and #42 both work.
Sharing sessions#
term-llm sessions share <id> [--visibility public|unlisted|private] [--new] [--include-raw-reasoning] [--json] publishes the complete visible session through the configured generic sharing provider. It updates a persisted compatible whole-session share unless --new is supplied. Raw reasoning is excluded unless --include-raw-reasoning is explicitly supplied; the flag prints a privacy warning and still respects the reasoning source policy. Human-readable output includes provider notes and help, such as GitHub authentication guidance and the warning that unlisted Gists are not private. The TUI equivalent is /share [new] [raw] [public|unlisted|private].
Web response/conversation shares capture a point in time and are not persisted. term-llm sessions export gist remains the explicit GitHub-only export path. See Sharing for provider configuration and the custom helper protocol.
Conversation paths#
Use /tree in the TUI, or the per-turn branch actions in the Web UI, to continue from an earlier message without deleting the current conversation. Every path is stored as a normal resumable child session; the tree view can switch between all surviving alternatives.
When starting a path, choose what it should retain from the later turns being left out:
- Start clean copies only the conversation prefix through the selected branch point.
- Bring useful context asks the current model for short, non-authoritative notes covering useful findings, attempts, test results, and files touched.
- Bring specific context… adds your focus instructions to that same bounded note-generation policy.
Path notes are inserted as internal developer context before the first new user turn and shown as an expandable/inspectable artifact. They do not count as user messages. Generation uses a one-turn ephemeral helper request with bounded input and output. In the TUI, the child path opens immediately and note creation appears as live transcript work; you can draft at once, and a message submitted before the notes finish is queued until they are inserted. If generation is cancelled or fails, the queued message is restored to the composer and the new path remains clean. The Web flow prepares the selected context as part of its branch request.
Conversation branching rewinds model context only. Filesystem changes, commands, network requests, and other tool side effects from the previous path are not undone.
Storage#
Sessions are stored in SQLite at:
~/.local/share/term-llm/sessions.dbLLM jobs use the same store by default, preserving their transcripts and tool history.
Session storage config:
sessions:
enabled: true
max_age_days: 0
max_count: 0
path: ""
strip_image_base64: falseBy default, session rows keep base64-encoded image data and any saved local path, so uploads remain portable. To reduce the database size, set sessions.strip_image_base64: true; image parts with an ImagePath will retain only their path and metadata.
CLI overrides:
term-llm chat --no-session
term-llm ask --session-db /tmp/term-llm.db ...Web projects and immutable session bindings#
With project mode enabled, the Web sidebar groups conversations by durable project and keeps unbound conversations under No project. On the first project-enabled startup, term-llm registers the canonical startup directory as a bootstrap project unless the registry already contains records. Git startup paths normalize to the main repository root; non-Git paths remain exact. Migration 47 adds nullable project_id metadata without changing existing cwd or worktree_dir snapshots. Only historical sessions that unambiguously match the bootstrap root are backfilled; all others remain under No project.
A project’s ID is stable even if the project is renamed, archived, or restored. A session’s project_id is grouping and provenance, while cwd and worktree_dir are its authoritative immutable execution snapshot. Existing project conversations may resume after archival, but archived projects cannot start new conversations. Missing paths, replaced symlinks, moved roots, and cross-project worktrees fail closed before a model or tool run. A missing managed worktree may fall back only to its owning project’s validated canonical root.
In the Web UI, New chat opens a single draft surface with a project picker. It defaults to the last project context and includes No project, which uses the server startup directory without project provenance. Each project can still keep one Hub-node-scoped local composer draft behind that surface. Its unsent prompt, provider/model/effort/reasoning choices, and selected managed worktree survive reloads independently; attachments remain isolated in memory for the current tab. Drafts are client-only until first send, when the server’s immutable project/worktree snapshot becomes authoritative. Archiving a project disables its unsent draft but does not block its existing conversations.
Eligible No project conversations can be assigned once through the dedicated action. Assignment proves that a Git CWD/worktree belongs to the same main repository, or that a non-Git CWD exactly matches, and writes only project_id; it never changes execution paths.
Project registration and selection do not grant filesystem or shell permission. Workspace confirmations, configured read/write directories, shell approvals, and Guardian remain separate and authoritative.
Worktree-bound sessions#
Chat sessions can bind their tools to a git worktree without changing the term-llm process working directory. In the TUI, use /worktree (or /wt) while in chat:
/worktree new [name] [--clean] [--base REF] [-b branch]
/worktree browse
/worktree switch <name-or-dir>
/worktree diff
/worktree promote [--branch]
/worktree root
/worktree rm [name-or-dir] [--force]By default, /worktree new transfers staged, unstaged, and untracked changes from the root checkout into the new worktree. Add --clean to create the new worktree from the selected base without transferring those changes; the existing work in progress remains in the root checkout.
A bound worktree becomes the session BaseDir: relative read_file, write_file, edit_file, grep, glob, shell working directories, image paths, and spawned agents resolve there. The binding is saved as worktree_dir in SQLite and is restored on resume. For file/path tools it is only a proposed primary workspace: the first access asks the human to allow session-scoped read/write for the whole canonical worktree and states that shell remains separately controlled. A confirmed binding resumes without another prompt. Switching worktrees invalidates a mismatched primary confirmation while preserving additional workspace grants; shell approval remains independent throughout.
/worktree promote always promotes the current bound worktree; it does not accept a worktree name or path. The root checkout must be clean. By default, promotion applies the worktree changes onto the branch currently checked out in root, leaves them staged and uncommitted, removes the source worktree when no other session is using it, and rebinds the session to root. If applying the changes conflicts, term-llm preserves the source worktree and offers assisted recovery. When confirmed, it rebinds the session to root, takes a fresh snapshot so changes made while the prompt was open are included, applies that snapshot directly on the current root branch, and asks the LLM to resolve conflicts there.
Use /worktree promote --branch to avoid applying onto the current root branch. This mode creates and checks out a new root branch named after the managed worktree at the worktree HEAD, applies dirty and untracked changes there as staged and uncommitted changes, rebinds the session to root, and leaves the original worktree in place. To promote another worktree in either mode, switch to it first with /worktree switch or select it in /worktree browse.
In the Web UI, the header worktree chip is scoped to the active project draft or persisted conversation. Choose or create a managed worktree before the first send; project_id and optional worktree_dir are validated together and then locked into the session. Managed worktrees may live under term-llm’s XDG data directory rather than inside the project path, so ownership is checked by main-repository identity. The accessible worktree sheet provides diff, merge, promote, and removal actions without browser prompt dialogs. When merge into root conflicts, the sheet presents the same assisted-recovery question, choices, details, and LLM prompt as the TUI; accepting ensures the calling conversation is on root before recovery begins. In --no-projects single-workspace mode, startup-repository worktree behavior remains available; the old /v1/worktrees route aliases are temporary compatibility shims.
File change history#
File tracking is enabled by default. term-llm records file changes made by agent tools and exposes them in the web UI as a per-session Changes panel. Changed sessions expose a Changes button; on narrower screens the panel opens as a drawer so it does not crush the chat column. To turn tracking off, override the default in your config:
file_tracking:
enabled: falseAgent Changes include witnessed write_file, edit_file, and unified_diff transitions plus shell transitions covered by a compatible pre-execution transform or generate output claim and verified by bounded snapshots. Direct and declared provenance are labeled separately. Materialized output (clone, checkout, install, download, extraction, initial copy) and unclaimed detected effects are presented in separate sections and never contribute file or line totals.
For shell calls, affected_paths is inspection scope only. Use output_claims for attribution:
{
"command": "go generate ./...",
"working_dir": "/path/to/repo",
"affected_paths": ["**/*.go", "go.mod", "go.sum"],
"output_claims": [
{"path": "internal/**/*.go", "kind": "transform"},
{"path": "internal/generated/**", "kind": "generate"}
]
}Without claims, detected transitions are observations. Git status and prior session paths remain best-effort discovery sources but cannot attribute changes. Unconfirmed claims and incomplete coverage are shown explicitly.
Last turn and Last 3 turns use a persisted completed-run index, so a latest conversational, failed, canceled, read-only, or observation-only run correctly produces an empty Agent Changes list rather than falling back to an older changing run.
The attributed store may retain before/after content under independent file, session, and global byte caps. Immutable content status distinguishes oversized, binary, unknown-side, and budget cases; totals are marked partial when line-count inputs are unavailable. Observations are metadata-only in a physically separate SQLite sidecar with independent retention, so clone/install/build noise cannot evict attributed blobs. Historical shell rows that predate output claims are labeled unverified and excluded from current totals.
Shell attribution is declaration-verified, not proof of exclusive process causation or intellectual authorship. A concurrent editor, watcher, language server, child process, or another agent can write during the observation window; broad or dishonest claims can misclassify imported content; opaque materialize-then-adapt phases cannot be reconstructed; same-size mtime-preserving changes may be missed when content was not captured; create/delete and modify/restore cycles are net no-ops; and symlinks, hardlinks, renames, remote filesystems, path reuse, and bounded observation can obscure identity or produce false negatives. Direct provenance has a stronger mutation witness but still does not establish legal authorship.
Context compaction#
Long sessions do not keep sending the entire transcript forever. When auto_compact is enabled (the default) and term-llm knows the model’s input limit, the engine tracks an estimated prompt size and compacts before the active context would grow too large.
Compaction is intentionally non-destructive:
- The full original transcript remains in the SQLite session store for scrollback and auditability.
- term-llm asks the model for an internal continuation summary of the old context.
- It appends a compacted active-context block to the same session: a
[Context Compaction]summary message followed by a recent raw tail of exact messages. - The session records
compaction_seqas the sequence number where active model context now begins, plus acompaction_count. - Future model requests load only messages at or after that boundary, plus the configured system/instruction prompt when needed.
The recent raw tail is duplicated on purpose. The original copy remains visible in the transcript where it happened; the appended copy gives the model exact recent wording, tool calls, and tool results after the summary. To avoid confusing UI echo, appended retained-tail rows are marked compaction_tail in storage. TUI and Web renderers suppress those rows, while the active model-context loader still sends them to the provider.
Practical consequences:
- You can still scroll/search the pre-compaction transcript; old history is not deleted.
- The visible compaction marker shows where the active context was reset.
- The hidden retained tail does not count as a visible message and is skipped by search/result continuation IDs, but it remains part of the active LLM context.
- Resuming a compacted session starts from
compaction_seqrather than replaying the whole transcript. - Older sessions compacted before
compaction_tailexisted are handled best-effort by matching the post-summary duplicate tail against the pre-summary transcript.
You can disable automatic compaction globally:
auto_compact: falseWhen disabled, sessions still persist normally, but term-llm will not automatically rewrite the active context to stay under known model limits.
Session titles#
Sessions can have titles set in two ways:
- Manual:
term-llm sessions name 42 "investigate auth flow"sets a custom name that always takes priority. - Auto-generated:
term-llm sessions autotitleuses the configured fast LLM provider to generate short and long titles from the first few messages of each session.
Titles are generated and saved by default. Use --dry-run to preview without saving:
# Generate and save titles for the 50 most recent sessions
term-llm sessions autotitle
# Preview without saving
term-llm sessions autotitle --dry-run
# Regenerate even for sessions that already have titles or custom names
term-llm sessions autotitle --force
# Title sessions older than 10 minutes instead of the default 3
term-llm sessions autotitle --min-age 10mThe command is safe to run repeatedly. It skips sessions that already have a generated title or a custom name (unless --force is used), and does not contact the LLM provider when there is nothing to do. Sessions updated less than 3 minutes ago are skipped by default (--min-age 3m) so the conversation has time to develop before titling.
When displaying sessions (in list, show, export, and browse), titles are chosen in priority order:
- User-set name (from
sessions name) - Generated short/long title (from
sessions autotitle) - Summary (first user message)
Conversation inspector#
While in chat or ask, press Ctrl+O to open the conversation inspector. The inspector is intended as a debug view of the persisted conversation context. For compacted sessions it shows Context compaction boundary blocks; press e to expand all hidden inspector details, including full internal compaction summaries, previous-turns excerpts, and retained raw tail rows that remain in active model context but are hidden from normal chat rendering.
| Key | Action |
|---|---|
j/k |
Scroll up/down |
g/G |
Go to top/bottom |
e |
Expand all hidden inspector details |
q |
Close inspector |
What sessions are for#
Use sessions when you want:
- resumable conversation state
- transcript search
- exported chat history
- per-session naming and tagging
- persisted LLM job transcripts and tool history for background runs
Use Memory when you want durable facts and behavioral insights that survive beyond one specific chat.
Running, input-required, and terminal attention#
Serve web distinguishes three session states. A green pulsing circle means the conversation is running. A solid green circle while the run is active means the agent is blocked on an actionable interaction such as ask_user, shell/file approval, or workspace access. That input-required state is level-triggered: visiting or dismissing the prompt does not clear it; answering, denying, cancelling, or terminating the run does.
When a run completes or fails, or when recovery marks an abandoned run as orphaned, the circle becomes solid until the conversation is visited in a visible browser tab and its transcript has loaded through the run’s final durable revision. A cancelled run receives the terminal marker only when it produced durable output.
Input-required state is deliberately separate from terminal seen state. The response lifecycle stores a fenced, payload-free count and broad interaction kinds; prompt questions, paths, and choices remain in the live response recovery state. If the owning process disappears, lease expiry removes the actionable state and orphan recovery creates the normal terminal marker instead of resurrecting a prompt that can no longer be answered.
The node database is authoritative. For terminal attention it stores monotonic latest_attention_seq and seen_through_seq watermarks; a conversation needs terminal review exactly when the former is greater than the latter. A browser acknowledges the exact sequence it rendered, so a delayed acknowledgement for one completion cannot clear a newer completion. Hidden tabs, link prefetches, notification clicks, and ordinary GET requests do not mark a conversation seen.
Attention is a single-operator feature in this version. All tabs, passkeys, and bearer clients connected to one node share the same watermark. Passkey credential labels are devices, not separate user principals. CLI/TUI ask and chat runs do not create terminal-attention markers because their terminal operator normally observes them; serve-origin web response runs are the marker source.
Node APIs:
GET /v1/attention?kind=unseen|input_required|running&limit=200&cursor=...returns a complete, stable-version, paginated projection for Hub collection. Protocol v2 addsinput_requiredwith counts, broad kinds, and waiting time, but no prompt or transcript content.POST /v1/sessions/{id}/attention/seenaccepts{"store_instance_id":"...","through_seq":N}after the visible transcript revision gate./healthzadvertises theattentioncapability, while/v1/capabilitiesincludes its protocol version and stable store instance ID.
Unsupported and read-only session stores do not advertise durable attention. They continue to serve conversations without pretending that unseen state or crash recovery is durable.