dsh-armory 2.4.2 → 2.4.3

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.en.md CHANGED
@@ -115,6 +115,7 @@ Experts (WorkBuddy expert center, 429 experts / 15 industries; category filter +
115
115
  - Skills belong to their respective authors. Both upstreams are third-party interfaces whose fields may change; the panel carries a persistent note saying so
116
116
  - **Experts (direct, 1.9.0)** — a dedicated board tab (the sidebar entry is renamed **Skills · MCP · Experts**):
117
117
  - **Directory**: reads the official WorkBuddy expert center distribution (`expert_center.json`, 429 experts / 15 industries, ETag-cached under `~/.dsh/wb-market/experts/`) with category filter, keyword search, and a detail dialog (persona, bundled skills, sample prompts) before you summon
118
+ - **Where the persona comes from (2.4.3)**: detail and summon resolve the persona in this order — local override, local WorkBuddy cache (`~/.workbuddy/plugins/cache/experts/<plugin>/<version>/agents/`), public distribution URL, then a stand-in. Roughly four in ten experts listed upstream (185 of 429, e.g. "Skill Smith" / skill-smith) have no publicly distributed persona file at all; if your WorkBuddy has used one, its cache is read directly, and when all three proper sources are empty a **stand-in persona** is built from the public description, tags and sample prompts of the expert center, with its first paragraph stating that it is not the original text. Every expert stays viewable and summonable (issue #5).
118
119
  - **Summoning** = appending one independent **agent preset** row to the profile patch (a `@deepseek-ai/dsh-agent-preset` entry that mirrors the built-in `standard` preset, with only the persona prefix swapped for the expert's full persona). New sessions then offer **WorkBuddy Expert · …** and that expert takes over the session, while the standard tools / planning / compaction / delegation stay intact. Multiple experts coexist; writes go through the existing patch pipeline (file lock + version check + atomic write), preserving other rows, comments and `!!js` expressions
119
120
  - **Bundled skills**: tick to install them too — a full local WorkBuddy cache (`~/.workbuddy/plugins/cache/experts/`) is copied wholesale into `~/.agents/skills/` (prefixed `wbexpert__`, discovered by the skill registry immediately); without a cache it falls back to the CDN's single `SKILL.md` and labels the source honestly
120
121
  - **Team experts (summonable since 1.9.4)**: WorkBuddy's 53 "expert teams" assume `TeamCreate` and `Agent(name=…)`, neither of which DSH has. Summoning now also writes one file per member persona under `~/.dsh/wb-market/experts/teams/<expert>/`, and appends a **runtime adaptation** block to the lead's persona: creating the team becomes a no-op (the roster is fixed at summon time); dispatching a member becomes `subagent` (after `read`-ing that member's persona into the prompt), with sequential SOPs passing `run_in_background: false` so the lead waits for the result; follow-ups use `send_message`. The original rules — never ghost-write a member's output, never connect members directly, everything relayed through the lead — are preserved verbatim. Member personas stay **out of the system prompt** (otherwise every turn would pay for all five), and are read only when dispatching
@@ -236,7 +237,10 @@ The detail pane lists the tools a server currently registers; expand one for its
236
237
  That server is disabled, so it registers no tools. Enable it in the panel.
237
238
 
238
239
  **I upgraded but the skill detail is still blank, or the floating button behaves as before.**
239
- Check two things. First, that the new package actually landed: after `dsh plugin --profile web update dsh-armory`, look at the version at the bottom of the panel. Second, **quit DSH completely and start it again** — the market field mapping lives in host-side modules (`lib/wb-market.js`, `lib/index.js`) that are loaded once at process start, so reloading the panel only refetches the UI bundle. If one specific skill still has no summary, post its slug in an issue and that response gets checked on its own.
240
+ Check two things. First, that the new package actually landed: after `dsh plugin --profile web update dsh-armory`, the first panel you open shows a **"vX.Y.Z update"** card whose title is the version currently installed — or read the `version` field of `~/.dsh/profiles/<profile>/node_modules/dsh-armory/package.json`. Second, **quit DSH completely and start it again** — the market field mapping lives in host-side modules (`lib/wb-market.js`, `lib/index.js`) that are loaded once at process start, so reloading the panel only refetches the UI bundle. If one specific skill still has no summary, post its slug in an issue and that response gets checked on its own.
241
+
242
+ **Opening an expert, the detail says this persona is not the original text?**
243
+ That is the stand-in, not an error. About four in ten experts in the upstream expert center (185 of 429, "Skill Smith" among them) only have a directory entry — there is no persona file at the public distribution URL — and when the local WorkBuddy cache does not have it either, the Armory builds a stand-in from the public description, tags and sample prompts, so the expert **stays viewable and summonable**; it only reproduces "who this roughly is and what they own", not the original working method. To use the original, save it as `~/.dsh/wb-market/experts/overrides/<plugin>.md` (e.g. `skill-smith.md`); the next detail view uses it automatically and labels the source "local override" — delete the file to fall back to cache, distribution or the stand-in. The first paragraph of the stand-in also gives the exact path on this machine.
240
244
 
241
245
  **How do I upgrade? Why did the panel not notify me?**
242
246
  GitHub tag-pinned installs never cross versions — re-add with the new tag (`github:2026heshao/dsh-armory#v1.9.0`). The banner only queries this repo's GitHub Releases, at most once a day, is dismissible, and never acts on its own.
package/README.md CHANGED
@@ -106,6 +106,7 @@ dsh plugin --profile web add github:2026heshao/dsh-armory
106
106
  - 技能版权归原作者所有;两个均为第三方接口,字段可能随上游改版变化(面板顶部有常驻说明)
107
107
  - **专家直连(1.9.0 新增,体验对齐 WorkBuddy 专家中心)**:看板独立分类「专家」(侧栏入口同步更名「技能 · MCP · 专家」)——
108
108
  - **目录**:直读 WorkBuddy 专家中心官方分发(`expert_center.json`,429 位专家 / 15 个行业分类,ETag 缓存在 `~/.dsh/wb-market/experts/`),分类筛选 + 关键词搜索 + 详情弹层(召唤前看人设正文、捆绑技能、试试这样问)
109
+ - **人设来源(2.4.3)**:详情与召唤按「本机覆盖 → 本机 WorkBuddy 缓存(`~/.workbuddy/plugins/cache/experts/<包>/<版本>/agents/`)→ 公开分发地址 → 兜底人设」取数。上游目录里约四成专家(429 位中的 185 位,例如「技能匠人」skill-smith)其实没有公开分发人设文件;本机 WorkBuddy 用过它就直接读缓存,三条正路都没有时会用专家中心公开的介绍、标签与示例问题生成一份**兜底人设**,正文第一段就写明「这份人设不是原文」,保证任何一位专家都能看详情、能召唤(issue #5)。
109
110
  - **召唤** = 往 profile 补丁写入一条**独立 Agent 预设**(`@deepseek-ai/dsh-agent-preset` 行,plugins 复刻内置 standard 预设、仅 persona 的 prefix 换成专家人设全文)——新建会话时选择「WorkBuddy 专家 · …」即专家接管该会话,标准工具/规划/压缩/委派原样保留;多个专家可共存,写入走既有 patch 写链(文件锁 + 版本校验 + 原子写),其余条目、注释与 `!!js` 表达式原样保留
110
111
  - **捆绑技能**:召唤时可勾选一并安装——本机 WorkBuddy 缓存(`~/.workbuddy/plugins/cache/experts/`)有完整技能树就整目录复制到 `~/.agents/skills/`(`wbexpert__` 前缀,即刻被技能注册表发现);无缓存时按 CDN 的 SKILL.md 单文件兜底并如实标注来源
111
112
  - **团队型专家(1.9.4 可召唤)**:WorkBuddy 的 53 个「专家团」人设原本依赖 `TeamCreate` / `Agent(name=…)` 两个 DSH 没有的工具。召唤时除了写主理人预设,还会把每位成员的人设各自落成一份文件(`~/.dsh/wb-market/experts/teams/<专家>/`),并在主理人人设末尾追加一段**运行时适配**:建团队 → 无需调用(编制已在召唤时固定);派活 → `subagent`(先把该成员人设 `read` 出来拼进 prompt),顺序 SOP 传 `run_in_background: false` 当场等结果;续聊用 `send_message`。原人设的铁律(不得代写成员产出、成员之间不直连、一切经主理人中转)原样保留。成员人设**不进系统提示**(否则每轮都要为 5 份人设白付上下文费),只在调度时按需读取
@@ -249,7 +250,10 @@ dsh plugin --profile web remove dsh-armory
249
250
  它们把两件独立的事分开说:**连接在不在** —— `已连接 · 当前无会话使用(连接保留,下次直接用)`(最后一个会话结束了,连接按空闲策略留着)/ `已连接 · N 个会话在用 · M 工具` / `连接中…` / `连接失败` / `尚未建立连接(新会话自动挂载)`;**几个会话在用** —— 就是那句里的 `N 个会话在用`。旧版本的「待会话挂载」正是把这两件事压成一句,所以容易被读成"还没连上"。
250
251
 
251
252
  **升级后技能详情页还是空的,或者悬浮按钮的行为没变?**
252
- 两步确认。先确认新包真装上去了:`dsh plugin --profile web update dsh-armory` 之后看面板底部的版本号。再**完全退出 DSH 重新启动**——市场的字段映射在宿主侧模块(`lib/wb-market.js`、`lib/index.js`)里,只在进程启动时加载一次,面板里刷新只会重取界面 JS,宿主那份映射仍是旧的。若只有某个具体技能的介绍仍然缺,欢迎把它的 slug 发到 issue 里,按那条响应单独对一次。
253
+ 两步确认。先确认新包真装上去了:`dsh plugin --profile web update dsh-armory` 之后,升级后第一次打开面板会弹出一张「vX.Y.Z 的更新」卡片,标题里的版本号就是当前安装的版本;也可以直接看本机 `~/.dsh/profiles/<profile>/node_modules/dsh-armory/package.json` 的 `version`。再**完全退出 DSH 重新启动**——市场的字段映射在宿主侧模块(`lib/wb-market.js`、`lib/index.js`)里,只在进程启动时加载一次,面板里刷新只会重取界面 JS,宿主那份映射仍是旧的。若只有某个具体技能的介绍仍然缺,欢迎把它的 slug 发到 issue 里,按那条响应单独对一次。
254
+
255
+ **点开专家,详情里写着「这份人设不是原文」?**
256
+ 这是兜底人设,不是错误。上游专家中心里约四成专家(429 位中的 185 位,「技能匠人」就在其中)只挂了目录条目,公开分发地址上没有它们的人设文件;本机 WorkBuddy 缓存里也没有时,军械库会用专家中心公开的介绍、标签与示例问题生成一份兜底人设,**保证这位专家仍能看详情、能召唤**,只是工作方法只能还原到「大致是谁、负责什么」。想要原文,把它存成 `~/.dsh/wb-market/experts/overrides/<包名>.md`(例如 `skill-smith.md`)即可,下次打开详情会自动改用原文,来源标注「本机覆盖」;删掉文件就回到缓存 / 分发 / 兜底。正文第一段也会给出这台机器上的确切路径。
253
257
 
254
258
  **怎么升级?为什么面板没提示?**
255
259
  从 npm 装的直接 `dsh plugin --profile web update dsh-armory` 就会升小版本,也可以直接点面板提示条上的「升级」(同一件事,免去敲命令)。面板的提示条只查 GitHub Releases、每天最多一次、可关闭、不会自己动;固定 tag 安装的用户不会自动跨版本,需要用新 tag 重新 `add`(见 [安装与升级](docs/installation.md))。
@@ -284,14 +288,14 @@ dsh plugin --profile web remove dsh-armory
284
288
 
285
289
  ```sh
286
290
  npm install --no-save yaml@2 eslint@9 # 测试与风格检查的唯一裸依赖(运行依赖全部由宿主提供)
287
- npm test # 全 54 套件(与 CI / 发版门槛同一条)
291
+ npm test # 全 55 套件(与 CI / 发版门槛同一条)
288
292
  npm run lint # ESLint:no-undef / no-unused-vars / no-empty 等
289
293
  npm run test:e2e # 可选:用宿主补丁算法验证专家预设进入组合树
290
294
  dsh --profile web --dump-config # 看 profile 的有效配置
291
295
  dsh web # 起 Web 做真实操作验证
292
296
  ```
293
297
 
294
- `npm test` 跑的是 `test/run-all.mjs`,依次执行 **54** 个独立套件(各自是独立 node 进程,任一非零退出即整体失败)。套件是显式白名单而非 `node --test` 自动发现——`test/` 下还有带副作用的迁移脚本与需要宿主模块的端到端脚本,自动发现会误跑。
298
+ `npm test` 跑的是 `test/run-all.mjs`,依次执行 **55** 个独立套件(各自是独立 node 进程,任一非零退出即整体失败)。套件是显式白名单而非 `node --test` 自动发现——`test/` 下还有带副作用的迁移脚本与需要宿主模块的端到端脚本,自动发现会误跑。
295
299
 
296
300
  | 分组 | 套件 | 覆盖 |
297
301
  | --- | --- | --- |
@@ -302,7 +306,7 @@ dsh web # 起 Web 做真实操作验证
302
306
  | SkillHub 修正(2.4.1) | `skillhub-detail-fix` `skillhub-ux-fix` | 详情字段回退链与目录 / 详情映射(fetch 全替身,逐条对上 issue #4 的响应形状);`skillhubHomepage` 域名改写与缺字段兜底;合并时空值不得覆盖、`0`/`false` 必须保留;`browsableHomepageUrl` 拦住接口域名;悬浮按钮订阅 / 看板激活自动收起 / 右键与长按菜单 / 唯一入口置灰 / 卸载清理(hooks 替身真渲染) |
303
307
  | 自定义套件(2.2.0) | `suites-registry` `suites-client` | 整集合覆盖 / 幂等 / 拒绝与截断两态;id 归属契约(保存后不分裂)、引用套件展开为多个 `/name`、三级写通道降级 |
304
308
  | 技能集合(2.2.0) | `skill-remove` `skill-refresh` | 卸载来源白名单与路径边界(根自身 / `..` 穿越 / 跨来源根 / 符号链接只删链接);装 / 卸后角标与看板实时刷新、乱序回包守卫 |
305
- | 专家与团队 | `run-expert-manual` `session-persona` `team-bridge` `expert-card-actions` `recent-sort-regression` | 专家市场写链(真网目录 + 人设 + patch 往返,33 条断言);会话级挂/换/摘;团队桥接;卡片按钮真值表;「最近使用」排序 |
309
+ | 专家与团队 | `run-expert-manual` `session-persona` `team-bridge` `expert-card-actions` `recent-sort-regression` `expert-persona-source` | 专家市场写链(真网目录 + 人设 + patch 往返,33 条断言);会话级挂/换/摘;团队桥接;卡片按钮真值表;「最近使用」排序;人设取数链(覆盖优先且零人设请求、本机缓存命中不联网、CDN 兜底、404 / 网络故障 / 空缓存都落到标注过的兜底人设、包名不能穿越目录、召唤前先取人设) |
306
310
  | 升级与安装真机 | `upgrade-plan` `verify-install-info` `verify-host-installinfo` `teardown-residue` | 一键升级判据(本地安装 / 来源未知一律不写);真 profile 数据下的来源判定(profile 不存在自行 SKIP);teardown 残留诊断 |
307
311
  | 市场与安装链(2.0.0) | `wb-registry` `wb-install-state` `wb-auth-rules` `wb-connector-meta` `wb-awesome-market` `wb-install-flow` `auth-gate-regression` `install-toast` | 多市场注册表原子写(只剩连接器源,旧 plugins 条目读侧丢弃/写侧拒绝);安装状态与来源锁;令牌槽扫描与注入规则;连接器 R1/R2/R3 投影;awesome 过滤/瘦身/304/双源切换与更新探测五枚举;令牌替换写链;装前 401 鉴权闸;顶部安装浮层跨卸载可见 |
308
312
  | 社区信号(2.3.0) | `community-meta` | GitHub 仓库写法解析、单次最多 8 条、6 小时缓存命中不联网、refresh + ETag 的 304 复用、404 文案、网络失败 stale 回落、403 限流跨调用生效、原子写(fetch 全替身 + mkdtemp 隔离) |
@@ -66,6 +66,9 @@ function skillsRoot() {
66
66
  return join(homedir(), ".agents", "skills");
67
67
  }
68
68
  function workbuddyExpertCache() {
69
+ // 测试隔离口(同 DSH_ARMORY_MARKET_HOME 的纪律):设置时本机 WorkBuddy 缓存根指到临时目录。
70
+ const override = process.env.DSH_ARMORY_WORKBUDDY_CACHE;
71
+ if (override) return override;
69
72
  return join(homedir(), ".workbuddy", "plugins", "cache", "experts");
70
73
  }
71
74
  // 团队专家:主理人 + N 个成员,每人一份人设。成员人设不塞进 preset(会白白撑大每轮
@@ -335,6 +338,94 @@ async function localExpertPluginJson(plugin) {
335
338
  return null;
336
339
  }
337
340
 
341
+ // 主理人人设的本机来源:WorkBuddy 自己下载过的专家包里就有 agents/<name>.md。
342
+ // 上游只把一部分专家放在公开分发地址上(2026-10-27 实测 428 个里 185 个 404),
343
+ // 所以本机缓存不是「优化」,而是这些专家唯一能拿到正文的途径。
344
+ async function localExpertPersona(plugin, agentName) {
345
+ const names = [agentName, plugin].map((value) => safeSegment(value || "")).filter(Boolean);
346
+ for (const version of await localExpertVersions(plugin)) {
347
+ const dir = join(version.dir, "agents");
348
+ for (const name of new Set(names)) {
349
+ try {
350
+ const text = await readFile(join(dir, `${name}.md`), "utf8");
351
+ if (text.trim()) return text;
352
+ } catch { /* 换个名字或换下一个版本 */ }
353
+ }
354
+ // 人设文件名与 plugin / agentName 都不一致时,接受 agents/ 下唯一那份 md。
355
+ try {
356
+ const files = (await readdir(dir)).filter((file) => /\.md$/i.test(file));
357
+ if (files.length === 1) {
358
+ const text = await readFile(join(dir, files[0]), "utf8");
359
+ if (text.trim()) return text;
360
+ }
361
+ } catch { /* 该版本没有 agents/ */ }
362
+ }
363
+ return null;
364
+ }
365
+
366
+ // 本机覆盖:用户自备的人设正文,优先级高于缓存与 CDN。上游 185/429 没有公开分发、
367
+ // 本机也没有缓存时,这是唯一能拿到「原文」的途径。文件名走 safeSegment,不能借 plugin 穿越目录。
368
+ function overridePersonaPath(plugin) {
369
+ return join(cacheDir(), "overrides", `${safeSegment(plugin)}.md`);
370
+ }
371
+ async function overridePersona(plugin) {
372
+ try {
373
+ const text = await readFile(overridePersonaPath(plugin), "utf8");
374
+ if (text.trim()) return text;
375
+ } catch { /* 绝大多数专家没有覆盖文件 */ }
376
+ return null;
377
+ }
378
+
379
+ /** 原文取不到的原因:上游缺包与网络故障说法不同,用户据此决定换专家还是重试。 */
380
+ function describePersonaFailure(error) {
381
+ const detail = String(error?.message || error);
382
+ if (/HTTP 404/.test(detail)) return "上游 404:专家中心目录里挂着它,但公开分发地址上没有这个包";
383
+ return `拉取原文失败:${detail}`;
384
+ }
385
+
386
+ /**
387
+ * 兜底人设:三条正路都拿不到原文时,用专家中心公开的介绍 / 专业 / 标签 / 示例问题拼一份替身。
388
+ * 必须显式标注非原文——召唤出来的会话会照这份文字行事,用户有权知道它不是原作者设定;
389
+ * 拿到原文后放进本机覆盖目录即自动改用原文(覆盖优先级最高)。
390
+ */
391
+ function generatedPersona(row, reason) {
392
+ const name = row.name || row.plugin;
393
+ const profession = row.profession || "该领域的专家";
394
+ const description = row.description || "(专家中心没有为它提供介绍文字)";
395
+ const category = row.categoryName || row.categoryId || "";
396
+ const tags = Array.isArray(row.tags) ? row.tags.filter(Boolean) : [];
397
+ const prompts = Array.isArray(row.quickPrompts) ? row.quickPrompts.filter(Boolean) : [];
398
+ const overridePath = overridePersonaPath(row.plugin).replace(/\\/g, "/");
399
+ const lines = [
400
+ `# ${name}(${profession})`,
401
+ "",
402
+ `> **这份人设不是原文。**${reason};本机 WorkBuddy 缓存里也没有它。`,
403
+ "> 下面的内容由 WorkBuddy 专家中心公开的介绍、标签与示例问题整理而成,",
404
+ "> 只能还原「大致是谁、负责什么」,不等同于原作者写的工作方法与铁律。",
405
+ `> 拿到原始人设后,放到 \`${overridePath}\` 即可自动改用原文。`,
406
+ "",
407
+ "## 身份",
408
+ `你是「${name}」,身份是「${profession}」${category ? `,属于「${category}」` : ""}。`,
409
+ "",
410
+ "## 你负责什么",
411
+ description,
412
+ "",
413
+ "## 工作原则",
414
+ "- 只做上面这段职责范围内的事;被问到范围外的内容时说明边界并给出替代方向,不冒充全才。",
415
+ "- 这不是原作者人设:不要声称掌握原作者未公开的资料、模板或内部流程,拿不准就直说。",
416
+ "- 先确认目标、已有输入与验收标准,再动手;信息不足时先问最关键的那一个。",
417
+ "- 给结论时附上依据与可执行的下一步,不写空泛的鼓励语。",
418
+ ];
419
+ if (tags.length) lines.push(`- 额外关注:${tags.join("、")}。`);
420
+ if (prompts.length) lines.push("", "## 用户常这样开始", ...prompts.map((prompt) => `- ${prompt}`));
421
+ lines.push(
422
+ "",
423
+ "## 开场",
424
+ "第一次对话先用一句话说明自己是谁、能做什么,然后按上面的原则推进;不要主动展开与任务无关的话题。",
425
+ );
426
+ return `${lines.join("\n")}\n`;
427
+ }
428
+
338
429
  // ── 专家详情:人设正文 + 捆绑技能清单 ──────────────────────────────────
339
430
  // 召唤前给用户看的全部真实内容:人设 MD 原文、plugin.json 的技能声明、quickPrompts。
340
431
  export async function wbExpertDetail(payload) {
@@ -356,13 +447,35 @@ export async function wbExpertDetail(payload) {
356
447
  if (!row.agentName && pluginJson?.agentName) row.agentName = String(pluginJson.agentName);
357
448
  const promptFile = String(row.promptFile || (pluginJson ? `/plugins/${plugin}/agents/${pluginJson.agentName || plugin}.md` : "")).trim();
358
449
  if (!promptFile) throw new Error(`专家 ${plugin} 没有人设文件路径(promptFile 缺失)`);
359
- let persona = "";
360
- try {
361
- persona = await fetchText(EXPERT_ORIGIN + promptFile);
362
- } catch (error) {
363
- throw new Error(`拉取人设失败:${error.message}`);
450
+ // 取数顺序:本机覆盖 → 本机 WorkBuddy 缓存 → 公开分发地址 → 兜底生成。
451
+ // 上游 185/429 没有公开分发人设(issue #5),所以「三条路都空」是常态而非异常:
452
+ // 这时给一份明确标注非原文的替身,保证专家至少能看详情、能召唤,不再抛 404。
453
+ let persona = (await overridePersona(plugin)) || "";
454
+ let personaSource = persona ? "override" : "";
455
+ let personaNotice = "";
456
+ if (!persona) {
457
+ persona = (await localExpertPersona(plugin, row.agentName || pluginJson?.agentName)) || "";
458
+ if (persona) personaSource = "workbuddy-cache";
459
+ }
460
+ if (!persona) {
461
+ try {
462
+ const fetched = await fetchText(EXPERT_ORIGIN + promptFile);
463
+ if (fetched.trim()) {
464
+ persona = fetched;
465
+ personaSource = "cdn";
466
+ } else {
467
+ personaNotice = "公开分发地址上的人设文件是空的";
468
+ }
469
+ } catch (error) {
470
+ personaNotice = describePersonaFailure(error);
471
+ }
472
+ }
473
+ if (!persona) {
474
+ persona = generatedPersona(row, personaNotice);
475
+ personaSource = "generated";
476
+ personaNotice = personaNotice || "原文不可用";
364
477
  }
365
- if (!persona.trim()) throw new Error("人设文件为空");
478
+ if (!persona.trim()) throw new Error("人设正文为空");
366
479
  const skillNames = Array.isArray(pluginJson?.skills)
367
480
  ? pluginJson.skills.map((entry) => basename(String(entry).replace(/\/+$/, ""))).filter(Boolean)
368
481
  : [];
@@ -374,6 +487,8 @@ export async function wbExpertDetail(payload) {
374
487
  promptFile,
375
488
  persona,
376
489
  personaChars: persona.length,
490
+ personaSource,
491
+ personaNotice,
377
492
  version: String(pluginJson?.version || ""),
378
493
  skills,
379
494
  avatarPluginUrl: pluginJson?.avatar ? `${EXPERT_ORIGIN}/plugins/${encodeURIComponent(plugin)}/${String(pluginJson.avatar).replace(/^\//, "")}` : row.avatarUrl,
@@ -458,6 +458,24 @@ export const WHATS_NEW_EN = {
458
458
  where: 'README -> FAQ',
459
459
  },
460
460
  ],
461
+ '2.4.3': [
462
+ {
463
+ text: '**Summoning an expert is no longer blocked by an upstream 404**: the persona now comes from "local WorkBuddy cache first, public distribution URL second". Roughly four in ten experts in the upstream directory (185 of 429) have no publicly distributed persona file at all, which used to fail with "failed to fetch persona" every time; now, as long as your local WorkBuddy has used that expert, its detail opens and it can be summoned (issue #5)',
464
+ where: 'Experts -> Detail / Summon',
465
+ },
466
+ {
467
+ text: '**An expert whose original persona is unavailable is still always usable**: when all three proper sources (local override, local WorkBuddy cache, public distribution) are empty, a **stand-in persona** is built from the public description, tags and sample prompts of the expert center. Its first paragraph states that this is not the original text and gives the path where the original belongs, so the expert can be viewed and summoned normally instead of turning into a dead 404. 185 of the 429 upstream experts fall into this case (issue #5)',
468
+ where: 'Experts -> Detail / Summon',
469
+ },
470
+ {
471
+ text: '**New local override**: `~/.dsh/wb-market/experts/overrides/<plugin>.md` has the highest priority — drop the original persona there (copied from another machine, exported from WorkBuddy, or written yourself) and it is used automatically, with the detail source labelled "local override"; delete the file to fall back to cache, distribution or the stand-in. The plugin name is sanitised, so no name can climb out of the directory (issue #5)',
472
+ where: 'Experts -> Detail',
473
+ },
474
+ {
475
+ text: '**Two README updates (Chinese and English)**: the experts section now states the resolution order (local override, WorkBuddy cache, public distribution, stand-in) and how the stand-in is labelled, and the FAQ gains "what does it mean when the detail says this is not the original text", with the path to restore the original; the FAQ correction left over from the previous release is in the npm package this time too',
476
+ where: 'README -> Experts / FAQ',
477
+ },
478
+ ],
461
479
  };
462
480
 
463
481
  /**
package/lib/whats-new.js CHANGED
@@ -529,4 +529,25 @@ export const WHATS_NEW = [
529
529
  },
530
530
  ],
531
531
  },
532
+ {
533
+ version: '2.4.3',
534
+ items: [
535
+ {
536
+ text: '**召唤专家不再被上游 404 挡住**:人设改为「本机 WorkBuddy 缓存 → 公开分发地址」取数。上游目录里约四成专家(429 位中的 185 位)其实没有公开分发人设文件,以前一律报「拉取人设失败」,现在只要本机 WorkBuddy 用过这位专家就能正常看详情、正常召唤(issue #5)',
537
+ where: '专家 → 详情 / 召唤',
538
+ },
539
+ {
540
+ text: '**拿不到原文的专家也一定能用**:三条正路(本机覆盖 / 本机 WorkBuddy 缓存 / 公开分发)都空时,用专家中心公开的介绍、标签与示例问题生成一份**兜底人设**,正文第一段就写明「这份人设不是原文」,并附上原作者人设的存放路径;召唤照常可用,不再弹 404 把专家变成摆设。上游 429 位里有 185 位属于这种情况(issue #5)',
541
+ where: '专家 → 详情 / 召唤',
542
+ },
543
+ {
544
+ text: '**新增本机覆盖**:`~/.dsh/wb-market/experts/overrides/<包名>.md` 优先级最高——拿到原始人设(从别的机器、从 WorkBuddy 导出或自己写的)放进去就自动改用原文,详情来源标注「本机覆盖」;删掉文件即回到缓存 / 分发 / 兜底。包名经过净化,不存在借名字穿越目录(issue #5)',
545
+ where: '专家 → 详情',
546
+ },
547
+ {
548
+ text: '**README(中 / 英)补两处**:专家一节写明「本机覆盖 → WorkBuddy 缓存 → 公开分发 → 兜底人设」的取数顺序与兜底人设的标注方式,常见问题新增「详情里写着『这份人设不是原文』怎么办」并给出换回原文的路径;上一版遗留的那条 FAQ 校正也一并进了 npm 包',
549
+ where: 'README → 专家 / 常见问题',
550
+ },
551
+ ],
552
+ },
532
553
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-armory",
3
- "version": "2.4.2",
3
+ "version": "2.4.3",
4
4
  "description": "Unified Skill + MCP + Expert + Plugin board for DeepSeek Harness (desktop app and Web GUI) — custom skill suites, skill install/uninstall with live refresh, global panel page, JSON import, WorkBuddy connector & SkillHub & Expert & DSH community plugin market installs, MCP OAuth authorization, tool-usage tailor with wire-tax trends, and profile-backed Host Remote management.",
5
5
  "repository": {
6
6
  "type": "git",