@kanadego/dsh-heartbeat 1.9.1 → 1.9.2

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 (36) hide show
  1. package/README.md +27 -5
  2. package/cordis.patch.yml +18 -39
  3. package/dist/{chunk-BQCXYE7N.js → chunk-3MDBU6WY.js} +2 -2
  4. package/dist/{chunk-HT57FOEF.js → chunk-AJ5HLAL7.js} +34 -11
  5. package/dist/chunk-AJ5HLAL7.js.map +1 -0
  6. package/dist/{chunk-OYVNWB5G.js → chunk-CUOSICYJ.js} +2 -2
  7. package/dist/{chunk-WRJCINZ7.js → chunk-IG6YMUHW.js} +4 -4
  8. package/dist/{chunk-KOOQOMQY.js → chunk-JDH2LDFS.js} +2 -2
  9. package/dist/chunk-K5Y6JP2B.js +399 -0
  10. package/dist/chunk-K5Y6JP2B.js.map +1 -0
  11. package/dist/{chunk-QOQ7EPZZ.js → chunk-KB5SMG3F.js} +3 -3
  12. package/dist/{chunk-2NB4QILF.js → chunk-XFNCTOW7.js} +2 -2
  13. package/dist/cli/index.js +9 -9
  14. package/dist/{inbox-JLT5G7TL.js → inbox-P26DDMWC.js} +3 -3
  15. package/dist/index.d.ts +33 -0
  16. package/dist/index.js +104 -20
  17. package/dist/index.js.map +1 -1
  18. package/dist/{migrate-WDVFHUP7.js → migrate-D4VKAKYT.js} +3 -3
  19. package/dist/{orchestrator-2ZLU7RGE.js → orchestrator-EUTZ3KG6.js} +10 -8
  20. package/dist/{pool-B34NTH6D.js → pool-UTXQJJ5C.js} +3 -3
  21. package/dist/{snapshot-JQDJ346Z.js → snapshot-K5VHETTK.js} +4 -4
  22. package/package.json +83 -83
  23. package/dist/chunk-7TW6DD6Q.js +0 -208
  24. package/dist/chunk-7TW6DD6Q.js.map +0 -1
  25. package/dist/chunk-HT57FOEF.js.map +0 -1
  26. /package/dist/{chunk-BQCXYE7N.js.map → chunk-3MDBU6WY.js.map} +0 -0
  27. /package/dist/{chunk-OYVNWB5G.js.map → chunk-CUOSICYJ.js.map} +0 -0
  28. /package/dist/{chunk-WRJCINZ7.js.map → chunk-IG6YMUHW.js.map} +0 -0
  29. /package/dist/{chunk-KOOQOMQY.js.map → chunk-JDH2LDFS.js.map} +0 -0
  30. /package/dist/{chunk-QOQ7EPZZ.js.map → chunk-KB5SMG3F.js.map} +0 -0
  31. /package/dist/{chunk-2NB4QILF.js.map → chunk-XFNCTOW7.js.map} +0 -0
  32. /package/dist/{inbox-JLT5G7TL.js.map → inbox-P26DDMWC.js.map} +0 -0
  33. /package/dist/{migrate-WDVFHUP7.js.map → migrate-D4VKAKYT.js.map} +0 -0
  34. /package/dist/{orchestrator-2ZLU7RGE.js.map → orchestrator-EUTZ3KG6.js.map} +0 -0
  35. /package/dist/{pool-B34NTH6D.js.map → pool-UTXQJJ5C.js.map} +0 -0
  36. /package/dist/{snapshot-JQDJ346Z.js.map → snapshot-K5VHETTK.js.map} +0 -0
package/README.md CHANGED
@@ -97,7 +97,9 @@ dsh plugin --profile web add file:D:/path/to/dsh-heartbeat
97
97
 
98
98
  重启 DSH 即生效——插件自动创建专用心跳会话并按节律运行,之后日常无需任何操作。
99
99
 
