import type { LspDiagnostic, LspPosition } from "./client.js"; import { type EditSource } from "./edit-telemetry.js"; import { type LspClientPool } from "./pool.js"; import { type LspServerSpec } from "./servers.js"; export interface LspManagerOptions { /** Server catalog override — tests inject a fake-server spec here. */ catalog?: readonly LspServerSpec[]; /** Hard diagnostics budget once a client has served at least one file. */ warmBudgetMs?: number; /** Hard budget for a client's very first file (spawn + init + indexing). */ firstBudgetMs?: number; /** Grace period for a corrected publish after an empty cold-load result. */ settleMs?: number; /** Maximum number of per-file latest outcomes retained. */ snapshotLimit?: number; /** * Client pool backing this manager. Defaults to the daemon-wide singleton so * sessions share servers; injectable so tests get isolation. */ pool?: LspClientPool; } export type LspOutcomeKind = "diagnostics" | "clean" | "low_confidence" | "timeout" | "unsupported" | "unavailable" | "server_failed"; interface LspOutcomeBase { kind: LspOutcomeKind; filePath: string; updatedAt: number; } export type LspDiagnosticOutcome = (LspOutcomeBase & { kind: "diagnostics"; diagnostics: LspDiagnostic[]; formatted: string; }) | (LspOutcomeBase & { kind: Exclude; }); /** * A navigation answer, or the specific reason there isn't one. Shares its * failure vocabulary with `LspDiagnosticOutcome` so both surfaces degrade in * the same, legible way. */ export type LspNavigationOutcome = { kind: "ok"; filePath: string; serverId: string; value: T; } | { kind: "timeout" | "unsupported" | "unavailable" | "server_failed"; filePath: string; serverId?: string; message?: string; }; /** * Per-session view over the process-wide language-server pool (see pool.ts). * * The manager owns only session-scoped state — per-file outcome snapshots and * which keys have gone warm — while the servers themselves are shared by every * session in the process and reclaimed when idle. That split is what stops two * windows open on one repo from running two full tsserver stacks. */ export declare class LspManager { private readonly cwd; private readonly catalog; private readonly warmBudgetMs; private readonly firstBudgetMs; private readonly settleMs; private readonly snapshotLimit; private readonly pool; /** * Keys that have completed a diagnostics pass, mapped to the pool generation * that served it. * * Storing the generation rather than a bare flag is what keeps "warm" honest * across idle reclamation: once the pool retires a server, the replacement is * genuinely cold again, and treating it as warm would give it the 3s budget * instead of 8s AND skip the cold-load settle guard — turning tsserver's * premature empty publish into a reported "clean", which is a false all-clear. */ private readonly warmKeys; private readonly latestOutcomes; private shutDown; constructor(cwd: string, options?: LspManagerOptions); /** * Compatibility surface used by edit/write tools. Diagnostics remain visible; * every clean/degraded outcome remains the exact historical empty string. * * When a baseline is available, the diagnostics are labelled as caused by * this edit or as pre-existing. Without that label the two are * indistinguishable in the output, so the model cannot tell "you just broke * this" from "this file was already failing" — and neither can we. */ diagnosticsAfterWrite(filePath: string, content: string, source?: EditSource): Promise; /** * How many errors this file had at the end of the last collect, or null when * nothing is known about it yet. * * Deliberately cache-only: re-running a full collect against the pre-edit * content would double every edit's LSP cost to answer a question that is * usually already answered. Degraded outcomes (timeout, server_failed) stay * null rather than being read as "clean", which would invent regressions. * * simplification: the cached count describes the file as of OUR last write, * so an edit made outside the session in between is attributed to this edit. * Upgrade path: stamp the outcome with the file's mtime and drop the baseline * when it no longer matches. */ private errorBaseline; /** Label the diagnostics against the baseline; empty when nothing is known. */ private attribute; /** Collect diagnostics with explicit confidence/failure evidence. */ diagnosticsAfterWriteDetailed(filePath: string, content: string): Promise; /** Latest bounded evidence for one normalized absolute/relative file path. */ getLatestOutcome(filePath: string): LspDiagnosticOutcome | undefined; /** * Has this session already completed a pass against the server that is live * NOW? False once the pool has rebuilt it, because the replacement is cold. */ private isWarm; /** * Retained stderr of a language server this session is using, for failure * reporting when a server accepts a document and then never answers. */ serverStderrTail(): Promise; /** Newest retained per-file evidence snapshots. */ getLatestOutcomes(): LspDiagnosticOutcome[]; /** * Release this session's claim on every server it used. Safe in process exit * handlers. * * This drops REFERENCES, not processes: a server another session still holds * keeps running, and one left with no holders is shut down immediately. The * name is kept because every caller wires it into an exit path. */ shutdownAll(): void; private outcome; private record; private collect; /** The diagnostics exchange itself, against an initialized live client. */ private collectFrom; /** Where the symbol under `position` is defined. */ definition(filePath: string, content: string, position: LspPosition): Promise>; /** Every reference to the symbol under `position`. */ references(filePath: string, content: string, position: LspPosition): Promise>; /** Symbol outline for one document. */ documentSymbols(filePath: string, content: string): Promise>; /** Type/signature summary under `position`. */ hover(filePath: string, content: string, position: LspPosition): Promise>; /** * Shared navigation path: resolve a server, sync the document, run one * request inside the same warm/first budget split diagnostics use. * * The outcome kinds match `LspDiagnosticOutcome`'s degraded kinds exactly * (`timeout` / `unsupported` / `unavailable` / `server_failed`), because a * navigation tool that answers "no results" when the truth is "no server" * teaches the model that the symbol does not exist. */ private navigate; } export {}; //# sourceMappingURL=manager.d.ts.map