@aiwayds/dsh-topics-memory 0.17.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/lib/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  * dsh-topics-memory — OKF topic memory for DeepSeek Harness.
3
3
  *
4
4
  * Wiring (per ADR 0001–0008):
5
- * - `topics` settings namespace (user-tunable via /topics set)
5
+ * - settings page = projection of the plugin `Config` schema, namespace =
6
+ * profile entry id `dsh-topics-memory` (user-tunable via /topics set)
6
7
  * - static systemPrompt section teaching the topic tools (never volatile
7
8
  * content — the provider cache prefix stays byte-stable)
8
9
  * - same-turn injection: retrieval runs synchronously at inbox-claim time
@@ -154,68 +155,20 @@ export function resolveClaimedText(input) {
154
155
  source: 'projection',
155
156
  };
156
157
  }
157
- // dsh-settings 0.1.2-alpha.3 removed the runtime settingsNamespace() helper:
158
- // register() now brand-checks the namespace at the type level
159
- // (SettingsNamespaceInput) and validates the same lowercase-hyphenated
160
- // pattern at runtime via parseSettingsNamespace. A plain literal is the
161
- // supported spelling (same adaptation as dsh-cron / dsh-model-sync).
162
- const OWN_NS = 'topics';
163
- /** Pre-rename settings namespace (dsh-llmwiki-memory ≤ 0.5.x). The plugin
164
- * itself never writes to it, but while the migration window is open the
165
- * registration shows up in config UIs like any other namespace (ADR 0013). */
166
- const LEGACY_NS = 'llmwiki';
167
- /**
168
- * One-time carry-over of user-tuned values from the pre-rename `llmwiki`
169
- * settings namespace into `topics` (ADR 0013). Constraints:
170
- * - one-time: only fires while the new namespace holds NO raw user overrides —
171
- * describe().user is the only way to tell user keys from schema defaults,
172
- * because scope.get() folds the defaults in;
173
- * - idempotent: after the copy (or any config written on the new name) the
174
- * override check short-circuits every later boot;
175
- * - fail-open: registering the legacy namespace can throw (duplicate or
176
- * schema-invalid stored section) and the write can reject — both contained,
177
- * so plugin startup never depends on the migration.
178
- * Keys whose stored value equals the schema default are not copied (nothing
179
- * user-tuned to preserve). Returns the in-flight write for tests; apply()
180
- * fire-and-forgets it.
181
- */
182
- export function migrateLegacySettings(settings, warn) {
183
- try {
184
- const describe = settings.describe?.bind(settings);
185
- if (describe === undefined)
186
- return undefined;
187
- const findUser = (ns) => {
188
- const user = describe().find((entry) => entry.ns === ns)?.user;
189
- return user !== undefined && user !== null && typeof user === 'object' ? user : undefined;
190
- };
191
- // Already migrated / user-configured on the new name — never touch it.
192
- if (findUser(OWN_NS) !== undefined)
193
- return undefined;
194
- // Old-plugin coexistence (ADR 0013): `llmwiki` already registered means
195
- // the pre-rename dsh-llmwiki-memory 0.5.x is still loaded in this host.
196
- // Our register below would hit the duplicate guard, migration would
197
- // silently skip, and both plugins would drift onto separate bundles
198
- // (split brain). Warn loudly instead of registering blind.
199
- if (describe().some((entry) => entry.ns === LEGACY_NS)) {
200
- warn?.('dsh-topics-memory: the legacy dsh-llmwiki-memory (0.5.x) is still running in this host — ' +
201
- 'settings migration was skipped because the "llmwiki" namespace is already registered. ' +
202
- 'Remove @aiwayds/dsh-llmwiki-memory from this profile so only dsh-topics-memory loads.');
203
- return undefined;
204
- }
205
- // Registering the legacy namespace is the only API that surfaces its
206
- // stored section; a duplicate or schema-invalid section throws — contained.
207
- settings.register(LEGACY_NS, TopicsConfig);
208
- const legacyUser = findUser(LEGACY_NS);
209
- if (legacyUser === undefined)
210
- return undefined;
211
- const ops = CONFIG_KEYS.filter((k) => k in legacyUser && legacyUser[k] !== DEFAULTS[k]).map((k) => ({ op: 'set', path: [k], value: legacyUser[k] }));
212
- if (ops.length === 0)
213
- return undefined;
214
- return settings.mutate?.call(settings, OWN_NS, ops)?.catch(() => undefined);
215
- }
216
- catch {
217
- return undefined;
218
- }
158
+ // dsh 0.1.7 settings: the runtime namespace registry (register/describe per
159
+ // plugin) is gone. A plugin's settings page is the projection of its `Config`
160
+ // schema below, and the settings namespace is the profile entry id
161
+ // (`dsh-topics-memory`, cordis.patch.yml) — the same spelling /topics set
162
+ // passes to ctx.settings.mutate(). A legacy top-level `topics:` section in
163
+ // settings.yaml is NOT auto-imported under this id — the host imports the
164
+ // old settings.yaml once, then renames it to settings.yaml.imported.
165
+ export const Config = TopicsConfig;
166
+ /** Settings namespace this plugin reads and writes: the profile entry id. */
167
+ const ENTRY_ID = 'dsh-topics-memory';
168
+ /** Snapshot one config slot: live ref → current value, plain value → itself. */
169
+ function readConfigValue(slot) {
170
+ const ref = slot;
171
+ return typeof ref?.get === 'function' ? ref.get() : slot;
219
172
  }
220
173
  // --- Bundled skill -----------------------------------------------------------
221
174
  /** Provider name under `ctx.skills`; doubles as the skill name. */
@@ -229,7 +182,7 @@ const SKILL_RESOURCE_BASE = {
229
182
  };
230
183
  const SKILL_INVOCATION = { modelInvocable: true, userInvocable: true };
231
184
  /** Routing description; must stay identical to the SKILL.md frontmatter (asserted in tests). */
232
- const SKILL_DESCRIPTION = 'dsh 记忆插件(@aiwayds/dsh-topics-memory)使用与配置指南。凡涉及 dsh 记忆/话题库/GitHub 同步/蒸馏/整理,或要配置 topics 段时先读本指南:settings.yaml 顶层 `topics:` 段全部键(repo/autoInject/topK/注入预算/蒸馏/整理/观察/图游走等)、/topics 命令族(onboard/status/distill/consolidate/stats/list/show/history/graph/sync/config/set)、首次配置 ask_user_question 向导(local-only 或绑 GitHub 仓、蒸馏模型路由、注入档位、自动观察)、注入形态 pointer/digest、legacy llmwiki 段自动迁移。触发词:topics、记忆、topic、蒸馏、distill、整理、consolidate、合并重复、deprecatedTtl、usageBoost、使用加成、autoInject、记忆库、llmwiki、include-subagents。';
185
+ const SKILL_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。';
233
186
  const SKILL_CANDIDATE = {
234
187
  name: SKILL_PROVIDER_NAME,
235
188
  description: SKILL_DESCRIPTION,
@@ -282,19 +235,16 @@ export function stripFrontmatter(raw) {
282
235
  }
283
236
  return raw;
284
237
  }
285
- export function apply(ctx) {
238
+ export function apply(ctx, config = {}) {
286
239
  // `inject = ['skills']` guarantees the service exists on every real host;
287
240
  // register unconditionally so a missing service fails loud instead of
288
241
  // silently dropping the bundled skill.
289
242
  ctx.skills.registerProvider(() => skillProvider);
290
- // dsh-settings is part of every real host closure but is a runtime-optional
291
- // peer so bare test harnesses can still load this module.
292
- const settingsNs = ctx.settings;
293
- if (settingsNs === undefined)
294
- return;
295
- const scope = settingsNs.register(OWN_NS, TopicsConfig);
243
+ // 0.1.7 hosts validate the profile patch against `Config` above and hand
244
+ // apply() live volatile references; bare harnesses pass plain values (or
245
+ // nothing) — readConfigValue treats both shapes the same.
296
246
  // Host logger access is best-effort: cordis always provides one, but bare
297
- // test harnesses may not — the migration stays silent rather than throwing.
247
+ // test harnesses may not — the logging stays silent rather than throwing.
298
248
  const warn = (message) => {
299
249
  try {
300
250
  const logger = ctx.logger;
@@ -305,10 +255,6 @@ export function apply(ctx) {
305
255
  // contained — a missing/broken logger must not break startup
306
256
  }
307
257
  };
308
- // Carry user-tuned values over from the pre-rename namespace (one-time,
309
- // fail-open, idempotent — see migrateLegacySettings). Fire-and-forget: the
310
- // write settles on its own; a rejection only skips the migration.
311
- void migrateLegacySettings(settingsNs, warn);
312
258
  // Low-noise observability for the fast lane's early exits. The 2026-09-21
313
259
  // silent-death incident (0.1.5-rc.2 claim order) died precisely because
314
260
  // every exit path was silent: the injected log stayed empty for weeks
@@ -326,7 +272,14 @@ export function apply(ctx) {
326
272
  }
327
273
  };
328
274
  const cfgNow = () => {
329
- const v = scope.get();
275
+ const v = {};
276
+ for (const k of CONFIG_KEYS) {
277
+ const slot = config[k];
278
+ // Skip absent keys so the DEFAULTS fold below keeps them (an explicit
279
+ // undefined spread would wipe the default).
280
+ if (slot !== undefined)
281
+ v[k] = readConfigValue(slot);
282
+ }
330
283
  // Schema defaults may not be applied by bare test harnesses; fill them in.
331
284
  return { ...DEFAULTS, ...v };
332
285
  };
@@ -616,6 +569,11 @@ export function apply(ctx) {
616
569
  let logEvents;
617
570
  if (typeof seq === 'number') {
618
571
  try {
572
+ // Soft-deprecated surface, deliberately kept (dsh 0.17 migration):
573
+ // `snapshotEvents` still ships in dsh-session 0.1.7-rc.1 and this is
574
+ // the replay's data source, reachable only behind event.seq with a
575
+ // full projection-read fallback below. Migrating to registered
576
+ // SessionMessageProjections is a dedicated project, not a drive-by.
619
577
  const snapshot = session.snapshotEvents;
620
578
  if (typeof snapshot === 'function')
621
579
  logEvents = snapshot.call(session, 0, seq);
@@ -668,10 +626,6 @@ export function apply(ctx) {
668
626
  if (event.type === 'turn/end')
669
627
  dispatchSlowLane(sessionId);
670
628
  }));
671
- // Session-start boot chain dedup: rc.2's creation transaction emits BOTH
672
- // `agent/session-start` and `agent/created`, so the pair collapses to one
673
- // chain run per session; teardown clears the entry.
674
- const bootedSessions = new Set();
675
629
  // ---- Session teardown: real cordis events, not session/event types ----
676
630
  // dsh-session 0.1.2-alpha.4's SessionEventMap has no `agent/disposed` or
677
631
  // `session/disposed` event type — the old `event.type === 'agent/disposed'`
@@ -696,7 +650,6 @@ export function apply(ctx) {
696
650
  observer.onSessionEvent(sessionId, name, undefined);
697
651
  injectedBySession.delete(sessionId);
698
652
  slowLane.clear(sessionId);
699
- bootedSessions.delete(sessionId);
700
653
  // A teardown-triggered run (session-end distill) reads the payload
701
654
  // capture lazily: drop the entry here only when no run can still read
702
655
  // it. The slow lane's in-flight pipeline reads it too (shared caller);
@@ -704,91 +657,128 @@ export function apply(ctx) {
704
657
  releaseSessionLlm(sessionId);
705
658
  }));
706
659
  }
707
- // ---- Sync lifecycle: pull on session start, local commit on dispose ----
660
+ // ---- Boot chain: pull on session start, replay-distill, consolidate, TTL ----
708
661
  // Session start is an agent-bus event, never a session/event firehose type:
709
- // the firehose only carries Session.append types. Tarball-verified emission
710
- // sites: `agent/session-start` (sync, payload { agent, source }) is emitted
711
- // by dsh-agent-loop up to 0.1.5; 0.1.6-alpha.1 merges the startup
712
- // notification into the serial `agent/created` announcement (payload
713
- // { agent, source, signal? }) and drops the old name. So one registration
714
- // per name — a host that never emits a name simply never calls that
715
- // listener. Only { agent } is structural; the rest is defensive.
662
+ // the firehose only carries Session.append types. dsh 0.1.7 announces ONE
663
+ // serial `agent/created` event (payload { agent, source, signal? }, source
664
+ // ∈ 'startup' | 'resume' | 'clear' | 'compact') and `agent/session-start`
665
+ // is gone with no shim — 0.16.1's dual-name registration collapses to a
666
+ // single listener, and with it the per-session dedup Set: the registry
667
+ // guarantees exactly one announce per agent entry (a second announce
668
+ // throws), so a 'clear'/'compact' re-creation legitimately re-runs the
669
+ // chain on its fresh entry and teardown has nothing to re-arm. Only
670
+ // { agent } is structural; the rest is defensive.
671
+ //
672
+ // BOOT GUARANTEE (dsh 0.1.7): the serial dispatch awaits every
673
+ // `agent/created` listener and a throw or rejection ROLLS THE ENTIRE AGENT
674
+ // CREATION BACK. This listener is therefore total and synchronous — it
675
+ // never throws, never returns a promise — and the chain is deferred off
676
+ // the creation transaction entirely (setImmediate, unref'd): the pull /
677
+ // replay-distill / consolidation slow work starts only after the serial
678
+ // dispatch has moved on. Plugin initialization must never block, delay,
679
+ // or abort session startup; a chain failure at most warns.
716
680
  const runBootChain = (sessionId) => {
717
- void sync
718
- .pull()
719
- .catch(() => undefined)
720
- .finally(() => store.ensure().catch(() => undefined))
721
- .then(() => {
722
- // Boot replay: the observations JSONL is the durable distill queue.
723
- // Whatever the previous exit skipped (local-only exit since the
724
- // no-network exit commit) or a killed run left unmarked is still
725
- // undistilled here. Empty pool and no-model are cheap no-ops inside
726
- // the distiller; the per-session dedup guards double runs.
727
- void store
728
- .undistilledObservations(1)
729
- .then((pending) => {
730
- if (pending.length === 0)
731
- return;
732
- // Same trigger-time capture the observer callback does: the
733
- // replay run's caller resolves candidates lazily and a fresh
734
- // session has no prior capture to lean on.
681
+ // Contained at the top too: a synchronous prologue throw (config read,
682
+ // sync bookkeeping) must degrade to a warn, not leak toward the host.
683
+ try {
684
+ void sync
685
+ .pull()
686
+ .catch((error) => {
687
+ // The pull failing must stay non-fatal for the chain (replay and
688
+ // TTL still run on the local pool), but no longer silent — a dead
689
+ // sync route should leave a host-log trace.
690
+ warn(`dsh-topics-memory 启动同步 pull 失败(继续本地启动链):${error instanceof Error ? error.message : String(error)}`);
691
+ })
692
+ .finally(() => store.ensure().catch(() => undefined))
693
+ .then(() => {
694
+ // Boot replay: the observations JSONL is the durable distill queue.
695
+ // Whatever the previous exit skipped (local-only exit since the
696
+ // no-network exit commit) or a killed run left unmarked is still
697
+ // undistilled here. Empty pool and no-model are cheap no-ops inside
698
+ // the distiller; the per-session dedup guards double runs.
699
+ void store
700
+ .undistilledObservations(1)
701
+ .then((pending) => {
702
+ if (pending.length === 0)
703
+ return;
704
+ // Same trigger-time capture the observer callback does: the
705
+ // replay run's caller resolves candidates lazily and a fresh
706
+ // session has no prior capture to lean on.
707
+ try {
708
+ captureFromAgent(agents()?.get(sessionId));
709
+ }
710
+ catch {
711
+ // contained — the run below fails with the readable
712
+ // no-adapter detail instead
713
+ }
714
+ const run = distiller.request(sessionId, 'boot-replay');
715
+ if (run !== undefined)
716
+ void run.catch(() => undefined);
717
+ })
718
+ .catch(() => undefined);
719
+ // Consolidation cadence check — after the pull (freshest pool),
720
+ // fully fire-and-forget: cadence/single-flight gates live inside
721
+ // maybeRun, and a no-model boot just skips (no stamp advanced).
722
+ // Failures are NOT silent at the host log: the 2026-09-09 real-host
723
+ // test showed a dead distill route would otherwise hide here with
724
+ // zero observable trace (the state file only advances on success).
735
725
  try {
736
726
  captureFromAgent(agents()?.get(sessionId));
737
727
  }
738
728
  catch {
739
- // contained — the run below fails with the readable
740
- // no-adapter detail instead
729
+ // contained — the run below fails with a readable no-model detail
741
730
  }
742
- const run = distiller.request(sessionId, 'boot-replay');
743
- if (run !== undefined)
744
- void run.catch(() => undefined);
745
- })
746
- .catch(() => undefined);
747
- // Consolidation cadence check — after the pull (freshest pool),
748
- // fully fire-and-forget: cadence/single-flight gates live inside
749
- // maybeRun, and a no-model boot just skips (no stamp advanced).
750
- // Failures are NOT silent at the host log: the 2026-09-09 real-host
751
- // test showed a dead distill route would otherwise hide here with
752
- // zero observable trace (the state file only advances on success).
753
- try {
754
- captureFromAgent(agents()?.get(sessionId));
755
- }
756
- catch {
757
- // contained — the run below fails with a readable no-model detail
758
- }
759
- void consolidator
760
- .maybeRun({ sessionId })
761
- .then((r) => {
762
- if (r !== undefined && !r.ok && r.reason !== 'no-clusters') {
763
- warn(`dsh-topics-memory 整理 lane 未执行:${r.reason ?? 'unknown'}${r.detail !== undefined ? `(${r.detail})` : ''}`);
764
- }
765
- })
766
- .catch(() => undefined);
767
- // Deprecated-TTL sweep — pure local rule, no model, runs even when
768
- // the distill route is unconfigured. Drops are logged: deletion is
769
- // the one housekeeping action the user should always see happened.
770
- const ttl = cfgNow().deprecatedTtlDays;
771
- if (ttl > 0) {
772
- void dropExpiredDeprecated(service, ttl)
773
- .then((dropped) => {
774
- if (dropped.length > 0) {
775
- warn(`dsh-topics-memory:按 ${ttl} 天 TTL 删除了 ${dropped.length} 条 deprecated topic(${dropped.map((s) => `topics/${s}`).join('、')});git 历史可找回`);
731
+ void consolidator
732
+ .maybeRun({ sessionId })
733
+ .then((r) => {
734
+ if (r !== undefined && !r.ok && r.reason !== 'no-clusters') {
735
+ warn(`dsh-topics-memory 整理 lane 未执行:${r.reason ?? 'unknown'}${r.detail !== undefined ? `(${r.detail})` : ''}`);
776
736
  }
777
737
  })
778
738
  .catch(() => undefined);
779
- }
780
- })
781
- .catch(() => undefined);
739
+ // Deprecated-TTL sweep — pure local rule, no model, runs even when
740
+ // the distill route is unconfigured. Drops are logged: deletion is
741
+ // the one housekeeping action the user should always see happened.
742
+ const ttl = cfgNow().deprecatedTtlDays;
743
+ if (ttl > 0) {
744
+ void dropExpiredDeprecated(service, ttl)
745
+ .then((dropped) => {
746
+ if (dropped.length > 0) {
747
+ warn(`dsh-topics-memory:按 ${ttl} 天 TTL 删除了 ${dropped.length} 条 deprecated topic(${dropped.map((s) => `topics/${s}`).join('、')});git 历史可找回`);
748
+ }
749
+ })
750
+ .catch(() => undefined);
751
+ }
752
+ })
753
+ .catch((error) => {
754
+ warn(`dsh-topics-memory 启动链失败(不影响会话启动):${error instanceof Error ? error.message : String(error)}`);
755
+ });
756
+ }
757
+ catch (error) {
758
+ warn(`dsh-topics-memory 启动链无法启动(不影响会话启动):${error instanceof Error ? error.message : String(error)}`);
759
+ }
782
760
  };
783
- for (const name of ['agent/session-start', 'agent/created']) {
784
- ctx.on(name, ((payload) => {
761
+ ctx.on('agent/created', ((payload) => {
762
+ // The boot-fallback contract lives HERE, not inside runBootChain: dsh
763
+ // 0.1.7 rolls agent creation back when this listener throws, so even a
764
+ // hostile/malformed payload (a throwing getter, a proxy trap) degrades to
765
+ // a warn. Returning undefined (never a thenable) keeps the serial
766
+ // dispatch from awaiting anything.
767
+ try {
785
768
  const sessionId = String(payload?.agent?.id ?? '');
786
- if (sessionId === '' || bootedSessions.has(sessionId))
769
+ if (sessionId === '')
787
770
  return;
788
- bootedSessions.add(sessionId);
789
- runBootChain(sessionId);
790
- }));
791
- }
771
+ // Off the creation transaction: the unref'd immediate fires after the
772
+ // current serial dispatch slice, so the chain's synchronous prologue
773
+ // (config read, sync state) never delays the remaining listeners nor
774
+ // the loop start, and the timer cannot hold a dying process open.
775
+ const deferred = setImmediate(() => runBootChain(sessionId));
776
+ deferred.unref?.();
777
+ }
778
+ catch (error) {
779
+ warn(`dsh-topics-memory boot 链触发失败(不影响会话启动):${error instanceof Error ? error.message : String(error)}`);
780
+ }
781
+ }));
792
782
  ctx.effect(() => {
793
783
  void store.ensure().catch(() => undefined);
794
784
  // Async disposer: cordis awaits it during unload (Disposable may be
@@ -821,7 +811,9 @@ export function apply(ctx) {
821
811
  const mutate = async (ops) => {
822
812
  if (settingsMutator?.mutate === undefined)
823
813
  throw new Error('settings 服务不可用,无法写入配置');
824
- await settingsMutator.mutate(OWN_NS, ops);
814
+ // 0.1.7: the settings namespace is the profile entry id, not the old
815
+ // `topics` namespace name.
816
+ await settingsMutator.mutate(ENTRY_ID, ops);
825
817
  };
826
818
  // Resolved lazily at invocation time: whichever UI registered the ask-user
827
819
  // provider (TUI panel / web composer / feishu card, ask-router optional)
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "dsh-topics-memory",
4
+ "description": "Quality-first topic memory: each topic keeps the question, conclusion, impact and dependencies in a local git-traced OKF bundle."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "dsh-topics-memory",
4
+ "description": "质量优先的主题记忆:每个主题留存问题、结论、影响与依赖,落在本地 git 追踪的 OKF 包里,注入零 LLM 开销。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@aiwayds/dsh-topics-memory",
3
- "version": "0.17.1",
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/settings-migration.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",
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.5-rc.2",
49
- "@deepseek-ai/dsh-llm": ">=0.1.5-rc.2",
50
- "@deepseek-ai/dsh-settings": ">=0.1.5-rc.2",
51
- "@deepseek-ai/dsh-skill": ">=0.1.5-rc.2",
52
- "@deepseek-ai/dsh-commands": ">=0.1.5-rc.2",
53
- "@deepseek-ai/dsh-util-values": ">=0.1.5-rc.2",
54
- "@deepseek-ai/schemastery": "^3.18.2"
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.5-rc.2",
85
- "@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
86
- "@deepseek-ai/dsh-brand": "0.1.5-rc.2",
87
- "@deepseek-ai/dsh-code-runtime": "0.1.5-rc.2",
88
- "@deepseek-ai/dsh-commands": "0.1.5-rc.2",
89
- "@deepseek-ai/dsh-invariants": "0.1.5-rc.2",
90
- "@deepseek-ai/dsh-llm": "0.1.5-rc.2",
91
- "@deepseek-ai/dsh-scope": "0.1.5-rc.2",
92
- "@deepseek-ai/dsh-session": "0.1.5-rc.2",
93
- "@deepseek-ai/dsh-settings": "0.1.5-rc.2",
94
- "@deepseek-ai/dsh-skill": "0.1.5-rc.2",
95
- "@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2",
96
- "@deepseek-ai/dsh-timeout": "0.1.5-rc.2",
97
- "@deepseek-ai/dsh-tools": "0.1.5-rc.2",
98
- "@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
99
- "@deepseek-ai/dsh-util-values": "0.1.5-rc.2",
100
- "@deepseek-ai/schemastery": "3.18.2",
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 段时先读本指南:settings.yaml 顶层 `topics:` 段全部键(repo/autoInject/topK/注入预算/蒸馏/整理/观察/图游走等)、/topics 命令族(onboard/status/distill/consolidate/stats/list/show/history/graph/sync/config/set)、首次配置 ask_user_question 向导(local-only 或绑 GitHub 仓、蒸馏模型路由、注入档位、自动观察)、注入形态 pointer/digest、legacy llmwiki 段自动迁移。触发词:topics、记忆、topic、蒸馏、distill、整理、consolidate、合并重复、deprecatedTtl、usageBoost、使用加成、autoInject、记忆库、llmwiki、include-subagents。"
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
- ## 配置入口(settings.yaml 顶层 `topics:` 段)
12
+ ## 配置入口(设置页 `dsh-topics-memory` 条目)
13
13
 
14
- 在 `~/.dsh/settings.yaml` 写顶层 `topics:` 段(只写非默认值即可):
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. **旧版 llmwiki 段自动迁移**:0.5.x 的 `llmwiki:` 设置段与 `~/.dsh/llmwiki` 数据目录在
99
- 首次启动时自动迁移到 `topics` / `~/.dsh/topics`,无需手工;旧插件还在运行时会警告跳过
100
- (防双载脑裂),先从 profile 移除旧包。
98
+ 5. **旧版数据目录迁移**:0.5.x 的 `~/.dsh/llmwiki` 数据目录在首次启动时自动迁移到
99
+ `~/.dsh/topics`,无需手工。旧版 `llmwiki:` 设置段不再自动迁移(0.1.7 宿主一次性导入
100
+ settings.yaml 后改名 settings.yaml.imported);如有残留旧值,在设置页手工搬入。