Documentation
¶
Overview ¶
Package memory implements harness pattern 3 (tiered memory): a conservative, cross-session memory facility exposed to the model as two tools (Remember and Recall) backed by a pluggable tool.MemoryStore.
This package contains BOTH the file-backed store adapter AND the tool.Tool values that drive it. They are kept together deliberately: the tools are thin adapters over the store seam (constructor-injected, mirroring NewShellTool), and shipping them as one opt-in unit lets the composition root wire memory with a single import. The tools depend only on the tool.MemoryStore interface, so a fake in-memory store can stand in for tests.
SCOPING: a Store is per-PROJECT. The composition root calls New(dir) once per project/workspace directory; entries persist across process restarts for that directory and are isolated from other projects. See tool.MemoryStore.
Index ¶
- Constants
- func NewCallerStore(store tool.MemoryStore, project bool) tool.MemoryStore
- func NewNamespacedStore(store tool.MemoryStore, namespace string) tool.MemoryStore
- func NewRecallTool(store tool.MemoryStore) tool.Tool
- func NewRememberTool(store tool.MemoryStore) tool.Tool
- func NewSearchMemoryTool(store tool.MemoryStore) tool.Tool
- func NewUserModelTools(store tool.MemoryStore) []tool.Tool
- func Register(cat *tool.Catalog, store tool.MemoryStore) error
- func RegisterUserModel(cat *tool.Catalog, store tool.MemoryStore) error
- func Tools(store tool.MemoryStore) []tool.Tool
- func WithWorkspace(ctx context.Context, workspace string) context.Context
- func WorkspaceFromContext(ctx context.Context) string
- type CallerStore
- func (s *CallerStore) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
- func (s *CallerStore) Index(ctx context.Context) ([]tool.MemoryEntry, error)
- func (s *CallerStore) Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
- func (s *CallerStore) List(ctx context.Context, prefix string) ([]tool.MemoryEntry, error)
- func (s *CallerStore) Recall(ctx context.Context, key string) (tool.MemoryEntry, bool, error)
- func (s *CallerStore) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
- func (s *CallerStore) Search(ctx context.Context, query string, limit int) ([]tool.MemoryEntry, error)
- func (s *CallerStore) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
- type NamespacedStore
- func (s *NamespacedStore) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
- func (s *NamespacedStore) Index(ctx context.Context) ([]tool.MemoryEntry, error)
- func (s *NamespacedStore) Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
- func (s *NamespacedStore) List(ctx context.Context, prefix string) ([]tool.MemoryEntry, error)
- func (s *NamespacedStore) Recall(ctx context.Context, key string) (tool.MemoryEntry, bool, error)
- func (s *NamespacedStore) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
- func (s *NamespacedStore) Search(ctx context.Context, query string, limit int) ([]tool.MemoryEntry, error)
- func (s *NamespacedStore) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
- type Store
- func (s *Store) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
- func (s *Store) Index(ctx context.Context) ([]tool.MemoryEntry, error)
- func (s *Store) Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
- func (s *Store) List(ctx context.Context, prefix string) ([]tool.MemoryEntry, error)
- func (s *Store) Recall(ctx context.Context, key string) (tool.MemoryEntry, bool, error)
- func (s *Store) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
- func (s *Store) RetireDuplicate(ctx context.Context, survivorKey string, survivorVersion tool.MemoryVersion, ...) (tool.MemoryRecord, error)
- func (s *Store) Search(ctx context.Context, query string, k int) ([]tool.MemoryEntry, error)
- func (s *Store) SynthesizeReplacement(ctx context.Context, survivor tool.MemoryEntry, ...) (tool.MemoryRecord, error)
- func (s *Store) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Constants ¶
const ( RememberToolName = "Remember" RecallToolName = "Recall" SearchMemoryToolName = "SearchMemory" InspectMemoryToolName = "InspectMemory" ForgetMemoryToolName = "ForgetMemory" UndoMemoryToolName = "UndoMemory" )
Tool names are kept here for compatibility with root composition callers.
const ( RememberUserToolName = "RememberUser" RecallUserToolName = "RecallUser" SearchUserModelToolName = "SearchUserModel" InspectUserMemoryToolName = "InspectUserMemory" ForgetUserMemoryToolName = "ForgetUserMemory" UndoUserMemoryToolName = "UndoUserMemory" )
Catalog names of the user-scoped portable memory tools.
Variables ¶
This section is empty.
Functions ¶
func NewCallerStore ¶
func NewCallerStore(store tool.MemoryStore, project bool) tool.MemoryStore
NewCallerStore returns a store partitioned by verified caller and, when project is true, by workspace.
func NewNamespacedStore ¶
func NewNamespacedStore(store tool.MemoryStore, namespace string) tool.MemoryStore
NewNamespacedStore confines a MemoryStore to namespace. Namespace is an adapter-private boundary, not a model-visible key prefix.
func NewRecallTool ¶
func NewRecallTool(store tool.MemoryStore) tool.Tool
NewRecallTool returns the portable project-scoped Recall implementation.
func NewRememberTool ¶
func NewRememberTool(store tool.MemoryStore) tool.Tool
NewRememberTool returns the portable project-scoped Remember implementation.
func NewSearchMemoryTool ¶
func NewSearchMemoryTool(store tool.MemoryStore) tool.Tool
NewSearchMemoryTool returns the portable project-scoped Search implementation.
func NewUserModelTools ¶
func NewUserModelTools(store tool.MemoryStore) []tool.Tool
NewUserModelTools returns the mandatory user-scoped portable family.
func Register ¶
func Register(cat *tool.Catalog, store tool.MemoryStore) error
Register adds the applicable project family to cat.
func RegisterUserModel ¶
func RegisterUserModel(cat *tool.Catalog, store tool.MemoryStore) error
RegisterUserModel adds the applicable user family to cat.
func Tools ¶
func Tools(store tool.MemoryStore) []tool.Tool
Tools returns the mandatory six-tool project family.
func WithWorkspace ¶
WithWorkspace annotates a run context for the caller-scoped project memory adapter. It is intentionally adapter-local: workspaces are already session state and do not widen any engine port.
func WorkspaceFromContext ¶ added in v0.0.24
WorkspaceFromContext returns the private runtime workspace root attached by server composition. It is intentionally not derived from a durable EnvironmentRef, whose ID is provider-opaque.
Types ¶
type CallerStore ¶
type CallerStore struct {
// contains filtered or unexported fields
}
CallerStore chooses a namespace from the verified caller on each operation. Project stores additionally bind that namespace to the run's workspace.
func (*CallerStore) Forget ¶
func (s *CallerStore) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Forget tombstones a key in the verified caller's namespace.
func (*CallerStore) Index ¶
func (s *CallerStore) Index(ctx context.Context) ([]tool.MemoryEntry, error)
Index returns verified caller entries without their values.
func (*CallerStore) Inspect ¶ added in v0.0.40
func (s *CallerStore) Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
Inspect returns the lifecycle record for a key in the verified caller's namespace.
func (*CallerStore) List ¶
func (s *CallerStore) List(ctx context.Context, prefix string) ([]tool.MemoryEntry, error)
List returns verified caller entries matching prefix.
func (*CallerStore) Recall ¶
func (s *CallerStore) Recall(ctx context.Context, key string) (tool.MemoryEntry, bool, error)
Recall retrieves key from the verified caller's namespace.
func (*CallerStore) Remember ¶ added in v0.0.40
func (s *CallerStore) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
Remember stores entry in the verified caller's namespace with mandatory CAS.
func (*CallerStore) Search ¶
func (s *CallerStore) Search(ctx context.Context, query string, limit int) ([]tool.MemoryEntry, error)
Search ranks entries in the verified caller's namespace.
func (*CallerStore) Undo ¶ added in v0.0.40
func (s *CallerStore) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Undo restores a key in the verified caller's namespace.
type NamespacedStore ¶
type NamespacedStore struct {
// contains filtered or unexported fields
}
NamespacedStore presents one logical MemoryStore namespace over a shared backing store. Every operation, including index/search and consolidation, is confined to namespace; callers never see the internal key prefix.
func (*NamespacedStore) Forget ¶
func (s *NamespacedStore) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Forget tombstones a logical key when its current version matches expected.
func (*NamespacedStore) Index ¶
func (s *NamespacedStore) Index(ctx context.Context) ([]tool.MemoryEntry, error)
Index returns namespace entries without their values.
func (*NamespacedStore) Inspect ¶ added in v0.0.40
func (s *NamespacedStore) Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
Inspect returns the lifecycle record for a logical key in this namespace.
func (*NamespacedStore) List ¶
func (s *NamespacedStore) List(ctx context.Context, prefix string) ([]tool.MemoryEntry, error)
List returns namespace entries matching prefix.
func (*NamespacedStore) Recall ¶
func (s *NamespacedStore) Recall(ctx context.Context, key string) (tool.MemoryEntry, bool, error)
Recall retrieves key from this namespace.
func (*NamespacedStore) Remember ¶ added in v0.0.40
func (s *NamespacedStore) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
Remember stores entry under this namespace with mandatory CAS.
func (*NamespacedStore) Search ¶
func (s *NamespacedStore) Search(ctx context.Context, query string, limit int) ([]tool.MemoryEntry, error)
Search ranks entries within this namespace.
func (*NamespacedStore) Undo ¶ added in v0.0.40
func (s *NamespacedStore) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Undo restores a logical key when its current version matches expected.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a file-backed, cross-process-safe tool.MemoryStore. It persists entries as a single JSON document at <dir>/memory.json and commits every write atomically (temp file + rename) so a crash mid-write cannot corrupt or truncate the on-disk file. A fresh Store opened over the same dir sees previously written entries, giving durability across process restarts.
Concurrency / locking. The store is safe for both in-process and cross-process concurrent use, and — critically — does not LOSE updates under either:
- In-process: s.mu serialises every method on a single Store, and is also held across the full read-modify-write of a mutation. This is the only lock that protects the flock handle, which is NOT goroutine-safe when shared across goroutines on one fd.
- Cross-process (several mecated/mecatui instances, agent-team / subagent runs sharing one memory.json): a gofrs/flock advisory lock on the STABLE sentinel <dir>/memory.lock guards the read-modify-write. Writes (Remember, Remember, Forget) take an EXCLUSIVE lock; reads (Recall, List, Index) take a SHARED lock. The lock spans the whole load→mutate→save sequence, so two processes can no longer interleave read-modify-write and clobber each other (the lost-update bug the bare temp+rename did not prevent).
Acquisition order is always s.mu THEN flock; the flock is acquired with a bounded TryLockContext/TryRLockContext (lockRetryDelay / lockTimeout, also honouring ctx cancellation) so a stuck holder fails loud instead of deadlocking, and released (Unlock) before return on every path including errors.
INVARIANT — at most ONE *Store per directory per process. gofrs/flock uses BSD flock(2), which contends across file descriptors even WITHIN a single process. Two *Store values opened over the SAME dir in the SAME process therefore hold DISTINCT fds on the sentinel and would self-deadlock: the second writer's exclusive acquire blocks on the first's lock until the lockTimeout expires (a loud error, but a needless one). The composition root upholds this today by constructing one shared *Store per project (see internal/app). If per-session memory is ever needed, SHARE a single *Store keyed by absolute dir (the way the session-engine map is keyed) rather than calling New per session — do NOT open a second *Store over a dir already owned in-process.
func New ¶
New constructs a file-backed Store rooted at dir, creating dir (and parents) if it does not exist. The store is scoped to dir: it owns <dir>/memory.json and the cross-process lock sentinel <dir>/memory.lock. Pass a per-project directory so memory is isolated per project.
Construct AT MOST ONE *Store per dir per process. Because the cross-process lock uses BSD flock(2) (per-fd, cross-fd-contending even in one process), a SECOND *Store opened over the same dir in this process would self-deadlock its own lock acquisition until the lockTimeout, since the two *Store values hold separate fds on the sentinel. Share one *Store keyed by absolute dir instead. See the Store type doc for the full rationale.
func (*Store) Forget ¶
func (s *Store) Forget(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Forget atomically appends a tombstone when expected is current.
func (*Store) Index ¶
Index returns the tier-0 routing table: every entry with the VALUE OMITTED and Description filled (explicit, else derived from the value's first line), sorted by key. It applies NO size cap — the consumer (the prompt assembler) caps and renders. It is the cheap, always-in-context summary view.
func (*Store) Inspect ¶
Inspect returns the current state and complete history, including deleted tombstones.
func (*Store) List ¶
List returns all entries whose key has the given prefix, sorted by key. An empty prefix returns every entry.
func (*Store) Remember ¶
func (s *Store) Remember(ctx context.Context, entry tool.MemoryEntry, expected tool.MemoryCurrent) (tool.MemoryRecord, error)
Remember performs a presence-and-version CAS in the same flocked transaction.
func (*Store) RetireDuplicate ¶
func (s *Store) RetireDuplicate(ctx context.Context, survivorKey string, survivorVersion tool.MemoryVersion, sourceKey string, sourceVersion tool.MemoryVersion) (tool.MemoryRecord, error)
RetireDuplicate atomically verifies the active survivor and source revisions and appends a source tombstone in one exclusive load/mutate/save transaction.
func (*Store) Search ¶
Search ranks entries by BM25 lexical relevance to query (over each entry's key + derived description + value) and returns the top k best-first, with the VALUE OMITTED and Description filled — mirroring Index's result shape. Ranking runs INSIDE the shared lock, over the freshly loaded data, so it sees a consistent snapshot and never races a concurrent write. An empty or whitespace-only query short-circuits to an empty result (no lock, no error); k <= 0 uses the default page size. See bm25Rank for the scoring/ordering rules.
func (*Store) SynthesizeReplacement ¶
func (s *Store) SynthesizeReplacement(ctx context.Context, survivor tool.MemoryEntry, survivorVersion tool.MemoryVersion, sourceKeys []string, sourceVersions []tool.MemoryVersion) (tool.MemoryRecord, error)
SynthesizeReplacement atomically verifies every participant, appends a new survivor revision with the reviewed bytes, and tombstones all sources in one exclusive load/mutate/save transaction. Undo remains per-key and is intentionally not exposed as a grouped inverse of this operation.
func (*Store) Undo ¶ added in v0.0.40
func (s *Store) Undo(ctx context.Context, key string, expected tool.MemoryVersion) (tool.MemoryRecord, error)
Undo appends a compensating revision for the newest mutation not already compensated. Recording the target version makes repeated undo walk backward and prevents concurrent callers from reversing one mutation twice.