dsh-code-server-app 0.3.21 → 0.3.23

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.
Files changed (42) hide show
  1. package/README.en.md +85 -18
  2. package/README.md +74 -20
  3. package/assets/extensions/dshcs-editor-bridge/extension.js +94 -50
  4. package/assets/extensions/dshcs-editor-bridge/lib/ask-panel.js +353 -174
  5. package/assets/extensions/dshcs-editor-bridge/lib/bridge-client.js +30 -4
  6. package/assets/extensions/dshcs-editor-bridge/package.json +2 -2
  7. package/assets/extensions/dshcs-editor-bridge/webview/THIRD-PARTY.md +21 -0
  8. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  9. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  10. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  11. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  12. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  13. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  14. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  15. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  16. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  17. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  18. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  19. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  20. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  21. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  22. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  23. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  24. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  25. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  26. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  27. package/assets/extensions/dshcs-editor-bridge/webview/fonts/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  28. package/assets/extensions/dshcs-editor-bridge/webview/src/app.jsx +199 -0
  29. package/assets/extensions/dshcs-editor-bridge/webview/src/approval.jsx +61 -0
  30. package/assets/extensions/dshcs-editor-bridge/webview/src/official-tokens.css +8 -0
  31. package/assets/extensions/dshcs-editor-bridge/webview/src/panel.css +346 -0
  32. package/assets/extensions/dshcs-editor-bridge/webview/src/thread.jsx +115 -0
  33. package/assets/extensions/dshcs-editor-bridge/webview/thread.css +1 -0
  34. package/assets/extensions/dshcs-editor-bridge/webview/thread.js +464 -0
  35. package/lib/bridge-approval.mjs +215 -0
  36. package/lib/bridge-session.mjs +3 -1
  37. package/lib/bridge-thread.mjs +374 -0
  38. package/lib/bridge.mjs +15 -6
  39. package/lib/index.js +142 -17
  40. package/package.json +29 -6
  41. package/vendor/VENDOR.json +1 -1
  42. package/lib/bridge-answer.mjs +0 -155
package/README.en.md CHANGED
@@ -12,6 +12,11 @@
12
12
 
