Help us shape the future of Go! Take the 2026 Go Developer Survey

memory

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package memory is tinyagent's cross-run project memory store: short, redacted notes that a run saves about a project and later runs load into their system prompt. A store is either a folder of markdown files (one <id>.md per entry, the default ./memory) or one SQLite database (see backend.go); the legacy per-project JSON file is still read so it can be imported. Entries carry provenance and may be held as pending until a person approves them; pending entries are never rendered into a prompt.

The package has no model, network or config dependency: callers decide when memory is enabled and pass the store path, policy and limits in.

Index

Constants

View Source
const (
	BackendMarkdown = "markdown"
	BackendSQLite   = "sqlite"
)

Store backends. A store is named by one path, and its kind follows from that path: a .sqlite, .sqlite3 or .db file is a SQLite database, a .json file is a legacy JSON store (read for import, still writable), and anything else is a markdown folder holding one <id>.md file per entry.

View Source
const (
	DefaultMarkdownPath = "memory"
	DefaultSQLitePath   = "memory/memory.sqlite"
)

Default store locations, relative to the working directory.

View Source
const (
	StatusActive  = "active"
	StatusPending = "pending"
)

Entry statuses.

View Source
const (
	// ApprovalPending saves every new entry as pending.
	ApprovalPending = "pending"
	// ApprovalAuto saves new entries as active unless the run had untrusted
	// content in context.
	ApprovalAuto = "auto"
)

Approval policies for new entries.

View Source
const (
	DefaultMaxWritesPerRun = 5
	DefaultMaxEntries      = 50
	DefaultMaxBytes        = 8 * 1024
	// MaxTextLen is the longest entry text, in characters.
	MaxTextLen = 500
)

Default limits.

Variables

View Source
var ErrNotFound = errors.New("no memory entry with that id")

ErrNotFound is returned for an id that isn't in the store.

Functions

func Approve

func Approve(path, id string, all bool) (int, error)

Approve activates the pending entry id, or every pending entry when all is true, and returns how many were approved. Approving a pending replacement removes the active entry it replaces.

func BackendOf

func BackendOf(path string) string

BackendOf returns the backend a store path selects.

func DefaultDir

func DefaultDir() (string, error)

DefaultDir returns the directory that holds project stores: $XDG_STATE_HOME/tinyagent/memory when XDG_STATE_HOME is set, else ~/.local/state/tinyagent/memory on Linux and other Unix systems, and tinyagent/memory under os.UserConfigDir on macOS and Windows.

func Exists

func Exists(path string) bool

Exists reports whether the store at path exists (a folder for markdown, a file otherwise).

func Forget

func Forget(path, id string) error

Forget removes entry id (active or pending), along with any pending replacement of it.

func ForgetAll

func ForgetAll(path string) (int, error)

ForgetAll removes every entry and returns how many there were. A missing store stays missing.

func ImportLegacy

func ImportLegacy(path, legacy string) (int, error)

ImportLegacy copies the entries of the legacy JSON store at legacy (the pre-markdown per-project file, see StorePath) into the store at path, once: only when path doesn't exist yet and legacy holds entries. It never changes or deletes legacy, and returns how many entries it copied. Once the new store exists (even emptied by --memory-forget all), it never imports again.

func MarshalMarkdown

func MarshalMarkdown(e Entry) ([]byte, error)

MarshalMarkdown renders e as one markdown file: YAML front matter, then the text as the body.

func NormalizeTags

func NormalizeTags(tags []string) []string

NormalizeTags lowercases, trims and de-duplicates tags, dropping empty ones. Tags may hold letters, digits and . _ - / : only; anything else becomes "-", so a tag is always one comma-free word.

func ProjectKey

func ProjectKey(root string) string

ProjectKey returns the store file's base name for a project root: a readable name plus a hash of the full path, so two projects with the same directory name never share a store.

func ProjectRoot

func ProjectRoot(cwd string) (string, error)

ProjectRoot returns the path that identifies cwd's project: the Git common directory when cwd is inside a Git work tree, so the main checkout and every linked worktree share one store, else the absolute cwd. Symlinks are resolved either way.

func RenderLines

func RenderLines(lines []Line, omitted int) string

RenderLines builds the prompt section from already-selected lines and the number of omitted entries, exactly as Render does. It returns "" when there are no lines and nothing was omitted.

func ResolveStore

func ResolveStore(backend, dir, sqlitePath, cwd string) (string, error)

ResolveStore returns the absolute store path for backend: dir (markdown) or sqlitePath (sqlite), each defaulting to its Default*Path, with relative paths resolved against cwd.

func StorePath

func StorePath(dir, cwd string) (string, error)

StorePath returns the store file for cwd's project under dir; an empty dir means DefaultDir.

func Update

func Update(path string, fn func([]Entry) ([]Entry, error)) error

Update re-reads the store at path under an exclusive lock, applies fn and writes the result atomically, so concurrent writers never lose each other's changes. If fn returns an error nothing is written. A corrupt store is left unchanged and its *CorruptError is returned.

func ValidApproval

func ValidApproval(s string) bool

ValidApproval reports whether s is a known approval policy.

func ValidBackend

func ValidBackend(s string) bool

ValidBackend reports whether s is a selectable backend.

Types

type CorruptError

type CorruptError struct {
	Path string
	Err  error
}

CorruptError reports a store file that exists but can't be parsed. A corrupt store is never overwritten.

func (*CorruptError) Error

func (e *CorruptError) Error() string

func (*CorruptError) Unwrap

func (e *CorruptError) Unwrap() error

type Entry

