Skip to content

Point-in-time recall

asOf answers: what did we believe, about that moment, at that moment?

Because nothing is ever physically deleted, the full history of what the system knew is reconstructable.


const { context, factCount } = await memory.recall({
dataset: 'user_42',
asOf: '2026-06-01T00:00:00Z',
});
Terminal window
curl -X POST $API/v1/memory/recall \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"dataset":"user_42","asOf":"2026-06-01T00:00:00Z"}'

Accepts a full ISO datetime or a bare date (2026-06-01, interpreted as midnight UTC).

Also available on the fact list:

await memory.listFacts('user_42', { asOf: '2026-06-01' });

Four conditions, all of which must hold:

created_at <= $asOf -- the row existed by then
AND valid_at <= $asOf -- and had come into effect
AND (valid_until IS NULL OR valid_until > $asOf) -- and had not expired
AND (invalid_at IS NULL OR invalid_at > $asOf) -- and was still believed

That last line is the interesting one. A fact superseded after asOf is included, because at asOf we still believed it.


2026-03-01 "I drive a Honda Civic."
→ honda civic created 03-01 validAt 03-01 invalidAt null
2026-08-16 "Actually I switched to a Tesla Model 3."
→ honda civic invalidAt 08-16
→ tesla model 3 created 08-16 validAt 08-16 invalidAt null
Query Result Why
recall() tesla model 3 Live now
asOf: 2026-06-01 honda civic Existed, valid, not yet superseded
asOf: 2026-02-01 nothing The fact didn’t exist yet
asOf: 2026-09-01 tesla model 3 Honda Civic was superseded on 08-16

async function explainDecision(dataset: string, at: string, question: string) {
const { context, facts } = await memory.recall({
dataset,
asOf: at,
include: ['raw'],
limit: 50,
});
return { question, at, knewAtTheTime: context, facts };
}
// "Why did it recommend a horror movie on 14 August?"
await explainDecision('user_42', '2026-08-14T18:30:00Z', 'movie recommendation');

A user reports the assistant “forgot” something.

const now = await memory.recall({ dataset, query: 'dietary requirements' });
const then = await memory.recall({ dataset, query: 'dietary requirements', asOf: reportedAt });
// then.context has it, now.context does not → the memory legitimately changed
// neither has it → it was never extracted
// both have it → a retrieval or prompt problem

That triage takes seconds and rules out two of three causes.

Reconstruct the state that drove an automated decision, without keeping a separate audit log.

const cutoff = '2026-08-01T00:00:00Z';
const { context } = await memory.recall({ dataset: 'fixture_user', asOf: cutoff });
expect(context).toMatchSnapshot();

Because asOf pins the fact set, the snapshot stays stable as new memory accumulates.


asOf bypasses hybrid retrieval.

Normal recall fuses vector, entity-anchor and keyword signals. Point-in-time recall falls back to keyword and recency only, because the vector and anchor paths assume the current live set.

Consequences:

Normal With asOf
Vector similarity
Entity anchor
Keyword
Recency
  • Results are correct, the right facts for that instant.
  • Ranking is weaker. A query still filters by keyword but will not find semantic matches.
  • Raise limit to compensate; with fewer signals the top few are less reliable.
await memory.recall({
dataset: 'user_42',
query: 'movies',
asOf: '2026-06-01',
limit: 30, // wider net, since ranking is weaker
});

For auditing this rarely matters, you usually want everything known at a moment, not the top 8.


const { facts } = await memory.listFacts('user_42', {
includeInvalidated: true,
});

Every fact ever, whatever its state. asOf overrides includeInvalidated when both are passed.

const { facts } = await memory.listFacts('user_42', {
q: 'honda civic',
includeInvalidated: true,
});
facts
.sort((a, b) => a.validAt.localeCompare(b.validAt))
.forEach((f) => {
console.log(
`${f.validAt.slice(0, 10)} ${f.predicate} ${f.object}` +
(f.invalidAt ? ` (superseded ${f.invalidAt.slice(0, 10)})` : ''),
);
});

Future-dated facts. asOf in the future returns facts whose validAt has arrived by then but which we already know about, including a stated future change. Correct, occasionally surprising.

Timezones. A bare date is midnight UTC. If your users are elsewhere, build the instant explicitly rather than trusting a date string.

createdAt vs validAt. A fact stated today about the past has validAt in the past but createdAt today. asOf before createdAt excludes it, we did not know it yet, even though it was true. That is the point of bi-temporality.

Not free. It is a database read with the same shape as normal recall. Cheaper than normal recall, actually, since it skips the embedding call.