/** Token counts of one assistant API message (`message.usage` in the .jsonl). */ export interface TokenUsage { input: number; output: number; cacheCreation: number; cacheRead: number; } /** * A streamed piece of an assistant turn, read from the session .jsonl. * * `at` is the moment Claude Code WROTE the block (the line's `timestamp`), not * the moment we read it. The distinction matters: the tail resumes at its * persisted offset after a restart (invariant #7), so a burst of blocks written * while it was down arrives at once — timestamping them at read time would date * them all "now". Absent when the line carries none (fixtures, old * transcripts). */ export type TailEvent = { kind: "text"; text: string; at?: number; afterInternal?: true; } | { kind: "tool"; id: string; name: string; summary: string; at?: number; } | { kind: "result"; toolUseId: string; text: string; isError: boolean; at?: number; } | { kind: "usage"; messageId: string; usage: TokenUsage; } /** The turn's answer was ONLY the silence placeholder. Emitted where the text * block is dropped, because dropping it without a trace made the guard that * honours the silence unreachable: the parent notification reads the last * STREAMED block, and this one never became one — so a quiet agent still woke * its parent, with an empty message or with a stray earlier thought. */ | { kind: "silent"; at?: number; }; /** * Path of the .jsonl transcript Claude Code writes for a session. * * The obvious path is `/.jsonl`, but Claude derives the * encoded dir from the cwd it was LAUNCHED with. After a repo/worktree move * (or the shadok-ai rename) a still-running agent keeps writing under its * original encoded dir, which no longer matches the current cwd — so the tail * would watch a stale file and nothing streams. To be immune to that drift we * prefer the newest `.jsonl` found anywhere under ~/.claude/projects; the * file being actively appended is always the newest. Falls back to the * cwd-derived path for a brand-new session whose file doesn't exist yet. */ export declare function sessionFilePath(cwd: string, sessionId: string): string; /** The most-recently-modified `.jsonl` across all project dirs, or null. */ export declare function newestTranscriptById(sessionId: string): string | null; /** * Tails a session .jsonl: watches for appended lines and yields the assistant * `text` and `tool_use` blocks as they are written — the authoritative, * untruncated content, streamed at message granularity. * * `onEvent` fires for each new block. Returns a stop() function. * Starts from the current end of file, so only NEW turns are streamed * (existing history is replayed separately via loadHistory). */ /** Beyond this backlog we give up catching up: a tmux agent that worked for * hours with no server would dump a wall of text in one go. */ export declare const MAX_CATCHUP: number; /** * Where to start reading the transcript. Pure — this is what the tests pin. * * Starting at the end (the old unconditional behaviour) SILENTLY lost * everything an agent wrote while no server was up — which happens on every * auto-update, hence on every merge to main. The web recovered (it reloads * history); Telegram did not: the message simply never existed. */ export declare function startOffset(size: number, stored: number | null, maxCatchUp?: number): number; /** Seed the resume position for a transcript BEFORE tailing it, so the next * `tailSession(file)` starts there instead of at EOF. Used when following a * fork: the new `.jsonl` was never shown in chat, so we seed 0 to replay it * from the start (still capped by MAX_CATCHUP via `startOffset`). */ export declare function seedTailPos(file: string, pos: number): void; /** Forget a finished session's position — it has nothing left to resume. */ export declare function clearTailPos(file: string): void; export declare function tailSession(file: string, onEvent: (e: TailEvent) => void, intervalMs?: number, /** Re-resolves the transcript path so the tail FOLLOWS the file if it moves * to another project dir mid-session (an agent switching git worktree changes * its cwd, and Claude Code re-homes the .jsonl). Without this the tail keeps * watching the stale path and recent messages never reach the chat. */ resolve?: () => string): () => void; export declare function isNothingToShow(text: string): boolean; /** * Pure parser for one transcript line → the events it yields. Exported for * tests; `tailSession` streams these live. Returns [] for anything that isn't * a streamable assistant/user event. */ export declare function parseLine(line: string): TailEvent[]; /** * ISO timestamp of a transcript line → ms epoch, or null when missing / * unreadable. Shared with `loadHistory` (extract.ts) so the displayed time is * the same whether history is replayed or the live stream is followed. */ export declare function parseTimestamp(v: any): number | null; export declare function parseUsage(message: any): TokenUsage | null; /** * Sums the token usage already written to a transcript, keyed by message id. * Streaming writes several records per message with the same id and growing * counts — keeping the last record per id yields each message's final usage. */ export declare function scanUsage(file: string): Map; /** Flattens a tool_result's content (string or block array) to display text. */ export declare function resultText(content: any): string;