type Entry struct {
	ID        string    `json:"id"`
	Text      string    `json:"text"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Source    Source    `json:"source"`
	// Tags are short lowercase labels (memory.default_tags plus any the
	// model gave); memory_search can filter by them.
	Tags []string `json:"tags,omitempty"`
	// Replaces is set on a pending entry that, once approved, replaces the
	// active entry with this id.
	Replaces string `json:"replaces,omitempty"`
}

Entry is one saved note.

func Active

func Active(entries []Entry) []Entry

Active returns the active entries, newest (by UpdatedAt) first; ties keep the later-stored entry first.

func Load

func Load(path string) ([]Entry, error)

Load reads the store at path, oldest first. A missing store is empty and is never created. A store that can't be parsed returns a *CorruptError.

func Recent

func Recent(entries []Entry, limit int) []Entry

Recent returns the newest active entries, at most limit (0: all).

func Search(entries []Entry, query, tag string, limit int) []Entry

Search returns the active entries whose text or tags contain every word of query (case-insensitive) and that carry tag when tag is set, newest first, at most limit (0 means no limit). Pending entries never match: a person hasn't approved them.

func UnmarshalMarkdown

func UnmarshalMarkdown(data []byte) (Entry, error)

UnmarshalMarkdown parses one markdown entry file. It accepts CRLF line endings, a UTF-8 byte-order mark, blank lines around the body and unknown front-matter keys (ignored).

type Limits

type Limits struct {
	MaxWritesPerRun int
	MaxEntries      int
	// MaxTextLen is the longest entry text in characters; 0 means
	// MaxTextLen (the package default).
	MaxTextLen int
}

Limits bound what one run may write and what the store may hold.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns the default per-run and store limits.

type Line

type Line struct {
	ID   string `json:"id"`
	Date string `json:"date"`
	Text string `json:"text"`
}

Line is one loaded entry as it appears in the prompt section: its id, its date (YYYY-MM-DD, UTC) and its text. A run records its loaded lines so a resume can rebuild the identical section without reading the store.

func LineOf

func LineOf(e Entry) Line

LineOf returns e as a prompt line.

func Lines

func Lines(entries []Entry) []Line

Lines returns entries as prompt lines, in order.

type Rendered

type Rendered struct {
	// Section is the text to add to the system prompt, or "" when no entry
	// is active.
	Section string
	// Loaded are the entries in Section, newest first.
	Loaded []Entry
	// Omitted counts active entries left out to stay within maxBytes.
	Omitted int
}

Rendered is the prompt section for one run plus what it contains.

func Render

func Render(entries []Entry, maxBytes int) Rendered

Render builds the "Project memory" prompt section from the active entries, newest first, keeping the newest entries whose lines fit within maxBytes (<= 0 means DefaultMaxBytes). Pending entries are never rendered. When entries are omitted, the section says how many.

type Result

type Result struct {
	Op       string `json:"op"`
	ID       string `json:"id"`
	Status   string `json:"status"`
	Redacted bool   `json:"redacted"`
}

Result describes one successful write.

type Saved

type Saved struct {
	Result
	UntrustedContext bool
}

Saved is one successful write a Writer made, reported as a memory.saved event: the result plus the entry's source (the untrusted-context flag).

func (Saved) Fields

func (s Saved) Fields() map[string]any

Fields returns s as memory.saved event fields. The entry text is never included, only its id and status.

type Source

type Source struct {
	RunID            string `json:"run_id,omitempty"`
	Model            string `json:"model,omitempty"`
	Version          string `json:"version,omitempty"`
	UntrustedContext bool   `json:"untrusted_context"`
}

Source records where an entry came from.

type Writer

type Writer struct {
	Path     string
	Approval string
	Limits   Limits
	// Redactor masks configured secret values; token-shaped strings are
	// always masked, whether or not it is set.
	Redactor *eventlog.Redactor
	Source   Source
	// Tags are added to every entry this writer saves (memory.default_tags).
	Tags []string
	// contains filtered or unexported fields
}

Writer applies one run's memory writes to the store. It enforces the per-run write limit, the store's active-entry limit, the approval policy and redaction. Writes go to the store only; they never change the run's own prompt. It is safe for concurrent use.

func NewWriter

func NewWriter(path, approval string, limits Limits, red *eventlog.Redactor, src Source, snapshot []Entry) *Writer

NewWriter returns a Writer for path whose replace and forget operations may target the ids in snapshot (the entries loaded at run start).

func (*Writer) Add

func (w *Writer) Add(text string) (Result, error)

Add saves a new entry.

func (*Writer) AddTagged

func (w *Writer) AddTagged(text string, tags []string) (Result, error)

AddTagged saves a new entry with tags on top of the writer's Tags.

func (*Writer) Forget

func (w *Writer) Forget(id string) (Result, error)

Forget removes entry id from the store.

func (*Writer) MarkUntrusted

func (w *Writer) MarkUntrusted()

MarkUntrusted records that untrusted external content (a fetched web page, an MCP result, a review diff) entered the run's context; every later write is saved pending with source.untrusted_context set.

func (*Writer) Replace

func (w *Writer) Replace(id, text string) (Result, error)

Replace changes the text of entry id. When the new text would be pending and id is active, a pending replacement is saved instead and the active entry stays in place until the replacement is approved.

func (*Writer) TakeSaved

func (w *Writer) TakeSaved() []Saved

TakeSaved returns the writes made since the last call and forgets them.

func (*Writer) Untrusted

func (w *Writer) Untrusted() bool

Untrusted reports whether MarkUntrusted was called.

func (*Writer) Writes

func (w *Writer) Writes() int

Writes returns how many writes succeeded in this run.

Jump to

Keyboard shortcuts

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