@openvulcan/dsh-vulcancode 0.4.3 → 0.4.4

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 (33) hide show
  1. package/README.md +30 -5
  2. package/cordis.patch.yml +2 -0
  3. package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +52 -14
  4. package/node_modules/@openvulcan/dsh-decision-guard/package.json +2 -2
  5. package/node_modules/@openvulcan/dsh-vulcan-account/package.json +2 -2
  6. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +30 -30
  7. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.d.ts +18 -0
  8. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.js +62 -0
  9. package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +1 -1
  10. package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +1 -1
  11. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +1 -0
  12. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +1 -1
  13. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +7 -0
  14. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +11 -0
  15. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +1 -1
  16. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +1 -1
  17. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +22 -3
  18. package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +1 -1
  19. package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +1 -1
  20. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +4 -0
  21. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +1 -1
  22. package/node_modules/@openvulcan/dsh-vulcan-voice-input/LICENSE +21 -0
  23. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/client.js +1567 -0
  24. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.d.ts +39 -0
  25. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.js +187 -0
  26. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.d.ts +58 -0
  27. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.js +1 -0
  28. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.d.ts +49 -0
  29. package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.js +204 -0
  30. package/node_modules/@openvulcan/dsh-vulcan-voice-input/package.json +46 -0
  31. package/node_modules/@openvulcan/openvulcan-account/package.json +1 -1
  32. package/node_modules/@openvulcan/vulcan-decision/package.json +2 -2
  33. package/package.json +13 -11
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  **简体中文** | [English](README.en.md)
16
16
 
