dsh-channel-memory 0.1.0 → 0.1.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 (3) hide show
  1. package/README.md +44 -9
  2. package/lib/index.js +31 -19
  3. package/package.json +55 -55
package/README.md CHANGED
@@ -44,31 +44,47 @@ DeepSeek Harness 插件:**手机通道专属工作区** + **跨会话互通记
44
44
 
45
45
  ## 安装
46
46
 
47
+ > ⚠️ npm 上 `dsh-channel-memory` 目前只有 `0.0.0-stage` 占位版(`latest` 指向它),
48
+ > 真实版本要等 staging approve 完成。**在此之前请用下面的 git 或本地目录方式安装**,
49
+ > 不要直接 `pnpm add dsh-channel-memory`。
50
+
51
+ 在 DSH 的应用 profile 目录里执行(Windows 桌面形态是
52
+ `%USERPROFILE%\.dsh\profiles\desktop`):
53
+
47
54
  ```bash
48
55
  cd %USERPROFILE%\.dsh\profiles\desktop
49
- node <DSH>\resources\runtime\..\..\resources\app.asar\dsh\... # 见下
56
+ pnpm add github:Yuncan050115/dsh-channel-memory
50
57
  ```
51
58
 
52
- 推荐用 DSH 插件管理器,或直接:
59
+ 或从本地目录安装:
53
60
 
54
61
  ```bash
55
- pnpm add dsh-channel-memory
62
+ pnpm add file:C:\path\to\dsh-channel-memory
56
63
  ```
57
64
 
58
- 然后把 bundle 名加进 `package.json` 的 `dsh.profile.bundles`:
65
+ 然后把 bundle 名加进同目录 `package.json` 的 `dsh.profile.bundles`
66
+ (DSH 插件管理器会自动做这一步,手工加也可以):
59
67
 
60
68
  ```json
61
69
  "dsh": { "profile": { "bundles": [ ..., "dsh-channel-memory" ] } }
62
70
  ```
63
71
 
64
- 重启 DSH 生效。校验(不启动 GUI,只做组合干跑):
72
+ 重启 DSH 生效。不想重启也可以先校验组合结果——用 DSH 自带运行时做一次干跑
73
+ (`--dump-config` 只组合配置、不启动 GUI)。`<APP>` 是 DSH 的安装目录,
74
+ Windows 桌面形态默认是 `C:\Program Files\DeepSeek Harness` 或
75
+ `D:\DeepSeek Harness`:
65
76
 
66
- ```bash
77
+ ```bat
67
78
  set ELECTRON_RUN_AS_NODE=1
68
- "DeepSeek Harness.exe" --expose-internals <profile>\..\..\..\app.asar\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js ^
79
+ set DSH_HOME=%USERPROFILE%\.dsh
80
+ "<APP>\DeepSeek Harness.exe" --expose-internals ^
81
+ "<APP>\resources\app.asar\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js" ^
69
82
  --profile desktop --dump-config
70
83
  ```
71
84
 
85
+ 输出里应能看到 `# == dsh-channel-memory` 这一层;若出现 `skipped bundles` 或
86
+ `did not activate`,说明 bundle 没挂上。
87
+
72
88
  ## 配置
73
89
 
74
90
  `cordis.patch.yml` 里 `channel-memory` 行的 `config` 全部可选,注释掉了默认值:
@@ -100,14 +116,33 @@ keeper → 桥的 `--cwd`),例如:
100
116
 
101
117
  ## 验证
102
118
 
103
- `plugin/contract_test.mjs` 用**宿主自己的 `defineTool`** 跑契约测试(不是 stub):
119
+ `test/contract_test.mjs` 用**宿主自己的 `defineTool`** 跑契约测试(不是 stub):
104
120
  value-schema DSL 违规会当场抛。夹具强制宿主不变量(cwd 不符则 `attachSession` 抛),
105
121
  因此「外来会话被拒绝挂载」是被真实验证的,而不是被假 object 允许的。
106
122
 
