import { type RootsSeat } from "../tools/fs/safety.js";
import { type ReadDenyMatcher } from "../tools/fs/read-deny.js";
import type { HomeViewsCarrier } from "../tools/fs/read-deny-home.js";
import type { ContainmentFace } from "../tools/fs/containment-face.js";
import type { AgentTool, BoundFileObject, ExecutionEnv } from "../internal/harness-types.js";
/**
 * LSP code-intelligence tool (design/64 §13.1) — the app-dev命门 (go-to-def / find-refs / hover across
 * Dart/Kotlin/TS). A single `lsp` tool with a 9-operation enum, built over a pluggable {@link LspServerManager}.
 *
 * **Seam note (core verifying the design against real code):** the design assumed core spawns a language
 * server inside the ExecutionEnv via `execStream`, but `execStream` is OUTPUT-only — it has no stdin, so the
 * base seam can't drive a persistent stdio language server generically. So core ships the **tool + manager
 * seam + graceful degrade + git-ignore filter + formatters**; the *real* in-env manager (E2B LSP sidecar, or
 * a future bidirectional-process seam) is deployment-provided via {@link RunnerDeps.lspManager}. When no
 * server is available for a file's language the tool **degrades gracefully** ("fall back to Grep/Read")
 * rather than erroring — the design's hard constraint for remote portability.
 */
export type LspOperation = "goToDefinition" | "findReferences" | "hover" | "documentSymbol" | "workspaceSymbol" | "goToImplementation" | "prepareCallHierarchy" | "incomingCalls" | "outgoingCalls";
export declare const LSP_OPERATIONS: readonly LspOperation[];
/**
 * A source location (a definition/reference/implementation hit). `uri` is a file path or file:// URI.
 *
 * Base convention (the seam's factual contract, service[64] flag①): `line` is **1-based**, `character`
 * is **0-based** — the tool-schema convention the model is taught. Core passes positions through
 * VERBATIM in both directions; the `LspServerManager` implementation owns the conversion to/from the
 * LSP wire protocol (which is 0-based for BOTH) — e.g. request line−1 on the way in, result line+1 on
 * the way out, call-hierarchy items relayed as wire values it already converted.
 */
export interface LspLocation {
    uri: string;
    /** 1-based (tool convention — manager converts to the 0-based LSP wire). */
    line: number;
    /** 0-based (matches the LSP wire). */
    character: number;
    /** Optional one-line preview of the matched line. */
    preview?: string;
}
/** A symbol (document/workspace symbol, call-hierarchy item). Same base convention as {@link LspLocation}. */
export interface LspSymbolInfo {
    name: string;
    kind?: string;
    uri: string;
    /** 1-based (tool convention — manager converts to the 0-based LSP wire). */
    line: number;
    /** 0-based (matches the LSP wire). */
    character: number;
    /**
     * HRD-LSP-18: the server supplied NO range for this symbol — `line`/`character` are placeholders, not a
     * location. LSP allows exactly this for an unresolved `WorkspaceSymbol` (`{ name, kind, location: { uri } }`,
     * resolved later via `workspaceSymbol/resolve`), and the coercion below used to substitute `{0,0}` silently,
     * which the tool then rendered as a precise-looking `uri:1:1` the server never said. Set ⇒ the renderer
     * prints the file WITHOUT coordinates.
     */
    positionUnknown?: boolean;
}
/**
 * WHY a "none" result was "none". Absent `reason` = a GENUINE empty answer (the server
 * answered null/[] — today's behavior byte-for-byte, and what an external manager returning bare
 * `{kind:"none"}` keeps getting). The failure reasons exist because one literal "No results." used to cover
 * both real emptiness and every transport-level fault — a findReferences that TIMED OUT read as "no
 * references, safe to delete" (CC 2.1.220 keeps these apart: `Error performing ${operation}: ...` vs the
 * plain empty-result strings, (pretty220.js:474840, historical; not relocated in 250 — see cc-250 anchors)).
 */
