Commit Graph
704 Commits
Author SHA1 Message Date
figmar 061964e3e8 fix(subagent): stop 401-ing the aside-open with a guessed child mode
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
The aside button could hit `subagent/unauthorized: subagent mode does not
match the supplied address`. The host's session-controller (dsh-api-session-
controller validateAddress) compares the `mode` in the resource address
against the child's durable descriptor mode and rejects any mismatch. The
address `mode` came from the row, and on the degraded path the row's mode is
a best-effort guess from the summary `running` flag (running => continuable)
— so a child that is genuinely one-shot but still running was guessed as
continuable and the very open it was meant to trigger 401'd.

Two layers, so it holds on both the real-catalog and the degraded path:

- subagentChatAddress: only pass a mode through when it is one of the two
  durable values (`one-shot` / `continuable`). Anything else is omitted,
  which the controller reads as `mode: "unknown"` and therefore skips the
  comparison — the safest form when the caller does not actually know.
- openChildAside: prefer the host's real descriptor mode for the child
  (the `subagentsByParent` entry carries the durable mode) over the address
  mode, falling back to the address only when the real catalog never arrived.

Gates: typecheck / lint / build / test 132 files 1392 passed.
2026-10-14 10:18:58 +08:00
figmar 6b4a29ba24 feat(subagent): open a child's chat in the native right sidebar
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
The host's own catalog puts an "open beside" action on every non-current
child row; this port had never wired it. Add it: the row grows a hover
button that opens the child's chat as a `subagentchat` resource in the
native right Sidebar, so a user can watch a child work without giving up
the pane he is in (the in-pane jump stays the plain row click).

- src/client/native/subagent-chat.ts: the resource-address codec, copied
  from @deepseek-ai/dsh-client-ui-subagent rather than imported (the
  package exports no helpers and the plugin must not reach into it).
- native/surface.ts: carry `kind` / `preferNewPane` on an open.
- SubagentView: `openChildAside` via `ctx.get?.('sidebarRight')` — an
  optional probe, since a fake ctx without `get` otherwise crashes the
  whole render (9 specs caught it) — threaded through all three
  CatalogRows call sites.
- locales: `subagentOpenAside` in zh/en plus all 20 third-language
  dictionaries (tests/locales.spec.ts enforces key-set parity).
- docs: the conventions page now documents the resource-open recipe.

Gates: typecheck / lint / build / test 132 files 1390 passed /
check:consumer-types, plus the repo's runtime-contract symbol check.
2026-10-14 09:32:16 +08:00
figmar 35d33c7f65 fix(subagent): hydrate the catalog via the host's refreshProjections
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
0.2.0-rc.2 withdrew every catalog/subagent seam this fork was written
against, and all three calls degraded to silent no-ops:

  openSubagent(address)             -> gone
  setSubagentCatalogOpen(id, open)  -> gone
  refreshSubagents(id)              -> gone

The host's own subagent UI (dsh-client-ui-subagent) rebuilds a parent's
child catalog with `sessions.refreshProjections(parentSessionId)`, so
that is the only kick that still exists. Wire it into `observe()` and the
catalog retry button, keeping the legacy names as fallbacks for older
hosts. Without it the root catalog never hydrated: the page sat on
"loading" or degraded to synthesized summary rows.

Also document what the investigation settled:
- docs/subagent-state-enumeration.md: the full state model (catalog
  child / diagnostic / session / job / sidechat) and proof that this
  fork already renders it 1:1 with upstream.
- docs/plugin-conventions.md: the hard rules another sidebar plugin
  must follow (host schemastery, volatile prefs, ns = profile entry id,
  no colons in tab ids, never read a volatile field directly) plus the
  withdrawn-API replacement table and a pre-release checklist.
- docs/feedback-0.2.0-rc2-contract-gaps.md: gaps 2/3 reframed. The ask
  is documenting the official paths (open(childSessionId) /
  refreshProjections(id)), not adding new APIs.

Gates: typecheck 0, lint 0, build 0, test 131 files / 1385 passed.
2026-10-13 23:18:30 +08:00
figmar aa8f417aa3 fix(settings): use the host's @deepseek-ai/schemastery so volatile prefs apply
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
Every side-card switch was dead: settings.update returned OK and
cordis.patch.yml was written, yet the running plugin never saw the new value
and nothing reported an error.

Root cause: the plugin declared Config with the plain npm `schemastery`
(3.18.0), which has no volatile support. The host resolves plugin config via
`runtime.Config['~standard'].validate()` — i.e. the plugin's OWN schemastery —
so `meta.volatile` nodes were never wrapped in cosmokit volatile references.
`cordis-plugin-loader`'s `_commitVolatile()` bails out as soon as
`volatileEntries(fiber.config)` is empty, so every volatile-only edit was
silently dropped: no in-place update, no plugin restart.

Fix: import z from '@deepseek-ai/schemastery' (peer provided by the host at
mount; pinned 3.18.4 as a devDependency for the dev tree), matching the
repo's existing pnpm-workspace convention for @deepseek-ai/* peers.

Verified: probe reports tabsIsVolatile=true and revision climbing 0->6 across
six writes, each read-back matching the written value (previously the value
was frozen and revision stuck at 1).

Also in this change:
- apes tab ids lose their colon (apes:tasks -> apesTasks, ...): the host's
  settings projection dropped map keys containing a colon.
- sidebar prefs ns follows the profile entry id (`better-sidebar`).
- subagent rows degrade to summary rows when the root catalog never hydrates.
- docs/feedback-0.2.0-rc2-contract-gaps.md: four host contract gaps, gap 4
  documenting the silent volatile-write drop above.

Gates: typecheck 0, lint 0, build 0, test 131 files / 1385 passed.
2026-10-13 22:27:19 +08:00
figmar 043340eb02 fix(subagent): degrade to summary rows when root catalog never hydrates
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
The Subagent/任务管理 page pinned on a permanent '加载中…' spinner
whenever the host's per-parent subagent catalog (subagentsByParent)
never hydrated for the root session — rootCatalog stayed undefined
while the session mirror already listed the children, so
summaryBackedLoading was true forever (SubagentView.tsx summaryBacked
gate). Root cause: a host-side hydrate gap (e.g. the
agent/disposed listener TypeError breaking the refresh chain) leaves
setSubagentCatalogOpen unanswered, and the UI had no escape.

Add a 5s hydrate timeout: once setSubagentCatalogOpen has had a chance
to land and the root catalog is still un-hydrated, render the known
children from the session mirror as CLICKABLE summary rows
(SummaryFallbackRows) instead of the infinite spinner. The timer resets
on root switch / catalog hydrate / page hidden. mode is inferred from
the summary's running flag (best-effort transport hint).