123
+ 在插件目录里运行(`<APP>` 同上;Windows 桌面形态必须用 DSH 自带的 Electron 跑,
124
+ 因为宿主模块在 `app.asar` 里):
125
+
126
+ ```bash
127
+ set ELECTRON_RUN_AS_NODE=1
128
+ "<APP>\DeepSeek Harness.exe" --expose-internals test\contract_test.mjs
129
+ ```
130
+
131
+ 可选覆盖(默认指向本机安装布局):`DSH_TOOLS_ENTRY`、`DSH_PLUGIN_ENTRY`。
132
+
133
+ 实测输出:
134
+
107
135
  ```
108
- RESULT: PASS (5 个工具注册、1 个提示词分节、外来会话未被挂载、post-apply 写入反映进索引)
136
+ tools registered: 5
137
+ prompt sections : channel-memory:index@3200(text=function)
138
+ attached (channel session): ["sess-abc"]
139
+ after foreign session : ["sess-abc"] (must not contain sess-elsewhere)
140
+ index reflects post-apply write: true
141
+ RESULT: PASS
109
142
  ```
110
143
 
144
+ 测试会把 `DSH_HOME` 指到临时目录,不会碰你真实的 `~/.dsh`。
145
+
111
146
  权限面见 `PERMISSIONS.md`。
112
147
 
113
148
  ## License
package/lib/index.js CHANGED
@@ -303,9 +303,25 @@ export function apply(ctx, config) {
303
303
  },
304
304
  ]
305
305
 