export type LspNoneReason = "unsupported_operation" | "server_error" | "timeout" | "cancelled" | "server_terminated" | "no_call_hierarchy_item"
/** HRD-LSP-7: the QUERIED file could not be read for the didOpen/didChange re-sync (deleted between the
 *  tool's precheck and the op, EACCES, non-regular, over the size cap, a symlinked over-cap target). The
 *  session used to swallow that and query the server anyway, so the resulting `null`/`[]` was
 *  indistinguishable from a confirmed-empty answer — the exact confusion the reason set exists to end. */
 | "file_sync_failed";
export type LspResult = {
    kind: "locations";
    locations: LspLocation[];
} | {
    kind: "symbols";
    symbols: LspSymbolInfo[];
} | {
    kind: "hover";
    contents: string;
} | {
    kind: "none";
    reason?: LspNoneReason;
    detail?: string;
};
/** The property {@link brandLspFailure} sets — a STRING property, not a class: the brand must survive a
 *  dual ESM/CJS build and duplicated module instances, where `instanceof` does not (same structural-
 *  detection philosophy as {@link pathToUri}'s form detection — never key on identity you don't control). */
export declare const LSP_FAILURE_BRAND = "lspFailureReason";
/** Brand a transport-layer error with its {@link LspNoneReason} at the THROW site — the throw site is the
 *  only place that still holds the discriminator (e.g. the JSON-RPC error CODE, which message-regex
 *  classification cannot recover, and which differs textually across transports). */
export declare function brandLspFailure<E extends Error>(e: E, reason: LspNoneReason, detail?: string): E;
/** Duck-typed read of {@link brandLspFailure}'s brand (membership-checked — an arbitrary string-valued
 *  property of the same name on a third-party error never becomes a reason we didn't define). */
export declare function lspFailureOf(e: unknown): {
    reason: LspNoneReason;
    detail?: string;
} | undefined;
/**
 * The abort signal for work shared by several callers: it fires only once EVERY registered caller has
 * abandoned it, so one caller's cancellation can never cancel another caller's work. A joiner that
 * passes no signal never abandons (the job then runs to completion regardless of the others).
 *
 * The scope is single-use: once it has aborted, the owner must NOT let a new caller join it (the job it
 * guards is already dying) — check {@link signal}`.aborted` before reusing an in-flight entry.
 */
export declare class SharedAbortScope {
    private readonly controller;
    private joiners;
    private abandoned;
    get signal(): AbortSignal;
    /** Register one caller (its `signal` may be absent = it never abandons). */
    join(signal?: AbortSignal): void;
    private abandon;
}
/**
 * Give ONE caller its own settle on a shared job: the caller's `signal` resolves it with `onAbort()`
 * without disturbing the job (which other callers may still need). The job's own settle is always
 * observed, so a later rejection can never surface as an unhandled rejection.
 */
export declare function settleOnAbort<T>(job: Promise<T>, signal: AbortSignal | undefined, onAbort: () => T): Promise<T>;
/**
 * HRD-LSP-5: resolve a model-supplied `filePath` against the TASK's cwd, once, for every leg.
 *
 * The tool used to join a relative path to `env.cwd` for the existence/size PRECHECK only and hand the
 * ORIGINAL string to `sessionFor` → `readText`/`pathToUri`, which resolve against the HOST process cwd.
 * With a task cwd that is not the host cwd (worktree/sandbox/service worker — i.e. every non-trivial
 * deployment) the two legs then addressed two different files: the precheck passed on the real file
 * while the server was handed `file:///<host-cwd>/<rel>`, and the model got a confident "No results."
 * for a file that was never analyzed.
 *
 * Absolute-form input (either family) passes the join; "no cwd known" keeps the caller's spelling.
 *
 * REF-C3 — the result is dot-folded, which is what makes this function THE key-minting seam: every URI
 * for a file (didOpen, publishDiagnostics, the diagnostics registry's `fileEdited` clear, buildRequest)
 * comes from here, and `pathToUri`'s POSIX branch is a hand-assembled per-segment encode that keeps a
 * `.` segment verbatim. Without the fold, `./src/a.ts` and `src/a.ts` minted two keys for one file:
 * delivered diagnostics never cleared, and the server held a duplicate document. The edit-nudge leg in
 * prepare-task.ts had grown its own copy of the fold for exactly this reason; it now calls this instead,
 * so "one file, one key" is a property of the code rather than of two spellings staying in step.
 */
