deepseek-harness
tiwe0/GeoChat会话隔离的 GeoGebra 画布,提供 20 个原生工具用于作图、场景读回、文字排版检查与动画采样,并内置 55 个数学技能和离线 Applet 资源。
DSH 的 LSP 动作面:诊断、格式化、补全、代码动作、符号、签名提示、inlay 提示与重命名,全部由真实语言服务器驱动。
npm i -g dsh1024 once, then dsh1024 plugin --profile web add dsh-lsp-actions (counts toward the deepseek1024.com install ranking).The LSP action surface for DeepSeek Harness — real language servers, real feedback, and the IDE integration backend for editors.
*Diagnostics, formatting, completion, code actions, symbols, signature help, inlay hints, and rename for your agent's editor loop — plus the stable editor action protocol (lsp.actions.*) that lets any editor consume them directly.*
English · 简体中文 · Español · Português · हिन्दी
dsh plugin --profile <name> add dsh-lsp-actions📖 Ecosystem knowledge base — measured data, not marketing: plugin development guide · plugin-selection data · maintenance criteria.
main): dsh plugin --profile web add github:PerryLink/dsh-lsp-actions — the prepare script builds (tsc --noEmitOnError && node scripts/fix-dts.mjs).dsh plugin --profile web add dsh-lsp-actions.pnpm pack in this repo, then dsh plugin --profile web add ./dsh-lsp-actions-.tgz.dsh plugin --profile web remove dsh-lsp-actions (or remove the row from the profile patch).这个插件是 DSH 插件家族的一员(95 个,全部 Apache-2.0)。如果你在用,给个 star —— 它不会解锁任何功能,但会让下一个人在搜索里更容易找到它。
*English:* part of a 95 plugin family for DeepSeek Harness. If it is useful, a star helps the next person find it — nothing is gated behind it.
The LSP action surface for DeepSeek Harness — real language servers, real feedback, and the IDE integration backend for editors.
Diagnostics, formatting, completion, code actions, symbols, signature help, inlay hints, and rename for your agent's editor loop — plus the stable editor action protocol (lsp.actions.*) that lets any editor consume them directly.
*Workflow overview: call → plugin → result. The examples below use the real command surface.*
Surface · Status Harness · DeepSeek Harness dsh-v0.2.1-alpha.1 (compat declared for >=0.1.2-rc.1 =0.1.5-alpha.1 =0.1.6-0 =0.1.7-0 =24.0.0 Platforms · All (pure host; subprocess + filesystem, no network) Model · Any (tools are model-agnostic; the plugin never calls a model)
dsh-lsp-actions mounts as a single host row (id: lsp-actions, name: dsh-lsp-actions, inject: [tools, fs, subprocess]). The official DeepSeek Harness ctx.lsp seam covers navigation (go-to-definition, references, implementation, hover); this plugin completes the action surface — the feedback loop an agent needs while it writes and fixes code:
lsp_* tools** — diagnostics, formatting, completion, code actions, symbols, signature help, inlay hints, and rename, all served by the same language servers your IDE uses.lsp.actions.list / lsp.actions.run / lsp.events) that lets any editor (VS Code first) consume those capabilities directly.typescript-language-server run is part of the test suite (self-contained, CI on Node 22/24 across Linux, Windows, and macOS), not just mocks.dsh plugin --profile web add github:PerryLink/dsh-lsp-actions# 1. install the bundle into your profile
dsh plugin --profile web add github:PerryLink/dsh-lsp-actions
# or from npm (published releases)
dsh plugin --profile web add dsh-lsp-actions
# 2. restart and verify the row
dsh --profile web --dump-config | grep -A3 'id: lsp-actions'These are the plugin's real entry points; install once, then call one.
Command
~~~sh lsp_diagnostics ~~~
Input
~~~json { "file_path": "a.ts" } ~~~
Output
~~~text a.ts:1:3 [Error] boom [ts 2304] a.ts:3:1 [Hint] hint ~~~
*Observed from plugins/dsh-lsp-actions/tests/render.spec.ts:32-37 (exact toBe); tool-level no-result render at plugins/dsh-lsp-actions/tests/tools.spec.ts:150-156*
Input
~~~text {"action":"format","requestId":"f1","params":{"filePath":"a.ts","workspaceRoot":""}} under a read-only permission preset ~~~
Output
~~~text { "requestId": "f1", "action": "format", "status": "failed", "error": { "code": "LSP_ACTION_READ_ONLY", "message": "[sandbox: file access denied under read-only mode]\n[sandbox: escalation available — retry this exact operation once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]" } } ~~~
*Observed from plugins/dsh-lsp-actions/src/tools.ts:213-223; plugins/dsh-lsp-actions/src/editor/actions.ts:320-330; plugins/dsh-lsp-actions/tests/editor-protocol.spec.ts:305-325*
lsp_formatlsp_completionAll tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need. cordis.patch.yml documents each key inline.
Key · Default · Meaning servers · {} · Named language servers; an empty table activates no servers editor.enabled · false · Serve the editor action protocol over JSON-RPC stdio (headless backend only) editor.requestTimeoutMs · 60000 · Per-run timeout budget (ms) for the editor protocol editor.diagnosticsCacheMaxFiles · 64 · Bounded LRU diagnostics-cache size (files) maxDiagnostics · 200 · Diagnostics cap per result maxCompletionItems · 20 · Completion-items cap per result maxCodeActions · 50 · Code-actions cap per result maxSymbols · 100 · Symbol-results cap maxSignatures · 10 · Signature-help cap maxInlayHints · 200 · Inlay-hints cap maxResultChars · 16000 · Rendered-result cap (chars) maxDocumentBytes · 4000000 · Document-read cap (bytes) timeoutMs · 60000 · Per-call timeout, enforced by the official timeout policy
Each servers entry is an LspServerEntry: command (executable resolved on PATH at load) and extensionToLanguage (".ts" → typescript) are required; optional fileGlobs, projectMarkers, args, env, initializationOptions, configuration, formattingOptions, maxMessageBytes, maxStderrBytes, killGraceMs, shutdownTimeoutMs, diagnosticsSettleMs, diagnosticsDebounceMs, and idleTimeoutMs (0 = keep the server process alive) tune the built-in stdio client.
Routing is deterministic and configuration-driven. Every file takes the first match from: (1) a server entry whose fileGlobs match the path; (2) the nearest ancestor directory — walking up to the workspace root — that holds a project config file listed in some entry's projectMarkers, matched against the entries that also map the file's extension; (3) the entry whose extensionToLanguage maps the file's extension, in config order. A workspace holding apps/node-app/{package.json,tsconfig.json,src/main.ts} and apps/deno-app/{deno.json,src/main.ts} therefore serves each main.ts from its own project's server, with no path rule to maintain and nothing to change when a project is added, renamed, or moved:
servers:
typescript:
command: typescript-language-server
args: ["--stdio"]
extensionToLanguage: { ".ts": typescript, ".tsx": typescriptreact }
projectMarkers: ["package.json", "tsconfig.json"]
deno:
command: deno
args: ["lsp"]
extensionToLanguage: { ".ts": typescript, ".tsx": typescriptreact }
projectMarkers: ["deno.json", "deno.jsonc"]A project marker only decides among servers that already map the file's extension (it never widens an entry's file types); the walk never leaves the workspace root, and a project config in one directory never applies to a sibling directory. Step (1) outranks a project marker: an entry whose fileGlobs match the path still wins, so drop project-level globs when you adopt projectMarkers — a lingering **/*.ts glob keeps claiming those files. These rules govern the built-in stdio client — a mounted ctx.lsp seam provider decides its own routing.
Surface · Kind · Notes lsp_diagnostics · tool · ` — compiler/analyzer errors, warnings, and hints with severity, range, message, and source server (read-only) lsp_format · tool · [range?] — formats a file/selection through the language server and applies it, returning the diff (writes via fs/write-intent) lsp_completion · tool · — completion suggestions at a cursor position, including the insertion text (read-only) lsp_code_action · tool · [range?] [only?] — server-verified quickfixes/refactorings with their edits, for a range or the first diagnostic (reference-only) lsp_symbols · tool · — workspace-wide symbol search by name, or one file's outline (read-only) lsp_signature · tool · — signature help (parameters and documentation) inside a call (read-only) lsp_inlay_hints · tool · [range?] — type annotations and parameter-name hints from the server (read-only) lsp_rename · tool · — server-verified rename, applied workspace-wide with per-file diffs (writes via fs/write-intent) lsp.actions.* · protocol · Editor action protocol v1: lsp.actions.list / lsp.actions.run / lsp.events over JSON-RPC examples/vscode/` · extension · UI-only VS Code extension plus the headless backend composition it connects to
When editor.enabled: true is set in a dedicated headless composition, dsh-lsp-actions serves a stable editor protocol over newline-delimited JSON-RPC 2.0 (the same wire framing as the official SDK/ACP transports):
Method · What it does lsp.actions.list · Returns the lsp-actions/v1 protocol version, the action catalog (diagnostics.get, completion.get, quickfix.apply, format — each flagged writes), and the addressable DSH sessions lsp.actions.run · Executes one action with a structured { requestId, action, status, result \ · error } envelope; errors carry the stable LSP_ACTION_* codes lsp.events · Subscribes to the streamed lsp.event notifications: diagnostics.updated, action.status, file.changed, sessions.changed
All write actions (quickfix.apply, format) go through the official permission presets and approval: a read-only session is refused with LSP_ACTION_READ_ONLY before any server round-trip, edits ride the fs/write-intent waterfall, and the sandbox_permissions + justification escalation pair resolves through the official approveEscalation ask (fail-closed when no answerer can decide). Full wire spec, bilingual: docs/editor-protocol.md · docs/editor-protocol.zh-CN.md.
Versioning and the backward-compatibility promise
lsp.actions.list returns protocol: "lsp-actions/v1", version: 1. v1 is frozen: field names, action ids, event kinds, and error codes stay stable forever.protocol version, which servers may serve side by side.code, never on message text.Error codes
Every failure carries a stable code; models and callers route on the code, never on message text.
Code · Meaning LSP_ACTION_UNAVAILABLE · No server entry and no seam provider handles this file LSP_ACTION_UNSUPPORTED · The server (or seam provider) does not advertise the operation LSP_ACTION_SERVER_FAILED · The server failed (with its stderr tail); startup failures retry once LSP_ACTION_MALFORMED_RESPONSE · The server sent a structurally invalid payload LSP_ACTION_CONFLICT · The file changed since it was read, or the edits overlap / go out of bounds / leave the workspace LSP_ACTION_READ_ONLY · The session's sandbox mode forbids the formatting/rename write LSP_ACTION_WORKSPACE_REQUIRED · The calling session has no workspace cwd to root the server in LSP_ACTION_NO_SYMBOL · The server found no renameable symbol at the cursor position LSP_ACTION_UNKNOWN · Editor protocol: unknown action id, or no code action matched title/index LSP_ACTION_INVALID_ARGS · Editor protocol: malformed action parameters LSP_ACTION_APPROVAL_UNAVAILABLE · Editor protocol: the approval path could not grant a wider sandbox mode (fail-closed) LSP_PROTOCOL_VERSION_UNSUPPORTED · Editor protocol: the declared protocol version is not supported
examples/vscode/ ships a UI-only extension (sidebar with the DSH sessions, the diagnostics list, one-click quickfix apply, open-at-range, and format) plus the headless backend composition (backend/cordis.yml) it connects to over ACP-style JSON-RPC. The extension implements zero LSP logic — every capability and every byte written belongs to the plugin. Install steps, settings, and the demo-gif recording script are in examples/vscode/README.md.
fs/write-intent waterfall and the sandbox_permissions / justification escalation pair resolved through ctx.approval. The plugin declares fs:read, fs:write, subprocess:spawn, and network:none in its workshop manifest.lsp_format and lsp_rename mutate, and they do so as real write/edit mutations.fs/write-intent waterfall (observation → guarded write → observation) and the per-call sandbox policy; escalation matches the official write/edit tools.servers + no ctx.lsp seam → LSP_ACTION_UNAVAILABLE; read-only sessions → LSP_ACTION_READ_ONLY before any server round-trip; command forms are reported and never executed.LSP_ACTION_CONFLICT; lsp_rename pre-flights every edited file before the first write.LSP_ACTION_SERVER_FAILED plus its stderr tail (after one fresh-spawn retry).Actions run official-seam-first and fall back to the plugin's own minimal stdio client:
lsp_diagnostics / lsp_format / lsp_completion / lsp_code_action /
lsp_symbols / lsp_signature / lsp_inlay_hints / lsp_rename
│
▼
ctx.lsp seam (extended: diagnostics / formatDocument / completion)
│ absent · legacy · no provider for this file
▼
built-in stdio client ← servers table (ctx.subprocess.spawn + JSON-RPC)The seam extension is proposed upstream (upstream/lsp-action-seam.patch, PR description in upstream/PR-description.md). Once it lands, the plugin keeps working unchanged — the built-in client simply stops being used. The built-in client stays as the standalone fallback for the servers table. The editor protocol rides the same runner, the same write path, and the same permission machinery. Full research and design notes: docs/seam-extension-notes.md.
The action backend is behind a public provider interface — ActionRunner (exported from the package) — implemented by the bundled runner and open to third-party providers: the eight tools consume only that interface, never a concrete client.
didChange, and the process shutdown closes every document — so project-based servers like tsls serve textDocument/signatureHelp and document-free workspace/symbol instead of answering null on a freshly-closed document. The resident set is bounded by the workspace and cleared with the instance (idle eviction or plugin disposal).LSP_ACTION_UNSUPPORTED.LSP_ACTION_UNSUPPORTED, and edits outside the workspace fail as LSP_ACTION_CONFLICT before anything is written.pnpm install # node ^22.19 || >=24
pnpm run lint # oxlint over src/ and tests/
pnpm test # vitest: unit + fixture-server integration + editor-protocol e2e + real tsls e2e
pnpm run test:coverage # coverage gate
pnpm build # tsc --noEmitOnError && node scripts/fix-dts.mjs → lib/
pnpm run prepare # tsc --noEmitOnError && node scripts/fix-dts.mjs (runs on install)
pnpm run prepublishOnly # tsc --noEmitOnError && node scripts/fix-dts.mjs (runs before publish)Verified against DSH `0.2.0-rc.2` (the runtime this README ships for) and the high-star plugin set surveyed on 2026-10-05.
This plugin does not interfere with other plugins, including the widely installed high-star ones:
shadows-shipped-ui seat.webServer prefix.inserts its own row; it never overrides a built-in row's config.process.env, or replace the global fetch dispatcher.Shared event listeners are non-interfering by construction. It observes the ordering-sensitive event fs/write-intent with ctx.on() — Cordis's broadcast registration, where every listener runs and none can starve another. Every listener here delegates through `next()`, so the chain is never short-circuited, and a mutation is applied to the value next() produced rather than returned in its place: