@kanadego/dsh-heartbeat 1.6.0 → 1.6.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/README.md CHANGED
@@ -117,6 +117,7 @@ node <插件目录>/dist/cli/index.js <命令>
117
117
  | 卡片分区报 `加载失败: … HTTP 405` | RPC 路由没注册成功:审计里应出现 `rpc_registered route=/api/heartbeat`,没有 = dist 未同步,重装/同步后重启 |
118
118
  | 节律配置保存不生效、重启后回默认(0.1.5 宿主) | v1.5.x 的已知问题(dsh-settings 包缺失 + 0.1.5 API 变更):升级到 v1.6.0 并重启 |
119
119
  | 投递没带「他此刻大概在:…」、审计有 `screen_vision ok:false` | 视觉识别失败:error 字段带 ModLens 具体报错;常见为 provider 未配置或网络超时。识别失败时画面/标题按隐私设计不进提示词,心跳其余功能不受影响 |
120
+ | 结果显示「心跳异常(decision: whenIdle timeout)」 | 反刍轮超时(240s 内没等到模型回话)。v1.6.0 起不再整跳报错:记 `decision_deferred`、本跳按沉默处理、素材自动留到下一跳。若反复出现,看该行 reason 与 ModLens 画面描述是否诱发跑题 |
120
121
  | 每跳 `beat_error: … reading 'length'` | 宿主移除了 `Session.events`——插件版本与宿主不匹配,对照〈版本兼容〉换版本 |
121
122
  | 每轮报 `prompt variable "{{model}}" has no value` | agent 没有模型路由(审计 `model` 字段为 `(none)`)——同样是版本不匹配症状 |
122
123
  | 素材投递偶尔不成功、`spoke_failed: non-Chinese output discarded` | 目标会话 agent 收到素材包后回合以工具调用收尾,末行是 `</tool_calls>`(纯英文)被旧判定误拦。v1.5 起已修:跳过工具收尾标签、取最后一个含中文的行;整段无中文才拒。升级后重启即可 |
@@ -139,7 +140,8 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
139
140
 
140
141
  | 插件版本 | 适配的 DSH | 备注 |
141
142
  |---|---|---|
142
- | **v1.6.0**(当前) | ≥ `0.1.2-rc.1` | 素材闭环 + 视觉识别;修复 `0.1.5` 下节律配置保存不生效(v1.5.x 在 0.1.5 上有此问题,务必升级) |
143
+ | **v1.6.1**(当前) | ≥ `0.1.2-rc.1` | v1.6.0 + 决策轮超时降级(反刍轮超时不再整跳报错,素材留到下一跳) |
144
+ | v1.6.0 | ≥ `0.1.2-rc.1` | 素材闭环 + 视觉识别;修复 `0.1.5` 下节律配置保存不生效(v1.5.x 在 0.1.5 上有此问题,务必升级) |
143
145
  | v1.5.x | ≥ `0.1.2-rc.1` | 反刍投递改版;v1.5.1 元数据补全 |
144
146
  | v1.4 | ≥ `0.1.2-rc.1` | M7 全部能力在 `0.1.5-rc.2` 验收;旧宿主上状态栏自动走 pre-step 轨道 |
145
147
  | v1.2.x – v1.3.x | ≥ `0.1.2-rc.1` | `0.1.5` 下须 ≥ v1.2.1,否则卡片 RPC 405 |
@@ -150,6 +152,10 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
150
152
 
151
153
  ## 版本更新
152
154
 
155
+ ### v1.6.1 · 2026-09-20
156
+
157
+ **补丁:反刍轮超时降级**——决策轮(反刍)在 240s 预算内没等到模型回话时,不再把整跳记成异常:改为记 `decision_deferred` 审计、best-effort 取消孤儿轮次、本跳按沉默处理,素材自动留到下一跳(与表达轮 `spoke_deferred` 同一语义)。同时在反刍提示词里钉死一条纪律:画面与窗口只用于写「他此刻大概在:」,即使画面出现想查的内容也不发起搜索、不调用工具。
158
+
153
159
  ### v1.6.0 · 2026-09-19
154
160
 
155
161
  **素材闭环**——截图→视觉识别→引擎室总结→投递→聊天→观察沉淀→consolidation 生成聊天种子→素材池→反刍投递,全链路打通:
package/dist/index.js CHANGED
@@ -1392,7 +1392,11 @@ function buildRuminationPrompt(input) {
1392
1392
  "## \u4EFB\u52A1\u680F\u7A97\u53E3(\u8F85\u52A9\u5224\u65AD)",
1393
1393
  input.screen.windows.length > 0 ? input.screen.windows.map((w) => `- ${w}`).join("\n") : "(\u65E0)",
1394
1394
  "",
1395
- 'doing = \u4F9D\u636E\u753B\u9762\u4E0E\u7A97\u53E3,\u7528\u4E00\u53E5\u8BDD\u5BA2\u89C2\u603B\u7ED3\u4ED6\u6B64\u523B\u5728\u5E72\u4EC0\u4E48(\u5982"\u6B63\u5728\u722C\u5854(\u6740\u622E\u5C16\u58542)""\u5728\u5199\u6587\u6863,\u770B\u8D77\u6765\u6709\u70B9\u5FD9");\u4E0D\u63A8\u6D4B\u60C5\u7EEA,\u4E0D\u63D0\u53CA\u672C\u63D0\u793A\u3002'
1395
+ 'doing = \u4F9D\u636E\u753B\u9762\u4E0E\u7A97\u53E3,\u7528\u4E00\u53E5\u8BDD\u5BA2\u89C2\u603B\u7ED3\u4ED6\u6B64\u523B\u5728\u5E72\u4EC0\u4E48(\u5982"\u6B63\u5728\u722C\u5854(\u6740\u622E\u5C16\u58542)""\u5728\u5199\u6587\u6863,\u770B\u8D77\u6765\u6709\u70B9\u5FD9");\u4E0D\u63A8\u6D4B\u60C5\u7EEA,\u4E0D\u63D0\u53CA\u672C\u63D0\u793A\u3002',
1396
+ // 2026-09-19: a decision turn that goes wandering over the screen
1397
+ // description (web_search loops) burns past the whenIdle budget — the
1398
+ // picture is for the doing line ONLY.
1399
+ "\u753B\u9762\u548C\u7A97\u53E3\u53EA\u7528\u4E8E\u5199 doing:\u5373\u4F7F\u753B\u9762\u91CC\u51FA\u73B0\u8BA9\u4F60\u60F3\u67E5\u7684\u4E1C\u897F,\u4E5F\u4E0D\u8981\u53D1\u8D77\u4EFB\u4F55\u641C\u7D22\u3001\u4E0D\u8981\u4F7F\u7528\u4EFB\u4F55\u5DE5\u5177,\u76F4\u63A5\u8F93\u51FA JSON\u3002"
1396
1400
  );
1397
1401
  } else {
1398
1402
  lines.push('doing = \u56FA\u5B9A\u8F93\u51FA\u7A7A\u5B57\u7B26\u4E32 ""(\u672C\u6B21\u6CA1\u6709\u753B\u9762\u4FE1\u606F,\u4E0D\u8981\u7F16\u9020\u4ED6\u5728\u5E72\u4EC0\u4E48)\u3002');
@@ -2197,7 +2201,23 @@ async function expressionPhases(bc) {
2197
2201
  max: 3,
2198
2202
  ...screen ? { screen } : {}
2199
2203
  });
2200
- const raw = await agentTurn(bc.deps, bc.agent, ruminationPrompt, "decision");
2204
+ const raw = await (async () => {
2205
+ try {
2206
+ return await agentTurn(bc.deps, bc.agent, ruminationPrompt, "decision");
2207
+ } catch (e) {
2208
+ try {
2209
+ bc.agent.cancel?.();
2210
+ } catch {
2211
+ }
2212
+ appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
2213
+ event: "decision_deferred",
2214
+ reason: String(e).slice(0, 160)
2215
+ });
2216
+ noteBeat("silent", { reason: "\u51B3\u7B56\u8F6E\u8D85\u65F6\uFF0C\u7D20\u6750\u7559\u5230\u4E0B\u4E00\u8DF3" });
2217
+ return null;
2218
+ }
2219
+ })();
2220
+ if (raw === null) return;
2201
2221
  let parsed;
2202
2222
  try {
2203
2223
  parsed = parseJsonBlock(raw);