@dshtrading/knowledge 0.4.1 → 0.6.0

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.
@@ -0,0 +1,13 @@
1
+ import { KnowledgeCard } from "./types.js";
2
+ //#region src/authors.d.ts
3
+ declare const AUTHOR_ALIASES: Readonly<Record<string, string>>;
4
+ /** 把作者名归一到规范名;未登记的写法原样返回(仅去除首尾空白)。 */
5
+ declare function canonicalAuthor(author: string): string;
6
+ /**
7
+ * 返回作者已归一的卡片副本;已是规范名时原样返回同一引用。
8
+ * 非破坏性:不修改传入卡片。缺 `source`/`author` 的畸形条目按原样放行——文件读取
9
+ * 层不为历史脏数据抛错,结构校验只在入库(`validateKnowledgeCard`)发生。
10
+ */
11
+ declare function canonicalizeCardAuthor(card: KnowledgeCard): KnowledgeCard;
12
+ //#endregion
13
+ export { AUTHOR_ALIASES, canonicalAuthor, canonicalizeCardAuthor };
package/lib/authors.js ADDED
@@ -0,0 +1,35 @@
1
+ //#region src/authors.ts
2
+ const AUTHOR_ALIASES = Object.freeze({
3
+ "鳄鱼派": "鳄鱼派(像鳄鱼一样思考)",
4
+ "鳄鱼派(公众号:像鳄鱼一样思考)": "鳄鱼派(像鳄鱼一样思考)",
5
+ "像鳄鱼一样思考(鳄鱼派)": "鳄鱼派(像鳄鱼一样思考)",
6
+ "zcl × DSH Master(自有分析,非外部素材)": "zcl × DSH Master(自有分析)",
7
+ "zcl × DSH Master(自有框架综合,非外部素材)": "zcl × DSH Master(自有分析)",
8
+ "山基说(素材提炼)/ zcl(分层核查,事实依据 facts/oil.md、facts/fed.md)": "山基说",
9
+ "小鹿投研日记(转播大摩闭门会 9.14,发言人Robin/Steven/钟申/Jenny;Laura股票部分视频截断未录)": "小鹿投研日记",
10
+ "中金点睛(刘刚、杨萱庭)": "中金点睛"
11
+ });
12
+ /** 把作者名归一到规范名;未登记的写法原样返回(仅去除首尾空白)。 */
13
+ function canonicalAuthor(author) {
14
+ const trimmed = author.trim();
15
+ return AUTHOR_ALIASES[trimmed] ?? trimmed;
16
+ }
17
+ /**
18
+ * 返回作者已归一的卡片副本;已是规范名时原样返回同一引用。
19
+ * 非破坏性:不修改传入卡片。缺 `source`/`author` 的畸形条目按原样放行——文件读取
20
+ * 层不为历史脏数据抛错,结构校验只在入库(`validateKnowledgeCard`)发生。
21
+ */
22
+ function canonicalizeCardAuthor(card) {
23
+ if (!card || typeof card !== "object" || !card.source || typeof card.source.author !== "string") return card;
24
+ const canonical = canonicalAuthor(card.source.author);
25
+ if (canonical === card.source.author) return card;
26
+ return {
27
+ ...card,
28
+ source: {
29
+ ...card.source,
30
+ author: canonical
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ export { AUTHOR_ALIASES, canonicalAuthor, canonicalizeCardAuthor };
package/lib/index.d.ts CHANGED
@@ -2,4 +2,5 @@ import { BuildGraphOptions, GraphLinkKind, KnowledgeCard, KnowledgeCardInput, Kn
2
2
  import { ValidationResult, generateCardId, validateKnowledgeCard } from "./validate.js";
3
3
  import { buildGraph } from "./graph.js";
4
4
  import { createMemoryKnowledgeCardStore } from "./store-memory.js";
5
- export { type BuildGraphOptions, type GraphLinkKind, type KnowledgeCard, type KnowledgeCardInput, type KnowledgeCardStore, type KnowledgeCredibility, type KnowledgeFactCheck, type KnowledgeGraphData, type KnowledgeGraphLink, type KnowledgeGraphNode, type KnowledgeSource, type KnowledgeSourceType, type ValidationResult, buildGraph, createMemoryKnowledgeCardStore, generateCardId, validateKnowledgeCard };
5
+ import { AUTHOR_ALIASES, canonicalAuthor, canonicalizeCardAuthor } from "./authors.js";
6
+ export { AUTHOR_ALIASES, type BuildGraphOptions, type GraphLinkKind, type KnowledgeCard, type KnowledgeCardInput, type KnowledgeCardStore, type KnowledgeCredibility, type KnowledgeFactCheck, type KnowledgeGraphData, type KnowledgeGraphLink, type KnowledgeGraphNode, type KnowledgeSource, type KnowledgeSourceType, type ValidationResult, buildGraph, canonicalAuthor, canonicalizeCardAuthor, createMemoryKnowledgeCardStore, generateCardId, validateKnowledgeCard };
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
+ import { AUTHOR_ALIASES, canonicalAuthor, canonicalizeCardAuthor } from "./authors.js";
1
2
  import { generateCardId, validateKnowledgeCard } from "./validate.js";
2
3
  import { buildGraph } from "./graph.js";
3
4
  import { createMemoryKnowledgeCardStore } from "./store-memory.js";
4
- export { buildGraph, createMemoryKnowledgeCardStore, generateCardId, validateKnowledgeCard };
5
+ export { AUTHOR_ALIASES, buildGraph, canonicalAuthor, canonicalizeCardAuthor, createMemoryKnowledgeCardStore, generateCardId, validateKnowledgeCard };
@@ -1,32 +1,54 @@
1
+ import { canonicalizeCardAuthor } from "./authors.js";
1
2
  import { readFile } from "node:fs/promises";
2
- import { writeJsonAtomic } from "@dshtrading/dsh-home";
3
+ import { SKIP_WRITE, transactStore } from "@dshtrading/dsh-home";
3
4
  //#region src/knowledge-fs.ts
4
5
  /**
5
6
  * 文件持久化版知识卡片存储(Node.js 宿主端专用)。
6
7
  *
7
- * 采用 tmp + rename 原子写入模式,并包含明确的错误日志与异常处理(逐行对齐 custom-fs.ts 先例)。
8
+ * 整表持久化经 `@dshtrading/dsh-home` 的 {@link transactStore}——跨进程排他锁 +
9
+ * 锁内新鲜读盘 + 原子写(2026-10-09 多进程互相覆盖事故修复)。读取面作者归一与坏
10
+ * 文件降级纪律不变(逐行对齐 custom-fs.ts 先例)。
8
11
  */
12
+ const LOG_PREFIX = "[dsh-trading/knowledge] failed to atomic flush knowledge cards to";
9
13
  function createFileKnowledgeCardStore(filePath) {
10
14
  let cache = null;
11
- async function load() {
12
- if (cache !== null) return cache;
15
+ /**
16
+ * 新鲜读盘(作者别名归一、坏 JSON 降级空表);供 load 与锁内重读共用。
17
+ *
18
+ * 作者别名在读取时归一(authors.ts):历史数据即刻在 GUI/检索层表现为规范名。
19
+ * 读本身不写盘,但归一结果留在缓存里,会被下一次写操作的全表回写落盘——
20
+ * 即一次静默迁移,故这里显式 log 一次,让「改别名表 = 改历史数据」可见。
21
+ * 缺 source / 缺 id 的脏行原样保留(不丢数据);结构性校验只在入库发生,
22
+ * 但这类行仍会让 getByUrl/search/graph 的解引用抛错(既有边界,未在此收口)。
23
+ */
24
+ async function readFromDisk() {
13
25
  try {
14
26
  const content = await readFile(filePath, "utf8");
15
27
  const parsed = JSON.parse(content);
16
28
  const map = /* @__PURE__ */ new Map();
17
29
  if (Array.isArray(parsed)) {
18
- for (const item of parsed) if (item && typeof item.id === "string") map.set(item.id, item);
30
+ let healed = 0;
31
+ for (const item of parsed) if (item && typeof item.id === "string") {
32
+ const card = canonicalizeCardAuthor(item);
33
+ if (card !== item) healed += 1;
34
+ map.set(item.id, card);
35
+ }
36
+ if (healed > 0) console.warn(`[dsh-trading/knowledge] 作者别名归一:读取时治愈 ${healed} 张历史卡片(${filePath});下一次写操作会把结果落盘`);
19
37
  }
20
- cache = map;
21
38
  return map;
22
39
  } catch (err) {
23
40
  if (err?.code !== "ENOENT") console.error(`[dsh-trading/knowledge] failed to read knowledge cards from ${filePath}:`, err);
24
- cache = /* @__PURE__ */ new Map();
25
- return cache;
41
+ return /* @__PURE__ */ new Map();
26
42
  }
27
43
  }
28
- async function flush(map) {
29
- await writeJsonAtomic(filePath, [...map.values()], "[dsh-trading/knowledge] failed to atomic flush knowledge cards to");
44
+ async function load() {
45
+ if (cache !== null) return cache;
46
+ cache = await readFromDisk();
47
+ return cache;
48
+ }
49
+ async function commit(mutate) {
50
+ cache = await transactStore(filePath, readFromDisk, mutate, (table) => [...table.values()], LOG_PREFIX);
51
+ return cache;
30
52
  }
31
53
  return {
32
54
  async list() {
@@ -41,14 +63,17 @@ function createFileKnowledgeCardStore(filePath) {
41
63
  for (const card of map.values()) if (card.source.url.trim() === trimmed) return card;
42
64
  },
43
65
  async save(card) {
44
- const map = await load();
45
- map.set(card.id, { ...card });
46
- await flush(map);
66
+ await commit((onDisk) => {
67
+ onDisk.set(card.id, canonicalizeCardAuthor({ ...card }));
68
+ return onDisk;
69
+ });
47
70
  },
48
71
  async delete(id) {
49
- const map = await load();
50
- const existed = map.delete(id);
51
- if (existed) await flush(map);
72
+ let existed = false;
73
+ await commit((onDisk) => {
74
+ existed = onDisk.delete(id);
75
+ return existed ? onDisk : SKIP_WRITE;
76
+ });
52
77
  return existed;
53
78
  }
54
79
  };
@@ -1,7 +1,8 @@
1
+ import { canonicalizeCardAuthor } from "./authors.js";
1
2
  //#region src/store-memory.ts
2
3
  function createMemoryKnowledgeCardStore(initialCards = []) {
3
4
  const map = /* @__PURE__ */ new Map();
4
- for (const c of initialCards) map.set(c.id, { ...c });
5
+ for (const c of initialCards) map.set(c.id, canonicalizeCardAuthor({ ...c }));
5
6
  return {
6
7
  async list() {
7
8
  return Array.from(map.values());
@@ -14,7 +15,7 @@ function createMemoryKnowledgeCardStore(initialCards = []) {
14
15
  for (const card of map.values()) if (card.source.url.trim() === trimmed) return card;
15
16
  },
16
17
  async save(card) {
17
- map.set(card.id, { ...card });
18
+ map.set(card.id, canonicalizeCardAuthor({ ...card }));
18
19
  },
19
20
  async delete(id) {
20
21
  return map.delete(id);
package/lib/tool.js CHANGED
@@ -30,72 +30,72 @@ function createKnowledgeIngestTool(store, options = {}) {
30
30
  const { onWritten } = options;
31
31
  return defineTool({
32
32
  name: "knowledge_ingest",
33
- description: "将经事实核查后的结构化知识卡片(Content Insight 产物)入库到本地知识库。支持自动 URL 查重(重复 URL 自动走更新且保持 ID 不变)及关联关系验证。",
33
+ description: "Ingest one fact-checked knowledge card (a Content Insight product) into the local knowledge base. Duplicate source URLs are detected automatically: an existing card with the same URL is updated in place and keeps its id. Related-card references are validated.",
34
34
  parameters: {
35
35
  title: {
36
36
  type: "string",
37
37
  required: true,
38
- description: "知识卡片主题(例如「高股息策略在低利率环境下的防御逻辑」)"
38
+ description: "Card subject, e.g. the defensive logic of high-dividend strategies in a low-rate environment."
39
39
  },
40
40
  summary: {
41
41
  type: "string",
42
42
  required: true,
43
- description: "2-4 条核心论点合并的一句话概述(图谱 hover 与快速导读展示)"
43
+ description: "One-sentence overview merging the 2-4 core claims; shown in the graph hover and quick read."
44
44
  },
45
45
  sourceType: {
46
46
  type: "string",
47
47
  required: true,
48
- description: "素材来源类型:\"bilibili\"(B站)、\"wechat\"(微信公众号)、\"manual\"(手工录入)"
48
+ description: "Source kind: \"bilibili\" | \"wechat\" | \"manual\"."
49
49
  },
50
50
  sourceUrl: {
51
51
  type: "string",
52
52
  required: true,
53
- description: "素材链接或去重标识(例如 B 站 BV 链接、公众号文章链接)"
53
+ description: "Source link or dedup key, e.g. a Bilibili BV link or a WeChat article link."
54
54
  },
55
55
  sourceAuthor: {
56
56
  type: "string",
57
57
  required: true,
58
- description: "来源作者/UP主/公众号名称"
58
+ description: "Source author, channel or account name."
59
59
  },
60
60
  publishedAt: {
61
61
  type: "string",
62
- description: "素材发布日期(ISO 格式如 2026-08-30,可选)"
62
+ description: "Optional source publish date, ISO format such as 2026-08-30."
63
63
  },
64
64
  credibility: {
65
65
  type: "string",
66
66
  required: true,
67
- description: "依据事实核查整体定级:\"high\"(事实严密证据充分)、\"medium\"(观点可取但部分有出入)、\"low\"(存在重大未核实或明显漏洞)"
67
+ description: "Overall fact-check grade: \"high\" | \"medium\" | \"low\"."
68
68
  },
69
69
  coreClaimsJson: {
70
70
  type: "string",
71
71
  required: true,
72
- description: "JSON 字符串数组,核心论点列表(保留原作者推理链条),例如 '[\"论点1\", \"论点2\"]'"
72
+ description: "JSON string array of core claims, preserving the author's reasoning chain, e.g. '[\"claim1\", \"claim2\"]'."
73
73
  },
74
74
  factCheckJson: {
75
75
  type: "string",
76
76
  required: true,
77
- description: "JSON 对象,事实核查三档分类,例如 '{\"verified\":[\"证实1\"],\"discrepancies\":[\"出入1\"],\"unverifiable\":[]}'"
77
+ description: "JSON object with the three fact-check buckets, e.g. '{\"verified\":[\"...\"],\"discrepancies\":[\"...\"],\"unverifiable\":[]}'."
78
78
  },
79
79
  takeawaysJson: {
80
80
  type: "string",
81
- description: "可选 JSON 字符串数组,可复用的分析框架与经验总结,例如 '[\"经验1\"]'"
81
+ description: "Optional JSON string array of reusable analysis frameworks and lessons."
82
82
  },
83
83
  boundariesJson: {
84
84
  type: "string",
85
- description: "可选 JSON 字符串数组,适用边界、失效情景与避坑指南,例如 '[\"边界1\"]'"
85
+ description: "Optional JSON string array of applicability boundaries, invalidation cases and pitfalls."
86
86
  },
87
87
  tagsJson: {
88
88
  type: "string",
89
89
  required: true,
90
- description: "JSON 字符串数组,受控主题标签列表,例如 '[\"宏观\", \"高股息\", \"红利策略\"]'"
90
+ description: "JSON string array of controlled topic tags, e.g. '[\"宏观\", \"高股息\", \"红利策略\"]'."
91
91
  },
92
92
  tickersJson: {
93
93
  type: "string",
94
- description: "可选 JSON 字符串数组,关联标的代码列表,例如 '[\"BTCUSDT\", \"600519.SH\"]'"
94
+ description: "Optional JSON string array of related instrument codes, e.g. '[\"BTCUSDT\", \"600519.SH\"]'."
95
95
  },
96
96
  relatedJson: {
97
97
  type: "string",
98
- description: "可选 JSON 字符串数组,显式关联的其他已入库卡片 ID 列表,例如 '[\"kc_01j...\", \"kc_01k...\"]'"
98
+ description: "Optional JSON string array of related card ids already in the library, e.g. '[\"kc_01j...\", \"kc_01k...\"]'."
99
99
  }
100
100
  },
101
101
  output: {
@@ -186,47 +186,50 @@ function createKnowledgeIngestTool(store, options = {}) {
186
186
  const card = validation.card;
187
187
  await store.save(card);
188
188
  onWritten?.(card);
189
- return `[knowledge_ingest] ${!!existingByUrl ? "成功更新已有知识卡片" : "成功创建新知识卡片"} [${card.id}] "${card.title}" (标签: ${card.tags.join(", ")}${card.related?.length ? `, 关联: ${card.related.join(", ")}` : ""})`;
189
+ const actionDesc = !!existingByUrl ? "成功更新已有知识卡片" : "成功创建新知识卡片";
190
+ const requestedAuthor = typeof args.sourceAuthor === "string" ? args.sourceAuthor.trim() : "";
191
+ const authorNote = requestedAuthor !== "" && requestedAuthor !== card.source.author ? `(作者已归一:${requestedAuthor} → ${card.source.author})` : "";
192
+ return `[knowledge_ingest] ${actionDesc} [${card.id}] "${card.title}"${authorNote} (标签: ${card.tags.join(", ")}${card.related?.length ? `, 关联: ${card.related.join(", ")}` : ""})`;
190
193
  }
191
194
  });
192
195
  }
193
196
  function createKnowledgeSearchTool(store) {
194
197
  return defineTool({
195
198
  name: "knowledge_search",
196
- description: "检索本地知识库中的知识卡片。有关键词时按字段命中相关度排序(标签 > 标题 > 核心论点 > 摘要/作者),无关键词时按更新时间倒序;支持按主体(cluster,配合 knowledge_graph 两级检索)、作者、来源类型、可信度多维过滤。detail=\"full\" 时同时返回核心论点、事实核查与可复用经验全文(上限 20 张)。",
199
+ description: "Search knowledge cards in the local library. With a keyword the results rank by field hit weight (tags > title > core claims > summary/author); without one they come newest-updated first. Filters: cluster (the graph clustering key, pair with knowledge_graph for two-stage retrieval), author, source kind and credibility. detail=\"full\" also returns core claims, the fact-check buckets and reusable lessons (capped at 20 cards).",
197
200
  parameters: {
198
201
  query: {
199
202
  type: "string",
200
- description: "搜索关键词(在标题、摘要、核心论点、标签中模糊匹配)"
203
+ description: "Search keyword, fuzzy-matched against title, summary, core claims and tags."
201
204
  },
202
205
  tags: {
203
206
  type: "string",
204
- description: "逗号分隔的标签过滤(例如 \"宏观,高股息\",匹配其中任意一个)"
207
+ description: "Comma-separated tag filter, e.g. \"宏观,高股息\"; matches any one of them."
205
208
  },
206
209
  cluster: {
207
210
  type: "string",
208
- description: "按主体精确过滤(主体 = 图谱聚类键 = 卡片首个标签)。两级检索第二级:先 knowledge_graph 看主体分布,再用本参数钻取该主体下的卡片"
211
+ description: "Exact subject filter (subject = graph clustering key = the card first tag). Second stage of a two-stage retrieval: call knowledge_graph for the subject distribution, then drill in here."
209
212
  },
210
213
  author: {
211
214
  type: "string",
212
- description: "按作者/UP主/公众号过滤(子串匹配)"
215
+ description: "Author/channel filter (substring match)."
213
216
  },
214
217
  sourceType: {
215
218
  type: "string",
216
- description: "按来源类型过滤:\"bilibili\" | \"wechat\" | \"manual\""
219
+ description: "Source kind filter: \"bilibili\" | \"wechat\" | \"manual\"."
217
220
  },
218
221
  credibility: {
219
222
  type: "string",
220
- description: "按可信度过滤:\"high\" | \"medium\" | \"low\""
223
+ description: "Credibility filter: \"high\" | \"medium\" | \"low\"."
221
224
  },
222
225
  limit: {
223
226
  type: "number",
224
- description: "最大返回数量(默认 20)",
227
+ description: "Maximum results to return (default 20).",
225
228
  default: 20
226
229
  },
227
230
  detail: {
228
231
  type: "string",
229
- description: "返回详略:\"summary\"(默认,摘要级)| \"full\"(附核心论点/事实核查/经验/边界全文)",
232
+ description: "Detail level: \"summary\" (default) | \"full\" (adds core claims, fact check, lessons and boundaries).",
230
233
  default: "summary"
231
234
  }
232
235
  },
@@ -299,11 +302,11 @@ function createKnowledgeSearchTool(store) {
299
302
  function createKnowledgeGetTool(store) {
300
303
  return defineTool({
301
304
  name: "knowledge_get",
302
- description: "按 id 读取单张知识卡片的完整内容(核心论点、事实核查三桶、可复用经验、适用边界)。id 可来自 knowledge_search 结果或历史分析中的引用标注。",
305
+ description: "Read one knowledge card in full by id: core claims, the three fact-check buckets, reusable lessons and applicability boundaries. Ids come from knowledge_search results or citation markers in earlier analyses.",
303
306
  parameters: { id: {
304
307
  type: "string",
305
308
  required: true,
306
- description: "知识卡片 id(kc_...)"
309
+ description: "Knowledge card id (kc_...)."
307
310
  } },
308
311
  output: {
309
312
  schema: { type: "string" },
@@ -334,16 +337,16 @@ function createKnowledgeDeleteTool(store, options = {}) {
334
337
  const { onWritten } = options;
335
338
  return defineTool({
336
339
  name: "knowledge_delete",
337
- description: "从本地知识库删除(下架)指定知识卡片。用于知识点/经验被事实证伪、来源撤稿或卡片重复等场景。删除时自动清理其他卡片指向本卡片的 related 引用,并在输出中回显被删卡片的标题与核心论点留痕;证伪结论本身应另行记入交易日志或对话沉淀。",
340
+ description: "Delete (retire) one knowledge card from the local library, for a claim disproved by evidence, a retracted source or a duplicate card. Other cards' related references to it are cleaned up automatically, and the deleted title and core claims are echoed as a trail. Record the disproof itself separately in the trading journal or the conversation.",
338
341
  parameters: {
339
342
  id: {
340
343
  type: "string",
341
344
  required: true,
342
- description: "要删除的知识卡片 id(kc_...)。不确定 id 时先用 knowledge_search 查询确认,不要凭模糊记忆删除。"
345
+ description: "Knowledge card id to delete (kc_...). Confirm the id with knowledge_search first; never delete from vague memory."
343
346
  },
344
347
  reason: {
345
348
  type: "string",
346
- description: "删除原因(如「核心论点被 XX 数据证伪」「来源撤稿」),回显在结果中供留痕"
349
+ description: "Deletion reason, echoed in the result as a trail."
347
350
  }
348
351
  },
349
352
  output: {
package/lib/validate.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { canonicalAuthor } from "./authors.js";
1
2
  //#region src/validate.ts
2
3
  const ALLOWED_CREDIBILITY = /* @__PURE__ */ new Set([
3
4
  "high",
@@ -103,7 +104,7 @@ function validateKnowledgeCard(input, existingCards = []) {
103
104
  source: {
104
105
  type: input.source.type,
105
106
  url: input.source.url.trim(),
106
- author: input.source.author.trim(),
107
+ author: canonicalAuthor(input.source.author),
107
108
  publishedAt: input.source.publishedAt
108
109
  },
109
110
  credibility: input.credibility,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dshtrading/knowledge",
3
3
  "description": "Knowledge base data plane for dsh-trading: KnowledgeCard data model, validation, graph builder, atomic file store, and knowledge_ingest / knowledge_search agent tools.",
4
- "version": "0.4.1",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -25,9 +25,9 @@
25
25
  ],
26
26
  "license": "PolyForm-Noncommercial-1.0.0",
27
27
  "dependencies": {
28
- "@deepseek-ai/dsh-tools": "0.1.5-rc.1",
29
- "@deepseek-ai/schemastery": "^3.18.2",
30
- "@dshtrading/dsh-home": "^0.4.1"
28
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
29
+ "@deepseek-ai/schemastery": "^3.18.4",
30
+ "@dshtrading/dsh-home": "^0.6.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/cordis": ">=4.0.0"