Documentation
¶
Overview ¶
Package store persists sessions and messages to a local SQLite database so the daemon remains the single source of truth across restarts — a client disconnecting, or the daemon itself being restarted, never loses a session's history, including its tool-call trail.
Index ¶
- Constants
- type MemoryEntry
- type Message
- type SearchResult
- type Session
- type Store
- func (s *Store) AppendMessage(sessionID string, msg Message) (Message, error)
- func (s *Store) Close() error
- func (s *Store) CreateSession(id, title string) (Session, error)
- func (s *Store) DeleteMemoryEntry(id int64) error
- func (s *Store) GetSession(id string) (Session, error)
- func (s *Store) ListMessages(sessionID string) ([]Message, error)
- func (s *Store) ListSessions() ([]Session, error)
- func (s *Store) RecentMemory(scopes []string, limit int) ([]MemoryEntry, error)
- func (s *Store) ScopeMemory(scope string) ([]MemoryEntry, error)
- func (s *Store) ScopeMemorySize(scope string) (int, error)
- func (s *Store) SearchMemory(scopes []string, query string, limit int) ([]MemoryEntry, error)
- func (s *Store) SearchMessages(query string, limit int, scope string) ([]SearchResult, error)
- func (s *Store) UpdateMemoryEntry(id int64, content string) error
- func (s *Store) WriteMemoryEntry(scope, content string, pinned bool) (MemoryEntry, error)
Constants ¶
const ( SearchScopeUser = "user" // default: exclude subagent-run sessions SearchScopeAll = "all" // include subagent-run sessions too )
SearchScope controls whether subagent-run sessions (title prefixed "subagent: ", see internal/daemon/agent RunTask) are included in session_search results. Subagent sessions are internal execution noise from delegate_task, not conversations the user had, so they're excluded by default rather than merely ranked lower — a noisy subagent transcript that happens to repeat the user's search term would otherwise crowd out the real conversation it was talking about.
const GlobalScope = "_global"
GlobalScope holds memory that applies across every project, not just the one it was written in — e.g. a standing user preference.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type MemoryEntry ¶
type MemoryEntry struct {
ID int64 `json:"id"`
Scope string `json:"scope"`
Content string `json:"content"`
Pinned bool `json:"pinned"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
MemoryEntry is one compiled fact or note the agent chose to remember — written by the agent itself via a tool call, not captured automatically from raw conversation logs. Scope is either a workspace path (memory specific to one project) or GlobalScope (memory that applies everywhere).
type Message ¶
type Message struct {
ID int64 `json:"id"`
SessionID string `json:"session_id"`
Role string `json:"role"`
Content string `json:"content"`
Provider string `json:"provider,omitempty"`
ToolCalls []openai.ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
Images []string `json:"images,omitempty"`
CreatedAt int64 `json:"created_at"`
}
Message is one turn within a session — a user message, an assistant reply (Provider set to whoever served it, ToolCalls set if it's requesting tool invocations instead of/before answering), or a tool result (Role "tool", ToolCallID + Name identifying which call it answers). Name doubles as the marker for a compaction summary message (Name == CompactionMarker) — see internal/daemon/compaction.
type SearchResult ¶
type SearchResult struct {
Message Message `json:"message"`
SessionTitle string `json:"session_title"`
IsSubagent bool `json:"is_subagent"`
Window []Message `json:"window"`
// Score is the raw FTS5 bm25() value for this match (negative,
// more-negative-is-better) — exported mainly so ranking behavior is
// directly testable/inspectable, not something callers need to
// interpret themselves.
Score float64 `json:"-"`
}
SearchResult is one match from SearchMessages: the message that matched the query, which session it's from, and a chronological window of surrounding messages from that same session for context.
type Session ¶
type Session struct {
ID string `json:"id"`
Title string `json:"title"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Session is a durable conversation thread owned by the daemon.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store wraps the SQLite connection and the daemon's persistence methods.
func Open ¶
Open opens (creating if needed) the SQLite database at path and applies the schema. The connection pool is capped at 1 writer to avoid SQLite's "database is locked" errors under concurrent writes — fine for a local daemon's request volume.
func (*Store) AppendMessage ¶
AppendMessage stores one message and touches the parent session's updated_at timestamp. Callers set only the fields relevant to the message's role; ID and CreatedAt are assigned here.
func (*Store) CreateSession ¶
CreateSession inserts a new, empty session.
func (*Store) DeleteMemoryEntry ¶
DeleteMemoryEntry removes one entry by ID.
func (*Store) GetSession ¶
GetSession returns one session and returns sql.ErrNoRows if it doesn't exist.
func (*Store) ListMessages ¶
ListMessages returns every message in a session, oldest first, including the full tool-call/tool-result trail and any compaction summaries.
func (*Store) ListSessions ¶
ListSessions returns all sessions, most recently updated first.
func (*Store) RecentMemory ¶
func (s *Store) RecentMemory(scopes []string, limit int) ([]MemoryEntry, error)
RecentMemory returns pinned entries first, then the most recently updated ones, across the given scopes (typically the current workspace plus GlobalScope) — this is what gets injected automatically at the start of every turn, so it's kept small and bounded by limit; anything older or from another scope is reachable via SearchMemory instead.
func (*Store) ScopeMemory ¶
func (s *Store) ScopeMemory(scope string) ([]MemoryEntry, error)
ScopeMemory returns every entry in one scope, pinned first then newest, with no limit — used to enforce the per-scope size cap and to show the agent everything it has to work with when consolidating.
func (*Store) ScopeMemorySize ¶
ScopeMemorySize is the total character count of a scope's memory — what the cap is enforced against.
func (*Store) SearchMemory ¶
SearchMemory does a full-text search (SQLite FTS5) over memory content within the given scopes — this is the agent's memory_search tool.
func (*Store) SearchMessages ¶
SearchMessages does a full-text search (SQLite FTS5, BM25 ranking) over real conversation history — user and assistant messages actually exchanged, never tool output or system/compaction framing (see searchSchema). Ranking is BM25 first; matches whose scores are within relevanceTieTolerance of each other are then reordered by recency (newest first), since "how relevant" and "how long ago" both matter and a near-tie on relevance shouldn't be decided by incidental row order. scope controls whether subagent-run sessions are included (SearchScopeUser excludes them, SearchScopeAll includes them); anything else is treated as SearchScopeUser.
func (*Store) UpdateMemoryEntry ¶
UpdateMemoryEntry replaces one entry's content in place, keeping its ID and created_at — the "replace" half of the add/replace/remove trio the agent needs to consolidate memory when it hits the size cap.
func (*Store) WriteMemoryEntry ¶
func (s *Store) WriteMemoryEntry(scope, content string, pinned bool) (MemoryEntry, error)
WriteMemoryEntry stores one compiled memory entry.