PluginWorld
Se

sessionrelay

MCP

Session-level (not fragment) local memory for AI coding agents: passively captures Claude Code, Codex, ZCode and Qoder conversations into one SQLite store, with provenance-cited retrieval and a human-only right to be forgotten.

@EwanJasper · v0.6.0 · MIT License · updated 9d ago

SECURITY

B

SCORE

69

STARS

▲ 2

PLUG IN

git clone https://github.com/EwanJasper/SessionRelay.git

See the README to configure this MCP server

README

SessionRelay 会话接力

English | 简体中文

CI npm downloads Node MCP Platform Semantic License

A local memory layer that belongs to your project — not to any vendor. Memory is always complete. Retrieval is always yours to shape.

You spent three days settling an architecture with your AI. That shouldn't vanish when the window closes. It belongs to the project — and to whoever picks it up next.

📖 中文文档:README.md

60-second demo

SessionRelay demo

The GIF is silent — full narrated versions (English / 中文, plus a 9:16 vertical cut) are playable directly on the Releases · v0.2.2 page.

What it solves

Pain point SessionRelay's answer
Amnesia across time: you ask a new session "what did we decide last week?" — the AI has no idea Search historical sessions on demand, with mandatory provenance
Fragmentation across tools: the plan was settled in Claude Code; ZCode knows nothing Multi-source, one database — any agent queries the same memory via MCP
Handoff across people: you spent 3 days with your AI; your teammate inherits "go read the code" Export a .hop handoff package — their AI instantly owns the full context

The architectural difference vs. claude-mem: session-level storage

This is the core design decision — not a feature difference, a storage-granularity difference:

Fragment memory (claude-mem style) SessionRelay
Unit of storage compressed shards, mixed across sessions complete sessions (title / timeline / source / status / decisions)
What the AI sees "you once said..." shards "ZCode session Database Selection, Aug 20, message 12..."
Hallucination risk high: context pollution + compression distortion + no provenance structurally suppressed: mandatory provenance + on-demand queries + jump back to the original text

Three anti-hallucination commitments (architecture, not features):

  1. Mandatory provenance — every answer must cite which session, which message, which tool. Traceable sources leave no room for confident guessing.
  2. Pull, not push — the AI queries only when needed. Clean context, no ambient memory pollution.
  3. Original text, always — the AI gets the full raw conversation, not a pre-chewed summary. The "why" survives.

Architecture overview

Everything you discuss with any AI tool flows into one project-level database; you, your AI, and your teammates can all query it. Switch tools without losing memory. Switch people without losing context.

graph TB
    subgraph Sources["🖥️ Your AI coding tools (each in its own silo)"]
        CC["Claude Code<br/>JSONL files"]
        ZC["ZCode<br/>SQLite database"]
        CX["Codex<br/>JSONL files"]
        QD["Qoder<br/>JSONL files"]
        TR["Trae<br/>partial*"]
    end

    subgraph Relay["🧠 SessionRelay (local memory layer)"]
        DAEMON["daemon (watch)<br/>auto-sync every 30s"]
        DB[("relay.sqlite<br/>unified memory")]
        EXTRACT["structured extractors<br/>decisions · topics · summaries · key exchanges"]
    end

    subgraph Consumers["👥 Who queries the memory"]
        YOU["You (CLI)<br/>search · decisions · history"]
        AGENT["AI agents (MCP)<br/>16 tools"]
        TEAM["Teammates (.hop handoff)<br/>export → import"]
    end

    CC -->|adapter| DAEMON
    ZC -->|adapter| DAEMON
    CX -->|adapter| DAEMON
    QD -->|adapter| DAEMON
    TR -.->|user prompts only| DAEMON
    DAEMON --> DB
    DB --> EXTRACT
    DB --> YOU
    DB --> AGENT
    DB --> TEAM

    style Relay fill:#1a1a2e,color:#e94560
    style Sources fill:#16213e,color:#fff
    style Consumers fill:#0f3460,color:#fff

* Trae encrypts assistant replies end-to-end; only user prompts are readable. Tool not on the list? Write a custom adapter — zero core changes.

Memory lifecycle

Every session passes through a two-phase verdict. Better to wait than to crystallize an unfinished conversation:

stateDiagram-v2
    [*] --> active: conversation starts / daemon captures
    active --> pending_end: 10 min without new messages
    pending_end --> active: you resume (revival)
    pending_end --> confirmed: 6 h of silence
    active --> active: new messages appended
    confirmed --> active: new message (rollback + re-extraction)
    confirmed --> archived: srelay archive
    archived --> active: new message arrives (revival)

    confirmed: ✅ Confirmed
    confirmed: decisions/topics extracted
    confirmed: enters the main full-text index

    archived: 📦 Archived
    archived: skeleton kept (decisions/topics)
    archived: bodies released (99.4% space)

