@aiwayds/dsh-topics-memory 0.17.1 → 0.18.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/README.md +5 -5
- package/README.zh.md +4 -4
- package/icon.svg +6 -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 +67 -36
- package/lib/index.js +169 -155
- package/lib/legacy-import.d.ts +94 -0
- package/lib/legacy-import.js +224 -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/config.ts +26 -26
- package/src/distill.ts +15 -1
- package/src/index.ts +182 -171
- package/src/legacy-import.ts +267 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
|
|
|
4
4
|
|
|
5
5
|
A dsh plugin: maintains "working topic memory" as an [OKF (Open Knowledge Format v0.2)](https://github.com/GoogleCloudPlatform/open-knowledge-format) knowledge bundle, persisted in a local git repository (optionally synced to a private GitHub repo), with conclusions traceable through git history, sessions automatically observed and distilled into knowledge, and relevant topics injected to the model before every turn.
|
|
6
6
|
|
|
7
|
-
> **Requires dsh >= 0.1.
|
|
7
|
+
> **Requires dsh >= 0.1.7-rc.1** — this plugin targets the dsh RC/stable line only (CI and releases resolve the newest of the `latest`/`next` dist-tags at runtime). **The alpha line is no longer supported.**
|
|
8
8
|
|
|
9
9
|
## Demo
|
|
10
10
|
|
|
@@ -90,14 +90,14 @@ The host reconciles the profile automatically: the `dsh.profile.bundles` entry i
|
|
|
90
90
|
What stays on disk (kept on purpose — this is your memory):
|
|
91
91
|
|
|
92
92
|
- `~/.dsh/topics/` — the whole topic bundle: topic markdown, `meta/`, and the embedded `.git` repo (the full history; it may carry an `origin` remote — GitHub sync stops with the plugin). To archive the bundle elsewhere, copy or clone this directory as-is.
|
|
93
|
-
-
|
|
94
|
-
-
|
|
93
|
+
- The settings-page `dsh-topics-memory` entry (profile patch) — user overrides. Reinstalling silently reactivates sync including any configured `repo`; clear the entry first if you want a clean start.
|
|
94
|
+
- A legacy `llmwiki:` section left inside the old `settings.yaml` (renamed `settings.yaml.imported` after the host's one-shot 0.1.7 import) is never touched again; mine it by hand if something still needs it.
|
|
95
95
|
|
|
96
96
|
Purge everything: back up `~/.dsh/topics` first, then `rm -rf ~/.dsh/topics`.
|
|
97
97
|
|
|
98
98
|
## Configuration
|
|
99
99
|
|
|
100
|
-
First-time setup belongs to `/topics onboard`; day-to-day tuning is `/topics set <key> <value>`
|
|
100
|
+
First-time setup belongs to `/topics onboard`; day-to-day tuning is `/topics set <key> <value>` — on dsh 0.1.7+ it writes the settings-page `dsh-topics-memory` entry (the profile patch) and every key is volatile: edits take effect immediately, no restart. Upgrading from a pre-0.1.7 install: a legacy top-level `topics:` section in the old `settings.yaml` is imported once into the new entry automatically at the next plugin boot (the audit record lands in `~/.dsh/storages/dsh-topics-memory/legacy-import.json`). All keys and defaults:
|
|
101
101
|
|
|
102
102
|
| Key | Default | Meaning |
|
|
103
103
|
|---|---|---|
|
|
@@ -142,7 +142,7 @@ This project's shape is directly inspired and supported by:
|
|
|
142
142
|
- **Subagents are out of memory by default — one switch to opt in**: by default (`include-subagents` off since 0.7.0) delegated sessions are skipped entirely — no injection, no observation, no distill triggers; `/topics set include-subagents on` applies injection and observation to them too. The topic tools stay on the global layer, so an explicit `topic_save` from a child still lands. Out-of-process subagents (claude-code/codex providers) never load this plugin anyway.
|
|
143
143
|
- **Exit is local-only (0.10.0)**: the plugin's disposer makes one local git commit of the meta sidecars (observations / injections / distill state) and never waits on the network — no pull, no push, no model call, so host exit no longer pays a git round-trip or a bounded distill wait (the old 90s cap is gone; only a 10s guard against a pathological git stall remains). The exit distill trigger is fired fire-and-forget and is skipped entirely while a session-end run is still in flight (the same pool head would otherwise be fed to the model twice). Nothing is lost by skipping: observations are write-through on disk, the deferred push is replayed by the next boot's pull, and the skipped distill is replayed there too (boot-replay). `meta/distill-state.json` records each lane's outcome, checkable via `/topics status`.
|
|
144
144
|
- **Observation GC (three strikes)**: an observation the model actually evaluated (parseable answer, however useless) but no op consumed accrues one failed attempt; the third failed attempt physically deletes it — explicitly authorized cleanup of raw data the lane demonstrably cannot process. Runs that never evaluated the batch never count: infrastructure failures (network errors, unconfigured distill route → readable `no-model` short-circuit) and unparseable output (`invalid-output`) are exempt, and a batch still mid-shrink on output-limit retries is only counted once a verdict is reached (success, floor stop, stall, or an explicit skip). Deletions are committed immediately (data destruction stays git-traceable); pure attempt counters follow the usual flush cadence.
|
|
145
|
-
- **Config read timing**: `/topics set` and
|
|
145
|
+
- **Config read timing**: `/topics set` and profile-patch edits take effect most reliably from the next session start (dsh 0.1.7: the settings document is the profile patch; the old `settings.yaml` is imported once and renamed).
|
|
146
146
|
- **Picking a distill model**: `/topics onboard` splits the distill decision into two dependent questions (provider first, then that provider's model catalog), pre-validated with `resolveModelInfo` — a provider with no live route blocks and re-asks, an off-catalog model (a non-NO_ADAPTER failure: outside the advisory catalog, possibly still usable) warns but is allowed; hosts without an ask UI or a usable model route fall back to typed input. The same validation backs the `/topics set` picker panels.
|
|
147
147
|
|
|
148
148
|
## Design docs
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
一个 dsh 插件:把「工作 topic 记忆」维护成 [OKF 标准(Open Knowledge Format v0.2)](https://github.com/GoogleCloudPlatform/open-knowledge-format)的知识 bundle,持久化在本地 git 仓库(可选同步到 GitHub 私有仓库),利用 git 历史提供结论可追溯性,自动观察会话沉淀知识,并在每轮对话前向模型注入相关 Topic。
|
|
6
6
|
|
|
7
|
-
> **要求 dsh >= 0.1.
|
|
7
|
+
> **要求 dsh >= 0.1.7-rc.1** — 本插件只跟随 dsh RC/stable 线(CI 与发版在运行时解析 latest/next 中更新的 dist-tag)。**不再支持 alpha 线。**
|
|
8
8
|
|
|
9
9
|
## 演示
|
|
10
10
|
|
|
@@ -90,14 +90,14 @@ dsh plugin --profile <name> remove @aiwayds/dsh-topics-memory
|
|
|
90
90
|
以下内容有意保留在磁盘上(这是你的记忆本体):
|
|
91
91
|
|
|
92
92
|
- `~/.dsh/topics/` —— 整个话题包:话题 markdown、`meta/`、以及内嵌的 `.git` 仓库(完整历史;可能带有 `origin` 远端 —— GitHub 同步随插件停止)。要异地归档就原样拷贝/克隆这个目录。
|
|
93
|
-
-
|
|
93
|
+
- 设置页 `dsh-topics-memory` 条目(profile patch)—— 用户覆盖项。注意:重装会静默恢复同步(包括已配置的 `repo`);想干净重来就先清空该条目。
|
|
94
94
|
- 0.5.x → 0.6.x 迁移留下的 `llmwiki:` 旧设置段不会被自动删除;确认无用后手动移除。
|
|
95
95
|
|
|
96
96
|
彻底清除:先备份 `~/.dsh/topics`,再 `rm -rf ~/.dsh/topics`。
|
|
97
97
|
|
|
98
98
|
## 配置
|
|
99
99
|
|
|
100
|
-
首次配置交给 `/topics onboard`;日常微调用 `/topics set <key> <value
|
|
100
|
+
首次配置交给 `/topics onboard`;日常微调用 `/topics set <key> <value>`——0.1.7+ 宿主上写设置页 `dsh-topics-memory` 条目(profile patch),全部键 volatile 免重启即时生效。从 0.1.7 之前升级:旧 `settings.yaml` 里的 `topics:` 段会在下次插件 boot 时一次性自动迁入新条目(审计档在 `~/.dsh/storages/dsh-topics-memory/legacy-import.json`)。全部键与默认值:
|
|
101
101
|
|
|
102
102
|
| 键 | 默认 | 说明 |
|
|
103
103
|
|---|---|---|
|
|
@@ -142,7 +142,7 @@ dsh plugin --profile <name> remove @aiwayds/dsh-topics-memory
|
|
|
142
142
|
- **子代理默认不参与记忆,可整体打开**:默认(`include-subagents` off,0.7.0 起)delegation depth > 0 的子代理会话被整体跳过——不注入、不观察、不触发蒸馏;`/topics set include-subagents on` 后注入与观察同样作用于子代理会话。topic 工具始终在全局层(子代理显式 `topic_save` 不受开关影响)。跨进程子代理(claude-code/codex 等 provider)本就不加载本插件。
|
|
143
143
|
- **退出路径本地化(0.10.0)**:插件 disposer 只做一步本地 git commit(meta 侧车文件:observations / injections / distill state),不再等待任何网络——没有 pull、没有 push、没有模型调用,宿主退出不再支付 git 双程与有界蒸馏等待(旧的 90s 上限移除,仅保留 10s 兜底以防病理性 git 卡死)。退出蒸馏触发改为 fire-and-forget;已有 session-end run 在跑时整体跳过(否则同一全局队头批次会被双份喂给模型)。跳过不丢活:observations 本就 write-through 落盘,推迟的 push 由下次启动的 pull 补推,被跳过的蒸馏同样由下次启动回放(boot-replay)。`meta/distill-state.json` 记录每次 lane 的结局,`/topics status` 可查。
|
|
144
144
|
- **观察 GC(三振删除)**:被模型实际评估(返回了可解析应答,无论内容有无价值)却未被任何 op 消费的观察记一次 failed attempt,连续 3 次即物理删除(用户已明确授权删除 lane 确实无法处理的原始观察)。模型从未评估过的批次永不计数:基础设施失败(网络错误、蒸馏路由未配置 → 可读 `no-model` 短路)与输出不可解析(`invalid-output`)豁免;输出上限减半重试中的批次只有到达裁决(成功 / 触底 / stalled / 明确跳过)才计一次。删除立即 commit(数据销毁 git 可追溯),纯计数沿用 flush 节奏。
|
|
145
|
-
- **配置读取时机**:`/topics set` 与 settings.yaml
|
|
145
|
+
- **配置读取时机**:`/topics set` 与 profile patch 修改在下次会话启动后生效最稳(dsh 0.1.7 起 settings 文档即 profile patch;旧 settings.yaml 导入一次后改名)。
|
|
146
146
|
- **蒸馏选模型**:`/topics onboard` 的蒸馏一步拆成两问(先 provider 后 model,模型列表取自该 provider 的目录),选完经 `resolveModelInfo` 预校验——provider 无活路由会阻断重选,模型目录校验非 NO_ADAPTER 失败(目录外,可能仍可用)则警告但放行;无 ask UI 或无可用模型路由的环境自动退回文本输入。`/topics set` 的选择面板走同一套校验。
|
|
147
147
|
|
|
148
148
|
## 设计文档
|
package/icon.svg
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<circle cx="6.5" cy="6" r="2.2"/>
|
|
3
|
+
<circle cx="17.5" cy="7.5" r="2.2"/>
|
|
4
|
+
<circle cx="12" cy="17.5" r="2.2"/>
|
|
5
|
+
<path d="M8.7 6.5 15.3 7M16.3 9.6 13.5 15.6M9.5 8.1l1.7 7.3"/>
|
|
6
|
+
</svg>
|
package/lib/config.d.ts
CHANGED
|
@@ -5,141 +5,141 @@
|
|
|
5
5
|
* @module config
|
|
6
6
|
*/
|
|
7
7
|
import z from '@deepseek-ai/schemastery';
|
|
8
|
-
export declare const TopicsConfig: z<Schemastery.ObjectS<{
|
|
8
|
+
export declare const TopicsConfig: z<Schemastery.ObjectS<NoInfer<{
|
|
9
9
|
/** GitHub repo `owner/name`; empty = local-only mode (ADR 0008). */
|
|
10
|
-
repo: z<string, string>;
|
|
10
|
+
repo: z<string, string, "volatile-defined">;
|
|
11
11
|
/** Master switch for per-turn injection. */
|
|
12
|
-
autoInject: z<boolean, boolean>;
|
|
12
|
+
autoInject: z<boolean, boolean, "volatile-defined">;
|
|
13
13
|
/** Skip re-injecting topics already injected earlier in the same session. */
|
|
14
|
-
injectDedup: z<boolean, boolean>;
|
|
14
|
+
injectDedup: z<boolean, boolean, "volatile-defined">;
|
|
15
15
|
/** Skip re-injecting topics distilled from the CURRENT session's own turns
|
|
16
16
|
* (蒸馏回声): the conversation already carries that knowledge, so a
|
|
17
17
|
* pointer is a stale echo at best (2026-09 audit: 3 of 6 useless rounds).
|
|
18
18
|
* Provenance rides the observations log (sessionId → distilledInto). */
|
|
19
|
-
suppressEcho: z<boolean, boolean>;
|
|
19
|
+
suppressEcho: z<boolean, boolean, "volatile-defined">;
|
|
20
20
|
/** Max topics injected per round (ADR 0006: ≤4). */
|
|
21
|
-
topK: z<number, number>;
|
|
21
|
+
topK: z<number, number, "volatile-defined">;
|
|
22
22
|
/** Per-topic digest budget in tokens. */
|
|
23
|
-
perTopicBudget: z<number, number>;
|
|
23
|
+
perTopicBudget: z<number, number, "volatile-defined">;
|
|
24
24
|
/** Total injection budget in tokens. */
|
|
25
|
-
totalBudget: z<number, number>;
|
|
25
|
+
totalBudget: z<number, number, "volatile-defined">;
|
|
26
26
|
/** Retrieval score threshold — tune via /topics stats near-miss evidence. */
|
|
27
|
-
matchThreshold: z<number, number>;
|
|
27
|
+
matchThreshold: z<number, number, "volatile-defined">;
|
|
28
28
|
/** Additive boost per tag hit (v4: total cap = this value, was ×3). */
|
|
29
|
-
tagBoost: z<number, number>;
|
|
29
|
+
tagBoost: z<number, number, "volatile-defined">;
|
|
30
30
|
/** Injection shape: pointer (default, ≤600 tok) keeps the legacy digest view. */
|
|
31
|
-
injectMode: z<string, string>;
|
|
31
|
+
injectMode: z<string, string, "volatile-defined">;
|
|
32
32
|
/** Slow quality lane (v4 §4.2): off | sampled (1/3 of turns) | always. */
|
|
33
|
-
qualityLane: z<string, string>;
|
|
33
|
+
qualityLane: z<string, string, "volatile-defined">;
|
|
34
34
|
/** depends-graph expansion depth (0 disables). */
|
|
35
|
-
graphDepth: z<number, number>;
|
|
35
|
+
graphDepth: z<number, number, "volatile-defined">;
|
|
36
36
|
/** Days within which a topic counts as recent (+0.2). */
|
|
37
|
-
recencyWindowDays: z<number, number>;
|
|
37
|
+
recencyWindowDays: z<number, number, "volatile-defined">;
|
|
38
38
|
/** Capture each turn's user/assistant text as raw observations (M2). */
|
|
39
|
-
autoObserve: z<boolean, boolean>;
|
|
39
|
+
autoObserve: z<boolean, boolean, "volatile-defined">;
|
|
40
40
|
/** Whether injection and observation also engage delegated subagent sessions (ADR 0011).
|
|
41
41
|
* v4 default flipped to false: one-shot subagent turns diluted the pool.
|
|
42
42
|
* The slow quality lane never runs for subagents either way (hard guard). */
|
|
43
|
-
includeSubagents: z<boolean, boolean>;
|
|
43
|
+
includeSubagents: z<boolean, boolean, "volatile-defined">;
|
|
44
44
|
/** Max auto-captured chars per side (user/assistant) per turn. */
|
|
45
|
-
observationMaxChars: z<number, number>;
|
|
45
|
+
observationMaxChars: z<number, number, "volatile-defined">;
|
|
46
46
|
/** Background distill cadence: every N turns of a long session. */
|
|
47
|
-
distillEveryTurns: z<number, number>;
|
|
47
|
+
distillEveryTurns: z<number, number, "volatile-defined">;
|
|
48
48
|
/** Distill once when a session ends. */
|
|
49
|
-
distillOnSessionEnd: z<boolean, boolean>;
|
|
49
|
+
distillOnSessionEnd: z<boolean, boolean, "volatile-defined">;
|
|
50
50
|
/** Distill lane model route; both must be set, else distill stays idle. */
|
|
51
|
-
distillProvider: z<string, string>;
|
|
52
|
-
distillModel: z<string, string>;
|
|
51
|
+
distillProvider: z<string, string, "volatile-defined">;
|
|
52
|
+
distillModel: z<string, string, "volatile-defined">;
|
|
53
53
|
/** Observations per distill model call; auto-halves on output-limit failures (floor 5). */
|
|
54
|
-
distillBatchSize: z<number, number>;
|
|
54
|
+
distillBatchSize: z<number, number, "volatile-defined">;
|
|
55
55
|
/** Max model calls per distill run — successful batches keep their marks (partial progress).
|
|
56
56
|
* Default 8: with the batch loop, one run then drains ~30-40 observations
|
|
57
57
|
* instead of ~10, which is what makes a real backlog actually shrink. */
|
|
58
|
-
distillMaxModelCalls: z<number, number>;
|
|
58
|
+
distillMaxModelCalls: z<number, number, "volatile-defined">;
|
|
59
59
|
/** Consolidation lane (整理) cadence: how often a session start may run the
|
|
60
60
|
* LLM gardener over the EXISTING pool (merge near-duplicates, promote
|
|
61
61
|
* settled drafts, deprecate superseded, refresh metadata). Reuses the
|
|
62
62
|
* distill model route; off disables the lane entirely. */
|
|
63
|
-
consolidateCadence: z<string, string>;
|
|
63
|
+
consolidateCadence: z<string, string, "volatile-defined">;
|
|
64
64
|
/** Deprecated topics older than this many days are dropped at session start
|
|
65
65
|
* (local TTL sweep, no model; each drop is its own git commit, so history
|
|
66
66
|
* stays recoverable on the remote). 0 disables the sweep. */
|
|
67
|
-
deprecatedTtlDays: z<number, number>;
|
|
67
|
+
deprecatedTtlDays: z<number, number, "volatile-defined">;
|
|
68
68
|
/** UsageBoost (ADR 0015): behavioral bonus for topics injected/opened in
|
|
69
69
|
* the last 30 days, folded into the gate score — capped at 0.2 and never
|
|
70
70
|
* granted to zero-lexical candidates; the structural gate still applies.
|
|
71
71
|
* 0 = off. */
|
|
72
|
-
usageBoost: z<number, number>;
|
|
72
|
+
usageBoost: z<number, number, "volatile-defined">;
|
|
73
73
|
/** Debounced push delay in GitHub mode. */
|
|
74
|
-
pushDebounceSeconds: z<number, number>;
|
|
75
|
-
}
|
|
74
|
+
pushDebounceSeconds: z<number, number, "volatile-defined">;
|
|
75
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
76
76
|
/** GitHub repo `owner/name`; empty = local-only mode (ADR 0008). */
|
|
77
|
-
repo: z<string, string>;
|
|
77
|
+
repo: z<string, string, "volatile-defined">;
|
|
78
78
|
/** Master switch for per-turn injection. */
|
|
79
|
-
autoInject: z<boolean, boolean>;
|
|
79
|
+
autoInject: z<boolean, boolean, "volatile-defined">;
|
|
80
80
|
/** Skip re-injecting topics already injected earlier in the same session. */
|
|
81
|
-
injectDedup: z<boolean, boolean>;
|
|
81
|
+
injectDedup: z<boolean, boolean, "volatile-defined">;
|
|
82
82
|
/** Skip re-injecting topics distilled from the CURRENT session's own turns
|
|
83
83
|
* (蒸馏回声): the conversation already carries that knowledge, so a
|
|
84
84
|
* pointer is a stale echo at best (2026-09 audit: 3 of 6 useless rounds).
|
|
85
85
|
* Provenance rides the observations log (sessionId → distilledInto). */
|
|
86
|
-
suppressEcho: z<boolean, boolean>;
|
|
86
|
+
suppressEcho: z<boolean, boolean, "volatile-defined">;
|
|
87
87
|
/** Max topics injected per round (ADR 0006: ≤4). */
|
|
88
|
-
topK: z<number, number>;
|
|
88
|
+
topK: z<number, number, "volatile-defined">;
|
|
89
89
|
/** Per-topic digest budget in tokens. */
|
|
90
|
-
perTopicBudget: z<number, number>;
|
|
90
|
+
perTopicBudget: z<number, number, "volatile-defined">;
|
|
91
91
|
/** Total injection budget in tokens. */
|
|
92
|
-
totalBudget: z<number, number>;
|
|
92
|
+
totalBudget: z<number, number, "volatile-defined">;
|
|
93
93
|
/** Retrieval score threshold — tune via /topics stats near-miss evidence. */
|
|
94
|
-
matchThreshold: z<number, number>;
|
|
94
|
+
matchThreshold: z<number, number, "volatile-defined">;
|
|
95
95
|
/** Additive boost per tag hit (v4: total cap = this value, was ×3). */
|
|
96
|
-
tagBoost: z<number, number>;
|
|
96
|
+
tagBoost: z<number, number, "volatile-defined">;
|
|
97
97
|
/** Injection shape: pointer (default, ≤600 tok) keeps the legacy digest view. */
|
|
98
|
-
injectMode: z<string, string>;
|
|
98
|
+
injectMode: z<string, string, "volatile-defined">;
|
|
99
99
|
/** Slow quality lane (v4 §4.2): off | sampled (1/3 of turns) | always. */
|
|
100
|
-
qualityLane: z<string, string>;
|
|
100
|
+
qualityLane: z<string, string, "volatile-defined">;
|
|
101
101
|
/** depends-graph expansion depth (0 disables). */
|
|
102
|
-
graphDepth: z<number, number>;
|
|
102
|
+
graphDepth: z<number, number, "volatile-defined">;
|
|
103
103
|
/** Days within which a topic counts as recent (+0.2). */
|
|
104
|
-
recencyWindowDays: z<number, number>;
|
|
104
|
+
recencyWindowDays: z<number, number, "volatile-defined">;
|
|
105
105
|
/** Capture each turn's user/assistant text as raw observations (M2). */
|
|
106
|
-
autoObserve: z<boolean, boolean>;
|
|
106
|
+
autoObserve: z<boolean, boolean, "volatile-defined">;
|
|
107
107
|
/** Whether injection and observation also engage delegated subagent sessions (ADR 0011).
|
|
108
108
|
* v4 default flipped to false: one-shot subagent turns diluted the pool.
|
|
109
109
|
* The slow quality lane never runs for subagents either way (hard guard). */
|
|
110
|
-
includeSubagents: z<boolean, boolean>;
|
|
110
|
+
includeSubagents: z<boolean, boolean, "volatile-defined">;
|
|
111
111
|
/** Max auto-captured chars per side (user/assistant) per turn. */
|
|
112
|
-
observationMaxChars: z<number, number>;
|
|
112
|
+
observationMaxChars: z<number, number, "volatile-defined">;
|
|
113
113
|
/** Background distill cadence: every N turns of a long session. */
|
|
114
|
-
distillEveryTurns: z<number, number>;
|
|
114
|
+
distillEveryTurns: z<number, number, "volatile-defined">;
|
|
115
115
|
/** Distill once when a session ends. */
|
|
116
|
-
distillOnSessionEnd: z<boolean, boolean>;
|
|
116
|
+
distillOnSessionEnd: z<boolean, boolean, "volatile-defined">;
|
|
117
117
|
/** Distill lane model route; both must be set, else distill stays idle. */
|
|
118
|
-
distillProvider: z<string, string>;
|
|
119
|
-
distillModel: z<string, string>;
|
|
118
|
+
distillProvider: z<string, string, "volatile-defined">;
|
|
119
|
+
distillModel: z<string, string, "volatile-defined">;
|
|
120
120
|
/** Observations per distill model call; auto-halves on output-limit failures (floor 5). */
|
|
121
|
-
distillBatchSize: z<number, number>;
|
|
121
|
+
distillBatchSize: z<number, number, "volatile-defined">;
|
|
122
122
|
/** Max model calls per distill run — successful batches keep their marks (partial progress).
|
|
123
123
|
* Default 8: with the batch loop, one run then drains ~30-40 observations
|
|
124
124
|
* instead of ~10, which is what makes a real backlog actually shrink. */
|
|
125
|
-
distillMaxModelCalls: z<number, number>;
|
|
125
|
+
distillMaxModelCalls: z<number, number, "volatile-defined">;
|
|
126
126
|
/** Consolidation lane (整理) cadence: how often a session start may run the
|
|
127
127
|
* LLM gardener over the EXISTING pool (merge near-duplicates, promote
|
|
128
128
|
* settled drafts, deprecate superseded, refresh metadata). Reuses the
|
|
129
129
|
* distill model route; off disables the lane entirely. */
|
|
130
|
-
consolidateCadence: z<string, string>;
|
|
130
|
+
consolidateCadence: z<string, string, "volatile-defined">;
|
|
131
131
|
/** Deprecated topics older than this many days are dropped at session start
|
|
132
132
|
* (local TTL sweep, no model; each drop is its own git commit, so history
|
|
133
133
|
* stays recoverable on the remote). 0 disables the sweep. */
|
|
134
|
-
deprecatedTtlDays: z<number, number>;
|
|
134
|
+
deprecatedTtlDays: z<number, number, "volatile-defined">;
|
|
135
135
|
/** UsageBoost (ADR 0015): behavioral bonus for topics injected/opened in
|
|
136
136
|
* the last 30 days, folded into the gate score — capped at 0.2 and never
|
|
137
137
|
* granted to zero-lexical candidates; the structural gate still applies.
|
|
138
138
|
* 0 = off. */
|
|
139
|
-
usageBoost: z<number, number>;
|
|
139
|
+
usageBoost: z<number, number, "volatile-defined">;
|
|
140
140
|
/** Debounced push delay in GitHub mode. */
|
|
141
|
-
pushDebounceSeconds: z<number, number>;
|
|
142
|
-
}
|
|
141
|
+
pushDebounceSeconds: z<number, number, "volatile-defined">;
|
|
142
|
+
}>>, "plain">;
|
|
143
143
|
export type TopicsConfigValue = {
|
|
144
144
|
repo: string;
|
|
145
145
|
autoInject: boolean;
|
package/lib/config.js
CHANGED
|
@@ -7,71 +7,71 @@
|
|
|
7
7
|
import z from '@deepseek-ai/schemastery';
|
|
8
8
|
export const TopicsConfig = z.object({
|
|
9
9
|
/** GitHub repo `owner/name`; empty = local-only mode (ADR 0008). */
|
|
10
|
-
repo: z.string().default(''),
|
|
10
|
+
repo: z.string().default('').volatile(),
|
|
11
11
|
/** Master switch for per-turn injection. */
|
|
12
|
-
autoInject: z.boolean().default(true),
|
|
12
|
+
autoInject: z.boolean().default(true).volatile(),
|
|
13
13
|
/** Skip re-injecting topics already injected earlier in the same session. */
|
|
14
|
-
injectDedup: z.boolean().default(true),
|
|
14
|
+
injectDedup: z.boolean().default(true).volatile(),
|
|
15
15
|
/** Skip re-injecting topics distilled from the CURRENT session's own turns
|
|
16
16
|
* (蒸馏回声): the conversation already carries that knowledge, so a
|
|
17
17
|
* pointer is a stale echo at best (2026-09 audit: 3 of 6 useless rounds).
|
|
18
18
|
* Provenance rides the observations log (sessionId → distilledInto). */
|
|
19
|
-
suppressEcho: z.boolean().default(true),
|
|
19
|
+
suppressEcho: z.boolean().default(true).volatile(),
|
|
20
20
|
/** Max topics injected per round (ADR 0006: ≤4). */
|
|
21
|
-
topK: z.number().default(4),
|
|
21
|
+
topK: z.number().default(4).volatile(),
|
|
22
22
|
/** Per-topic digest budget in tokens. */
|
|
23
|
-
perTopicBudget: z.number().default(300),
|
|
23
|
+
perTopicBudget: z.number().default(300).volatile(),
|
|
24
24
|
/** Total injection budget in tokens. */
|
|
25
|
-
totalBudget: z.number().default(1500),
|
|
25
|
+
totalBudget: z.number().default(1500).volatile(),
|
|
26
26
|
/** Retrieval score threshold — tune via /topics stats near-miss evidence. */
|
|
27
|
-
matchThreshold: z.number().default(0.3),
|
|
27
|
+
matchThreshold: z.number().default(0.3).volatile(),
|
|
28
28
|
/** Additive boost per tag hit (v4: total cap = this value, was ×3). */
|
|
29
|
-
tagBoost: z.number().default(0.15),
|
|
29
|
+
tagBoost: z.number().default(0.15).volatile(),
|
|
30
30
|
/** Injection shape: pointer (default, ≤600 tok) keeps the legacy digest view. */
|
|
31
|
-
injectMode: z.string().default('pointer'),
|
|
31
|
+
injectMode: z.string().default('pointer').volatile(),
|
|
32
32
|
/** Slow quality lane (v4 §4.2): off | sampled (1/3 of turns) | always. */
|
|
33
|
-
qualityLane: z.string().default('sampled'),
|
|
33
|
+
qualityLane: z.string().default('sampled').volatile(),
|
|
34
34
|
/** depends-graph expansion depth (0 disables). */
|
|
35
|
-
graphDepth: z.number().default(2),
|
|
35
|
+
graphDepth: z.number().default(2).volatile(),
|
|
36
36
|
/** Days within which a topic counts as recent (+0.2). */
|
|
37
|
-
recencyWindowDays: z.number().default(7),
|
|
37
|
+
recencyWindowDays: z.number().default(7).volatile(),
|
|
38
38
|
/** Capture each turn's user/assistant text as raw observations (M2). */
|
|
39
|
-
autoObserve: z.boolean().default(true),
|
|
39
|
+
autoObserve: z.boolean().default(true).volatile(),
|
|
40
40
|
/** Whether injection and observation also engage delegated subagent sessions (ADR 0011).
|
|
41
41
|
* v4 default flipped to false: one-shot subagent turns diluted the pool.
|
|
42
42
|
* The slow quality lane never runs for subagents either way (hard guard). */
|
|
43
|
-
includeSubagents: z.boolean().default(false),
|
|
43
|
+
includeSubagents: z.boolean().default(false).volatile(),
|
|
44
44
|
/** Max auto-captured chars per side (user/assistant) per turn. */
|
|
45
|
-
observationMaxChars: z.number().default(2000),
|
|
45
|
+
observationMaxChars: z.number().default(2000).volatile(),
|
|
46
46
|
/** Background distill cadence: every N turns of a long session. */
|
|
47
|
-
distillEveryTurns: z.number().default(5),
|
|
47
|
+
distillEveryTurns: z.number().default(5).volatile(),
|
|
48
48
|
/** Distill once when a session ends. */
|
|
49
|
-
distillOnSessionEnd: z.boolean().default(true),
|
|
49
|
+
distillOnSessionEnd: z.boolean().default(true).volatile(),
|
|
50
50
|
/** Distill lane model route; both must be set, else distill stays idle. */
|
|
51
|
-
distillProvider: z.string().default(''),
|
|
52
|
-
distillModel: z.string().default(''),
|
|
51
|
+
distillProvider: z.string().default('').volatile(),
|
|
52
|
+
distillModel: z.string().default('').volatile(),
|
|
53
53
|
/** Observations per distill model call; auto-halves on output-limit failures (floor 5). */
|
|
54
|
-
distillBatchSize: z.number().default(40),
|
|
54
|
+
distillBatchSize: z.number().default(40).volatile(),
|
|
55
55
|
/** Max model calls per distill run — successful batches keep their marks (partial progress).
|
|
56
56
|
* Default 8: with the batch loop, one run then drains ~30-40 observations
|
|
57
57
|
* instead of ~10, which is what makes a real backlog actually shrink. */
|
|
58
|
-
distillMaxModelCalls: z.number().default(8),
|
|
58
|
+
distillMaxModelCalls: z.number().default(8).volatile(),
|
|
59
59
|
/** Consolidation lane (整理) cadence: how often a session start may run the
|
|
60
60
|
* LLM gardener over the EXISTING pool (merge near-duplicates, promote
|
|
61
61
|
* settled drafts, deprecate superseded, refresh metadata). Reuses the
|
|
62
62
|
* distill model route; off disables the lane entirely. */
|
|
63
|
-
consolidateCadence: z.string().default('daily'),
|
|
63
|
+
consolidateCadence: z.string().default('daily').volatile(),
|
|
64
64
|
/** Deprecated topics older than this many days are dropped at session start
|
|
65
65
|
* (local TTL sweep, no model; each drop is its own git commit, so history
|
|
66
66
|
* stays recoverable on the remote). 0 disables the sweep. */
|
|
67
|
-
deprecatedTtlDays: z.number().default(15),
|
|
67
|
+
deprecatedTtlDays: z.number().default(15).volatile(),
|
|
68
68
|
/** UsageBoost (ADR 0015): behavioral bonus for topics injected/opened in
|
|
69
69
|
* the last 30 days, folded into the gate score — capped at 0.2 and never
|
|
70
70
|
* granted to zero-lexical candidates; the structural gate still applies.
|
|
71
71
|
* 0 = off. */
|
|
72
|
-
usageBoost: z.number().default(0.15),
|
|
72
|
+
usageBoost: z.number().default(0.15).volatile(),
|
|
73
73
|
/** Debounced push delay in GitHub mode. */
|
|
74
|
-
pushDebounceSeconds: z.number().default(45),
|
|
74
|
+
pushDebounceSeconds: z.number().default(45).volatile(),
|
|
75
75
|
});
|
|
76
76
|
export const CONFIG_KEYS = [
|
|
77
77
|
'repo',
|
package/lib/distill.d.ts
CHANGED
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
* @module distill
|
|
11
11
|
*/
|
|
12
12
|
import type { TopicsService } from './service.ts';
|
|
13
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
14
|
+
interface MessageSourceMap {
|
|
15
|
+
/** Producer context contributed by the dsh-topics-memory plugin. */
|
|
16
|
+
'topics-memory': {
|
|
17
|
+
kind: 'topics-memory';
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
export interface ModelRequest {
|
|
14
22
|
system: string;
|
|
15
23
|
user: string;
|
package/lib/distill.js
CHANGED
|
@@ -710,7 +710,7 @@ export function defaultModelCaller(getCandidates, getRoute) {
|
|
|
710
710
|
const messages = [
|
|
711
711
|
createUserMessage({
|
|
712
712
|
content: [{ type: 'text', text: req.user }],
|
|
713
|
-
source: { kind: '
|
|
713
|
+
source: { kind: 'topics-memory' },
|
|
714
714
|
}),
|
|
715
715
|
];
|
|
716
716
|
const options = deepFreeze({
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import { type TopicsConfigValue } from './config.ts';
|
|
2
3
|
export declare const name = "dsh-topics-memory";
|
|
3
4
|
/**
|
|
4
5
|
* Bounded wait for the process-exit meta commit. The exit path is
|
|
@@ -85,42 +86,72 @@ export declare function resolveClaimedText(input: {
|
|
|
85
86
|
start: number;
|
|
86
87
|
removedCount: number;
|
|
87
88
|
}): ClaimedTextResolution;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
89
|
+
export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
90
|
+
repo: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
91
|
+
autoInject: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
92
|
+
injectDedup: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
93
|
+
suppressEcho: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
94
|
+
topK: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
95
|
+
perTopicBudget: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
96
|
+
totalBudget: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
97
|
+
matchThreshold: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
98
|
+
tagBoost: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
99
|
+
injectMode: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
100
|
+
qualityLane: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
101
|
+
graphDepth: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
102
|
+
recencyWindowDays: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
103
|
+
autoObserve: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
104
|
+
includeSubagents: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
105
|
+
observationMaxChars: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
106
|
+
distillEveryTurns: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
107
|
+
distillOnSessionEnd: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
108
|
+
distillProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
109
|
+
distillModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
110
|
+
distillBatchSize: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
111
|
+
distillMaxModelCalls: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
112
|
+
consolidateCadence: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
113
|
+
deprecatedTtlDays: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
114
|
+
usageBoost: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
115
|
+
pushDebounceSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
116
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
117
|
+
repo: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
118
|
+
autoInject: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
119
|
+
injectDedup: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
120
|
+
suppressEcho: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
121
|
+
topK: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
122
|
+
perTopicBudget: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
123
|
+
totalBudget: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
124
|
+
matchThreshold: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
125
|
+
tagBoost: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
126
|
+
injectMode: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
127
|
+
qualityLane: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
128
|
+
graphDepth: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
129
|
+
recencyWindowDays: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
130
|
+
autoObserve: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
131
|
+
includeSubagents: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
132
|
+
observationMaxChars: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
133
|
+
distillEveryTurns: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
134
|
+
distillOnSessionEnd: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
135
|
+
distillProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
136
|
+
distillModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
137
|
+
distillBatchSize: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
138
|
+
distillMaxModelCalls: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
139
|
+
consolidateCadence: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
140
|
+
deprecatedTtlDays: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
141
|
+
usageBoost: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
142
|
+
pushDebounceSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
143
|
+
}>>, "plain">;
|
|
144
|
+
/** Live config reference as a 0.1.7 host passes it (cron / mcp-adapter
|
|
145
|
+
* spelling): `.get()` snapshots the current value — the host swaps the
|
|
146
|
+
* reference in-place when a volatile field is edited on the settings page. */
|
|
147
|
+
interface VolatileRef<T> {
|
|
148
|
+
get(): T;
|
|
107
149
|
}
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* because scope.get() folds the defaults in;
|
|
114
|
-
* - idempotent: after the copy (or any config written on the new name) the
|
|
115
|
-
* override check short-circuits every later boot;
|
|
116
|
-
* - fail-open: registering the legacy namespace can throw (duplicate or
|
|
117
|
-
* schema-invalid stored section) and the write can reject — both contained,
|
|
118
|
-
* so plugin startup never depends on the migration.
|
|
119
|
-
* Keys whose stored value equals the schema default are not copied (nothing
|
|
120
|
-
* user-tuned to preserve). Returns the in-flight write for tests; apply()
|
|
121
|
-
* fire-and-forgets it.
|
|
122
|
-
*/
|
|
123
|
-
export declare function migrateLegacySettings(settings: SettingsNsLike, warn?: (message: string) => void): Promise<void> | undefined;
|
|
150
|
+
/** apply()-time config: live VolatileRef fields on real hosts, plain values
|
|
151
|
+
* on bare test harnesses — {@link readConfigValue} accepts both. */
|
|
152
|
+
export type TopicsRuntimeConfig = Partial<{
|
|
153
|
+
[K in keyof TopicsConfigValue]: VolatileRef<TopicsConfigValue[K]> | TopicsConfigValue[K];
|
|
154
|
+
}>;
|
|
124
155
|
/**
|
|
125
156
|
* Strip a leading YAML frontmatter block (`---` / body / `---`) from a skill
|
|
126
157
|
* markdown file. `SkillDefinition.content` must be the instruction body after
|
|
@@ -132,5 +163,5 @@ export declare function migrateLegacySettings(settings: SettingsNsLike, warn?: (
|
|
|
132
163
|
* delimiter semantics of the upstream skill-filesystem provider.
|
|
133
164
|
*/
|
|
134
165
|
export declare function stripFrontmatter(raw: string): string;
|
|
135
|
-
export declare function apply(ctx: Context): void
|
|
166
|
+
export declare function apply(ctx: Context, config?: TopicsRuntimeConfig): Promise<void>;
|
|
136
167
|
export {};
|