9.0 KiB
description, kind
| description | kind |
|---|---|
| The LSP capability seam (ctx.lsp): provider selection by file extension, four normalized code-navigation operations, and structured errors, for users and maintainers composing or extending code navigation. | package-reference |
@deepseek-ai/dsh-lsp
English | 中文
Summary
dsh-lsp provides the harness's language-server code navigation: an agent can go to a symbol's definition, find its references, jump to its implementations, or read hover documentation, and the code-navigation service (ctx.lsp) routes each query to the language-server provider that owns the file's extension. Providers register by branded id and file extension, so a provider swap never changes how navigation is requested or what the model sees. The service exposes exactly four read-only operations and no generic JSON-RPC escape hatch, and it contributes no prompt or tool schema itself — the model-facing lsp tool lives in dsh-tool-lsp. Compose it with a provider such as dsh-lsp-stdio and the tool to give agents precise navigation; this package does nothing on its own.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Mount a language-server provider and the lsp tool to give agents semantics-based code navigation that text search cannot reliably provide — distinguishing same-named functions, following import aliases, connecting an interface to its implementations, or reading inferred types. This package is the service those packages register against; it defines no UI, tool, or provider of its own.
When to choose it
Choose this service when a deployment wants model-visible code navigation backed by language servers. It covers read-only navigation — definitions, references, implementations, and hover — and deliberately omits mutations (rename, code actions, formatting), symbol lists, and diagnostics. The service is provider-neutral: local stdio servers, remote servers, and sandbox-native providers register the same way, so replacing the backend does not change what the model sees or how it asks.
Composing a navigation stack
The seam needs a provider and a consumer to do anything. A minimal composition mounts the service, a stdio provider, and the tool:
- name: '@deepseek-ai/dsh-fs-local'
- name: '@deepseek-ai/dsh-subprocess-local'
- name: '@deepseek-ai/dsh-lsp'
- name: '@deepseek-ai/dsh-lsp-stdio'
- name: '@deepseek-ai/dsh-tool-lsp'
Server commands, extension mappings, and the filesystem/subprocess pairing are configured in the provider and tool packages; see dsh-lsp-stdio and dsh-tool-lsp.
The four operations
Each query asks one of four semantic questions at a cursor position in a source file; results are normalized locations or hover content, never raw protocol payloads.
| Operation | What the agent gets |
|---|---|
goToDefinition |
The declaration site(s) of the symbol at the cursor |
findReferences |
Every reference, always including the declaration |
goToImplementation |
The concrete implementation site(s) |
hover |
Normalized documentation for the symbol, or none |
findReferences always includes declarations, so impact analysis never misses the defining site. Positions are zero-based UTF-16 on the wire; the model-facing tool accepts one-based cursor coordinates and converts them.
Failures and recovery
A query fails with the structured error LSP_UNAVAILABLE when no registered provider handles the file's extension — add a provider for that extension or query a supported file. Invalid or conflicting provider registrations fail with LSP_INVALID_PROVIDER or LSP_CONFLICT before any route is published, and a query against a disposed provider fails with LSP_DISPOSED. Consumers catch LspError and route on its stable code; through the tool, these surface as error results the model can read.
Understand the implementation
Implementation internals — click to expand
This section explains the design decisions behind the seam and where the code realizes them; observable behavior is covered in Use this package.
Design philosophy
- Capability seam, Service Definition role. The package owns
ctx.lspand the provider registry; providers register capabilities, not tools, anddsh-tool-lspis the only owner of the model-facing surface. - Atomic registration.
registerProvider()validates and conflict-checks everything before mutating: an invalid or conflicting registration publishes nothing, and its disposer releases the id and every extension reservation together. - Order-independent selection.
query()routes by the file's final extension, normalized to lowercase leading-dot form; registration and HMR order never change routing. The language id only synchronizes the transient document and never participates in selection. - Closed vocabulary. The four-operation union is closed — adding an operation is a compile-enforced change across the seam, providers, and the tool. There is no JSON-RPC escape hatch, and every request field is required, so there is no
resolve()step. - Provider-owned workspace coordinate. Location results carry the provider's canonical workspace URI, so consumers relativize file URIs in the execution world's namespace instead of applying host-platform path rules.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: Lsp service, registerProvider/query, finalExtension, LspError codes |
src/types.ts |
Seam vocabulary: request, result, provider, and service contracts |
src/brand.ts |
LspProviderId branded-id type and factory |
src/invariant.ts |
Invariant companion (no runtime invariant; routes are private atomic state) |
Registration and selection lifecycle
Registration and disposal run through ctx.effect(), so provider routes live and die with the registering fiber. finalExtension() splits on both path separators and returns '' for names without an extension or leading-dot dotfiles, which no route matches. LspError extends HarnessError with stable codes (LSP_INVALID_PROVIDER, LSP_CONFLICT, LSP_UNAVAILABLE, LSP_DISPOSED, LSP_UNSUPPORTED_OPERATION, LSP_MALFORMED_RESPONSE) that callers route on instead of parsing message.
Further Exploration
Read these pages when the package-level contract is not enough. They move from the shared navigation model to the provider, the tool, and the decision evidence.
- LSP navigation subsystem — operations, coordinates, requests and results, and
LspErrorcodes. - LSP capability seam Agent Note — design rationale, alternatives, and deliberately deferred API.
- dsh-lsp-stdio — the stdio provider that registers against this seam.
- dsh-tool-lsp — the model-facing tool over this seam.
- lsp group map — the three-package family and its related documentation.
Model Experience
Indirectly, through dsh-tool-lsp, which owns the model-facing lsp schema, prompt guidance, and rendered results while this registry contributes no prompt or schema itself.
KV Cache effect
No direct invalidation; dsh-tool-lsp owns request-prefix changes.
Known Limitations and Deferred Work
These limits define the seam's current scope. They are package constraints, not a task backlog.
- Exclusive extension ownership within one runtime — two providers cannot both claim
.ts, even with different language ids; overlaps fail registration. A deployment-configured selector above registrations is the intended extension, which can relax exclusive reservation without adding provider choice to model input (seam Agent Note). - Four read-only operations only — symbols and call hierarchy are deferred because they need different schemas; diagnostics need separate freshness and accumulation rules; mutations (rename, code actions, formatting) require separate tools with preview, permission, and write-policy integration.
- No observation API — availability is observed only by running
query()and routing the thrownLspErrorcodes; there is no provider-change event or capability-status query.
Dev Note
Working context for maintainers — click to expand
None.