dsh-mindmap 0.7.0 → 0.8.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
@@ -23,6 +23,11 @@ All notable changes to this project are documented here. Release-specific notes
23
23
 
24
24
  ### Fixed
25
25
 
26
+ - Compatibility with dsh 0.1.2-rc.1 (statically verified, docs/023), while keeping 0.1.1-rc.2 working — all fixes are dual-path with the old path first:
27
+ - **Host tools**: `Agent` no longer carries a live `session` (it is reduced to `{ id }`), so `sessionCwd` now falls back from `exec.agent.session.header.cwd` (dsh ≤0.1.1) to looking the session up via `ctx.sessions.get(exec.agent.id).header.cwd` (0.1.2-rc.1). Without this, every tool failed with "the session has no working directory" on 0.1.2-rc.1.
28
+ - **Live panel data**: the session snapshot no longer carries a flat `nodes` array (conversation content moved to the Chat view), so the panel now reads nodes via `useChat → ChatSnapshot.legacy.nodes` (0.1.2-rc.1+) first and falls back to `useSession → SessionSnapshot.nodes` (dsh ≤0.1.1). `ToolResultNode` field names are unchanged across both, so document replay, auto-open, and error surfacing work as before.
29
+ - **Settings panel**: `settings.describe` over the client connection now returns the descriptor array directly (0.1.2-rc.1+) instead of an aggregate `result.value.namespaces` (dsh ≤0.1.1); both envelope shapes are parsed.
30
+ - **Hygiene**: `dsh.client.inject` additionally lists `@deepseek-ai/dsh-cordis-client-runner` (the 0.1.2-rc.1 browser runtime module id; `@deepseek-ai/dsh-client-runtime` is kept for 0.1.1-rc.2 — a missing id is silently skipped by the loader, so both coexist safely).
26
31
  - The panel now reliably auto-opens when the AI completes `mindmap_open` / `mindmap_create`. A structural fingerprint of the session nodes (`nodesFingerprint`) feeds a second `useSession` selector; its value comparison bypasses the reference-equality short-circuit that starved the auto-open effect whenever the host store mutated the nodes array in place.
27
32
  - The "AI 正在打开脑图…" loading state is no longer a dead end. Snapshot documents whose path differs from the tree-click key only by letter case (macOS case-insensitive filesystem) now merge automatically; errored mindmap tool results (`isError` or `ok !== true`) surface as an inline error; a ~30s watchdog switches to a timeout state. Both failure states offer a one-click retry that re-sends the open request.
28
33
  - A code-review sweep (`_issues/002`) landed 11 fixes, each with a failing regression test first:
package/client.js CHANGED
@@ -7,9 +7,10 @@
7
7
  // - 脑图面板:014 起注册在 shell.overlay(list 槽、root scope、点击穿透层),
8
8
  // 右缘贴边全高悬浮、左缘拖拽调宽(280~80% 视口,localStorage 持久化);
9
9
  // details 槽已归还官方(原生「工具详情」栏恢复,003 的顶替方案退役)。
10
- // - 实时数据通路:消费会话快照(useSession → nodes)里 mindmap_* 工具的
11
- // ToolResultNode,重放出各文档的最新内容并渲染(002/003:无自定义事件通道,
12
- // 工具调用本身就是事件流)。
10
+ // - 实时数据通路:消费会话快照里 mindmap_* 工具的 ToolResultNode,重放出各文档
11
+ // 的最新内容并渲染(002/003:无自定义事件通道,工具调用本身就是事件流)。
12
+ // 023 双代:useChat → ChatSnapshot.legacy.nodes(dsh 0.1.2-rc.1+)优先,
13
+ // useSession → SessionSnapshot.nodes(dsh ≤0.1.1)兜底。
13
14
  // - markdown→脑图树:本文件内置零依赖解析器(MarkGrove mdastConverter 的映射
14
15
  // 语法移植:标题栈→树、列表→子节点、空列表项=占位节点、代码块→首行摘要叶
15
16
  // 节点、段落→挂标题的正文说明、结构路径稳定 ID)。
@@ -1498,20 +1499,35 @@ window.__ModuleLoader__.load({
1498
1499
  ] });
1499
1500
  }
1500
1501
 
