dsh-sidecard-ask 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.1
4
+
5
+ **主题:让"两半代际不一致"成为一个被处理的状态,而不是噪声。**
6
+
7
+ 这个插件的宿主半只在**重启 DSH** 时更新,客户端半在**页面加载**时更新,所以"新客户端 + 旧宿主"是结构性常态
8
+ (当前本机就是这个状态:页面已跑到 1.3.x,宿主仍是重启前的 1.2.0)。
9
+
10
+ - 客户端在自检上报被宿主以 `not-found`(该路由还不存在,旧宿主)、`bad-response`(SPA 兜底返回 HTML)
11
+ 或 `unreachable` 拒绝时,**记住"此宿主不支持自检"并停止重试**,只打一条 info 说明"重启 DSH 后生效";
12
+ 其它错误(例如配置类真实错误)仍按 warning 记录,不会错误地关闭通道。
13
+ - 新增纯函数 `isDiagnosticsUnsupported(error)` 并纳入契约测试(5 条断言覆盖各错误码)。
14
+
15
+ 自测 322 → **327 项**(verify 53 / contract 140 / smoke 134)。
16
+
3
17
  ## 1.3.0
4
18
 
5
19
  **主题:把"客户端半的状态"变成宿主可读——补上导致前两次缺陷排查困难的观测盲点。**
package/README.md CHANGED
@@ -396,7 +396,7 @@ node test/smoke-test.mjs # 端到端:流式/截断/取消/持久化/失
396
396
  ```
397
397
 
398
398
  三个脚本都以 `process.exitCode` 反映结果,失败会列出具体条目;测试会把 `DSH_HOME` 指向临时目录,不会污染真实配置。
399
- 当前规模:verify 53 项 + contract 135 项 + smoke 134 项 = **322 项全部通过**。
399
+ 当前规模:verify 53 项 + contract 140 项 + smoke 134 项 = **327 项全部通过**。
400
400
 
401
401
  ### 10.2 版本能力探测(§7 矩阵的来源)
402
402
 
@@ -447,6 +447,10 @@ $tmp = Join-Path $env:TEMP 'probe.json'
447
447
  `native.reason` / `better.reason` 会写明**为什么**某个承载面不可用(例如 `no-tab-meta` 或注册表的报错原文)——
448
448
  1.2.1 修掉的那个 tab kind 冲突正是靠这类信息才定位到的。
449
449
 
450
+ > **两半代际不一致是常态**:宿主半只在重启 DSH 时更新,客户端半在页面加载时更新。
451
+ > 所以"新客户端 + 旧宿主"很常见——此时 `/diagnose` 还不存在,客户端会**识别这一点、只提示一次并停止重试**
452
+ > (`not-found` / SPA 兜底 HTML / 不可达三类失败),不会每次加载都刷警告。
453
+
450
454
  **2026-09-28 在 DSH 0.1.7-rc.2 上的实测结果**(`provider=spawn`、`sideTools=readonly`):
451
455
  `start x1 → reasoning x181 → delta x90 → status x1 → done x1`,耗时 2.6 s,`done.text` 为真实模型答案。
452
456
  这条记录同时说明:子代理启动、只读工具白名单(与真实工具表求交后)、流式帧桥接、SSE 线协议、运行结束后的 `dispose()` 都是**在真实宿主上跑通的**,
package/client.js CHANGED
@@ -47,7 +47,7 @@ window.__ModuleLoader__.load({
47
47
  * the Host and the module's `api.version` both read it, so a report can
48
48
  * never claim a generation the browser is not actually running.
49
49
  */
50
- const CLIENT_VERSION = '1.3.0'
50
+ const CLIENT_VERSION = '1.3.1'
51
51
 
52
52
  /**
53
53
  * Tab kind served by the DSH native right rail (also its implementation id).
@@ -948,6 +948,23 @@ window.__ModuleLoader__.load({
948
948
  */
949
949
  let capturedComposerActions = null
950
950
 
951
+ /**
952
+ * Whether `POST /diagnose` is worth calling on this host generation.
953
+ *
954
+ * The two halves update independently — the Host half only on a DSH
955
+ * restart, the Client half on a page load — so a page can easily run a
956
+ * NEWER Client against an older Host that has no `/diagnose` route. The
957
+ * first refusal turns the channel off for this page instead of retrying
958
+ * (and warning) on every report.
959
+ *
960
+ * @param {unknown} error - the thrown wire error.
961
+ * @returns {boolean} true when the failure means "this host lacks the route".
962
+ */
963
+ function isDiagnosticsUnsupported(error) {
964
+ const code = error?.code
965
+ return code === 'not-found' || code === 'bad-response' || code === 'unreachable'
966
+ }
967
+
951
968
  /**
952
969
  * What this Client half wants the Host to know about its own state.
953
970
  *
@@ -2164,14 +2181,23 @@ window.__ModuleLoader__.load({
2164
2181
 
2165
2182
  /**
2166
2183
  * Debounced self-report to the Host. Diagnostics are best-effort: a
2167
- * failure is logged and never surfaces as a user-visible error.
2184
+ * failure is logged and never surfaces as a user-visible error, and a
2185
+ * host too old to have the route is remembered so the channel is not
2186
+ * retried for the rest of the page's life.
2168
2187
  */
2169
2188
  let reportTimer = null
2189
+ let diagnosticsSupported = true
2170
2190
  const scheduleReport = (delay = 600) => {
2191
+ if (!diagnosticsSupported) return
2171
2192
  if (reportTimer !== null) clearTimeout(reportTimer)
2172
2193
  reportTimer = setTimeout(() => {
2173
2194
  reportTimer = null
2174
2195
  void postJson('diagnose', buildClientReport(store.state)).catch((error) => {
2196
+ if (isDiagnosticsUnsupported(error)) {
2197
+ diagnosticsSupported = false
2198
+ console.info(`[${PLUGIN_ID}] 宿主半尚不支持自检上报(宿主半比客户端旧,重启 DSH 后生效)`)
2199
+ return
2200
+ }
2175
2201
  console.warn(`[${PLUGIN_ID}] 自检上报失败(不影响功能):`, error?.message ?? error)
2176
2202
  })
2177
2203
  }, delay)
@@ -2456,6 +2482,7 @@ window.__ModuleLoader__.load({
2456
2482
  composeMainPrompt,
2457
2483
  askInMainConversation,
2458
2484
  buildClientReport,
2485
+ isDiagnosticsUnsupported,
2459
2486
  renderRichText,
2460
2487
  pickSurface,
2461
2488
  dict: DICT,
package/index.js CHANGED
@@ -42,7 +42,7 @@ export const name = 'dsh-sidecard-ask'
42
42
  export const inject = ['webServer']
43
43
 
44
44
  /** Version of this plugin (kept in step with package.json by test/verify.mjs). */
45
- export const PLUGIN_VERSION = '1.3.0'
45
+ export const PLUGIN_VERSION = '1.3.1'
46
46
 
47
47
  /** Route prefix of the plugin's own API. */
48
48
  export const ROUTE_PREFIX = '/sidecard-ask/api'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-sidecard-ask",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "DSH 划词追问(侧边卡片作答):在聊天区与任务区选中文本就地追问,答案由独立子代理在侧边卡片里流式呈现,也可选择落回主对话。",
5
5
  "type": "module",
6
6
  "main": "index.js",