Documentation
¶
Overview ¶
Package memory — semantic + episodic memory orchestration (Go side). Delegates embeddings + vector search to the Python bridge when available. Wired via run.go boot sequence; Postgres-backed.
Package memory — segment-based memory (§8.2 Phase 5 #3, inspired by MemAgent / Yu 2025).
The problem: in a massive codebase, dumping the whole context into a single cold blob is wasteful — most queries only need a slice (the auth module, or just files matching `*_test.go`, or the segment we used last turn). MemAgent's idea: split memory into labeled SEGMENTS with per-segment metadata, then load segments on-demand based on query relevance + recency.
This package provides:
- Segment: a labeled chunk of context (file globs, a directory, or an arbitrary user-defined group) with metadata for retrieval scoring.
- SegmentStore: durable per-segment state under ~/.overkill/segments/<id>.json. Touch updates last-access for recency scoring; LoadFiles materializes the contents.
- Retrieval: top-K selection by a composite score (recency × match × inverse size). Composes with the existing vector path in internal/journal — the journal owns observation similarity, this package owns codebase segmentation.
What this is NOT: a replacement for the agent's context window. Segments are a *retrieval layer* on top of the filesystem. The agent calls segment tools, picks one, and asks Read on the files the segment names. No magic context compression here.
Package memory — bridge-backed semantic recall (master plan §6.1).
The Orchestrator can be augmented with an EmbeddingClient (typically a thin wrapper around bridge.Client) so that Remember computes an embedding and stores a vector, and SemanticRecall searches by query embedding with optional rerank. When no client is wired, the Orchestrator degrades to substring recall via the Postgres store — semantic features are best-effort.
Index ¶
- Variables
- type BridgeAdapter
- func (a *BridgeAdapter) DeleteVector(ctx context.Context, id string) (bool, error)
- func (a *BridgeAdapter) Embed(ctx context.Context, text, model string) ([]float32, int32, error)
- func (a *BridgeAdapter) Rerank(ctx context.Context, query string, documents []string, topN int) ([]RerankHit, error)
- func (a *BridgeAdapter) SearchVectors(ctx context.Context, query []float32, topK int, threshold float64) ([]VectorHit, error)
- func (a *BridgeAdapter) StoreVector(ctx context.Context, id string, embedding []float32, content string, ...) (string, error)
- type EmbeddingClient
- type ListOptions
- type Memory
- type MemoryType
- type Orchestrator
- func (o *Orchestrator) AttachEmbeddings(client EmbeddingClient, cfg SemanticConfig)
- func (o *Orchestrator) ExtractMemories(ctx context.Context, content string, sessionID string) ([]Memory, error)
- func (o *Orchestrator) ForgetSemantic(ctx context.Context, id string) error
- func (o *Orchestrator) HasEmbeddings() bool
- func (o *Orchestrator) Recall(ctx context.Context, query string, limit int) (*SearchResult, error)
- func (o *Orchestrator) Remember(ctx context.Context, content string, memoryType MemoryType, tags []string, ...) error
- func (o *Orchestrator) RememberSemantic(ctx context.Context, content string, memType MemoryType, tags []string, ...) (*Memory, error)
- func (o *Orchestrator) SemanticRecall(ctx context.Context, query string, topK int) (*SearchResult, error)
- func (o *Orchestrator) Summarize(ctx context.Context, memories []Memory) (string, error)
- type PostgresStore
- func (s *PostgresStore) Close() error
- func (s *PostgresStore) Delete(ctx context.Context, id string) error
- func (s *PostgresStore) Get(ctx context.Context, id string) (*Memory, error)
- func (s *PostgresStore) List(ctx context.Context, opts ListOptions) ([]Memory, error)
- func (s *PostgresStore) Prune(ctx context.Context, olderThan time.Time) error
- func (s *PostgresStore) Retrieve(ctx context.Context, query Query) (*SearchResult, error)
- func (s *PostgresStore) SetRetention(d time.Duration)
- func (s *PostgresStore) Store(ctx context.Context, memory *Memory) error
- type Query
- type RankOptions
- type RerankHit
- type SearchResult
- type Segment
- type SegmentHit
- type SegmentStore
- func (s *SegmentStore) All() ([]*Segment, error)
- func (s *SegmentStore) Create(seg *Segment) (*Segment, error)
- func (s *SegmentStore) Delete(id string) error
- func (s *SegmentStore) Get(id string) (*Segment, error)
- func (s *SegmentStore) LoadFiles(id string) ([]string, error)
- func (s *SegmentStore) Rank(query string, topK int, opts RankOptions) ([]SegmentHit, error)
- func (s *SegmentStore) Search(query string) ([]*Segment, error)
- func (s *SegmentStore) Touch(id string) error
- type SemanticConfig
- type Store
- type VectorHit
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("memory: not found")
Functions ¶
This section is empty.
Types ¶
type BridgeAdapter ¶
type BridgeAdapter struct {
// contains filtered or unexported fields
}
BridgeAdapter wraps a *bridge.Client so it satisfies EmbeddingClient.
memory does not import bridge directly to keep the dependency one-way and to allow tests to swap in a fake. Callers that already have a bridge client wrap it via NewBridgeAdapter at the wiring layer.
func NewBridgeAdapter ¶
func NewBridgeAdapter(c *bridge.Client) *BridgeAdapter
NewBridgeAdapter wraps c. Returns nil when c is nil.
func (*BridgeAdapter) DeleteVector ¶
func (*BridgeAdapter) SearchVectors ¶
type EmbeddingClient ¶
type EmbeddingClient interface {
Embed(ctx context.Context, text, model string) ([]float32, int32, error)
StoreVector(ctx context.Context, id string, embedding []float32, content string, metadata map[string]string) (string, error)
SearchVectors(ctx context.Context, query []float32, topK int, threshold float64) ([]VectorHit, error)
Rerank(ctx context.Context, query string, documents []string, topN int) ([]RerankHit, error)
DeleteVector(ctx context.Context, id string) (bool, error)
}
EmbeddingClient is the minimal embedding/vector/rerank surface the Orchestrator needs. bridge.Client satisfies this. Defined locally to keep the dependency direction one-way (memory doesn't import bridge).
type ListOptions ¶
type ListOptions struct {
Type MemoryType
SessionID string
Limit int
Offset int
}
type MemoryType ¶
type MemoryType string
const ( MemoryEpisodic MemoryType = "episodic" MemorySemantic MemoryType = "semantic" MemoryProcedural MemoryType = "procedural" )
type Orchestrator ¶
type Orchestrator struct {
// contains filtered or unexported fields
}
func NewOrchestrator ¶
func NewOrchestrator(store Store, provider providers.Provider, model string) *Orchestrator
func (*Orchestrator) AttachEmbeddings ¶
func (o *Orchestrator) AttachEmbeddings(client EmbeddingClient, cfg SemanticConfig)
AttachEmbeddings wires an EmbeddingClient into the Orchestrator.
func (*Orchestrator) ExtractMemories ¶
func (*Orchestrator) ForgetSemantic ¶
func (o *Orchestrator) ForgetSemantic(ctx context.Context, id string) error
ForgetSemantic removes the memory from the local store and from the vector store (when wired). Vector deletion errors are swallowed (best-effort).
func (*Orchestrator) HasEmbeddings ¶
func (o *Orchestrator) HasEmbeddings() bool
HasEmbeddings reports whether semantic features are available.
func (*Orchestrator) Recall ¶
func (o *Orchestrator) Recall(ctx context.Context, query string, limit int) (*SearchResult, error)
func (*Orchestrator) Remember ¶
func (o *Orchestrator) Remember(ctx context.Context, content string, memoryType MemoryType, tags []string, sessionID string) error
func (*Orchestrator) RememberSemantic ¶
func (o *Orchestrator) RememberSemantic(ctx context.Context, content string, memType MemoryType, tags []string, sessionID string) (*Memory, error)
RememberSemantic stores a memory in the local store AND computes/stores its embedding via the bridge. Falls back to plain Remember if no client is wired.
func (*Orchestrator) SemanticRecall ¶
func (o *Orchestrator) SemanticRecall(ctx context.Context, query string, topK int) (*SearchResult, error)
SemanticRecall searches by embedding similarity, optionally reranks, and returns the corresponding Memory rows from the local store.
When no client is wired this falls back to substring recall via Recall.
type PostgresStore ¶
type PostgresStore struct {
// contains filtered or unexported fields
}
PostgresStore implements Store backed by PostgreSQL.
func NewPostgresStore ¶
func NewPostgresStore(db *sql.DB) (*PostgresStore, error)
NewPostgresStore returns a Store backed by the given *sql.DB. Starts a background goroutine that prunes memories older than 90 days every hour. Override with SetRetention().
func (*PostgresStore) Close ¶
func (s *PostgresStore) Close() error
Close cancels the background prune goroutine. The caller owns the DB lifecycle.
func (*PostgresStore) Delete ¶
func (s *PostgresStore) Delete(ctx context.Context, id string) error
Delete removes a memory by ID.
func (*PostgresStore) List ¶
func (s *PostgresStore) List(ctx context.Context, opts ListOptions) ([]Memory, error)
List returns memories with optional filtering.
func (*PostgresStore) Retrieve ¶
func (s *PostgresStore) Retrieve(ctx context.Context, query Query) (*SearchResult, error)
Retrieve searches memories. Pushes type filtering and LIMIT into SQL so we don't pull the entire table into Go. Content scoring still happens in Go on the already-limited result set.
func (*PostgresStore) SetRetention ¶
func (s *PostgresStore) SetRetention(d time.Duration)
SetRetention configures automatic pruning. Memories older than the configured duration are deleted by the background goroutine. Set to 0 to disable automatic pruning.
func (*PostgresStore) Store ¶
func (s *PostgresStore) Store(ctx context.Context, memory *Memory) error
Store persists a Memory. Deduplicates by content hash (SHA-256): when identical content is stored again, the existing row's timestamp is bumped to the newer value instead of creating a duplicate row. The Store interface is unchanged — callers still pass a Memory and receive an error.
type RankOptions ¶
type RankOptions struct {
// RecencyHalfLife is the time after which the recency score
// drops to 0.5. Default: 24h.
RecencyHalfLife time.Duration
// MatchWeight scales the name/desc/tag match contribution.
MatchWeight float64
// RecencyWeight scales the recency contribution.
RecencyWeight float64
// SizeWeight scales the inverse-size contribution (smaller =
// faster to load).
SizeWeight float64
}
RankOptions tunes scoring weights. Zero-value gives sensible defaults: weight recency 1.0, name match 2.0, inverse size 0.5.
type SearchResult ¶
type Segment ¶
type Segment struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
// Globs is the file-pattern list that defines this segment.
// Relative to the segment's RootDir. Standard glob syntax via
// filepath.Match plus a `**` recursive wildcard.
Globs []string `json:"globs"`
// RootDir is the absolute path the globs resolve against.
// Empty → store's default root.
RootDir string `json:"root_dir,omitempty"`
// Tags is operator-supplied labels for filtering.
Tags []string `json:"tags,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// LastAccessed is bumped by Touch. Recency component of the
// retrieval score.
LastAccessed time.Time `json:"last_accessed,omitempty"`
// AccessCount tracks how often the segment has been loaded —
// stable signal that this segment is "hot".
AccessCount int `json:"access_count"`
// CachedFileCount + CachedTotalBytes are snapshot stats from
// the last LoadFiles call. Used by the inverse-size component
// of the retrieval score and surfaced to the user.
CachedFileCount int `json:"cached_file_count,omitempty"`
CachedTotalBytes int64 `json:"cached_total_bytes,omitempty"`
}
Segment is one labeled slice of the codebase.
type SegmentHit ¶
SegmentHit is a ranked retrieval result.
type SegmentStore ¶
type SegmentStore struct {
// contains filtered or unexported fields
}
SegmentStore wraps the on-disk segment directory.
func NewSegmentStore ¶
func NewSegmentStore(dir, defaultRoot string) *SegmentStore
NewSegmentStore wires the store. defaultRoot is the base path segments use when they don't set their own RootDir — typically the user's project root (passed in by the wiring layer).
func (*SegmentStore) All ¶
func (s *SegmentStore) All() ([]*Segment, error)
All returns every persisted segment.
func (*SegmentStore) Create ¶
func (s *SegmentStore) Create(seg *Segment) (*Segment, error)
Create persists a new Segment. ID is auto-assigned; CreatedAt / UpdatedAt are stamped. Empty Globs is rejected (a segment with no files is useless).
func (*SegmentStore) Delete ¶
func (s *SegmentStore) Delete(id string) error
Delete removes a segment. Idempotent.
func (*SegmentStore) Get ¶
func (s *SegmentStore) Get(id string) (*Segment, error)
Get returns a segment by ID, or (nil, nil) when not found.
func (*SegmentStore) LoadFiles ¶
func (s *SegmentStore) LoadFiles(id string) ([]string, error)
LoadFiles resolves the segment's Globs to concrete files and returns the resulting paths. Updates CachedFileCount + CachedTotalBytes so future Rank calls have accurate size info.
Returns paths absolute, sorted, deduplicated. Recursive `**` glob is supported.
func (*SegmentStore) Rank ¶
func (s *SegmentStore) Rank(query string, topK int, opts RankOptions) ([]SegmentHit, error)
Rank returns the top-K segments for the query, scored by a composite (recency × match × inverse-size). Empty query relies on recency + size alone (closest to "what should I load by default?").
func (*SegmentStore) Search ¶
func (s *SegmentStore) Search(query string) ([]*Segment, error)
Search returns segments whose Name, Description, or Tags substring-match the query (case-insensitive). Empty query → all.
func (*SegmentStore) Touch ¶
func (s *SegmentStore) Touch(id string) error
Touch updates LastAccessed and bumps AccessCount. Called by retrieval consumers so recency reflects actual use, not just existence.
type SemanticConfig ¶
type SemanticConfig struct {
// EmbedModel is the model name passed to the embedding service. When
// empty the orchestrator skips embedding entirely.
EmbedModel string
// SearchThreshold is the cosine-similarity floor for SearchVectors.
SearchThreshold float64
// RerankTopN, when > 0, runs results through Rerank and trims to N.
RerankTopN int
}
SemanticConfig controls embedding-backed behaviour.