dsh-lsp-actions 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,131 +4,161 @@
4
4
 
5
5
  **The LSP action surface for DeepSeek Harness — real language servers, real feedback, and the IDE integration backend for editors.**
6
6
 
7
- Diagnostics, formatting, code completion, quickfixes, symbols, signature help, inlay hints, and workspace-wide rename for your agent's editor loop, powered by the same language servers your IDE uses — plus a stable **editor action protocol** (`lsp.actions.list` / `lsp.actions.run` / `lsp.events`) that lets any editor (VS Code first) consume those capabilities directly over JSON-RPC.
7
+ *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.*
8
8
 
9
- [![Topic: dsh](https://img.shields.io/badge/Topic-dsh-4D6BFE?style=for-the-badge)](https://github.com/topics/dsh)
10
- [![Topic: dsh-plugin](https://img.shields.io/badge/Topic-dsh--plugin-8257D0?style=for-the-badge)](https://github.com/topics/dsh-plugin)
11
- [![CI](https://github.com/PerryLink/dsh-lsp-actions/actions/workflows/ci.yml/badge.svg)](https://github.com/PerryLink/dsh-lsp-actions/actions/workflows/ci.yml)
12
- [![npm downloads](https://img.shields.io/npm/dw/dsh-lsp-actions?style=flat-square)](https://www.npmjs.com/package/dsh-lsp-actions)
13
- [![npm](https://img.shields.io/npm/v/dsh-lsp-actions?style=flat-square)](https://www.npmjs.com/package/dsh-lsp-actions)
14
- [![License](https://img.shields.io/badge/License-Apache%202.0-D22128?style=flat-square)](LICENSE)
15
- [![Node](https://img.shields.io/badge/Node-%5E22.19%20%7C%7C%20%3E%3D24-43853D?style=flat-square)](package.json)
9
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
10
+ [![DSH plugin](https://img.shields.io/badge/dsh-plugin-✅-green)](https://github.com/topics/dsh-plugin)
11
+ [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
12
+ [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-lsp-actions/ci.yml?branch=main&label=CI)](https://github.com/PerryLink/dsh-lsp-actions/actions)
13
+ [![Version](https://img.shields.io/github/v/tag/PerryLink/dsh-lsp-actions?label=version)](https://github.com/PerryLink/dsh-lsp-actions/releases)
14
+ [![npm version](https://img.shields.io/npm/v/dsh-lsp-actions)](https://www.npmjs.com/package/dsh-lsp-actions)
15
+ [![npm downloads](https://img.shields.io/npm/dm/dsh-lsp-actions)](https://www.npmjs.com/package/dsh-lsp-actions)
16
16
 
17
- [English](README.md) · [简体中文](README.zh-CN.md) · [Español](README.es.md) · [हिन्दी](README.hi.md) · [Português](README.pt.md)
17
+ [English](README.md) · [简体中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
18
18
 
19
19
  </div>
20
20
 
21
21
  ---
22
22
 
23
- ## What this plugin gives your agent
23
+ ## Compatibility
24
24
 
25
- The official DeepSeek Harness `ctx.lsp` seam covers **navigation** (go-to-definition, references, implementation, hover). `dsh-lsp-actions` completes the **action surface** — the feedback loop an agent needs while it writes and fixes code:
25
+ | Surface | Status |
26
+ |---|---|
27
+ | Harness | DeepSeek Harness `0.1.0-rc.6` (compat declared for `>=0.1.0-rc.6`) |
28
+ | Node | `^22.19.0 \|\| >=24.0.0` |
29
+ | Platforms | All (pure host; subprocess + filesystem, no network) |
30
+ | Model | Any (tools are model-agnostic; the plugin never calls a model) |
31
+
32
+ ## What you get
26
33
 
27
- | Tool | What it does | Writes? |
28
- | --- | --- | --- |
29
- | `lsp_diagnostics <file>` | Compiler/analyzer errors, warnings and hints with severity, range, message and source server | ❌ read-only |
30
- | `lsp_format <file> [range?]` | Formats a file or selection through the language server and applies the result, returning the diff | ✅ via `fs/write-intent` + sandbox policy |
31
- | `lsp_completion <file> <line> <character>` | Completion suggestions at a cursor position, including the actual insertion text | ❌ read-only |
32
- | `lsp_code_action <file> [range?] [only?]` | Server-verified quickfixes/refactorings (with their edits) for a range or the first diagnostic | ❌ reference-only |
33
- | `lsp_symbols <query?> <file_path?>` | Workspace-wide symbol search by name, or one file's symbol outline | ❌ read-only |
34
- | `lsp_signature <file> <line> <character>` | Signature help (parameters and documentation) inside a call | ❌ read-only |
35
- | `lsp_inlay_hints <file> [range?]` | Type annotations and parameter-name hints from the server | ❌ read-only |
36
- | `lsp_rename <file> <line> <character> <new_name>` | Server-verified symbol rename, applied workspace-wide with per-file diffs | ✅ via `fs/write-intent` + sandbox policy |
34
+ `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:
37
35
 
38
- > ✨ A real `typescript-language-server` run is part of the test suite: diagnostics, formatting, completion, symbol search, and rename are verified end-to-end against a live server, not just mocks. The suite is self-contained (tsls is a devDependency) and runs in CI on Node 22/24 across Linux, Windows, and macOS.
36
+ 1. **Eight `lsp_*` tools** — diagnostics, formatting, completion, code actions, symbols, signature help, inlay hints, and rename, all served by the same language servers your IDE uses.
37
+ 2. **Editor action protocol v1** — a stable JSON-RPC surface (`lsp.actions.list` / `lsp.actions.run` / `lsp.events`) that lets any editor (VS Code first) consume those capabilities directly.
38
+ 3. **Real-server verification** — a real `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.
39
39
 
40
40
  ## Quick start
41
41
 
42
42
  ```sh
43
- dsh plugin --profile <name> add dsh-lsp-actions
44
- ```
43
+ # 1. install the bundle into your profile
44
+ dsh plugin --profile web add "github:PerryLink/dsh-lsp-actions#main"
45
45
 
46
- Remove it with:
46
+ # or from npm (published releases)
47
+ dsh plugin --profile web add dsh-lsp-actions
47
48
 
48
- ```sh
49
- dsh plugin --profile <name> remove dsh-lsp-actions
49
+ # 2. restart and verify the row
50
+ dsh --profile web --dump-config | grep -A3 'id: lsp-actions'
50
51
  ```
51
52
 
52
- Configure one entry per language server (the shape mirrors the official `lsp-stdio` config):
53
-
54
- ```yaml
55
- # in your profile's cordis.patch.yml (or the bundle row)
56
- - insert:
57
- - id: lsp-actions
58
- name: dsh-lsp-actions
59
- inject: [tools, fs, subprocess]
60
- config:
61
- servers:
62
- ts:
63
- command: typescript-language-server
64
- args: [--stdio]
65
- extensionToLanguage:
66
- ".ts": typescript
67
- formattingOptions: { tabSize: 2, insertSpaces: true }
68
- py:
69
- command: pyright-langserver
70
- args: [--stdio]
71
- extensionToLanguage:
72
- ".py": python
73
- # The IDE integration backend — leave disabled in Web/CLI profiles;
74
- # enable only in a dedicated headless backend composition (stdout is the wire):
75
- editor:
76
- enabled: false
77
- requestTimeoutMs: 60000
78
- diagnosticsCacheMaxFiles: 64
79
- maxDiagnostics: 200
80
- maxCompletionItems: 20
81
- maxCodeActions: 50
82
- maxSymbols: 100
83
- maxSignatures: 10
84
- maxInlayHints: 200
85
- maxResultChars: 16000
86
- timeoutMs: 60000
87
- ```
88
-
89
- The eight tools are always registered. With an **empty `servers` table and no `ctx.lsp` seam mounted, calls fail loudly** with `LSP_ACTION_UNAVAILABLE` telling the user what to configure — the plugin never starts servers you did not configure. A `ctx.lsp` seam mounted **after** this plugin is picked up on the next call (seam detection is per-call, so load order does not matter).
90
-
91
- ## IDE integration backend: the editor action protocol v1
92
-
93
- The plugin is also DSH's **IDE integration backend**. When `editor.enabled: true` is set in a dedicated headless composition, it serves a stable editor protocol over newline-delimited JSON-RPC 2.0 (the same wire framing as the official SDK/ACP transports):
53
+ ## Install & uninstall
54
+
55
+ - **git channel** (latest `main`): `dsh plugin --profile web add "github:PerryLink/dsh-lsp-actions#main"` — the `prepare` script builds (`tsc --noEmitOnError`).
56
+ - **npm channel** (published releases): `dsh plugin --profile web add dsh-lsp-actions`.
57
+ - **tarball channel**: `pnpm pack` in this repo, then `dsh plugin --profile web add ./dsh-lsp-actions-<version>.tgz`.
58
+ - **uninstall**: `dsh plugin --profile web remove dsh-lsp-actions` (or remove the row from the profile patch).
59
+
60
+ ## Configuration
61
+
62
+ All 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.
63
+
64
+ | Key | Default | Meaning |
65
+ |---|---|---|
66
+ | `servers` | `{}` | Named language servers; an empty table activates no servers |
67
+ | `editor.enabled` | `false` | Serve the editor action protocol over JSON-RPC stdio (headless backend only) |
68
+ | `editor.requestTimeoutMs` | `60000` | Per-run timeout budget (ms) for the editor protocol |
69
+ | `editor.diagnosticsCacheMaxFiles` | `64` | Bounded LRU diagnostics-cache size (files) |
70
+ | `maxDiagnostics` | `200` | Diagnostics cap per result |
71
+ | `maxCompletionItems` | `20` | Completion-items cap per result |
72
+ | `maxCodeActions` | `50` | Code-actions cap per result |
73
+ | `maxSymbols` | `100` | Symbol-results cap |
74
+ | `maxSignatures` | `10` | Signature-help cap |
75
+ | `maxInlayHints` | `200` | Inlay-hints cap |
76
+ | `maxResultChars` | `16000` | Rendered-result cap (chars) |
77
+ | `maxDocumentBytes` | `4000000` | Document-read cap (bytes) |
78
+ | `timeoutMs` | `60000` | Per-call timeout, enforced by the official timeout policy |
79
+
80
+ Each `servers` entry is an `LspServerEntry`: `command` (executable resolved on PATH at load) and `extensionToLanguage` (`".ts"` → `typescript`) are required; optional `fileGlobs`, `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.
81
+
82
+ ## Tools & surfaces
83
+
84
+ | Surface | Kind | Notes |
85
+ |---|---|---|
86
+ | `lsp_diagnostics` | tool | `<file>` — compiler/analyzer errors, warnings, and hints with severity, range, message, and source server (read-only) |
87
+ | `lsp_format` | tool | `<file> [range?]` — formats a file/selection through the language server and applies it, returning the diff (writes via `fs/write-intent`) |
88
+ | `lsp_completion` | tool | `<file> <line> <character>` — completion suggestions at a cursor position, including the insertion text (read-only) |
89
+ | `lsp_code_action` | tool | `<file> [range?] [only?]` — server-verified quickfixes/refactorings with their edits, for a range or the first diagnostic (reference-only) |
90
+ | `lsp_symbols` | tool | `<query?> <file_path?>` — workspace-wide symbol search by name, or one file's outline (read-only) |
91
+ | `lsp_signature` | tool | `<file> <line> <character>` — signature help (parameters and documentation) inside a call (read-only) |
92
+ | `lsp_inlay_hints` | tool | `<file> [range?]` — type annotations and parameter-name hints from the server (read-only) |
93
+ | `lsp_rename` | tool | `<file> <line> <character> <new_name>` — server-verified rename, applied workspace-wide with per-file diffs (writes via `fs/write-intent`) |
94
+ | `lsp.actions.*` | protocol | Editor action protocol v1: `lsp.actions.list` / `lsp.actions.run` / `lsp.events` over JSON-RPC |
95
+ | `examples/vscode/` | extension | UI-only VS Code extension plus the headless backend composition it connects to |
96
+
97
+ ## Editor action protocol v1
98
+
99
+ 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):
94
100
 
95
101
  | Method | What it does |
96
- | --- | --- |
97
- | `lsp.actions.list` | The `lsp-actions/v1` protocol version, the action catalog (`diagnostics.get`, `completion.get`, `quickfix.apply`, `format` — each flagged `writes`), and the addressable DSH sessions. |
98
- | `lsp.actions.run` | Executes one action with a structured `{ requestId, action, status, result \| error }` envelope. Errors carry the stable `LSP_ACTION_*` codes below. |
99
- | `lsp.events` | Subscribes to the streamed `lsp.event` notifications: `diagnostics.updated`, `action.status`, `file.changed`, `sessions.changed`. |
102
+ |---|---|
103
+ | `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 |
104
+ | `lsp.actions.run` | Executes one action with a structured `{ requestId, action, status, result \| error }` envelope; errors carry the stable `LSP_ACTION_*` codes |
105
+ | `lsp.events` | Subscribes to the streamed `lsp.event` notifications: `diagnostics.updated`, `action.status`, `file.changed`, `sessions.changed` |
100
106
 
101
107
  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.md) · [`docs/editor-protocol.zh-CN.md`](docs/editor-protocol.zh-CN.md).
102
108
 
103
- ### Versioning and the backward-compatibility promise
109
+ **Versioning and the backward-compatibility promise**
104
110
 
105
111
  - The protocol is versioned — `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.
106
- - Evolution is **additive only**: new actions, new fields, and new event kinds arrive without a version bump; existing semantics never change in place; a breaking change ships under a new `protocol` version, which servers may serve side by side.
112
+ - Evolution is **additive only**: new actions, fields, and event kinds arrive without a version bump; existing semantics never change in place; a breaking change ships under a new `protocol` version, which servers may serve side by side.
107
113
  - Clients must ignore unknown fields, unknown event kinds, and unknown actions, and route on the stable error `code`, never on message text.
108
114
 
109
- ### Minimal VS Code extension
115
+ **Error codes**
116
+
117
+ Every failure carries a stable `code`; models and callers route on the code, never on message text.
118
+
119
+ | Code | Meaning |
120
+ |---|---|
121
+ | `LSP_ACTION_UNAVAILABLE` | No server entry and no seam provider handles this file |
122
+ | `LSP_ACTION_UNSUPPORTED` | The server (or seam provider) does not advertise the operation |
123
+ | `LSP_ACTION_SERVER_FAILED` | The server failed (with its stderr tail); startup failures retry once |
124
+ | `LSP_ACTION_MALFORMED_RESPONSE` | The server sent a structurally invalid payload |
125
+ | `LSP_ACTION_CONFLICT` | The file changed since it was read, or the edits overlap / go out of bounds / leave the workspace |
126
+ | `LSP_ACTION_READ_ONLY` | The session's sandbox mode forbids the formatting/rename write |
127
+ | `LSP_ACTION_WORKSPACE_REQUIRED` | The calling session has no workspace cwd to root the server in |
128
+ | `LSP_ACTION_NO_SYMBOL` | The server found no renameable symbol at the cursor position |
129
+ | `LSP_ACTION_UNKNOWN` | Editor protocol: unknown action id, or no code action matched `title`/`index` |
130
+ | `LSP_ACTION_INVALID_ARGS` | Editor protocol: malformed action parameters |
131
+ | `LSP_ACTION_APPROVAL_UNAVAILABLE` | Editor protocol: the approval path could not grant a wider sandbox mode (fail-closed) |
132
+ | `LSP_PROTOCOL_VERSION_UNSUPPORTED` | Editor protocol: the declared protocol version is not supported |
133
+
134
+ ## VS Code extension
110
135
 
111
136
  [`examples/vscode/`](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`](examples/vscode/README.md).
112
137
 
113
138
  ![Editor demo](docs/editor-demo.gif)
114
139
 
115
- ## Why it is safe by construction
140
+ ## Permissions & data
116
141
 
117
- - **Formatting and rename are real mutations, treated like `write`/`edit`.** Every byte goes through the `fs/write-intent` waterfall (observation → guarded write → observation) and the per-call sandbox policy. `lsp_rename` pre-flights every edited file (workspace containment, overlap check, byte-capped read) *before* the first write, so a bad server response cannot leave a half-applied rename.
118
- - **Everything else is read-only by design.** Code actions, completions, symbols, signatures, and hints are reported as reference material; applying them is the model's own write/edit decision. Command forms are reported and **never executed**.
119
- - **Read-only sessions fail loud, fast, and structured** — `LSP_ACTION_READ_ONLY` with the shared `[sandbox: …]` marker, raised *before* any server round-trip.
120
- - **Escalation matches the official tools.** Under a confining filesystem, `lsp_format` and `lsp_rename` advertise the same `sandbox_permissions` / `justification` one-shot retry as `write`/`edit`, resolved through `ctx.approval`.
121
- - **Conflicts never clobber.** If the file changed on disk after it was read, the guarded write fails with `LSP_ACTION_CONFLICT` and the model is told to choose: re-read and re-run, or apply the diff manually.
122
- - **Timeouts are the platform's.** Each tool declares `timeoutMs`; the official `dsh-tool-call-timeout-policy` enforces it, and every await honors `exec.signal`.
123
- - **Nothing is cached on the model path.** Tool results live only in the session log; there is no cross-session persistence. The editor protocol keeps exactly one bounded cache — the LRU diagnostics snapshot cache (config `editor.diagnosticsCacheMaxFiles`), freshness-stamped and invalidated by filesystem observations and the plugin's own writes; it never persists across restarts.
124
- - **Prompt hygiene.** The plugin injects no persona or prompt prose into the session system prompt — its model-facing surface is the eight tool schemas. Should any future prompt segment be added, it must open with one short role sentence and stay brief (aligned with the official Minimal persona style: `You are a helpful software engineer assistant.`).
142
+ - **Permissions**: formatting and rename ride the official permission presets and approval — the `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.
143
+ - **Data**: nothing is stored on disk; tool results live only in the session log (no cross-session persistence). The editor protocol keeps one bounded in-memory LRU diagnostics cache, freshness-stamped and never persisted across restarts.
144
+ - **No network**: the plugin makes no network requests; it talks to language servers over local subprocess stdio.
145
+
146
+ ## Security boundaries
147
+
148
+ - **Read-only by default.** Six of the eight tools are reference-only; only `lsp_format` and `lsp_rename` mutate, and they do so as real `write`/`edit` mutations.
149
+ - **Official seams, not re-implemented.** Every byte goes through the `fs/write-intent` waterfall (observation → guarded write → observation) and the per-call sandbox policy; escalation matches the official `write`/`edit` tools.
150
+ - **Fail loud, fast, structured.** Empty `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.
151
+ - **Conflicts never clobber.** A file changed on disk after it was read fails with `LSP_ACTION_CONFLICT`; `lsp_rename` pre-flights every edited file before the first write.
152
+ - **Bounded work.** Result caps, byte caps, and the platform's timeout policy bound every call; the diagnostics cache is a bounded LRU.
153
+ - **Nothing cached on the model path.** Tool results live only in the session log; the diagnostics cache never persists across restarts.
125
154
  - **Bad servers fail loudly.** A missing executable fails at load; a server that dies at startup fails the call with `LSP_ACTION_SERVER_FAILED` plus its stderr tail (after one fresh-spawn retry).
155
+ - **Prompt hygiene.** The plugin injects no persona or prompt prose into the session system prompt — its model-facing surface is the eight tool schemas.
126
156
 
127
157
  ## Architecture
128
158
 
129
159
  Actions run **official-seam-first** and fall back to the plugin's own minimal stdio client:
130
160
 
131
- ```
161
+ ```text
132
162
  lsp_diagnostics / lsp_format / lsp_completion / lsp_code_action /
133
163
  lsp_symbols / lsp_signature / lsp_inlay_hints / lsp_rename
134
164
  │
@@ -139,124 +169,33 @@ lsp_symbols / lsp_signature / lsp_inlay_hints / lsp_rename
139
169
  built-in stdio client ← servers table (ctx.subprocess.spawn + JSON-RPC)
140
170
  ```
141
171
 
142
- 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. Full research and design notes: [`docs/seam-extension-notes.md`](docs/seam-extension-notes.md).
172
+ 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`](docs/seam-extension-notes.md).
143
173
 
144
- The **editor protocol** rides the same runner, the same write path, and the same permission machinery:
174
+ ## Known limitations
145
175
 
146
- ```
147
- ┌────────────────────────────────────────────────────┐
148
- │ one DSH runtime (headless editor backend) │
149
- VS Code (UI) ───┤ editor action protocol — this plugin │
150
- │ lsp.actions.list / run / lsp.events │
151
- │ (editor.enabled: true; same seam-first runner) │
152
- ├────────────────────────────────────────────────────┤
153
- official ACP ───┤ @deepseek-ai/dsh-acp — agent chat sessions │
154
- clients │ session/new · prompt · session/request_permission │
155
- ├────────────────────────────────────────────────────┤
156
- Python SDK ─────┤ @deepseek-ai/dsh-sdk-jsonrpc-server │
157
- (deepseek- │ session/prompt · session.event notifications │
158
- harness-sdk) └────────────────────────────────────────────────────┘
159
- ```
160
-
161
- - **Official ACP server** is the agent-conversation transport; its sessions' agents call the `lsp_*` tools, and an ACP client answers editor write escalations through `session/request_permission` (see [`docs/editor-protocol.md`](docs/editor-protocol.md)).
162
- - **Python SDK** (`pip install deepseek-harness-sdk`) drives the same runtime over the official SDK wire; since the editor protocol shares that wire's framing, SDK-grade clients can call `lsp.actions.*` unchanged.
163
-
164
- ## Configuration reference
165
-
166
- ```ts
167
- interface Config {
168
- /** Named language servers; empty = the plugin's own client serves nothing. */
169
- servers?: Record<string, LspServerEntry>
170
- /** Editor action protocol (the IDE integration backend). */
171
- editor?: {
172
- /** Serve lsp.actions.* over JSON-RPC stdio. Default false — enable only in a
173
- * dedicated headless backend composition whose stdout nothing else claims. */
174
- enabled?: boolean // default false
175
- /** Per-run timeout budget in ms, enforced inside the plugin. */
176
- requestTimeoutMs?: number // default 60000
177
- /** Bounded LRU diagnostics-cache size in files (least-recently-used eviction). */
178
- diagnosticsCacheMaxFiles?: number // default 64
179
- }
180
- maxDiagnostics?: number // default 200
181
- maxCompletionItems?: number // default 20
182
- maxCodeActions?: number // default 50
183
- maxSymbols?: number // default 100
184
- maxSignatures?: number // default 10
185
- maxInlayHints?: number // default 200
186
- maxResultChars?: number // default 16000 (complete rendered result cap)
187
- maxDocumentBytes?: number // default 4000000
188
- timeoutMs?: number // default 60000 (enforced by the official timeout policy)
189
- }
190
-
191
- interface LspServerEntry {
192
- command: string // executable, resolved on PATH at load
193
- extensionToLanguage: Record<string, string> // ".ts" → "typescript"
194
- fileGlobs?: string[] // optional; glob matches beat the extension map
195
- args?: string[] // no shell
196
- env?: Record<string, string>
197
- initializationOptions?: unknown
198
- configuration?: unknown // object form answers workspace/configuration per section
199
- formattingOptions?: unknown // e.g. { tabSize: 2, insertSpaces: true }
200
- maxMessageBytes?: number // default 16000000
201
- maxStderrBytes?: number // default 1000000
202
- killGraceMs?: number // default 2000
203
- shutdownTimeoutMs?: number // default 5000
204
- diagnosticsSettleMs?: number // default 2000 (push-only diagnostics window)
205
- diagnosticsDebounceMs?: number // default 250 (quiet period after the last pushed batch)
206
- idleTimeoutMs?: number // default 0 (0 = keep the server process alive)
207
- }
208
- ```
209
-
210
- ### Error codes
211
-
212
- Every failure carries a stable `code` on the error result; models and callers route on the code, never on message text.
213
-
214
- | Code | Meaning |
215
- | --- | --- |
216
- | `LSP_ACTION_UNAVAILABLE` | No server entry and no seam provider handles this file. |
217
- | `LSP_ACTION_UNSUPPORTED` | The server (or seam provider) does not advertise the operation. |
218
- | `LSP_ACTION_SERVER_FAILED` | The server failed (with its stderr tail); startup failures retry once. |
219
- | `LSP_ACTION_MALFORMED_RESPONSE` | The server sent a structurally invalid payload. |
220
- | `LSP_ACTION_CONFLICT` | The file changed since it was read, or the server's edits overlap / go out of bounds / leave the workspace. |
221
- | `LSP_ACTION_READ_ONLY` | The session's sandbox mode forbids the formatting/rename write. |
222
- | `LSP_ACTION_WORKSPACE_REQUIRED` | The calling session has no workspace cwd to root the server in. |
223
- | `LSP_ACTION_NO_SYMBOL` | The server found no renameable symbol at the cursor position. |
224
- | `LSP_ACTION_UNKNOWN` | Editor protocol: unknown action id, or no code action matched `title`/`index`. |
225
- | `LSP_ACTION_INVALID_ARGS` | Editor protocol: malformed action parameters. |
226
- | `LSP_ACTION_APPROVAL_UNAVAILABLE` | Editor protocol: the official approval path could not grant a wider sandbox mode (fail-closed). |
227
- | `LSP_PROTOCOL_VERSION_UNSUPPORTED` | Editor protocol: the run declared a protocol version this server does not speak. |
228
-
229
- ### Host version support
230
-
231
- The plugin declares its DeepSeek Harness packages as **peer dependencies** (`@deepseek-ai/dsh-fs`, `dsh-llm`, `dsh-sandbox`, `dsh-subprocess`, `dsh-tools` ≥ `0.1.0-rc.6`), so one copy serves both the host and the plugin. Tested against `0.1.0-rc.6`; last verified 2026-08-15.
232
-
233
- ### Known limitations
234
-
235
- - **Transient documents.** Every action opens the file, runs one request, and closes it again (matching the official stdio host). Project-based servers that require a resident open file for document-free requests (tsls refuses `workspace/symbol` without one) are served by passing `file_path` to `lsp_symbols`, which keeps the routing file open for that request. tsls also answers `textDocument/signatureHelp` with `null` under this lifecycle; other servers (gopls, pyright, rust-analyzer) serve it normally.
176
+ - **Transient documents.** Every action opens the file, runs one request, and closes it again (matching the official stdio host). Project-based servers that require a resident open file for document-free requests (tsls refuses `workspace/symbol` without one) are served by passing `file_path` to `lsp_symbols`. tsls also answers `textDocument/signatureHelp` with `null` under this lifecycle; other servers (gopls, pyright, rust-analyzer) serve it normally.
236
177
  - **Range formatting requires the server's range provider.** Servers that only advertise whole-document formatting fail range requests with `LSP_ACTION_UNSUPPORTED`.
237
- - **Rename applies text edits only.** Resource operations (create/delete/rename files) in a server's rename answer are refused with `LSP_ACTION_UNSUPPORTED`, and edits outside the workspace fail as `LSP_ACTION_CONFLICT` before anything is written. On `utf-8`/`utf-32` servers, cross-file rename positions are decoded by reading each edited file; an unreadable edited file fails the call as a conflict instead of mis-decoding positions.
178
+ - **Rename applies text edits only.** Resource operations (create/delete/rename files) in a server's rename answer are refused with `LSP_ACTION_UNSUPPORTED`, and edits outside the workspace fail as `LSP_ACTION_CONFLICT` before anything is written.
238
179
 
239
180
  ## Development
240
181
 
241
182
  ```sh
242
- pnpm install
243
- pnpm run lint # oxlint over src/ and tests/
244
- pnpm test # 290+ tests: unit + fixture-server integration + editor-protocol e2e + real tsls e2e
245
- pnpm run test:coverage # gates: lines/statements/functions ≥ 90%, branches ≥ 85%
246
- pnpm build # emits lib/
183
+ pnpm install # node ^22.19 || >=24
184
+ pnpm run lint # oxlint over src/ and tests/
185
+ pnpm test # vitest: unit + fixture-server integration + editor-protocol e2e + real tsls e2e
186
+ pnpm run test:coverage # coverage gate
187
+ pnpm build # tsc --noEmitOnError → lib/
188
+ pnpm run prepare # tsc --noEmitOnError (runs on install)
189
+ pnpm run prepublishOnly # tsc --noEmitOnError (runs before publish)
247
190
  ```
248
191
 
249
- The editor protocol has dedicated coverage: [`tests/editor-cache.spec.ts`](tests/editor-cache.spec.ts) (bounded LRU), [`tests/editor-protocol.spec.ts`](tests/editor-protocol.spec.ts) (service semantics, permission gating, timeouts, and the full diagnostics → quickfix → format chain over JSON-RPC frames against the fixture LSP server). The `examples/vscode/` backend and extension are standalone npm projects — see [`examples/vscode/README.md`](examples/vscode/README.md).
250
-
251
- ### Releasing
192
+ ## Topics
252
193
 
253
- CI runs the lint/build/test matrix plus the coverage gate on every push and pull request. Pushing a `v*` tag triggers the publish workflow, which verifies the suite and publishes the package to npm — it needs an `NPM_TOKEN` Actions secret (a publish-scoped npm access token) set once on the repository. The version is bumped manually in `package.json`/`CHANGELOG.md` before tagging.
194
+ `dsh`, `dsh-plugin`, `deepseek-harness`, `lsp`, `language-server`, `diagnostics`, `formatting`, `completion`, `code-action`, `symbols`, `signature-help`, `inlay-hints`, `rename`, `refactor`, `ide`, `editor`, `vscode`, `acp`, `json-rpc`
254
195
 
255
196
  ## Contributors
256
197
 
257
- Thanks to everyone who has contributed to this project:
258
-
259
- - [PerryLink](https://github.com/PerryLink) — the plugin itself: the LSP action client and server lifecycle, all eight tools, tests, CI, and documentation.
198
+ - [@PerryLink](https://github.com/PerryLink) — creator and maintainer: the LSP action client and server lifecycle, all eight tools, the editor action protocol, tests, CI, and the five-language docs.
260
199
 
261
200
  ## PerryLink DSH Plugin Family
262
201
 
@@ -282,4 +221,4 @@ This project is one of the [15 DeepSeek Harness plugins](https://github.com/Perr
282
221
 
283
222
  ## License
284
223
 
285
- [Apache License 2.0](LICENSE)
224
+ [Apache License 2.0](LICENSE) © 2026 dsh-lsp-actions contributors