@aiwayds/dsh-topics-memory 0.16.1-next.1 → 0.18.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.
- package/README.md +4 -4
- package/README.zh.md +3 -3
- package/icon.svg +6 -0
- package/lib/commands.js +29 -0
- package/lib/config.d.ts +55 -55
- package/lib/config.js +26 -26
- package/lib/distill.d.ts +8 -0
- package/lib/distill.js +1 -1
- package/lib/index.d.ts +131 -32
- package/lib/index.js +295 -162
- package/lib/store.d.ts +13 -0
- package/lib/store.js +25 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +30 -27
- package/skills/dsh-topics-memory-config/SKILL.md +6 -6
- package/src/commands.ts +26 -0
- package/src/config.ts +26 -26
- package/src/distill.ts +15 -1
- package/src/index.ts +376 -182
- package/src/store.ts +25 -0
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwayds/dsh-topics-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "dsh-plugin: quality-first topic memory for dsh — a topic keeps the starting question, conclusion, impact and dependencies, not the process; local-first git-traced OKF bundle, budgeted LLM-free injection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|
|
8
|
+
"icon": "./icon.svg",
|
|
8
9
|
"license": "MIT",
|
|
9
10
|
"keywords": [
|
|
10
11
|
"dsh",
|
|
@@ -33,25 +34,27 @@
|
|
|
33
34
|
"lib",
|
|
34
35
|
"cordis.patch.yml",
|
|
35
36
|
"src",
|
|
36
|
-
"skills"
|
|
37
|
+
"skills",
|
|
38
|
+
"icon.svg",
|
|
39
|
+
"locale/*.json"
|
|
37
40
|
],
|
|
38
41
|
"scripts": {
|
|
39
42
|
"build": "tsc -p tsconfig.json",
|
|
40
43
|
"check": "tsc --noEmit -p tsconfig.json",
|
|
41
44
|
"pretest": "tsc -p tsconfig.json",
|
|
42
|
-
"test": "node --test test/yaml.test.mjs test/okf.test.mjs test/retrieval.test.mjs test/digest.test.mjs test/ilog.test.mjs test/store.test.mjs test/paths.test.mjs test/git.test.mjs test/sync.test.mjs test/tools.test.mjs test/dedup.test.mjs test/commands.test.mjs test/onboard.test.mjs test/delegation.test.mjs test/observer.test.mjs test/distill.test.mjs test/consolidate.test.mjs test/viz.test.mjs test/corpus.test.mjs test/llm-scope.test.mjs test/
|
|
45
|
+
"test": "node --test test/yaml.test.mjs test/okf.test.mjs test/retrieval.test.mjs test/digest.test.mjs test/ilog.test.mjs test/store.test.mjs test/paths.test.mjs test/git.test.mjs test/sync.test.mjs test/tools.test.mjs test/dedup.test.mjs test/commands.test.mjs test/onboard.test.mjs test/delegation.test.mjs test/observer.test.mjs test/distill.test.mjs test/consolidate.test.mjs test/viz.test.mjs test/corpus.test.mjs test/llm-scope.test.mjs test/quality.test.mjs test/replay-gate.test.mjs test/depends-repair.test.mjs test/skill.test.mjs test/fast-lane.test.mjs",
|
|
43
46
|
"smoke": "node scripts/smoke-boot.mjs"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {},
|
|
46
49
|
"peerDependencies": {
|
|
47
50
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
48
|
-
"@deepseek-ai/dsh-tools": ">=0.1.
|
|
49
|
-
"@deepseek-ai/dsh-llm": ">=0.1.
|
|
50
|
-
"@deepseek-ai/dsh-settings": ">=0.1.
|
|
51
|
-
"@deepseek-ai/dsh-skill": ">=0.1.
|
|
52
|
-
"@deepseek-ai/dsh-commands": ">=0.1.
|
|
53
|
-
"@deepseek-ai/dsh-util-values": ">=0.1.
|
|
54
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
51
|
+
"@deepseek-ai/dsh-tools": ">=0.1.7-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-llm": ">=0.1.7-rc.1",
|
|
53
|
+
"@deepseek-ai/dsh-settings": ">=0.1.7-rc.1",
|
|
54
|
+
"@deepseek-ai/dsh-skill": ">=0.1.7-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-commands": ">=0.1.7-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-util-values": ">=0.1.7-rc.1",
|
|
57
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
55
58
|
},
|
|
56
59
|
"peerDependenciesMeta": {
|
|
57
60
|
"@deepseek-ai/cordis": {
|
|
@@ -81,23 +84,23 @@
|
|
|
81
84
|
},
|
|
82
85
|
"devDependencies": {
|
|
83
86
|
"@deepseek-ai/cordis": "4.0.2",
|
|
84
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
85
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
86
|
-
"@deepseek-ai/dsh-brand": "0.1.
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
93
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
94
|
-
"@deepseek-ai/dsh-skill": "0.1.
|
|
95
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.
|
|
96
|
-
"@deepseek-ai/dsh-timeout": "0.1.
|
|
97
|
-
"@deepseek-ai/dsh-tools": "0.1.
|
|
98
|
-
"@deepseek-ai/dsh-user-approval": "0.1.
|
|
99
|
-
"@deepseek-ai/dsh-util-values": "0.1.
|
|
100
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
87
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-brand": "0.1.7-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-commands": "0.1.7-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-invariants": "0.1.7-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-scope": "0.1.7-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-sandbox": "0.1.7-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
97
|
+
"@deepseek-ai/dsh-skill": "0.1.7-rc.1",
|
|
98
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
|
|
99
|
+
"@deepseek-ai/dsh-timeout": "0.1.7-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.1",
|
|
101
|
+
"@deepseek-ai/dsh-user-approval": "0.1.7-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-rc.1",
|
|
103
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
101
104
|
"@types/node": "^22.19.0",
|
|
102
105
|
"typescript": "^5.9.2"
|
|
103
106
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dsh-topics-memory-config
|
|
3
|
-
description: "dsh 记忆插件(@aiwayds/dsh-topics-memory)使用与配置指南。凡涉及 dsh 记忆/话题库/GitHub 同步/蒸馏/整理,或要配置 topics
|
|
3
|
+
description: "dsh 记忆插件(@aiwayds/dsh-topics-memory)使用与配置指南。凡涉及 dsh 记忆/话题库/GitHub 同步/蒸馏/整理,或要配置 topics 时先读本指南:设置页 `dsh-topics-memory` 条目全部键(repo/autoInject/topK/注入预算/蒸馏/整理/观察/图游走等)、/topics 命令族(onboard/status/distill/consolidate/stats/list/show/history/graph/sync/config/set)、首次配置 ask_user_question 向导(local-only 或绑 GitHub 仓、蒸馏模型路由、注入档位、自动观察)、注入形态 pointer/digest。触发词:topics、记忆、topic、蒸馏、distill、整理、consolidate、合并重复、deprecatedTtl、usageBoost、使用加成、autoInject、记忆库、include-subagents。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# dsh-topics-memory 使用指南(工作记忆 / 蒸馏 / 注入)
|
|
@@ -9,9 +9,9 @@ description: "dsh 记忆插件(@aiwayds/dsh-topics-memory)使用与配置指
|
|
|
9
9
|
> 可选同步 GitHub 私有仓跨机共享。记忆是编辑出来的不是攒出来的——一个 topic 只记四件事
|
|
10
10
|
> (起始问题、结论、影响、依赖),过程属于 session,结束就随它去。
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## 配置入口(设置页 `dsh-topics-memory` 条目)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
0.1.7+ 宿主在设置页编辑 `dsh-topics-memory` 条目(全部字段免重启热更);`/topics set` 写同一处,profile patch 持久化:
|
|
15
15
|
|
|
16
16
|
| 键 | 默认 | 作用 |
|
|
17
17
|
|---|---|---|
|
|
@@ -95,6 +95,6 @@ GitHub 模式凭据走 `$GITHUB_TOKEN` 或已登录的 gh CLI(`gh auth status`
|
|
|
95
95
|
4. **GitHub 同步**:repo 须形如 owner/name;推送有 push-debounce-seconds 去抖;退出只做
|
|
96
96
|
本地 commit 不等网络,推迟的 push 由下次启动 pull 后补推;rebase 冲突的 topic 降权标记,
|
|
97
97
|
`/topics status` 可查。
|
|
98
|
-
5.
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
5. **旧版数据目录迁移**:0.5.x 的 `~/.dsh/llmwiki` 数据目录在首次启动时自动迁移到
|
|
99
|
+
`~/.dsh/topics`,无需手工。旧版 `llmwiki:` 设置段不再自动迁移(0.1.7 宿主一次性导入
|
|
100
|
+
settings.yaml 后改名 settings.yaml.imported);如有残留旧值,在设置页手工搬入。
|
package/src/commands.ts
CHANGED
|
@@ -149,6 +149,32 @@ async function renderStatus(service: TopicsService): Promise<string> {
|
|
|
149
149
|
const gc = typeof lastRun.gcDropped === 'number' && lastRun.gcDropped > 0 ? `,GC 回收 ${lastRun.gcDropped}` : ''
|
|
150
150
|
rows.push(['最近蒸馏', `${outcome}${gc} @ ${cell(String(lastRun.at ?? '').slice(0, 19).replace('T', ' '))}`])
|
|
151
151
|
}
|
|
152
|
+
// Fast-lane heartbeat (0.17.0): pair "observer lane recorded recently"
|
|
153
|
+
// with "no injection round for >48h" — exactly the asymmetry a silent
|
|
154
|
+
// fast-lane death produces (the 0.1.5-rc.2 claim-order incident shape).
|
|
155
|
+
// With autoInject off there is no fast lane to be stale.
|
|
156
|
+
if (cfg.autoInject) {
|
|
157
|
+
const HOUR = 3_600_000
|
|
158
|
+
const hb = await service.store.readLaneHeartbeat()
|
|
159
|
+
const stamp = (iso: string): string => cell(iso.slice(0, 19).replace('T', ' '))
|
|
160
|
+
const ago = (ms: number): string => {
|
|
161
|
+
const h = ms / HOUR
|
|
162
|
+
if (!Number.isFinite(h)) return ''
|
|
163
|
+
return h < 1 ? `${Math.max(1, Math.round(h * 60))} 分钟前` : h < 48 ? `${Math.round(h)}h 前` : `${Math.round(h / 24)} 天前`
|
|
164
|
+
}
|
|
165
|
+
const lastAt = hb.lastInjectionAt
|
|
166
|
+
const fastIdleMs = lastAt !== undefined ? Date.now() - Date.parse(lastAt) : Number.POSITIVE_INFINITY
|
|
167
|
+
const obsAgeMs = hb.lastObservationAt !== undefined ? Date.now() - Date.parse(hb.lastObservationAt) : Number.POSITIVE_INFINITY
|
|
168
|
+
const obsRecent = obsAgeMs < 24 * HOUR
|
|
169
|
+
if (lastAt === undefined) {
|
|
170
|
+
rows.push(['快道心跳', obsRecent ? `⚠️ 从未注入,但观察 lane ${ago(obsAgeMs)}仍在记录——快道疑似静默死亡` : '从未注入'])
|
|
171
|
+
} else if (fastIdleMs > 48 * HOUR && obsRecent) {
|
|
172
|
+
rows.push(['快道心跳', `⚠️ ${stamp(lastAt)}(${ago(fastIdleMs)})无注入轮次,而观察 lane ${ago(obsAgeMs)}仍在记录——快道疑似静默死亡`])
|
|
173
|
+
} else {
|
|
174
|
+
const since = ago(fastIdleMs)
|
|
175
|
+
rows.push(['快道心跳', `${stamp(lastAt)}${since === '' ? '' : `(${since})`}`])
|
|
176
|
+
}
|
|
177
|
+
}
|
|
152
178
|
const lastConsolidate = await service.store.readConsolidateState()
|
|
153
179
|
if (lastConsolidate !== undefined) {
|
|
154
180
|
const count = (v: unknown): number => (Array.isArray(v) ? v.length : 0)
|
package/src/config.ts
CHANGED
|
@@ -9,71 +9,71 @@ import z from '@deepseek-ai/schemastery'
|
|
|
9
9
|
|
|
10
10
|
export const TopicsConfig = z.object({
|
|
11
11
|
/** GitHub repo `owner/name`; empty = local-only mode (ADR 0008). */
|
|
12
|
-
repo: z.string().default(''),
|
|
12
|
+
repo: z.string().default('').volatile(),
|
|
13
13
|
/** Master switch for per-turn injection. */
|
|
14
|
-
autoInject: z.boolean().default(true),
|
|
14
|
+
autoInject: z.boolean().default(true).volatile(),
|
|
15
15
|
/** Skip re-injecting topics already injected earlier in the same session. */
|
|
16
|
-
injectDedup: z.boolean().default(true),
|
|
16
|
+
injectDedup: z.boolean().default(true).volatile(),
|
|
17
17
|
/** Skip re-injecting topics distilled from the CURRENT session's own turns
|
|
18
18
|
* (蒸馏回声): the conversation already carries that knowledge, so a
|
|
19
19
|
* pointer is a stale echo at best (2026-09 audit: 3 of 6 useless rounds).
|
|
20
20
|
* Provenance rides the observations log (sessionId → distilledInto). */
|
|
21
|
-
suppressEcho: z.boolean().default(true),
|
|
21
|
+
suppressEcho: z.boolean().default(true).volatile(),
|
|
22
22
|
/** Max topics injected per round (ADR 0006: ≤4). */
|
|
23
|
-
topK: z.number().default(4),
|
|
23
|
+
topK: z.number().default(4).volatile(),
|
|
24
24
|
/** Per-topic digest budget in tokens. */
|
|
25
|
-
perTopicBudget: z.number().default(300),
|
|
25
|
+
perTopicBudget: z.number().default(300).volatile(),
|
|
26
26
|
/** Total injection budget in tokens. */
|
|
27
|
-
totalBudget: z.number().default(1500),
|
|
27
|
+
totalBudget: z.number().default(1500).volatile(),
|
|
28
28
|
/** Retrieval score threshold — tune via /topics stats near-miss evidence. */
|
|
29
|
-
matchThreshold: z.number().default(0.3),
|
|
29
|
+
matchThreshold: z.number().default(0.3).volatile(),
|
|
30
30
|
/** Additive boost per tag hit (v4: total cap = this value, was ×3). */
|
|
31
|
-
tagBoost: z.number().default(0.15),
|
|
31
|
+
tagBoost: z.number().default(0.15).volatile(),
|
|
32
32
|
/** Injection shape: pointer (default, ≤600 tok) keeps the legacy digest view. */
|
|
33
|
-
injectMode: z.string().default('pointer'),
|
|
33
|
+
injectMode: z.string().default('pointer').volatile(),
|
|
34
34
|
/** Slow quality lane (v4 §4.2): off | sampled (1/3 of turns) | always. */
|
|
35
|
-
qualityLane: z.string().default('sampled'),
|
|
35
|
+
qualityLane: z.string().default('sampled').volatile(),
|
|
36
36
|
/** depends-graph expansion depth (0 disables). */
|
|
37
|
-
graphDepth: z.number().default(2),
|
|
37
|
+
graphDepth: z.number().default(2).volatile(),
|
|
38
38
|
/** Days within which a topic counts as recent (+0.2). */
|
|
39
|
-
recencyWindowDays: z.number().default(7),
|
|
39
|
+
recencyWindowDays: z.number().default(7).volatile(),
|
|
40
40
|
/** Capture each turn's user/assistant text as raw observations (M2). */
|
|
41
|
-
autoObserve: z.boolean().default(true),
|
|
41
|
+
autoObserve: z.boolean().default(true).volatile(),
|
|
42
42
|
/** Whether injection and observation also engage delegated subagent sessions (ADR 0011).
|
|
43
43
|
* v4 default flipped to false: one-shot subagent turns diluted the pool.
|
|
44
44
|
* The slow quality lane never runs for subagents either way (hard guard). */
|
|
45
|
-
includeSubagents: z.boolean().default(false),
|
|
45
|
+
includeSubagents: z.boolean().default(false).volatile(),
|
|
46
46
|
/** Max auto-captured chars per side (user/assistant) per turn. */
|
|
47
|
-
observationMaxChars: z.number().default(2000),
|
|
47
|
+
observationMaxChars: z.number().default(2000).volatile(),
|
|
48
48
|
/** Background distill cadence: every N turns of a long session. */
|
|
49
|
-
distillEveryTurns: z.number().default(5),
|
|
49
|
+
distillEveryTurns: z.number().default(5).volatile(),
|
|
50
50
|
/** Distill once when a session ends. */
|
|
51
|
-
distillOnSessionEnd: z.boolean().default(true),
|
|
51
|
+
distillOnSessionEnd: z.boolean().default(true).volatile(),
|
|
52
52
|
/** Distill lane model route; both must be set, else distill stays idle. */
|
|
53
|
-
distillProvider: z.string().default(''),
|
|
54
|
-
distillModel: z.string().default(''),
|
|
53
|
+
distillProvider: z.string().default('').volatile(),
|
|
54
|
+
distillModel: z.string().default('').volatile(),
|
|
55
55
|
/** Observations per distill model call; auto-halves on output-limit failures (floor 5). */
|
|
56
|
-
distillBatchSize: z.number().default(40),
|
|
56
|
+
distillBatchSize: z.number().default(40).volatile(),
|
|
57
57
|
/** Max model calls per distill run — successful batches keep their marks (partial progress).
|
|
58
58
|
* Default 8: with the batch loop, one run then drains ~30-40 observations
|
|
59
59
|
* instead of ~10, which is what makes a real backlog actually shrink. */
|
|
60
|
-
distillMaxModelCalls: z.number().default(8),
|
|
60
|
+
distillMaxModelCalls: z.number().default(8).volatile(),
|
|
61
61
|
/** Consolidation lane (整理) cadence: how often a session start may run the
|
|
62
62
|
* LLM gardener over the EXISTING pool (merge near-duplicates, promote
|
|
63
63
|
* settled drafts, deprecate superseded, refresh metadata). Reuses the
|
|
64
64
|
* distill model route; off disables the lane entirely. */
|
|
65
|
-
consolidateCadence: z.string().default('daily'),
|
|
65
|
+
consolidateCadence: z.string().default('daily').volatile(),
|
|
66
66
|
/** Deprecated topics older than this many days are dropped at session start
|
|
67
67
|
* (local TTL sweep, no model; each drop is its own git commit, so history
|
|
68
68
|
* stays recoverable on the remote). 0 disables the sweep. */
|
|
69
|
-
deprecatedTtlDays: z.number().default(15),
|
|
69
|
+
deprecatedTtlDays: z.number().default(15).volatile(),
|
|
70
70
|
/** UsageBoost (ADR 0015): behavioral bonus for topics injected/opened in
|
|
71
71
|
* the last 30 days, folded into the gate score — capped at 0.2 and never
|
|
72
72
|
* granted to zero-lexical candidates; the structural gate still applies.
|
|
73
73
|
* 0 = off. */
|
|
74
|
-
usageBoost: z.number().default(0.15),
|
|
74
|
+
usageBoost: z.number().default(0.15).volatile(),
|
|
75
75
|
/** Debounced push delay in GitHub mode. */
|
|
76
|
-
pushDebounceSeconds: z.number().default(45),
|
|
76
|
+
pushDebounceSeconds: z.number().default(45).volatile(),
|
|
77
77
|
})
|
|
78
78
|
|
|
79
79
|
export type TopicsConfigValue = {
|
package/src/distill.ts
CHANGED
|
@@ -13,6 +13,20 @@
|
|
|
13
13
|
import * as okf from './okf.ts'
|
|
14
14
|
import type { TopicsService } from './service.ts'
|
|
15
15
|
|
|
16
|
+
// dsh 0.1.7 removed the shared catch-all 'plugin' message-source kind:
|
|
17
|
+
// MessageSourceMap is now a merge-extensible sum type and every producer
|
|
18
|
+
// declares its own kind in its own module (dsh-skill: 'skill-invocation',
|
|
19
|
+
// dsh-agent: 'model-selection', …). This plugin's distill call therefore
|
|
20
|
+
// registers 'topics-memory' — consumers fall through unknown kinds by
|
|
21
|
+
// contract, so hosts without the augmentation still render the message.
|
|
22
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
23
|
+
interface MessageSourceMap {
|
|
24
|
+
/** Producer context contributed by the dsh-topics-memory plugin. */
|
|
25
|
+
'topics-memory': { kind: 'topics-memory' }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
16
30
|
export interface ModelRequest {
|
|
17
31
|
system: string
|
|
18
32
|
user: string
|
|
@@ -838,7 +852,7 @@ export function defaultModelCaller(
|
|
|
838
852
|
const messages = [
|
|
839
853
|
createUserMessage({
|
|
840
854
|
content: [{ type: 'text', text: req.user }],
|
|
841
|
-
source: { kind: '
|
|
855
|
+
source: { kind: 'topics-memory' },
|
|
842
856
|
}),
|
|
843
857
|
]
|
|
844
858
|
const options = deepFreeze({
|