Chat sources: per-source control over external agent chat import (Claude/Codex) with a real settings surface
Problem
Screenpipe auto-imports chat history from external agents (~/.claude/projects, ~/.codex/sessions) into the chat sidebar's RECENTS list. The sync:
- starts unconditionally when the chat sidebar mounts —
chat-sidebar.tsxcallsstartExternalChatSync()with no settings gate - scans the last 7 days, up to 100 conversations per source
- installs a live filesystem watcher on those directories, so new Claude/Codex sessions keep appearing in RECENTS in real time
For users who live in both tools, RECENTS becomes mostly imported Claude/Codex sessions and Screenpipe's own chats get buried. There is currently:
- no way to disable the import/sync itself
- no per-source import control (e.g. import Codex but not Claude)
- no "remove imported chats" cleanup action — deleting one imported chat from the sidebar is temporary; the watcher re-imports it on the next transcript change
- disconnecting the AI Apps integrations (MCP/skills) does NOT affect this — the importer reads transcripts straight off disk
What already exists (worth building on)
The sidebar already has the view-level half of this feature. In chat-sidebar.tsx:
RECENTS_SOURCE_FILTER_KEY = "screenpipe:recents-hidden-sources"— a localStorage-backed hidden-sources set- RECENTS header → "⋯" menu → "show in recents" checkboxes (screenpipe / Codex / Claude, with keyboard shortcuts S/C/L)
- layout toggle: group By source vs In one list
- every conversation already carries
importedFrom: { source, importedAt, ... }, so filtering/deletion by source is trivial to implement on existing data
This means the data model and UI idioms are already in place — what's missing is import-level control and a discoverable settings surface.
Proposed Solution
1. A "Chat sources" card in Settings (the missing piece)
Add a card under Settings (AI section, next to AI features) titled Chat sources with one row per source:
Chat sources
Choose which agents' history appears in chat.
● Screenpipe always on
☐ Claude import + live sync
☐ Codex import + live syncEach external row controls import itself, not just visibility. Unchecking a source:
- stops its filesystem watcher (
controller.stop()per-source, or filter targets inwatchTargets()) - skips it in
scanExternalChatHistory - offers a one-time "Remove N imported Claude chats" destructive action — deletes only conversations where
importedFrom.source === "<source>"(safe: imported copies in~/.screenpipe/chats/, never the original transcripts in~/.claude/projects)
Re-checking re-runs the existing scan/import — all the machinery already exists in external-chat-import.ts / external-chat-sync.ts.
2. Make the existing sidebar filter discoverable
The RECENTS "⋯" → "show in recents" checkboxes are already exactly the right UI — but hidden behind a hover-only ghost button that most users will never find. Two cheap fixes:
- always-visible filter icon on the RECENTS header when any source is hidden (with a dot indicator), so the state is visible at a glance
- link the Settings card and the sidebar filter to the same source of truth: the sidebar "show in recents" toggle stays view-only (hide temporarily), while the Settings card controls per import (don't bring them in at all). Gray out / mark "not imported" in the sidebar menu for sources disabled in Settings.
3. Import dialog alignment
import-chats-dialog.tsx already has per-source checkboxes for one-shot imports. Keep it, but have it respect the Settings card: sources disabled in Settings are hidden from the dialog (or shown disabled with a "disabled in Settings → Chat sources" hint).
Implementation notes (from the code)
- Gate point:
startExternalChatSync()inlib/chat/external-chat-sync.ts— accept anenabledSourcesset; filterwatchTargets(home)and thescanExternalChatHistorysources by it. Persist next to the existingRECENTS_SOURCE_FILTER_KEY(or a settings store key likescreenpipe:chat-sources-disabled). ExternalChatSourcetype +importedFrom.sourcetagging already exist on every imported conversation and message — per-source delete is afilter + deleteConversationFilepass.- Claude/Codex scan roots are already isolated per-source functions (
~/.claude/projectsscanner atexternal-chat-import.ts:243,~/.codex/sessionsat:303), so per-source enable/disable slots in cleanly. - Watch out for the live-turn case:
externalTurnActiveByConversationtracks in-progress external turns; disabling a source mid-turn should finish (or cleanly abandon) the active turn rather than dropping state.
Why this matters
The current behavior conflates two different user intents: "I want Screenpipe to see my agent history as searchable context" (the AI Apps integration — MCP/skills) and "I want my agent sessions listed as chats in the Screenpipe sidebar" (this importer). Users who want the first but not the second currently have no supported path, and the mixed RECENTS list degrades the core chat UX for anyone using Claude Code or Codex heavily.
Environment
- macOS, screenpipe app v2.7.x
- heavy Claude Code + Codex usage — RECENTS fills with imported sessions within hours of a clean state
Source: screenpipe/screenpipe