Repository navigation
feat(ai-vault): add full-transcript content search to Agent Session History - #18393
cuijieshan3-collab wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthroughAdds bounded AI Vault transcript search with query validation, request limits, snippet extraction, file scanning, abort handling, and truncation reporting. Exposes the search through Electron IPC and typed preload APIs, with an empty web implementation. Adds renderer hooks and controls for on-demand searches, filters sessions to matches, and displays highlighted transcript snippets with localized labels and theme-specific colors. Adds shared and filesystem search tests. Merge Risk: 🟡 Moderate · up to Transcript search adds local filesystem scanning and transcript snippets to session history. It is not ready to merge without accepting or resolving the remaining risk that a renderer-supplied request can read an arbitrary accessible local path rather than only an owned session transcript. 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
Full details: Description checkExplanation The description covers the change, rationale, issue, testing, review focus, compatibility, security, and checklist. However, the required visual proof for this UI change is not attached; it only states that screenshots are being attached.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Team
Run ID: c7d444dc-8856-4db2-bb4a-f26d763d88e6
📥 Commits
Reviewing files that changed from the base of the PR and between a9f2fbb and fe9be324d29f03ebb050d8f812b462aee1fffaa6.
📒 Files selected for processing (16)
src/main/ai-vault/session-transcript-search.test.tssrc/main/ai-vault/session-transcript-search.tssrc/main/ipc/ai-vault-transcript-search-handler.tssrc/main/ipc/register-core-handlers/register-core-handlers.tssrc/preload/api/ai-vault-api.tssrc/preload/api/ai-vault-bridge.tssrc/renderer/src/assets/main.csssrc/renderer/src/components/right-sidebar/AiVaultPanel.tsxsrc/renderer/src/components/right-sidebar/AiVaultPanelHeader.tsxsrc/renderer/src/components/right-sidebar/AiVaultSessionRow.tsxsrc/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsxsrc/renderer/src/components/right-sidebar/ai-vault-transcript-deep-search.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/web/preload-api/web-ai-vault-api.tssrc/shared/ai-vault-transcript-search.test.tssrc/shared/ai-vault-transcript-search.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| // is the point. A stale result from a superseded query must not show. | ||
| const transcriptMatchById = useMemo( | ||
| () => | ||
| deepSearchStatus === 'done' && deepSearchQuery === query ? matchBySessionId(sessions) : null, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Invalidate a completed search when the input is cleared or changed.
Query-text equality does not prove that the current input has been searched. After users clear the normal search field and enter the same text again, the old result becomes active and the search action stays hidden.
src/renderer/src/components/right-sidebar/ai-vault-transcript-deep-search.ts#L189-L189: track an input revision, or clear deep-search state when the input changes, before deriving active transcript matches.src/renderer/src/components/right-sidebar/AiVaultPanelHeader.tsx#L203-L205: use that current-search state when deciding whether to hide the transcript-search action.
📍 Affects 2 files
src/renderer/src/components/right-sidebar/ai-vault-transcript-deep-search.ts#L189-L189(this comment)src/renderer/src/components/right-sidebar/AiVaultPanelHeader.tsx#L203-L205
| if (!raw || typeof raw !== 'object') { | ||
| continue | ||
| } | ||
| const filePath = typeof raw.filePath === 'string' ? raw.filePath.trim() : '' |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Trace every IPC caller and identify any sender authorization or path-root validation.
rg -n -C 6 'aiVault:searchTranscripts|searchAiVaultTranscripts' src/main src/preload src/renderer
rg -n -C 4 'realpath|resolve\(|relative\(|startsWith\(|filePath|sessionId' src/main src/sharedRepository: stablyai/orca
Length of output: 50370
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- IPC handler ---'
sed -n '1,80p' src/main/ipc/ai-vault-transcript-search-handler.ts
printf '%s\n' '--- scanner path use ---'
sed -n '1,220p' src/main/ai-vault/session-transcript-search.ts
printf '%s\n' '--- preload exposure ---'
sed -n '1,100p' src/preload/api/ai-vault-bridge.ts
sed -n '1,100p' src/preload/api/ai-vault-api.ts
printf '%s\n' '--- relevant path/session definitions ---'
rg -n -C 5 'filePath|sessionId|searchTranscripts|canUseLocalAiVaultSessionPathActions|executionHostId' \
src/main/ai-vault src/main/ipc src/preload/api src/renderer/src/components/right-sidebar/ai-vault-transcript-deep-search.tsRepository: stablyai/orca
Length of output: 50370
🏁 Script executed:
#!/bin/bash
set -euo pipefail
log=/tmp/coderabbit-shell-logs/shell-output-7PdO4q
sed -n '1,260p' "$log"
printf '%s\n' '--- exact scanner file operations ---'
rg -n -C 12 'stat\(|createReadStream|open\(|readFile|searchTranscriptFile' \
src/main/ai-vault/session-transcript-search.ts
printf '%s\n' '--- exact IPC/preload references ---'
rg -n -C 8 'aiVault:searchTranscripts|searchTranscripts' \
src/main/ipc/ai-vault-transcript-search-handler.ts \
src/preload/api/ai-vault-bridge.ts \
src/preload/api/ai-vault-api.tsRepository: stablyai/orca
Length of output: 16963
Information Disclosure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor
Reachability: External · Exploitability: Moderate
Restrict filePath to vault-owned transcripts.
The IPC handler accepts renderer-supplied paths, and the scanner passes them directly to stat() and createReadStream(). Validate each canonical path against the allowed transcript roots before opening it, or resolve paths from a main-process session registry.
There was a problem hiding this comment.
Important
The "full-transcript" search only ever reads the first 2 MB and last 1 MB of each transcript — a match anywhere in between is silently missed with no partial-results indicator. Details are in the inline comment on the head/tail windowing.
Reviewed changes
- Shared contract (
src/shared/ai-vault-transcript-search.ts) — request clamp/dedupe, query/path size bounds, and a hit-front snippet windower, plus a composite(agent, filePath)join key. - Main-process searcher (
src/main/ai-vault/session-transcript-search.ts) — bounded head/tail streaming read with a 5 s deadline andAbortSignal; missing files skipped silently, unreadable ones surfaced as issues. - IPC + preload wiring — additive
aiVault:searchTranscriptschannel registered end-to-end with a no-op web fallback. - Renderer results view — deep-search hook joins matches back to rows, shows only matched sessions while a search is active, and highlights the term per-row with theme-aware tokens.
ℹ️ Nitpicks
matchCountis a count of matching lines, not occurrences, but the per-row badge and the header title ("Sessions matching inside transcript contents") read as an occurrence count. A line containing the term ten times counts as1. Consider renaming tomatchedLineCountor wording the UI accordingly.- No renderer tests cover the new deep-search hook, the header show/clear state machine, or
HighlightedSnippet; the 20 added tests cover only the shared contract and the main searcher. Given the PR body calls out the button/count/clear interaction as a focus area, a small renderer test would pin that logic.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
Add a deep-search affordance to Agent Session History that searches the conversation body (not just title/cwd metadata) so users can find "the session where the agent fixed X" months later. Requests are narrowed to local sessions and streamed through bounded head/tail file windows with a 5s deadline and an AbortSignal, so a large vault never stalls the main process. SSH/remote hosts and the web build degrade gracefully. Implementation notes - shared contract (clamp, dedupe, snippet extraction) in src/shared/ai-vault-transcript-search.ts - bounded streaming searcher in src/main/ai-vault/session-transcript-search.ts (missing files skipped silently, tail-first then head window) - new additive IPC channel aiVault:searchTranscripts wired through preload and the web fallback - renderer hook + panel/row/virtual-list wiring; results that fail the metadata filter still surface alongside a transcript snippet - 19 unit tests for the shared contract and the main-process searcher
fe9be32 to
ba49131
Compare
|
Thanks @coderabbitai and @pullfrog — all six findings are addressed in ba49131:
Typecheck (node + web), oxlint, oxfmt, |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The delta since the prior review is the review-fix hardening: the silent head/tail middle-skip is now surfaced, and the renderer request-cap truncation is no longer silent.
- Partial-coverage signal —
searchTranscriptFilenow returns afullyScannedflag (fileSize <= HEAD + TAIL), andsearchAiVaultTranscriptsfolds any!fullyScannedintotruncated, so a mid-transcript hit on a large file no longer reads as an honest zero. - Hard byte budget — the head window passes
endtocreateReadStream, so a single record larger than the window cannot be buffered past the budget. - Renderer truncation —
localSearchRequestsreportstruncatedwhen eligible local sessions exceed the 250 cap, and the panel folds main-processtruncatedandissuesinto the same signal. - Results-view state machine — zero-hit searches stay in the results view, and deep-search state resets when the input moves off the searched query (with a
runIdRefguard against stale responses). - Tests — regression coverage for mid-file truncation, the giant-record bound, and request-count truncation.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Team
Run ID: 38ac12bc-8fd5-4921-8ad3-2380667e8ed3
📥 Commits
Reviewing files that changed from the base of the PR and between fe9be324d29f03ebb050d8f812b462aee1fffaa6 and ba49131.
📒 Files selected for processing (5)
src/main/ai-vault/session-transcript-search.test.tssrc/main/ai-vault/session-transcript-search.tssrc/main/ipc/ai-vault-transcript-search-handler.tssrc/renderer/src/components/right-sidebar/AiVaultPanel.tsxsrc/renderer/src/components/right-sidebar/ai-vault-transcript-deep-search.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/ai-vault/session-transcript-search.ts
- src/main/ai-vault/session-transcript-search.test.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Surfaced head/tail partial coverage as
truncated—searchTranscriptFilenow returns afullyScannedflag, and the sweep setstruncatedwhenever any transcript's middle was skipped (plus a post-loop re-check for a deadline hit inside the final window). - Enforced the head window's byte budget in the stream —
createReadStreamnow setsend: window.start + byteBudget - 1, and the budget check moved after the line match so a partial final line is still searched. - Reported the renderer's 250-request cap —
localSearchRequestscounts eligible candidates and flags when the cap dropped any, folded intodeepSearchTruncated. - Reset deep-search state on input change — a
useEffectclears results when the query moves off the searched text, so query equality can no longer resurrect stale results. - Kept the results view for zero-hit searches — the panel stays on the matched list with an honest count of zero instead of falling back to metadata matches.
- Corrected the handler comment — it now states normalization bounds payload size and request count only, matching the other read-only vault handlers.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
|
Tip For best results, initiate chat on the files or code changes.
🐇 ✅ If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! You are interacting with an AI system. |
Deep-search results were derived from the raw session list, so disabled agents, scope selection, hide-empty, and the selected sort stopped applying once a transcript search completed, and filtered-out sessions still consumed the 250-request cap. Build the deep-search candidate list from the same view filters with an empty text query: agent, scope, hide-empty, and sort stay active in the results view, only the metadata text predicate is bypassed so transcript-only hits remain visible, and the request cap is spent on sessions the user can actually see.
|
Fixed in 694c2a0 — deep-search results now keep the non-query view filters active.
|
There was a problem hiding this comment.
ℹ️ No critical issues — one minor suggestion inline.
Reviewed changes
The delta since the prior pullfrog review is a single renderer fix that routes the deep-search request through the panel's non-query view filters.
- View-filtered search candidates —
AiVaultPanelsplits the metadata-filtered list fromcandidates(same non-query filters — agents, scope, hide-empty, sort — withquery: '') and passescandidatesto the deep-search hook, so transcript results respect the active view settings and the 250-request cap is spent only on visible sessions.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
… live view The results view now follows the live candidate list, but the match-count badge stayed frozen at search time and a broadened filter silently omitted sessions that were never searched. Track the request keys each run actually sent: the badge counts the matches joined to the current candidates (always agreeing with the visible rows), and a local candidate that entered the view unsearched folds into the `truncated` flag instead of passing for a complete result. The search itself is not re-run and existing results are not reset.
|
Fixed in 862e553 — the badge and the truncation flag now track the live view instead of the frozen search-time state:
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The delta since the prior pullfrog review is a single renderer fix that keeps the deep-search results view self-consistent as the candidate list changes after a search has run.
- Live-synced badge and truncation —
useAiVaultTranscriptDeepSearchPanelnow recordssearchedKeys(the request keys actually sent), recomputesdeepSearchMatchCountfrom matches joined to the current candidates (transcriptMatchById.size) instead of the frozenstate.matchByKey.size, and flags astruncatedany local session that entered the view unsearched. This closes the earlier desync where broadening a non-query filter could silently omit never-searched sessions and the badge could disagree with the visible rows.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
|
Superseded by the merged transcript-search implementation: #20029 (engine), #20277 (contract/transports), #20516 (scanner lifecycle), and #20580 (panel). The linked issue #18397 is now satisfied: searching conversation content beyond the preview returns sessions with safely highlighted matching snippets. The shipped architecture uses a host-owned persistent index instead of this PR's separate head/tail scanner and renderer-path IPC. Verified the current engine/panel/highlight/lifecycle coverage on main This is supersession, not a claim that every branch detail landed: the separate deep-search button and per-row occurrence count are not in the current UI. An occurrence-count enhancement can be proposed separately if still desired. Closing this alternate implementation without merging; thanks for the bounded-search and highlighting work. |




ELI5
Agent Session History only lets you filter sessions by metadata (title, cwd,
newest preview). If you remember what the agent did but not the session's
title, you were out of luck. This adds a deep search: type a word, search
inside the actual conversation transcripts, and see exactly which sessions
contain it — with the match highlighted in yellow right in the list.
What Changed
src/shared/ai-vault-transcript-search.ts): clamps /dedupes requests, and extracts a windowed snippet with the hit kept near the
front so narrow rows never truncate the keyword away.
src/main/ai-vault/session-transcript-search.ts):reads local transcripts in head/tail file windows with a 5s deadline and an
AbortSignal; a missing file is skipped silently, never shown as an error.aiVault:searchTranscripts, wired end-to-end(main → preload → renderer) with a no-op web fallback; SSH/remote hosts
degrade to empty results instead of pretending to work.
active (no clutter), with per-row match count + yellow theme-aware keyword
highlight (
--vault-search-match-*CSS variables adapt to light/dark).The deep-search button reappears whenever the current keyword is not the one
whose results are on screen, so re-searching after clearing works.
Non-query view filters (disabled agents, scope, hide-empty, sort) also stay
active in the results view, the request cap is spent on visible sessions,
and the match count / truncation flag track the live view (a session that
enters the results view unsearched is flagged, never passed off as covered).
truncated; the stream enforces its byte budget with a hardendso a single record larger than the window cannot bypass it; a zero-hit transcript search stays in the results view; deep-search state resets when the input changes.Why
The vault list filter only matches metadata, so sessions can only be found if
you remember their title. Full-transcript search closes the gap for "find the
session where the agent fixed X". It reads local files only, bounds every
search call (byte windows + wall-clock deadline) so a vault with hundreds of MB
of transcripts never stalls the main process, and the additive IPC channel
keeps wire/remote compatibility.
Linked Issue
Fixes #18397
Tracked via feat(ai-vault): full-transcript content search for Agent Session History.
Visual Proof
UI change (new search affordance + highlighted matches). BEFORE/AFTER screenshots
are being attached by the contributor — the PR is opened ahead of them so review
can start. The feature is exercised manually via
pnpm dev.Testing
pnpm vitest(20 new tests, spanning the shared contract, the searcher'swindow/deadline/missing-file behavior, and the request normalization),
pnpm tc:node,pnpm tc:web,pnpm oxlint,pnpm oxfmt, andpnpm check:code-quality:changedall pass. Localization catalog/extraction/coverage remain verified. Platform coverage: changes are local-FS-only with
deliberate no-op degradation for SSH/remote/mobile/web; the bigger desktop
suite's residual failures are pre-existing flakes unrelated to this change.
AI Disclosure
This change was written with assistance from Claude (Anthropic, via Claude Code).
Review
Focus areas: snippet windowing (hit-at-front), the main-process searcher's
bounded I/O size and deadline, and the results-view interaction (button /
count / clear states across light+dark).
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Security: reads transcript files only for paths the renderer already holds from
a successful vault scan. Like the other read-only vault handlers, the main
process trusts the renderer's paths — normalization bounds the payload size and
request count, it does not enforce path containment. Cross-platform: no platform-specific
code — the searcher uses
node:fsanchors (start offsets, byte budgets) validon Windows, WSL and Linux. Remote SSH: non-local sessions are excluded from
requests, so nothing is attempted against remote hosts. Performance: bounded
per-file byte windows cap main-process I/O per call. Backwards compatibility:
the IPC channel is additive and isolated in its own handler module.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)