sessions.md
docs/integrations/sessions.md
Session graph
<!-- translations:start -->
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
<!-- translations:end -->
Tesserae's session graph turns your Claude Code / Codex conversations about a project into first-class nodes in the typed knowledge graph, linked back to the documents that came up. After a compile, you can ask tesserae ask "what did we decide about 3D Gaussian Splatting?" and get back specific Insight / Decision / Question / TODO / Hypothesis / Takeaway nodes with provenance back to the session that produced them.
How it works
The pipeline is two passes per session:
- Structural (always-on, no LLM). Reads the normalised
HarnessSessionrecords thattesserae sessions discover --importwrites to.tesserae/harness_sessions/. For each session it mints aSessionenvelope node, emitsdiscussed_inedges from every doc the agent opened, and turns the existingdecisionsfield intoSessionDecisionnodes. - LLM (opt-in, no API key required). Sends the normalised transcript turns (the
metadata["turns"]field — not the raw transcript file) to Claude with a JSON-only finding schema. Returns six kinds of findings, each citing back to specific turns and specific doc node IDs in the current graph. Cached by content_hash + project_root_hash so unchanged sessions skip the call on the next compile.
Backend resolution matches the rest of Tesserae's "common path uses no API keys" promise:
claudeCLI over OAuth (preferred) — if theclaudebinary is on PATH and signed in (claude /login), Tesserae shells out to it the same way the existingClaudeCLIResearchExtractordoes. Multi-account users can pin a specific config dir viaCLAUDE_CONFIG_DIR.ANTHROPIC_API_KEY(fallback) — used only if the CLI isn't available, e.g. headless servers and CI.- Neither configured → no LLM pass. The structural pass still runs.
Two ways to feed sessions in: batch vs. live
The pipeline above is the same no matter how sessions arrive. What differs is when they get discovered and compiled:
- Batch (manual). Run
tesserae sessions discover --importand thentesserae compileyourself. Best for one-shot backfills or CI. This is the path the rest of this page walks through. - Live (continuous). Let the engine watch the project and recompile as work happens, so the graph stays fresh without you remembering to run anything:
- Supervisor daemon —
tesserae engineruns a single-owner asyncio loop that watches the configured sources, coalesces bursts of edits into onePipeline.run(), and auto-recompiles. Pass--oncefor a single deterministic drain, or--interval/--debounceto tune coalescing.tesserae refreshruns the same import → compile → vault-sync chain once, in-process. - Claude Code plugin hooks — with the plugin installed, the
SessionEndhook backgrounds an import + compile when a conversation ends, so this session's insights become graph nodes for the next one. TheSessionStarthook prints the current graph summary on the way in. This is the closest thing to capturing sessions "as they happen" — no manual discover/compile step at all.
Setup
# One-time: sign into Claude via the CLI (skip if already signed in).
claude /login
# One-time: import sessions for this project into .tesserae/harness_sessions/.
# Filters by cwd so only sessions that ran inside this project are imported.
tesserae sessions discover --import
# Compile. Structural pass runs free. LLM pass runs automatically when the
# `claude` CLI is signed in — no API keys, no env vars to set.
tesserae compile
To run compile without sessions (e.g. on a server without any harness history):
tesserae compile --no-sessions
To force structural-only (skip the LLM call even when a key is set):
# Set compile_options.sessions_llm = false in .tesserae/config.json, then:
tesserae compile
Configuration
.tesserae/config.json accepts a sessions block:
{
"sessions": {
"enabled": true, // default true
"llm_enabled": "auto", // auto | true | false
"max_turns_per_chunk": 30, // LLM-chunking threshold
"model": "claude-sonnet-4-7-20251201", // override default
"include_doc_id_context": 200 // top-N doc IDs sent to LLM
}
}
CLI flags override config. llm_enabled = "auto" (default) runs the LLM pass when the claude CLI is signed in OR when ANTHROPIC_API_KEY is set; without either, only the structural pass runs (no error, no outbound calls).
Query
Two MCP tools land on top of the existing search/wiki ones:
list_sessions(since?, limit?)— Session envelopes (id, started_at, title, finding counts) for the resolved project.find_session_findings(node_id, kinds?)— every Session-derived finding linked tonode_idviadiscussed_inorreferences, optionally filtered to insight / decision / question / todo / hypothesis / takeaway.
From the CLI:
tesserae sessions list # human view, shows per-session finding counts
tesserae ask "what did we decide about extractor dedup?"
Privacy
- With no signed-in
claudeCLI AND noANTHROPIC_API_KEY(or with--sessions-llm=false), there are zero outbound network calls. Only the structural pass runs. - When the LLM pass runs, the full normalised transcript turns for not-yet-cached sessions are sent to Claude. The transcript file itself stays on disk; only the LLM's JSON output is persisted to the graph and the per-session cache. The CLI path routes through your existing OAuth session — no new auth surface, no API key handling.
- Cache files live at
.tesserae/session_findings/<session_id>.findings.jsonwith both acontent_hashand aproject_root_hash. A cache file copied between projects is rejected on read — no cross-project replay. - Sessions are filtered through
session_matches_projectafter loading, so a transcript whosecwdwas a sibling project never produces nodes in this project's graph.
Vault layout
Findings render under the Obsidian vault as one page per finding, grouped by session:
<vault>/
sessions/
<session-id-slug>/
cache-findings-by-content-hash.md # SessionDecision
path-index-needs-basename-suppression.md # SessionInsight
…
User notes inside the <!-- user-notes:start --> … <!-- user-notes:end --> block on any finding page survive recompile — same contract as every other vault page.
Troubleshooting
- No Session nodes appear after compile. Did you run
tesserae sessions discover --importfirst? The compile path only consumes.tesserae/harness_sessions/; it does NOT scan~/.claude/projects/automatically (that scan can take minutes on machines with thousands of historical sessions). - LLM cost concerns. The CLI path costs nothing — it runs under your existing Claude OAuth quota. Cost only applies on the
ANTHROPIC_API_KEYfallback. The cache means each session is sent to the LLM at most once per content-hash. Long sessions chunk atmax_turns_per_chunk(default 30) with 5-turn overlap. To bound any usage, lowermax_turns_per_chunk, lowerinclude_doc_id_context, or set--sessions-llm=false. - A finding cites a node ID that doesn't exist. The orchestrator validates every cited reference against the live doc graph and silently drops unknowns. If you see the warning in logs, the LLM hallucinated a citation — the surviving references are still trustworthy.
Spec
The full design lives at docs/superpowers/specs/2026-05-19-session-graph-extractor-design.md. The implementation plan is docs/superpowers/plans/2026-05-19-session-graph-extractor-plan.md.