Documentation
¶
Overview ¶
Package memory implements durable agent memory: project-scoped and org-scoped entries with a strict Markdown format. Storage is SQLite by default, with an in-memory backend for tests and ephemeral sessions.
One entry is one row; the content column holds the rendered Markdown. The format is the contract: clean, tidy, concrete entries with a title, a short summary, what worked, what did not work, and why. Agents write entries through the memory_save and memory_search tools; humans can read the same Markdown directly.
Index ¶
Constants ¶
const ( BackendMemory = "memory" BackendSQLite = "sqlite" )
Backend names.
const ( DefaultMaxEntries = 500 DefaultMaxSearchResults = 8 )
Store defaults.
const CoreTierCap = 24
CoreTierCap bounds the number of "core" entries per (scope, org) bucket (decision 1: 24 rows, sized to keep the auto-injected block small).
const (
DefaultMaxEntryBytes = 8192
)
Limits defaults. The rendered template stays small by design: a memory is a digest of a learning, not a document.
Variables ¶
var ErrCoreTierFull = fmt.Errorf("memory: core tier is full (max %d entries); merge or archive an existing core entry first", CoreTierCap)
ErrCoreTierFull is returned by PromoteToCore when the target (scope, org) bucket already holds CoreTierCap core entries.
var ErrDumpUnsupported = errors.New("memory: dump is only supported for the sqlite backend")
ErrDumpUnsupported is returned by Dump for a non-sqlite-backed Store.
var ErrEntryNotFound = errors.New("memory: entry not found")
ErrEntryNotFound is returned by PromoteToCore when no entry with the given id exists in any store this Store has access to.
Functions ¶
func Dump ¶
Dump writes store's rows as deterministic JSONL to w (D5). Only the sqlite backend supports it - dump exists to make a committed .mivia/memory.db reviewable, and the in-memory backend has nothing committed to review. store must be a value returned by Open with Backend: BackendSQLite; any other Store returns ErrDumpUnsupported.
func NormalizeOrgID ¶
NormalizeOrgID validates and normalizes a user-supplied org identity.
The identity is a plain string column in the org store; it never becomes a filesystem path. It may contain letters, digits, dots, hyphens, underscores and slashes (host/org), is case-insensitive, and is stored lowercase.
Types ¶
type Config ¶
type Config struct {
// Backend is "memory" or "sqlite". Empty defaults to "sqlite".
Backend string
// ProjectPath is the project memory database file. Required for sqlite.
// A repo owner may point it at a tracked path and commit memories with
// the repository.
ProjectPath string
// OrgPath is the user-level org memory database file. Optional; without
// it org scope is unavailable.
OrgPath string
// OrgID is the user-owned org identity. Empty means org scope is
// unavailable. It must come from the user config, never from a workspace
// file: a workspace config is repo-controlled and must not name the org
// store its agents write into.
OrgID string
// MaxEntryBytes caps one rendered entry. Default 8192.
MaxEntryBytes int
// MaxEntries caps the row count per store file. Default 500.
MaxEntries int
// MaxSearchResults caps search results. Default 8.
MaxSearchResults int
// BlockPatterns are regexes; matching content is refused at save.
BlockPatterns []string
// ReadOnly opens the store without writing the database file: open skips
// the journal_mode(WAL) switch, the schema CREATE, and the FTS5 rebuild
// backfill; Close skips the WAL checkpoint; Save is refused. Search
// works via the LIKE fallback with identical results; the file must
// already exist with the schema. Zero value false = read-write.
ReadOnly bool
// HardenTempStore marks ProjectPath as an ad-hoc, OS-temp-dir-backed store
// (config.TempStorePath) rather than an operator-managed project path.
// When true, openSQLiteStore chmods the project database file to 0600 and
// its parent directory to 0700, failing closed on error, the same way the
// org store is always hardened: an ad-hoc temp-dir store has no project
// directory whose permissions an operator manages, so mivia must protect
// it itself.
HardenTempStore bool
}
Config selects the backend and bounds.
type Entry ¶
type Entry struct {
Title string
Scope Scope
Verdict Verdict
Tags []string
Created string // YYYY-MM-DD; empty means "today" at save time
Summary string
Good string
Bad string
Why string
References []string
}
Entry is one memory. Render produces the stored Markdown; Parse reads it back tolerantly.
func Parse ¶
Parse reads a stored or hand-edited memory document back into an Entry. It is tolerant: missing sections become empty fields, unknown header keys and extra lines are ignored. It never panics.
func (Entry) Clamp ¶
Clamp returns a copy of e with every free-text field truncated to its rune limit, so a save never fails on an over-length field. It is the lenient counterpart to Validate: agents routinely over-shoot the summary (400), why (1000), title (120), and body (2000) limits, and a hard rejection just makes them retry the same long text. Clamp keeps the leading content (the most informative part) and drops the tail. It never changes scope, verdict, tags, or references, and it never makes a field empty that was non-empty.
type Limits ¶
type Limits struct {
// MaxEntryBytes caps the rendered entry size. Default 8192.
MaxEntryBytes int
// BlockPatterns are regexes; content matching any of them is refused.
// Configuration-only, like the privacy redaction patterns: nothing is
// compiled into the binary.
BlockPatterns []string
}
Limits bounds one entry. Zero values use the defaults.
type Query ¶
type Query struct {
Text string
Scope Scope // ScopeProject, ScopeOrg, or ScopeAll
MaxResults int // 0 uses the store default
}
Query is a search request.
type Result ¶
type Result struct {
ID string
Scope Scope
Org string
Title string
Verdict Verdict
Tags []string
Created string
Snippet string
}
Result is one search hit.
type Scope ¶
type Scope string
Scope is the visibility scope of a memory.
const ( // ScopeProject scopes a memory to one workspace. The project store is a // per-workspace database, so it never leaks into other projects. ScopeProject Scope = "project" // ScopeOrg scopes a memory to the configured org. The org store is a // user-level database shared by every project of that org on this machine. ScopeOrg Scope = "org" // ScopeAll selects both scopes in a search. ScopeAll Scope = "all" )
type Store ¶
type Store interface {
// Save validates and stores one entry. An identical re-save is
// idempotent: it returns the existing result and stores no duplicate.
// Save never sets tier: every entry it writes lands as "archive" (the
// schema default). Promotion to "core" is a separate, operator-facing
// action (PromoteToCore) - not reachable from Save, by design (D1a).
Save(ctx context.Context, e Entry) (Result, error)
// Search returns up to the configured limit ranked matches. ScopeAll
// merges project and org results. Org scope without a configured org
// identity returns an empty result, never an error.
Search(ctx context.Context, q Query) ([]Result, error)
// Count returns the number of stored entries for one scope.
Count(ctx context.Context, scope Scope) (int, error)
// PromoteToCore marks one existing entry (by id) as tier "core", subject
// to CoreTierCap per (scope, org). Promoting an already-core entry is a
// no-op. Returns ErrEntryNotFound if no entry with that id exists, or
// ErrCoreTierFull if the bucket is already at CoreTierCap.
PromoteToCore(ctx context.Context, id string) error
// CoreEntries returns up to CoreTierCap "core"-tier entries for scope,
// ordered by created DESC, title ASC, id ASC (the same tie-break Search
// uses). Not a search - no query text, just the current core set. Used
// to build the auto-injected system-prompt block (D1).
CoreEntries(ctx context.Context, scope Scope) ([]Result, error)
// Delete removes one entry (by id) from whichever store this Store has
// access to (project, then org). Returns ErrEntryNotFound if no entry
// with that id exists in any store. Refused on a read-only store.
Delete(ctx context.Context, id string) error
Close() error
}
Store is the durable memory backend. Implementations must be safe for concurrent use: subagents share one store per session.