Every Relay deposit can now ship with a context_snapshot — a structured inventory of the files the depositing agent was looking at when the decision was made. The receiving session pulls a package and sees not just what was decided, but what was on the previous agent's desk.
Before this release, a Relay deposit answered three questions: what was decided, what's still open, and what's being handed off. The receiving session would pull the package and start oriented — on the conclusions.
The missing piece was always context. A decision is more legible when you can see what the previous agent was looking at when they made it. So we added a fourth question: what was being seen. Every deposit can now carry a context_snapshot — a structured inventory of files in the depositing agent's conversation context at deposit time, alongside the decisions and the handoff note.
Three uses where this changes the receiving-agent experience materially.
Picking up a session cold, the receiving agent gets the same files the previous agent had loaded — paths, roles, why each one mattered. No "let me grep around to figure out what we were working on." The snapshot is the map; the agent goes straight to the work.
When a deposit crosses actors (you deposit into a shared project, another Relay instance picks it up), the receiving actor now sees not just your decisions but the surface area you were considering. Less context loss across the boundary; better follow-up questions.
For human reviewers and orchestrator agents alike, "what files were in scope when this decision happened" is a useful signal. A deposit that touches authentication while only having one auth-related file in context reads differently from one that had the full subtree loaded.
A context_snapshot is a JSON object on the package payload. Type definitions live in @relay/core (ContextSnapshot, ContextSnapshotFile, ContextSnapshotRanked).
{ "session_shape": { "files": 8, "lines": 1101, "dominant_categories": ["instructions", "major-doc"] }, "files": [ { "path": "src/auth/session.ts", "role": "edit", "category": "source-code", "lines": 214, "why": "current edit target — refresh token logic" } ], "heavyweights": { "biggest": [{ "path": "...", "metric": 270, "note": "..." }], "most_touched": [{ "path": "...", "metric": 3, "note": "..." }], "stale": [{ "path": "...", "metric": null, "note": "..." }] }, "category_totals": { "instructions": 420, "major-doc": 651 } }
Top-of-the-fold summary. files and lines are coarse counts; dominant_categories ranks the 2–3 categories that dominate the context budget. The dashboard chip renders from these three fields alone.
Per-file entries with path, role (read / edit / write / session-load / plan / reference), category, approximate lines, and a short why phrase explaining why the file was in context. When 3+ siblings share a directory and extension, they collapse into one row (is_group: true with group_count) — keeps the payload compact for big codebases.
Three ranked lists for fast rendering: biggest (by line count), most_touched (by reference count this session), and stale (loaded early and unused since — candidates for /clear if context pressure is a concern).
Total lines per category. Powers the dashboard bar chart without needing to re-walk files[] at render time.
Snapshots are paths-only — no file contents are ever transmitted or stored. Even so, path strings themselves can hint at secrets (infra/secrets/prod.key, ~/.aws/credentials). Before any snapshot is persisted, redactSensitivePaths in @relay/core replaces matched paths with the literal string [redacted].
# Anything matching these regexes is replaced with [redacted] .env* # dotenv files credentials* # AWS/GCP credentials files *.key *.pem *.p12 *.pfx # private keys secrets/ # any path under a secrets dir .ssh/ # SSH config and identity files .aws/ # AWS CLI / SDK state .gnupg/ # GPG keyring id_rsa(.pub) # SSH RSA identities
@relay/core and runs server-side on every deposit path — CLI, MCP, and any future client share identical behavior. The redaction step is idempotent: re-running on an already-redacted snapshot is a no-op. Counts are preserved — a redacted file still appears in files[] and heavyweights as [redacted], so the receiver knows the slot exists even though the path is suppressed.
Pass a JSON file matching the ContextSnapshot shape to relay deposit --context-snapshot <path>. The CLI validates the shape with validateContextSnapshot before constructing the package; a malformed file fails fast with a descriptive error.
# 1. Write the snapshot JSON somewhere $cat > /tmp/snap.json << 'EOF' { "session_shape": { "files": 3, "lines": 412, "dominant_categories": ["source-code"] }, ... } EOF # 2. Deposit, attaching the snapshot $relay deposit \ --title "[SIG] refactored session token handling" \ --description "Split refresh logic out of session.ts; tests still failing on expiry edge case" \ --decisions "Refresh tokens now opaque, not JWT — easier to revoke" \ --questions "Do we keep the JWT path behind a flag, or remove entirely?" \ --handoff "Tests: auth.test.ts:128 (expired-token edge case)" \ --context-snapshot /tmp/snap.json
The relay_deposit MCP tool gains a top-level context_snapshot parameter. Agents pass the object directly — no file roundtrip. The tool runs the same validator and the same redactor as the CLI path; malformed input is rejected with a structured error response so the agent can correct and retry.
# Inside the agent (Claude Code, Cursor, any MCP-aware client) relay_deposit({ title: "[SIG] refactored session token handling", description: "...", decisions: ["Refresh tokens now opaque, not JWT"], open_questions: ["JWT path: flag or remove?"], handoff_note: "Tests still failing on auth.test.ts:128", context_snapshot: { // top-level — not nested in description session_shape: { ... }, files: [...], heavyweights: { ... }, category_totals: { ... } } })
context_snapshot is a top-level argument on the tool call. Some agents have a habit of stuffing structured data inside the description string as XML-like tags — that path leaks the raw markup into the package's description field and leaves context_snapshot empty. Pass the object as a proper argument; the validator will tell you immediately if the shape is off.
On the Relay dashboard at relaymemory.com/dashboard, every package row in the live timeline that carries a snapshot picks up a small phosphor-green chip:
# What the chip looks like on a timeline row ⌗ 8 files in context · ~1,101 lines · instructions, major-doc
Hover the chip and the tooltip surfaces the biggest file in scope at deposit time. A future iteration (queued for the next dashboard PR) expands the chip into a full "Context at deposit" panel — sortable file table, category bar chart, heavyweights side panel — mirroring the standalone visualizer aesthetic shipped with the /tpl-context-report skill.
The /tpl-context-report Claude Code skill is the live-mode preview of what a deposit's context_snapshot field carries. Run it during a session and it inventories every file in the agent's conversation context, renders a self-contained dark-mode HTML visualizer, and (with --deposit) emits the JSON in the exact shape Relay expects.
# macOS / Linux $mkdir -p ~/.claude/skills $cp -r skills/tpl-context-report ~/.claude/skills/ # Windows (PowerShell) >Copy-Item -Recurse -Force skills\tpl-context-report "$HOME\.claude\skills\"
Restart Claude Code so the skill registry reloads, then invoke it.
/tpl-context-report # Output: # - Markdown table of files in context (grouped) # - Self-contained HTML visualizer opens in your default browser # - Heavyweights panel: biggest, most-touched, stale
/tpl-context-report --deposit # Writes: # %TEMP%\tpl-reports\context-snapshot-<timestamp>.json (Windows) # $TMPDIR/tpl-reports/context-snapshot-<ts>.json (macOS/Linux) # Then you can deposit: $relay deposit --title "..." --context-snapshot "<path-above>"
With --deposit --inline, the JSON also prints to chat so you can paste it directly into the relay_deposit MCP tool's context_snapshot parameter without writing a file.
skills/tpl-context-report/ alongside using-relay and tpl-handoff. See github.com/Tensorpunk-Labs/relay/tree/main/skills.
The stop-hook auto-deposit runs after the agent has exited, so it can't ask the agent for an inventory. The solution is passive observation: a PostToolUse hook fires for every file tool call (Read, Edit, Write, Glob, Grep, NotebookEdit, MultiEdit) while the agent is still running and appends a record to .relay/context-log.jsonl in the project's working directory. When the stop hook then runs relay deposit --auto, the CLI reads the log, synthesizes a ContextSnapshot from it (de-duped, categorized, ranked), attaches it to the package, and truncates the log so the next session starts clean.
Add a single block to your global ~/.claude/settings.json:
{ "hooks": { "PostToolUse": [ { "matcher": "Read|Edit|Write|Glob|Grep|NotebookEdit|MultiEdit", "hooks": [ { "type": "command", "command": "relay hook log-tool" } ] } ] } }
The handler is defensive — it never throws, never blocks the agent's tool loop, never returns non-zero. If anything goes wrong (no stdin, malformed JSON, disk error), it exits silently and the next auto-deposit just falls back to git-state-only.
The dashboard at relaymemory.com/dashboard ships a semantic + keyword search panel that queries the deposit history across every project. Two modes:
Embedding-based retrieval (Xenova/all-MiniLM-L6-v2) + hybrid BM25/pgvector fusion with cross-encoder reranking via Xenova/bge-reranker-base. Best for fuzzy conceptual queries — "how did we handle auth tokens" returns deposits about auth even if they never used that exact phrasing. Click Synthesize for a Claude-generated paragraph distilling the through-line across top hits (Haiku / Sonnet / Sonnet-deep depth presets).
Substring match (PostgREST ilike) across title, description, handoff_note, and context_md. Ranked by per-column weight with recency tiebreak. Best for exact identifiers, file paths, package IDs, and specific phrasings. No model load — sub-second cold start.
Filter by day-window (1d / 7d / 30d / 90d / all) and per-project. Click any result to expand inline and see the full package — id, decisions, open questions, handoff, deliverables, callsign, and a copy-id button.
relaymemory.com/dashboard, semantic search and synthesize are gated to a pre-loaded canned example (real output from a live session, served instantly without API calls) so visitors can see the visual + behavior without driving up embedding / Anthropic costs. Keyword search stays fully live against the real data. Self-hosted instances run all features live by default — set NEXT_PUBLIC_DEMO_MODE=false (the default) in apps/web/.env.local.
One remaining follow-up, called out so you know what's coming.
The current dashboard surface is the timeline chip (file count, lines, top categories, biggest-file tooltip). Next pass expands it into a panel matching the standalone visualizer: sortable file table, category bar chart, heavyweights, live filter. Schema and data are already in place; this is a rendering follow-up.