1502
+ /**
1503
+ * 会话内容节点的双代快照选择(023):dsh ≤0.1.1 的 useSession 快照带
1504
+ * 平铺 nodes;0.1.2-rc.1 起 SessionSnapshot 拆成纯控制状态,会话内容
1505
+ * 迁入 useChat 的 ChatSnapshot.legacy.nodes(官方兼容面,ToolResultNode
1506
+ * 字段同名)。legacy 优先、旧 nodes 兜底,两代通吃。
1507
+ */
1508
+ function conversationNodesOf(s) {
1509
+ if (!s) return EMPTY_NODES;
1510
+ const legacy = s.legacy;
1511
+ if (legacy && Array.isArray(legacy.nodes)) return legacy.nodes;
1512
+ return Array.isArray(s.nodes) ? s.nodes : EMPTY_NODES;
1513
+ }
1514
+
1501
1515
  /**
1502
1516
  * 「思维脑图」槽位组件(014):同一槽位渲染 M 按钮 + 悬浮面板宿主层。
1503
1517
  * session scope 的 useSession/sessionId/inputActions 直给,经 props 传给
1504
1518
  * MindmapDetailsPanel(无桥、无 useSyncExternalStore——shell.overlay 跨槽
1505
- * 方案实测未渲染,弃用后顺手把桥也删了)。
1519
+ * 方案实测未渲染,弃用后顺手把桥也删了)。023:内容钩子改为
1520
+ * useChat(0.1.2-rc.1+)优先、useSession(≤0.1.1)兜底。
1506
1521
  */