Verified: lint 0 / typecheck 0 / test 131 files·1385 passed / build 0.
Rollback: git revert <this-sha> or reset SubagentView.tsx to 99bef0f.
2026-10-13 09:28:47 +08:00
figmar 99bef0f877 chore(lint): drop unused imports flagged at HEAD
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s
eslint was red at HEAD (5b715ac) in files untouched by this branch: unused statSync/dirname (check-plugin-api.mjs), unused PrefsSchema (src/index.ts, tests/smoke.spec.ts) and two Function types (tests/smoke.spec.ts). No behaviour change.
2026-10-11 21:18:08 +08:00
figmar 66ee3953c9 feat(apes): revive the task/message tabs in the info layer
C6 removed apes:tasks as a duplicate of the host session panel; in practice the two answer different questions (host panel = switch workspaces, plugin tab = who is running right now), so the tab is restored and the info layer goes back to five types.

tasks: live session list (current / running / subagent markers) + showInactive toggle via pluginSettings['apes:tasks']; messages: placeholder that states the host has no message feed yet; both get badge counters, and the whole layer now honours the visible gate (auto-collapse) so hidden tabs stop waking on session events.

zh/en/ja dictionaries in sync (AGENTS 5); wiki 02/03/07 reverted from three to five tabs; decision record in docs/plans/2026-10-11-apes-tab-revival.md.
2026-10-11 21:18:08 +08:00
figmar 5a715acb85 fix(test): remove jsdom pragma + add window stub DOM surface
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
A/B fix per WorkBuddy review:
- browser-globals.ts: add addEventListener/removeEventListener/dispatchEvent
  to window stub, add compatMode to document stub
- Remove // @vitest-environment jsdom from 5 specs that only use
  renderToString/createElement (SSR, no DOM needed)

Test Files 131 passed (131), Tests 1367 passed, EXIT=0
v1.2.0-gen3
2026-10-11 20:01:21 +08:00
figmar 2b8ba87d81 chore: add check-plugin-api.mjs contract check + verify script
- Copy check-plugin-api.mjs into scripts/ (from WorkBuddy diagnosis)
- Add 'verify' script: node scripts/check-plugin-api.mjs
- 5-section contract check: dependency versions vs DSH runtime,
  import symbols vs runtime exports, slot registration, removed/deprecated
  APIs, inline package dependency closure
- All 5 sections pass (4 deprecation warnings, non-blocking)
2026-10-11 19:33:44 +08:00
figmar a6dd8aa42a fix(test): add missing devDeps + sourcemap transform + jsdom directives
WorkBuddy review found 38 of 131 spec files failed at collection time
(0 tests executed) — the '1015 passed' was a false green because vitest
only counts 'Tests failed', not 'Test Files failed'.

Root cause: dsh-client-ui-primitives@0.2.0-rc.2 declares dependencies:null
but statically imports 23 external root packages. Our project declared
only 18. Under pnpm strict layout, the missing 5 (dsh-client-store,
dsh-util-code-language, dsh-util-workspace-path, diff, simple-icons)
were unresolvable when Vite inlined the primitives package.

Fixes:
- Add 7 missing devDeps: dsh-client-store@0.2.0-rc.2,
  dsh-util-code-language@0.2.0-rc.2, dsh-util-workspace-path@0.2.0-rc.2,
  diff@9.0.0, simple-icons@16.35.0, zustand@5.0.15, immer@11.1.21
- vitest.config.ts: add transform plugin to strip sourceMappingURL
  comments from dsh-client-ui-primitives (upstream ships lib/index.js
  with trailing sourceMappingURL but package.json files excludes .map)
- 5 spec files: add @vitest-environment jsdom directive (they import
  dsh-client-ui-primitives which calls window.addEventListener at module
  load; node environment lacks window)
- Remove empty .map file hack from node_modules (ineffective + .gitignore
  blocks it from version control)

Result: Test Files 131 passed (131), Tests 1367 passed | 9 skipped,
EXIT=0.

Verified: simple-icons 25 named exports all present.
Typecheck + build pass.
2026-10-11 19:33:01 +08:00
figmar f4c44d13d6 feat: align devDeps to 0.2.0-rc.2 (21 packages)
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
- Bump 21 @deepseek-ai devDeps from 0.1.5-rc.2 → 0.2.0-rc.2
  (dsh-code-runtime stays at 0.1.5-rc.2 — no 0.2.0-rc.2 published)
- Fix sidechat-routes.ts: source.kind 'plugin' → 'user'
  (0.2.0-rc.2 removed 'plugin' from MessageSourceMap)
- Remove unused SIDE_INJECTION_PLUGIN import
- Fix test fixtures for 0.2.0-rc.2 stricter validation:
  - tool/result message role: 'user' → 'tool'
  - add toolCallId at message top level
  - assertion kind: 'plugin' → 'user'
- Icon migration: Icon*14 → Icon*Regular, Icon*16 → Icon*Medium (19 files)
- SideChatView API migration to 0.2.0-rc.2
- Add 4 new locale keys (sideChatBlockNoExitCode, codeLabel, wrapLabel, unwrapLabel)
- intercept.tsx: guard against undefined matched in SidebarProducedFiles

Verification:
- pnpm run typecheck: PASS
- pnpm run build: PASS
- npx vitest run: 1015 passed | 0 failed | 9 skipped
v1.2.0-gen2
2026-10-11 18:25:35 +08:00
figmar 0aafa94c4d chore(gen1): 第一代版本封板 — DSH 2.0.17 兼容 + settings API 迁移 + React #130 修复
- peerDeps 升级到 0.2.0-rc.2
- settings API 迁移: register() → describe() + settings/document-updated
- turnTail slots.register 补 id 字段
- EditorHost viewer.component undefined guard
- 56/56 测试全绿, typecheck/build 通过

tag: v1.2.0-gen1
v1.2.0-gen1
2026-10-11 16:52:48 +08:00
figmar df6f99c4a3 chore(peer-deps): bump to 0.2.0-rc.2 for DSH 2.0.17 compatibility
PeerDependencies changed from ^0.1.5-rc.1 to ^0.2.0-rc.2 to match
DSH Desktop 2.0.17's built-in dsh packages (Harness v0.2.0-rc.2).

Rollback anchor: git revert this commit to restore 0.1.5-rc.1 peerDeps.

Context: DSH 2.0.17 release notes state '新版内核会检查插件声明的
兼容版本,不兼容的插件可能被跳过'. This is Plan D (force-install
via peerDeps bump) — if runtime API breakage occurs, fall back to
Plan A (rebase onto upstream dsh-better-sidebar 0.25.0).
2026-10-11 14:44:51 +08:00
figmar 40a05af9ba docs(wiki): 01 需求分析改口径——对比官方原生(不再把上游操作面单列一层)
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
用户纠正:editor/git/subagent/sidechat/terminal/browser 也是 apes-sidebar
打包进来的,对外是一个完整需求整体,对比的另一端只能是 DSH 官方原生。

- 用官方包自带 README 取证:原生右列 = 容器(sidebar-right) + 唯一页「文件」
  (sidebar-files, 只读) + 唯一查看器(documentpreview, kind=text, 纯文本兜底)
