Product Target
Engram should answer three questions during every chat turn:- What did the assistant remember?
- What prior memory influenced the answer?
- Why did that memory become active?
Current Baseline
Already implemented:EngramMemory,EngramEvent, andStreamChunkinsrc/types.ts.- SSE encoding/parsing in
src/lib/events. - In-memory session helpers in
src/lib/memory/store.ts. - Basic retrieve/consolidate logic in
src/lib/memory. - Memory tool wrappers in
src/lib/memory/tools.ts. - Stateless
/api/chatroute that streams deterministic or OpenAI-backed events. - Unit tests and Playwright smoke coverage.
Runtime Modes
Demo Mode
Used for local development, tests, screenshots, and no-key usage.- No live model call.
- Deterministic responses.
- Emits valid
StreamChunkvalues. - Remains the default when provider env vars are absent.
OpenAI Mode
Used whenOPENAI_API_KEY is present and CHAT_PROVIDER=openai.
- Calls OpenAI through a provider wrapper.
- Uses the same memory engine and emits the same
StreamChunkcontract. - Normal unit tests mock the provider; live calls are not part of regular CI.
Future Supabase Mode
Supabase is useful once we want durable sessions, shareable demos, or login. It should not block the next milestone. The immediate architecture should use a storage interface with an in-memory adapter first. A Supabase adapter can implement the same interface later.Core Interfaces
Memory Store
Create a store interface insrc/lib/memory/store-interface.ts:
InMemoryMemoryStore- Backed by
Map<string, MemorySession> - Created fresh for each
/api/chatrequest and hydrated fromclientMemories. - A shared instance is used only by direct engine tests that exercise multi-turn behavior.
SupabaseMemoryStore- Tables can be added when persistence becomes a product requirement.
- Suggested table shape:
sessions:id,created_at,last_seen_atmemories:id,session_id,text,topic,importance,region,created_at,last_accessed,access_count,embedding
Memory Engine
Create a service insrc/lib/memory/engine.ts:
- New user facts start in
hippocampus. - Retrieval emits
retrieveand thenfire. - Retrieval updates access metadata but never changes a memory’s anatomical region.
- Only explicit consolidation or an accepted Dream proposal creates
temporalstable knowledge. - Prefrontal represents loaded/active context, not durable storage.
- Decay dims lower-ranked memories; it should not delete them.
Chat Provider
Create provider wrappers undersrc/lib/chat/providers.
- Current user message.
- Recent chat history.
- Retrieved memories.
- Tool results or memory instructions.
- text delta
- memory action intent
- final usage/error metadata
OPENAI_MODEL.
/api/chat Contract
POST /api/chat remains SSE.
Request:
sessionId namespaces event and memory IDs; it is not an authentication credential. Because the route creates a fresh store for every request, knowing another session ID does not expose server-retained memory. The browser sends its current validated memory projection explicitly.
Response stream:
initorloadevent for current session state.retrieveevent for relevant memories.fireevent for active memory regions.- Text deltas from the provider.
- Optional
store,consolidate, ordecayevents. done.
{ kind: "error" } when possible, not throw an opaque response after partial output.
Frontend State
The frontend should consume onlyStreamChunk.
Add or extend hooks:
useChat: owns SSE request lifecycle, text deltas, errors, cancellation.useEventQueue: remains the source for visualization events.useMemoryStore: derives visible memories from event history.useExplanationState: tracks selected/hovered memory and “why this fired.”
EngramEvent[].
Explainability Panel
This is the highest-leverage product surface after the chat loop. For each retrieval/fire event, show:- Memory text.
- Score or rough reason.
- Region.
- Recency/access count.
- Why it was relevant to the current user message.
- keyword overlap
- importance
- access count
- recency
Supabase Path
Supabase is a good fit after the in-memory loop works. Use it when we need:- persistent sessions
- shareable demo links
- saved memory histories
- user accounts
Parallelization Plan
Do not split until the interfaces above are committed. After that, work can fan out safely:-
Agent A: memory engine and store interface
- Owns
src/lib/memory/** - Adds focused unit tests for store/retrieve/fire/consolidate/decay.
- Owns
-
Agent B: chat provider and
/api/chat- Owns
src/lib/chat/**andsrc/app/api/chat/route.ts - Adds mocked OpenAI tests and SSE error tests.
- Owns
-
Agent C: frontend live stream and explainability UI
- Owns
src/hooks/**,src/components/UI/**, and non-asset visual event behavior. - Adds hook/component tests and updates smoke expectations if needed.
- Owns
src/types.ts unless coordinated first.
Next Commit-Sized Milestone
Implement the in-memory live loop without Supabase:- Add
MemoryStoreinterface andInMemoryMemoryStore. - Add
MemoryEngineservice around existing memory helpers. - Update
/api/chatso demo mode uses the engine instead of fixture-only streams. - Add mocked OpenAI provider boundary but keep it disabled unless env vars are present.
- Add tests for SSE order and memory events per chat turn.