Session Detection

How Irrlicht discovers and tracks AI coding agent sessions.

Detection Methods

Irrlicht uses three complementary methods to detect and track sessions. Together they provide fast, reliable discovery from the moment a process starts until the session ends.

Filesystem Watching

  • Technology: fsnotify (kqueue on macOS)
  • Recursively watches transcript directories for changes
  • Events: CREATE -- new session, WRITE -- activity, REMOVE -- session ended
  • Filters: only .jsonl files, ignores files older than max age (5 days default)

Process Scanning

  • Polls pgrep -x claude every 1 second
  • Creates pre-sessions (proc-<pid>) before transcripts exist
  • Discovers CWD via lsof -a -p <pid> -d cwd
  • Pre-sessions are replaced by real sessions when the transcript arrives

Process Exit Monitoring

  • kqueue EVFILT_PROC NOTE_EXIT for instant (~1ms) exit detection
  • Fallback: periodic liveness sweep via syscall.Kill(pid, 0) every 5s
  • Startup cleanup: synchronous dead PID check in seedFromDisk()

Supported Agents

Claude Code

  • Transcript location: ~/.claude/projects/<project>/<session-id>.jsonl
  • Flat directory structure (one level under projects/)
  • Adapter name: claude-code

OpenAI Codex

  • Transcript location: ~/.codex/sessions/YYYY/MM/DD/<session-id>.jsonl
  • Deep directory structure (recursive watching)
  • Adapter name: codex
  • Model detection from ~/.codex/config.toml

Pi Coding Agent

  • Transcript location: ~/.pi/agent/sessions/--<cwd-dashed>--/<timestamp>_<uuid>.jsonl
  • JSONL v3 format — session header on first line with cwd, parentSession, and version fields
  • Messages use role field: user, assistant, toolResult, bashExecution
  • Turn completion detected from stopReason: "stop" on assistant messages
  • Adapter name: pi
  • Model detection from ~/.pi/agent/settings.json (defaultModel field)
  • Supports multiple LLM providers (Anthropic, OpenAI, Google, xAI, Groq)

PID Discovery

Phase Mechanism
Discovery lsof -t <transcript> with retry at 500ms, 1s, 2s intervals
CWD fallback If lsof fails, matches claude processes by working directory
Registration kqueue EVFILT_PROC NOTE_EXIT
Liveness sweep syscall.Kill(pid, 0) every 5s
Startup cleanup Synchronous in seedFromDisk()

Subagent Detection

When Claude Code spawns subagents (via the Agent tool), each subagent creates its own transcript file at:

~/.claude/projects/<project>/<parent-session-id>/subagents/<agent-id>.jsonl

The daemon detects these files through the same filesystem watcher and derives the parent-child relationship from the path structure. Each subagent becomes an independent session with its own state machine, linked to the parent via parent_session_id.

Lifecycle

  • Detection: Filesystem watcher sees the new .jsonl file in the subagents/ directory
  • Tracking: Session created with parent_session_id set; exempt from orphan cleanup (no PID of its own)
  • Display: Parent session shows a purple badge with the count of active subagents
  • Cleanup: Child sessions are deleted when they finish (ready state), when their transcript becomes stale (>2min), or when the parent session is deleted (cascade)

In-Process vs File-Based

Claude Code runs two types of subagents:

  • In-process (Explore, Plan) — detected via open Agent tool calls in the parent transcript; no separate file
  • File-based (background agents, worktree agents) — create their own transcript in the subagents/ directory

The API's SubagentSummary merges both types into a single count.

Session ID

  • File-based: UUID extracted from the filename (<uuid>.jsonl)
  • Process-based: proc-<pid> format, used for pre-sessions before a transcript file is found

Git Metadata

  • Branch: git rev-parse --abbrev-ref HEAD (strips worktree- prefix)
  • Project name: derived from git-common-dir (works correctly with worktrees)
  • CWD: tail-reads the last 32KB of the transcript to find the latest working directory

Transcript Parsing

The tailer reads JSONL transcripts line by line and extracts:

  • Model name -- normalized from various format variations
  • Token counts -- input, output, cache read, cache creation
  • Tool call state -- open/closed tracking with tool names
  • Context window size -- used for utilization pressure calculation
  • Cost estimation -- computed from token breakdown and model pricing tables
Note Transcript parsing is incremental. The tailer remembers its read offset and only processes new bytes when the file grows, keeping CPU usage minimal even with large transcripts.