import type { AgentTool, ExecutionEnv } from "../internal/harness.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; } export type LspResult = { kind: "locations"; locations: LspLocation[]; } | { kind: "symbols"; symbols: LspSymbolInfo[]; } | { kind: "hover"; contents: string; } | { kind: "none"; }; /** 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 { request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal): Promise; } /** * 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; /** 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; /** 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. TOC = local fs; TOB = * the sandbox ExecutionEnv. */ export type LspReadText = (filePath: string, signal?: AbortSignal) => Promise; /** * 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; /** 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 * `` + 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; /** 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; } /** 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; //# sourceMappingURL=lsp.d.ts.map