306
- const diag = { toolsService: 'ok', defineTool: typeof defineTool, errors: [], registered: [] }
306
+ const diag = { toolsService: 'absent', defineTool: typeof defineTool, errors: [], registered: [] }
307
+
308
+ // Written before it is called: registration can land either synchronously (hosts
309
+ // that resolve `tools` eagerly) or from the inject callback, and both paths report
310
+ // through here. A single early snapshot would always show registered:0 on profiles
311
+ // where inject defers, which previously read as "plugin not installed".
312
+ const writeStatus = () => {
313
+ try {
314
+ writeJson(join(cfg.dataDir, 'status.json'), {
315
+ version: 1, at: new Date().toISOString(), registered: diag.registered.length, total: tools.length,
316
+ diag, workspace: state.workspaceId || null, memories: mem.items.length,
317
+ })
318
+ } catch { }
319
+ }
320
+
307
321
  const registerAll = (toolsSvc) => {
322
+ if (diag.registered.length) return // exactly once
308
323
  if (!toolsSvc || typeof toolsSvc.register !== 'function') { diag.toolsService = 'unavailable'; return }
324
+ diag.toolsService = 'ok'
309
325
  for (const t of tools) {
310
326
  try {
311
327
  toolsSvc.register(defineTool({ ...t, output: OUT() }))
@@ -316,29 +332,25 @@ export function apply(ctx, config) {
316
332
  ctx.logger?.warn?.(`channel-memory: tool ${msg}`)
317
333
  }
318
334
  }
335
+ writeStatus()
319
336
  }
320
337
 
321
- // inject 声明保证了 ctx.tools 可用;注册同步完成后才落快照,
322
- // 不会再出现 "toolsService absent / registered 0" 的时序假象。
323
- registerAll(ctx.tools)
338
+ // `tools` is requested through inject rather than read off the root context:
339
+ // on some profiles (observed under `--profile acp`, the path phone turns take)
340
+ // touching ctx.tools without declaring it throws
341
+ // `cannot get property "tools" without inject`, which would abort apply() and
342
+ // leave the plugin mounted but tool-less.
343
+ ctx.inject(['tools'], (host) => registerAll(host.tools))
324
344
 
325
- // Observability: without this, a silent registration failure is indistinguishable
326
- // from "plugin not installed" when looking at the profile from the outside.
327
- // Tool registration is synchronous above, but the workspace is created by an
328
- // async effect, so a single snapshot here always shows workspace:null. Re-write
329
- // after the effect has had time to land, otherwise the file contradicts
330
- // channel_workspace_info and looks like a mount failure.
331
- const writeStatus = () => {
332
- try {
333
- writeJson(join(cfg.dataDir, 'status.json'), {
334
- version: 1, at: new Date().toISOString(), registered: diag.registered.length, total: tools.length,
335
- diag, workspace: state.workspaceId || null, memories: mem.items.length,
336
- })
337
- } catch { }
345
+ // Hosts that already resolved the service call the callback synchronously; where
346
+ // they do not, keep the direct handle as a guarded fallback so registration can
347
+ // never silently no-op. registerAll ignores the second attempt.
348
+ if (!diag.registered.length) {
349
+ try { registerAll(ctx.tools) } catch (e) { diag.errors.push('direct: ' + String(e?.message ?? e).slice(0, 120)) }
338
350
  }
339
351
  writeStatus()
340
- setTimeout(writeStatus, 2000)
352
+ setTimeout(writeStatus, 2000) // after the workspace effect has had time to land
341
353
  setTimeout(writeStatus, 10000)
342
354
 
343
355
  ctx.logger?.info?.(`channel-memory ready: tools=${diag.registered.length}/${tools.length} memories=${mem.items.length} ws=${cfg.workspacePath}`)
344
- }
356
+ }
package/package.json CHANGED
@@ -1,55 +1,55 @@
1
- {
2
- "name": "dsh-channel-memory",
3
- "version": "0.1.0",
4
- "description": "微信通道专属工作区 + 跨会话互通记忆:把手机发起的 DSH 会话归到一个独立工作区,并让所有会话共享一份可检索的长期记忆。",
5
- "license": "MIT",
6
- "type": "module",
7
- "main": "./lib/index.js",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/Yuncan050115/dsh-channel-memory.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/Yuncan050115/dsh-channel-memory/issues"
14
- },
15
- "homepage": "https://github.com/Yuncan050115/dsh-channel-memory#readme",
16
- "exports": {
17
- ".": "./lib/index.js",
18
- "./package.json": "./package.json"
19
- },
20
- "files": [
21
- "lib",
22
- "cordis.patch.yml",
23
- "README.md",
24
- "PERMISSIONS.md",
25
- "LICENSE",
26
- "package.json"
27
- ],
28
- "dsh": {
29
- "bundle": {
30
- "patch": "./cordis.patch.yml"
31
- }
32
- },
33
- "peerDependencies": {
34
- "@deepseek-ai/cordis": "^4.0.1",
35
- "@deepseek-ai/dsh-tools": ">=0.2.1-rc.5"
36
- },
37
- "peerDependenciesMeta": {
38
- "@deepseek-ai/cordis": {
39
- "optional": true
40
- },
41
- "@deepseek-ai/dsh-tools": {
42
- "optional": true
43
- }
44
- },
45
- "engines": {
46
- "node": ">=22"
47
- },
48
- "keywords": [
49
- "deepseek-harness",
50
- "dsh-plugin",
51
- "wechat",
52
- "memory",
53
- "workspace"
54
- ]
55
- }
1
+ {
2
+ "name": "dsh-channel-memory",
3
+ "version": "0.1.2",
4
+ "description": "微信通道专属工作区 + 跨会话互通记忆:把手机发起的 DSH 会话归到一个独立工作区,并让所有会话共享一份可检索的长期记忆。",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./lib/index.js",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Yuncan050115/dsh-channel-memory.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/Yuncan050115/dsh-channel-memory/issues"
14
+ },
15
+ "homepage": "https://github.com/Yuncan050115/dsh-channel-memory#readme",
16
+ "exports": {
17
+ ".": "./lib/index.js",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "lib",
22
+ "cordis.patch.yml",
23
+ "README.md",
24
+ "PERMISSIONS.md",
25
+ "LICENSE",
26
+ "package.json"
27
+ ],
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "peerDependencies": {
34
+ "@deepseek-ai/cordis": "^4.0.1",
35
+ "@deepseek-ai/dsh-tools": ">=0.2.1-rc.5"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "@deepseek-ai/cordis": {
39
+ "optional": true
40
+ },
41
+ "@deepseek-ai/dsh-tools": {
42
+ "optional": true
43
+ }
44
+ },
45
+ "engines": {
46
+ "node": ">=22"
47
+ },
48
+ "keywords": [
49
+ "deepseek-harness",
50
+ "dsh-plugin",
51
+ "wechat",
52
+ "memory",
53
+ "workspace"
54
+ ]
55
+ }