Search
how a query finds a session you half-remember
Search is lexical by default — fast, offline, no model required — with graceful fallbacks so natural phrasing still works.
Query forms
- Tokens —
deja connection poolmatches records containing both words (AND). - Phrases —
deja '"connection pool exhausted"'requires the exact contiguous phrase. - Regex —
deja --re "timeout|deadline exceeded". - Filters —
--harness,--project,--since 30d,--role assistant. Beyond the conversation roles, the index keeps what the agent did:--role filesfor the paths a turn opened,--role commandfor the commands that ran,--role editfor the spans an edit replaced,--role tool-outputfor what a tool printed. These are searchable but stay out of ordinary results, since a path that happens to contain the words of a question is not an answer to it.
What a subagent did
When an agent hands work to a subagent — a Claude Code Task, a Cursor subagent — that run gets its own transcript. The parent session keeps the launch and a summary of what came back; the turns and the tool stream exist only in the child file. Claude's child runs are read as the task they were handed and the answer they came back with — measured on a seeded store, that is a 12% larger index against a 345% one for the whole run, and it is where the settled answers sit. Set DEJA_INCLUDE_SUBAGENTS=1 for the whole child transcript, or =0 to leave them out entirely. Each child is a session of its own, naming the session that spawned it, so a hit can say which run it came from. Grok Build records the same edge itself, and deja show names the parent and the children either way.
When nothing matches exactly
On zero results deja widens the net in order, and marks how each hit was found:
- Stop-word handling — natural phrasing like "have we dealt with jwt refresh rotation" drops filler words so the content tokens drive the search.
- Word forms —
rotation↔rotatedvia suffix folding. - Typos — close spellings within edit distance.
- Error signature — paste a failing stack trace and deja matches the sessions that hit the same error by its signature, not its words, and can name the command that cleared it last time (
deja fix, MCPfix). - Semantic — if a vector sidecar exists, a rephrased query with no shared words still matches.
Each result carries a tier — exact, close (with the variant), or semantic (with the cosine) — in the CLI, JSON and MCP output.
Semantic recall
deja embed # build a vector sidecar from an embedding endpoint deja "ran out of db connections" # matches "connection pool exhausted"
Resolution: DEJA_EMBED_URL, else a probed Ollama or LM Studio. Authenticated endpoints use DEJA_EMBED_KEY; an HTTPS api.openai.com URL also accepts OPENAI_API_KEY. No endpoint, no semantic layer — search is unchanged.
Natural-language queries
Stop words are dropped, word forms bridge morphology in both directions (failing finds fails), and when no session holds every remaining token deja drops up to two — telling you exactly which (ignoring "why" — no session matches it together with the rest). Zero results now mean the content genuinely is not there.
Search by time
Every message also carries its month and year in the postings, and a query with no exact match reads them: deja "what did we do in may" meets May's sessions structurally, and relative phrases resolve against the moment you ask — a week ago, last month, 10 days ago. They rank the fallback, they never filter, and a query whose words already match something is answered on those words alone — so for a hard cutoff, or to narrow a query that does match, use --since.
Earlier attempts
When an old session and a newer one from the same project match the same ground, the older hit is labeled an earlier attempt with the newer session's date — in CLI output, MCP digests, and as an additive superseded field in --json. History stays; the direction the project moved is visible.
What ranks first
Lexical overlap decides who is a candidate; what settled something decides who wins. Above the raw term match, deja weighs a few signals — none of them able to outrank plain relevance, only to break the ties it leaves:
- The session that concluded, not the one that kept asking. A session that reached a decision outranks one that only discussed the topic, and recall surfaces the decision-carrying line, not just where the words appeared.
- What held over what was backed out. A session whose own text says it reverted an approach and settled nothing else ranks below one that held — the dead end is still there, marked, but it is not the first thing an agent sees. A session that reverted one thing and settled another keeps its place.
- A recorded judgement over a raw guess. A note you promoted outranks the transcript it came from, and a lifecycle state travels with the session on every hit: tried and rejected, replaced by a later decision, marked stale, with the reason and the date. Nothing is deleted — the agent sees what was decided and that it changed.
- What gets reused. A session agents keep pulling back is lifted a little, on the theory that what the machine keeps needing is worth surfacing — bounded, so popularity never buries a stronger match.