- 新增官方原生做不到的九件事表,作为 user 处境的 before
- 洞察 0 改为「官方假设你只是看,而真实使用是在这儿干活」(只读→可写)
- 新增洞察 6:官方只有一个入口→罗盘不出现;入口一多罗盘才登场(单一工具→工作台)
- 附录补官方证据引文;上游/fork 的区别降为对内维护说明
2026-10-04 02:02:55 +08:00
figmar 2b88c85ea7 docs(wiki): 01-需求分析 改为「原生 vs 上游 vs 本 fork」对比式需求分析
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
把用户贴的 10 行 tab 表按供给来源拆成三层,逐行追问没有它时他会怎么办:
- DSH 原生 = 右列容器 + 「文件」页 + documentpreview 文本兜底(证据 external-plugin-guide.md)
- 上游 = 六个操作面(文件/变动/子代理/侧边对话/终端/浏览器)
- 本 fork = 三个态势面(agents/apps/profile)

新增 7 条需求变化洞察(操作面→态势面、零切换棘轮、提问副作用归零、
看得见→敢放手、心智模型外化、价值随并行度放大、增量成本≈0),
U1-U6 收敛为由分析推出的需求表;实现/根因只留附录索引。
2026-10-04 02:00:25 +08:00
figmar 2224991047 docs(wiki): 需求分析改为使用者视角重写
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
原稿按「现象/根因(含证据)/需求/验收标准」组织,实质是在讲我们怎么修的;
现在从「用户为什么会需要」写起:谁在用 → 他会怎么说 → 为什么难受 → 他想要什么
→ 怎样算满意(全部是用户能亲手点到、亲眼看到的行为)。

- 五条实现导向的需求 → U1~U6 用户问题(U5 拆成两套侧边栏 / 弹窗被切)
- 新增:不做什么(省得期待错位)、已知的暂时满足不了(应用层 feed、完整子代理树、
  沙箱态相对资源),如实交代边界
- 新增:验收总表(动作 → 应看到)与优先级/状态表
- 实现机制与提交号全部移入附录 A「根因档案」索引,指向 02/03/05/06
2026-10-04 01:55:35 +08:00
figmar 63bcb47ed0 docs(wiki): 建立项目百科(需求分析 → 架构 → 功能 → 部署 → 运维 → 排障 → 开发与测试)
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
Gitea 的 wiki 此前是空的。本次把百科的**源文件版本化进仓库**(docs/wiki/),
再用脚本发布到 wiki——文档因此与代码同源、可 review、可回溯,而不是只存在于网页上。

八页(Home 作为导航与项目速览):

  Home          项目速览 / 两条版本线的宿主兼容性差异 / 文档地图 / 谱系说明
  01-需求分析    五条核心需求(现象·根因·验收标准·状态)+ 非目标与约束 + 需求→实现→锚点
  02-系统架构    host/client 分层与产物、三层接口面、面板宿主层叠契约与 isolate 陷阱、关键不变量
  03-功能说明    面板/预览器总表、信息层三 tab 数据来源、HTML 预览交付方式、i18n 与令牌、配置面
  04-部署指南    宿主版本矩阵与选线决策、profile 接线(file: + bundles)、构建、验收清单、卸载与切换
  05-运维手册    分支/tag 策略与回滚剧本、发布纪律(版本三元组/非确定性产物)、巡检、备份铁律、诊断
  06-故障排查    8 类症状 → 根因 → 处置(含本会话实机定位过的全部案例)+ 定位手法
  07-开发与测试  工程约定、目录、门禁基线、测试分层与契约 spec、扩展点、测量式 re-base 方法

脚本 scripts/push-wiki.sh:幂等发布(POST 新建 / PATCH 覆盖),凭据走 git credential helper
(临时 netrc,600,退出即删),不落盘、不进 argv。已实测:8 页全部发布成功,复推为更新。

内容纪律:只写**本次会话实证过**的事实(提交号、行数、层级取值、产物大小、宿主版本矩阵、
静默禁用机制、re-base 步骤),未验证的部分明确标注为待验证或已知限制;并如实说明本仓库
历代承载过 v1 / v2 / 当前 fork 三代实现,本百科描述的是 main 上的 fork 线。
2026-10-04 01:19:31 +08:00
figmar 4422001c5e fix(panels): mount the panel host inside the shell's framed content viewport
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
On DSH Desktop the plugin's panel layer covered host modals — the community
market dialog (dsh-community-market, which renders into the shell's own
`shell.overlay` slot) had its lower-left corner clipped by our workbench.

The fence is not a z-index mistake on either side. The shell's framed modes do:

  const contentViewport = document.getElementById('root')
  document.body.dataset.dshDesktopMode = environment.mode
  /* …and tags #root with data-dsh-desktop-content-viewport */

with

  body:is([data-dsh-desktop-mode="compatibility"], […"extended"]) [data-dsh-desktop-content-viewport] {
    isolation: isolate;
  }

`isolation: isolate` makes #root a stacking context (z-index: auto) wrapping the
whole app INCLUDING the shell's overlay layer (`.dshDesktopOverlay`,
z-index: 1000) that hosts plugin overlays. Our host was appended to
document.body — OUTSIDE that context — and because it is a positioned element
with z-index 25 it painted above the entire context, so the market's 1000 never
competed. The shell's overlay layer is the boundary plugins are meant to sit
under, so the fix is to join the layer, not to renumber ours: panelHostParent()
mounts the host into #root when the shell framed one (body otherwise, i.e. the
plain web deployment keeps today's behaviour), where the documented 25 keeps
its meaning — above the app frame (z-index auto), below the overlay layer.

The re-anchor guard now re-appends into the resolved parent instead of always
`document.body`, and also watches `data-dsh-desktop-mode` on <body> (the same
shell effect that stamps the viewport) so a shell that frames #root after our
mount still gets the host moved.

Verified: typecheck clean; build clean; 1367 passed / 9 skipped (131 files,
+3 parenting specs). Revert is one line per call site if the shell's overlay
layer turns out to sit below our panels for a reason not visible in the CSS.
apes-v1.2.0
2026-10-04 00:08:13 +08:00
figmar 4c242acf7b fix(html-preview): deliver the document through srcdoc so a sandboxed frame can load it
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
The preview showed nothing but the bare word `forbidden` in DSH Desktop. The
cause is not this plugin's routes but the host, one layer above them:
`DesktopWebServer` wraps EVERY route with `permits()`, and in the default
isolated/扩展 mode `ordinaryBrowserEnabled` is false, so a request is served
only when it carries the Electron renderer capability
(`x-dsh-desktop-renderer`). That header is attached solely to MAIN-frame and
SAME-ORIGIN-subframe traffic (`requestComesFromRendererOrigin`), and a frame
sandboxed WITHOUT allow-same-origin is an opaque origin at request time — so
the preview document request could never be served, while every other sidebar
route (all main-frame) worked fine.

