Documentation
¶
Overview ¶
Package session persists agent conversation history across runs.
Sessions enable multi-turn conversations: a user runs a task, the agent responds, and the user continues the conversation with "odek continue", picking up the full message history from the previous turn.
Storage: ~/.odek/sessions/<id>.json. Each file is a full conversation transcript including system messages, user turns, assistant responses, tool calls, and tool results. Sessions are loaded by ID for continuation or by listing metadata for browsing.
The Store is intentionally minimal — it's a JSON file manager, not a database. Session struct fields are all public, so callers can mutate the session directly and call Save(). This makes advanced operations (editing, truncating, merging sessions) trivial at the CLI layer.
Index ¶
- func BuildConversationText(messages []llm.Message) string
- func ValidateSessionID(id string) error
- type IndexEntry
- type SearchResult
- type Session
- type Store
- func (s *Store) Append(id string, newMsgs []llm.Message) error
- func (s *Store) Cleanup(before time.Time) (int, error)
- func (s *Store) Create(messages []llm.Message, model, task string) (*Session, error)
- func (s *Store) Delete(id string) error
- func (s *Store) Dir() string
- func (s *Store) InitVectorIndex() error
- func (s *Store) Latest() (*Session, error)
- func (s *Store) List(limit int) ([]Session, error)
- func (s *Store) Load(id string) (*Session, error)
- func (s *Store) Path(id string) string
- func (s *Store) Save(sess *Session) error
- type VectorIndex
- func (vi *VectorIndex) Add(sessionID string, messages []llm.Message) error
- func (vi *VectorIndex) Init(dir string) error
- func (vi *VectorIndex) Ready() bool
- func (vi *VectorIndex) Remove(sessionID string) error
- func (vi *VectorIndex) Save() error
- func (vi *VectorIndex) Search(query string, k int) ([]SearchResult, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildConversationText ¶ added in v0.58.0
BuildConversationText extracts user and assistant text from messages for embedding. Tool calls and results are excluded — they add noise.
func ValidateSessionID ¶
ValidateSessionID validates that a session ID is safe for filesystem use. Rejects empty strings, path separators, traversal patterns, and dot names.
Types ¶
type IndexEntry ¶
type IndexEntry struct {
ID string `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Turns int `json:"turns"`
}
IndexEntry holds minimal session metadata for the session index. This avoids loading every session file just to list or find the latest.
type SearchResult ¶ added in v0.58.0
type SearchResult struct {
SessionID string `json:"session_id"`
Score float32 `json:"score"` // cosine similarity, higher = more relevant
}
SearchResult holds a single session search result.
type Session ¶
type Session struct {
ID string `json:"id"` // e.g. "20260518-abc123"
CreatedAt time.Time `json:"created_at"` // first message time
UpdatedAt time.Time `json:"updated_at"` // last append time
Model string `json:"model"` // model name used
Turns int `json:"turns"` // number of user turns
Task string `json:"task"` // first user message (label)
Sandbox bool `json:"sandbox"` // was sandboxed — auto-apply on resume
Messages []llm.Message `json:"messages"` // full conversation history
Buffer []string `json:"buffer,omitempty"` // last N turn summaries (memory tier 2)
}
Session represents a single multi-turn conversation with the agent. All fields are exported for direct manipulation at the CLI layer.
func (*Session) GetMessages ¶
GetMessages returns the session's message slice. Nil-safe. Returns an empty (non-nil) slice for a session with no messages.
type Store ¶
type Store struct {
// Vec is the optional semantic search index. When non-nil, every
// Save/Delete/Cleanup call updates the vector index automatically.
// Call InitVectorIndex() to initialize.
Vec *VectorIndex
// contains filtered or unexported fields
}
Store manages session files in a directory on disk. Operations are simple file reads/writes — no locking, no caching.
func NewStore ¶
NewStore creates a session store rooted at ~/.odek/sessions/. The directory is created if it doesn't exist.
func (*Store) Append ¶
Append adds new messages to an existing session, updates timestamps and turn counts, and saves the result atomically. The full read-modify-write is serialized by s.mu to prevent both concurrent-write data loss and symlink-swap TOCTOU attacks.
func (*Store) Cleanup ¶
Cleanup deletes all sessions whose UpdatedAt is before the given time. Returns the count of deleted sessions. Idempotent — nonexistent files are skipped silently. Uses the session index for efficient batch operations. Falls back to scanning individual session files when no index exists (backward compat).
func (*Store) Create ¶
Create persists a new session with the given messages and metadata. It generates an ID, sets timestamps, counts user turns, and saves.
func (*Store) Delete ¶
Delete removes a session file from disk and removes its entry from the session index. Returns nil if the file doesn't exist (idempotent delete).
func (*Store) Dir ¶
Dir returns the session store directory path. Exported for testing and debugging.
func (*Store) InitVectorIndex ¶ added in v0.58.0
InitVectorIndex initializes the semantic search index. Must be called after NewStore, before the first Save. Safe to call multiple times — subsequent calls are no-ops.
func (*Store) Latest ¶
Latest returns the most recently updated session, or nil if no sessions exist. Returns an error when no sessions exist. Uses the session index for O(1) lookups. Falls back to scanning individual session files when no index exists (backward compat).
func (*Store) List ¶
List returns session summaries ordered by UpdatedAt descending (most recent first). limit caps the number returned (0 = all). Only metadata fields are populated — Messages is nil to keep listings lightweight. Uses the session index for O(n) reads (n = session count, but no JSON parsing per session). Falls back to loading each session file when no index exists (backward compat).
func (*Store) Load ¶
Load reads a session from disk by ID. Returns an error if the file doesn't exist or can't be parsed.
func (*Store) Path ¶
Path returns the absolute filesystem path for a session file. Exported for testing and debugging.
type VectorIndex ¶ added in v0.58.0
type VectorIndex struct {
// contains filtered or unexported fields
}
VectorIndex provides semantic session search using go-vector's RandomProjections embedder + brute-force k-NN store.
Lifecycle:
- Init() loads persisted state, or fits+embeds from all sessions.
- On Add(): embed conversation text and insert into store.
- On Search(): embed query, k-NN search, return ranked results.
- On Remove(): delete from store.
- Save() persists both embedder and store to disk atomically.
Thread-safe: all exported methods hold a RWMutex.
func (*VectorIndex) Add ¶ added in v0.58.0
func (vi *VectorIndex) Add(sessionID string, messages []llm.Message) error
Add embeds the conversation text and adds the session to the index. If the session already exists, it is replaced (remove then add).
func (*VectorIndex) Init ¶ added in v0.58.0
func (vi *VectorIndex) Init(dir string) error
Init creates or loads the vector index from the session directory. If persisted state exists, it is loaded directly. Otherwise, it scans all session JSON files, fits the embedder, indexes every session, and persists the result.
func (*VectorIndex) Ready ¶ added in v0.58.0
func (vi *VectorIndex) Ready() bool
Ready returns true if the index has been initialized.
func (*VectorIndex) Remove ¶ added in v0.58.0
func (vi *VectorIndex) Remove(sessionID string) error
Remove deletes a session from the index. Idempotent.
func (*VectorIndex) Save ¶ added in v0.58.0
func (vi *VectorIndex) Save() error
Save persists both the embedder state and the vector store to disk.
func (*VectorIndex) Search ¶ added in v0.58.0
func (vi *VectorIndex) Search(query string, k int) ([]SearchResult, error)
Search embeds the query and returns the k most similar sessions ranked by cosine similarity. Returns nil if the index is not ready or no results found.