search_sessions
Search stored coding-agent sessions by text, with filters for agent, scope, and boolean logic; sort by relevance, date, or meaning, and redact secrets.
Instructions
Case-insensitive search across sessions.
Args:
query: Search string. Supports:
* Bare words: pwa manifest (AND default)
* Quoted phrases: "exact phrase"
* Negative prefix: -claude (Google-style, always excluded)
agent: Optional agent filter (claude/codex/opencode/antigravity/pi).
scope: Where to look.
* "title" — only session.title (default, backward-compat)
* "body" — message text + tool_use[*].input +
tool_result[*].content
* "all" — title OR body
operator: How to combine positive terms.
* "AND" — all positive terms must appear (default)
* "OR" — at least one positive term must appear
* "NOT" — no term (positive or negative) may appear
Negative -term prefixes are always excluded regardless
of operator.
limit: Maximum number of results. 0 or negative = no limit.
Applied after sorting, so it keeps the top-ranked matches.
sort: Result ordering.
* "relevance" — BM25 relevance over the matched text
(default). Pure-stdlib scoring; ties keep newest-first.
* "date" — newest-first by session date (the historical
pre-ranking order).
* "semantic" — F5.1 (optional ai-r[semantic]): the
BM25 top-50 candidates re-ranked by meaning with a local
multilingual embedding model (cross-lingual ru↔en,
synonyms); the response carries a semantic dict —
either the active ranking (active: true, model,
candidate count, blend weight) or the honest degradation
notice (active: false + plain-words reason +
fallback: "bm25", order stays BM25 — never a crash).
noise: Noise filter — a session is noise when it is a spawned
subagent (kind == "subagent" or parent_uuid set).
* "include" — no filtering (default).
* "exclude" — search only top-level agent sessions.
* "only" — search only subagent sessions.
Applied before matching, so excluded sessions never pay
the body-scan cost.
redact: When True (default) secrets in the emitted title /
snippet / extra fields are masked as
[REDACTED_<TYPE>] and the response carries a
redactions type→count dict when any replacement
happened; False returns raw content. Matching always
runs on the RAW stored text, so searching for a literal
secret still finds its session — only the displayed
snippet is masked.
include_thinking: When True fold model reasoning
(message.thinking) into the body/all search haystack
so a search matches text that lives only in the model's thoughts.
Default False: reasoning is excluded from matching to save
the caller's budget (turn it on only when a query must reach into
reasoning). No effect on scope="title". The two modes are
cached under separate keys, so toggling never serves a stale
haystack of the other mode.
Returns:
A dict {"results": [...], "count": N} where results is the
list of session summaries and count is their total. When
scope is "body" or "all" and a match is found, each
summary includes a "snippet" field with the first matching
message excerpt (up to 200 chars) and may carry body_truncated.
When a scan matches nothing (count == 0), the dict additionally
carries diagnostics (scanned agents + session counts, corpus
date bounds, cause hints) so an empty result is explainable. With
sort="semantic" the dict also carries a semantic report
(active ranking vs BM25 fallback + reason).
Errors are returned as a top-level {"error": ..., "message": ...}
dict (matches the existing convention).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | relevance | |
| agent | No | ||
| limit | No | ||
| noise | No | include | |
| query | Yes | ||
| scope | No | title | |
| redact | No | ||
| operator | No | AND | |
| include_thinking | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||