memory_search
Find relevant personal memories by keyword query, with options to filter by scope, type, and date. Retrieves matching records from your local memory database for recall.
Instructions
Search active memories with FTS5. Default global only; project/domain may include global.
query is plain text (all terms must match), not SQL/FTS syntax. as_of filters validity, not historical revisions. Forgotten records are always excluded. Paginate with offset.
query_variants (max 5, each max 100 chars) runs each keyword set as its own strict FTS query and merges the ranked lists by reciprocal rank fusion, score = sum(1/(60+rank)). This is lexical keyword fusion, NOT vector or semantic search: a variant only helps if its characters actually occur in the record. Each variant gets at most 100 candidates before fusion, so offsets past that pool return fewer or no rows. Every variant still passes the same scope, type, validity and forgotten filters. With variants present the original query is fused too; an empty query is skipped instead of browsing everything.
The response keeps the memories array and adds a retrieval object so an empty page can be read correctly. reason is "empty_scope" (no current record passes the scope, type, as_of and forgotten filters), "no_lexical_match" (records exist in scope but none match the keywords), "offset_beyond_pool" (the page starts past the available candidates), "relaxed_match" (search_mode="auto" widened the search; see below) or "matched". None of these means that the user never recorded anything: scope/type/as_of filters and lexical matching both narrow the result. scoped_active counts records before any keyword match; candidate_pool counts the candidates this strategy actually collected, so when candidate_limit_reached is true it is a floor, not the total; for the multi-query strategy per_query_matches holds the exact per-query counts instead.
search_mode defaults to "strict", which is the historical keyword behaviour and is unchanged. search_mode="auto" runs the same strict queries first and, only when the candidate pool is empty and no page has been skipped, makes one bounded relaxed pass: it splits the query and the variants into keyword fragments, measures the in-scope document frequency of every one of them, drops the fragments that match nothing and the ones too common to discriminate, then keeps the most discriminative dozen (lowest frequency first, original order for ties, dropped ones listed in fallback.dropped_by_cap_fragments), recalls candidates with OR over those fragments, discards candidates covering too few of them, and ranks the rest by coverage. Scope, type, validity and forgotten filters always apply, and a pagination overflow never triggers it. Records from that pass carry match_quality="relaxed" and retrieval.fallback reports the method, trigger, keywords used and dropped, the coverage requirement, how many candidates were recalled, rejected and survived, and whether the recall cap was hit; such results need their relevance checked and their confidence is never rewritten. candidate_pool and has_more_in_pool describe the pool that survived coverage filtering, so a rejected candidate is never implied on a later page; when fallback.candidate_limit_reached is true there may be uninspected candidates and the pool is a floor, not the number of matching records. A relaxed pass can still return nothing: an empty result stays a valid answer, and fallback.coverage_required == 1 marks the weakest single-keyword tier.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| selection | Yes |