export declare function resolveLspPath(filePath: string, cwd?: string): string;
/** Request positions use the same base convention as {@link LspLocation}: line 1-based, character 0-based. */
export interface LspRequestParams {
    filePath: string;
    /** 1-based (tool convention) — the manager converts to the 0-based LSP wire (line−1). */
    line?: number;
    /** 0-based (matches the LSP wire). */
    character?: number;
    /** For workspaceSymbol: the symbol query string. */
    symbol?: string;
}
/** An active language-server session for one language, scoped to a workspace. */
export interface LspSession {
    /** `gate` = the caller's read face at the session's file reads ({@link LspReadGate}); absent ⇒ a caller with none. */
    request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal, gate?: LspReadGate): Promise<LspResult>;
}
/** What a session's read gate answered for one file: the object its judgment named, opened and HELD (the environment's
 *  `openBound`) under the canonical key it was judged as — the session reads that object, never the name again, and closes
 *  it — or a refusal with its reason. */
export type LspReadAdmission = {
    readonly ok: true;
    readonly key: string;
    readonly object: BoundFileObject;
} | {
    readonly ok: false;
    readonly reason: string;
};
/** May a session read `filePath` (a path {@link resolveLspPath} minted) for this caller, now — and if so, WHAT: the gate
 *  admits an object, not a name ({@link LspReadAdmission}). A session reads a file only from the object its caller's gate
 *  admitted, and closes an opened document the gate does not admit. */
export type LspReadGate = (filePath: string, signal?: AbortSignal) => Promise<LspReadAdmission>;
/** The read face one LSP mount reads under: the seats the fs read tools take, so the gate and the tool resolve and judge
 *  a `filePath` alike (see {@link LspToolOptions.readFace}). */
export interface LspReadFace {
    /** The canonical task root: the primary containment root, and the base when no working directory is tracked. */
    readonly rootCanonical: string;
    /** The extra read roots (write-capable and read-only), read per call. */
    readonly roots?: RootsSeat;
    /** The tracked working directory; `current` is read per call as the base of a relative `filePath`. */
    readonly cwdRef?: {
        readonly current: string;
    };
    /** The read-deny table in force, with its placement's home views. */
    readonly readDeny?: ReadDenyMatcher & HomeViewsCarrier;
    /** The read containment face. Absent ⇒ `roots`. */
    readonly face?: ContainmentFace;
    /** The file read station's admission: an out-of-root target the gate asked about and let through, read once. */
    readonly admitted?: (toolCallId: string | undefined, canonicalKey: string) => boolean;
    /** Settles once the read-deny table and read roots a judgment takes are ready (a Runner compiles them per read of the
     *  person's rules): a call's own (`toolCallId`), or — with none — the run's newest; at `signal` too; never rejects;
     *  absent ⇒ ready. The tool waits for it before it takes its call's; the session's own reads before they judge. */
    readonly ready?: (toolCallId: string | undefined, signal?: AbortSignal) => Promise<void>;
}
/** The base a relative `filePath` resolves against: the catalog declaration through the one selector the fences read it
 *  with; with no read face the env's working directory is the only base there is. */