Why the pending stage exists: conversations resurrect (you --resume the next morning). Crystallize too early and the extracted decisions are incomplete. Pending is a 6-hour buffer; even after confirmation, appended messages trigger a rollback and re-extraction. Raw session files remain the single source of truth — the database can always be rebuilt with srelay rebuild.

Core capabilities

🖥️ Passive capture (zero friction)

  • Five-source adapters: Claude Code · ZCode · Codex · Qoder (full) + Trae (partial: user prompts only) + custom adapters (drop in one JS file, zero core changes)
  • Three privacy modes: full (default) / meta (metadata only) / off (manual only)
  • .sessionrelayignore hard boundary: privacy exclusions apply to auto-capture, manual save, and export alike
  • Two-phase verdict: active → pending_end → confirmed, auto-rollback on resume; raw session files are the single source of truth (the DB is always rebuildable)
  • Backfill: srelay init backfills the last 30 days by default; srelay sync --backfill all imports your full history

🔍 Chinese-optimized search

  • jieba tokenization + SQLite FTS5 dual indexes (gated by six Chinese acceptance cases)
  • Session-level AND coverage with OR fallback: compound words split ("认证方案" → 认证 + 方案), quoted phrases match exactly ("按月分区")
  • Every result carries a mandatory provenance block (session ID / source tool / date / message numbers / snippet)
  • Semantic search (optional): srelay semantic enable — paraphrased queries now hit ("登录" ↔ "authentication", "很卡" ↔ "performance"). Local CPU inference (bge-small-zh), zero cloud; literal hits always rank first and are never replaced — semantic only appends, flagged viaSemantic, capped at top-5. Not enabled = byte-identical behavior to pure literal search. Measured: synonym-query miss rate 33% → 0%

🧹 Right to be forgotten (srelay forget)

Deletion belongs to humans — AI / MCP tools have zero delete power (tool list stays at 15, forever):

srelay forget a3f8c2d1        # preview (what gets removed vs kept)
srelay forget a3f8c2d1 --yes  # execute (irreversible)
srelay forget --history       # audit of what was forgotten when
  • Rule of thumb: aging & disk space → archive; make one conversation disappear forever → forget
  • Dual anti-resurrection barriers: a precise .sessionrelayignore rule (survives rebuild) + a tombstone table — the raw file stays on disk but is never re-ingested
  • Prefix ambiguity guard: multiple matches are listed and rejected, never silently picking one

🤖 MCP Server (16 tools: 9 read + 7 write-domain)

Once your AI agent connects via MCP, it stops being amnesiac in this project:

9 read tools — questions your AI can finally answer
Tool Question it answers
search_sessions "Did we ever discuss X?" (Chinese/English full-text + metadata filters)
get_session_detail "What exactly did we say?" (full messages, ranged, role-filtered)
list_sessions "What topics has this project covered?"
get_decisions "Why X instead of Y?" (all confirmed decisions, with provenance)
get_file_history "Why is this file written this way?" (cross-session file discussion history)
suggest_related_sessions "What else relates to this discussion?" (algorithmically discovered, explainable reasons)
get_unresolved "What's still undecided?" (open questions)
get_stats "How big is the memory?" (sessions/sources/size)
set_scope Retrieval-boundary escape hatch
7 write-domain tools — the AI is no longer just a reader
Tool Capability Safety boundary
annotate_session tag sessions / write human summaries metadata only, never rewrites conversations
save_note AI records conclusions as notes (decision phrasing enters the decision library) source=note, identifiable and auditable
export_handoff produce a .hop handoff package read-only export
import_handoff import a package (sha256 verified + normalized) quarantine by default (bodies pending release)
release_quarantine release quarantined message bodies explicit call required
link_sessions relate sessions (continues/related/pinned) links are queryable and revocable
get_linked_sessions bidirectional link queries read-only

Write-domain principles (D21): bypass the write path (never touches the state machine) · import defaults to quarantine · notes are traceable — every AI write is identifiable, auditable, revocable.

🖥️ Local dashboard (srelay ui, new in 0.6.0)

