You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: Search across my message history to quickly locate past conversations/content ("find what I told opencode before") #41354
Over time I have hundreds of sessions across many projects. When I remember that "I told opencode something important a while ago" (a requirement, a decision, a constraint, a command I typed), there is currently no way to find it:
The TUI/Desktop session list only shows sessions by title/project — no content search.
The only workaround is recalling the approximate session/title, then scrolling through it manually. For long-running threads this is extremely inefficient; for forgotten sessions it's often impossible.
This is especially painful for agent-driven work: users increasingly delegate multi-day tasks, and "what did I tell the agent earlier" becomes a recurring lookup need.
Feature request
Add a content search across the user's message history:
Scope: search my (user) messages — preferably all sessions, with per-project filtering (project:name, date range).
Trigger: a search entry from the session list / project page (e.g. a search box or a /search command in the TUI); shortcut in Desktop.
Constraints: search user messages by default; option to include assistant messages/tool calls; fuzzy match would beat SQL-like exact match for human recall.
Many projects/sessions accumulated daily; data presumably already stored in local session store (opencode.*.dat), so a local index/search over it should be feasible without server-side changes.
Cross-link with #19143 (message search Ctrl+F in Desktop) — same underlying need (find past conversations/content). #19143 is the in-session UI; #41354 the cross-session/history search. Tracking together covers the full "search my opencode history" request. +1, would test on Windows Desktop.
The ask in this thread — semantic search across message history — has no substrate to run on today: transcript storage is keyed by id, no content index of any kind (no FTS/vector table anywhere in packages/core).
Checked on current dev: persistence is now event-sourced SQLite (<data>/opencode.db, bun:sqlite — packages/core/src/database/database.ts). Messages/parts are projections (MessageTable/PartTable, packages/core/src/session/sql.ts) kept current by a projector subscribed to durable MessageUpdated/PartUpdated events published from Session.updatePart.
That makes an opt-in local embedding index incremental and fully off the hot path:
Background embedder subscribes to post-commit durable events — zero changes to turn handling
Rebuild/backfill = replay durable events or scan PartTable; deletion parity already mirrored by the projector
One deterministic anchor chunk per session (title + compaction summaries) sits on top of per-part chunks, so aggregate questions get a high-precision entry point; a hierarchical/graph index over that substrate is a natural follow-up
Provider pluggable, local-first by default (no network, data stays on device); API providers via existing config
Gated behind OPENCODE_EXPERIMENTAL_*, following existing precedent
Why core rather than a plugin: plugins can't subscribe to internal EventV2 durable events (server SSE only), so incremental indexing needs core access.
Positioning vs prior art: #20322/#39772 target native auto-memory/learning; this is mechanical retrieval over the same substrate — different consumer, complementary. Cross-project/cross-session recall comes almost free once indexing exists, detail dropped by compaction (#40614) stays queryable, and subagents get fast lookup without extra model calls.
Out of scope: replacing compaction, remote sync, auto-injecting recalled context into prompts.
I have a working POC on this exact design (verified end-to-end: live incremental indexing, cross-session recall through the tool, deletion parity, clean rebuild on restart). Happy to open a draft PR behind the flag — would that be a useful next step here?
liudongyan13701205717-source commented on Aug 27, 2026
Thanks a lot for the detailed POC! A working local semantic index over the existing SQLite store is exactly the direction I was hoping for. Please go ahead and open a draft PR — I'm happy to help test against real conversation exports and give feedback on indexing performance. Really appreciate you picking this up.
Thanks — really appreciate the green light! Draft PR opened: #45920
It's the POC behind the OPENCODE_EXPERIMENTAL_TRANSCRIPT_RECALL flag: background embedder on durable events, per-session anchor chunks from titles/summaries, and a recall tool. Happy to prioritize indexing-performance feedback or testing against real conversation exports as you'd like.
liudongyan13701205717-source commented on Aug 30, 2026
Thanks for the engagement check. Rebased + non-draft PR opened: #46397
Same design as before (EventV2 durable events, session anchor chunks, per-part chunking, deterministic hash provider), rebased on current dev. Clean commit history (11 files, 499 insertions), zero new typecheck errors in packages/core or packages/opencode, existing loader tests pass.
Ready for feedback on indexing performance or testing against real conversation exports whenever you are.
@liudongyan13701205717-source thanks for the +1 and the design feedback! The previous PR #46397 was auto-closed by the compliance bot (missing PR template sections). Rebased + non-draft PR opened with the full template: #46823
What changed since the auto-close (increment, not new feature):
M7: write metrics immediately (was buffered, never flushed on nssm stop)
M5: LRU cache in search() (28x speedup on cache hit)
M3 + D8: auto-invoke recall on regex trigger or semantic fallback (gated by OPENCODE_RECALL_AUTO_INVOKE, default off)
M6: pluggable OpenAI-compatible provider (default remains hashing, no external service wired)
D8 regex tightening (removed broad triggers like 'antes' and 'decidimos' that caused 20% FP)
Lab validation on real conversation data:
H1 P50 cache hit: 3ms (was full scan, ~85ms)
H2 quality: 100% top-3/5
H3 adoption: 80% with trigger, 60% semantic fallback
H5 FP: 0% (was 20%)
Tested end-to-end with 9router/free-combo + isolated lab DB. Happy to iterate on indexing performance or run against your real conversation exports.
For anyone who needs this before an in-core index lands: it works today from outside the core, because opencode already keeps every session in ~/.local/share/opencode/opencode.db.
deja indexes that database in place (BM25, no LLM, no network) and gives you the lookup from the OP — "what did I tell opencode three weeks ago in another project":
brew install deja-vu # or: npm i -g @vshulcz/deja-vu
deja "the retry budget we agreed on"
deja last --harness opencode # recent sessions, any project
And as an opencode plugin (npm i opencode-deja, or deja install opencode-auto) the agent gets the same search as tools — deja_recall, deja_session, deja_blame — so "didn't I tell you X" is answered from the transcript rather than from memory. It reads the sessions from before it was installed; there is no capture step.
Not a substitute for #46823 in the Desktop UI, and I maintain deja, so weigh that. Mentioning it because the thread reads as if the only path is waiting for core.
Context: I independently opened #53063 with a full-text search proposal before finding this thread (my pre-flight search only covered open PRs, not prior issues — my miss). Consolidating the technical content here and closing mine, since this thread is the right track.
What #53063 added that this thread doesn't have yet — a concrete zero-dependency implementation path:
The gap is confirmed and small to state: the session list query matches search against session.title only; message content is not indexed anywhere.
No ask that wasn't already made above — @liudongyan13701205717-source's three-way question to maintainers from Aug 30 still stands, and #46823's author already paid the compliance-bot toll once. Just adding the cheaper-third-option data point to the track: if maintainer bandwidth is the constraint, the FTS5 slice is small enough to review as a single Phase-1 PR (migration + query + HTTP route, no UI).
liudongyan13701205717-source commented on Oct 4, 2026
ggbdpq's FTS5 zero-dependency path is the right shape for v1 — the session list query only matching session.title is the actual gap, and an external-content session_message_fts over the existing message storage adds no embedding infra, no background worker, and no new dependency.
That said, two points worth threading together before closing this out:
Write-path cost: triggers on every message insert need measuring against the p95 turn latency on a busy session. If the trigger adds even 1-2 ms per write, that compounds across 80K+ parts.
Scope of recall: full-text over message content is table-stakes, but the semantic recall that AllanSantos-DV's PR feat(core): transcript recall index for semantic session history #46823 adds (hybrid FTS5 + semantic with RRF/MMR, gated behind OPENCODE_RECALL_AUTO_INVOKE) is what actually answers "what did I tell opencode three weeks ago" when you don't remember the exact wording.
Practically: the FTS5 table is the dependency-free foundation, and the semantic layer can sit on top as an opt-in later. Both deserve a home in this thread.
Ran the write-path measurement for point 1 (@liudongyan13701205717-source's trigger-cost concern). Methodology: bun:sqlite (same driver family opencode uses), in-memory, external-content FTS5 + AFTER INSERT trigger, synthetic parts (~60 words each, mixed text/reasoning), 80K rows.
baseline insert: 0.46µs/row
with FTS5 trigger: 15.13µs/row (+14.7µs)
a typical turn at ~40 parts pays ~0.6ms total added latency — noise against any realistic p95 turn latency
one-time backfill of 80K existing parts: ~1.2s
MATCH query at 80K rows: ~4ms
Caveats: synthetic schema (opencode's real part rows carry more columns; WAL and on-disk I/O will shift both baselines), but the delta is dominated by FTS5 index construction, so the order of magnitude should hold. Even at 10× this density the trigger doesn't meaningfully move turn latency.
On point 2 — agreed: FTS5 as the zero-dependency v1 floor, semantic recall layered on top as opt-in. If useful, I can prepare the Phase-1 slice (migration + backfill + query + HTTP route) as a PR so the thread has something concrete to review.
Revalidated on V2 dev (c272a63). Still no content index; session.list({search}) is title-only, and #58445 adds a LIKE scan as Code Mode tools. Proposing FTS5-only as v1, as discussed above, scoped to be additive to that PR:
Index only text users and assistants wrote (user text and assistant text parts). No reasoning, tool input/output or shell. Standalone FTS5 table with its own text, bm25 ranking, snippet() excerpts. No embeddings, no background worker, no new dependency.
Kept in sync by triggers on session_message; created lazily on first search (derived data; users who never search pay nothing). Not a drizzle migration, because fresh databases bootstrap from the generated schema and virtual tables are not drizzle-expressible. Looking for guidance on the preferred place for this.
Lesson from our V1 prototype (feat(core): transcript recall index for semantic session history #46850, closed): a score threshold cannot tell relevant from irrelevant, but "none of the query words occur in any transcript" is a reliable signal. In that case the tool returns the most common indexed words so the model can rephrase instead of concluding there is no history. Short EN+PT stopword list; OR of content words ranked by bm25.
Measured on 100K synthetic messages: backfill 1.3 s, rare-word query 1.5 ms, about 55 us added per indexed insert.
Open questions: (1) migration vs lazy creation as above; (2) should cross-project reads be behind a permission or config switch; (3) background vs first-use backfill for databases with about a million messages; (4) should this be the backend for session_list({search}) in feat(core): add session_list and session_read tools #58445 rather than a second tool?
A working local branch exists; I will open a PR once the shape is agreed.
Thanks for the detailed write-up! Being able to search your messages across all sessions to find which one has something you told OpenCode earlier sounds really useful.
You're on OpenCode Desktop 1.18.15, which is OpenCode v1. v1 now only gets fixes for critical bugs (data loss, security problems, crashes with no workaround), so a new feature like this is unlikely to be added there.
New work goes into OpenCode v2 (2.x, the @opencode/cli package), and we'd encourage you to upgrade. To be upfront: as far as I can tell, v2 doesn't have search across session content yet either. If you'd like this in v2, reply here (or open a new request) saying so and that you've tried v2. That way maintainers can consider it alongside related requests:
Closing this as not planned. The report is about OpenCode v1 (Desktop 1.18.15), which only gets fixes for critical bugs, so search across all sessions won't be added there, and we haven't heard back since the last comment.
Thanks again for the detailed suggestion. If you move to OpenCode v2 and still want to search past messages across sessions, please open a new feature request saying you're on v2. Related in-session search issues are #19101 and #19143.
Activity
liudongyan13701205717-source commentedon Aug 15, 2026
Cross-link with #19143 (message search Ctrl+F in Desktop) — same underlying need (find past conversations/content). #19143 is the in-session UI; #41354 the cross-session/history search. Tracking together covers the full "search my opencode history" request. +1, would test on Windows Desktop.
AllanSantos-DV commentedon Aug 23, 2026
The ask in this thread — semantic search across message history — has no substrate to run on today: transcript storage is keyed by id, no content index of any kind (no FTS/vector table anywhere in
packages/core).Checked on current
dev: persistence is now event-sourced SQLite (<data>/opencode.db, bun:sqlite —packages/core/src/database/database.ts). Messages/parts are projections (MessageTable/PartTable,packages/core/src/session/sql.ts) kept current by a projector subscribed to durableMessageUpdated/PartUpdatedevents published fromSession.updatePart.That makes an opt-in local embedding index incremental and fully off the hot path:
PartTable; deletion parity already mirrored by the projectorOPENCODE_EXPERIMENTAL_*, following existing precedentWhy core rather than a plugin: plugins can't subscribe to internal EventV2 durable events (server SSE only), so incremental indexing needs core access.
Positioning vs prior art: #20322/#39772 target native auto-memory/learning; this is mechanical retrieval over the same substrate — different consumer, complementary. Cross-project/cross-session recall comes almost free once indexing exists, detail dropped by compaction (#40614) stays queryable, and subagents get fast lookup without extra model calls.
Out of scope: replacing compaction, remote sync, auto-injecting recalled context into prompts.
I have a working POC on this exact design (verified end-to-end: live incremental indexing, cross-session recall through the tool, deletion parity, clean rebuild on restart). Happy to open a draft PR behind the flag — would that be a useful next step here?
liudongyan13701205717-source commentedon Aug 27, 2026
Thanks a lot for the detailed POC! A working local semantic index over the existing SQLite store is exactly the direction I was hoping for. Please go ahead and open a draft PR — I'm happy to help test against real conversation exports and give feedback on indexing performance. Really appreciate you picking this up.
AllanSantos-DV commentedon Aug 28, 2026
Thanks — really appreciate the green light! Draft PR opened: #45920
It's the POC behind the
OPENCODE_EXPERIMENTAL_TRANSCRIPT_RECALLflag: background embedder on durable events, per-session anchor chunks from titles/summaries, and arecalltool. Happy to prioritize indexing-performance feedback or testing against real conversation exports as you'd like.liudongyan13701205717-source commentedon Aug 30, 2026
跟进:社区提出的 POC PR #45920(本地语义索引 /
OPENCODE_EXPERIMENTAL_TRANSCRIPT_RECALL)已被作者自行关闭(draft 未合并,needs:title也未解决)。这意味着"跨会话/全文搜索"目前仍没有任何代码落地,本需求 track 依旧开放。想确认维护者的态度,三选一即可:
保持本 issue 作为"搜索消息历史"的需求 track。
AllanSantos-DV commentedon Aug 31, 2026
Thanks for the engagement check. Rebased + non-draft PR opened: #46397
Same design as before (EventV2 durable events, session anchor chunks, per-part chunking, deterministic hash provider), rebased on current
dev. Clean commit history (11 files, 499 insertions), zero new typecheck errors inpackages/coreorpackages/opencode, existing loader tests pass.Ready for feedback on indexing performance or testing against real conversation exports whenever you are.
AllanSantos-DV commentedon Sep 2, 2026
@liudongyan13701205717-source thanks for the +1 and the design feedback! The previous PR #46397 was auto-closed by the compliance bot (missing PR template sections). Rebased + non-draft PR opened with the full template: #46823
What changed since the auto-close (increment, not new feature):
Lab validation on real conversation data:
Tested end-to-end with 9router/free-combo + isolated lab DB. Happy to iterate on indexing performance or run against your real conversation exports.
vshulcz commentedon Sep 2, 2026
For anyone who needs this before an in-core index lands: it works today from outside the core, because opencode already keeps every session in
~/.local/share/opencode/opencode.db.deja indexes that database in place (BM25, no LLM, no network) and gives you the lookup from the OP — "what did I tell opencode three weeks ago in another project":
And as an opencode plugin (
npm i opencode-deja, ordeja install opencode-auto) the agent gets the same search as tools —deja_recall,deja_session,deja_blame— so "didn't I tell you X" is answered from the transcript rather than from memory. It reads the sessions from before it was installed; there is no capture step.Not a substitute for #46823 in the Desktop UI, and I maintain deja, so weigh that. Mentioning it because the thread reads as if the only path is waiting for core.
feat(core): transcript recall index for semantic session history
AllanSantos-DV commentedon Sep 5, 2026
Hi! Quick ping — no human reply since 2026-09-02, so heads-up that the draft is still live with updated and locally tested code:
PR #46850 (OPEN draft, updated 2026-09-05, 9 commits): \eat(core): transcript recall index for semantic session history
#46850 — Closes #41354
What's new since #46823/#46397: sprint 5 per-session call cap (M7), sprint 6 hybrid search (FTS5 + semantic with RRF/MMR), M4 endpoint re-add + wire-error fix, auto-invoke flag \OPENCODE_EXPERIMENTAL_RECALL_AUTO_INVOKE, typed errors, lab E2E H1/H3/H4/H5 PASS (H2 80% top-1 by content, dataset mismatch documented).
GW test: P50 11ms / P95 59ms, 18.96 KB/part, 0% FP. Lab isolation verified, DB real untouched.
Let me know if you'd like a rebase or a narrower scope — happy to adjust to the preferred design direction.
ggbdpq commentedon Oct 4, 2026
Context: I independently opened #53063 with a full-text search proposal before finding this thread (my pre-flight search only covered open PRs, not prior issues — my miss). Consolidating the technical content here and closing mine, since this thread is the right track.
What #53063 added that this thread doesn't have yet — a concrete zero-dependency implementation path:
searchagainstsession.titleonly; message content is not indexed anywhere.session_message_ftsvirtual table over the existing message storage needs no new dependency, no embedding infrastructure, and no background worker: one migration + backfill, then either triggers or a write-path hook (both sketched in session: full-text search across session message content (only titles are searchable today) #53063).No ask that wasn't already made above — @liudongyan13701205717-source's three-way question to maintainers from Aug 30 still stands, and #46823's author already paid the compliance-bot toll once. Just adding the cheaper-third-option data point to the track: if maintainer bandwidth is the constraint, the FTS5 slice is small enough to review as a single Phase-1 PR (migration + query + HTTP route, no UI).
liudongyan13701205717-source commentedon Oct 4, 2026
ggbdpq's FTS5 zero-dependency path is the right shape for v1 — the session list query only matching
session.titleis the actual gap, and an external-contentsession_message_ftsover the existing message storage adds no embedding infra, no background worker, and no new dependency.That said, two points worth threading together before closing this out:
OPENCODE_RECALL_AUTO_INVOKE) is what actually answers "what did I tell opencode three weeks ago" when you don't remember the exact wording.Practically: the FTS5 table is the dependency-free foundation, and the semantic layer can sit on top as an opt-in later. Both deserve a home in this thread.
ggbdpq commentedon Oct 4, 2026
Ran the write-path measurement for point 1 (@liudongyan13701205717-source's trigger-cost concern). Methodology: bun:sqlite (same driver family opencode uses), in-memory, external-content FTS5 +
AFTER INSERTtrigger, synthetic parts (~60 words each, mixed text/reasoning), 80K rows.MATCHquery at 80K rows: ~4msCaveats: synthetic schema (opencode's real part rows carry more columns; WAL and on-disk I/O will shift both baselines), but the delta is dominated by FTS5 index construction, so the order of magnitude should hold. Even at 10× this density the trigger doesn't meaningfully move turn latency.
On point 2 — agreed: FTS5 as the zero-dependency v1 floor, semantic recall layered on top as opt-in. If useful, I can prepare the Phase-1 slice (migration + backfill + query + HTTP route) as a PR so the thread has something concrete to review.
AllanSantos-DV commentedon Oct 24, 2026
Revalidated on V2
dev(c272a63). Still no content index;session.list({search})is title-only, and #58445 adds aLIKEscan as Code Mode tools. Proposing FTS5-only as v1, as discussed above, scoped to be additive to that PR:snippet()excerpts. No embeddings, no background worker, no new dependency.session_message; created lazily on first search (derived data; users who never search pay nothing). Not a drizzle migration, because fresh databases bootstrap from the generated schema and virtual tables are not drizzle-expressible. Looking for guidance on the preferred place for this.session_list({search})in feat(core): add session_list and session_read tools #58445 rather than a second tool?opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up! Being able to search your messages across all sessions to find which one has something you told OpenCode earlier sounds really useful.
You're on OpenCode Desktop 1.18.15, which is OpenCode v1. v1 now only gets fixes for critical bugs (data loss, security problems, crashes with no workaround), so a new feature like this is unlikely to be added there.
New work goes into OpenCode v2 (2.x, the
@opencode/clipackage), and we'd encourage you to upgrade. To be upfront: as far as I can tell, v2 doesn't have search across session content yet either. If you'd like this in v2, reply here (or open a new request) saying so and that you've tried v2. That way maintainers can consider it alongside related requests:todowrite/todoreadcannot be enabled for subagents via agentpermissionconfig #19101: Find within the currently open session (Cmd/Ctrl+F), which would pair with thisIf you think this is critical, please reply here too.
opencodejr commentedon Oct 29, 2026
Closing this as not planned. The report is about OpenCode v1 (Desktop 1.18.15), which only gets fixes for critical bugs, so search across all sessions won't be added there, and we haven't heard back since the last comment.
Thanks again for the detailed suggestion. If you move to OpenCode v2 and still want to search past messages across sessions, please open a new feature request saying you're on v2. Related in-session search issues are #19101 and #19143.