export declare function lspBaseOf(readFace: LspReadFace | undefined, env?: ExecutionEnv): string | undefined;
/** The session gate over a read face — the bound read a user attachment makes, at the session's read: the name is judged
 *  as `Read` judges its target (a judgment that throws refuses), the object the judgment named is opened and held
 *  (`openBound`; an environment without it cannot bind and refuses), and the name, judged again, must still land on the
 *  held key. The admission carries the held object; the session reads only it. `authorizedKey` = the canonical key this
 *  call's target was authorized as (the station's one-read admission already spent on it): a spelling that still lands
 *  there passes outside the roots, nothing else does. */
export declare function lspReadGateOf(env: ExecutionEnv, face: LspReadFace, authorizedKey?: string): LspReadGate;
/** Why a file the language server named is withheld: a read-deny row reaches one of its views, or its views could not be
 *  read (`unreadable` — the judgment's absence word). */
export type LspListedWithholding = {
    readonly pattern: string;
} | {
    readonly unreadable: true;
};
/**
 * The files a language server named (result locations, symbols, drained diagnostics), judged as ONE bounded batch against
 * the read face's deny table on every view of each file — the spelling, the canonical target, the home view and the link
 * hops, through the same `resolveKey` a read target meets (containment is not this question: the face is judged open).
 * Fail-closed: a file whose views cannot be read, and every file when the batch outlives the gate's deadline, is withheld.
 * Kept files are absent from the result.
 */
export declare function lspListedWithheld(env: ExecutionEnv, face: LspReadFace, uris: readonly string[], signal?: AbortSignal): Promise<ReadonlyMap<string, LspListedWithholding>>;
/**
 * A JSON-RPC transport to ONE language server, POST-`initialize`. The DEPLOYMENT provides the concrete
 * transport; the protocol logic ({@link buildRequest}/`parseResult`, `TransportLspSession`) drives the LSP
 * over this single seam, so the two lanes can't drift:
 *  - **TOC** = {@link StdioLspTransport} (spawn the server LOCALLY; Content-Length JSON-RPC over its stdio — a
 *    faithful copy of CC's `services/lsp/LSPClient`);
 *  - **TOB** = a WS bridge (the server runs INSIDE a remote sandbox, reached over a WebSocket — service's
 *    `ws-transport`, because `execStream` is output-only so the base seam can't drive a stdio server remotely).
 */
export interface LspTransport {
    /** A JSON-RPC request; resolves the `result` (or rejects on a JSON-RPC error / timeout / abort). */
    request(method: string, params: unknown, signal?: AbortSignal): Promise<unknown>;
    /** A JSON-RPC notification (no response) — e.g. `textDocument/didOpen`. */
    notify(method: string, params: unknown): void;
    /**
     * design/121: subscribe to server→client NOTIFICATIONS (a message with a `method` and no `id` —
     * `textDocument/publishDiagnostics`, `window/logMessage`, …). Returns an unsubscribe. Optional and
     * ADDITIVE: a transport without it — or with no subscriber — behaves exactly as before (notifications
     * dropped). The transport must swallow a throwing handler (a bad consumer must never kill the read loop).
     */
    onNotification?(handler: (method: string, params: unknown) => void): () => void;
    close(): Promise<void>;
    /** True once the underlying connection is gone (closed/errored) — the manager evicts + reopens. Optional so
     *  a test mock stays minimal. */
    readonly closed?: boolean;
}
/** Reads a workspace file's CURRENT text for the session's didOpen/didChange re-sync BY NAME — the read of a caller with no
 *  gate (no read face) only; a gated read is the admitted held object's ({@link LspReadAdmission}). TOC = local fs; TOB =
 *  the sandbox ExecutionEnv. */
export type LspReadText = (filePath: string, signal?: AbortSignal) => Promise<string>;
/** CC 2.1.220's LSP file cap (10MB — `File too large for LSP analysis`, (pretty220.js:474745, historical; not relocated in 250 — see cc-250 anchors)). Single-sourced
 *  in core: the TOOL-layer check in {@link createLspTool} (the model-visible enforcement, CC-parity) and the
 *  defense-in-depth throw in the engine's `defaultLspReadText` must agree on one number. */
