@modusensus/dsh-mneme 0.7.10 → 0.7.12

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 (158) hide show
  1. package/README.en.md +437 -0
  2. package/README.md +364 -166
  3. package/bin/cli.mjs +603 -0
  4. package/{dsh-mneme/cordis.patch.yml → cordis.patch.yml} +1 -0
  5. package/lib/api-standalone.js +264 -0
  6. package/{dsh-mneme/lib → lib}/api.js +133 -289
  7. package/{dsh-mneme/lib → lib}/client.js +1008 -922
  8. package/{dsh-mneme/lib → lib}/config.js +54 -99
  9. package/{dsh-mneme/lib → lib}/dream/decisions.js +94 -143
  10. package/{dsh-mneme/lib → lib}/dream/sleep.js +12 -152
  11. package/{dsh-mneme/lib → lib}/dream.js +22 -168
  12. package/{dsh-mneme/src → lib}/hot-memory.js +0 -7
  13. package/{dsh-mneme/lib → lib}/index.js +45 -42
  14. package/{dsh-mneme/lib → lib}/inject.js +11 -53
  15. package/{dsh-mneme/src → lib}/mirror.js +1 -12
  16. package/{dsh-mneme/lib → lib}/quality-filter.js +1 -3
  17. package/{dsh-mneme/lib → lib}/reranker.js +1 -4
  18. package/{dsh-mneme/lib → lib}/service.js +20 -410
  19. package/{dsh-mneme/src → lib}/settings.js +34 -64
  20. package/{dsh-mneme/lib → lib}/store.js +38 -450
  21. package/{dsh-mneme/lib → lib}/summarize.js +10 -10
  22. package/lib/tools.js +274 -0
  23. package/package.json +45 -19
  24. package/{dsh-mneme/scripts → scripts}/e2e-dsh.js +3 -4
  25. package/{dsh-mneme/scripts → scripts}/sync-lib.js +5 -10
  26. package/src/api-standalone.js +264 -0
  27. package/{dsh-mneme/src → src}/api.js +133 -289
  28. package/{dsh-mneme/src → src}/config.js +54 -99
  29. package/{dsh-mneme/src → src}/dream/decisions.js +94 -143
  30. package/{dsh-mneme/src → src}/dream/sleep.js +12 -152
  31. package/{dsh-mneme/src → src}/dream.js +22 -168
  32. package/{dsh-mneme/lib → src}/hot-memory.js +0 -7
  33. package/{dsh-mneme/src → src}/index.js +45 -42
  34. package/{dsh-mneme/src → src}/inject.js +11 -53
  35. package/{dsh-mneme/lib → src}/mirror.js +1 -12
  36. package/{dsh-mneme/src → src}/quality-filter.js +1 -3
  37. package/{dsh-mneme/src → src}/reranker.js +1 -4
  38. package/{dsh-mneme/src → src}/service.js +20 -410
  39. package/{dsh-mneme/lib → src}/settings.js +34 -64
  40. package/{dsh-mneme/src → src}/store.js +38 -450
  41. package/{dsh-mneme/src → src}/summarize.js +10 -10
  42. package/src/tools.js +274 -0
  43. package/{dsh-mneme/test → test}/api.test.js +45 -156
  44. package/{dsh-mneme/test → test}/client.test.js +10 -206
  45. package/{dsh-mneme/test → test}/config.test.js +0 -21
  46. package/{dsh-mneme/test → test}/dream.test.js +1 -160
  47. package/{dsh-mneme/test → test}/graph-api.test.js +0 -34
  48. package/{dsh-mneme/test → test}/hot-memory.test.js +0 -29
  49. package/test/inject.test.js +120 -0
  50. package/{dsh-mneme/test → test}/llm-audit.test.js +4 -4
  51. package/{dsh-mneme/test → test}/reasoning-effort.test.js +0 -27
  52. package/{dsh-mneme/test → test}/recall-layer.test.js +5 -26
  53. package/{dsh-mneme/test → test}/reranker.test.js +0 -43
  54. package/{dsh-mneme/test → test}/service-search.test.js +0 -25
  55. package/{dsh-mneme/test → test}/service.test.js +0 -106
  56. package/{dsh-mneme/test → test}/settings.test.js +16 -35
  57. package/{dsh-mneme/test → test}/sleep.test.js +4 -171
  58. package/test/standalone-api.test.js +326 -0
  59. package/{dsh-mneme/test → test}/store.test.js +0 -76
  60. package/{dsh-mneme/test → test}/summarize.test.js +16 -15
  61. package/test/tools.test.js +265 -0
  62. package/.github/workflows/publish.yml +0 -58
  63. package/.github/workflows/test.yml +0 -32
  64. package/CHANGELOG.md +0 -91
  65. package/CONTRIBUTING.md +0 -293
  66. package/SECURITY.md +0 -718
  67. package/docs/devlog/2026-08-14-dsh-mneme-dev-log.md +0 -247
  68. package/docs/devlog/2026-08-15-dsh-mneme-audit-stress-dev-log.md +0 -145
  69. package/docs/devlog/2026-08-15-dsh-mneme-pipeline-dev-log.md +0 -56
  70. package/docs/devlog/2026-08-15-dsh-mneme-reflection-dev-log.md +0 -77
  71. package/docs/devlog/2026-08-15-dsh-mneme-review-fixes-dev-log.md +0 -64
  72. package/docs/devlog/2026-08-15-dsh-mneme-semantic-dev-log.md +0 -90
  73. package/dsh-mneme/CHANGELOG.md +0 -419
  74. package/dsh-mneme/LICENSE +0 -21
  75. package/dsh-mneme/README.md +0 -501
  76. package/dsh-mneme/docs/AGENT_MEMORY_RESEARCH.md +0 -183
  77. package/dsh-mneme/docs/ENTITIES.md +0 -245
  78. package/dsh-mneme/docs/LOCAL_MODEL.md +0 -141
  79. package/dsh-mneme/docs/MIGRATION.md +0 -127
  80. package/dsh-mneme/docs/SEMANTIC.md +0 -256
  81. package/dsh-mneme/docs/SLEEP.md +0 -163
  82. package/dsh-mneme/lib/dream/tag-extractor.js +0 -156
  83. package/dsh-mneme/lib/heat.js +0 -136
  84. package/dsh-mneme/lib/parser/tag.js +0 -59
  85. package/dsh-mneme/lib/parser/wiki-link.js +0 -38
  86. package/dsh-mneme/lib/search/tag-boost.js +0 -61
  87. package/dsh-mneme/lib/tools.js +0 -458
  88. package/dsh-mneme/package-lock.json +0 -1936
  89. package/dsh-mneme/package.json +0 -83
  90. package/dsh-mneme/scripts/check-sync.js +0 -42
  91. package/dsh-mneme/src/client.js +0 -2242
  92. package/dsh-mneme/src/dream/tag-extractor.js +0 -156
  93. package/dsh-mneme/src/heat.js +0 -136
  94. package/dsh-mneme/src/parser/tag.js +0 -59
  95. package/dsh-mneme/src/parser/wiki-link.js +0 -38
  96. package/dsh-mneme/src/search/tag-boost.js +0 -61
  97. package/dsh-mneme/src/tools.js +0 -458
  98. package/dsh-mneme/test/boundary-v0625.test.js +0 -82
  99. package/dsh-mneme/test/directory.test.js +0 -134
  100. package/dsh-mneme/test/heat.test.js +0 -148
  101. package/dsh-mneme/test/inject.test.js +0 -206
  102. package/dsh-mneme/test/layered-types-stats.test.js +0 -144
  103. package/dsh-mneme/test/lib-smoke.test.js +0 -109
  104. package/dsh-mneme/test/normalize-decisions.test.js +0 -120
  105. package/dsh-mneme/test/provenance.test.js +0 -103
  106. package/dsh-mneme/test/recall-runs.test.js +0 -93
  107. package/dsh-mneme/test/sleep-heat.test.js +0 -112
  108. package/dsh-mneme/test/tag-boost.test.js +0 -125
  109. package/dsh-mneme/test/tag.test.js +0 -426
  110. package/dsh-mneme/test/tools.test.js +0 -674
  111. package/dsh-mneme/test/updated-at-semantics.test.js +0 -113
  112. package/dsh-mneme/test/wiki-link.test.js +0 -332
  113. package//346/250/252/345/271/205.png +0 -0
  114. /package/{dsh-mneme/lib → lib}/commands.js +0 -0
  115. /package/{dsh-mneme/lib → lib}/dream/clustering.js +0 -0
  116. /package/{dsh-mneme/lib → lib}/embedding.js +0 -0
  117. /package/{dsh-mneme/lib → lib}/entities/extractor.js +0 -0
  118. /package/{dsh-mneme/lib → lib}/local-embedder.js +0 -0
  119. /package/{dsh-mneme/lib → lib}/search/adaptive.js +0 -0
  120. /package/{dsh-mneme/lib → lib}/search/bm25.js +0 -0
  121. /package/{dsh-mneme/lib → lib}/vector-index.js +0 -0
  122. /package/{dsh-mneme/scripts → scripts}/benchmark-embed.js +0 -0
  123. /package/{dsh-mneme/scripts → scripts}/benchmark-recall.js +0 -0
  124. /package/{dsh-mneme/scripts → scripts}/benchmark-rerank.js +0 -0
  125. /package/{dsh-mneme/scripts → scripts}/stress-dsh.js +0 -0
  126. /package/{dsh-mneme/src → src}/commands.js +0 -0
  127. /package/{dsh-mneme/src → src}/dream/clustering.js +0 -0
  128. /package/{dsh-mneme/src → src}/embedding.js +0 -0
  129. /package/{dsh-mneme/src → src}/entities/extractor.js +0 -0
  130. /package/{dsh-mneme/src → src}/local-embedder.js +0 -0
  131. /package/{dsh-mneme/src → src}/search/adaptive.js +0 -0
  132. /package/{dsh-mneme/src → src}/search/bm25.js +0 -0
  133. /package/{dsh-mneme/src → src}/vector-index.js +0 -0
  134. /package/{dsh-mneme/test → test}/audit.test.js +0 -0
  135. /package/{dsh-mneme/test → test}/benchmark.test.js +0 -0
  136. /package/{dsh-mneme/test → test}/clustering.test.js +0 -0
  137. /package/{dsh-mneme/test → test}/commands.test.js +0 -0
  138. /package/{dsh-mneme/test → test}/conflict-freeze.test.js +0 -0
  139. /package/{dsh-mneme/test → test}/entities.test.js +0 -0
  140. /package/{dsh-mneme/test → test}/epistemic.test.js +0 -0
  141. /package/{dsh-mneme/test → test}/fnew-0112.test.js +0 -0
  142. /package/{dsh-mneme/test → test}/fnew-03.test.js +0 -0
  143. /package/{dsh-mneme/test → test}/helpers/dream-mock.js +0 -0
  144. /package/{dsh-mneme/test → test}/local-embedder.test.js +0 -0
  145. /package/{dsh-mneme/test → test}/mirror-dirty.test.js +0 -0
  146. /package/{dsh-mneme/test → test}/mirror-edit-digest.test.js +0 -0
  147. /package/{dsh-mneme/test → test}/mirror-generation.test.js +0 -0
  148. /package/{dsh-mneme/test → test}/mirror.test.js +0 -0
  149. /package/{dsh-mneme/test → test}/peer-blockers.test.js +0 -0
  150. /package/{dsh-mneme/test → test}/policy-epoch.test.js +0 -0
  151. /package/{dsh-mneme/test → test}/quality-filter.test.js +0 -0
  152. /package/{dsh-mneme/test → test}/recall-evals.test.js +0 -0
  153. /package/{dsh-mneme/test → test}/receipt-chain.test.js +0 -0
  154. /package/{dsh-mneme/test → test}/reflection.test.js +0 -0
  155. /package/{dsh-mneme/test → test}/search-fusion.test.js +0 -0
  156. /package/{dsh-mneme/test → test}/semantic.test.js +0 -0
  157. /package/{dsh-mneme/test → test}/stress.test.js +0 -0
  158. /package/{dsh-mneme/test → test}/vector-index.test.js +0 -0
