/** * Default (real-filesystem) `ProviderSessionReaders` for the identity resolver * (DEC-116). Every reader is **best-effort + total**: any missing file, format * drift, or parse error yields `undefined`, so the resolver degrades to * `{ source: "none" }` and the caller mints a keypair-anchored id (never throws, * never blocks connect). The transcript layouts were live-verified 2026-05-30 * (gemini's path layout drifts across versions — we match on `.project_root`). */ import type { ProviderSessionReaders } from "./resolver.js"; export declare const defaultProviderSessionReaders: ProviderSessionReaders; /** * How much of the tail of a Claude transcript to scan for the current title. * * A transcript is append-only, and `customTitle` is carried by dedicated * **rename-event records** (`type: "custom-title"`) — NOT by ordinary records. * Measured 2026-07-25 over all 8078 local transcripts: title-bearing records are * 0.06% of tail records (5.1% within the one heavily-renamed transcript that * motivated this fix). The newest such record is therefore near the END, and a * 64 KiB window reaches it: of the 45 transcripts that carry a title at all, * **all 45** are resolved from this window — 0 missed, 0 wrong values. * * Bounded rather than whole-file because this now runs on the heartbeat and live * transcripts reach hundreds of MB (largest local: 233 MB; cost 0.30 ms/call). */ export declare const CLAUDE_TITLE_TAIL_BYTES: number; /** * How much of the TAIL of `~/.codex/session_index.jsonl` to read. Codex resolves * last-match-wins, so the tail is what matters; the file grows monotonically and * this also runs on the heartbeat (48 KiB today, so today's whole file fits). * A session whose entry has aged out of the window simply yields `undefined`, * which the caller treats as "keep the name you have". */ export declare const CODEX_INDEX_TAIL_BYTES: number; /** * Upper bound on a display name written into presence. A host title is * user-controlled and unbounded; `isH2ASession` only checks `typeof`, so an * absurd title would otherwise land in every peer's presence read. Truncated * rather than rejected so the name stays findable by the substring match that * `discover_sessions(name:)` performs. */ export declare const MAX_DISPLAY_NAME_CHARS = 200; /** Initial re-scan delay after a transcript lookup MISSES (negative cache). */ export declare const TRANSCRIPT_MISS_BACKOFF_MS = 60000; /** Ceiling for the negative-cache backoff. */ export declare const TRANSCRIPT_MISS_BACKOFF_MAX_MS = 300000; /** * Injectable FS readers for `readHostSessionName` (test-friendly). * Only reading is required; real-FS defaults are `defaultHostNameReaders`. */ export interface HostNameReaders { /** * Read the LAST `maxBytes` of a file as newline-delimited lines, dropping a * leading partial record when the file was truncated. Returns [] on any error. * * Deliberately not `readLines(path, maxLines)` (the pre-fix head reader): a * head read of an append-only transcript returns the title as of session * START and can never observe a later rename. An external implementor of this * interface gets a compile error rather than silently-stale names. */ readTailLines(path: string, maxBytes: number): string[]; /** * List newline-delimited JSONL entries from the TAIL of * `~/.codex/session_index.jsonl` (see `CODEX_INDEX_TAIL_BYTES`). */ readCodexSessionIndex(): string[]; /** Whether the home dir root is known (for path construction). */ homedir(): string; /** * Locate the transcript file for a Claude session id. Optional: defaults to * scanning `~/.claude/projects`. Injectable so a test can COUNT the scans and * pin the negative-cache behaviour — that walk is 87 directories / 14345 files * and it sits on a 5-second timer, so a repeated miss must not re-walk. */ findClaudeTranscript?(cwd: string, sessionId: string, home: string): string | undefined; } export declare const defaultHostNameReaders: HostNameReaders; /** * Read the host-native session name for the given session. * * - **claude**: reads the transcript JSONL (located via the CLAUDE_CODE_SESSION_ID * resolver); scans the TAIL for the LAST `customTitle` (the current user * rename — the transcript is append-only), then falls back to the first * `agentName` seen. Never returns `aiTitle`. * - **codex**: reads `~/.codex/session_index.jsonl`; returns `thread_name` for * the entry whose `id === sessionId` (last-match wins). * - Other hosts: returns undefined. * * Always best-effort (returns undefined on any parse/IO error). */ export declare function readHostSessionName(opts: { host: string; cwd: string; sessionId?: string; readers?: HostNameReaders; }): string | undefined; /** * Build a re-callable resolver for this session's host-native display name, for * the heartbeat refresh path (spec 2026-07-25-h2a-lane-addressing §D1b). * * `readHostSessionName` is a one-shot: it re-scans every project directory to * locate the transcript. This factory memoizes the transcript path (the session * id is fixed for the life of the server process) so a steady-state heartbeat * costs one bounded tail read. * * **Negative caching (required, not an optimization).** A session whose * transcript never appears is a real, observed state — it is the RC-3 case in the * spec, where `CLAUDE_CODE_SESSION_ID` names a session with no transcript at all. * Memoizing only on success meant that session re-walked * `~/.claude/projects` — 87 directories, 14345 files, 8.73 ms — on EVERY * heartbeat, i.e. every 5 seconds, forever. So a miss is cached too, with * exponential backoff from `TRANSCRIPT_MISS_BACKOFF_MS` to * `TRANSCRIPT_MISS_BACKOFF_MAX_MS`. A transcript that appears later is still * picked up, just not instantly — bounded by the current backoff. * * Returns `undefined` whenever the title cannot be read — the caller must treat * that as "keep the name you have", never as "fall back to the cwd basename". */ export declare function createHostSessionNameRefresher(opts: { host: string; cwd: string; sessionId?: string; readers?: HostNameReaders; /** Injectable clock, for testing the negative cache without real waiting. */ now?: () => number; }): () => string | undefined; //# sourceMappingURL=readers.d.ts.map