export declare const MAX_LSP_FILE_BYTES: number;
/**
 * Resolves a language server session for a file. Returns `undefined` when no server is available for the
 * file's language — the tool then degrades gracefully (design/64 §13.1 remote-portability constraint).
 */
export interface LspServerManager {
    /**
     * Resolve a session for `filePath`'s language, or `undefined` to degrade. `env` is the task's
     * ExecutionEnv (the SAME sandbox the agent runs in) — core passes it because a real in-env language
     * server must run inside that sandbox, and core already holds the per-task env at mount time. Supplying
     * it here means a manager can be **stateless** (it doesn't need a sessionId→env registry). It is
     * optional/last for back-compat: a manager that resolves the env another way (e.g. a sessionId registry
     * keyed off `executionEnvFactory`) may ignore it.
     */
    sessionFor(filePath: string, signal?: AbortSignal, env?: ExecutionEnv): Promise<LspSession | undefined>;
    /**
     * HRD-LSP-14 — OPTIONAL: why the last {@link sessionFor} for this file returned `undefined`, as a phrase
     * for the model-facing degrade line ("no language server for its language in this environment" is used
     * when absent). `undefined` ⇒ nothing more specific to say. ADDITIVE: a manager without it behaves
     * exactly as before. Exists because `undefined` collapses "this language has no server configured",
     * "the binary is not installed" and "the server is installed and fails to start" into one sentence that
     * is only true for the first — and the operator can only act on the third if someone says it.
     */
    unavailableReason?(filePath: string, env?: ExecutionEnv): string | undefined;
    /** design/121: the manager's workspace diagnostics registry, when the deployment's manager supports
     *  pushed diagnostics (NodeLspManager always does). The Runner drains it at turn boundaries to inject
     *  `<new-diagnostics>` + emit the structured `diagnostics` frame. Absent ⇒ no diagnostics lane. */
    readonly diagnostics?: import("./lsp-diagnostics.js").LspDiagnosticsRegistry;
}
export interface LspToolOptions {
    /** Drop results in files ignored by git (CC parity, design/64 §16/§17). Default: keep all. */
    isPathIgnored?: (uri: string) => boolean | Promise<boolean>;
    /** The task's ExecutionEnv, passed through to {@link LspServerManager.sessionFor} (the agent's sandbox —
     *  where an in-env language server must run). The Runner wires this from the per-task env. */
    env?: ExecutionEnv;
    /**
     * The read face this mount reads under (needs `env`). PRESENT ⇒ every read the tool causes passes it: the `filePath`
     * target is resolved against the declared base (the tracked working directory, else the root) and fenced as `Read`
     * fences its own — containment with the station's one-read admission, the read-deny table, blocked devices; the file
     * preconditions (exists / a regular file / the size cap) are read from the object that judgment named, held at its key
     * (the environment's `openBound`), never from the spelling again; the session reads an opened file only from the
     * object the face admitted and held (an environment without `openBound` reads nothing) and closes one it does not
     * admit; every location or symbol in a file the deny table reaches is withheld, and the result says how many. ABSENT ⇒
     * no read face judged this mount (a library-direct tool with no gate): a relative `filePath` resolves against
     * `env.cwd`, the preconditions and the session's reads go by name, nothing is fenced and nothing is withheld.
     */
    readFace?: LspReadFace;
}
/** Build the `lsp` tool over a {@link LspServerManager}. Mounted only when a manager is wired (opt-in). */
export declare function createLspTool(manager: LspServerManager, opts?: LspToolOptions): AgentTool;
/**
 * A git-check-ignore-based path filter for {@link LspToolOptions.isPathIgnored} (CC parity). Best-effort:
 * if git isn't available or the check fails, nothing is treated as ignored (keep all). Caches per path.
 */
export declare function gitCheckIgnoreFilter(env: ExecutionEnv, root: string): (uri: string) => Promise<boolean>;
