import * as v from 'valibot'; /** * How many transcripts the cache retains. Entries are evicted least-recently- * used, tracked by an explicit `order` list rather than a timestamp so eviction * is deterministic and testable without a clock. * * The bound exists because nothing prunes by age: an entry is keyed by absolute * path, and a machine accumulates sessions indefinitely. Evicting an entry costs * one full re-parse of that transcript, never a wrong number. */ export declare const MAX_CACHED_TRANSCRIPTS = 256; declare const CacheEntrySchema: v.ObjectSchema<{ /** * `stat` taken *before* the read that produced `offset`. A concurrent append * during the read therefore leaves the file looking larger than `size` next * time — stale, so the new bytes get read — rather than matching and being * treated as fully consumed. */ readonly size: v.SchemaWithPipe, v.IntegerAction, v.MinValueAction]>; readonly mtimeMs: v.NumberSchema; /** Bytes consumed, always ending on a newline. */ readonly offset: v.SchemaWithPipe, v.IntegerAction, v.MinValueAction]>; /** * Fingerprint of the last bytes of the consumed prefix, so growth is only * treated as an append once the prefix is confirmed to be the same prefix. * * Size-increase alone cannot tell an append from a path replaced by a larger, * unrelated file: resuming at the old offset would then start mid-record and * persist stale totals as the new complete state. `mtime` does not help — the * replacement has its own. Verifying costs one ~64-byte read against the * whole-file parse it protects. */ readonly tailHash: v.StringSchema; /** * The session id the fold fell back to for records that carry none of their * own (`file.sessionId`, else the filename). * * Entries are keyed by path, and `resolveSessionTranscripts` deliberately * lets one path be reported by different resumed sessions — dropping the hint * entirely when two sessions claim it (`roots.ts:174-178`). A cached fold made * under a different fallback attributes those records to the wrong session, so * a change here forces a re-parse. */ readonly fallbackSessionId: v.StringSchema; readonly sessions: v.ArraySchema; readonly usage: v.ArraySchema; readonly provider: v.OptionalSchema, undefined>; readonly inputTokens: v.NumberSchema; readonly outputTokens: v.NumberSchema; readonly cacheReadTokens: v.OptionalSchema, undefined>; readonly cacheWriteTokens: v.OptionalSchema, undefined>; }, undefined>, undefined>; readonly skillUsage: v.ArraySchema; readonly model: v.StringSchema; readonly inputTokens: v.NumberSchema; readonly cacheWriteTokens: v.OptionalSchema, undefined>; }, undefined>, undefined>; }, undefined>, undefined>; /** * A `Skill` invocation still waiting for the next assistant message to supply * the usage to attribute to it. Persisted rather than replayed because a * cursor that resumes past the boundary would drop that skill load * permanently, and silently — nothing downstream knows a load was expected * (docs/plans/default-transcript-discovery-plan.md → "Incremental re-read"). */ readonly pendingSkillLoads: v.ArraySchema; readonly skillName: v.StringSchema; }, undefined>, undefined>; /** * Dedup keys already counted in `sessions`. Claude Code writes one record per * content block sharing a `message.id`, and those records can straddle the * cursor, so resuming without them would re-count the tail message. * * Kept in full rather than trimmed to the tail. Only the last message's keys * can straddle *if* a message's records are always contiguous — which is the * observed habit, not a documented contract, and betting on it would * over-count silently. The cost is that this list grows with the session: * roughly one ~70-byte key per assistant message, so a few percent of the * transcript it saves re-parsing. Bound it only against a measurement. */ readonly seenMessageKeys: v.ArraySchema, undefined>; }, undefined>; declare const CacheFileSchema: v.ObjectSchema<{ readonly version: v.NumberSchema; /** Most-recently-used first. Paths not present in `entries` are ignored. */ readonly order: v.ArraySchema, undefined>; readonly entries: v.RecordSchema, v.ObjectSchema<{ /** * `stat` taken *before* the read that produced `offset`. A concurrent append * during the read therefore leaves the file looking larger than `size` next * time — stale, so the new bytes get read — rather than matching and being * treated as fully consumed. */ readonly size: v.SchemaWithPipe, v.IntegerAction, v.MinValueAction]>; readonly mtimeMs: v.NumberSchema; /** Bytes consumed, always ending on a newline. */ readonly offset: v.SchemaWithPipe, v.IntegerAction, v.MinValueAction]>; /** * Fingerprint of the last bytes of the consumed prefix, so growth is only * treated as an append once the prefix is confirmed to be the same prefix. * * Size-increase alone cannot tell an append from a path replaced by a larger, * unrelated file: resuming at the old offset would then start mid-record and * persist stale totals as the new complete state. `mtime` does not help — the * replacement has its own. Verifying costs one ~64-byte read against the * whole-file parse it protects. */ readonly tailHash: v.StringSchema; /** * The session id the fold fell back to for records that carry none of their * own (`file.sessionId`, else the filename). * * Entries are keyed by path, and `resolveSessionTranscripts` deliberately * lets one path be reported by different resumed sessions — dropping the hint * entirely when two sessions claim it (`roots.ts:174-178`). A cached fold made * under a different fallback attributes those records to the wrong session, so * a change here forces a re-parse. */ readonly fallbackSessionId: v.StringSchema; readonly sessions: v.ArraySchema; readonly usage: v.ArraySchema; readonly provider: v.OptionalSchema, undefined>; readonly inputTokens: v.NumberSchema; readonly outputTokens: v.NumberSchema; readonly cacheReadTokens: v.OptionalSchema, undefined>; readonly cacheWriteTokens: v.OptionalSchema, undefined>; }, undefined>, undefined>; readonly skillUsage: v.ArraySchema; readonly model: v.StringSchema; readonly inputTokens: v.NumberSchema; readonly cacheWriteTokens: v.OptionalSchema, undefined>; }, undefined>, undefined>; }, undefined>, undefined>; /** * A `Skill` invocation still waiting for the next assistant message to supply * the usage to attribute to it. Persisted rather than replayed because a * cursor that resumes past the boundary would drop that skill load * permanently, and silently — nothing downstream knows a load was expected * (docs/plans/default-transcript-discovery-plan.md → "Incremental re-read"). */ readonly pendingSkillLoads: v.ArraySchema; readonly skillName: v.StringSchema; }, undefined>, undefined>; /** * Dedup keys already counted in `sessions`. Claude Code writes one record per * content block sharing a `message.id`, and those records can straddle the * cursor, so resuming without them would re-count the tail message. * * Kept in full rather than trimmed to the tail. Only the last message's keys * can straddle *if* a message's records are always contiguous — which is the * observed habit, not a documented contract, and betting on it would * over-count silently. The cost is that this list grows with the session: * roughly one ~70-byte key per assistant message, so a few percent of the * transcript it saves re-parsing. Bound it only against a measurement. */ readonly seenMessageKeys: v.ArraySchema, undefined>; }, undefined>, undefined>; }, undefined>; export type TranscriptUsageCacheEntry = v.InferOutput; export type TranscriptUsageCache = v.InferOutput; export declare const emptyTranscriptUsageCache: () => TranscriptUsageCache; /** * Read the cache, degrading to empty on anything unexpected — missing file, * malformed JSON, a shape that fails validation, or a version this build does * not recognise. * * Same efficiency-not-correctness contract as `AUDEN_ACTION_CURSORS`: losing * the cache costs a full re-parse, never a wrong number, because * `ingestRunUsage` replaces per `(runId, model, source)` with whatever * full-session totals the next extraction sends. */ export declare function readTranscriptUsageCache(path: string): Promise; /** * Persist the cache, trimmed to `MAX_CACHED_TRANSCRIPTS` by the `order` list. * * Never throws: this runs on every default sync, and a state file that cannot * be written must not take the sync's pending actions down with it. */ export declare function writeTranscriptUsageCache(path: string, cache: TranscriptUsageCache): Promise; /** Move `path` to the front of the MRU list. */ export declare function touchCacheOrder(order: readonly string[], path: string): string[]; export {}; //# sourceMappingURL=usage-cache.d.ts.map