feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
/ * *
* Model - facing ` lsp ` tool over ` ctx.lsp ` . One read - only tool with four operations
2026-07-21 13:29:40 +08:00
* ( ` goToDefinition ` / ` findReferences ` / ` goToImplementation ` / ` hover ` ) ; it converts one - based UTF - 16
* cursor coordinates to the seam ' s zero - based positions , requires the session workspace with no
* fallback , caps and renders results , and attaches a configurable timeout budget for
2026-08-13 00:36:22 +08:00
* ` dsh-tool-call-timeout-policy ` to enforce . It runtime - injects only ` tools ` , ` lsp ` , and ` systemPrompt ` and
2026-07-21 13:29:40 +08:00
* imports no provider .
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
*
* Namespace plugin ( named exports , no default export ) .
* @module @deepseek - ai / dsh - tool - lsp
* /
build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.
Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.
The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.
Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:06 +08:00
import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
import { defineTool } from '@deepseek-ai/dsh-tools'
import { LspError } from '@deepseek-ai/dsh-lsp'
import type { } from '@deepseek-ai/dsh-lsp'
2026-07-21 13:29:40 +08:00
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
2026-08-30 02:29:52 +08:00
import { assertNever } from '@deepseek-ai/dsh-util-values'
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
import {
DEFAULT_MAX_LOCATIONS ,
2026-07-21 13:29:40 +08:00
DEFAULT_MAX_RESULT_CHARS ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
formatHover ,
formatLocations ,
LSP_OPERATIONS ,
parseLspArgs ,
presentLspCall ,
} from './render.ts'
import { sessionCwd } from './session-cwd.ts'
export {
DEFAULT_MAX_LOCATIONS ,
2026-07-21 13:29:40 +08:00
DEFAULT_MAX_RESULT_CHARS ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
formatHover ,
formatLocations ,
LSP_OPERATIONS ,
parseLspArgs ,
presentLspCall ,
renderUri ,
} from './render.ts'
export { sessionCwd } from './session-cwd.ts'
/** Cordis plugin name for loader diagnostics. */
export const name = 'tool-lsp'
/** Services required by this plugin. */
export const inject = [ 'tools' , 'lsp' , 'systemPrompt' ]
/** Default tool-call timeout budget (ms), covering the queued open/query/close lifecycle. */
export const DEFAULT_LSP_TOOL_TIMEOUT_MS = 60 _000
/** The stable system-prompt guidance positioning LSP as a precision aid. */
export const LSP_PROMPT_TEXT =
2026-07-21 13:29:40 +08:00
'Use search/read for ordinary navigation. Use lsp when textual matches are ambiguous or before a change requires precise definitions, implementations, or references. Positions are one-based line and character (UTF-16) at the cursor; an off-symbol position may return no results. findReferences always includes the declaration.'
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
/** Plugin configuration: result caps and the timeout budget. */
export interface Config {
/** Largest number of rendered locations before an omission marker (default 100). */
maxLocations? : number
2026-07-21 13:29:40 +08:00
/** Largest complete rendered result in characters, including truncation metadata (default 16000). */
maxResultChars? : number
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
/** Tool-call timeout budget in ms (default 60000). */
timeoutMs? : number
}
export const Config : z < Config > = z . object ( {
maxLocations : z.number ( ) . default ( DEFAULT_MAX_LOCATIONS ) ,
2026-07-21 13:29:40 +08:00
maxResultChars : z.number ( ) . default ( DEFAULT_MAX_RESULT_CHARS ) ,
timeoutMs : z.number ( ) . max ( MAX_TIMER_DELAY_MS ) . default ( DEFAULT_LSP_TOOL_TIMEOUT_MS ) ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
} )
type ResolvedConfig = Required < Config >
2026-07-21 19:15:18 +08:00
const LSP_POSITION_OUTPUT_SCHEMA = {
type : 'object' ,
additionalProperties : false ,
properties : {
line : { type : 'integer' , required : true } ,
character : { type : 'integer' , required : true } ,
} ,
} as const
const LSP_RANGE_OUTPUT_SCHEMA = {
type : 'object' ,
additionalProperties : false ,
properties : {
start : { . . . LSP_POSITION_OUTPUT_SCHEMA , required : true } ,
end : { . . . LSP_POSITION_OUTPUT_SCHEMA , required : true } ,
} ,
} as const
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
/ * *
* Register the ` lsp ` tool and its system - prompt guidance .
* @param ctx - the plugin context ( must inject ` tools ` , ` lsp ` , ` systemPrompt ` ) .
* @param config - the resolved plugin configuration .
* /
export function apply ( ctx : Context , config : Config ) : void {
const resolved = config as ResolvedConfig
assertPositiveInteger ( 'maxLocations' , resolved . maxLocations )
2026-07-21 13:29:40 +08:00
assertPositiveInteger ( 'maxResultChars' , resolved . maxResultChars )
assertTimer ( 'timeoutMs' , resolved . timeoutMs )
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
2026-08-25 11:06:01 +08:00
ctx . systemPrompt . section ( {
name : 'tool:lsp' ,
2026-08-30 02:29:52 +08:00
order : ctx.systemPrompt.getSectionOrder ( 'TOOL_LSP' ) ,
2026-08-25 11:06:01 +08:00
text : LSP_PROMPT_TEXT ,
} )
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
ctx . tools . register ( defineTool ( {
name : 'lsp' ,
description :
2026-07-21 13:29:40 +08:00
'Query a language server for precise code navigation. operation is one of goToDefinition, findReferences, goToImplementation, hover. line and character are one-based UTF-16 cursor coordinates. findReferences includes the declaration.' ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
parameters : {
operation : {
type : 'string' ,
required : true ,
enum : [ . . . LSP_OPERATIONS ] ,
2026-07-21 13:29:40 +08:00
description : 'goToDefinition, findReferences, goToImplementation, or hover.' ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
} ,
file_path : { type : 'string' , required : true , description : 'The source file to query, relative to the workspace or absolute.' } ,
line : { type : 'number' , required : true , description : 'One-based line of the cursor.' } ,
character : { type : 'number' , required : true , description : 'One-based UTF-16 column of the cursor.' } ,
} ,
2026-07-21 19:15:18 +08:00
output : {
schema : {
oneOf : [
{
type : 'object' ,
additionalProperties : false ,
properties : {
kind : { type : 'string' , required : true , const : 'locations' } ,
locations : {
type : 'array' ,
required : true ,
items : {
type : 'object' ,
additionalProperties : false ,
properties : {
uri : { type : 'string' , required : true } ,
range : { . . . LSP_RANGE_OUTPUT_SCHEMA , required : true } ,
} ,
} ,
} ,
2026-07-29 04:19:18 +08:00
resolvedWorkspaceUri : { type : 'string' , required : true } ,
2026-07-21 19:15:18 +08:00
} ,
} ,
{
type : 'object' ,
additionalProperties : false ,
properties : {
kind : { type : 'string' , required : true , const : 'hover' } ,
hover : {
required : true ,
oneOf : [
{ type : 'null' } ,
{
type : 'object' ,
additionalProperties : false ,
properties : {
contents : { type : 'string' , required : true } ,
range : LSP_RANGE_OUTPUT_SCHEMA ,
} ,
} ,
] ,
} ,
} ,
} ,
] ,
} ,
render : ( _args , value ) = > {
switch ( value . kind ) {
case 'locations' :
2026-07-29 04:19:18 +08:00
return [ { type : 'text' , text : formatLocations ( value . locations , value . resolvedWorkspaceUri , resolved . maxLocations , resolved . maxResultChars ) } ]
2026-07-21 19:15:18 +08:00
case 'hover' :
return [ { type : 'text' , text : formatHover ( value . hover , resolved . maxResultChars ) } ]
/* v8 ignore next -- exhaustive over the output schema's closed union; unreachable. */
default :
return assertNever ( value , 'tool-lsp output' )
}
} ,
} ,
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
timeoutMs : resolved.timeoutMs ,
2026-07-21 19:15:18 +08:00
async execute ( args , exec ) {
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
const input = parseLspArgs ( args )
const workspaceRoot = sessionCwd ( exec )
if ( workspaceRoot === undefined ) {
throw new LspError ( 'the lsp tool requires a session workspace cwd' , 'LSP_WORKSPACE_REQUIRED' )
}
const result = await ctx . lsp . query ( {
operation : input.operation ,
filePath : input.filePath ,
position : input.position ,
workspaceRoot ,
} , exec . signal )
switch ( result . kind ) {
case 'locations' :
2026-07-21 19:15:18 +08:00
return {
kind : 'locations' as const ,
locations : result.locations.map ( location = > ( {
uri : location.uri ,
range : {
start : { line : location.range.start.line , character : location.range.start.character } ,
end : { line : location.range.end.line , character : location.range.end.character } ,
} ,
} ) ) ,
2026-07-29 04:19:18 +08:00
resolvedWorkspaceUri : result.resolvedWorkspaceUri ,
2026-07-21 19:15:18 +08:00
}
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
case 'hover' :
2026-07-21 19:15:18 +08:00
return {
kind : 'hover' as const ,
hover : result.hover === null
? null
: {
contents : result.hover.contents ,
. . . result . hover . range === undefined
? { }
: {
range : {
start : { line : result.hover.range.start.line , character : result.hover.range.start.character } ,
end : { line : result.hover.range.end.line , character : result.hover.range.end.character } ,
} ,
} ,
} ,
}
2026-07-21 13:29:40 +08:00
/* v8 ignore next -- exhaustive over the closed LspQueryResult union; unreachable. */
default :
return assertNever ( result , 'tool-lsp result' )
feat(lsp): LSP capability seam, generic stdio provider, and lsp tool
Implements the LSP capability seam RFC as three packages: dsh-lsp (the
ctx.lsp interface — provider registry by branded id + exclusive extension
mapping, per-query order-independent selection, closed request/result
vocabulary, LspError taxonomy), dsh-lsp-local (a generic stdio language-server
provider — Content-Length JSON-RPC framing, per-(provider, workspace) process
single-flight, transient didOpen/query/didClose, an abortable per-instance
queue, UTF-16 negotiation, host-namespace source reads outside ctx.fs, and
bounded shutdown/kill teardown), and dsh-tool-lsp (the model-facing lsp tool —
four operations, one-based UTF-16 cursor conversion, workspace-grouped location
rendering, hover capping, a required session workspace, and a timeout budget).
Why: an agent had text search and file reads but no way to identify a program
symbol — follow an alias, connect an interface to implementations, or read an
inferred type — before changing code. Splitting model contract, seam, and local
subprocess behavior keeps the four semantic queries stable across future remote
or sandbox-native providers without leaking a JSON-RPC escape hatch.
2026-07-16 12:05:35 +08:00
}
} ,
presentCall : presentLspCall ,
} ) )
}
/** Reject a non-positive-integer config value at load, so misconfiguration fails loud. */
function assertPositiveInteger ( name : string , value : number ) : void {
if ( ! Number . isInteger ( value ) || value < 1 ) {
throw new Error ( ` tool-lsp: ${ name } must be a positive integer ` )
}
}
2026-07-21 13:29:40 +08:00
/** Reject a timer value Node would clamp instead of scheduling as configured. */
function assertTimer ( name : string , value : number ) : void {
if ( ! Number . isInteger ( value ) || value < 1 || value > MAX_TIMER_DELAY_MS ) {
throw new Error ( ` tool-lsp: ${ name } must be a positive integer no greater than ${ MAX_TIMER_DELAY_MS } ` )
}
}