Documentation
¶
Overview ¶
Session storage on disk. One JSON file per session at
<APP_HOME>/sessions/<workdir-slug>/<session-id>.json
The store is intentionally small: Save / Load / List / Delete on straightforward filesystem primitives. List sorts by file mtime descending so the most recently active session lands at the top of the resume picker.
Corrupt files (truncated writes from a crashed evva, JSON drift after a schema bump) are skipped with a warning during List — one broken file must never disable the picker for the whole directory.
Index ¶
- Constants
- func Delete(appHome, workdirSlug, sessionID string) error
- func FirstUserPromptPreview(msgs []llm.Message) string
- func Save(appHome string, snap *Snapshot) error
- func SessionFilePath(appHome, workdirSlug, sessionID string) string
- func SessionsDir(appHome, workdirSlug string) string
- type ListEntry
- type Session
- func (s *Session) AddUsage(u llm.Usage)
- func (s *Session) Append(msg llm.Message)
- func (s *Session) FullCompact(messages []llm.Message, briefTokens int)
- func (s *Session) GetFullCompactCount() int
- func (s *Session) GetMessages() []llm.Message
- func (s *Session) IsMicroCompacted() bool
- func (s *Session) LastTurnInputTokens() int
- func (s *Session) MicroCompact(messages []llm.Message)
- func (s *Session) RecordTurn(u llm.Usage)
- func (s *Session) SetCompactState(micro bool, fullCount int)
- func (s *Session) SetLastTurnInputTokens(n int)
- func (s *Session) SetUsage(u llm.Usage)
- func (s *Session) ToSnapshot() SessionState
- type SessionState
- type Snapshot
Constants ¶
const PreviewMaxBytes = 200
PreviewMaxBytes caps the persisted first-user-prompt preview. The resume overlay renders only 150 chars, but we store 200 so trailing truncation never produces a half-word in the visible window.
const SessionsSubdir = "sessions"
SessionsSubdir is the directory under APP_HOME that holds every persisted session, organized one level deeper by workdir slug.
const SnapshotVersion = 1
SnapshotVersion is the on-disk schema version. Bump on breaking changes to the JSON layout; older files become unreadable, which the store's List path tolerates by skipping with a warning rather than aborting the picker.
Variables ¶
This section is empty.
Functions ¶
func Delete ¶
Delete removes a single snapshot file. Missing files are not an error (idempotent — second delete is a no-op).
func FirstUserPromptPreview ¶
FirstUserPromptPreview returns up to PreviewMaxBytes from the first RoleUser message's content. Whitespace at both ends is trimmed; CR/LF inside the body are flattened to single spaces so the preview always fits on one line in the resume picker.
Empty result means the session has no user messages yet (rare — typically only when the file is saved between session start and the first user prompt being routed).
func Save ¶
Save serializes snap to <SessionsDir>/<SessionID>.json atomically (temp + rename in the same directory). Creates the parent directory chain if missing. Returns an error only on real I/O failure.
func SessionFilePath ¶
SessionFilePath resolves the snapshot file for one session-id.
func SessionsDir ¶
SessionsDir returns the absolute path of the per-workdir directory holding this workdir's session files. Empty inputs yield "".
Types ¶
type ListEntry ¶
type ListEntry struct {
Snapshot *Snapshot
MTime int64 // unix nano of file mtime; List sorts by this desc
}
ListEntry is one row in the resume picker: the snapshot plus the file mtime List uses to sort. The picker only needs a small subset of the snapshot fields — exposed separately so the picker can stay shallow.
func List ¶
List enumerates every session under <SessionsDir>/<workdir-slug>/, sorted by mtime descending (most recently saved first). Files that fail to parse are skipped — the corresponding error appears in the returned warnings slice so the caller can surface them.
Returns an empty slice (not an error) when the directory does not exist yet — that's the normal "no prior sessions" state.
type Session ¶
type Session struct {
// LLM context payload
Messages []llm.Message
// Usage is the running sum of every turn's reported token usage in this
// session. Compaction is expected to reset Messages but leave Usage as
// the running tab of what the user has already paid for.
Usage llm.Usage
// contains filtered or unexported fields
}
Session holds the live conversation history for a single agent run. The agent appends every message (user, assistant, tool result) here so the LLM always receives the full context on the next turn. tools, agent, llm, tui will use it.
func FromSnapshot ¶
func FromSnapshot(state SessionState) *Session
FromSnapshot returns a fresh *Session rehydrated from the persisted state. The slice is copied so callers can mutate the snapshot's Messages without aliasing into the live session (and vice versa).
func (*Session) AddUsage ¶
AddUsage folds one usage entry into the cumulative session total only. Use this for non-turn usage events whose input-token count does NOT represent the current prompt size — e.g. the LLM call inside full compaction, where InputTokens reflects the size of the conversation we just summarized, not the size of the post-compaction prompt.
func (*Session) FullCompact ¶
FullCompact replaces Messages with the summarization brief and resets the in-flight compaction state. lastTurnInputTokens is set to briefTokens — the brief is now the entirety of the prompt the next turn will send, so callers (the TUI's context bar in particular) can read accurate "current prompt size" without waiting for the next thinking call to land.
Cumulative Usage is also reset: in=briefTokens, out=0. The HUD reads as "fresh context after compact" so the user can visually confirm the bar drop (e.g. 80% → 40%) without the cumulative tail dragging the numbers up. The compaction caller is responsible for logging the pre-reset totals before invoking this — they're gone after.
func (*Session) GetFullCompactCount ¶
func (*Session) GetMessages ¶
func (*Session) IsMicroCompacted ¶
func (*Session) LastTurnInputTokens ¶
LastTurnInputTokens returns the InputTokens from the most recent agent turn (zero before the first turn completes, or right after a full-compact reset). This is the canonical "how full is the prompt right now" signal — preferred over Usage.Total for ratio checks.
func (*Session) MicroCompact ¶
func (*Session) RecordTurn ¶
RecordTurn marks u as the most recent agent-turn usage: it folds u into the cumulative total AND updates lastTurnInputTokens so compaction can measure live prompt pressure. The agent loop calls this after every Complete / Stream that drove a real iteration.
func (*Session) SetCompactState ¶
SetCompactState rehydrates the micro/full compaction counters. Used by session.FromSnapshot to round-trip persisted state; not for live use.
func (*Session) SetLastTurnInputTokens ¶
SetLastTurnInputTokens overrides the cached turn-input figure. Used by the resume path to rehydrate a snapshot's previously-recorded value; production code should prefer RecordTurn so the cumulative Usage is kept in sync.
func (*Session) SetUsage ¶
SetUsage overrides the cumulative usage total. Same caveat as SetLastTurnInputTokens: only the resume path should use it. Production turns flow through AddUsage / RecordTurn.
func (*Session) ToSnapshot ¶
func (s *Session) ToSnapshot() SessionState
ToSnapshot copies the live session into a JSON-friendly DTO. The caller fills in the envelope fields (SessionID, Workdir, Profile, etc.) — Session has no view of those.
type SessionState ¶
type SessionState struct {
Messages []llm.Message `json:"messages"`
Usage llm.Usage `json:"usage"`
LastTurnInputTokens int `json:"last_turn_input_tokens"`
MicroCompacted bool `json:"micro_compacted"`
FullCompactCount int `json:"full_compact_count"`
}
SessionState carries the live conversation fields persisted alongside the snapshot envelope. The unexported Session fields are surfaced via the SetCompactState / SetLastTurnInputTokens accessors on rehydrate.
type Snapshot ¶
type Snapshot struct {
Version int `json:"version"`
SessionID string `json:"session_id"`
Workdir string `json:"workdir"`
WorkdirSlug string `json:"workdir_slug"`
Profile string `json:"profile"`
Provider string `json:"provider"`
Model string `json:"model"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
FirstUserPrompt string `json:"first_user_prompt"`
Session SessionState `json:"session"`
}
Snapshot is the JSON shape of one persisted session.
SessionID identifies the file on disk and equals the live agent's UUID at the moment the session was first saved; the agent overwrites its own ID with this value on resume so subsequent writes target the same file.
Profile + Provider + Model capture the agent setup at save time so the resume code can rebuild the right persona and LLM client even if the user's defaults have changed since.