/** * Index staleness detection. * * Compares the newest XML mtime in the active model's metadata folder with * the index's last_indexed_at timestamp and produces a warning when the * workspace has changed since the last (re)index. */ /** Drop cached scans (test isolation, or after a known workspace write). */ export declare function resetMetadataMtimeCache(): void; export interface MtimeScanResult { /** Epoch ms of the newest .xml/.label.txt file found */ newestMtime: number; newestFile: string; scannedFiles: number; /** True when the scan stopped at MAX_SCANNED_FILES */ truncated: boolean; } /** * Recursively find the newest metadata file mtime under rootDir. * Returns null when the directory does not exist or contains no metadata files. * * Cached for SCAN_CACHE_MS per root — see that constant for why. */ export declare function findNewestMetadataMtime(rootDir: string): MtimeScanResult | null; /** * A scan result, or the fact that one is being computed. * * The scan itself cannot be made cheap — it is up to MAX_SCANNED_FILES * synchronous `statSync` calls, and on a cold Windows file cache the first one * is seconds, not milliseconds. What it CAN stop being is the first thing a * session waits for: `get_workspace_info` averaged 31.5 s over 31 real calls and * is neither bridge-gated nor DB-gated, so the cost was inside the tool. */ export type MtimeScanState = { status: 'ready'; result: MtimeScanResult | null; scannedAt: number; } | { status: 'pending'; }; /** * Cached scan, computed in the background when the caller cannot wait. * * `blocking: true` is the old behaviour — scan now, answer now — and is what * `diagnostics: true` uses, because the whole point of diagnostics is the full * picture. `blocking: false` answers from cache and schedules the walk on a later * tick, so the request path never carries it. * * 'pending' is returned only while NO scan has ever completed for this root. Once * one has, an expired entry is served — with the time it was taken — and a refresh * is scheduled behind it. Expiring back to 'pending' made the "call again for the * verdict" the caller is told to act on unwinnable: the entry lives SCAN_CACHE_MS * (30 s) and an agent that re-asks any later than that gets the identical * "still running" line, forever. Observed live on 2026-09-07: three * get_workspace_info calls 105 s apart, byte-identical output every time. */ export declare function findNewestMetadataMtimeCached(rootDir: string, opts?: { blocking?: boolean; }): MtimeScanState; export interface StalenessReport { status: 'fresh' | 'stale' | 'unknown' | 'pending'; /** Full "## Index Freshness" section — diagnostics=true only. */ lines: string[]; /** * Compact default: one `Index : …` line, and the fix only when the index is * actually stale. The scan detail (newest file, files scanned) is diagnostics * material — it changes nothing about what the agent should do next. */ compactLines: string[]; } /** * Compare workspace mtimes against the index timestamp and render a report * section for get_workspace_info. * * `blocking` defaults to TRUE so every existing caller keeps the full answer; * the request path passes `{ blocking: false }` and gets a 'pending' report on a * cold cache — see findNewestMetadataMtimeCached. */ export declare function checkIndexStaleness(lastIndexedAt: string | null, modelMetadataDir: string | null, opts?: { blocking?: boolean; }): StalenessReport; //# sourceMappingURL=indexStaleness.d.ts.map