Consumer agents run for years with the same user. A companion, a coach, or a journaling app collects thousands of small facts, most of which are only true for a while or evolve over time. Things like a race next Sunday, a trial ending in January, a trip last week, etc. With time, the memories stay in the pool even after the event time has passed. Most setups store what the user said and when the system stored it, and nothing about when it actually happened. And the agent has no way to tell what is still true at retrieval time. For continuity, we will use one running example in this article - a fitness coach app called Stride, with a user named Sam.Every memory has two clocks. Event time is when the thing happened: Sam ran the race on Sunday. Write time is when your app got to know about it: during a user’s chat session or during a later data import. Tracking these two independent timelines, when the event happened and when the system learned about it, is bitemporal reasoning, and relational databases have done it for decades. Most memory setups keep only write time, and that holds until the two timelines disagree. They disagree often, and we’ll discuss the list of possible cases here. 1. Loading old history Stride has run for two years and wants to import old chats. Sam wrote "I ran my first 10K last weekend" in June 2025. Store it today, and the system reads "last weekend" against today, so the memory becomes "Sam ran a 10K this week," wrong by a year and stuck that way since the import loses the original date. Pass the original conversation time on every write and resolve the phrase against it. memory.add( [{"role": "user", "content": "I ran my first 10K last weekend, finished in 58 minutes."}], user_id="sam", event_time=1750075200, # 2025-06-16, the day Sam said it ) The stored memory now reads "User ran a 10K on the weekend of 2025-06-14 to 2025-06-15," its created date reads June 2025, and a date filter for June 2025 finds it.2. Time-relevant terms in chats Sam says "I started the new knee meds yesterday." Resolve the phrase at write time, because only then do you know the actual conversation date, and store the date instead of the word. You also need a precision scale: "yesterday" is a day, "last month" is a month, and you should not invent an exact day from a vague phrase. Pass the exact instant the message was sent so time zones work out. "This morning" sent at 5 AM in Singapore is still the previous day in UTC. 3. Changing facts Sam is vegetarian, then starts eating fish. Sam lives in Lisbon, then moves to Berlin. Both were true at different times, and a coach app that mixes them suggests tofu for lunch recommendations in a city Sam doesn’t live in anymore. Do not delete the old fact. It needs to be retained and linked to the new fact to create a chain of events. A user may ask need the current value or the history depending on different cases: memory.search("Where do I go for dinner this weekend?", user_id="sam") memory.search("Where did I use to go for my weekend runs last year?", user_id="sam") 4. Facts with an end date "Training for the Berlin half on Nov 15" should stop affecting responses after Nov 15. "Dry January" ends Jan 31. Certain memories need an expiration setting at the time of writing, and the expiration should hide a memory rather than delete it, because the record still matters for history. The memory needs to stay visible through the date and drop out of search results the day after, with a way to show expired ones on request. 5. Time-aware retrievals Before each reply, the coach app asks "what did Sam struggle with last week?" or "my knee injury’s progress from July to September." Two tools do different jobs: Filters on actual event times give you hard windows for deterministic questions; that is how a filter on June 2025 would find the memory you created recently in a bulk import. Temporal reranking reorders memories by how well each memory's date fits a natural-language question when an explicit date range is not mentioned. One caution: some queries like "suggest a workout for next week" use a time word as part of the request itself - don’t turn it into a filter. 6. Replaying time Pass "now" into each read instead of letting the system decide the time autonomously. A nightly job at 02:00 UTC is morning in Singapore and the previous evening in San Francisco. An eval asking "what would the app have known on 2026-03-21?" should stay fixed as the day changes. A reference date fixes this. memory.search("What did I eat last night?", user_id="sam", as_of="2025-08-10T12:00:00Z") 7. Old facts crowding out new ones After 18 months, 2025 knee injury memories continue to match for the keyword "knee." Memory age alone is not a strong enough signal; how often a memory still gets retrieved is a stronger one. Memories need to be retrieved based on usage-based reranking and not directly filtered, so a two-year-old allergy stays reachable while a healed injury drifts down, and comes back up only if Sam specifically mentions the knee again. 8. "You forgot" complaints Sam says "I told you about my injury and you still pushed me to run hills." You need a way to reliably investigate what the app knew on a given day. Pull every memory written up to that date without any filters, and check whether the injury's creation date precedes the hill session. For a single memory, its change history should return each write and update with the old text, the new text, and when it changed.The capabilities to look for If you build or buy a memory layer for long-running consumer agents, these are the knobs it needs: Problem What you need Old data with original dates event-time, duration, and precision parameters on write Facts that changed linking chain of events, with a current-only filter Facts with an end date an expiry field, plus a way to show expired Hard time windows created-at filters with required logic operations Time words in a query temporal reranking A fixed "now" reference-date override on search Stale memories rank lower usage-based decay on ranking Changes to one memory a created-at filter with per-memory history The idea is simple: track both event time and write time for every memory with the above parameters and filters, and your app’s memory setup should be able to take care of almost all time-related scenarios.
Your AI Agent's Memory Needs Two Clocks

Agent memory goes stale because it tracks when a fact was stored and not when it happened. A practical guide to bitemporal memory: event time and write time.
Key Takeaways
- Your AI Agent's Memory Needs Two Clocks.
- Agent memory goes stale because it tracks when a fact was stored and not when it happened.
- A practical guide to bitemporal memory: event time and write time.
Read Complete Original Article
Published originally on HackerNoon




