/** * Claude Session Token Reader * * Reads per-turn token usage from Claude Code's session JSONL files. * Claude Code stores conversations at: * ~/.claude/projects/{path-slug}/{sessionId}.jsonl * or, for a session that runs on another of the owner's Claude Code accounts * (issue #942, `CLAUDE_CONFIG_DIR` set to the account's config dir): * /projects/{path-slug}/{sessionId}.jsonl * * Each assistant message includes an API `usage` object with exact * token counts (input_tokens, output_tokens, cache tokens). * * Used as the primary token source for claude-code agents because * Claude Code's TUI status bar is not capturable from PTY scrollback. * * @module services/monitoring/claude-session-tokens */ /** Aggregated token usage for a time window. */ export interface SessionTokenSummary { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheCreateTokens: number; cost: number; model: string; turnCount: number; } /** * Encodes an absolute path into the slug format used by Claude Code * for its projects directory. Replaces `/` with `-`. * * @param projectPath - Absolute path (e.g. /Users/alice/.crewly) * @returns Encoded slug (e.g. -Users-alice--crewly) */ export declare function encodeProjectSlug(projectPath: string): string; /** * Builds the ordered, de-duplicated list of Claude Code project slugs that may * hold transcripts for a working directory. * * Claude Code names its project directory after the *resolved* cwd (it sees * `process.cwd()`, which the OS has already run through any symlinks). On * macOS `/tmp` and `/var` are symlinks into `/private`, so an agent launched * in `/tmp/proj` writes to `-private-tmp-proj`, not `-tmp-proj` (#938). * * The realpath slug comes first because that is where Claude Code writes. The * raw slug is kept as a fallback for a cwd that cannot be resolved (deleted, * permission denied) and for transcripts written before a directory was moved * behind a symlink. Resolution failures never throw. * * @param projectPath - The agent's working directory, as Crewly recorded it * @returns Slugs to try, most likely first; never empty * * @example * ```typescript * await resolveProjectSlugCandidates('/tmp/proj'); * // macOS: ['-private-tmp-proj', '-tmp-proj']; Linux: ['-tmp-proj'] * ``` */ export declare function resolveProjectSlugCandidates(projectPath: string): Promise; /** * Synchronous {@link resolveProjectSlugCandidates}, for callers that cannot * await (launch-time resume checks). * * The raw slug is taken from the absolute (`path.resolve`d) cwd. * * @param projectPath - The agent's working directory * @returns Slugs to try, realpath slug first; never empty * * @example * ```typescript * resolveProjectSlugCandidatesSync('/tmp/proj'); * // macOS: ['-private-tmp-proj', '-tmp-proj'] * ``` */ export declare function resolveProjectSlugCandidatesSync(projectPath: string): string[]; /** * The `projects/` directories Claude Code may have written a session's * transcripts to, most likely first. * * A session on another of the owner's Claude Code accounts (issue #942) runs * with `CLAUDE_CONFIG_DIR` set to that account's config dir, so its * transcripts land in `/projects/`. The default login's * `~/.claude/projects/` is always included last: a session that switched * accounts during the day has transcripts in both. * * @param homeDir - Home directory that contains `.claude/` * @param configDirs - Claude config dirs of the session's accounts (current account first) * @returns Absolute `projects/` directory paths, de-duplicated * * @example * ```typescript * claudeProjectsRoots('/Users/a', ['/Users/a/.crewly/claude-accounts/work']); * // ['/Users/a/.crewly/claude-accounts/work/projects', '/Users/a/.claude/projects'] * ``` */ export declare function claudeProjectsRoots(homeDir: string, configDirs?: readonly string[]): string[]; /** * Lists the Claude Code project directories that may hold transcripts for a * working directory: every root of {@link claudeProjectsRoots}, each with the * slugs of {@link resolveProjectSlugCandidates}. The directories are not * required to exist. * * @param projectPath - The agent's working directory * @param homeDir - Home directory that contains `.claude/` (defaults to the OS home) * @param configDirs - Config dirs of the owner's other Claude Code accounts the session runs on (searched first) * @returns Absolute candidate directory paths, most likely first */ export declare function resolveProjectDirCandidates(projectPath: string, homeDir?: string, configDirs?: readonly string[]): Promise; /** * Locates a known conversation's transcript across all candidate project * directories, returning the first one that exists. * * @param projectPath - The agent's working directory * @param sessionId - Claude Code conversation UUID * @param homeDir - Home directory that contains `.claude/` (defaults to the OS home) * @param configDirs - Config dirs of the owner's other Claude Code accounts (searched first) * @returns Absolute path to the existing .jsonl file, or null if none exists */ export declare function findSessionJsonlPath(projectPath: string, sessionId: string, homeDir?: string, configDirs?: readonly string[]): Promise; /** * Lists every transcript file across all candidate project directories. * * Merges rather than stopping at the first existing directory: if both the * resolved and the raw slug directories exist, the live conversation may be in * either, and picking a directory by existence alone could lock onto a stale * one. A file name seen in an earlier (more likely) directory shadows the same * name in a later one, so a conversation is never listed twice. * * @param projectPath - The agent's working directory * @param homeDir - Home directory that contains `.claude/` (defaults to the OS home) * @param configDirs - Config dirs of the owner's other Claude Code accounts (searched first) * @returns Absolute paths of all .jsonl files found, grouped by candidate directory order */ export declare function listProjectTranscripts(projectPath: string, homeDir?: string, configDirs?: readonly string[]): Promise; /** * Finds the most recently modified transcript across all candidate project * directories for a working directory. * * @param projectPath - The agent's working directory * @param homeDir - Home directory that contains `.claude/` (defaults to the OS home) * @param configDirs - Config dirs of the owner's other Claude Code accounts (searched too) * @returns Absolute path of the newest .jsonl file, or null if there is none */ export declare function findLatestSessionFile(projectPath: string, homeDir?: string, configDirs?: readonly string[]): Promise; /** * Returns the path to a Claude Code session JSONL file under the *raw* cwd * slug. Does not resolve symlinks — prefer {@link findSessionJsonlPath}, * which also checks the realpath slug Claude Code actually writes to. * * @param projectPath - The agent's working directory * @param sessionId - Claude Code conversation UUID * @returns Absolute path to the .jsonl file */ export declare function getSessionJsonlPath(projectPath: string, sessionId: string): string; /** * Finds the most recently modified .jsonl session file in a Claude Code * project directory. Used as a fallback when the session ID is not known * (e.g. the orchestrator session, which may not have been persisted yet). * Checks both the realpath and the raw cwd slug directories. * * @param projectPath - The agent's working directory * @returns The session UUID, or null if no JSONL files found */ export declare function findLatestSessionId(projectPath: string): Promise; /** * Reads token usage from a Claude Code session JSONL, summing all * assistant turns whose timestamp is at or after `since`. * * Reads the file from the end backwards for efficiency — recent * entries are at the tail. * * @param projectPath - The agent's working directory * @param sessionId - Claude Code conversation UUID (if null, auto-detects latest) * @param since - Only count turns at or after this time * @param until - Only count turns before this time (defaults to now) * @param configDirs - Config dirs of the owner's other Claude Code accounts the session runs on * @returns Aggregated token summary, or null if the file doesn't exist */ export declare function getTokensSince(projectPath: string, sessionId: string | null, since: Date, until?: Date, configDirs?: readonly string[]): Promise; /** * Scans all Claude Code session JSONL files for a project and feeds * aggregated per-session token data into TokenUsageService. * * This bridges the gap between Claude Code's file-based token tracking * and the in-memory TokenUsageService that powers the Usage dashboard. * Should be called once during server startup. * * Only processes sessions modified in the last `maxAgeDays` to avoid * scanning hundreds of old session files. * * @param projectPath - The project's working directory * @param maxAgeDays - Only process sessions modified within this many days (default: 7) * @returns Number of sessions loaded */ export declare function syncSessionsToTokenUsageService(projectPath: string, maxAgeDays?: number): Promise; //# sourceMappingURL=claude-session-tokens.service.d.ts.map