8.2 KB · updated 2026-07-06 · md

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:

  1. Structural (always-on, no LLM). Reads the normalised HarnessSession records that tesserae sessions discover --import writes to .tesserae/harness_sessions/. For each session it mints a Session envelope node, emits discussed_in edges from every doc the agent opened, and turns the existing decisions field into SessionDecision nodes.
  2. 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:

  1. claude CLI over OAuth (preferred) — if the claude binary is on PATH and signed in (claude /login), Tesserae shells out to it the same way the existing ClaudeCLIResearchExtractor does. Multi-account users can pin a specific config dir via CLAUDE_CONFIG_DIR.
  2. ANTHROPIC_API_KEY (fallback) — used only if the CLI isn't available, e.g. headless servers and CI.
  3. 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 --import and then tesserae compile yourself. 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 daemontesserae engine runs a single-owner asyncio loop that watches the configured sources, coalesces bursts of edits into one Pipeline.run(), and auto-recompiles. Pass --once for a single deterministic drain, or --interval / --debounce to tune coalescing. tesserae refresh runs the same import → compile → vault-sync chain once, in-process.
  • Claude Code plugin hooks — with the plugin installed, the SessionEnd hook backgrounds an import + compile when a conversation ends, so this session's insights become graph nodes for the next one. The SessionStart hook 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 to node_id via discussed_in or references, 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 claude CLI AND no ANTHROPIC_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.json with both a content_hash and a project_root_hash. A cache file copied between projects is rejected on read — no cross-project replay.
  • Sessions are filtered through session_matches_project after loading, so a transcript whose cwd was 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 --import first? 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_KEY fallback. The cache means each session is sent to the LLM at most once per content-hash. Long sessions chunk at max_turns_per_chunk (default 30) with 5-turn overlap. To bound any usage, lower max_turns_per_chunk, lower include_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.