13
13
  A static profile plugin (npm package with host + client bundle) that ships the **VS Code server tree** from a [code-server](https://github.com/coder/code-server) release as a **platform-independent dependency package** (pack-time artifact `vendor/vscode` → `@jinsiyu/dshcs-vscode-server`, no install scripts, no postinstall). The code-server **Node service layer is replaced by the plugin's own `lib/launcher.mjs`**: it drives `<tree>/lib/vscode/out/server-main.js` (`loadCodeWithNls()` / `createServer()` / `handleRequest()` / `handleUpgrade()`) directly and re-adds the few HTTP endpoints code-server used to provide (`/healthz`, `/manifest.json`, `/_static/*`, `/proxy/:port`). The 16 native modules (node-pty / @vscode/sqlite3 / spdlog / …) come from `@jinsiyu/dshcs-*-win32-<arch>` platform packages selected automatically per architecture by the platform aggregator. VS Code's inner dependencies and the prebuilt native modules are **all installed by the package manager together with the plugin** — no global npm install, no `bin` configuration, no profile config changes, no second install command, **no argon2/C++ toolchain**.
14
14
 
15
+ > Since 0.3.22 the ask panel renders the session's new content with **DSH's own Markdown renderer** (the same
16
+ > renderer and design tokens as the DSH UI; new content only) and can **answer approval requests in place**
17
+ > (writing outside the workspace / running commands). See "Working with DSH: the editor bridge" and section 21 of
18
+ > `docs/analysis-code-server-as-dsh-plugin.md`.
19
+
15
20
  ## UI carrier and required DSH version (0.2.3: right-sidebar DSH only)
16
21
 
17
22
  | DSH version | Carrier | Entry points |
@@ -198,23 +203,41 @@ only the editor knows, and lets editor gestures drive the current session.
198
203
  | Direction | Capability | Mechanism |
199
204
  |---|---|---|
200
205
  | editor → agent | **unsaved buffers** (disk ≠ what the user sees), active file and selection, **language-server diagnostics** with `file:line`, source and code | agent tools `editor_context` / `editor_diagnostics`; plus a notice attached before writing a dirty file |
201
- | editor → DSH | select code → context menu **"DSH: ask about selection"** → an **ask panel** opens (carrying `file:line` and the selection); the question enters the current session as **user input**, and the reply is mirrored back into the panel | extension command `dsh-code-server.askAboutSelection` (one of the **top two** editor context-menu items) + a webview panel + `POST /ask` + the `answers` field of `/sync` |
206
+ | editor → DSH | select code → context menu **"DSH: ask about selection"** → an **ask panel** opens (carrying `file:line` and the selection); the question enters the current session as **user input**, and that session's **new content** is rendered in the panel by DSH's own Markdown renderer | extension command `dsh-code-server.askAboutSelection` (one of the **top two** editor context-menu items) + a webview panel + `POST /ask` + the `thread` field of `/sync` |
207
+ | DSH → editor (approval) | when the agent wants to **write outside the workspace or run a command**, the approval request shows up as a card in the panel (tool, reason, countdown); "allow once" / "reject" takes effect immediately | the `approvals` field of `/sync` + `POST /approve` (the bridge's **only** non-read-only route; constraints under "Security model") |
202
208
  | agent → editor | the agent changed a file → a **native diff** opens; if that buffer has unsaved changes you get a warning and **no overwrite** | host watches `tools/result`, the extension polls and opens the diff |
203
209
 
204
210
  - The tools are only registered while the bridge is live (so the model never sees an unusable tool), and the
205
211
  system-prompt section renders only then too.
206
- - **Ask panel** (extension 0.2.0): the context-menu command no longer pops a one-line input box but opens a panel —
207
- transcript on top, input at the bottom. The selection is captured **at send time** (so you can change it while the
208
- panel stays open), and the reply refreshes with the same polling round trip: no need to switch back to the DSH UI.
212
+ - **Ask panel** (extension 0.2.0; the official renderer since 0.2.3; a **dialog** since 0.2.4): the context-menu
213
+ command opens a chat window that fills the editor area with a centred, width-limited column (the ✕ in its header
214
+ closes it) instead of a cramped side column — and the selection can still be changed while it stays open.
209
215
  The two commands **remember their intent** (0.3.21): "ask about selection" carries a **line range + selection text**
210
216
  only when something is actually selected, while "ask about file" **never carries line numbers or a selection** — the
211
217
  cursor line is irrelevant to the question and only misleads the agent. With no selection, the selection command also
212
218
  degrades to the plain file.
219
+ - **The panel renders exactly what DSH renders** (0.3.22): the panel bundles DSH's official Markdown renderer
220
+ (`MarkdownText` from `@deepseek-ai/dsh-client-ui-primitives`) plus the official design tokens — the same
221
+ micromark/mdast pipeline, the same incremental streaming parser, the same shiki highlighting (boot set:
222
+ typescript / shellscript / json), KaTeX math and the same heading/table typography. Only **new content** is
223
+ rendered (from the moment the panel subscribes); history is **not replayed** and there is no "load earlier".
224
+ - **Thinking shows up like in DSH** (0.3.23): assistant reasoning becomes a Think row — **collapsed by default**,
225
+ showing its first line (or the latest line while streaming) and expanding on a row click, built from the official
226
+ `DisclosureRow` plus the official think icon and typography language.
227
+ - **Approvals are handled right in the panel** (0.3.22; window fixed in 0.3.23): while the panel is open, that
228
+ session's approval requests ask the panel first (5 minutes by default). Clicking "allow once" / "reject" settles it
229
+ immediately; **closing the panel** or letting the window expire hands the request back **unchanged** to the official
230
+ path (the DSH UI shows the same card). 0.3.22's 8-second window was far too short for a human — the buttons went
231
+ grey before anyone could click (reported as "the approval box stopped working"); the window is now 5 minutes and
232
+ closing the panel hands off immediately instead of waiting it out.
233
+ **Nothing is ever auto-approved** — `allowed-once` can only come from a click, and there is no "always allow".
213
234
  - The question enters the DSH session as a **plain user message** (`source: { kind: 'user' }`, host 0.3.19): earlier
214
235
  versions used `{kind:'plugin'}`, which DSH renders as a *context update* — it did not look like something the user
215
236
  said. Provenance stays in the first line of the text: `From the editor: <file>[:<line>]`.
216
- - **Everything is read-only**: the bridge never writes files, applies edits, or runs commands. The agent's writes
217
- still go through its own `fs` tools; the bridge only *knows about* them.
237
+ - **Read-only with one constrained exception**: the bridge never writes files, applies edits, or runs commands; the
238
+ single non-read-only route is `POST /approve`, which can only **answer an approval request that already exists**
239
+ (see invariant 2 below). The agent's writes still go through its own `fs` tools; the bridge only *knows about* them
240
+ and carries your answer back.
218
241
  - Status bar shows `$(plug) DSH` while connected (click it for the log in the "DSH Editor Bridge" output channel).
219
242
  - **The extension ships as a built-in** (fixed in 0.3.12): `dshcs-editor-bridge` is installed into
220
243
  `<tree>/lib/vscode/extensions/` next to `dshcs-open-file`. 0.3.0–0.3.11 installed it as a *user* extension
@@ -226,8 +249,9 @@ only the editor knows, and lets editor gestures drive the current session.
226
249
  ### The channels (since 0.3.13 over **local IPC**: a Windows named pipe / unix socket)
227
250
 
228
251
  ```
229
- extension → host POST /code-server-bridge/sync one round trip: push editor state + take pending events
252
+ extension → host POST /code-server-bridge/sync one round trip: push editor state (+ which session the panel watches) + take events and thread deltas
230
253
  extension → host POST /code-server-bridge/ask push an editor question into the current session
254
+ extension → host POST /code-server-bridge/approve answer an approval request that **already exists** (the only non-read-only route)
231
255
  extension → host GET /code-server-bridge/health unauthenticated liveness probe
232
256
  extension → host POST /code-server-bridge/event extension reports open/close etc. (host log tail)
233
257
  host → extension <extensionsDir>/.dshcs-bridge/bridge.json endpoint + token, re-read every 5s
@@ -239,6 +263,14 @@ host → extension <extensionsDir>/.dshcs-bridge/bridge.json endpoint + toke
239
263
 
240
264
  Requests use `http.request({ socketPath })` (`fetch` has no socket support) and **no port is ever opened**.
241
265
 
266
+ The three `/sync` fields the panel actually consumes (0.3.22):
267
+
268
+ | Field | Content | How the panel uses it |
269
+ |---|---|---|
270
+ | `thread` | **new content** entries (user / assistant / tool / approval) of the session the panel watches; bounded: ≤120 entries per session, ≤8000 chars per body, ≤4 watched sessions | assistant bodies go to the official renderer; tools and approvals become compact summary rows |
271
+ | `approvals` | pending approval requests `[{id, toolName, reason, at}]` (≤4) | renders the card with a countdown; a click posts `/approve` |
272
+ | `approvalHoldMs` / `uiVersion` | the approval window (300000 ms = 5 minutes by default) / the DSH UI version | countdown basis; a renderer-version mismatch is surfaced in the panel |
273
+
242
274
  > **Why not HTTP (settled in 0.3.13, all three measured)**
243
275
  > 1. **Desktop has no HTTP surface at all**: the renderer calls `host.fetch()` through Electron IPC
244
276
  > (`createSharedFetchHandler('/api')` in `apps/desktop-host/src/index.ts:308`) — an in-process call, unreachable
@@ -264,20 +296,30 @@ of passing stale data off as fresh).
264
296
  request/response round trip every 600 ms. Polling also buys two useful properties: it is idempotent (a dropped event
265
297
  only costs one notification — the data always lives in the editor) and the cached state is inherently fresh.
266
298
 
267
- ### Security model (four invariants; read before touching `lib/bridge.mjs`)
299
+ ### Security model (five invariants; read before touching `lib/bridge.mjs`)
268
300
 
269
301
  The token lives in `<extensionsDir>/.dshcs-bridge/bridge.json`, **readable by any process of the same local
270
302
  user**, so:
271
303
 
272
- 1. **`/code-server-bridge/*` is permanently read-only.** No route writes files, edits documents, or runs
273
- commands. A leaked token is therefore bounded to "sees information that is in the editor" and **can never**
274
- become arbitrary file writes or command execution. A whitelist assertion in `scripts/test-bridge-routes.mjs`
275
- guards this.
276
- 2. **Any request carrying `Origin` gets 403.** Browsers always send one (including a sandboxed iframe's literal
304
+ 1. **`/code-server-bridge/*` is read-only, with `/approve` as the single exception.** No route writes files,
305
+ edits documents, runs commands, or spawns processes. A leaked token is therefore bounded to "sees information
306
+ that is in the editor" and **can never** become arbitrary file writes or command execution. A whitelist
307
+ assertion in `scripts/test-bridge-routes.mjs` guards this.
308
+ 2. **The four constraints on `/approve`** (drop one and it becomes an arbitrary-command-execution back door):
309
+ (a) it can only **answer** an approval request that already exists — the body is exactly `{id, outcome}`, with
310
+ **no free text, paths, or command arguments**, so it can answer questions but never start an action;
311
+ (b) `id` must belong to a request this process created and that is **still pending** (single use);
312
+ (c) `outcome` accepts only `allowed-once` / `rejected` — there is **no "always allow"**;
313
+ (d) when no panel is watching, the panel is closed, or the window (5 minutes by default) expires, the request goes
314
+ **back to the official
315
+ path** — never auto-approved (DSH's `approval/request` itself fails closed; this bridge can only keep
316
+ "nobody answered" as "nobody answered"). `pnpm test:webview` asserts these four plus the host-side whitelist.
317
+ 3. **Any request carrying `Origin` gets 403.** Browsers always send one (including a sandboxed iframe's literal
277
318
  `Origin: null`); the Node extension host never does. Origin is checked **before** the token — otherwise the
278
319
  bridge would be a "did you guess the token right" oracle for a web page.
279
- 3. **Paths are confined to the editor's current workspace folders.**
280
- 4. **Everything is bounded**: 200 diagnostics, 500-char messages, 256 KB request bodies, a 64-entry event ring.
320
+ 4. **Paths are confined to the editor's current workspace folders.**
321
+ 5. **Everything is bounded**: 200 diagnostics, 500-char messages, 256 KB request bodies, a 64-entry event ring,
322
+ ≤120 thread entries per session (≤8000 chars each, ≤4 watched sessions) and ≤4 pending approvals.
281
323
 
282
324
  This layer stops "another local app or a browser page that got hold of the file". A malicious program running as
283
325
  the same user could read your files and the token anyway — that is outside this plugin's threat model, exactly
@@ -354,13 +396,14 @@ Diagnostics: `GET /api/code-server/status` exposes
354
396
 
355
397
  ```powershell
356
398
  cd C:\Users\User\Desktop\dsh-code-server-app
357
- pnpm install # dev deps (esbuild + motion); allowBuilds is explicit → no postinstall runs
399
+ pnpm install # dev deps (esbuild + the official-renderer bundling deps); allowBuilds is explicit → no postinstall runs
358
400
  pnpm run build:client # src/factory.js → lib/client.js (not committed; must be built first)
401
+ pnpm run build:webview # ask panel: official Markdown renderer + panel shell → webview/thread.{js,css} (not committed; must be built first)
359
402
  pnpm run vendor:check # optional: show the bundled tree version vs the latest code-server release
360
403
  pnpm run vendor:vscode # ① produce vendor/vscode (the trimmed VS Code tree, ~197MB)
361
404
  pnpm run repack:build -- --target win32-arm64,win32-x64 --pack # ② one script builds every sub-package
362
405
  pnpm run publish:repacks # ③ publish every @jinsiyu/* sub-package (default dist-tag: next)
363
- pnpm pack # ④ → dsh-code-server-app-<version>.tgz (~107KB)
406
+ pnpm pack # ④ → dsh-code-server-app-<version>.tgz (~750KB, including the panel renderer assets)
364
407
  pnpm run publish:plugin # ⑤ publish the plugin itself (default dist-tag: next)
365
408
  # once the user has restarted dsh web and confirmed it works, promote latest:
366
409
  pnpm run promote -- <version>
@@ -375,6 +418,14 @@ pnpm run promote -- <version>
375
418
  > so their dist-tags do not affect resolution, but they default to `next` as well.
376
419
  > Inspect the current tags with `npm dist-tag ls dsh-code-server-app`.
377
420
 
421
+ > `build:webview` bundles DSH's **official** Markdown renderer and design tokens into the panel assets
422
+ > (~1.34MB: 996KB JS + 87KB CSS + 254KB KaTeX fonts), so it needs a local DSH deployment: the script reads the
423
+ > `@deepseek-ai/dsh-web-frontend` version from that deployment and compares it with the renderer version pinned in
424
+ > devDependencies — a mismatch **fails the build** (unless `--allow-version-mismatch`). Same convention as
425
+ > `lib/client.js`: the artifacts are not committed and `prepack` rebuilds them.
426
+ > Rationale (why not an iframe, where the tokens come from, size trade-offs) is section 21 of
427
+ > `docs/analysis-code-server-as-dsh-plugin.md`.
428
+
378
429
  `repack:build` (`scripts/vendor-repacks.mjs`) is the **single script that produces every sub-package**:
379
430
 
380
431
  | Sub-package | Content | os/cpu |
@@ -484,7 +535,9 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
484
535
  >
485
536
  > **Changing the client bundle**: edit `src/factory.js` then run `pnpm run build:client`
486
537
  > to regenerate `lib/client.js` (that artifact is not tracked; a browser refresh picks it up — no host restart needed).
487
- > Window animations are driven by the embedded `motion`; feel parameters live in `winPhysics` (one spot) in `src/factory.js`.
538
+ > **Changing the ask panel**: edit `assets/extensions/dshcs-editor-bridge/webview/src/*` then run
539
+ > `pnpm run build:webview` (same convention: generated, not tracked; the IDE must be restarted once to pick it up,
540
+ > because the extension host caches the webview resources).
488
541
 
489
542
  ### Pack-machine environment (the user machine needs nothing)
490
543
 
@@ -704,6 +757,20 @@ What remains on the plugin side:
704
757
  or by simply calling `editor_context` — 0.3.0–0.3.11 sat in the state "health says bridge:true, extension never
705
758
  loaded" (cause above: the user-level install was marked `.obsolete`).
706
759
  - **Bridged state can lag by up to 600 ms**, and the tools say "stale" rather than serving data older than 10 s.
760
+ - **The ask panel renders only "new content" (0.3.22)**: the subscription starts when the panel opens, the history
761
+ `records` from `follow`'s opening frame are discarded, and the panel has **no "load earlier"** (the history-paging
762
+ API `sessionController.page()` is deliberately not called in this version). Switch to the DSH UI for older content.
763
+ - **Panel highlighting ships only DSH's boot grammar set** (typescript / shellscript / json): the rest of the
764
+ official grammars load lazily through `import()` (~1.6MB total), and the panel is a single-file IIFE with no
765
+ lazy loading, so those languages render as plain text (exactly like DSH's own first render, no errors).
766
+ For the full set: `node scripts/build-webview.mjs --all-grammars`.
767
+ - **Panel assets are pinned to the DSH version**: the renderer is bundled against the UI version of the deployed
768
+ DSH, so after upgrading DSH you must rebuild the panel (`pnpm run build:webview`; the build fails loudly on a
769
+ version mismatch). The panel also shows a mismatch notice at runtime instead of silently using the wrong renderer.
770
+ - **The approval window in the panel is 5 minutes**: while the panel is open, approvals ask the panel first (the card
771
+ shows a countdown); **closing the panel** or letting the 5 minutes run out hands the request back to the DSH UI —
772
+ after that, that request can **only** be answered there (the card disappears from the panel and the thread keeps an
773
+ audit row).
707
774
  - **Unsaved buffers are reported, not taken over.** The agent still edits via its own `fs` tools, i.e. against
708
775
  disk. What the bridge adds is a notice *before* writing a dirty file, a diff *after*, and a warning instead of
709
776
  an overwrite. It does not decide whether the user saves — that would mean changing the agent's read path,
package/README.md CHANGED
@@ -21,6 +21,10 @@
21
21
  > 0.2.0 起:**argon2 与 code-server 的 136 个运行时依赖(express / proxy-agent / js-yaml / pem / limiter …)
22
22
  > 全部不再随包分发**(减少 ~34.5MB + 一条原生构建链);IDE 提供方式见下方「服务方式(serve)」。
23
23
  > 依据与实测证据见 `docs/analysis-code-server-as-dsh-plugin.md`(含子路径挂载、WS 路径、命名管道、fence 的逐项验证)。
24
+ >
25
+ > 0.3.22 起:「问 DSH」面板直接渲染 **DSH 官方的 markdown 结果**(与 DSH 界面同一份渲染器 + 同一套设计令牌,
26
+ > 只渲染该会话的新内容),并且**就在面板里处理授权**(工作区外写入 / 执行命令)—— 详见
27
+ > 「与 DSH 的协同:编辑器桥」与 `docs/analysis-code-server-as-dsh-plugin.md` 第 21 节。
24
28
 
25
29
  ## UI 载体与 DSH 版本要求(0.2.3 起只支持带右侧栏的 DSH)
26
30
 
@@ -194,20 +198,33 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
194
198
  | 方向 | 能力 | 落地方式 |
195
199
  |---|---|---|
196
200
  | 编辑器 → agent | **未保存缓冲区**(磁盘内容 ≠ 用户所见)、活动文件与选区、**语言服务器诊断**(含 file:line、来源、code) | agent 工具 `editor_context` / `editor_diagnostics`;写脏文件前额外附一条提醒 |
197
- | 编辑器 → DSH | 选中代码 → 右键「DSH: 针对选中内容提问」→ 打开**提问面板**(带 `文件:行` 与选中内容);提问以**用户输入**进当前会话,回答**同步显示回面板** | 命令 `dsh-code-server.askAboutSelection`(编辑器右键菜单**最上面两条**之一)+ webview 面板 + `POST /ask` + `/sync` 的 `answers` 字段 |
201
+ | 编辑器 → DSH | 选中代码 → 右键「DSH: 针对选中内容提问」→ 打开**提问面板**(带 `文件:行` 与选中内容);提问以**用户输入**进当前会话,该会话的**新内容**用 DSH 官方 markdown 渲染器显示在面板里 | 命令 `dsh-code-server.askAboutSelection`(编辑器右键菜单**最上面两条**之一)+ webview 面板 + `POST /ask` + `/sync` 的 `thread` 字段 |
202
+ | DSH → 编辑器(授权) | agent 要**写工作区外的文件 / 执行命令**时的授权请求 → 面板里就地弹卡片(工具名 + 原因 + 倒计时),点「允许一次 / 拒绝」立刻生效 | `/sync` 的 `approvals` 字段 + `POST /approve`(桥里**唯一**的非只读路由,约束见「安全模型」) |
198
203
  | agent → 编辑器 | agent 改了哪个文件 → 开**原生 diff** 审阅;缓冲区有未保存改动时**告警而不覆盖** | host 观察 `tools/result`,扩展轮询后开 diff + 非模态告警 |
199
204
 
200
205
  - 工具只在桥就绪时注册(IDE 没起来时模型看不到"有个用不了的工具");提示词段落也只在桥存活时渲染。
201
- - **提问面板**(扩展 0.2.0 起):右键命令不再弹一行输入框,而是一个面板 —— 上面是问答记录、下面是输入框;
202
- 发送时**现取当前选区**(可以在面板开着的同时改选区再问);回答跟着轮询一起刷新,不用切回 DSH 界面。
206
+ - **提问面板**(扩展 0.2.0 起;0.2.3 起正文走官方渲染器;0.2.4 起是**对话框形式**):右键命令打开的不是
207
+ 侧栏里的一列,而是一扇占满编辑器区、内容居中限宽的**对话窗**(右上角 ✕ 关闭;面板开着的**同时**还能改选区再问)。
203
208
  两个命令的**意图分开记**(0.3.21):「针对选中内容提问」只有真的选了内容才带**行区间 + 选区正文**;
204
209
  「针对当前文件提问」**永远不带行号、不带选区** —— 光标停在哪一行跟问题无关,行号只会误导 agent;
205
210
  没选区时用选中命令提问也会退化成纯文件。
211
+ - **面板里的正文就是 DSH 的渲染结果**(0.3.22):面板打包了 DSH 官方的 markdown 渲染器
212
+ (`@deepseek-ai/dsh-client-ui-primitives` 的 `MarkdownText`)与官方设计令牌 —— 同一套 micromark/mdast 管线、
213
+ 同一个增量流式解析器、同一个 shiki 高亮(启动集 typescript / shellscript / json)、KaTeX 公式、同样的标题与表格排版。
214
+ 只渲染**新内容**(从面板订阅那一刻起),**不重放历史**、没有"加载更早"。
215
+ - **思考过程也照官方显示**(0.3.23):助手的 reasoning 以「思考」行出现 —— **默认收起**、收起时显示首行
216
+ (流式时显示最新一行)、点整行展开全文,用的就是官方 `DisclosureRow` + 官方的思考图标与排版语言。
217
+ - **授权就在面板里处理**(0.3.22;0.3.23 修好窗口):面板打开着的时候,该会话的授权请求**先问面板**(默认 5 分钟),
218
+ 点「允许一次」/「拒绝」立刻生效;**关掉面板**或等满窗口就把请求**原样交回官方链路**(DSH 界面照旧弹卡)。
219
+ 0.3.22 的 8 秒窗口对人来说太短 —— 卡片还没读完按钮就灰了(实测反馈「授权框失效了」),现在窗口是 5 分钟,
220
+ 而且面板一关就立刻交回、不干等。
221
+ **永不自动放行** —— `allowed-once` 只能来自你的一次点击,面板里没有"以后都允许"这种入口。
206
222
  - 提问进 DSH 会话时是**普通用户消息**(`source: { kind: 'user' }`,host 0.3.19 修正):早期版本用
207
223
  `{kind:'plugin'}` 会被 DSH 渲染成"上下文更新",看起来不像自己说的话;来源信息靠正文首行
208
224
  `From the editor: <file>[:<行>]` 保留。
209
- - **全部只读**:桥不写文件、不改文档、不执行命令。agent 的写操作仍然全部走它自己的 `fs` 工具,
210
- 桥只是"知道它写了什么"。
225
+ - **只读 + 一个受限例外**:桥不写文件、不改文档、不执行命令;唯一的非只读路由是 `POST /approve`,
226
+ 它只能**回答**已经存在的授权请求(见「安全模型」第 2 条)。agent 的写操作仍然全部走它自己的 `fs` 工具,
227
+ 桥只是"知道它写了什么"、并把你对授权的答复带回去。
211
228
  - 编辑器侧的入口还有状态栏的 `$(plug) DSH`(连通时显示,点击打开日志),日志在输出面板
212
229
  「DSH Editor Bridge」里 —— 出问题时先看它。
213
230
  - **扩展装在内置目录**(0.3.12 修正):`dshcs-editor-bridge` 与 `dshcs-open-file` 一样装进
@@ -219,10 +236,11 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
219
236
  ### 三条通道(0.3.13 起走**本机 IPC**:Windows 命名管道 / unix socket)
220
237
 
221
238
  ```
222
- 扩展 → host POST /code-server-bridge/sync 一趟来回:上报编辑器状态 + 取回待处理事件
223
- 扩展 → host POST /code-server-bridge/ask 把编辑器里的提问投进当前会话
224
- 扩展 → host GET /code-server-bridge/health 无鉴权探活(便于重启后一眼确认)
225
- 扩展 → host POST /code-server-bridge/event 扩展上报打开/关闭文件等(进 host 日志尾)
239
+ 扩展 → host POST /code-server-bridge/sync 一趟来回:上报编辑器状态(+ 面板在看哪个会话)+ 取回待处理事件与对话流
240
+ 扩展 → host POST /code-server-bridge/ask 把编辑器里的提问投进当前会话
241
+ 扩展 → host POST /code-server-bridge/approve 回答一条**已经存在**的授权请求(唯一的非只读路由)
242
+ 扩展 → host GET /code-server-bridge/health 无鉴权探活(便于重启后一眼确认)
243
+ 扩展 → host POST /code-server-bridge/event 扩展上报打开/关闭文件等(进 host 日志尾)
226
244
  host → 扩展 <extensionsDir>/.dshcs-bridge/bridge.json 端点 + 令牌(扩展每 5s 重读)
227
245
  (同一份内容还会写到**内置扩展旁边** `<树>/lib/vscode/extensions/.dshcs-bridge/` ——
228
246
  环境变量只在 host spawn IDE 时注入,而被**接管**的 IDE 是上一次启动的进程、拿不到它,
@@ -231,6 +249,14 @@ host → 扩展 <extensionsDir>/.dshcs-bridge/bridge.json 端点 + 令牌(
231
249
 
232
250
  请求走 `http.request({ socketPath })`(`fetch` 不支持 socket),**不开任何端口**。
233
251
 
252
+ `/sync` 的响应里,面板真正用到的三段(0.3.22):
253
+
254
+ | 字段 | 内容 | 面板怎么用 |
255
+ |---|---|---|
256
+ | `thread` | 被面板 `watch` 的会话的**新内容**条目(user / assistant / tool / approval),有界:每会话 ≤120 条、单条正文 ≤8000 字符、同时 watch ≤4 个会话 | 助手正文交给官方渲染器;工具与授权是紧凑摘要行 |
257
+ | `approvals` | 待决授权请求 `[{id, toolName, reason, at}]`(≤4 条) | 弹卡片 + 倒计时;点按后 `POST /approve` |
258
+ | `approvalHoldMs` / `uiVersion` | 授权窗口长度(默认 300000ms = 5 分钟)/ DSH 界面版本 | 倒计时基准;渲染器版本不一致时提示 |
259
+
234
260
  > **为什么不是 HTTP(0.3.13 定论,三条都实测过)**
235
261
  > 1. **desktop 根本没有 HTTP 面**:渲染进程经 Electron IPC 调 `host.fetch()`
236
262
  > (`apps/desktop-host/src/index.ts:308` 的 `createSharedFetchHandler('/api')`)—— 那是进程内函数调用,
@@ -253,21 +279,29 @@ host 反向请求不到它。所以编辑器状态只能在扩展主动发起的
253
279
  **为什么不用 SSE/WebSocket**:扩展宿主里没有 HTTP 服务器,而桥的形态是"每 600ms 一趟请求/响应"。
254
280
  轮询给了两条好性质:幂等(丢一次事件只是少一次提示,数据本身永远在编辑器里),以及状态天然最新(每趟都刷新)。
255
281
 
256
- ### 安全模型(四条不变量,改 `lib/bridge.mjs` 之前先读)
282
+ ### 安全模型(五条不变量,改 `lib/bridge.mjs` 之前先读)
257
283
 
258
284
  桥的令牌写在 `<extensionsDir>/.dshcs-bridge/bridge.json`(**对本机同用户进程可读**),所以:
259
285
 
260
- 1. **`/code-server-bridge/*` 永久只读。** 没有写文件、改文档、执行命令的路由。
261
- 令牌泄露的爆炸半径被封在"看到编辑器里的信息",**不会**变成任意文件写/任意命令执行。
286
+ 1. **`/code-server-bridge/*` 只读,只有 `/approve` 一个例外。** 没有写文件、改文档、执行命令、
287
+ 拉起进程的路由。令牌泄露的爆炸半径被封在"看到编辑器里的信息",**不会**变成任意文件写/任意命令执行。
262
288
  `scripts/test-bridge-routes.mjs` 里有一条白名单断言盯着这件事(未知后缀一律 404)。
263
- 2. **带 `Origin` 的请求一律 403。** 浏览器发起必带 Origin(含沙箱 iframe 的 `Origin: null`),
289
+ 2. **`/approve` 的四条约束(缺一条就等于开了任意命令执行的后门,不许放宽)**:
290
+ (a) 只能**回答**已经存在的授权请求,请求体只有 `{id, outcome}`,**不接受任何自由文本 / 路径 / 命令参数**
291
+ —— 它只能"回答问题",不能"发起动作";(b) `id` 必须是本进程自己发起、且**仍未决**的请求(用后即废);
292
+ (c) `outcome` 只接受 `allowed-once` / `rejected`,**没有"永久允许"**;
293
+ (d) 没有面板在看 / 面板关掉 / 窗口超时(默认 5 分钟)→ **交回官方链路**,绝不自动放行
294
+ (DSH 的 `approval/request` 本身 fail closed,这里只能把"没人答"保持成"没人答")。
295
+ `pnpm test:webview-bundle` 里有针对这四条与宿主侧白名单的断言。
296
+ 3. **带 `Origin` 的请求一律 403。** 浏览器发起必带 Origin(含沙箱 iframe 的 `Origin: null`),
264
297
  扩展宿主是 Node 进程、不带。判定顺序上 Origin **先于令牌** —— 否则等于给浏览器一个
265
298
  "令牌猜对没有"的 oracle。
266
299
  实现细节:Node 路由 → Fetch 适配器把**原始 headers** 挂在 request 上(`dshcsRawHeaders`),
267
300
  因为 undici 的 `Request` 构造器会把 `origin` 当 forbidden header 归一化掉 —— 读 `request.headers`
268
301
  会让这道 403 静默失效(测试里有这条实测记录)。
269
- 3. **路径收敛在编辑器当前工作区**(`workspaceFolder` 之外的诊断直接丢弃)。
270
- 4. **有界**:诊断默认 200 条 / 单条截断 500 字符 / 上报体上限 256KB / 事件环形缓冲 64 条。
302
+ 4. **路径收敛在编辑器当前工作区**(`workspaceFolder` 之外的诊断直接丢弃)。
303
+ 5. **有界**:诊断默认 200 条 / 单条截断 500 字符 / 上报体上限 256KB / 事件环形缓冲 64 条 /
304
+ 对话流每会话 ≤120 条(单条正文 ≤8000 字符、同时 watch ≤4 个会话)/ 待决授权 ≤4 条。
271
305
 
272
306
  这一层挡的是"本机其它应用或浏览器页面拿到那个文件后乱调桥";**同用户的本地恶意程序**
273
307
  本来就能直接读你的文件与令牌文件 —— 那不在本插件的威胁模型内(与「回环端口的安全模型」同一句话)。
@@ -337,18 +371,25 @@ host 反向请求不到它。所以编辑器状态只能在扩展主动发起的
337
371
 
338
372
  ```powershell
339
373
  cd C:\Users\User\Desktop\dsh-code-server-app
340
- pnpm install # 开发依赖(esbuild + motion);allowBuilds 已显式声明 → 不执行任何 postinstall
374
+ pnpm install # 开发依赖(esbuild + 官方渲染器打包依赖);allowBuilds 已显式声明 → 不执行任何 postinstall
341
375
  pnpm run build:client # src/factory.js → lib/client.js(不入库,必须先构建)
376
+ pnpm run build:webview # 「问 DSH」面板:官方 markdown 渲染器 + 面板外壳 → webview/thread.{js,css}(不入库,必须先构建)
342
377
  pnpm run vendor:check # 可选:查看内置 VS Code 树版本 vs code-server 最新版
343
378
  pnpm run vendor:vscode # ① 生成 vendor/vscode(精简 VS Code 树,≈197MB)
344
379
  pnpm run repack:build -- --target win32-arm64,win32-x64 --pack # ② 统一脚本产出全部子包(见下表)
345
380
  pnpm run publish:repacks # ③ 发布全部 @jinsiyu/* 子包(默认 dist-tag = next)
346
- pnpm pack # ④ → dsh-code-server-app-<version>.tgz(约 107KB)
381
+ pnpm pack # ④ → dsh-code-server-app-<version>.tgz(约 750KB,含面板渲染器产物)
347
382
  pnpm run publish:plugin # ⑤ 发布插件本体(默认 dist-tag = next)
348
383
  # 用户重启 dsh web 确认无误后,再把 latest 推进到该版本:
349
384
  pnpm run promote -- <version>
350
385
  ```
351
386
 
387
+ > `build:webview` 会把 DSH **官方**的 markdown 渲染器与设计令牌打进面板产物(≈1.34MB:JS 996KB +
388
+ > CSS 87KB + KaTeX 字体 254KB),所以它要求本机有 DSH 部署:脚本读部署里 `@deepseek-ai/dsh-web-frontend`
389
+ > 的版本,与 devDependency 钉住的渲染器版本比对,**不一致就报错退出**(`--allow-version-mismatch` 才放行)。
390
+ > 它与 `lib/client.js` 同一约定:产物不入 git,`prepack` 里会自动重跑。
391
+ > 细节(为什么不是 iframe、令牌从哪来、体积取舍)见 `docs/analysis-code-server-as-dsh-plugin.md` 第 21 节。
392
+
352
393
  > **dist-tag 政策(必须遵守)**:发布一律发到 **`next`**,**不动 `latest`**;
353
394
  > `latest` 只保留「最近一个确认无 bug 的版本」,由 `pnpm run promote -- <version>`
354
395
  > (= `npm dist-tag add dsh-code-server-app@<version> latest`)在**用户重启 dsh web 确认无误后**才推进。
@@ -388,8 +429,9 @@ pnpm run promote -- <version>
388
429
  ```powershell
389
430
  pnpm test:apply # 桩 ctx 下跑通 apply(回归:apply 期的 ReferenceError)
390
431
  pnpm test:claim-types # 认领类型语法与默认值
391
- pnpm test:bridge-routes # 编辑器桥:路由表只读白名单 / Origin 与令牌的判定顺序 / 令牌头三处一致
392
- pnpm test:bridge-extension # 编辑器桥扩展侧纯逻辑:未保存缓冲区上报、诊断排序截断、diff 判据、投递降级
432
+ pnpm test:bridge-routes # 编辑器桥:路由表白名单(只读 + /approve)/ Origin 与令牌的判定顺序 / 令牌头三处一致
433
+ pnpm test:bridge-extension # 编辑器桥扩展侧纯逻辑:未保存缓冲区上报、诊断排序截断、diff 判据、投递降级、面板状态机
434
+ pnpm test:webview # 面板 webview 产物:官方渲染器与令牌打包、版本一致、/approve 的四条约束(先跑 build:webview)
393
435
  pnpm test:launcher-routes # launcher 的 HTTP 面(起真进程,较慢)
394
436
  pnpm test:workspace-switch # 切工作区不重启进程
395
437
  pnpm test:fullscreen # 打开标签即全屏
@@ -490,7 +532,8 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
490
532
  >
491
533
  > **改动 client bundle**:编辑 `src/factory.js` 后执行 `pnpm run build:client`
492
534
  > 重新生成 `lib/client.js`(仓库不跟踪该产物;浏览器刷新即生效,host 无需重启)。
493
- > 窗口动画由内嵌 `motion` 驱动,手感参数在 `src/factory.js` 的 `winPhysics`(一处)。
535
+ > **改动提问面板**:编辑 `assets/extensions/dshcs-editor-bridge/webview/src/*` 后执行
536
+ > `pnpm run build:webview`(同一约定:产物不入库;IDE 需重启一次才会加载新产物,扩展宿主会缓存 webview 资源)。
494
537
 
495
538
  ### 打包机环境要求(使用者机器什么都不需要)
496
539
 
@@ -684,6 +727,17 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
684
727
  - **未保存缓冲区是"上报"而不是"接管"**:agent 仍然通过它自己的 `fs` 工具按磁盘内容编辑。
685
728
  桥能做的是**在写之前提醒**、**写之后给 diff**、**冲突时告警而不覆盖** ——
686
729
  它不能替用户决定保存与否(那需要改动 agent 的读路径,不在本版本范围内)。
730
+ - **提问面板只渲染"新内容"(0.3.22)**:订阅从面板建立那一刻开始,`follow` 开帧里的历史 `records` 被丢弃,
731
+ 面板里**没有"加载更早"**(历史分页 API `sessionController.page()` 在这个版本里刻意不调用)。
732
+ 想看更早的内容请回 DSH 界面。
733
+ - **面板的高亮只带 DSH 启动集的三套语法**(typescript / shellscript / json):官方其余语法走**懒加载**
734
+ (按需 `import()`,合计约 1.6MB),面板是单文件 IIFE、没有按需加载,所以那些语言的代码块**纯文本**显示
735
+ (与 DSH 首次渲染时的样子一致,不报错)。要全量:`node scripts/build-webview.mjs --all-grammars`。
736
+ - **面板产物与 DSH 版本绑定**:渲染器按构建时 DSH 部署的界面版本打包,DSH 升级后**要重打面板**
737
+ (`pnpm run build:webview`;构建脚本会在版本不一致时直接报错)。运行期面板顶部也会提示版本不一致,
738
+ 不会悄悄用错版本的渲染器。
739
+ - **面板里的授权窗口是 5 分钟**:面板打开着的时候授权先问面板(卡片上有倒计时);**关掉面板**或等满 5 分钟
740
+ 就交回 DSH 界面 —— 交回之后这一条**只能**在 DSH 界面里处理(卡片从面板消失,对话流里留一行授权审计)。
687
741
 
688
742
  - ~~子路径不支持~~ **已不成立(0.2.0 实测更正)**:VS Code 渲染出的 workbench HTML 里
689
743
  **资源引用全是相对路径**(实测 9 条引用中绝对路径 0 条,`serverBasePath="."`、`rootEndpoint="."`),