17
- [特色功能](#特色功能) · [架构与包结构](#架构与包结构) · [快速开始](#快速开始) · [开发](#开发)
17
+ [特色功能](#特色功能) · [界面截图](#界面截图) · [架构与包结构](#架构与包结构) · [快速开始](#快速开始) · [开发](#开发)
18
18
 
19
19
  </div>
20
20
 
@@ -33,16 +33,27 @@ dsh-vulcancode 是 DeepSeek Harness(dsh)的社区插件套件。安装一个
33
33
  - **工具调用可审查**,Vulcan Decision 在执行前使用云端或本机模型判定,并支持配置复核和人工确认。
34
34
  - **技能与经验可复用**,LuaSkills 把技能发布为 AI 可调用的工具,Markdown 长期记忆保留用户、空间和项目经验。
35
35
  - **模型可单独配置思考级别**,pi-ai 模型的可选级别和接口传输值可按供应商、按模型设置。
36
+ - **语音输入**使用 OpenVulcan 账号所属站点的免费转写服务,把口述内容插入对话草稿。
37
+
38
+ ## 界面截图
39
+
40
+ ### 暗色主题 · 工具分类卡片
41
+
42
+ ![Vulcan 暗色主题下,交互式回答以卡片展示工具分类](docs/assets/dsh-vulcan-dark.png)
43
+
44
+ ### 指挥舱主题 · 记忆工具验证
45
+
46
+ ![Vulcan 指挥舱主题下,交互式回答以统计卡片、检查清单和表格展示记忆工具验证结果](docs/assets/dsh-vulcan-command.png)
36
47
 
37
48
  ## 架构与包结构
38
49
 
39
- dsh 通过套件的 `cordis.patch.yml` 装载七个插件。自动审批调用判定引擎,使用云端判定时通过 `openvulcanAccount` 获取账号凭据。账号插件和判定引擎复用同一账号库。发布包随附这九个工作区依赖,用户只需安装套件。
50
+ dsh 通过套件的 `cordis.patch.yml` 装载八个插件。自动审批和语音输入通过 `openvulcanAccount` 获取云端服务所需的凭据。账号插件和判定引擎复用同一账号库。发布包随附工作区依赖,用户只需安装套件。
40
51
 
41
52
  ### 套件入口
42
53
 
43
54
  #### `@openvulcan/dsh-vulcancode`
44
55
 
45
- 负责声明 dsh 兼容范围和插件组。安装入口是 `dsh plugin add @openvulcan/dsh-vulcancode`,插件管理页可查看套件。源码在 [packages/dsh-vulcancode](packages/dsh-vulcancode)。
56
+ 套件通过 `dsh.bundle.patch` 声明插件组。dsh 加载各子插件的 `apply(ctx)` 入口,账号插件则导出 Cordis 服务类。安装后可在插件管理页查看套件及其组件。源码在 [packages/dsh-vulcancode](packages/dsh-vulcancode)。
46
57
 
47
58
  ### dsh 插件
48
59
 
@@ -74,6 +85,10 @@ dsh 通过套件的 `cordis.patch.yml` 装载七个插件。自动审批调用
74
85
 
75
86
  在「设置 › 通用设置」的「Vulcan 界面风格」选择跟随系统、亮色、暗色、指挥舱或 dsh 自带主题。直角开关可与任意主题搭配,覆盖 Windows 桌面内容区等界面边角。设置布局为模型目录、内置插件和 LuaSkills 等页面留出更多可用宽度。源码在 [packages/dsh-vulcan-style](packages/dsh-vulcan-style)。
76
87
 
88
+ #### `@openvulcan/dsh-vulcan-voice-input`
89
+
90
+ 登录 OpenVulcan 后,将鼠标移到对话输入框的麦克风按钮,可选择所属站点发布的免费转写模型、优先语言、麦克风和录音快捷键。可在「设置 › OpenVulcan 语音输入」测试麦克风和查看服务状态。录音快捷键支持单独绑定右 Ctrl 等左右修饰键。按住麦克风按钮或在输入框内按住所设快捷键说话,松开后转写内容会回填当前草稿。连续录满 120 秒会自动停止并转写。源码在 [packages/dsh-vulcan-voice-input](packages/dsh-vulcan-voice-input)。
91
+
77
92
  ### 共享库与命令行
78
93
 
79
94
  #### `@openvulcan/vulcan-decision`
@@ -88,8 +103,18 @@ dsh 通过套件的 `cordis.patch.yml` 装载七个插件。自动审批调用
88
103
 
89
104
  套件声明兼容 dsh `>=0.2.1-rc.2 <0.3.0`。
90
105
 
106
+ ### 桌面版
107
+
108
+ 在 dsh 侧栏打开「插件」,点击「添加插件」,在安装框中粘贴以下 npm 包名并安装。
109
+
110
+ ```text
111
+ @openvulcan/dsh-vulcancode
112
+ ```
113
+
114
+ ### 网页版
115
+
91
116
  ```sh
92
- dsh plugin add @openvulcan/dsh-vulcancode
117
+ dsh plugin --profile web add @openvulcan/dsh-vulcancode
93
118
  ```
94
119
 
95
120
  ### 使用
@@ -116,7 +141,7 @@ pnpm run test
116
141
  pnpm run pack:dsh # 生成 dist/openvulcan-dsh-vulcancode-<版本>.tgz
117
142
  ```
118
143
 
119
- 本地安装打包结果使用 `dsh plugin add ./dist/openvulcan-dsh-vulcancode-<版本>.tgz`。
144
+ 在网页版配置中安装本地包时,执行 `dsh plugin --profile web add ./dist/openvulcan-dsh-vulcancode-<版本>.tgz`。
120
145
 
121
146
  ## 致谢
122
147
 
package/cordis.patch.yml CHANGED
@@ -17,3 +17,5 @@
17
17
  name: '@openvulcan/dsh-vulcan-memory'
18
18
  - id: vulcan-reasoning
19
19
  name: '@openvulcan/dsh-vulcan-reasoning'
20
+ - id: vulcan-voice-input
21
+ name: '@openvulcan/dsh-vulcan-voice-input'
@@ -323,6 +323,7 @@ window.__ModuleLoader__.load({
323
323
  'status.auto.standalone': '独立开关方式不使用该预设,所有会话都受判定',
324
324
  'status.decision.cloud': 'OpenVulcan 云端 · {model}',
325
325
  'status.decision.cloudNoModel': 'OpenVulcan 云端 · 没有可用的模型',
326
+ 'status.decision.cloudLoadFailed': 'OpenVulcan 云端 · 无法读取判定模型',
326
327
  'status.decision.signedOut': 'OpenVulcan 云端 · 未登录,受管控的调用会询问你',
327
328
  'status.decision.local': '本机 · {pack} 运行 {model}',
328
329
  'status.decision.localMissing': '本机 · 尚未选择运行包与模型,受管控的调用会询问你',
@@ -680,6 +681,7 @@ window.__ModuleLoader__.load({
680
681
  'status.auto.standalone': 'The standalone switch does not use the preset; every session is judged',
681
682
  'status.decision.cloud': 'OpenVulcan cloud · {model}',
682
683
  'status.decision.cloudNoModel': 'OpenVulcan cloud · no model available',
684
+ 'status.decision.cloudLoadFailed': 'OpenVulcan cloud · could not load decision models',
683
685
  'status.decision.signedOut': 'OpenVulcan cloud · not signed in; governed calls ask you',
684
686
  'status.decision.local': 'This machine · {pack} runs {model}',
685
687
  'status.decision.localMissing': 'This machine · no runtime pack and model chosen; governed calls ask you',
@@ -1076,26 +1078,45 @@ window.__ModuleLoader__.load({
1076
1078
  ctx.effect(() => ctx.locale.register(i18n_1.NAMESPACE, { zh: i18n_1.zh, en: i18n_1.en }), 'vulcan-decision-guard: dictionaries');
1077
1079
  ctx.effect(() => {
1078
1080
  let accountKey;
1081
+ // A reconnect can keep the same authorization ID while replacing the backend.
1082
+ // 重连可能更换宿主,但仍保留相同的授权标识。
1083
+ let refreshCloud = true;
1084
+ /**
1085
+ * Invalidate account-scoped data and read the new Host state after reconnect or plugin changes.
1086
+ * 重连或插件变更后清除账号相关数据,并读取新的宿主状态。
1087
+ */
1079
1088
  const refreshAll = () => {
1089
+ refreshCloud = true;
1090
+ services.panel.reset();
1091
+ services.cloudModels.reset();
1092
+ services.endpoints.reset();
1080
1093
  services.autoReview.refresh();
1081
1094
  services.autoListed.refresh();
1082
1095
  services.panel.refresh();
1083
1096
  };
1084
1097
  const stops = [
1085
- ctx.remote.$on('plugin-manager/changed', () => { services.autoReview.refresh(); }),
1098
+ ctx.remote.$on('plugin-manager/changed', refreshAll),
1086
1099
  ctx.remote.$on('permission-presets/catalog-changed', () => { services.autoListed.refresh(); }),
1087
1100
  ctx.on('connection/reset', refreshAll),
1088
- // Signing in and out happens in the account plugin; the models and endpoints belong to the account.
1101
+ // Load the model list when the first signed-in account arrives, without opening any UI.
1102
+ // 首次确认账号已登录时主动加载模型目录,无需打开界面。
1089
1103
  services.panel.subscribe(() => {
1090
1104
  const account = services.panel.getSnapshot()?.account;
1091
1105
  if (account === undefined)
1092
1106
  return;
1093
- const key = account.signedIn ? account.authorizationId ?? '' : null;
1094
- if (accountKey !== undefined && key !== accountKey) {
1107
+ const key = account.signedIn ? `${account.site ?? ''}:${account.authorizationId ?? ''}` : null;
1108
+ if (!refreshCloud && key === accountKey)
1109
+ return;
1110
+ const hadAccount = accountKey !== undefined;
1111
+ accountKey = key;
1112
+ refreshCloud = false;
1113
+ services.cloudModels.reset();
1114
+ services.endpoints.reset();
1115
+ if (account.signedIn) {
1095
1116
  services.cloudModels.refresh();
1096
- services.endpoints.refresh();
1117
+ if (hadAccount)
1118
+ services.endpoints.refresh();
1097
1119
  }
1098
- accountKey = key;
1099
1120
  }),
1100
1121
  ];
1101
1122
  refreshAll();
@@ -1279,8 +1300,8 @@ window.__ModuleLoader__.load({
1279
1300
  return null;
1280
1301
  const governed = value.mode === 'standalone' || permissions?.currentValue === shared_1.AUTO_PRESET;
1281
1302
  const unattended = view?.unattended === true;
1282
- const decision = (0, settings_1.decisionStatus)(t, value, live, models?.kind === 'ready' ? models.models : undefined);
1283
- const dot = !governed ? 'off' : decision.tone === 'ok' ? 'ok' : 'warn';
1303
+ const decision = (0, settings_1.decisionStatus)(t, value, live, models);
1304
+ const dot = !governed || decision.tone === 'outline' ? 'off' : decision.tone === 'ok' ? 'ok' : 'warn';
1284
1305
  return ((0, jsx_runtime_1.jsxs)("span", { className: "vdg", children: [(0, jsx_runtime_1.jsxs)("button", { ref: anchor, type: "button", className: "vdg-trigger", "aria-haspopup": "dialog", "aria-expanded": open, title: decision.text, onClick: () => {
1285
1306
  if (!open) {
1286
1307
  services.panel.refresh();
@@ -1568,16 +1589,25 @@ window.__ModuleLoader__.load({
1568
1589
  // The sections appear in the same order in navigation and in the page.
1569
1590
  // 分区在导航和正文中按同一顺序排列。
1570
1591
  const SETTINGS_VIEWS = ['engine', 'policy', 'records'];
1571
- /** What the status section says about decisions, from the saved settings and this machine's state. */
1572
- function decisionStatus(t, value, live, models) {
1592
+ /**
1593
+ * Describe the configured backend using the latest account and model-list response.
1594
+ * 使用最新账号和模型目录响应描述已配置的判定后端。
1595
+ */
1596
+ function decisionStatus(t, value, live, list) {
1573
1597
  if (live == null)
1574
1598
  return { tone: 'outline', text: t('loading') };
1575
1599
  if (value.backend === 'cloud') {
1576
1600
  if (!live.account.signedIn)
1577
1601
  return { tone: 'warn', text: t('status.decision.signedOut') };
1578
- const model = value.cloudModel === '' ? models?.[0] : value.cloudModel;
1579
- if (model === undefined || (models !== undefined && !models.includes(model)))
1602
+ if (list === undefined)
1603
+ return { tone: 'outline', text: t('model.loading') };
1604
+ if (list.kind === 'failed')
1605
+ return { tone: 'warn', text: t('status.decision.cloudLoadFailed') };
1606
+ const model = value.cloudModel === '' ? list.models[0] : value.cloudModel;
1607
+ if (list.models.length === 0 || model === undefined)
1580
1608
  return { tone: 'warn', text: t('status.decision.cloudNoModel') };
1609
+ if (!list.models.includes(model))
1610
+ return { tone: 'warn', text: (0, i18n_1.fill)(t('model.disabled'), { model }) };
1581
1611
  return { tone: 'ok', text: (0, i18n_1.fill)(t('status.decision.cloud'), { model }) };
1582
1612
  }
1583
1613
  const pack = live.packs.find(item => item.id === value.localPack && item.installed !== null);
@@ -1590,7 +1620,7 @@ window.__ModuleLoader__.load({
1590
1620
  const heldByReview = (0, ui_1.useWatch)(services.autoReview);
1591
1621
  const listed = (0, ui_1.useWatch)(services.autoListed);
1592
1622
  const models = (0, ui_1.useWatch)(services.cloudModels);
1593
- const decision = decisionStatus(t, value, live, models?.kind === 'ready' ? models.models : undefined);
1623
+ const decision = decisionStatus(t, value, live, models);
1594
1624
  let auto;
1595
1625
  if (value.mode === 'standalone')
1596
1626
  auto = { tone: 'outline', text: 'status.auto.standalone', badge: 'tone.off' };
@@ -1600,7 +1630,7 @@ window.__ModuleLoader__.load({
1600
1630
  auto = { tone: 'ok', text: 'status.auto.provided', badge: 'tone.ok' };
1601
1631
  else
1602
1632
  auto = { tone: 'outline', text: 'status.auto.pending', badge: 'status.auto.pending' };
1603
- return ((0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('status.title'), flush: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "vdg-rows", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.decision') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [decision.text, (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: decision.tone, children: t(decision.tone === 'ok' ? 'tone.ok' : 'tone.warn') })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.auto') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [t(auto.text), (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: auto.tone, children: t(auto.badge) })] })] })] }) }));
1633
+ return ((0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('status.title'), flush: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "vdg-rows", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.decision') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [decision.text, decision.tone === 'outline' ? null : (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: decision.tone, children: t(decision.tone === 'ok' ? 'tone.ok' : 'tone.warn') })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.auto') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [t(auto.text), (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: auto.tone, children: t(auto.badge) })] })] })] }) }));
1604
1634
  }
1605
1635
  function OptionsSection({ t, value, disabled, save }) {
1606
1636
  const row = (field, prefix, values) => ((0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { children: t(`${prefix}.title`) }), (0, jsx_runtime_1.jsx)(ui_1.Segments, { label: t(`${prefix}.title`), value: value[field], options: values.map(item => ({ value: item, title: t(`${prefix}.${item}`) })), disabled: disabled, onChoose: (next) => { save({ [field]: next }); } })] }));
@@ -1824,6 +1854,14 @@ window.__ModuleLoader__.load({
1824
1854
  this.#set(undefined);
1825
1855
  });
1826
1856
  }
1857
+ /**
1858
+ * Clear the previous Host value and discard reads still in flight, keeping subscribers.
1859
+ * 清除旧宿主值并丢弃未完成的读取,同时保留订阅。
1860
+ */
1861
+ reset() {
1862
+ ++this.#generation;
1863
+ this.#set(undefined);
1864
+ }
1827
1865
  /** Stop publishing answers that are still in flight. */
1828
1866
  close() {
1829
1867
  ++this.#generation;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openvulcan/dsh-decision-guard",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Vulcan Decision permission guard for DeepSeek Harness: every tool call is judged before it runs.",
5
5
  "license": "MIT",
6
6
  "icon": "./assets/decision-guard-icon.png",
@@ -44,7 +44,7 @@
44
44
  }
45
45
  },
46
46
  "dependencies": {
47
- "@openvulcan/vulcan-decision": "0.4.3"
47
+ "@openvulcan/vulcan-decision": "0.4.4"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@deepseek-ai/cordis": "^4.0.5",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openvulcan/dsh-vulcan-account",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "OpenVulcan account for DeepSeek Harness: sign in once, used by every VulcanCode plugin.",
5
5
  "license": "MIT",
6
6
  "icon": "./assets/account-icon.png",
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@openvulcan/openvulcan-account": "0.4.3"
45
+ "@openvulcan/openvulcan-account": "0.4.4"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@deepseek-ai/cordis": "^4.0.5",