@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.
- package/README.en.md +437 -0
- package/README.md +364 -166
- package/bin/cli.mjs +603 -0
- package/{dsh-mneme/cordis.patch.yml → cordis.patch.yml} +1 -0
- package/lib/api-standalone.js +264 -0
- package/{dsh-mneme/lib → lib}/api.js +133 -289
- package/{dsh-mneme/lib → lib}/client.js +1008 -922
- package/{dsh-mneme/lib → lib}/config.js +54 -99
- package/{dsh-mneme/lib → lib}/dream/decisions.js +94 -143
- package/{dsh-mneme/lib → lib}/dream/sleep.js +12 -152
- package/{dsh-mneme/lib → lib}/dream.js +22 -168
- package/{dsh-mneme/src → lib}/hot-memory.js +0 -7
- package/{dsh-mneme/lib → lib}/index.js +45 -42
- package/{dsh-mneme/lib → lib}/inject.js +11 -53
- package/{dsh-mneme/src → lib}/mirror.js +1 -12
- package/{dsh-mneme/lib → lib}/quality-filter.js +1 -3
- package/{dsh-mneme/lib → lib}/reranker.js +1 -4
- package/{dsh-mneme/lib → lib}/service.js +20 -410
- package/{dsh-mneme/src → lib}/settings.js +34 -64
- package/{dsh-mneme/lib → lib}/store.js +38 -450
- package/{dsh-mneme/lib → lib}/summarize.js +10 -10
- package/lib/tools.js +274 -0
- package/package.json +45 -19
- package/{dsh-mneme/scripts → scripts}/e2e-dsh.js +3 -4
- package/{dsh-mneme/scripts → scripts}/sync-lib.js +5 -10
- package/src/api-standalone.js +264 -0
- package/{dsh-mneme/src → src}/api.js +133 -289
- package/{dsh-mneme/src → src}/config.js +54 -99
- package/{dsh-mneme/src → src}/dream/decisions.js +94 -143
- package/{dsh-mneme/src → src}/dream/sleep.js +12 -152
- package/{dsh-mneme/src → src}/dream.js +22 -168
- package/{dsh-mneme/lib → src}/hot-memory.js +0 -7
- package/{dsh-mneme/src → src}/index.js +45 -42
- package/{dsh-mneme/src → src}/inject.js +11 -53
- package/{dsh-mneme/lib → src}/mirror.js +1 -12
- package/{dsh-mneme/src → src}/quality-filter.js +1 -3
- package/{dsh-mneme/src → src}/reranker.js +1 -4
- package/{dsh-mneme/src → src}/service.js +20 -410
- package/{dsh-mneme/lib → src}/settings.js +34 -64
- package/{dsh-mneme/src → src}/store.js +38 -450
- package/{dsh-mneme/src → src}/summarize.js +10 -10
- package/src/tools.js +274 -0
- package/{dsh-mneme/test → test}/api.test.js +45 -156
- package/{dsh-mneme/test → test}/client.test.js +10 -206
- package/{dsh-mneme/test → test}/config.test.js +0 -21
- package/{dsh-mneme/test → test}/dream.test.js +1 -160
- package/{dsh-mneme/test → test}/graph-api.test.js +0 -34
- package/{dsh-mneme/test → test}/hot-memory.test.js +0 -29
- package/test/inject.test.js +120 -0
- package/{dsh-mneme/test → test}/llm-audit.test.js +4 -4
- package/{dsh-mneme/test → test}/reasoning-effort.test.js +0 -27
- package/{dsh-mneme/test → test}/recall-layer.test.js +5 -26
- package/{dsh-mneme/test → test}/reranker.test.js +0 -43
- package/{dsh-mneme/test → test}/service-search.test.js +0 -25
- package/{dsh-mneme/test → test}/service.test.js +0 -106
- package/{dsh-mneme/test → test}/settings.test.js +16 -35
- package/{dsh-mneme/test → test}/sleep.test.js +4 -171
- package/test/standalone-api.test.js +326 -0
- package/{dsh-mneme/test → test}/store.test.js +0 -76
- package/{dsh-mneme/test → test}/summarize.test.js +16 -15
- package/test/tools.test.js +265 -0
- package/.github/workflows/publish.yml +0 -58
- package/.github/workflows/test.yml +0 -32
- package/CHANGELOG.md +0 -91
- package/CONTRIBUTING.md +0 -293
- package/SECURITY.md +0 -718
- package/docs/devlog/2026-08-14-dsh-mneme-dev-log.md +0 -247
- package/docs/devlog/2026-08-15-dsh-mneme-audit-stress-dev-log.md +0 -145
- package/docs/devlog/2026-08-15-dsh-mneme-pipeline-dev-log.md +0 -56
- package/docs/devlog/2026-08-15-dsh-mneme-reflection-dev-log.md +0 -77
- package/docs/devlog/2026-08-15-dsh-mneme-review-fixes-dev-log.md +0 -64
- package/docs/devlog/2026-08-15-dsh-mneme-semantic-dev-log.md +0 -90
- package/dsh-mneme/CHANGELOG.md +0 -419
- package/dsh-mneme/LICENSE +0 -21
- package/dsh-mneme/README.md +0 -501
- package/dsh-mneme/docs/AGENT_MEMORY_RESEARCH.md +0 -183
- package/dsh-mneme/docs/ENTITIES.md +0 -245
- package/dsh-mneme/docs/LOCAL_MODEL.md +0 -141
- package/dsh-mneme/docs/MIGRATION.md +0 -127
- package/dsh-mneme/docs/SEMANTIC.md +0 -256
- package/dsh-mneme/docs/SLEEP.md +0 -163
- package/dsh-mneme/lib/dream/tag-extractor.js +0 -156
- package/dsh-mneme/lib/heat.js +0 -136
- package/dsh-mneme/lib/parser/tag.js +0 -59
- package/dsh-mneme/lib/parser/wiki-link.js +0 -38
- package/dsh-mneme/lib/search/tag-boost.js +0 -61
- package/dsh-mneme/lib/tools.js +0 -458
- package/dsh-mneme/package-lock.json +0 -1936
- package/dsh-mneme/package.json +0 -83
- package/dsh-mneme/scripts/check-sync.js +0 -42
- package/dsh-mneme/src/client.js +0 -2242
- package/dsh-mneme/src/dream/tag-extractor.js +0 -156
- package/dsh-mneme/src/heat.js +0 -136
- package/dsh-mneme/src/parser/tag.js +0 -59
- package/dsh-mneme/src/parser/wiki-link.js +0 -38
- package/dsh-mneme/src/search/tag-boost.js +0 -61
- package/dsh-mneme/src/tools.js +0 -458
- package/dsh-mneme/test/boundary-v0625.test.js +0 -82
- package/dsh-mneme/test/directory.test.js +0 -134
- package/dsh-mneme/test/heat.test.js +0 -148
- package/dsh-mneme/test/inject.test.js +0 -206
- package/dsh-mneme/test/layered-types-stats.test.js +0 -144
- package/dsh-mneme/test/lib-smoke.test.js +0 -109
- package/dsh-mneme/test/normalize-decisions.test.js +0 -120
- package/dsh-mneme/test/provenance.test.js +0 -103
- package/dsh-mneme/test/recall-runs.test.js +0 -93
- package/dsh-mneme/test/sleep-heat.test.js +0 -112
- package/dsh-mneme/test/tag-boost.test.js +0 -125
- package/dsh-mneme/test/tag.test.js +0 -426
- package/dsh-mneme/test/tools.test.js +0 -674
- package/dsh-mneme/test/updated-at-semantics.test.js +0 -113
- package/dsh-mneme/test/wiki-link.test.js +0 -332
- package//346/250/252/345/271/205.png +0 -0
- /package/{dsh-mneme/lib → lib}/commands.js +0 -0
- /package/{dsh-mneme/lib → lib}/dream/clustering.js +0 -0
- /package/{dsh-mneme/lib → lib}/embedding.js +0 -0
- /package/{dsh-mneme/lib → lib}/entities/extractor.js +0 -0
- /package/{dsh-mneme/lib → lib}/local-embedder.js +0 -0
- /package/{dsh-mneme/lib → lib}/search/adaptive.js +0 -0
- /package/{dsh-mneme/lib → lib}/search/bm25.js +0 -0
- /package/{dsh-mneme/lib → lib}/vector-index.js +0 -0
- /package/{dsh-mneme/scripts → scripts}/benchmark-embed.js +0 -0
- /package/{dsh-mneme/scripts → scripts}/benchmark-recall.js +0 -0
- /package/{dsh-mneme/scripts → scripts}/benchmark-rerank.js +0 -0
- /package/{dsh-mneme/scripts → scripts}/stress-dsh.js +0 -0
- /package/{dsh-mneme/src → src}/commands.js +0 -0
- /package/{dsh-mneme/src → src}/dream/clustering.js +0 -0
- /package/{dsh-mneme/src → src}/embedding.js +0 -0
- /package/{dsh-mneme/src → src}/entities/extractor.js +0 -0
- /package/{dsh-mneme/src → src}/local-embedder.js +0 -0
- /package/{dsh-mneme/src → src}/search/adaptive.js +0 -0
- /package/{dsh-mneme/src → src}/search/bm25.js +0 -0
- /package/{dsh-mneme/src → src}/vector-index.js +0 -0
- /package/{dsh-mneme/test → test}/audit.test.js +0 -0
- /package/{dsh-mneme/test → test}/benchmark.test.js +0 -0
- /package/{dsh-mneme/test → test}/clustering.test.js +0 -0
- /package/{dsh-mneme/test → test}/commands.test.js +0 -0
- /package/{dsh-mneme/test → test}/conflict-freeze.test.js +0 -0
- /package/{dsh-mneme/test → test}/entities.test.js +0 -0
- /package/{dsh-mneme/test → test}/epistemic.test.js +0 -0
- /package/{dsh-mneme/test → test}/fnew-0112.test.js +0 -0
- /package/{dsh-mneme/test → test}/fnew-03.test.js +0 -0
- /package/{dsh-mneme/test → test}/helpers/dream-mock.js +0 -0
- /package/{dsh-mneme/test → test}/local-embedder.test.js +0 -0
- /package/{dsh-mneme/test → test}/mirror-dirty.test.js +0 -0
- /package/{dsh-mneme/test → test}/mirror-edit-digest.test.js +0 -0
- /package/{dsh-mneme/test → test}/mirror-generation.test.js +0 -0
- /package/{dsh-mneme/test → test}/mirror.test.js +0 -0
- /package/{dsh-mneme/test → test}/peer-blockers.test.js +0 -0
- /package/{dsh-mneme/test → test}/policy-epoch.test.js +0 -0
- /package/{dsh-mneme/test → test}/quality-filter.test.js +0 -0
- /package/{dsh-mneme/test → test}/recall-evals.test.js +0 -0
- /package/{dsh-mneme/test → test}/receipt-chain.test.js +0 -0
- /package/{dsh-mneme/test → test}/reflection.test.js +0 -0
- /package/{dsh-mneme/test → test}/search-fusion.test.js +0 -0
- /package/{dsh-mneme/test → test}/semantic.test.js +0 -0
- /package/{dsh-mneme/test → test}/stress.test.js +0 -0
- /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
|
|
33
|
+
const rawCfg = Config(config);
|
|
33
34
|
|
|
34
35
|
// Resolve memoryDir: expand leading "~"
|
|
35
|
-
const memoryDir =
|
|
36
|
-
? join(homedir(),
|
|
37
|
-
:
|
|
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 (
|
|
51
|
-
const retentionMs = Number.isInteger(
|
|
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
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
235
|
-
|
|
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 {
|
|
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
|
|