100
- **心跳预设是自动装的**:`0.1.7` 起的宿主不再从文件系统目录加载预设,插件改为在安装清单里**声明式注册**(与官方 standard 预设同机制),重启即注册,不需要任何手动操作。更早的宿主仍走原来的目录安装路径:启动时检查 `<DSH_HOME>/.agent-presets/heartbeat/`,缺失就按包内模板建好,**已有文件永不覆写**(你手改过的预设原样保留)。无论走哪条路径,没有这个预设心跳 agent 就是个"裸 agent",连 `web_search` 都看不见——想手动检查/修复用 `preset status` / `preset install`(见下节);在走目录安装的老宿主上,还可以把插件 config 的 `installPreset` 设为 `false` 关掉自装。
100
+ **心跳预设是自动装的**:插件加载时直接向宿主注册(`agentPresets.register()`,v1.9.2 起),不需要任何手动操作,**安装清单里也不再声明宿主包**。早先那种"声明式"写法要求 profile 自己能解析 `@deepseek-ai/dsh-agent-preset`:解析不到,整个插件树加载失败(0.1.5 宿主);手工把它的 peer 闭包补进 profile,又会把 cordis 的装载机制装成两份,之后**任何会话都无法恢复**(`prompt section "deployment:persona-prefix" is already registered`,0.2.0-rc.2)——两种都踩过,见 issue #2。`0.1.7` 之前的宿主仍走目录安装路径:启动时检查 `<DSH_HOME>/.agent-presets/heartbeat/`,缺失就按包内模板建好,**已有文件永不覆写**(你手改过的预设原样保留)。无论走哪条路径,没有这个预设心跳 agent 就是个"裸 agent",连 `web_search` 都看不见——想手动检查/修复用 `preset status` / `preset install`(见下节);把插件 config 的 `installPreset` 设为 `false` 可同时关掉运行时注册与目录自装,预设完全由你管。
101
+
102
+ **数据默认放在包外**:`<DSH_HOME>/heartbeat-data`(没有 `DSH_HOME` 就是 `~/.dsh/heartbeat-data`),重装与升级都不会碰它。老版本装在包目录里的 `data/` 会在首次启动时自动搬过去(搬迁前先备份、逐文件校验通过才删原件,见下节「卸载」)。要换位置就用插件 config 的 `dataDir`,或环境变量 `HEARTBEAT_DATA_DIR`。
101
103
 
102
104
  ## CLI 命令
103
105
 
@@ -192,15 +194,28 @@ node <插件目录>/dist/cli/index.js <命令>
192
194
  dsh plugin --profile web remove @kanadego/dsh-heartbeat
