Semantic search over coding-agent conversation history. One persistent HTTP server indexes transcripts as they change and serves hybrid vector and keyword search over MCP.
SessionFlow watches session transcripts from Claude Code and other providers. It embeds every turn with a local MLX model (EmbeddingGemma-300M on Apple Silicon) and exposes the index through MCP tools. Search by meaning, keyword, date range, project, git branch or issue ID.
MCP clients (Claude Code, Codex, OpenCode, Antigravity)
↓ POST /mcp (MCP StreamableHTTP)
SessionFlow HTTP server (SESSIONFLOW_PORT, default 7102)
├── Embedding: EmbeddingGemma-300M via MLX Metal (local)
├── Vectors: Milvus Standalone (HNSW index), or Milvus Lite
├── Keywords: SQLite FTS5 sidecar
├── Search: Reciprocal Rank Fusion of vector and keyword results
└── Watcher: FSEvents → debounce → incremental parse → index
- The embedding model runs on the local GPU through Metal. SessionFlow makes no embedding API calls.
SESSIONFLOW_MILVUS_URIselects Milvus Standalone. When it is unset, SessionFlow uses embedded Milvus Lite.- One server process serves every client. It loads the model once and pools connections.
- A file watcher on
~/.claude/projects/indexes new turns as they are written.
| Tool | Returns |
|---|---|
search_session |
Turns that match a query, scoped to the current project. Omit query to list recent turns. |
search_all_sessions |
Turns that match a query, with project_root, git_branch, provider, date_from, date_to and issue_id filters. |
get_turns |
The turns around one turn of a session. Requires session_id and turn_index from a search result. |
get_session_stats |
Turn, session, branch and provider counts for the index. |
get_issue_timeline |
Every turn that mentions an issue ID, oldest first. Requires issue_id. |
cleanup_sessions |
Deletes turns by age, session ID or git branch, across every project. |
sanitize_index |
A dry-run report of secrets in the index. Removes them with apply and confirm. |
search_all_sessions("deployment decisions", date_from="2026-04-08", date_to="2026-04-08")
search_all_sessions("what broke in production", date_from="2026-04-01")
search_all_sessions(project_root="*")
get_turns(session_id="<session_id from a result>", turn_index=12)
./setup.sh # venv, dependencies, model download, hooksRestart Claude Code after setup to load the MCP server.
| Variable | Default | Description |
|---|---|---|
SESSIONFLOW_MILVUS_URI |
~/.sessionflow/milvus.db |
Milvus URI: http://host:port for Standalone, a file path for Lite. |
SESSIONFLOW_HOST |
127.0.0.1 |
HTTP server bind address. |
SESSIONFLOW_PORT |
7102 |
HTTP server port. |
SESSIONFLOW_MODEL |
embeddinggemma |
Embedding model: embeddinggemma or modernbert. |
SESSIONFLOW_EXPIRE_DAYS |
365 |
Prune turns older than this many days. |
SESSIONFLOW_WATCH |
true |
Turn the transcript file watcher on or off. |
SESSIONFLOW_URL |
http://127.0.0.1:7102 |
Server URL that the hooks call. |
./sessionflow-server.sh start # Start the server and watchdog
./sessionflow-server.sh stop # Stop the server
./sessionflow-server.sh restart # Restart the server
./sessionflow-server.sh status # Check health
~/.sessionflow/sessionflow-launcher.sh start # Hook-safe start through the LaunchAgent
curl http://127.0.0.1:7102/health # Health checkSeveral harnesses can start at the same time, and their SessionStart hooks then race to start the server. The optional LaunchAgent starts SessionFlow at login, before any hook runs. Each harness then connects to the running server.
./sessionflow-server.sh install-agent # Write and bootstrap ~/Library/LaunchAgents/cc.lbruton.sessionflow.plist
./sessionflow-server.sh agent-status # Show the plist and launchctl state
./sessionflow-server.sh uninstall-agent # Boot out and remove the plistThe start, stop, restart and status commands work the same with or without the LaunchAgent. setup.sh offers to install it. Set SESSIONFLOW_INSTALL_AGENT=1 to install it without a prompt.
SessionFlow ingests native structured transcripts. It does not read terminal logs.
| Provider | Status | Source kind | Notes |
|---|---|---|---|
claude_code_cli |
Searchable | claude_code_jsonl (~/.claude/projects/**/*.jsonl) |
Watcher and backfill. Includes Claude Desktop Code-tab sessions. |
claude_cowork_legacy |
Searchable, one-time | claude_cowork_legacy_jsonl (~/Library/Application Support/Claude/local-agent-mode-sessions/*/*/local_*/.claude/projects/**/*.jsonl) |
Legacy Cowork agent-mode sessions. Backfill only, enqueued by hand. |
codex |
Searchable | codex_rollout_jsonl |
Native rollout JSONL. |
opencode |
Searchable | opencode_storage |
Native storage. |
antigravity_cli |
Searchable | antigravity_cli_transcript_jsonl (brain/<id>/.system_generated/logs/transcript.jsonl) |
Sibling .pb artifacts are not parsed. |
antigravity_desktop |
Searchable | antigravity_desktop_transcript_jsonl |
Desktop and IDE transcript JSONL. |
Antigravity replaces Gemini CLI. The .pb (protobuf) artifacts beside Antigravity transcripts have no stable schema, so SessionFlow does not parse them. Legacy Gemini CLI history (legacy_gemini_history) is a source-kind constant for one-time import work. SessionFlow does not ingest it automatically.
Claude Desktop Code-tab sessions, former Cowork projects included, are ordinary Claude Code transcripts under ~/.claude/projects/. They are searchable as claude_code_cli. The claude-code-sessions/**/local_*.json files beside them hold only metadata (title, cliSessionId).
Legacy Cowork agent-mode sessions under ~/Library/Application Support/Claude/local-agent-mode-sessions/ ran Claude Code with a sandboxed config directory. The claude_cowork_legacy provider ingests them. It tags each session with the first folder the user selected (from the sibling local_<id>.json), or unknown. This provider is not in the startup backfill. Run it once:
python cleanup.py backfill enqueue --provider claude_cowork_legacy --mode fullIf Desktop has pruned or moved that folder, set SESSIONFLOW_COWORK_LEGACY_ROOT to a backup copy first.
All embedding is local MLX. Backfill runs under a budget so a long import cannot saturate the GPU. embedding_control.py holds the full list.
| Variable | Default | Purpose |
|---|---|---|
SESSIONFLOW_EMBED_BATCH_SIZE |
16 |
Maximum turns per embedding batch. |
SESSIONFLOW_EMBED_COOLDOWN_MS |
200 |
Sleep between batches. Values below 200 are raised to 200, because the MLX Metal driver crashes under sustained load below it. |
SESSIONFLOW_BACKFILL_MAX_TURNS_PER_RUN |
100000 |
Maximum turns embedded per backfill run. |
SESSIONFLOW_BACKFILL_MAX_FILES_PER_RUN |
100 |
Maximum source files visited per backfill run. |
SESSIONFLOW_BACKFILL_RECENT_DAYS |
14 |
Window for recent mode. |
SESSIONFLOW_BACKFILL_MODE |
recent |
One of recent, incremental or full. |
SESSIONFLOW_BACKFILL_PAUSED |
unset | When true at startup, backfill starts paused. |
Backfill is per provider. Its queue lives in the index state directory and survives a restart.
recentindexes sources modified in the lastSESSIONFLOW_BACKFILL_RECENT_DAYS. It is the default, so recent turns become searchable first.incrementalresumes each source from its last cursor and does not rescan.fullvisits every source for the provider. The per-run turn and file caps still apply.
Maintenance commands (see cleanup.py):
python cleanup.py status # Provider, embedding and backfill snapshot
python cleanup.py status --provider codex # One provider
python cleanup.py backfill status # Queue status
python cleanup.py backfill pause # Pause all providers
python cleanup.py backfill pause --provider codex # Pause one provider
python cleanup.py backfill resume # Resume all providers
python cleanup.py backfill enqueue --provider antigravity_cli --mode recentPause state and queued jobs persist on disk. A restart or a LaunchAgent relaunch resumes the same plan.
The ingestion guard redacts secrets in new turns. cleanup.py sanitize finds secrets that were indexed before the guard caught them. It removes them from the Milvus document field, the FTS content column and the embedding vector. It uses the same detector as the ingestion guard.
A dry run is the default. It reports per-rule counts, the number of affected turns and an audit path, and writes nothing to the index:
python cleanup.py sanitize # Dry run over the whole index
python cleanup.py sanitize --provider claude_code_cli # Scope by provider
python cleanup.py sanitize --project /path/to/repo --since 2026-05-01--apply redacts the text of each affected turn, re-embeds it and overwrites the row. --apply --drop deletes the affected turns instead. Both need --yes; there is no interactive prompt. Without --yes, --apply refuses before any read or write and exits non-zero:
python cleanup.py sanitize --apply --yes # Redact and re-embed in place
python cleanup.py sanitize --apply --yes --drop # Delete affected turnsThe scope flags (--project, --provider, --session, --since) apply to a dry run and to --apply. --drop is valid only with --apply.
Every run writes a JSONL audit trail under ~/.sessionflow/audit/ with mode 0600. It holds rule names, tiers, integer offsets and masked snippets, and never a secret value. Standard output holds counts only.
Removing a secret from the index does not revoke it. Rotate any credential that was indexed. The sanitizer prints this warning on every apply run.
SessionFlow embeds locally only. It has no hosted or OpenAI embedding path, credentials or collections. A hosted path would need its own embedding identity and collection, so that vectors from two models never mix.
- Hybrid search: Milvus vector similarity and FTS5 keyword matches, merged with Reciprocal Rank Fusion.
- HNSW index on Milvus Standalone.
- A pure-Python git root resolver with a cache, so indexing forks no subprocesses.
- Non-blocking startup: the server binds its port first, then loads the model and runs backfill in the background.
- Multi-project search through the
project_rootfilter. date_fromanddate_tofilters onsearch_all_sessionsandget_issue_timeline.- Incremental indexing: byte offsets per transcript, with a checkpoint every 100 files.
SessionFlow started as a fork of mwgreen/claude-code-session-rag. It is an independent project with Milvus Standalone, HNSW indexing, background startup and multi-client hardening.
MIT