/** * Index host — the main-thread coordinator for all codebase-index operations. * * Production mode runs every operation (full scans, per-file reindexes, * searches, stats) in a dedicated worker thread (`worker.ts`), so the * synchronous `node:sqlite` calls and the TypeScript parser can never block * the main event loop — the failure mode that used to freeze terminals is * structurally impossible. When the built worker file is not present (tests * run from source, exotic runtimes) or `WRONGSTACK_INDEX_INLINE=1` is set, * operations fall back to running inline through the same service layer. * * Concerns owned here, in front of either execution mode: * * 1. **Serialization** — every write run (startup scan, per-edit incremental, * external file-watch, manual reindex) goes through one process-wide * promise-chain mutex so two runs never race the same `index.db` writer. * 2. **Debounce** — rapid successive edits to the same file coalesce, then * files that become ready in the same event-loop turn share one index run. * 3. **Watchdog** — every operation is raced against a timeout. In worker * mode a timeout hard-terminates the worker (it respawns lazily on the * next request); inline it aborts the run's signal. Either way the mutex * chain always advances and the promise always settles. * 4. **Circuit breaker** — repeated failures/timeouts pause indexing instead * of queuing more work behind a wedged pipeline. See circuit-breaker.ts. * 5. **State tracking** — ready/indexing/progress flags + change listeners * for the TUI status chip and the search/stats tools' gating. */ import { type CircuitSnapshot } from './circuit-breaker.js'; import type { ContextResult } from './context-retrieval.js'; import { type IncomingCallsResult, type OutgoingCallsResult } from './index-service.js'; import { type ProjectIndexServerClientHealth, type ProjectIndexServerConnectionState, type ProjectIndexServerShutdownResult } from './project-server-client.js'; import type { CodeMapGraph, IndexResult, IndexStats } from './schema.js'; import type { CallRefsOpArgs, ContextOpArgs, FileGraphOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs, SymbolGraphOpArgs, VectorSearchOpArgs, VectorSearchOpResult } from './worker-protocol.js'; /** * The error read tools gate on, from this process's own runs and the shared * project server's activity. * * `remote?.lastError ?? local` let a stale local failure outlive the server's * later successful generation (`null ?? local` is `local`), so search, * incoming/outgoing calls and impact analysis kept refusing with "Index build * failed" against a healthy index. The newer report wins; a tie trusts the * server, which owns the index. */ export declare function resolveLastError(remote: { lastError: string | null; updatedAt: number | null; } | null | undefined, local: string | null, localAt: number): string | null; /** True once the first full-project index has completed (success or failure). */ export declare function isIndexReady(): boolean; /** * Mark the index as ready so downstream tools (codebase-search, codebase-stats) * don't gate on a startup index that never ran. */ export declare function setIndexReady(): void; /** True while an index build is actively running. */ export declare function isIndexing(): boolean; /** Current indexing progress: { currentFile, totalFiles, ready, indexing, circuit }. */ export declare function getIndexState(): { ready: boolean; indexing: boolean; currentFile: number; totalFiles: number; lastError: string | null; /** Detached per-project server connection owned by this client process. */ server: ProjectIndexServerConnectionState; /** Circuit-breaker state — `open` means indexing is paused after repeated failures. */ circuit: CircuitSnapshot; }; /** * Optional callback fired on every lifecycle transition (started, progress, * completed, failed). Plug into the event bus or a TUI dispatcher to surface * the indexing state in real time. */ type IndexStateListener = (state: ReturnType) => void; export declare function onIndexStateChange(listener: IndexStateListener): () => void; /** * Tear down the index host (worker + pending debounces). Call on process * shutdown; safe to call when nothing is running. */ export declare function shutdownCodebaseIndexHost(): Promise; /** True when the file's extension maps to a language the indexer can parse. */ export declare function isIndexableFile(filePath: string): boolean; export declare function runStartupIndex(opts: { projectRoot: string; indexDir?: string | undefined; force?: boolean | undefined; langs?: string[] | undefined; signal?: AbortSignal | undefined; /** Watchdog timeout for the whole run. Default: 120s. */ timeoutMs?: number | undefined; }): Promise; /** * Debounced, fire-and-forget incremental reindex of specific files. Used by the * per-edit toolCall middleware and the external file watcher. Non-indexable * paths are dropped. Errors are reported via the optional `onError` callback and * never thrown to the caller (background work must not crash a turn). */ export declare function enqueueReindex(opts: { projectRoot: string; files: string[]; indexDir?: string | undefined; debounceMs?: number | undefined; /** * Per-project trailing coalescing window. After a file's debounce timer * fires, the ready batch stays open for this long before flushing. Any file * whose timer fires within the window joins the same batch and resets the * timer (sliding). Default: 50ms; set to 0 for immediate flush. */ coalesceWindowMs?: number | undefined; /** Watchdog timeout per file. Default: 30s. */ timeoutMs?: number | undefined; onError?: ((err: unknown) => void) | undefined; }): Promise; /** Cancel all pending debounced reindexes. For teardown / tests. */ export declare function cancelPendingReindexes(): void; /** * Ranked symbol search against the index. The query runs in the index worker * (or inline in fallback mode) — the main thread never opens SQLite. Reads * don't take the write mutex (WAL readers don't block the writer) and don't * feed the circuit breaker; a wedged read still trips the watchdog, which * recycles the worker. */ export declare function searchCodebaseIndex(args: SearchOpArgs, opts?: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined; }): Promise; /** Index health/statistics, fetched off the main thread like searches. */ export declare function codebaseIndexStats(args: StatsOpArgs, opts?: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined; }): Promise; /** * Personalised retrieval — the single call that answers "which files does this * task touch?", served by the same per-project index process so the wiring * graph is built once per generation rather than once per query. */ export declare function codebaseContext(args: ContextOpArgs): Promise; /** * Nearest files to an already-embedded query vector. The model stays in the * caller's process; only numbers cross IPC. */ export declare function codebaseVectorSearch(args: VectorSearchOpArgs): Promise; /** Package dependency graph, served by the same per-project index process. */ export declare function packageGraphService(args: StatsOpArgs): Promise; /** File dependency graph, served by the same per-project index process. */ export declare function fileGraphService(args: FileGraphOpArgs): Promise; /** Symbol dependency graph, served by the same per-project index process. */ export declare function symbolGraphService(args: SymbolGraphOpArgs): Promise; /** Incoming call sites for a named symbol (who calls/uses this symbol?). */ export declare function incomingCallsService(args: CallRefsOpArgs): Promise; /** Outgoing call sites for a named symbol (what does this symbol call/use?). */ export declare function outgoingCallsService(args: CallRefsOpArgs): Promise; /** * Stop this project's detached index server. Unlike * shutdownCodebaseIndexHost(), this intentionally affects every connected * TUI/CLI/WebUI client for the project. */ export declare function shutdownCodebaseIndexServer(projectRoot: string, indexDir?: string, reason?: string): Promise; /** Probe the connected project server without starting a missing server. */ export declare function checkCodebaseIndexServerHealth(projectRoot: string, indexDir?: string, options?: { timeoutMs?: number | undefined; }): Promise; /** Ensure the detached project server exists and owns external watching. */ export declare function ensureCodebaseIndexServer(options: { projectRoot: string; indexDir?: string | undefined; watchExternal?: boolean | undefined; debounceMs?: number | undefined; coalesceWindowMs?: number | undefined; }): Promise; /** * Reset all process-global indexing state for test isolation. * * Vitest runs test files in parallel within the same process, so module-level * state (`_indexing`, `_ready`, `chain`, `indexCircuitBreaker`) leaks between * tests. Call this in `beforeEach` to ensure each test starts with a clean * slate. Production code should NEVER call this. */ export declare function resetIndexStateForTesting(): void; export {}; //# sourceMappingURL=background-indexer.d.ts.map