// Package memory is Keel's durable, cross-run memory port. Modes Record derived // events and read them back with Recent; the AW-backed adapter persists them to // an ActivityWatch bucket. Event-kind names are owned by the modes that write // them — this package stays a generic event store. package memory import ( "context" "sync" "time" ) // Event is one derived event Keel chooses to remember. Kind is the event type // (e.g. "proposal_made"); Data carries pointers and small values, not copies of // other tools' source of truth. type Event struct { Kind string At time.Time Data map[string]any } // Store records derived events and reads recent ones back by kind. type Store interface { Record(ctx context.Context, e Event) error Recent(ctx context.Context, kind string, n int) ([]Event, error) } // nopStore is the no-memory fallback used when AW is unreachable or disabled. type nopStore struct{} // NewNop returns a Store that drops writes and returns no history. func NewNop() Store { return nopStore{} } func (nopStore) Record(context.Context, Event) error { return nil } func (nopStore) Recent(context.Context, string, int) ([]Event, error) { return nil, nil } // Fake is an in-memory Store for tests. type Fake struct { mu sync.Mutex events []Event } // NewFake returns an empty in-memory Store. func NewFake() *Fake { return &Fake{} } func (f *Fake) Record(_ context.Context, e Event) error { f.mu.Lock() f.events = append(f.events, e) f.mu.Unlock() return nil } func (f *Fake) Recent(_ context.Context, kind string, n int) ([]Event, error) { f.mu.Lock() defer f.mu.Unlock() var out []Event for i := len(f.events) - 1; i >= 0 && len(out) < n; i-- { if f.events[i].Kind == kind { out = append(out, f.events[i]) } } return out, nil } // Events returns a snapshot of everything recorded, for test assertions. func (f *Fake) Events() []Event { f.mu.Lock() defer f.mu.Unlock() return append([]Event(nil), f.events...) }