193
195
  ```
194
196
 
195
- 卸载只拆插件本体(包、挂载行、注册项,无残留引用);**`data/` 用户数据默认保留在插件目录**,重装后记忆原样回来。想连数据一起删,手动删除 `data/` 目录或先用 `burn --all` 焚毁敏感内容。
197
+ 卸载只拆插件本体(包、挂载行、注册项,无残留引用),**不动用户数据**——v1.9.2 起数据默认住在包外的 `<DSH_HOME>/heartbeat-data`(`~/.dsh/heartbeat-data`),重装、升级、卸载都不会碰它。想连数据一起删,先 `burn --all` 焚毁敏感内容再删目录。
198
+
199
+ 老版本(≤ v1.9.1)装在包目录里的 `data/` 会被自动搬出去:启动时若发现包内有数据、包外还没有,就递归复制过去——**先备份一份、再逐文件校验**(文件清单、大小、sha256 全部一致),全部通过才删原目录与备份;任何一步失败都原地不动,继续用包内那份,并留痕 `data_dir_relocate_failed`。搬完之后老位置不会再被读取,也不会再创建。
200
+
201
+ 如果想自己指定位置(比如放到别的盘),在 profile 的 `cordis.patch.yml` 里给 `heartbeat` 覆盖:
202
+
203
+ ```yaml
204
+ - id: heartbeat
205
+ config:
206
+ dataDir: D:/your/workspace/heartbeat-data
207
+ ```
208
+
209
+ 显式指定时**不会**触发自动搬迁,也不会被上面的默认值覆盖——从包内搬出去这件事需要你手动做(照第 1 条的路径拷过去即可)。
196
210
 
197
211
  ## 版本兼容
198
212
 
199
213
  | 插件版本 | 适配的 DSH |
200
214
  |---|---|
201
- | **v1.7.4 – v1.9.1**(当前) | `0.1.2-rc.1` – `0.2.0-rc.2` |
202
- | v1.7.2 – v1.7.3 | `0.1.2-rc.1` – `0.2.0-rc.1` |
203
- | v1.7.0 – v1.7.1 | `0.1.2-rc.1` – `0.1.7-rc.2` |
215
+ | **v1.9.2**(当前) | `0.2.0-rc.1` – `0.2.0-rc.2` |
216
+ | v1.7.4 – v1.9.1 | `0.2.0-rc.1` – `0.2.0-rc.2` |
217
+ | v1.7.2 – v1.7.3 | `0.1.7-rc.1` – `0.2.0-rc.1` |
218
+ | v1.7.0 – v1.7.1 | `0.1.5-rc.2` – `0.1.7-rc.2` |
204
219
  | v1.2.x – v1.6.4 | `0.1.2-rc.1` – `0.1.5-rc.2` |
205
220
  | v1.1 | `0.1.2-rc.1` |
206
221
  | v1.0 | ≤ `0.1.1-rc.2` |
@@ -214,6 +229,13 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
214
229
  <details open>
215
230
  <summary><b>v1.9.x</b></summary>
216
231
 
232
+ ### v1.9.2 · 2026-10-10(修复:预设改为运行时注册 + 数据目录移出包外)
233
+
234
+ - **预设不再写进安装清单**:修复了心跳模式预设在 profile 解析不到宿主包时,导致整个插件树加载失败、DSH 启动不起来的问题。现已改为插件在加载时自己向宿主注册,超时或失败会自动回退并留痕。
235
+ - **补依赖不再把宿主装成两份**:修复了为解析预设包手工补齐依赖后,宿主装载机制出现两份实例、所有会话都无法恢复的问题。现在清单里不再出现任何宿主包,那条补法也用不上了。
236
+ - **会话恢复失败不再无限重试**:修复了旧格式会话迁移被拒时,引擎室被反复重试拖住、永远空转的问题。现已把这类永久性失败单独识别,当场重建会话并把正身指过去;真正暂时性的错误仍照旧退避重试。
237
+ - **数据目录默认移出包外**:修复了数据默认住在包目录里、`dsh plugin add` 与每次升级替换包目录时连带删掉画像、素材池与正身记录的问题(原本只有显式 `burn` 才会销毁)。默认位置现为 `<DSH_HOME>/heartbeat-data`;老版本装在包内的 `data/` 首次启动自动搬迁——先备份、再逐文件校验(清单、大小、sha256 全部一致),全部通过才删原件与备份,任何一步失败都原地不动。
238
+
217
239
  ### v1.9.1 · 2026-10-07(代码审查:86 项修复)
218
240
 
219
241
  进行了一次全量代码审查,共 86 项。没有功能上的加减,优化了代码结构,清理并修复了一批 bug 与潜在问题:
package/cordis.patch.yml CHANGED
@@ -8,46 +8,25 @@
8
8
  # - id: heartbeat
9
9
  # config:
10
10
  # dataDir: D:/your/workspace/data
11
- # Default: <packageRoot>/data.
11
+ # Default: <DSH_HOME>/heartbeat-data (i.e. ~/.dsh/heartbeat-data), outside the
12
+ # package. Data written by v1.9.1 and earlier sits at <packageRoot>/data and is
13
+ # migrated out automatically on first start (backup, verify, then delete).
12
14
  - insert:
13
15
  - id: heartbeat
14
16
  name: '@kanadego/dsh-heartbeat'
15
17
 
16
- # ── Agent preset heartbeat(0.1.7 声明式预设,v1.7.1)─────────────────
17
- # 0.1.7 起 agentPresets 名册不再挂载文件系统用户根(~/.dsh/.agent-presets),
18
- # 预设改为组合里的 @deepseek-ai/dsh-agent-preset 声明行(与官方 preset-standard
19
- # 同款机制)。内容 = assets/presets/heartbeat/agent.cordis.yml 的移植:
20
- # compaction 组 + 唯一模型工具 tool-web(fetch:false)。
21
- # 刻意没有 persona 行:部署人格(soul card)+ 心跳提示词已够,绝不能让
22
- # "You are a coding agent" 盖掉人格。≤0.1.5 宿主继续走文件系统预设自装。
23
- - insert:
24
- - id: preset-heartbeat
25
- name: '@deepseek-ai/dsh-agent-preset'
26
- config:
27
- id: heartbeat
28
- name: 心跳模式
29
- description: 心跳 agent 专用:仅 web_search + 上下文折叠;无 shell、无文件、无子代理。
30
- order: 20
31
- plugins:
32
- - id: compaction
33
- name: cordis:group
34
- group: true
35
- isolate:
36
- compaction: true
37
- toolResultPruner: true
38
- config:
39
- - id: compaction-basic
40
- name: '@deepseek-ai/dsh-compaction-basic'
41
- - id: command-compact
42
- name: '@deepseek-ai/dsh-command-compact'
43
- - id: tool-result-pruner
44
- name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
45
- config:
46
- thresholdChars: 8192
47
- headChars: 4096
48
- tailChars: 1024
49
- - id: tool-web
50
- name: '@deepseek-ai/dsh-tool-web'
51
- config:
52
- fetch: false
53
- searchTimeoutMs: 60000
18
+ # ── Agent preset heartbeat(改为插件运行时注册,v1.9.2 / issue #2)────────
19
+ # 这里**故意不再**声明 `@deepseek-ai/dsh-agent-preset` 行。那行会让 profile
20
+ # 必须自己能解析该包,而它的 peer 闭包里含着 cordis 自己的装载机制
21
+ # (cordis-plugin-loader / -include / -group):
22
+ # · profile 解析不到 → 整个插件树加载失败(0.1.5-rc.2 实测);
23
+ # · 手工补进 peer 闭包 → 装载机制双实例,之后**任何会话 resume 全部失败**
24
+ # (`prompt section "deployment:persona-prefix" is already registered`,
25
+ # 0.2.0-rc.2 实测)。
26
+ # 两种都在 2026-10-10 被复现(见 issue #2)。现在预设定义活在插件里
27
+ # (src/core/preset-definition.ts),加载时经 `agentPresets.register()` 运行时
28
+ # 注册——declaring plugin 自己提供 definition 正是该 API 的用途;宿主没有这个
29
+ # API 时只记一条审计,绝不让宿主挂掉。老宿主(仍扫名册文件系统用户根)继续由
30
+ # preset-install 自装维持。内容与 assets/presets/heartbeat/agent.cordis.yml 一致:
31
+ # compaction 组 + 唯一模型工具 tool-web(fetch:false);刻意没有 persona 行——
32
+ # 部署人格(soul card)+ 心跳提示词已够,绝不能让 "You are a coding agent" 盖掉人格。
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  loadEncryptedText,
6
6
  saveEncryptedText
7
- } from "./chunk-7TW6DD6Q.js";
7
+ } from "./chunk-K5Y6JP2B.js";
8
8
 
9
9
  // src/profile/inbox.ts
10
10
  import fs from "fs";
@@ -110,4 +110,4 @@ export {
110
110
  inboxHealthCheck,
111
111
  inboxFileExists
112
112
  };
113
- //# sourceMappingURL=chunk-BQCXYE7N.js.map
113
+ //# sourceMappingURL=chunk-3MDBU6WY.js.map
@@ -20,7 +20,7 @@ import {
20
20
  sendNewMessageHint,
21
21
  sendWeeklyReadyHint,
22
22
  weeklyDue
23
- } from "./chunk-WRJCINZ7.js";
23
+ } from "./chunk-IG6YMUHW.js";
24
24
  import {
25
25
  getRuntime
26
26
  } from "./chunk-3QJJRXIR.js";
@@ -31,17 +31,17 @@ import {
31
31
  loadPool,
32
32
  seedsFilePath,
33
33
  surfaceSeed
34
- } from "./chunk-KOOQOMQY.js";
34
+ } from "./chunk-JDH2LDFS.js";
35
35
  import {
36
36
  dedupeItems,
37
37
  inboxClear,
38
38
  inboxCount,
39
39
  inboxDrain,
40
40
  inboxFilePath
41
- } from "./chunk-BQCXYE7N.js";
41
+ } from "./chunk-3MDBU6WY.js";
42
42
  import {
43
43
  snapshotIfDue
44
- } from "./chunk-QOQ7EPZZ.js";
44
+ } from "./chunk-KB5SMG3F.js";
45
45
  import {
46
46
  appendAuditLine,
47
47
  applyOpsToDoc,
@@ -53,7 +53,7 @@ import {
53
53
  readAuditLines,
54
54
  readJournalTexts,
55
55
  runDeterministicAging
56
- } from "./chunk-OYVNWB5G.js";
56
+ } from "./chunk-CUOSICYJ.js";
57
57
  import {
58
58
  decryptFile,
59
59
  encryptFile,
@@ -63,7 +63,7 @@ import {
63
63
  saveEncryptedText,
64
64
  saveJson,
65
65
  writeText
66
- } from "./chunk-7TW6DD6Q.js";
66
+ } from "./chunk-K5Y6JP2B.js";
67
67
  import {
68
68
  atomicWriteJsonSync,
69
69
  burnFileSync
@@ -990,7 +990,7 @@ async function runConsolidation(guard, paths, policy, llm, now = Date.now()) {
990
990
  const report = applyOpsToDoc(guard, paths.dataDir, doc, profileOpsCast, schema, policy, now);
991
991
  const aged = runDeterministicAging(doc, policy, now);
992
992
  persistWithJournal(guard, paths.dataDir, doc, { runId, applied: report.applied, rejected: report.rejected });
993
- const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-B34NTH6D.js");
993
+ const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-UTXQJJ5C.js");
994
994
  const seedAudit = (entry) => {
995
995
  try {
996
996
  appendAuditLine(paths.dataDir + "/logs/heartbeat.jsonl", entry);
@@ -1277,6 +1277,13 @@ function applyHeartbeatInterval(deps, intervalMin) {
1277
1277
  reschedule?.(v);
1278
1278
  appendAuditLine(deps.paths.logsDir + "/heartbeat.jsonl", { event: "interval_changed", intervalMin: v });
1279
1279
  }
1280
+ function looksPermanentResumeFailure(error) {
1281
+ return [
1282
+ /SessionFormatUnsupportedError/i,
1283
+ /refuses the transformed artifact/i,
1284
+ /session migration[^]*refus/i
1285
+ ].some((re) => re.test(error));
1286
+ }
1280
1287
  function sessionEvents(session, fromSeq) {
1281
1288
  if (!session) return [];
1282
1289
  if (typeof session.snapshotEvents === "function") {
@@ -1398,6 +1405,11 @@ function makeHomeSetup(deps) {
1398
1405
  return async (agentCtx) => {
1399
1406
  const notes = [];
1400
1407
  const presetId = deps.agentPreset ?? "heartbeat";
1408
+ try {
1409
+ await withTimeout(deps.presetRegistration?.() ?? Promise.resolve(), 3e4, "preset registration timeout (30s)");
1410
+ } catch (e) {
1411
+ notes.push(`preset-register=threw(${String(e).slice(0, 120)})`);
1412
+ }
1401
1413
  try {
1402
1414
  const presets = agentCtx.get("agentPresets");
1403
1415
  if (typeof presets?.mount !== "function") {
@@ -1539,12 +1551,15 @@ async function ensureAgent(deps) {
1539
1551
  agent = unwrap(handle);
1540
1552
  appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "agent_resume_ok", sessionId: savedId, model: agent.options?.model ?? "(none)" });
1541
1553
  } catch (resumeErr) {
1554
+ const resumeMessage = String(resumeErr);
1542
1555
  appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
1543
1556
  event: "agent_resume_failed",
1544
1557
  sessionId: savedId,
1545
- error: String(resumeErr).slice(0, 160)
1558
+ error: resumeMessage.slice(0, 160)
1546
1559
  });
1547
- if (!/not found/i.test(String(resumeErr))) {
1560
+ const gone = /not found/i.test(resumeMessage);
1561
+ const permanent = !gone && looksPermanentResumeFailure(resumeMessage);
1562
+ if (!gone && !permanent) {
1548
1563
  appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
1549
1564
  event: "agent_deferred",
1550
1565
  sessionId: savedId,
@@ -1553,6 +1568,13 @@ async function ensureAgent(deps) {
1553
1568
  agentPromise = null;
1554
1569
  return null;
1555
1570
  }
1571
+ if (permanent) {
1572
+ appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
1573
+ event: "agent_resume_permanent",
1574
+ sessionId: savedId,
1575
+ error: resumeMessage.slice(0, 160)
1576
+ });
1577
+ }
1556
1578
  try {
1557
1579
  const handle = await withTimeout(Promise.resolve(ctx.agents.create({ sessionId: savedId, meta: { cwd: paths.dataDir }, ...agentOptions ? { agentOptions } : {}, setup })), 3e4, "agents.create (self-heal) timeout");
1558
1580
  agent = unwrap(handle);
@@ -1760,7 +1782,7 @@ async function observeBoundSessions(bc) {
1760
1782
  const { deps, now } = bc;
1761
1783
  const { guard, paths } = deps;
1762
1784
  const { loadBindings, observeTargets } = await import("./bindings-225SI7KC.js");
1763
- const { inboxAppend, inboxFilePath: inboxFilePath2 } = await import("./inbox-JLT5G7TL.js");
1785
+ const { inboxAppend, inboxFilePath: inboxFilePath2 } = await import("./inbox-P26DDMWC.js");
1764
1786
  const data = loadBindings(guard, paths.settingsDir);
1765
1787
  const targets = observeTargets(data);
1766
1788
  if (targets.length === 0) return;
@@ -2428,6 +2450,7 @@ export {
2428
2450
  StatusReader,
2429
2451
  buildDigest,
2430
2452
  applyHeartbeatInterval,
2453
+ looksPermanentResumeFailure,
2431
2454
  sessionEvents,
2432
2455
  sessionEventCount,
2433
2456
  HOME_ROTATE_EVENT_COUNT,
@@ -2451,4 +2474,4 @@ export {
2451
2474
  beat,
2452
2475
  startOrchestrator
2453
2476
  };
2454
- //# sourceMappingURL=chunk-HT57FOEF.js.map
2477
+ //# sourceMappingURL=chunk-AJ5HLAL7.js.map