A third thin presentation layer: CLI, MCP, and the browser share the same query functions — zero core changes, zero new dependencies.

  • Registry overview: cards for every registered project — session composition, source distribution, disk usage, daemon liveness
  • Project detail: interactive growth chart (messages + active-sessions dual metric, click a bar to drill into that day), full-text filter, health cards with the daemon log tail
  • Session drill-down: metadata, decisions, open questions, paged message flow, and in-session keyword search — click a hit or a decision to jump to that message, highlighted
  • First write op — session forget: the exact same core as CLI srelay forget (transactional delete + tombstone + ignore-rule anti-resurrection + audit), behind a confirmation dialog; message-level deletion deliberately not offered (sessions are immutable, forgetting is session-granular)
  • Security model: off by default · binds 127.0.0.1 only · one-time token + Host validation (anti DNS-rebinding) · never returns CORS headers · foreground lifecycle (Ctrl-C stops it)
srelay ui    # start the local dashboard (random port + one-time token, opens the browser)

🎯 Scope: retrieval boundaries

  • Tier B: scope.json project contract (shared by CLI and MCP), intersection semantics — can only narrow
  • Tier A: auto-scope backstop (MCP-side, recent-30-days configurable) against context pollution
  • attach: mount specific historical sessions before starting new work (highest-priority predicate)
  • Hot reload: scope changes take effect on the next call

📦 HOP handoff protocol (hop/1.0, open format)

  • Standalone protocol spec (MIT, vendor-neutral, third-party readers welcome)
  • Per-file sha256 integrity (any tampering rejects the whole package)
  • Default secret redaction: AWS keys / private keys / Bearer tokens / password assignments / DB connection strings, with a redaction report
  • Quarantined import: metadata and summaries first; bodies released one by one — a structural defense against prompt injection
  • HANDOFF.md generated automatically (decision table / files involved / open questions / timeline / signature footer)
  • Cross-project import: packages land in any project path or name (auto-normalized, origin_project kept for provenance)

Quick start

Requires Node ≥ 22 (Windows / macOS / Linux):

# Option 1: npm (recommended)
npm install -g @ewanjasper/sessionrelay

# Option 2: zero-install trial
npx @ewanjasper/sessionrelay init

# Option 3: from source
git clone https://github.com/EwanJasper/SessionRelay.git
cd SessionRelay && npm install && npm run build && npm link

Initialize a project

cd your-project
srelay init                  # initialize + backfill last 30 days (last month searchable within a minute)
srelay sync --backfill all   # or backfill your entire history (old sessions auto-confirm + extract decisions)

Daily use

srelay search 中文关键词 [--topic --source --since --json]
srelay decisions         # all confirmed decisions, with provenance
srelay history src/db/   # which sessions discussed this file
srelay watch --install-service  # register the daemon (Windows), starts at login
srelay doctor            # environment self-check

Team handoff

srelay export --all      # handoff package (redacted by default) → send to a teammate
# The teammate, in any project root:
srelay import xxx.hop --from your-name

👉 Full walkthrough (five scenarios: project handoff / onboarding / safe import / cross-project migration / periodic archiving): Import & Export Guide

⚠️ Why the daemon is not optional

ZCode physically deletes old messages on context compaction (measured: one compaction deleted 3,976 messages from a long session). The daemon syncs every 30 seconds so messages land in the database before deletion.

Without the daemon: anything the AI compacts between two manual syncs is permanently lost.

srelay watch --install-service   # Windows registry autostart (no admin required)
srelay watch --foreground        # macOS/Linux foreground (service registration in progress)

Daemon overhead: ~0% idle CPU (event-driven), ~80MB memory (resident Node.js), negligible incremental disk I/O, zero network calls.