@@ -7,13 +7,14 @@ import { createSummarizer } from "./summarize.js";
7
7
  import { createDreamScheduler } from "./dream.js";
8
8
  import { createSleepScheduler, runSleep } from "./dream/sleep.js";
9
9
  import { createApi } from "./api.js";
10
+ import { createStandaloneApi } from "./api-standalone.js";
10
11
  import { createSettings } from "./settings.js";
11
12
  import { createCommandManager } from "./commands.js";
12
13
  import { createEmbedder } from "./embedding.js";
13
14
  import { createEmbedderByProvider } from "./local-embedder.js";
14
15
  import { LocalReranker } from "./reranker.js";
15
16
  import { createVectorIndex } from "./vector-index.js";
16
- import { Config } from "./config.js";
17
+ import { Config, applyLightModePreset } from "./config.js";
17
18
  import { extractEntities } from "./entities/extractor.js";
18
19
  import { mkdirSync } from "node:fs";
19
20
  import { join } from "node:path";
@@ -29,12 +30,12 @@ export { Config };
29
30
  // has no prototype, is called normally, and its returned disposer is collected
30
31
  // and run by the fiber on unload.
31
32
  export const apply = (ctx, config) => {
32
- const cfg = Config(config);
33
+ const rawCfg = Config(config);
33
34
 
34
35
  // Resolve memoryDir: expand leading "~"
35
- const memoryDir = cfg.memoryDir.startsWith("~")
36
- ? join(homedir(), cfg.memoryDir.slice(1))
37
- : cfg.memoryDir;
36
+ const memoryDir = rawCfg.memoryDir.startsWith("~")
37
+ ? join(homedir(), rawCfg.memoryDir.slice(1))
38
+ : rawCfg.memoryDir;
38
39
  mkdirSync(memoryDir, { recursive: true });
39
40
 
40
41
  const store = createStore(join(memoryDir, "memory.db"));
@@ -47,25 +48,28 @@ export const apply = (ctx, config) => {
47
48
  // default 90). Best-effort like the failure prune — the audit trail is
48
49
  // bookkeeping and a failed purge must never block plugin boot.
49
50
  try {
50
- if (cfg.llmAudit?.enabled !== false) {
51
- const retentionMs = Number.isInteger(cfg.llmAudit?.retentionDays) ? cfg.llmAudit.retentionDays : 90;
51
+ if (rawCfg.llmAudit?.enabled !== false) {
52
+ const retentionMs = Number.isInteger(rawCfg.llmAudit?.retentionDays) ? rawCfg.llmAudit.retentionDays : 90;
52
53
  store.deleteOldLlmAudits(new Date(Date.now() - retentionMs * 86400000).toISOString());
53
54
  }
54
55
  } catch { /* non-fatal */ }
55
- // v0.7.0: recall_runs 滚动清理 —— recordRecall 默认开且注入路径也记账,表随
56
- // 活跃度增长;启动时按 recallRetentionDays(默认 90 天)清一次,防膨胀。
57
- // llm_audit 同策略:纯 bookkeeping,清理失败绝不阻塞插件启动。
58
- try {
59
- const recallRetentionDays = Number.isInteger(cfg.recallRetentionDays) ? cfg.recallRetentionDays : 90;
60
- store.purgeRecallRunsOlderThan(recallRetentionDays);
61
- } catch { /* non-fatal */ }
62
- const mirror = createMirror(memoryDir);
63
- // User-configurable settings (profile, rules) and custom commands share the
64
- // same SQLite file but live in dedicated tables, isolated from memories.
65
- // Created before the service so the tag-config resolver (issue #31) can merge
66
- // the Web panel's persisted toggles over the plugin config at runtime.
56
+
57
+ // User-configurable settings (profile, rules, panel mode, standalone API
58
+ // token) share the same SQLite file in dedicated tables, isolated from
59
+ // memories. Created before the config is finalized: the persisted
60
+ // panel_mode participates in light-mode resolution below.
67
61
  const settings = createSettings(store.db);
68
- const service = createService({ store, mirror, config: cfg, logger: ctx.logger, settings });
62
+
63
+ // Light mode (v0.7.12): the bundle config flag OR a persisted panel_mode of
64
+ // "light" (the panel switch wins over the bundle config so it survives
65
+ // config redeploys). applyLightModePreset turns every heavy background /
66
+ // semantic feature off and keeps the core loop (autoInject, autoSummarize,
67
+ // hot memory, quality filter).
68
+ const lightMode = rawCfg.lightMode === true || settings.getPanelMode() === "light";
69
+ const cfg = applyLightModePreset({ ...rawCfg, lightMode });
70
+
71
+ const mirror = createMirror(memoryDir);
72
+ const service = createService({ store, mirror, config: cfg, logger: ctx.logger });
69
73
 
70
74
  // F-NEW-03: if the mirror sync failed last run (persisted dirty state), retry
71
75
  // a safe re-render at boot so a stale mirror converges without needing a
@@ -115,7 +119,13 @@ export const apply = (ctx, config) => {
115
119
 
116
120
  let embedder = null;
117
121
  let reranker = null;
118
- if (cfg.embedProvider === "openai") {
122
+ if (lightMode) {
123
+ // Light mode: the whole vector pipeline stays off — no embedder (nothing
124
+ // pulls in ONNX/transformers), no reranker, no boot backfill (the preset
125
+ // also cleared autoReindexOnBoot). Recall degrades to keyword search and
126
+ // human mirror edits still merge on boot.
127
+ applyHumanEdits();
128
+ } else if (cfg.embedProvider === "openai") {
119
129
  // vectorIndex is passed so the legacy OpenAI embedder records the producing
120
130
  // model fingerprint after each successful embed (Bug3).
121
131
  embedder = createEmbedder({ store, settings, logger: ctx.logger, vectorIndex });
@@ -252,7 +262,7 @@ export const apply = (ctx, config) => {
252
262
  delayMs: cfg.dreamDelayMs,
253
263
  logger: ctx.logger,
254
264
  semantic: { embedder, vectorIndex },
255
- onRun: () => (dream ? dream.runDream(ctx, service, cfg, settings) : Promise.resolve({ ok: true, skipped: true }))
265
+ onRun: () => (dream ? dream.runDream(ctx, service, cfg) : Promise.resolve({ ok: true, skipped: true }))
256
266
  });
257
267
  service.setDreamHook(() => dream.maybeSchedule(service));
258
268
  }
@@ -325,26 +335,6 @@ export const apply = (ctx, config) => {
325
335
  const summarizer = createSummarizer(ctx, service, cfg);
326
336
  disposers.push(summarizer.dispose);
327
337
 
328
- // Session lifecycle (v0.6.0): when a session leaves the store and the toggle
329
- // is enabled, mark every memory born in it as session-disposed (hidden from
330
- // injection/search/dream but never destroyed — recoverable via
331
- // restoreBySession). Default off, so a disposed session leaves its memories
332
- // active (legacy behavior). Every path is guarded: a failure inside the
333
- // callback must never propagate into DSH's session teardown (that would crash
334
- // the plugin on the very delete action it serves).
335
- if (cfg.sessionLifecycleEnabled) {
336
- disposers.push(ctx.on("session/disposed", (session) => {
337
- const sessionId = session?.id;
338
- if (!sessionId) return;
339
- try {
340
- const { disposed } = service.disposeBySession(sessionId);
341
- ctx.logger?.info?.(`[dsh-mneme] session disposed, hid ${disposed} memory(s) for ${sessionId}`);
342
- } catch (error) {
343
- ctx.logger?.warn?.(`[dsh-mneme] session dispose failed for ${sessionId}: ${String(error)}`);
344
- }
345
- }));
346
- }
347
-
348
338
  if (ctx.webServer) {
349
339
  const api = createApi(ctx, service, settings, commands ?? {
350
340
  add: () => { throw new Error("commands unavailable"); },
@@ -354,6 +344,19 @@ export const apply = (ctx, config) => {
354
344
  disposers.push(api.dispose);
355
345
  }
356
346
 
347
+ // Standalone external API (v0.7.12): plain node:http server for ecosystem
348
+ // integrations outside the DSH host. Persisted external_api settings win
349
+ // over the bundle config (enabled/port); the Bearer token lives in the same
350
+ // kv and is auto-generated on first boot by createStandaloneApi. Binding a
351
+ // non-loopback host is the operator's documented responsibility.
352
+ if ((settings.getExternalApi?.()?.enabled ?? cfg.externalApiEnabled) === true) {
353
+ const standalone = createStandaloneApi({ service, store, config: cfg, logger: ctx.logger, settings });
354
+ disposers.push(() => standalone.server.close());
355
+ standalone.ready.catch((error) => {
356
+ ctx.logger?.warn?.(`[dsh-mneme] standalone API failed to start: ${String(error)}`);
357
+ });
358
+ }
359
+
357
360
  // Async disposer: cordis awaits the returned promise on unload (runDisposable),
358
361
  // so an in-flight dream run is allowed to finish before the SQLite store is
359
362
  // closed — dream.dispose() resolves only after its current run settles.
@@ -8,7 +8,10 @@ import { createHotMemory } from "./hot-memory.js";
8
8
  // falls back to the legacy rule-based pick, never breaking the render.
9
9
  function lastUserQuery(ctx) {
10
10
  try {
11
- const events = ctx?.agent?.session?.snapshotEvents?.() ?? ctx?.agent?.session?.events;
11
+ const session = ctx?.agent?.session;
12
+ // DSH ≥0.1.2-rc only exposes events via snapshotEvents(); older builds
13
+ // still have the .events property, so fall back to it.
14
+ const events = session?.snapshotEvents?.() ?? session?.events;
12
15
  if (!Array.isArray(events) || events.length === 0) return "";
13
16
  for (let i = events.length - 1; i >= 0; i--) {
14
17
  const event = events[i];
@@ -34,7 +37,8 @@ function lastUserQuery(ctx) {
34
37
  // returns [] on any failure, and the hot block simply does not render.
35
38
  function extractRounds(ctx, maxRounds) {
36
39
  try {
37
- const events = ctx?.agent?.session?.snapshotEvents?.() ?? ctx?.agent?.session?.events;
40
+ const session = ctx?.agent?.session;
41
+ const events = session?.snapshotEvents?.() ?? session?.events;
38
42
  if (!Array.isArray(events) || events.length === 0) return [];
39
43
  const rounds = [];
40
44
  let pendingQuery = null;
@@ -75,49 +79,6 @@ export function createInjector(ctx, service, settings, config) {
75
79
  const maxItems = config.maxInjectedItems ?? 5;
76
80
  const threshold = config.importanceThreshold ?? 3;
77
81
 
78
- // Prompt-variable escaping (issue #40): DSH's interpolate() treats `{{name}}`
79
- // as a prompt variable and strictly validates the name against
80
- // /^[a-z][a-z0-9_]*$/ — memory/profile content carrying legal template syntax
81
- // (Obsidian-style `{{hl|}}`, `{{挖空}}`, `{{关键词}}`) would hit an illegal
82
- // variable name and throw, crashing the whole turn. At the injection boundary
83
- // we escape every run of 2+ consecutive braces, inserting a `\` between each
84
- // pair, so no `{{`/`}}` substring survives into the prompt: `{{a}}` → `{\{a\}\}`,
85
- // and odd runs like `{{{a}}}` (which pair-wise escaping would leave with a
86
- // literal `{{`) are handled too. The text keeps its readable template form,
87
- // the transform is idempotent (escaped braces are single + `\`, never two
88
- // adjacent), and single braces pass through untouched — interpolate only
89
- // scans `{{`. Off via config.escapePromptVariables=false (default true).
90
- function escapePromptVars(text) {
91
- if (config.escapePromptVariables === false) return String(text);
92
- return String(text).replace(/[{}]{2,}/g, (run) => run.split("").join("\\"));
93
- }
94
-
95
- // Time-prefix injection (issue #34): opt-in, off by default. When enabled the
96
- // current date/time is injected once per conversation — at the first prompt
97
- // assembly of a new session — so the model can sense what time/day it is.
98
- // A per-session latch means later assemblies in the same session never
99
- // re-inject; a bare render ctx (no session id) falls back to once per
100
- // injector lifetime.
101
- const WEEKDAYS = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
102
- let timePrefixSession = null;
103
-
104
- function renderTimePrefix(ctx) {
105
- if (config.injectTimePrefix !== true) return "";
106
- const sessionId = ctx?.agent?.session?.id;
107
- if (sessionId === undefined) {
108
- if (timePrefixSession !== null) return "";
109
- timePrefixSession = true;
110
- } else {
111
- if (sessionId === timePrefixSession) return "";
112
- timePrefixSession = sessionId;
113
- }
114
- const now = new Date();
115
- const pad = (n) => String(n).padStart(2, "0");
116
- const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
117
- const time = `${pad(now.getHours())}:${pad(now.getMinutes())}`;
118
- return `[当前时间: ${date} ${WEEKDAYS[now.getDay()]} ${time}]`;
119
- }
120
-
121
82
  // Bug6: bound the injected memory block. Each entry's content is truncated to
122
83
  // MAX_CONTENT chars (trailing `…`); the whole block gets a MAX_BLOCK budget
123
84
  // and an entry that would exceed it collapses to its title only, so a long
@@ -151,7 +112,7 @@ export function createInjector(ctx, service, settings, config) {
151
112
  for (const r of rounds) hot.add(r);
152
113
  const body = hot.getContext();
153
114
  if (!body) return "";
154
- return escapePromptVars(`[短期上下文] 最近对话(共 ${rounds.length} 轮):\n${body}`);
115
+ return `[短期上下文] 最近对话(共 ${rounds.length} 轮):\n${body}`;
155
116
  }
156
117
 
157
118
  function render(candidates) {
@@ -175,7 +136,7 @@ export function createInjector(ctx, service, settings, config) {
175
136
  lines.push(`- [${m.type}] ${verified}${title}`);
176
137
  }
177
138
  }
178
- return escapePromptVars(lines.join("\n"));
139
+ return lines.join("\n");
179
140
  }
180
141
 
181
142
  // Bug4: the system-prompt render is synchronous, so the semantic query vector
@@ -209,7 +170,7 @@ export function createInjector(ctx, service, settings, config) {
209
170
  const lines = ["[用户设置] 来自 dsh-mneme 的用户画像与规则:"];
210
171
  if (profile) lines.push(`- 用户画像:${profile}`);
211
172
  for (const rule of rules) lines.push(`- 规则:${rule}`);
212
- return escapePromptVars(lines.join("\n"));
173
+ return lines.join("\n");
213
174
  }
214
175
 
215
176
  const disposers = [
@@ -231,11 +192,8 @@ export function createInjector(ctx, service, settings, config) {
231
192
  // separate context) keeps the prompt assembly stable at two blocks.
232
193
  const hotText = renderHotContext(ctx);
233
194
  const body = render(candidates);
234
- let out = !hotText ? body : (body ? `${hotText}\n\n${body}` : hotText);
235
- // Time prefix (issue #34): leads the injection block once per session.
236
- const timePrefix = renderTimePrefix(ctx);
237
- if (!timePrefix) return out;
238
- return out ? `${timePrefix}\n\n${out}` : timePrefix;
195
+ if (!hotText) return body;
196
+ return body ? `${hotText}\n\n${body}` : hotText;
239
197
  }
240
198
  }),
241
199
  ctx.systemPrompt.context({
@@ -7,9 +7,7 @@ export const TYPE_FILE = {
7
7
  project: "projects.md",
8
8
  decision: "decisions.md",
9
9
  history: "history.md",
10
- summary: "summary.md",
11
- user: "user.md",
12
- fact: "facts.md"
10
+ summary: "summary.md"
13
11
  };
14
12
 
15
13
  const ESCAPE = /([\\`*_[\]{}()#+.!|>~-])/g;
@@ -33,15 +31,6 @@ function renderMemory(m) {
33
31
  .digest("hex");
34
32
  const lines = [];
35
33
  lines.push(`## ${esc(m.title)}`);
36
- // v0.6.2 tag line: entity_attrs-backed tags (attached by the service as
37
- // `entityTags`) rendered as `#tag` space-separated under the title. No tags
38
- // → no line. Legacy `- **标签**:` metadata below keeps the memories.tags
39
- // column (still written by save/update) readable.
40
- const entityTags = Array.isArray(m.entityTags) ? m.entityTags : [];
41
- if (entityTags.length) {
42
- lines.push("");
43
- lines.push(entityTags.map((t) => `#${esc(t)}`).join(" "));
44
- }
45
34
  lines.push("");
46
35
  lines.push(`- **ID**: \`${m.id}\``);
47
36
  lines.push(`- **类型**: ${m.type}`);
@@ -34,9 +34,7 @@ const TYPE_LABELS = {
34
34
  decision: ["decision", "决策", "决定"],
35
35
  history: ["history", "历史", "事件"],
36
36
  summary: ["summary", "总结", "摘要", "总览"],
37
- pattern: ["pattern", "模式", "规律"],
38
- user: ["user", "用户"],
39
- fact: ["fact", "事实", "原子事实"]
37
+ pattern: ["pattern", "模式", "规律"]
40
38
  };
41
39
 
42
40
  /** Normalized bigram-overlap similarity in [0,1]; 0 for tiny/empty inputs. */
@@ -21,10 +21,7 @@ function modelHash(model) {
21
21
 
22
22
  /** Lazy default pipeline factory: dynamic import keeps module load cheap. */
23
23
  async function defaultPipelineLoader(task, model, options) {
24
- const { env, pipeline } = await import("@huggingface/transformers");
25
- // issue #13: mirror cache_dir onto env.cacheDir so the tokenizer pre-check
26
- // resolves locally too (same fix as local-embedder.js).
27
- if (options?.cache_dir) env.cacheDir = options.cache_dir;
24
+ const { pipeline } = await import("@huggingface/transformers");
28
25
  return pipeline(task, model, options);
29
26
  }
30
27