1507
1522
  function MindmapSlot(props) {
1508
- const { useSession, sessionId, inputActions, mindmapFace } = props;
1509
- const nodes = useSession ? useSession((s) => (s && s.nodes) || EMPTY_NODES) : EMPTY_NODES;
1523
+ const { useSession, useChat, sessionId, inputActions, mindmapFace } = props;
1524
+ const nodesHook = useChat ?? useSession;
1525
+ const nodes = nodesHook ? nodesHook(conversationNodesOf) : EMPTY_NODES;
1510
1526
  // 016 可靠性加固:结构指纹作第二 selector。store 原地改数组(引用
1511
1527
  // 不变)时,nodes prop 不换、memo 命中缓存、auto-open effect 永不
1512
1528
  // 重跑——「AI 打开了脑图但面板不展开」的根因。指纹是原始值字符串,
1513
- // 值比较天然绕过引用相等短路;useSession 不可用时回退空串。
1514
- const nodesVersion = useSession ? useSession((s) => nodesFingerprint((s && s.nodes) || EMPTY_NODES)) : "";
1529
+ // 值比较天然绕过引用相等短路;内容钩子不可用时回退空串。
1530
+ const nodesVersion = nodesHook ? nodesHook((s) => nodesFingerprint(conversationNodesOf(s))) : "";
1515
1531
  const [open, setOpen] = react.useState(false);
1516
1532
  return (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
1517
1533
  (0, react_jsx_runtime.jsxs)("button", {
@@ -3270,6 +3286,19 @@ window.__ModuleLoader__.load({
3270
3286
  }
3271
3287
  //#endregion
3272
3288
 
3289
+ /**
3290
+ * settings describe 应答的双代信封解析(023):dsh ≤0.1.1 的远端把
3291
+ * 描述符聚合在 result.value.namespaces[];0.1.2-rc.1 起直接返回描述符
3292
+ * 数组(每项 {ns, schema, value, …},字段两代同名)。数组优先、
3293
+ * namespaces 兜底,两代通吃。
3294
+ */
3295
+ function settingsNamespacesOf(res) {
3296
+ const value = res?.result?.value;
3297
+ if (Array.isArray(value)) return value;
3298
+ const list = value?.namespaces;
3299
+ return Array.isArray(list) ? list : [];
3300
+ }
3301
+
3273
3302
  function apply(ctx) {
3274
3303
  const face = {};
3275
3304
 
@@ -3331,7 +3360,7 @@ window.__ModuleLoader__.load({
3331
3360
  face.readSettings = async () => {
3332
3361
  if (!settingsApi || typeof settingsApi.settings?.describe !== "function") return null;
3333
3362
  const res = await settingsApi.settings.describe({});
3334
- const namespaces = res?.result?.value?.namespaces ?? [];
3363
+ const namespaces = settingsNamespacesOf(res);
3335
3364
  const ns = namespaces.find((n) => n?.ns === "mindmap");
3336
3365
  return ns?.value ?? null;
3337
3366
  };
@@ -3413,6 +3442,9 @@ window.__ModuleLoader__.load({
3413
3442
  isActivatable,
3414
3443
  TOOL_NAMES,
3415
3444
  OPENING_OPS,
3445
+ // 023 双代兼容纯函数(供测试):会话内容节点 / settings 信封。
3446
+ conversationNodesOf,
3447
+ settingsNamespacesOf,
3416
3448
  // 021 画布组件:仅供测试驱动平移手势(不参与运行时契约)。
3417
3449
  MindmapCanvas,
3418
3450
  });
package/index.js CHANGED
@@ -65,9 +65,21 @@ function textOut(value) {
65
65
  return [{ type: 'text', text: String(value) }]
66
66
  }
67
67
 
68
- /** 会话工作目录:工具执行的 agent → session → header.cwd(dsh-session 契约)。 */
69
- function sessionCwd(exec) {
70
- return exec?.agent?.session?.header?.cwd
68
+ /**
69
+ * 会话工作目录:工具执行的 agent → session → header.cwd(dsh-session 契约)。
70
+ * 023 双路径:dsh ≤0.1.1 的 Agent 直挂 live session(agent.session.header.cwd);
71
+ * 0.1.2-rc.1 起 Agent 只剩 { id },改经 sessions 服务按 id 查 header.cwd
72
+ * (SessionStore.get / SessionHeader.cwd 两代同名)。旧链优先,新链兜底。
73
+ */
74
+ function sessionCwd(exec, sessions) {
75
+ const direct = exec?.agent?.session?.header?.cwd
76
+ if (direct) return direct
77
+ const id = exec?.agent?.id
78
+ if (id && sessions?.get) {
79
+ const cwd = sessions.get(id)?.header?.cwd
80
+ if (cwd) return cwd
81
+ }
82
+ return undefined
71
83
  }
72
84
 
73
85
  //#region 013 目录树 API(host 自建只读 HTTP 路由;dsh-better-sidebar 同款机制)
@@ -355,7 +367,7 @@ export function apply(ctx, config = {}) {
355
367
  output: { schema: { type: 'string' }, render: (_args, value) => textOut(value) },
356
368
  timeoutMs: TOOL_TIMEOUT_MS,
357
369
  async execute(args, exec) {
358
- const cwd = sessionCwd(exec)
370
+ const cwd = sessionCwd(exec, ctx.sessions)
359
371
  if (!cwd) throw new Error('The session has no working directory; cannot create a mindmap.')
360
372
  const stem = sanitizeStem(args?.name)
361
373
  const path = await resolveMindmapPath(cwd, `${stem}.md`)
@@ -385,7 +397,7 @@ export function apply(ctx, config = {}) {
385
397
  output: { schema: { type: 'string' }, render: (_args, value) => textOut(value) },
386
398
  timeoutMs: TOOL_TIMEOUT_MS,
387
399
  async execute(args, exec) {
388
- const path = await resolveMindmapPath(sessionCwd(exec), args?.path)
400
+ const path = await resolveMindmapPath(sessionCwd(exec, ctx.sessions), args?.path)
389
401
  const content = await readFile(path, 'utf8')
390
402
  return buildResult('open', path, { content })
391
403
  },
@@ -404,7 +416,7 @@ export function apply(ctx, config = {}) {
404
416
  output: { schema: { type: 'string' }, render: (_args, value) => textOut(value) },
405
417
  timeoutMs: TOOL_TIMEOUT_MS,
406
418
  async execute(args, exec) {
407
- const path = await resolveMindmapPath(sessionCwd(exec), args?.path)
419
+ const path = await resolveMindmapPath(sessionCwd(exec, ctx.sessions), args?.path)
408
420
  const content = await readFile(path, 'utf8')
409
421
  return buildResult('get', path, { content })
410
422
  },
@@ -425,7 +437,7 @@ export function apply(ctx, config = {}) {
425
437
  output: { schema: { type: 'string' }, render: (_args, value) => textOut(value) },
426
438
  timeoutMs: TOOL_TIMEOUT_MS,
427
439
  async execute(args, exec) {
428
- const cwd = sessionCwd(exec)
440
+ const cwd = sessionCwd(exec, ctx.sessions)
429
441
  const path = await resolveMindmapPath(cwd, args?.path)
430
442
  const hasContent = typeof args?.content === 'string'
431
443
  if (!hasContent && typeof args?.renameRoot !== 'string') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mindmap",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Mindmap plugin for DeepSeek Harness: a plain markdown file in the working directory IS the mindmap; the chat edits it step by step and the right-side panel follows live.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,6 +35,9 @@
35
35
  "dependencies": {
36
36
  "@deepseek-ai/schemastery": "^3.18.0"
37
37
  },
38
+ "peerDependencies": {
39
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.1.1-rc.0 || ^0.1.2-alpha.0 || ^0.1.3-alpha.0"
40
+ },
38
41
  "dsh": {
39
42
  "bundle": {
40
43
  "patch": "./cordis.patch.yml"
@@ -42,6 +45,7 @@
42
45
  "client": {
43
46
  "inject": [
44
47
  "@deepseek-ai/dsh-client-runtime",
48
+ "@deepseek-ai/dsh-cordis-client-runner",
45
49
  "@deepseek-ai/dsh-client-ui-layout"
46
50
  ],
47
51
  "platform": "web",