The fix keeps the sandbox boundary exactly where it was and moves the delivery:
the document is fetched by the MAIN frame — which the host does trust — and
handed to the frame through `srcdoc`. New shared component HtmlPreviewFrame
owns that path plus the sandbox tokens for BOTH surfaces (the editor viewer and
the changes tab's op render preview, which had no escape hatch at all), so the
two can no longer drift; a failed fetch falls back to handing the frame the
route URL, leaving the route's own answer visible instead of a blank pane.

`withPreviewBase` injects `<base href="<the file's route directory>">`: a
srcdoc document has no URL of its own, so without it `./style.css` would
resolve against the GUI page. A document that declares its own base is left
alone.

Two consequences, both deliberate:
- sandboxed: the frame stays opaque, so a self-contained document previews
  completely while a multi-file one shows its markup with the relative assets
  missing (those requests are opaque-origin too — inlining them is a separate
  job);
- unlocked (the warned per-surface unlock, or the setting): the srcdoc frame
  inherits the GUI origin — which is what that escape hatch already meant — and
  there relative references resolve and load like any other same-origin
  request, so multi-file previews work in that mode.

Verified: typecheck clean; build clean; 1364 passed / 9 skipped (130 files,
+13: the pure base-rewrite spec, the delivery spec, and the updated
sandbox-contract assertions, which now pin srcdoc + no route src instead of
the old route-src shape).
2026-10-03 23:15:44 +08:00
figmar e5d34ac89f merge: sync fork onto upstream v0.19.1 — one sidebar instead of two
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
Brings 5a7e213 (the v0.19.1 re-base) onto main. The merge result is deliberately
the sync branch's tree verbatim: every file both sides touched is upstream
v0.19.1 + our rename, and the v0.18.0-era content main still carries for those
files (including the two package.json.bak.* scratch backups, FreeWindow, the
hand-written openpath interception and the v0.18.0 service surface) is
superseded rather than textually merged — a hunk-level merge of a 135-commit
upstream jump would be a guess, not an integration.

Net effect on main: the fork now runs on the base that registers its content
into DSH's OWN right sidebar, so the duplicate second sidebar and its duplicate
toggle cluster are gone. The fork's own contribution is unchanged and intact:
the apes:* tabs (agents / apps / profile) in src/client/apes/, the switch.sh
two-line switcher, and the fork identity (1.2.0).

Tree verified identical to sync/v0.19.1 (git diff --quiet) before this commit.
2026-10-03 22:51:27 +08:00
figmar 5a7e2139a0 chore(sync): re-base the fork onto upstream dsh-better-sidebar v0.19.1
The fork was pinned at upstream v0.18.0. That is the entire reason the window
ran TWO sidebars and TWO near-identical panel toggles: DSH 2.0.10 ships its own
right sidebar (@deepseek-ai/dsh-client-ui-sidebar-right), and upstream only
adopted it in v0.19.0-alpha.1 — which arrived after our fork point. v0.19.1 is
also the one upstream line whose support range actually matches our host
(engines.dsh >=0.1.5-rc.1, 14 DSH peers ^0.1.5-rc.1, host = 0.1.5-rc.2), so it
is the only tag the host's startup precheck accepts.

What upstream v0.18.0 → v0.19.1 brings
- src/client/native/{index,surface,tab-adapter}.tsx: every tab the plugin can
  draw becomes an OFFICIAL native tab type + body, and an external plugin's
  descriptor registered later gets its native type too. This is what retires
  the fork's own right panel — and with it the duplicate toggle cluster
  (`toggleCluster` is gone from v0.19.1).
- native tab-chip icons via the `sidebar.right.pane.tab.title` seat, coloured
  built-in tab icons, file/folder glyphs from DSH's own `FileTypeIcon`.
- guide descriptions (DSH renders them while the guide lists <= 4 entries),
  tab bodies that fill the panel, and the 0.1.5-rc.2 adaptation work we were
  carrying by hand (peer/devDep pinning, primitives' transitive devDeps).

How the sync was done
Rebuilt the tree from the v0.19.1 tag rather than resolving 53 conflicted
files: our v0.18.0 delta was measured first and is only (a) one mechanical
rename `dsh-better-sidebar` -> `dsh-apes-sidebar` and (b) ten real edits. So:
tag tree + rename (scoped: code/config/scripts/tests/README, while the 46
historical docs/plans records and switch.sh's upstream package name stay
untouched) + re-applied deltas + our apes module.

Fork deltas re-applied on the new base
- src/client/apes/ (icons / locales / tabs): the three apes:* information-layer
  tabs (agents / apps / profile), registered through the same BetterSidebarService
  as the builtins — which is exactly why they now ride upstream's native adapter
  into the official sidebar for free.
- identity: package.json (name/version 1.2.0/description/forkedFrom), dsh.plugin.json
  (id/version), SIDEBAR_SERVICE_VERSION 1.2.0, README fork notice,
  docs/external-plugin-guide.md.
- scripts/switch.sh + the pnpm switch* scripts (apes <-> upstream two-line switch).
- tests/apes-tabs.spec.ts (unescaped: it starts from the v0.19.1 base).

Dropped
- the Phase A native-registration spike (`apes/native-tabs.tsx` + its spec): it
  re-derived by hand exactly what upstream's native layer already does — the
  hand-written bridge is dead weight now, and v0.19.1's own e2e/jsdom lanes
  cover the same seat contract far better.
- the unused apesProfileGuideDesc key and the two package.json.bak.* backups.

Verified on the new base: typecheck clean; build clean; 1351 passed / 9 skipped
(128 files), up from 1255 on the old base.
2026-10-03 22:51:09 +08:00
figmar 96cc334143 spike(A): publish apes:profile as an official right-sidebar tab type
Phase A spike of the C8 root fix: instead of only rendering in the fork's
own shell, apes:profile now also registers with DSH's OFFICIAL right
sidebar through its documented two-stage extension protocol:

  1. ctx.sidebarRightTabs.register({ id, kind, priority:'extension',
     title, guide })  — the type declaration
  2. ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register(
     { name:'sidebar.right.pane.tab', key: id }, Body))  — the keyed body

The registry is reached through ctx.inject(['sidebarRightTabs'], ...) (the
same pattern intercept.tsx uses for the async remote.session namespace), so
activation order cannot strand the bridge and a deployment without the
official sidebar stays a silent no-op — the fork's own shell is untouched.

The body reuses the existing ApesProfileView (content is shared, never
duplicated); only the shell-scope binding differs. Guide entry order 70
keeps the apes trio's position in the official entry list.

Verified: typecheck clean; build clean; 1255 passed / 9 skipped (120 files,
6 new bridge specs pinning the two-stage contract, both no-op paths, and the
HMR-safe unwind).

Local commit only — not pushed until the in-app spike is confirmed.
apes-v1.1.0-pre-sync
2026-10-03 20:42:13 +08:00
figmar 9ea9e9b9fa fix(c8): match native icon family — outline glyphs at native scale
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
The apes tab glyphs (智能体/应用/画像) were fill-only paths spanning only
10-12 of the 16 viewBox units, so they rendered visibly smaller and lighter
than the DSH native glyphs (which fill ~92-96% of the box) in the + new-tab
menu and the right-panel tile grid.

Rewrite them in this repo's own outline recipe (src/client/icons.tsx): 16x16
viewBox, artwork on 1.5-14.5, 1.5px currentColor stroke, round caps/joins.
Renamed IconAgentFill16/IconGaugeFill16/IconUserFill16 ->
IconAgentOutline16/IconAppsOutline16/IconUserOutline16.
2026-10-03 20:05:45 +08:00
figmar 2f3944a8bd fix(c8): fill-style icons — visual unity with native tabs
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
Root cause: DSH's @deepseek-ai/dsh-client-ui-primitives only ships
outline variants (mask+stroke) for agent/gauge/user icons, while native
tabs use fill:currentColor paths (solid, heavy visual weight). This made
the apes tabs look 'lighter' than the official ones in the + menu.

Fix: custom fill-style SVG icons in src/client/apes/icons.tsx
(IconAgentFill16, IconGaugeFill16, IconUserFill16) — matching the
fill:currentColor path style of DSH native tabs (IconCodeOutline16,
IconFolderOpen16, etc.).

Also: order 40/45/50 → 60/65/70 (avoid clash with terminal=40,
browser=50 in the native tab order).
2026-10-03 19:31:00 +08:00
figmar fbd183e93f fix(c8): order 60/65/70 — 不与原生 terminal(40)/browser(50) 冲突
CI / ci (push) Canceled after 0s
CI / ci-windows (push) Canceled after 0s
CI / plugin-mount (push) Canceled after 0s
2026-10-03 19:20:20 +08:00
figmar cebc21aef2 fix(c8): sidebar entry unity — DSH Icon*16 + order 40-50
Root cause of the visual split:
1. Icon system mismatch: apes used react-icons (FiCpu/FiGrid/FiUser,
   static 14px ReactNode) while native builtins use DSH's Icon*16
   components (function form, host-controlled size). Two different
   icon libraries → inconsistent visual style.
2. Order 901-903 pushed apes tabs far after native builtins (10-35),
   creating two visually separate groups in the tab strip.

Fix:
- icon: react-icons Fi* → @deepseek-ai/dsh-client-ui-primitives
  Icon*16 (function form, same as builtins):
    agents  → IconAgentPresetOutline16
    apps    → IconGaugeOutline16
    profile → IconUserOutline16
- order: 901/902/903 → 40/45/50 (immediately after native builtins)
- SIDEBAR_SERVICE_VERSION + dsh.plugin.json version → 1.1.0
  (lockstep with package.json, required by manifest tests)

Gate: typecheck ✓ / build ✓ / test 119 files · 1249 passed · 9 skipped · 0 failed ✓
2026-09-16 02:44:35 +08:00
figmar aa28c1fbc7 feat(v1.1.0): adapt to DSH 2.0.10 (0.1.5-rc.2), drop apes:tasks, DSW token styles
C1-C5 env repair:
- align all @deepseek-ai/dsh-* peer+devDeps to 0.1.5-rc.2 (DSH 2.0.10 ships it)
- fix 0.1.5-rc.2 npm packaging defect: 15 primitives runtime deps (anser,
  shiki, katex, marked, ... ) were mis-declared as devDeps in the published
  package -> add to local devDependencies so vitest can resolve them
- fix sidechat-seed contract: dsh-session@0.1.5 assertAssistantSettlementShape
  now requires a top-level data.stream: Array on assistant messages -> seed
  helper emits stream: []

C6 slim tabs:
- remove apes:tasks (overlaps the official DSH session tree in the native
  sidebar); keep apes:agents / apes:apps / apes:profile
- clean dead code: ApesTasksView + job helpers import + apes i18n dead keys
  (tasks/status/duration) and the unused menu-title keys

C7 style align:
- apes view inline colors -> DSW alias tokens (--dsw-alias-bg/border/label/
  state-success/interactive) with literal fallbacks; geometry (font/spacing/
  radius) stays inline (DSH exposes no --ds-* geometry tokens to plugin panels)

gate: typecheck ok / build ok / test 119 files · 1249 passed
2026-09-16 01:57:46 +08:00
Atlas Zeng 8753096a58 Merge pull request #628 from omdsh-dev/fix/waitfor-skip-echo-hazard
修复 main 变红的真实断言:skip 类用例的命令里含等待 needle,被交互式 shell 回显后进了 transcript,waitFor 走快路径导致 skipWait 无等待可跳。改用 bare shell + waitForShellReady。三个 check 全绿。以管理员合并:main 保护要求 1 个批准 review,仓库只有 owner 一人。
v0.19.1
2026-09-11 16:42:20 +08:00
Menghuan1918 b2e497b9c9 fix(test): keep the wait needles out of the shells the skip cases spawn
main went red on the merge commit (run 34579312780) with a real assertion:
`tests/agent-pty.spec.ts:413` expected `skipWait` to report 1 registered wait
and got 0. The cause is the test's own preparation, not the registry: an
interactive shell ECHOES what is written to it, so a terminal opened with a
command that contains the wait needle already has that needle in its
transcript by the time `waitFor` runs — `waitFor`'s documented fast path then
resolves the wait immediately and `skipWait` finds nothing to skip.

POSIX /bin/sh does not echo a command written to a non-tty, so this had been
latent for as long as the suite existed; PowerShell does echo it, which is why
it only ever failed on Windows, and why it took a Windows run to expose it
rather than a code change.

The three affected cases (skip / waiting-snapshot / wait-notification) now open
a BARE shell and wait for the shell to be up rather than for a printed marker,
via a new `waitForShellReady` helper that watches for output of any kind. That
removes both halves of the hazard: no needle in the command, and no dependency
on which words a given shell prints at startup. Nothing about what the cases
assert changed — they are about the registry's wait bookkeeping, which needs a
live terminal and no particular output.

Verified: tests/agent-pty.spec.ts 33 passed (and the three cases now finish in
milliseconds instead of waiting out a transcript poll), full suite 127 files /
1343 passed / 9 skipped.
2026-09-11 16:37:03 +08:00
Atlas Zeng 817ee8fc6d Merge pull request #626 from omdsh-dev/feat/dsh-0.1.5-rc.2
适配 DSH 0.1.5-rc.2(v0.19.1)+ CI 三类失败修复 + 文件图标与内置 Tab 图标彩色(含 #531 / #594 的成果)。三个 check 全绿;ci-windows 在单 fork 下连续五次绿(127 files / 0 errors / 0 worker 死亡)。以仓库管理员权限合并:main 的分支保护要求 1 个批准 review,仓库只有 owner 一人,无第三方可批准。
2026-09-11 16:27:56 +08:00
Menghuan1918 d725e54608 docs(plans): record the outline revert (only the terminal size was asked for) 2026-09-11 16:20:58 +08:00
Menghuan1918 da5540c9ac revert(icons): drop the glyph outlines, keep only the terminal's smaller size
The hairline outline I added to the colored tab glyphs reads as a thick stroke
in the live instance, so it goes: the glyphs stay exactly as the icon set draws
them, tinted only through the wrapper's token. What the feedback actually asked
for was the terminal SIZE, and that is all that remains — it renders a step
down (0.85x, floor 10px) because the VSCodicon terminal is a wide filled
rectangle that read heavier than its neighbours.

tests/builtins.spec.ts keeps pinning both readings (tasks = layers, terminal =
the shrunk VscTerminal with no style override), so the assertion now also
guards against an outline creeping back in.
2026-09-11 16:20:07 +08:00
Menghuan1918 0c18e492cf docs(plans): record the icon rework feedback and the green single-fork Windows runs 2026-09-11 15:59:13 +08:00
Menghuan1918 1ef2192a44 docs(plans): record the two green single-fork Windows runs 2026-09-11 15:53:06 +08:00
Menghuan1918 4fe67e3965 fix(ci): run the Windows suite in a single fork (the bounded pool was not enough)
The `--maxWorkers=2` fix from the previous commit was falsified by real CI:
run 34574847774 still ended with `[vitest-pool]: Worker forks emitted error /
Worker exited unexpectedly` while reporting 126 of 127 files passed and zero
FAIL lines. The worker that died was the one running
tests/native-surface.spec.ts — it produced NO results at all, which is the
same shape as the two earlier crashes (125/126 and 123/124: N-1 files
reported plus a silent worker exit at teardown).

So the consistent signature across all four crashes is a worker-recycling
race, not a test failure. `test:windows` now runs the whole suite in ONE fork
(`--maxWorkers=1`), which removes recycling entirely; the cost is wall-clock
(14 s -> 41 s locally, and the Windows step stays well inside its 30-minute
budget) and the signal is unchanged. Retries are still deliberately not used —
they would hide a real regression instead of removing the race.

The CI comment now records both Windows failure classes with their evidence,
so the next person can tell what this flag is for.
2026-09-11 15:43:22 +08:00
Menghuan1918 fe2017af49 fix(icons): say "background work", not "to-do list", and lighten the terminal glyph
Two glyphs read wrong in the live instance:

- Tasks shows subagent sessions AND the host's background jobs, so a checklist
  glyph described a page that does not exist. `VscLayers` (sheets stacked, i.e.
  work running behind the conversation) says what the page is; the amber token
  stays, so it still reads as live activity.
- The terminal was the widest glyph in the set and the only bare fill in a
  strip of the plugin's own 1.5px-stroked icons, which made it look like a
  heavy foreign block. It now renders a step down (0.85x the strip size, floor
  10px) and every colored tab glyph gets a hairline outline of its own ink
  (`stroke: currentColor` + `non-scaling-stroke`, so the line stays one device
  pixel across the sets' mixed 16- and 24-unit viewBoxes; `paint-order: stroke`
  centres it on the fill edge instead of fattening the shape).

tests/builtins.spec.ts pins both readings by glyph identity (tasks = layers,
terminal = the shrunk, outlined VscTerminal), so a future swap has to argue
with a failing assertion rather than with a comment.

Gates: typecheck / lint green, pnpm test 127 files / 1343 passed / 9 skipped.
2026-09-11 15:40:32 +08:00
Menghuan1918 6317dde7c0 docs(icons): record the host-artwork decision and update the rules that cite it
- docs/plans/2026-09-11-file-icons-and-tab-glyphs.md: the design record —
  what the two stacked PRs did, the measured upstream facts that made their
  colored implementation redundant (rc.2's `FileTypeIcon`/`CodeFileIcon` with
  the host's own palette, the classifier never returning a folder, the native
  tab definition having no icon field while its title slot IS the chip), the
  three merge conflicts and how each was resolved, what was deleted, the
  catch-all semantics change, and the verification evidence.
- docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md: cross-link it from the
  change table.
- docs/external-plugin-guide.md: the file-icon fallback chain now ends at the
  host's artwork, with the catch-all caveat spelled out; §12 loses its "one
  exemption" paragraph entirely — the plugin draws no color of its own any
  more (file icons are host pixels, tab glyphs ride tokens).
- AGENTS.md §5: replaces the icon-theme exemption with the two new rules
  (file-icon chain + catch-all hazard, and where the colored tab glyphs live
  plus the fact that the native chip glyph is drawn by the plugin inside the
  host's title slot).
- README.md / README_EN.md: the v0.19.1 entry gains the icon section
  (#611 / #531 / #594) with the dataset deletion and the new public API.
2026-09-11 15:32:07 +08:00
Menghuan1918 22e772ce45 feat(icons): file icons and tab glyphs in color, from the host's own artwork
Issue #611 asked for file-type icons like the official explorer's, and #429
built a colored theme for them. DSH 0.1.5-rc.2 makes most of that work
unnecessary: `@deepseek-ai/dsh-client-ui-primitives` now exports
`FileTypeIcon` / `CodeFileIcon` with the host's own 48-type full-color code
artwork plus category-colored sheet glyphs (and its classifier behind them).
So the plugin renders the host's icons instead of shipping a color table, and
color becomes the only behaviour rather than an opt-in theme.

File icons (src/client/file-icons.tsx)
- `builtinFileIcon(path, size)` → `<FileTypeIcon path size />`; folders use the
  host's folder category. The plugin's own per-extension VSCodicon map is gone.
- The public `registerFileIcon` API from #531 is unchanged and still outranks
  the built-ins, so an external plugin can take over any row. One semantic
  consequence, now documented and tested: because the host classifier covers
  every path, a registered catch-all (`exts: []`) claims rows it used to lose
  to the built-in map. An unregistered catch-all (the old icon theme's shape)
  is unaffected.
- Nothing is lazy any more: the artwork lives in a platform module the client
  already holds, so `lib/client-file-icons.js` and its 563-entry dataset (255
  kB) are deleted outright — the core bundle grew by 10 kB (0.85 → 0.86 MB).
- `src/client/file-icon-theme.ts`, the `fileIconTheme` pref (prefs-shared /
  config / client prefs), the settings-page select row and the six
  `settingsFileIconTheme*` keys in all 20 dictionaries are removed with it.

Tab glyphs (src/client/builtins/tab-icons.tsx)
- Every built-in tab type now declares a colored glyph: files (the host's
  folder artwork), changes/diff (commit), tasks (checklist), side chat
  (discussion), terminal (shell), browser (globe).
- The color comes from a `--dsw-alias-*` token through a wrapper class, never
  from a literal — the skin contract (§12) holds and `tests/theme.spec.ts`
  enforces both halves (no literals in the module, every color declaration in
  the stylesheet resolves to a token).

Native right Sidebar chips (src/client/native/tab-adapter.tsx)
- The host's tab definition has no icon field, but the
  `sidebar.right.pane.tab.title` slot IS the chip's content, so the plugin
  draws the glyph itself: an editor tab with a path shows that file's icon,
  every other tab shows its descriptor's icon — the same `descriptor.icon` the
  workbench strip and the guide capsules already render, so the three cannot
  drift. The glyph is `aria-hidden`, which keeps the chip's accessible name
  exactly the title the mount lane matches on.

Gates: typecheck / lint / check:consumer-types green; pnpm test 127 files /
1342 passed / 9 skipped (3 new chip cases, 1 rewritten built-in case).
2026-09-11 15:28:49 +08:00
Menghuan1918 a4e94c976e Merge the file-icon theme shell from #594 (feat/file-icon-theme)
Brings in the theme plumbing around #531's API — the `fileIconTheme` pref
thread, the settings row, the 20 dictionaries' keys and the theme loader's
test — so the working tree is a complete, green starting point before the
colored implementation is replaced.

Conflicts (three; resolved by taking this line's side where it had moved on):

- src/client/service.ts: the import block keeps `baseName, extOf` (the
  `isNarrowWidth` import the theme branch needed belongs to its own
  fresh-session seeding, which main has since rewritten) and the feature-list
  doc comment keeps main's "v0.19.0 REMOVED 'floatWindows'" note while
  adopting the sharper `fileIcons` wording (matched by exts / names /
  folderNames).
- tests/plugin-shape.spec.ts and tests/prefs.spec.ts: main DELETED the store
  seeding tests these files used to hold and rewrote the schema assertions
  (the `SIDEBAR_PREFS_DEFAULTS` destructuring), so this line's side wins —
  the theme branch's re-added blocks describe behaviour main no longer has.

Next commit removes `fileIconTheme` again along with the 563-entry dataset and
its lazy chunk: DSH 0.1.5-rc.2 exports the official `FileTypeIcon` artwork, so
the plugin renders the host's icons instead of shipping its own color table.
2026-09-11 15:14:47 +08:00
Menghuan1918 1d05e39be2 Merge the file-icon API from #531 (feat/file-icons-api) into the rc.2 line
Brings in the plugin-side file-icon feature (issue #611): the public
`registerFileIcon` API plus the resolver chain the tree, the editor tab and
the external-plugin surface all read.

Conflicts (three, all comment/list unions — resolved by union, verified green):

- src/client/service.ts: main's "v0.19.0 REMOVED 'floatWindows'" note stays
  (the capability is gone and must not come back); the feature list gains
  'fileIcons'.
- src/client/Sidebar.tsx: one comment above `tabIconOf`; both sides' wording
  is kept (the editor-with-path special case and the descriptor fallback).
- docs/external-plugin-guide.md: the `features` list comment keeps main's
  "additive only, the one exception being floatWindows" wording plus the new
  'fileIcons' entry.

Notes on what this merge does NOT bring in: #594 (feat/file-icon-theme) is
stacked on this branch and lands next, but its colored-theme implementation
(563-entry react-icons dataset in a 255 kB lazy chunk plus a `fileIconTheme`
switch) is being replaced — DSH 0.1.5-rc.2 exports the official
`FileTypeIcon`/`CodeFileIcon` artwork from ui-primitives, so the plugin does
not need to ship its own color dataset.

Gates on the merge result: pnpm typecheck clean, pnpm test 127 files /
1331 passed / 9 skipped.
2026-09-11 15:09:32 +08:00
Menghuan1918 d5bbb52c89 docs(plans): record the two consecutive green Windows runs (rc.2 baseline)
ci-windows is green twice on 5451f5c — the PR-triggered run 34561189320 and a
follow-up workflow_dispatch run 34561457067, both 126 files / 1318 passed /
3 skipped with zero `Unhandled Errors` and zero `Worker exited unexpectedly`
(the window that motivated this work saw two of the latter). plugin-mount is
green twice as well: `added 521 packages in 24s` with no ERESOLVE lines
against 3062 in the historical OOM run, mount smoke 7 passed, aggregate lane
passed.

Also records the falsification this PR went through — the first cut still
failed on real CI in two places (the third 5000 ms default victim,
tests/git.spec.ts:85 at 9607 ms, and `--legacy-peer-deps` breaking the CLI
install with ERR_MODULE_NOT_FOUND) — and states plainly that two green runs
do not prove the worker-death root cause, only that it stopped reproducing
once concurrency was bounded and the timeout budgets were corrected.
2026-09-11 12:18:34 +08:00
Menghuan1918 5451f5c699 fix(ci): raise the global testTimeout and drop --legacy-peer-deps (real-CI feedback)
The first run of this PR against real Windows and real npm found two things
the local macOS pass could not:

1. ci-windows lost a THIRD file to vitest's 5000 ms default —
   `tests/git.spec.ts:85` ("caps status entries at the truncation limit")
   took 9607 ms building a pathological untracked set with real git calls.
   Three different files (git / powershell / agent-pty), one root cause, so
   per-file timeouts were whack-a-mole: vitest.config.ts now sets a global
   `testTimeout: 15_000`. The two heaviest suites keep their own 30 s budgets
   and a genuinely hung case still fails.

2. `--legacy-peer-deps` made the mount lane's CLI install broken instead of
   robust: it skips exactly the peers a global install has to supply, and
   `@deepseek-ai/cordis-plugin-group` — required at boot by
   `@deepseek-ai/dsh-app-boot` — is a peer, not a dependency, so the CLI died
   with ERR_MODULE_NOT_FOUND (`Cannot find package
   '@deepseek-ai/cordis-plugin-group'`) before the smoke could start. The
   flag is removed; the pin to a fully-published rc.2 (no ETARGET) plus the
   4 GB heap (no resolver OOM) is the whole fix.

Verified locally: a plain `npm install -g @deepseek-ai/dsh@0.1.5-rc.2` into a
scratch prefix (with NODE_OPTIONS=--max-old-space-size=4096) resolves the peer
(`dsh/node_modules/@deepseek-ai/cordis-plugin-group` present), that CLI reports
0.1.5-rc.2, and `DSH_CMD` pointed at it runs the mount lane green — 7 passed.
`pnpm test` stays green with the new timeout: 126 files / 1312 passed / 9 skipped.

Docs (plan §C, AGENTS.md §2, both READMEs' v0.19.1 entries) record the rejected
middle step and why, so nobody re-adds it.
2026-09-11 12:10:16 +08:00
Menghuan1918 62c83ff611 docs(plans): record the 3080 deployment (plugin 0.19.1 on DSH rc.2) with route evidence 2026-09-11 12:03:53 +08:00
Menghuan1918 112f151c04 docs(plans): link PR #626 and record the local rc.2 mount-smoke evidence 2026-09-11 12:03:07 +08:00
Menghuan1918 787cff4319 docs(plans): add the DSH 0.1.5-rc.2 adaptation and CI-failure plan 2026-09-11 12:02:45 +08:00
Menghuan1918 e567bf6924 docs: record the rc.2 baseline (README badges, guide, AGENTS, plan)
- docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md: the adaptation plan —
  the upstream rc.1 -> rc.2 delta per affected area, why there is no
  plugin-side code change, and the CI failure taxonomy (A real Windows
  timeouts, B the two silent worker deaths, C the mount lane's npm
  ETARGET/OOM) with the evidence and the fix for each.
- AGENTS.md §2/§3: CI pins rc.2, the mount lane's npm flags and their reason
  (floating transitives + staged prerelease publishing), the Windows lane's
  bounded pool and why, and the primitives-dependencies note extended to rc.2.
- README.md / README_EN.md: support badge (v0.19.1, verified on rc.2), the
  install section's stable-release note (rc.1 hosts need no upgrade — the peer
  floor is unchanged) and a v0.19.1 changelog entry covering the two CI fixes.
- docs/external-plugin-guide.md: baseline line -> v0.19.1 / DSH 0.1.5-rc.1+,
  verified on rc.2.
- Comment-only: `DSH 0.1.5-rc.1` -> `0.1.5-rc.1+` where the note describes a
  behaviour that arrived in rc.1 and is unchanged in rc.2 (native guide
  description rendering, in src/client/native/index.ts, src/client/service.ts
  and the three specs that assert it).
2026-09-11 12:02:44 +08:00
Menghuan1918 9ffaeb2f78 fix(ci): stop the Windows lane's real timeouts and the mount lane's npm failures
Two independent, evidence-backed defects in the 2026-09-09/10 CI window
(60 completed runs: 26 success / 17 failure; ci-windows|Test red 8 times,
plugin-mount|Install dsh CLI red 4 times).

1. Windows: six of the eight red ci-windows runs were genuine 5000 ms
   vitest timeouts in specs that really start a process —
   `tests/agent-pty.spec.ts` (`:71` / `:106`; a PowerShell + ConPTY pair per
   terminal) and `tests/install-powershell.spec.ts` (`:105`; a cold
   `powershell.exe` start measured 12.1 s). vitest.config.ts sets no
   testTimeout, so both suites ran on the 5000 ms default, and
   `waitForTranscript`'s own poll budget was *also* 5000 ms — the inner wait
   consumed the whole outer budget, which is a structurally guaranteed
   timeout, not a flake. Both suites now declare a 30 s budget (describe-level
   options, verified working on vitest 4) and the inner poll drops to 15 s so
   it stays strictly below the outer bound.

2. Windows: `ci-windows`'s Test step runs `pnpm test:windows`
   (`vitest run --maxWorkers=2`, the script added with the baseline commit)
   instead of `pnpm test`. Five specs spawn real processes, node-pty's ConPTY
   helper and a cold powershell.exe are both console-bound, and an unbounded
   fork pool on a 2-core runner is the amplifier behind both the timeouts
   above and the two silent worker deaths (`Worker exited unexpectedly` with
   0 FAIL and every file reported passing — one of them on a commit whose
   sibling run was green). Local `pnpm test` semantics are unchanged; only the
   Windows lane is bounded.

   The worker-death mode itself is NOT proven fixed here: it does not
   reproduce on macOS and the two occurrences have no same-commit rerun. This
   is the evidence-backed part; the real Windows result decides whether more
   is needed, and retries are deliberately not used to paper over it.

3. plugin-mount: `npm install -g @deepseek-ai/dsh@<ver>` never really pinned
   anything — the pin's transitives are floating `^` ranges, so while upstream
   publishes a prerelease in stages (rc.2 went out package by package between
   14:43 and 14:57 UTC on 09-10) npm resolved a mixed rc.1/rc.2 peer graph:
   3062 ERESOLVE lines then `Ineffective mark-compacts near heap limit`
   (exit 134), or a hard ETARGET on a sibling that appeared 5.5 minutes (resp.
   25 seconds) later. The same mechanism hit the alpha line on 09-09. The lane
   now pins an already-complete rc.2 and passes `--legacy-peer-deps` plus
   `NODE_OPTIONS=--max-old-space-size=4096` to decouple the install from the
   upstream publish window; a mismatch would not be silent because the mount
   smoke in the same lane boots the real shell.

   The stale comment claiming rc.1 is both `latest` and `next` is corrected —
   it documented a reality that had already changed.
2026-09-11 12:02:41 +08:00
Menghuan1918 52a7e6a553 feat(dsh): adapt to DSH 0.1.5-rc.2 (baseline pin advance, v0.19.1)
The pinned baseline moves to DSH 0.1.5-rc.2 (npm `next`; `latest` is still
rc.1) while the plugin's peer floor stays ^0.1.5-rc.1, so rc.1 hosts need no
DSH upgrade to run this version. Nothing in the rc.1 -> rc.2 delta touches
this plugin: of 300 changed upstream files, the overwhelming majority are
single-line `version` bumps in per-package package.json files, and the only
real code edits are the message-feedback dialog, deliverables card CSS and
the CodeFileIcon SVG artwork split (`code-file-icon-artwork.ts`). There are
zero changes under packages/api|host|session|agent and zero type-surface
changes beyond one line in message-feedback/types.ts, so the adaptation is
the pin itself — devDependencies, the CI mount lane and
SIDEBAR_SERVICE_VERSION — with no plugin-side code change and no runtime
compatibility layer.

- package.json: devDependency pins 0.1.5-rc.1 -> 0.1.5-rc.2 (including
  dsh-client-locale, which is still published on the DSH tuple); peers keep
  the ^0.1.5-rc.1 floor; version -> 0.19.1
- dsh.plugin.json: version -> 0.19.1; engines.dsh keeps >=0.1.5-rc.1 (same
  supported range as the peer floor — the two must not fork)
- pnpm-lock.yaml: lockfile realigned to rc.2
- src/client/service.ts: SIDEBAR_SERVICE_VERSION -> 0.19.1 (tests/service.spec.ts
  asserts it equals package.json's version)

Verified locally on macOS arm64 against the rc.2 tree: pnpm install clean,
`pnpm peers check` reports no peer dependency issues, typecheck / lint /
check:consumer-types green, `pnpm test` 126 files / 1312 passed / 9 skipped.
The real-mount smoke (scripts/e2e-mount.sh with DSH_CMD pinned to rc.2) and
the aggregate double-mount lane both pass — evidence recorded in
docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md.
2026-09-11 12:02:31 +08:00
Atlas Zeng 0a2f1ac018 Merge pull request #606 from omdsh-dev/feat/wait-banner
feat(terminal): 等待 banner + 跳过等待(terminal_wait_for 增 skipped)
2026-09-11 11:28:21 +08:00
Menghuan1918 7f2674ab70 docs+test: 对齐等待 banner 的注释/设计口径并钉住 skip-wait 的 404 契约
v0.19.0 发版前 review 发现两处「文档与实现互相矛盾」(均不改运行时行为):

- use-host-feeds.ts 的 effect 注释仍写「terminal tab 类型禁用时 pushes are
  ignored」,而 mirrorAgentWaits 恰恰改成了不忽略(只跳过 tab 增删 reconcile)。
  注释改为如实描述;
- 设计文档 §1 写「轮询每圈优先检查 skipped」,实现是 aborted → exited →
  skipped。维持实现现状(pty 已死是客观事实,两种顺序下用户可见结果——banner
  消失 / skipWait 归零 / 等待结束——完全相同,只有 kind 文案不同),设计原文
  同步为「退出优先」,并在实施偏差记录写清未采纳「用户意图优先」的理由与日后
  改动点;
- src/index.ts 的 agent-pty.skip-wait 注释把「无活动等待 → 0」泛化成了
  「幂等」,收窄为「无活动等待返回 {skipped:0};未知 uuid 走 expect → 404,
  客户端 .catch 容忍并等下一次推送收敛」。

另把 skipWait 未知 uuid 的既有断言从「只校验 /not found/ 文案」升级为
「SidebarError + status 404 + code not-found」——HTTP 层 writeError 直接把
status 映射成响应码,此前这条契约没有测试守护(把 404 改成 410 时新断言即
失败,已验证)。

验证:pnpm test 126 files / 1312 passed / 0 failed,tsc --noEmit 与 eslint
均 exit 0;改动面仅 5 个文件,src/client/service.ts、builtins/ 与
external-plugin-guide.md 继续零改动。
2026-09-10 22:26:02 +08:00