recall
Retrieve relevant past events from the user's memory vault before answering questions, making decisions, or shifting topics. Avoids repeating context by surfacing prior conversations, preferences, deadlines, and ongoing projects.
Instructions
Read the user's memory vault, afair, the persistent substrate they share across every session and every AI tool. CALL THIS BEFORE you respond to anything where the user's history might be relevant. Always.
The user installed afair so their context doesn't have to be repeated to every new conversation. A session where you have access to afair and don't call it is worse than a session without afair, because you're silently failing to use the memory they chose to maintain.
WHEN TO CALL:
At the START of every substantive task. Don't ask "do you want me to check?" Just check. Recall is cheap; missing context isn't.
Before answering questions that benefit from prior context: preferences, past decisions, names, ongoing projects, history with people (at work and outside it), important dates, recurring themes, deadlines, commitments.
When the user asks "do you remember X?", "what did we say about Y?", "remind me of Z?".
When the user wants the FULL content of a specific event ("show me the whole document"): use
by_idorby_content_hashwithfull_payload=True.When you want a snapshot of the vault's contents ("what's in there?"): use
stats=True.On topic shifts mid-conversation. New topic = fresh recall.
WHEN NOT TO CALL:
Pure compute questions ("what's 2+2", "translate this") that don't depend on the user's history.
When you just retrieved the same query a moment ago in this session.
Trivial conversational responses where no memory could help.
ARGUMENTS (all optional; combine as needed):
query: Natural-language search. Examples: "what did Sajinth say about the roadmap", "deadlines for the API project", "what does Mara like to drink", "when is my sister's birthday".
by_id: ULID of one specific event. Returns that event in full. Use after a prior recall hit when you need the whole content.
by_content_hash: sha256-prefixed hash of one specific event. Same lookup semantics as by_id.
scope: Optional substring filter. Reserved, currently no-op until Phase 3.5 emergent context detection lands.
depth: One of "auto" (default), "shallow", "normal", "deep". "auto" → system picks based on query shape (identifiers and single tokens → shallow; multi-token natural language → normal hybrid). Recommended default. "shallow" → FTS5 keyword only. Cheapest. "normal" → Hybrid FTS5 + vector. Local embedding inference, ~120ms. "deep" → Hybrid like normal, but the flat history lens: temporal relevance decay is OFF, so past-dated and superseded memories rank by match strength alone. Use for history / as-of questions ("what did I know back then", "show me past appointments"). Default recall instead de-prioritizes memories whose moment has passed, without dropping them.
limit: Max hits to return. Omitted → 10 in compact verbosity, 20 otherwise. Server cap 100 (larger values are clamped, not rejected).
verbosity: "compact" (default), "standard", or "full". Controls how much of each hit's interpretation/conflicts/linked-list detail is served — NOT the payload (see full_payload). "compact" → the AI-useful minimum: capped summary + payload text, top canonical entities and edges, only the top caveat-bearing conflicts. Use this by default. "standard"→ the full interpretation minus the redundant raw entity list and null edge validity bounds. "full" → every field, plus the durability rationale (interpretation.salience / salience_components / why_durable: a short "why this memory ranks as durable" line). Use standard/full, or by_id + full_payload, when you need salient_facts / raw entities / the complete conflict history. by_id/by_content_hash always serve full.
cursor: Opaque paging token for search/browse. Pass the
next_cursorfrom a prior recall back here verbatim to get the next page. Best-effort: rankings are recomputed per call. A bad cursor serves page 1 with a note.full_payload: Default false. When true, each hit's payload is the full untruncated content (for text-large events, the blob is read back into text). When false, text payloads are clipped (~500 chars standard/full, ~300 compact). Lookup modes (by_id, by_content_hash) imply full_payload=true.
stats: Default false. When true, the response includes a
summaryfield with total_events, by_kind, by_origin counts, useful for "what's the lay of the land" queries. Combines with any other mode.feedback: Optional dict reporting on hits from a PRIOR recall. Shape: {"useful_event_ids": [event_id, ...], "not_useful_event_ids": [event_id, ...], "missing_topic": "free-text note if expected content was absent"} This is the signal the self-improvement tuner reads to validate parameter changes (sharper salience, smarter mode-switching, better surprise calibration). After a recall, the NEXT time you call recall, include feedback referring to the prior hits. Don't ask whether to. Just send it. Empty payload is a no-op.
decide: Confirm/reject pending review proposals. Accepts a SINGLE decision or a LIST of up to 50 (batch-drain the queue in one call). Each: {"proposal_id": "...", "verdict": "confirm"|"reject"|"retract", "to_kind": "..."}. The per-decision outcomes come back in
decisions(see RETURN). A bad decision in a batch is reported as that item's outcome (status "error"); the rest still apply.pending_limit / pending_offset: Page the review queue. pending_limit (default 20, server cap 200) sets the page size; pending_offset skips that many rows. Passing pending_limit alone includes the list even without stats=True. While DRAINING the queue, decide a page then re-fetch at pending_offset=0 — deciding removes rows from the open set, so advancing the offset would skip the new head.
RETURN: {"hits": [{"event_id": "...", "content_hash": "...", "created_at": "...", "kind": "...", "origin": "...", "payload": {...}, "truncated": bool, "interpretation": {...} | null, "linked_event_ids": [...], "parent_hashes": [...], "invalidation": {...} | null, "conflicts": [...], "client": null | "..."}], "depth_used": "shallow" | "normal" | "deep", "note": null | "...", "summary": null | {total_events, by_kind, by_origin, by_client}, "decisions": [{"proposal_id": "...", "status": "...", "note": "..."}, ...], "next_cursor": null | "..."}
client on a hit is the AI tool that wrote the event, derived
server-side from the writing credential (not something the caller set).
It is null for events written before provenance existed, and is served at
verbosity "standard"/"full" and on by_id/by_content_hash lookups. The
summary.by_client map (on stats=True) counts events per writing client
— a different axis from by_origin, useful for "which tools have touched
this vault".
decisions is populated only when this call carried decide= — one
outcome per decision sent, in order (empty otherwise). next_cursor is
non-null when a next page is reachable; pass it back verbatim as cursor.
It is null once the pageable window is exhausted OR capped (the server bounds
how deep paging can go — at that edge a note says the window was capped, so
a client paging "until next_cursor is None" always terminates).
Each hit's payload is either the truncated summary or the full content,
depending on the full_payload flag (and lookup mode). truncated tells
you which form you got.
If hits is empty for a query, the user genuinely has no relevant memory yet. Consider asking them for context rather than guessing.
If invalidation is non-null on a hit, the fact was marked superseded
by a later event. For current-state questions, prefer hits where
invalidation is null. For historical questions, treat all hits as
relevant context.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| by_id | No | ||
| depth | No | auto | |
| limit | No | ||
| query | No | ||
| scope | No | ||
| stats | No | ||
| cursor | No | ||
| decide | No | ||
| feedback | No | ||
| verbosity | No | compact | |
| full_payload | No | ||
| pending_limit | No | ||
| pending_offset | No | ||
| by_content_hash | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| hits | Yes | ||
| note | No | ||
| summary | No | ||
| coverage | No | ||
| decisions | No | ||
| depth_used | Yes | ||
| next_cursor | No | ||
| pending_counts | No | ||
| pending_corrections | No | ||
| pending_corrections_count | No |