← Back to the collection

Session ID Discovery

This is a standalone utility. You don’t need the rest of Hotline to use it.

The Answer: CLAUDE_CODE_SESSION_ID

Claude Code exports CLAUDE_CODE_SESSION_ID into every Bash tool call it spawns as of 2.1.132 (per the changelog; the environment variables reference documents it for hook commands and stdio MCP servers too — the Bash path is the one Hotline exercises and has live-verified). It matches the resumable session ID and the transcript filename, so a running agent reads its own identity straight out of the environment:

printf '%s\n' "$CLAUDE_CODE_SESSION_ID"

That’s what Hotline uses. Self-awareness of the session ID is what lets an agent reconnect to itself via --resume, key per-session state without collisions, or call another agent and keep track of the conversation — which is kind of the whole point of Hotline.

Don’t read the variable by hand, though: session-init.sh resolves identity across every harness Hotline supports, in one call, and validates what it finds:

Rung Source caller_kind
1 $HOTLINE_CALLER_SESSION_ID — explicit override override
2 $CLAUDE_CODE_SESSION_ID, validated as a UUID native
3 $CODEX_THREAD_ID — Codex callers codex
4 Fingerprint discovery (below) —
bash /path/to/plugins/hotline/scripts/session-init.sh
# → {"status":"cached","session_id":"…","caller_kind":"native"}

bash /path/to/plugins/hotline/scripts/session-init.sh --expanded
# → also transcript_path, claude_pid, project_dir

The capability was requested in anthropics/claude-code#25642, anthropics/claude-code#13733 and anthropics/claude-code#17188 — all three now closed. See the environment variables reference for the variable itself.

The Compatibility Path: Fingerprinting

Before 2.1.132 there was no such variable. Hooks received the session ID in their stdin payload, but the running agent itself had no way to know its own identity. Hotline’s original mechanism solved that, and it’s still shipped and still the fallback whenever the native value is missing or malformed — a client older than 2.1.132, an environment that strips the variable, a wrapper that sanitizes the shell.

Two scripts, a two-step fingerprint method:

  1. session-fingerprint.sh — Checks for a cached session ID. If found (exit 0), it writes the ID to stdout and you’re done. If not found (exit 1), it generates a unique fingerprint string and writes it to stderr.

  2. session-discover.sh — Takes that fingerprint string, greps the recent transcript files for it, and extracts the session ID from the matching filename. Caches the result so all future calls are instant.

The trick: the fingerprint string gets emitted into stderr during a Bash tool call, which means it appears in the conversation transcript. The discover script then finds which transcript file contains it. Transcript filename minus .jsonl = session ID. Boom.

Fingerprint Usage

The two steps must be separate tool calls — the fingerprint reaches the transcript only after the first call returns.

First call (two-step):

# Step 1: Check cache / generate fingerprint
bash /path/to/plugins/hotline/scripts/session-fingerprint.sh

# Exit 0 → stdout has your session ID. Done!
# Exit 1 → stderr has a fingerprint like SESSION_FINGERPRINT_<uuid>

# Step 2: Discover session from fingerprint (must be a separate tool call —
#          the transcript needs to be written first)
bash /path/to/plugins/hotline/scripts/session-discover.sh "SESSION_FINGERPRINT_<uuid>"

# Exit 0 → stdout has your session ID, now cached for future calls

Subsequent calls (cached):

bash /path/to/plugins/hotline/scripts/session-fingerprint.sh
# Exit 0, stdout = session ID. Instant.

Exit Codes

Script Exit 0 Exit 1 Exit 2
session-fingerprint.sh Cache hit — session ID on stdout Cache miss — fingerprint on stderr No claude process in ancestry
session-discover.sh Found — session ID on stdout Fingerprint not found in transcripts —

Global PATH Access

For convenience, symlink the scripts so they’re available everywhere:

ln -s /path/to/plugins/hotline/scripts/session-fingerprint.sh ~/bin/session-fingerprint
ln -s /path/to/plugins/hotline/scripts/session-discover.sh ~/bin/session-discover

Now any hook or script can call session-fingerprint without knowing where the plugin lives.

How the Fingerprint Works Under the Hood

  1. session-fingerprint.sh walks the process tree ($$ → $PPID → …) to find the claude parent PID
  2. Checks /tmp/claude-session-<pid> for a cached session ID
  3. On cache miss, generates SESSION_FINGERPRINT_<uuid> — a unique string that will appear in the transcript
  4. session-discover.sh greps the 5 most recent .jsonl transcript files for that fingerprint
  5. The transcript filename IS the session ID (e.g., 609dc906-c675-440a-b694-25f828519f3f.jsonl)
  6. Caches the result to /tmp/claude-session-<pid> so all future calls skip discovery

Two Claude sessions in the same directory get different claude parent PIDs, so their caches never collide.