timeline
    title Without vs. with the daemon
    Without daemon : you chat (messages land in ZCode's store) : AI triggers context compaction : 💥 ZCode physically deletes messages : you remember to run sync : ❌ only the compaction summary remains; the original text is gone
    With daemon : you chat (messages land in ZCode's store) : daemon syncs within 30s : AI triggers compaction : ZCode deletes messages : 😌 no problem — the original text is already in your memory : the compaction summary is captured too

MCP integration

Claude Code (one command)

claude mcp add sessionrelay --scope user -- srelay serve

Run /mcp in Claude Code — sessionrelay should show as connected with 16 tools ready.

ZCode / other MCP clients

Add to your project's MCP config (.mcp.json or client settings):

{
  "mcpServers": {
    "sessionrelay": { "command": "srelay", "args": ["serve"] }
  }
}

Qoder users: Qoder launches MCP subprocesses without setting the working directory to the project root. Since 0.5.0 this needs no config: serve tries the MCP roots protocol (asks the client "which workspace") first, then the global project registry (auto-selects when exactly one project is alive); if neither resolves, the connection stays up and the first tool call returns a candidate list — the AI passes project once to pick (remembered for the connection). The env var below remains the most robust explicit pin.

Most robust fallback (any MCP client, bypasses PATH / cwd issues)

SRELAY_PROJECT_ROOT pins the project root regardless of the subprocess working directory — it saves you when PATH cannot resolve srelay or when the client does not set cwd (Qoder, in practice).

{
  "mcpServers": {
    "sessionrelay": {
      "command": "node",
      "args": ["/your/install/path/SessionRelay/dist/srelay.js", "serve"],
      "env": { "SRELAY_PROJECT_ROOT": "/your/project/path" }
    }
  }
}

How to verify it works

Open a fresh AI session and ask:

"Why did we decide on PostgreSQL?"

Correct behavior: the AI calls get_decisions or search_sessions and answers with provenance (date, source tool, session ID, message numbers). If it says "I don't know," MCP isn't connected — run srelay doctor.

Context safety (the AI never "eats too much")

  • get_session_detail defaults to 20 messages × 1,000 chars ≈ 20KB, hard-capped at 50KB
  • When truncated, responses carry truncated: true plus actionable hints (role="user" to see only your prompts / get_decisions() for conclusions / paginate)
  • More requires explicit parameters — safe by default, no trust in model restraint

Claude Code lifecycle hook (optional, speeds up session confirmation)

{
  "hooks": {
    "Stop": [{ "hooks": [{ "type": "command", "command": "srelay hook session-end --id $CLAUDE_SESSION_ID" }] }]
  }
}

Custom adapters (adding a new agent)

SessionRelay's adapters are pluggable — adding a new AI tool = zero core changes:

.sessionrelay/adapters/my-agent.js   ← just drop in a JS file
// minimal implementation
module.exports = {
  id: 'my-agent',
  displayName: 'My Agent',
  discover(projectRoot, config) {
    // return the sessions belonging to this project
    return [{ source: 'my-agent', sourceSessionId: 'xxx', sourceFile: '...', sizeBytes: 1024, mtimeMs: Date.now() }];
  },
  async readNew(ds, cursor, config) {
    // incrementally read new messages (the cursor is your own watermark object)
    return { messages: [{ role: 'user', content: '...', seqNum: 1 }], badLines: 0, cursor: { offset: 100 } };
  },
};

Full interface and more capabilities (watchRoots / healthCheck / detectCompaction) in the Adapter SDK docs.

Privacy design

  • Local-first: zero cloud dependencies, zero runtime network calls (telemetry = a local counter + voluntary submission)
  • Three capture modes + ignore hard boundary + redacted-by-default export + quarantined import — four lines of defense
  • Trust model: handoff contents are data, not instructions — written into the hop/1.0 protocol

Quality & verification

  • 260 tests (unit / integration / MCP stdio real-handshake contract / serve root resolution / live daemon-service install / UI security matrix / end-to-end), one npm test
  • CI green across 3 platforms × Node 22/24 (typecheck + test + build + dist smoke)
  • TypeScript strict, npm run typecheck clean
  • Real-machine acceptance at every phase (including the product recording its own birth)

Known limitations (honest list)

  • Rule-based extraction precision is ~60-70% (provenance blocks let you verify item by item; --ai enhancement is Phase 4)
  • Daemon service registration on all three platforms (Windows registry run key / macOS launchd / Linux systemd user, all login-autostart)
  • "Auto-attach important sessions to new ones" needs session identity (branch/PID), landing in Phase 4; for now use manual attach
  • Concurrent multi-agent sessions on one project scope by project+cwd
  • Historical sessions under renamed/moved directories are not auto-discovered (use a handoff package to migrate)
  • Trae is partial only: user prompts are readable, assistant replies are end-to-end encrypted (use save_note to record conclusions)
  • DSH / Cursor and other tools are on the roadmap (custom adapters work today)

Roadmap (Phase 4)

--ai summary/extraction enhancement · session identity (branch/PID) and auto-attach · suggest_related_sessions (now cheap: session vectors from semantic search already encode topic similarity) · official DSH / Cursor adapters · macOS/Linux daemon service registration · third-party adoption of the HOP protocol

Documentation

Non-Chinese readers: the README you're reading is fully bilingual, but most deep-dive docs above (user guide, import/export, privacy design) are still Chinese-only. English translations are in progress — if you'd like to help, translation PRs are very welcome. The User Guide is structured enough that machine translation reads reasonably well in the meantime.

License

MIT © 2026 EwanJasper

SIMILAR PLUGINS