lazyagent doesn’t wrap, inject, or modify any agent. It reads whatever each agent already writes to disk — JSONL transcripts, SQLite databases, thread JSON files — and reconstructs state from there. Your agents stay on their own paths; lazyagent is a listener.
The pipeline
Agent CLIs / IDEs Session data on disk lazyagent
───────────────── ─────────────────────── ──────────────
claude, codex, … → JSONL / JSON files → SessionProvider
grok, pi, amp, … session directories ↓
cursor, opencode, kilo → SQLite databases → Session model
↓
TUI / GUI / API
Each agent has a provider that knows where its session data lives and how to parse it into a common Session struct. Providers can be file-watched (fsnotify) or polled (for SQLite-backed agents), and new sessions are merged into the shared view as they appear.
The shared core
Everything useful — the activity state machine, the file watcher, session caching, cost estimation, configuration — lives in one place (internal/core). The three interfaces (TUI, GUI, API) and the maintenance subcommands (prune, compact, search, limits) all import it, so there’s no behavioral drift between them.
Sessions are cached by transcript path + mtime + size, so subsequent scans only re-parse what changed. Grok uses the chat_history.jsonl inside each session directory as its cache key; Kimi uses wire.jsonl. For large JSONL transcripts the parser resumes from the last byte offset rather than re-reading the whole file when the agent format supports it.
Activity inference
lazyagent classifies each session into a state (idle, thinking, writing, running, …) by looking at the last few entries in the transcript: which tool fired, whether its output has arrived, how long ago the last entry was. The full set of states is documented in Activity states.
What lazyagent never does
- It doesn’t talk to any LLM. The only outbound network calls lazyagent makes are explicit
lazyagent limitschecks for Claude, Codex, Grok, Kimi, and Cursor billing/rate-limit data. Everything else (monitoring, prune, compact, and search) is purely local. - It doesn’t interrupt or control agents. You can’t kill a session from lazyagent; it only watches.
- It doesn’t move or copy session files — except when you explicitly run
pruneorcompact, which operate on the same files the agents read. - It doesn’t send telemetry. No analytics, no crash reporter, no phone-home.
What this implies
Because every piece of state comes from a file on disk:
- Closing lazyagent never disrupts a running agent session.
- Multiple lazyagent processes (e.g. GUI + TUI on the same machine) are always consistent — they read the same files.
- Moving a session folder (or deleting a project directory) is reflected on the next scan. This is also how the
--orphanedfilter inpruneworks.