session

package
v0.2.8-alpha.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2026 License: MIT Imports: 10 Imported by: 0

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

View Source
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.

View Source
const SessionsSubdir = "sessions"

SessionsSubdir is the directory under APP_HOME that holds every persisted session, organized one level deeper by workdir slug.

View Source
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

func Delete(appHome, workdirSlug, sessionID string) error

Delete removes a single snapshot file. Missing files are not an error (idempotent — second delete is a no-op).

func FirstUserPromptPreview

func FirstUserPromptPreview(msgs []llm.Message) string

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

func Save(appHome string, snap *Snapshot) error

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

func SessionFilePath(appHome, workdirSlug, sessionID string) string

SessionFilePath resolves the snapshot file for one session-id.

func SessionsDir

func SessionsDir(appHome, workdirSlug string) string

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

func List(appHome, workdirSlug string) ([]ListEntry, []string, error)

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 New

func New() *Session

func (*Session) AddUsage

func (s *Session) AddUsage(u llm.Usage)

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) Append

func (s *Session) Append(msg llm.Message)

func (*Session) FullCompact

func (s *Session) FullCompact(messages []llm.Message, briefTokens int)

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 (s *Session) GetFullCompactCount() int

func (*Session) GetMessages

func (s *Session) GetMessages() []llm.Message

func (*Session) IsMicroCompacted

func (s *Session) IsMicroCompacted() bool

func (*Session) LastTurnInputTokens

func (s *Session) LastTurnInputTokens() int

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 (s *Session) MicroCompact(messages []llm.Message)

func (*Session) RecordTurn

func (s *Session) RecordTurn(u llm.Usage)

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

func (s *Session) SetCompactState(micro bool, fullCount int)

SetCompactState rehydrates the micro/full compaction counters. Used by session.FromSnapshot to round-trip persisted state; not for live use.

func (*Session) SetLastTurnInputTokens

func (s *Session) SetLastTurnInputTokens(n int)

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

func (s *Session) SetUsage(u llm.Usage)

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.

func Load

func Load(appHome, workdirSlug, sessionID string) (*Snapshot, error)

Load reads a single snapshot off disk. Returns os.ErrNotExist (wrapped) when the file is missing so callers can distinguish "no such session" from real I/O / parse errors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL