@modusensus/dsh-mneme 0.7.9 → 0.7.11
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 +368 -0
- package/README.md +298 -167
- package/{dsh-mneme/cordis.patch.yml → cordis.patch.yml} +1 -0
- package/{dsh-mneme/lib → lib}/api.js +58 -284
- package/lib/client.js +2086 -0
- package/{dsh-mneme/src → lib}/config.js +6 -100
- package/{dsh-mneme/lib → lib}/dream/decisions.js +94 -143
- package/{dsh-mneme/src → 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/src → lib}/index.js +6 -34
- 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/lib → lib}/settings.js +0 -70
- package/{dsh-mneme/lib → lib}/store.js +35 -450
- package/{dsh-mneme/lib → lib}/summarize.js +10 -10
- package/lib/tools.js +274 -0
- package/package.json +41 -19
- package/{dsh-mneme/scripts → scripts}/e2e-dsh.js +3 -3
- package/{dsh-mneme/scripts → scripts}/sync-lib.js +5 -10
- package/{dsh-mneme/src → src}/api.js +58 -284
- package/{dsh-mneme/lib → src}/config.js +6 -100
- package/{dsh-mneme/src → src}/dream/decisions.js +94 -143
- package/{dsh-mneme/lib → 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/lib → src}/index.js +6 -34
- 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/src → src}/settings.js +0 -70
- package/{dsh-mneme/src → src}/store.js +35 -450
- package/{dsh-mneme/src → src}/summarize.js +10 -10
- package/src/tools.js +274 -0
- package/{dsh-mneme/test → test}/api.test.js +1 -156
- package/{dsh-mneme/test → test}/client.test.js +22 -207
- 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 +0 -43
- package/{dsh-mneme/test → test}/sleep.test.js +4 -171
- 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 -291
- 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 -406
- package/dsh-mneme/LICENSE +0 -21
- package/dsh-mneme/README.md +0 -498
- 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/client.js +0 -2050
- 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 -2050
- 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
|
@@ -88,23 +88,20 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
88
88
|
const type = url.searchParams.get("type") ?? undefined;
|
|
89
89
|
const limit = Number(url.searchParams.get("limit") ?? 50);
|
|
90
90
|
const offset = Number(url.searchParams.get("offset") ?? 0);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const raw = url.searchParams.get("days");
|
|
106
|
-
const days = Math.min(30, Math.max(1, parseInt(raw ?? "7", 10) || 7));
|
|
107
|
-
sendJson(res, 200, service.stats({ days }));
|
|
91
|
+
// order=chrono: pure newest-first for paged browsing; default keeps
|
|
92
|
+
// the importance-ranked order other callers rely on.
|
|
93
|
+
const order = url.searchParams.get("order") ?? undefined;
|
|
94
|
+
// minImportance: numeric lower bound on importance (absent/NaN → no
|
|
95
|
+
// floor); source: exact match on the source column (empty → no filter).
|
|
96
|
+
const minRaw = url.searchParams.get("minImportance");
|
|
97
|
+
const minImportance = minRaw !== null && minRaw !== "" && !Number.isNaN(Number(minRaw))
|
|
98
|
+
? Number(minRaw)
|
|
99
|
+
: undefined;
|
|
100
|
+
const source = url.searchParams.get("source") || undefined;
|
|
101
|
+
const items = service.toApiList(service.list({ type, limit, offset, order, minImportance, source }));
|
|
102
|
+
// Total honors the same filters as the rows, or the pager's
|
|
103
|
+
// has-more math breaks whenever minImportance/source is active.
|
|
104
|
+
sendJson(res, 200, { items, total: service.count(type, { minImportance, source }) });
|
|
108
105
|
} catch {
|
|
109
106
|
sendJson(res, 500, { error: "internal" });
|
|
110
107
|
}
|
|
@@ -175,6 +172,40 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
175
172
|
}
|
|
176
173
|
});
|
|
177
174
|
|
|
175
|
+
// --- delete one memory by id ---
|
|
176
|
+
// Mutation route: apiToken-gated like the profile/rules writes above. POST
|
|
177
|
+
// body is JSON { id }. store.remove deletes silently, so existence is checked
|
|
178
|
+
// up front to give clients a distinguishable 404 instead of a fake success.
|
|
179
|
+
register({
|
|
180
|
+
kind: "exact",
|
|
181
|
+
path: "/api/dsh-mneme/delete",
|
|
182
|
+
handler(req, res) {
|
|
183
|
+
try {
|
|
184
|
+
if (req.method !== "POST") {
|
|
185
|
+
sendJson(res, 404, { error: "not-found" });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (!requireAuth(req, res, apiToken)) return;
|
|
189
|
+
return readBody(req).then((text) => {
|
|
190
|
+
const body = parseBody(text);
|
|
191
|
+
const id = typeof body.id === "string" ? body.id.trim() : "";
|
|
192
|
+
if (!id) {
|
|
193
|
+
sendJson(res, 400, { error: "missing-id" });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!service.getById(id)) {
|
|
197
|
+
sendJson(res, 404, { error: "not-found" });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
service.remove(id);
|
|
201
|
+
sendJson(res, 200, { ok: true });
|
|
202
|
+
});
|
|
203
|
+
} catch {
|
|
204
|
+
sendJson(res, 500, { error: "internal" });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
178
209
|
// --- rules ---
|
|
179
210
|
register({
|
|
180
211
|
kind: "exact",
|
|
@@ -233,102 +264,6 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
233
264
|
}
|
|
234
265
|
});
|
|
235
266
|
|
|
236
|
-
// --- app config read/write (partial: pass only the fields to change) ---
|
|
237
|
-
register({
|
|
238
|
-
kind: "exact",
|
|
239
|
-
path: "/api/dsh-mneme/config",
|
|
240
|
-
handler: async (req, res) => {
|
|
241
|
-
try {
|
|
242
|
-
if (!requireAuth(req, res, apiToken)) return;
|
|
243
|
-
if (req.method === "PUT" || req.method === "POST") {
|
|
244
|
-
const body = parseBody(await readBody(req));
|
|
245
|
-
const patch = {};
|
|
246
|
-
const setIfBoolean = (key) => {
|
|
247
|
-
if (Object.prototype.hasOwnProperty.call(body, key)) {
|
|
248
|
-
if (typeof body[key] !== "boolean") {
|
|
249
|
-
sendJson(res, 400, { error: `${key} must be a boolean` });
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
patch[key] = body[key];
|
|
253
|
-
}
|
|
254
|
-
return true;
|
|
255
|
-
};
|
|
256
|
-
if (!setIfBoolean("autoTagEnabled") || !setIfBoolean("manualTagEnabled") || !setIfBoolean("showSidebarTrigger")) return;
|
|
257
|
-
if (Object.keys(patch).length === 0) {
|
|
258
|
-
sendJson(res, 400, { error: "no valid config field provided" });
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
// Each setter ignores the keys it doesn't own, so one patch can feed
|
|
262
|
-
// both stores safely (tag toggles → "autoTag", UI prefs → "ui").
|
|
263
|
-
settings.setAutoTagConfig(patch);
|
|
264
|
-
settings.setUiConfig(patch);
|
|
265
|
-
// Respond with the effective (settings-over-config merged) toggles
|
|
266
|
-
// so the panel always sees booleans that actually gate runtime
|
|
267
|
-
// behaviour (issue #31).
|
|
268
|
-
sendJson(res, 200, { config: service.getAppConfig() });
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
sendJson(res, 200, { config: service.getAppConfig() });
|
|
272
|
-
} catch {
|
|
273
|
-
sendJson(res, 500, { error: "internal" });
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// --- delete a memory by exact id or best query match (mirrors memory_delete tool) ---
|
|
279
|
-
register({
|
|
280
|
-
kind: "exact",
|
|
281
|
-
path: "/api/dsh-mneme/memories",
|
|
282
|
-
handler: async (req, res) => {
|
|
283
|
-
try {
|
|
284
|
-
if (!requireAuth(req, res, apiToken)) return;
|
|
285
|
-
if (req.method !== "DELETE") {
|
|
286
|
-
sendJson(res, 405, { error: "method not allowed" });
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
const body = parseBody(await readBody(req));
|
|
290
|
-
const hasId = Object.prototype.hasOwnProperty.call(body, "id");
|
|
291
|
-
const hasQuery = Object.prototype.hasOwnProperty.call(body, "query");
|
|
292
|
-
if ((hasId && hasQuery) || (!hasId && !hasQuery)) {
|
|
293
|
-
sendJson(res, 400, { error: "provide exactly one of id or query" });
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
if (hasId) {
|
|
297
|
-
if (typeof body.id !== "string" || !body.id.trim()) {
|
|
298
|
-
sendJson(res, 400, { error: "invalid id" });
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
const mem = service.getById(body.id);
|
|
302
|
-
if (!mem) {
|
|
303
|
-
sendJson(res, 200, { deleted: false });
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
service.remove(body.id);
|
|
307
|
-
sendJson(res, 200, { deleted: true, id: body.id });
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
if (typeof body.query !== "string" || !body.query.trim()) {
|
|
311
|
-
sendJson(res, 400, { error: "invalid query" });
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
const results = await service.searchMemories(body.query, { mode: "auto", topK: 1, useRerank: true });
|
|
315
|
-
if (!Array.isArray(results) || results.length === 0) {
|
|
316
|
-
sendJson(res, 200, { deleted: false });
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
const mem = results[0];
|
|
320
|
-
if (!mem || !mem.id) {
|
|
321
|
-
sendJson(res, 200, { deleted: false });
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
service.remove(mem.id);
|
|
325
|
-
sendJson(res, 200, { deleted: true, id: mem.id });
|
|
326
|
-
} catch {
|
|
327
|
-
sendJson(res, 500, { error: "internal" });
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
|
|
332
267
|
// --- vector re-index (backfill embeddings for rows missing them) ---
|
|
333
268
|
register({
|
|
334
269
|
kind: "exact",
|
|
@@ -479,10 +414,7 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
479
414
|
name: n.name,
|
|
480
415
|
type: n.type ?? null,
|
|
481
416
|
mention_count: n.mention_count ?? 1,
|
|
482
|
-
distance: n.distance
|
|
483
|
-
// v0.7.0 实体热投影:实体热 = 关联记忆 heat 聚合(max),前端据此
|
|
484
|
-
// 缩放节点大小/明暗。heatEnabled=false 时 entityHeat 返回 null。
|
|
485
|
-
heat: service.entityHeat?.(n.id) ?? null
|
|
417
|
+
distance: n.distance
|
|
486
418
|
})),
|
|
487
419
|
edges: [...edgeMap.values()].map((e) => ({
|
|
488
420
|
id: e.id,
|
|
@@ -536,101 +468,19 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
536
468
|
}
|
|
537
469
|
});
|
|
538
470
|
|
|
539
|
-
// ---
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
//
|
|
471
|
+
// --- entity directory (entity gene v0.3.0): read-only list for the UI ---
|
|
472
|
+
// Flat entity list (newest-seen first). Attributes and relations stay on
|
|
473
|
+
// their own per-entity endpoints, the directory only carries the columns
|
|
474
|
+
// the rail needs: name, type, mention_count, first/last seen.
|
|
543
475
|
register({
|
|
544
476
|
kind: "exact",
|
|
545
|
-
path: "/api/dsh-mneme/
|
|
477
|
+
path: "/api/dsh-mneme/entities",
|
|
546
478
|
handler(req, res) {
|
|
547
479
|
try {
|
|
548
480
|
const url = new URL(req.url, "http://localhost");
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
const memory = service.getById?.(id) ?? null;
|
|
555
|
-
const backlinks = (service.getBacklinks?.(id) ?? []).map(({ source, relation }) => ({
|
|
556
|
-
id: source.id,
|
|
557
|
-
title: source.title,
|
|
558
|
-
type: source.type,
|
|
559
|
-
created_at: relation.created_at
|
|
560
|
-
}));
|
|
561
|
-
sendJson(res, 200, {
|
|
562
|
-
memoryId: id,
|
|
563
|
-
memory: memory ? { id: memory.id, title: memory.title, type: memory.type } : null,
|
|
564
|
-
backlinks
|
|
565
|
-
});
|
|
566
|
-
} catch {
|
|
567
|
-
sendJson(res, 500, { error: "internal" });
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// --- wiki-link forward links (v0.6.1) -----------------------------------
|
|
573
|
-
// GET /api/dsh-mneme/wikilinks/forward?id=<memoryId> → memories the given
|
|
574
|
-
// memory explicitly links to. Unresolved target titles surface with id:null.
|
|
575
|
-
register({
|
|
576
|
-
kind: "exact",
|
|
577
|
-
path: "/api/dsh-mneme/wikilinks/forward",
|
|
578
|
-
handler(req, res) {
|
|
579
|
-
try {
|
|
580
|
-
const url = new URL(req.url, "http://localhost");
|
|
581
|
-
const id = (url.searchParams.get("id") ?? "").trim();
|
|
582
|
-
if (!id) {
|
|
583
|
-
sendJson(res, 400, { error: "missing-id" });
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
const memory = service.getById?.(id) ?? null;
|
|
587
|
-
const links = (service.getForwardLinks?.(id) ?? []).map(({ target, relation }) => ({
|
|
588
|
-
id: target?.id ?? null,
|
|
589
|
-
title: target?.title ?? relation.to_entity,
|
|
590
|
-
type: target?.type ?? null,
|
|
591
|
-
created_at: relation.created_at
|
|
592
|
-
}));
|
|
593
|
-
sendJson(res, 200, {
|
|
594
|
-
memoryId: id,
|
|
595
|
-
memory: memory ? { id: memory.id, title: memory.title, type: memory.type } : null,
|
|
596
|
-
links
|
|
597
|
-
});
|
|
598
|
-
} catch {
|
|
599
|
-
sendJson(res, 500, { error: "internal" });
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
// --- wiki-link resolve (v0.6.1) -----------------------------------------
|
|
605
|
-
// GET /api/dsh-mneme/wikilinks/resolve?title=<title> → case-insensitive exact
|
|
606
|
-
// title match against the memories table (the resolution used when writing
|
|
607
|
-
// links_to relations). 404 when no memory matches.
|
|
608
|
-
register({
|
|
609
|
-
kind: "exact",
|
|
610
|
-
path: "/api/dsh-mneme/wikilinks/resolve",
|
|
611
|
-
handler(req, res) {
|
|
612
|
-
try {
|
|
613
|
-
const url = new URL(req.url, "http://localhost");
|
|
614
|
-
const title = (url.searchParams.get("title") ?? "").trim();
|
|
615
|
-
if (!title) {
|
|
616
|
-
sendJson(res, 400, { error: "missing-title" });
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
const memory = service.resolveWikiLink?.(title) ?? null;
|
|
620
|
-
if (!memory) {
|
|
621
|
-
sendJson(res, 404, { error: "memory-not-found", title });
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
// Note: no `source` field — it may carry file paths/internal host info
|
|
625
|
-
// and this endpoint is read-only without auth when apiToken is set.
|
|
626
|
-
sendJson(res, 200, {
|
|
627
|
-
title,
|
|
628
|
-
memory: {
|
|
629
|
-
id: memory.id,
|
|
630
|
-
title: memory.title,
|
|
631
|
-
type: memory.type
|
|
632
|
-
}
|
|
633
|
-
});
|
|
481
|
+
const limit = Math.min(1000, Math.max(1, Number(url.searchParams.get("limit") ?? 500) || 500));
|
|
482
|
+
const entities = service.listEntities?.({ limit }) ?? [];
|
|
483
|
+
sendJson(res, 200, { entities, total: entities.length });
|
|
634
484
|
} catch {
|
|
635
485
|
sendJson(res, 500, { error: "internal" });
|
|
636
486
|
}
|
|
@@ -722,84 +572,8 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
722
572
|
}
|
|
723
573
|
});
|
|
724
574
|
|
|
725
|
-
// --- memory tags (v0.6.2) ------------------------------------------------
|
|
726
|
-
// GET /api/dsh-mneme/memory/tags?id=<memoryId> → live entity_attrs-backed
|
|
727
|
-
// tag set for one memory plus the manualTagEnabled gate (so the panel
|
|
728
|
-
// can hide tag editing when the manual path is off). Read-only, stays
|
|
729
|
-
// open when apiToken is set (like list/search/semantic).
|
|
730
|
-
// POST /api/dsh-mneme/memory/tags { id, tags } → overwrite the live tag set
|
|
731
|
-
// via service.setMemoryTags (manualTagEnabled gate); 409 when the gate
|
|
732
|
-
// is closed. Auth-gated like the other write endpoints.
|
|
733
|
-
register({
|
|
734
|
-
kind: "exact",
|
|
735
|
-
path: "/api/dsh-mneme/memory/tags",
|
|
736
|
-
handler(req, res) {
|
|
737
|
-
try {
|
|
738
|
-
if (req.method === "POST" || req.method === "PUT") {
|
|
739
|
-
if (!requireAuth(req, res, apiToken)) return;
|
|
740
|
-
return readBody(req).then((text) => {
|
|
741
|
-
const body = parseBody(text);
|
|
742
|
-
const id = typeof body.id === "string" ? body.id.trim() : "";
|
|
743
|
-
if (!id) {
|
|
744
|
-
sendJson(res, 400, { error: "missing-id" });
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
const memory = service.getById?.(id) ?? null;
|
|
748
|
-
if (!memory) {
|
|
749
|
-
sendJson(res, 404, { error: "memory-not-found" });
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
const result = service.setMemoryTags(id, Array.isArray(body.tags) ? body.tags : []);
|
|
753
|
-
if (result?.ok === false) {
|
|
754
|
-
sendJson(res, 409, { error: result.error || "tags-disabled" });
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
sendJson(res, 200, { ok: true, memoryId: id, tags: result?.tags ?? [] });
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
const url = new URL(req.url, "http://localhost");
|
|
761
|
-
const id = (url.searchParams.get("id") ?? "").trim();
|
|
762
|
-
if (!id) {
|
|
763
|
-
sendJson(res, 400, { error: "missing-id" });
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
const memory = service.getById?.(id) ?? null;
|
|
767
|
-
if (!memory) {
|
|
768
|
-
sendJson(res, 404, { error: "memory-not-found" });
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
const tags = service.getMemoryTags?.(id) ?? [];
|
|
772
|
-
sendJson(res, 200, {
|
|
773
|
-
memoryId: id,
|
|
774
|
-
tags: Array.isArray(tags) ? tags : [],
|
|
775
|
-
manualTagEnabled: service.manualTagEnabled?.() ?? true
|
|
776
|
-
});
|
|
777
|
-
} catch {
|
|
778
|
-
sendJson(res, 500, { error: "internal" });
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
|
|
783
|
-
// --- directory view (v0.6.3) ---------------------------------------------
|
|
784
|
-
// GET /api/dsh-mneme/directory → memories grouped by tag as
|
|
785
|
-
// { groups: [{ tag, memories: [...] }], untagged: [...] }. Live-only
|
|
786
|
-
// (forgotten/archived/session-disposed excluded), groups tag-sorted, members
|
|
787
|
-
// importance+updated DESC. Read-only, stays open when apiToken is set.
|
|
788
|
-
register({
|
|
789
|
-
kind: "exact",
|
|
790
|
-
path: "/api/dsh-mneme/directory",
|
|
791
|
-
handler(req, res) {
|
|
792
|
-
try {
|
|
793
|
-
const dir = service.getDirectory?.() ?? { groups: [], untagged: [] };
|
|
794
|
-
sendJson(res, 200, dir);
|
|
795
|
-
} catch {
|
|
796
|
-
sendJson(res, 500, { error: "internal" });
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
});
|
|
800
|
-
|
|
801
575
|
return {
|
|
802
|
-
routes:
|
|
576
|
+
routes: 15,
|
|
803
577
|
dispose: () => {
|
|
804
578
|
for (const dispose of disposers) dispose();
|
|
805
579
|
}
|