neo4j-agent-memory 0.4.0 → 0.5.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/README.md +94 -2
- package/dist/cypher/auto_relate_memory_by_tags.cypher +9 -0
- package/dist/cypher/fallback_retrieve_memories.cypher +68 -0
- package/dist/cypher/feedback_batch.cypher +20 -26
- package/dist/cypher/get_memories_by_id.cypher +41 -0
- package/dist/cypher/get_memory_graph.cypher +108 -0
- package/dist/cypher/index.ts +4 -0
- package/dist/cypher/list_memory_edges.cypher +37 -0
- package/dist/index.cjs +189 -19
- package/dist/index.d.ts +96 -9
- package/dist/index.js +189 -19
- package/package.json +1 -1
- package/dist/index.d.cts +0 -246
package/dist/index.js
CHANGED
|
@@ -36,7 +36,11 @@ var cypher = {
|
|
|
36
36
|
feedbackCoUsed: loadCypher("feedback_co_used_with_batch.cypher"),
|
|
37
37
|
listMemories: loadCypher("list_memories.cypher"),
|
|
38
38
|
relateConcepts: loadCypher("relate_concepts.cypher"),
|
|
39
|
-
autoRelateByTags: loadCypher("auto_relate_memory_by_tags.cypher")
|
|
39
|
+
autoRelateByTags: loadCypher("auto_relate_memory_by_tags.cypher"),
|
|
40
|
+
getMemoriesById: loadCypher("get_memories_by_id.cypher"),
|
|
41
|
+
getMemoryGraph: loadCypher("get_memory_graph.cypher"),
|
|
42
|
+
fallbackRetrieveMemories: loadCypher("fallback_retrieve_memories.cypher"),
|
|
43
|
+
listMemoryEdges: loadCypher("list_memory_edges.cypher")
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
// src/neo4j/schema.ts
|
|
@@ -96,6 +100,20 @@ function envHash(env) {
|
|
|
96
100
|
function clamp01(x) {
|
|
97
101
|
return Math.max(0, Math.min(1, x));
|
|
98
102
|
}
|
|
103
|
+
function parseJsonField(value) {
|
|
104
|
+
if (value === null || value === void 0) return void 0;
|
|
105
|
+
if (typeof value !== "string") return value;
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(value);
|
|
108
|
+
} catch {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function toDateString(value) {
|
|
113
|
+
if (value === null || value === void 0) return void 0;
|
|
114
|
+
if (typeof value?.toString === "function") return value.toString();
|
|
115
|
+
return String(value);
|
|
116
|
+
}
|
|
99
117
|
var DEFAULT_AUTO_RELATE = {
|
|
100
118
|
enabled: true,
|
|
101
119
|
minSharedTags: 2,
|
|
@@ -122,6 +140,23 @@ function toBetaEdge(raw) {
|
|
|
122
140
|
updatedAt: raw?.updatedAt ?? null
|
|
123
141
|
};
|
|
124
142
|
}
|
|
143
|
+
function toMemoryRecord(raw) {
|
|
144
|
+
return {
|
|
145
|
+
id: raw.id,
|
|
146
|
+
kind: raw.kind,
|
|
147
|
+
polarity: raw.polarity ?? "positive",
|
|
148
|
+
title: raw.title,
|
|
149
|
+
content: raw.content,
|
|
150
|
+
tags: raw.tags ?? [],
|
|
151
|
+
confidence: raw.confidence ?? 0.7,
|
|
152
|
+
utility: raw.utility ?? 0.2,
|
|
153
|
+
createdAt: toDateString(raw.createdAt),
|
|
154
|
+
updatedAt: toDateString(raw.updatedAt),
|
|
155
|
+
triage: parseJsonField(raw.triage),
|
|
156
|
+
antiPattern: parseJsonField(raw.antiPattern),
|
|
157
|
+
env: raw.env ?? void 0
|
|
158
|
+
};
|
|
159
|
+
}
|
|
125
160
|
function defaultPolicy(req) {
|
|
126
161
|
return {
|
|
127
162
|
minConfidence: req?.minConfidence ?? 0.65,
|
|
@@ -193,6 +228,10 @@ var MemoryService = class {
|
|
|
193
228
|
cyListMemories = cypher.listMemories;
|
|
194
229
|
cyRelateConcepts = cypher.relateConcepts;
|
|
195
230
|
cyAutoRelateByTags = cypher.autoRelateByTags;
|
|
231
|
+
cyGetMemoriesById = cypher.getMemoriesById;
|
|
232
|
+
cyGetMemoryGraph = cypher.getMemoryGraph;
|
|
233
|
+
cyFallbackRetrieve = cypher.fallbackRetrieveMemories;
|
|
234
|
+
cyListMemoryEdges = cypher.listMemoryEdges;
|
|
196
235
|
cyGetRecallEdges = `
|
|
197
236
|
UNWIND $ids AS id
|
|
198
237
|
MATCH (m:Memory {id:id})
|
|
@@ -280,7 +319,7 @@ var MemoryService = class {
|
|
|
280
319
|
contentHash,
|
|
281
320
|
tags,
|
|
282
321
|
confidence: clamp01(l.confidence),
|
|
283
|
-
utility: 0.2,
|
|
322
|
+
utility: typeof l.utility === "number" ? clamp01(l.utility) : 0.2,
|
|
284
323
|
// start modest; reinforce via feedback
|
|
285
324
|
triage: l.triage ? JSON.stringify(l.triage) : null,
|
|
286
325
|
antiPattern: l.antiPattern ? JSON.stringify(l.antiPattern) : null
|
|
@@ -357,6 +396,13 @@ var MemoryService = class {
|
|
|
357
396
|
await session.close();
|
|
358
397
|
}
|
|
359
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Create a new Case with an auto-generated id if none is provided.
|
|
401
|
+
*/
|
|
402
|
+
async createCase(c) {
|
|
403
|
+
const id = c.id ?? newId("case");
|
|
404
|
+
return this.upsertCase({ ...c, id });
|
|
405
|
+
}
|
|
360
406
|
/**
|
|
361
407
|
* Retrieve a ContextBundle with separate Fix and Do-not-do sections, using case-based reasoning.
|
|
362
408
|
* The key idea: match cases by symptoms + env similarity, then pull linked memories.
|
|
@@ -382,28 +428,49 @@ var MemoryService = class {
|
|
|
382
428
|
halfLifeSeconds: this.halfLifeSeconds
|
|
383
429
|
});
|
|
384
430
|
const sections = r.records[0].get("sections");
|
|
385
|
-
const
|
|
386
|
-
id: m.id,
|
|
387
|
-
kind: m.kind,
|
|
388
|
-
polarity: m.polarity ?? "positive",
|
|
389
|
-
title: m.title,
|
|
390
|
-
content: m.content,
|
|
391
|
-
tags: m.tags ?? [],
|
|
392
|
-
confidence: m.confidence ?? 0.7,
|
|
393
|
-
utility: m.utility ?? 0.2,
|
|
394
|
-
updatedAt: m.updatedAt?.toString?.() ?? null
|
|
395
|
-
}));
|
|
396
|
-
const doNot = (sections.doNot ?? []).map((m) => ({
|
|
431
|
+
const mapSummary = (m, fallbackPolarity) => ({
|
|
397
432
|
id: m.id,
|
|
398
433
|
kind: m.kind,
|
|
399
|
-
polarity: m.polarity ??
|
|
434
|
+
polarity: m.polarity ?? fallbackPolarity,
|
|
400
435
|
title: m.title,
|
|
401
436
|
content: m.content,
|
|
402
437
|
tags: m.tags ?? [],
|
|
403
438
|
confidence: m.confidence ?? 0.7,
|
|
404
439
|
utility: m.utility ?? 0.2,
|
|
405
440
|
updatedAt: m.updatedAt?.toString?.() ?? null
|
|
406
|
-
})
|
|
441
|
+
});
|
|
442
|
+
let fixes = (sections.fixes ?? []).map((m) => mapSummary(m, "positive"));
|
|
443
|
+
let doNot = (sections.doNot ?? []).map((m) => mapSummary(m, "negative"));
|
|
444
|
+
const fallback = args.fallback ?? {};
|
|
445
|
+
const shouldFallback = fallback.enabled === true && fixes.length === 0 && doNot.length === 0;
|
|
446
|
+
if (shouldFallback) {
|
|
447
|
+
const fallbackFixLimit = fallback.limit ?? fixLimit;
|
|
448
|
+
const fallbackDontLimit = fallback.limit ?? dontLimit;
|
|
449
|
+
try {
|
|
450
|
+
const fallbackRes = await session.run(this.cyFallbackRetrieve, {
|
|
451
|
+
prompt: args.prompt ?? "",
|
|
452
|
+
tags: args.tags ?? [],
|
|
453
|
+
kinds: args.kinds ?? [],
|
|
454
|
+
fulltextIndex: this.fulltextIndex,
|
|
455
|
+
vectorIndex: this.vectorIndex,
|
|
456
|
+
embedding: fallback.embedding ?? null,
|
|
457
|
+
useFulltext: fallback.useFulltext ?? true,
|
|
458
|
+
useVector: fallback.useVector ?? false,
|
|
459
|
+
useTags: fallback.useTags ?? true,
|
|
460
|
+
fixLimit: fallbackFixLimit,
|
|
461
|
+
dontLimit: fallbackDontLimit
|
|
462
|
+
});
|
|
463
|
+
const fbSections = fallbackRes.records[0]?.get("sections");
|
|
464
|
+
fixes = (fbSections?.fixes ?? []).map((m) => mapSummary(m, "positive"));
|
|
465
|
+
doNot = (fbSections?.doNot ?? []).map((m) => mapSummary(m, "negative"));
|
|
466
|
+
} catch (err) {
|
|
467
|
+
this.emit({
|
|
468
|
+
type: "read",
|
|
469
|
+
action: "retrieveContextBundle.fallbackError",
|
|
470
|
+
meta: { message: err instanceof Error ? err.message : String(err) }
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
407
474
|
const allIds = [.../* @__PURE__ */ new Set([...fixes.map((x) => x.id), ...doNot.map((x) => x.id)])];
|
|
408
475
|
const edgeAfter = /* @__PURE__ */ new Map();
|
|
409
476
|
if (allIds.length > 0) {
|
|
@@ -478,6 +545,66 @@ ${m.content}`).join("");
|
|
|
478
545
|
await session.close();
|
|
479
546
|
}
|
|
480
547
|
}
|
|
548
|
+
async getMemoriesById(args) {
|
|
549
|
+
const ids = [...new Set((args.ids ?? []).filter(Boolean))];
|
|
550
|
+
if (ids.length === 0) return [];
|
|
551
|
+
const session = this.client.session("READ");
|
|
552
|
+
try {
|
|
553
|
+
const res = await session.run(this.cyGetMemoriesById, { ids });
|
|
554
|
+
const memories = res.records[0]?.get("memories") ?? [];
|
|
555
|
+
return memories.map(toMemoryRecord);
|
|
556
|
+
} finally {
|
|
557
|
+
await session.close();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async getMemoryGraph(args) {
|
|
561
|
+
const ids = [...new Set((args.memoryIds ?? []).filter(Boolean))];
|
|
562
|
+
if (ids.length === 0) return { nodes: [], edges: [] };
|
|
563
|
+
const session = this.client.session("READ");
|
|
564
|
+
try {
|
|
565
|
+
const res = await session.run(this.cyGetMemoryGraph, {
|
|
566
|
+
agentId: args.agentId ?? null,
|
|
567
|
+
memoryIds: ids,
|
|
568
|
+
includeNodes: args.includeNodes ?? true,
|
|
569
|
+
includeRelatedTo: args.includeRelatedTo ?? false
|
|
570
|
+
});
|
|
571
|
+
const record = res.records[0];
|
|
572
|
+
const nodesRaw = record?.get("nodes") ?? [];
|
|
573
|
+
const edges = record?.get("edges") ?? [];
|
|
574
|
+
return {
|
|
575
|
+
nodes: nodesRaw.map(toMemoryRecord),
|
|
576
|
+
edges
|
|
577
|
+
};
|
|
578
|
+
} finally {
|
|
579
|
+
await session.close();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async listMemoryEdges(args = {}) {
|
|
583
|
+
const session = this.client.session("READ");
|
|
584
|
+
try {
|
|
585
|
+
const res = await session.run(this.cyListMemoryEdges, {
|
|
586
|
+
limit: args.limit ?? 200,
|
|
587
|
+
minStrength: args.minStrength ?? 0
|
|
588
|
+
});
|
|
589
|
+
return res.records[0]?.get("edges") ?? [];
|
|
590
|
+
} finally {
|
|
591
|
+
await session.close();
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async retrieveContextBundleWithGraph(args) {
|
|
595
|
+
const bundle = await this.retrieveContextBundle(args);
|
|
596
|
+
const ids = [
|
|
597
|
+
...bundle.sections.fix.map((m) => m.id),
|
|
598
|
+
...bundle.sections.doNotDo.map((m) => m.id)
|
|
599
|
+
];
|
|
600
|
+
const graph = await this.getMemoryGraph({
|
|
601
|
+
agentId: args.agentId,
|
|
602
|
+
memoryIds: ids,
|
|
603
|
+
includeNodes: args.includeNodes ?? false,
|
|
604
|
+
includeRelatedTo: args.includeRelatedTo ?? false
|
|
605
|
+
});
|
|
606
|
+
return { bundle, graph };
|
|
607
|
+
}
|
|
481
608
|
async listEpisodes(args = {}) {
|
|
482
609
|
return this.listMemories({ ...args, kind: "episodic" });
|
|
483
610
|
}
|
|
@@ -508,6 +635,22 @@ ${m.content}`).join("");
|
|
|
508
635
|
this.emit({ type: "write", action: "captureEpisode", meta: { runId: args.runId, title } });
|
|
509
636
|
return result;
|
|
510
637
|
}
|
|
638
|
+
async captureUsefulLearning(args) {
|
|
639
|
+
if (args.useful === false) {
|
|
640
|
+
return { saved: [], rejected: [{ title: args.learning.title, reason: "not marked useful" }] };
|
|
641
|
+
}
|
|
642
|
+
const result = await this.saveLearnings({
|
|
643
|
+
agentId: args.agentId,
|
|
644
|
+
sessionId: args.sessionId,
|
|
645
|
+
learnings: [args.learning]
|
|
646
|
+
});
|
|
647
|
+
this.emit({
|
|
648
|
+
type: "write",
|
|
649
|
+
action: "captureUsefulLearning",
|
|
650
|
+
meta: { title: args.learning.title, savedCount: result.saved.length }
|
|
651
|
+
});
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
511
654
|
async captureStepEpisode(args) {
|
|
512
655
|
const title = `Episode ${args.workflowName} - ${args.stepName}`;
|
|
513
656
|
const base = {
|
|
@@ -537,29 +680,45 @@ ${m.content}`).join("");
|
|
|
537
680
|
const used = new Set(fb.usedIds ?? []);
|
|
538
681
|
const useful = new Set(fb.usefulIds ?? []);
|
|
539
682
|
const notUseful = new Set(fb.notUsefulIds ?? []);
|
|
683
|
+
const neutral = new Set(fb.neutralIds ?? []);
|
|
540
684
|
const prevented = new Set(fb.preventedErrorIds ?? []);
|
|
685
|
+
const updateUnratedUsed = fb.updateUnratedUsed ?? true;
|
|
541
686
|
for (const id of prevented) useful.add(id);
|
|
542
687
|
for (const id of useful) notUseful.delete(id);
|
|
688
|
+
for (const id of neutral) notUseful.delete(id);
|
|
543
689
|
for (const id of useful) used.add(id);
|
|
544
690
|
for (const id of notUseful) used.add(id);
|
|
691
|
+
for (const id of neutral) used.add(id);
|
|
545
692
|
const quality = clamp01(fb.metrics?.quality ?? 0.7);
|
|
546
693
|
const hallucRisk = clamp01(fb.metrics?.hallucinationRisk ?? 0.2);
|
|
547
694
|
const baseY = clamp01(quality - 0.7 * hallucRisk);
|
|
548
695
|
const w = 0.5 + 1.5 * quality;
|
|
549
696
|
const yById = /* @__PURE__ */ new Map();
|
|
550
697
|
for (const id of used) {
|
|
551
|
-
|
|
698
|
+
if (useful.has(id)) {
|
|
699
|
+
yById.set(id, baseY);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (notUseful.has(id)) {
|
|
703
|
+
yById.set(id, 0);
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (neutral.has(id) || !updateUnratedUsed) {
|
|
707
|
+
yById.set(id, 0.5);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
yById.set(id, 0);
|
|
552
711
|
}
|
|
553
712
|
const items = [...used].map((memoryId) => ({
|
|
554
713
|
memoryId,
|
|
555
714
|
y: yById.get(memoryId) ?? 0,
|
|
556
715
|
w
|
|
557
716
|
}));
|
|
558
|
-
if (items.length === 0) return;
|
|
717
|
+
if (items.length === 0) return { updated: [] };
|
|
559
718
|
const session = this.client.session("WRITE");
|
|
560
719
|
try {
|
|
561
720
|
await session.run("MERGE (a:Agent {id:$id}) RETURN a", { id: fb.agentId });
|
|
562
|
-
await session.run(this.cyFeedbackBatch, {
|
|
721
|
+
const feedbackRes = await session.run(this.cyFeedbackBatch, {
|
|
563
722
|
agentId: fb.agentId,
|
|
564
723
|
nowIso,
|
|
565
724
|
items,
|
|
@@ -567,6 +726,16 @@ ${m.content}`).join("");
|
|
|
567
726
|
aMin: 1e-3,
|
|
568
727
|
bMin: 1e-3
|
|
569
728
|
});
|
|
729
|
+
const updated = feedbackRes.records.map((rec) => {
|
|
730
|
+
const raw = {
|
|
731
|
+
a: rec.get("a"),
|
|
732
|
+
b: rec.get("b"),
|
|
733
|
+
strength: rec.get("strength"),
|
|
734
|
+
evidence: rec.get("evidence"),
|
|
735
|
+
updatedAt: rec.get("updatedAt")
|
|
736
|
+
};
|
|
737
|
+
return { id: rec.get("id"), edge: toBetaEdge(raw) };
|
|
738
|
+
});
|
|
570
739
|
const ids = [...used];
|
|
571
740
|
const pairs = [];
|
|
572
741
|
for (let i = 0; i < ids.length; i++) {
|
|
@@ -588,6 +757,7 @@ ${m.content}`).join("");
|
|
|
588
757
|
});
|
|
589
758
|
}
|
|
590
759
|
this.emit({ type: "write", action: "feedback", meta: { agentId: fb.agentId, usedCount: used.size } });
|
|
760
|
+
return { updated };
|
|
591
761
|
} finally {
|
|
592
762
|
await session.close();
|
|
593
763
|
}
|
package/package.json
CHANGED
package/dist/index.d.cts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { Session } from 'neo4j-driver';
|
|
2
|
-
|
|
3
|
-
type MemoryKind = "semantic" | "procedural" | "episodic";
|
|
4
|
-
type MemoryPolarity = "positive" | "negative";
|
|
5
|
-
interface EnvironmentFingerprint {
|
|
6
|
-
hash?: string;
|
|
7
|
-
os?: "macos" | "linux" | "windows";
|
|
8
|
-
distro?: string;
|
|
9
|
-
ci?: string;
|
|
10
|
-
container?: boolean;
|
|
11
|
-
filesystem?: string;
|
|
12
|
-
workspaceMount?: "local" | "network" | "bind" | "readonly";
|
|
13
|
-
nodeVersion?: string;
|
|
14
|
-
packageManager?: "npm" | "pnpm" | "yarn";
|
|
15
|
-
pmVersion?: string;
|
|
16
|
-
}
|
|
17
|
-
interface DistilledInvariant {
|
|
18
|
-
invariant: string;
|
|
19
|
-
justification?: string;
|
|
20
|
-
verification?: string[];
|
|
21
|
-
applicability?: string[];
|
|
22
|
-
risks?: string[];
|
|
23
|
-
}
|
|
24
|
-
interface MemoryRecord {
|
|
25
|
-
id: string;
|
|
26
|
-
kind: MemoryKind;
|
|
27
|
-
polarity: MemoryPolarity;
|
|
28
|
-
title: string;
|
|
29
|
-
content: string;
|
|
30
|
-
tags: string[];
|
|
31
|
-
confidence: number;
|
|
32
|
-
utility: number;
|
|
33
|
-
createdAt?: string;
|
|
34
|
-
updatedAt?: string;
|
|
35
|
-
signals?: {
|
|
36
|
-
symptoms?: string[];
|
|
37
|
-
environment?: string[];
|
|
38
|
-
};
|
|
39
|
-
distilled?: {
|
|
40
|
-
invariants?: DistilledInvariant[];
|
|
41
|
-
steps?: string[];
|
|
42
|
-
verificationSteps?: string[];
|
|
43
|
-
gotchas?: string[];
|
|
44
|
-
};
|
|
45
|
-
antiPattern?: {
|
|
46
|
-
action: string;
|
|
47
|
-
whyBad: string;
|
|
48
|
-
saferAlternative?: string;
|
|
49
|
-
};
|
|
50
|
-
env?: EnvironmentFingerprint;
|
|
51
|
-
}
|
|
52
|
-
interface CaseRecord {
|
|
53
|
-
id: string;
|
|
54
|
-
title: string;
|
|
55
|
-
summary: string;
|
|
56
|
-
outcome: "resolved" | "unresolved" | "workaround";
|
|
57
|
-
symptoms: string[];
|
|
58
|
-
env: EnvironmentFingerprint;
|
|
59
|
-
resolvedByMemoryIds: string[];
|
|
60
|
-
negativeMemoryIds: string[];
|
|
61
|
-
resolvedAtIso?: string | null;
|
|
62
|
-
}
|
|
63
|
-
interface RetrieveContextArgs {
|
|
64
|
-
agentId: string;
|
|
65
|
-
prompt: string;
|
|
66
|
-
symptoms?: string[];
|
|
67
|
-
tags?: string[];
|
|
68
|
-
kinds?: MemoryKind[];
|
|
69
|
-
env?: EnvironmentFingerprint;
|
|
70
|
-
baseline?: Record<string, {
|
|
71
|
-
a: number;
|
|
72
|
-
b: number;
|
|
73
|
-
}>;
|
|
74
|
-
caseLimit?: number;
|
|
75
|
-
fixLimit?: number;
|
|
76
|
-
dontLimit?: number;
|
|
77
|
-
nowIso?: string;
|
|
78
|
-
}
|
|
79
|
-
interface BetaEdge {
|
|
80
|
-
a: number;
|
|
81
|
-
b: number;
|
|
82
|
-
/** Posterior mean a/(a+b), cached for query speed */
|
|
83
|
-
strength: number;
|
|
84
|
-
/** Evidence mass a+b, cached for query speed */
|
|
85
|
-
evidence: number;
|
|
86
|
-
updatedAt: string | null;
|
|
87
|
-
}
|
|
88
|
-
type ContextMemoryBase = Pick<MemoryRecord, "id" | "kind" | "polarity" | "title" | "content" | "tags" | "confidence" | "utility" | "updatedAt">;
|
|
89
|
-
type ContextMemorySummary = ContextMemoryBase & {
|
|
90
|
-
/** Posterior snapshot before the current run (baseline) */
|
|
91
|
-
edgeBefore?: BetaEdge;
|
|
92
|
-
/** Posterior snapshot after the current run (undefined until feedback arrives) */
|
|
93
|
-
edgeAfter?: BetaEdge;
|
|
94
|
-
};
|
|
95
|
-
interface ContextBundle {
|
|
96
|
-
sessionId: string;
|
|
97
|
-
sections: {
|
|
98
|
-
fix: ContextMemorySummary[];
|
|
99
|
-
doNotDo: ContextMemorySummary[];
|
|
100
|
-
};
|
|
101
|
-
injection: {
|
|
102
|
-
fixBlock: string;
|
|
103
|
-
doNotDoBlock: string;
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
interface FeedbackMetrics {
|
|
107
|
-
durationMs?: number;
|
|
108
|
-
quality?: number;
|
|
109
|
-
hallucinationRisk?: number;
|
|
110
|
-
toolCalls?: number;
|
|
111
|
-
verificationPassed?: boolean;
|
|
112
|
-
}
|
|
113
|
-
interface MemoryFeedback {
|
|
114
|
-
agentId: string;
|
|
115
|
-
sessionId: string;
|
|
116
|
-
usedIds: string[];
|
|
117
|
-
usefulIds: string[];
|
|
118
|
-
notUsefulIds: string[];
|
|
119
|
-
preventedErrorIds?: string[];
|
|
120
|
-
metrics?: FeedbackMetrics;
|
|
121
|
-
notes?: string;
|
|
122
|
-
}
|
|
123
|
-
interface LearningCandidate {
|
|
124
|
-
kind: MemoryKind;
|
|
125
|
-
polarity?: MemoryPolarity;
|
|
126
|
-
title: string;
|
|
127
|
-
content: string;
|
|
128
|
-
tags: string[];
|
|
129
|
-
confidence: number;
|
|
130
|
-
signals?: MemoryRecord["signals"];
|
|
131
|
-
env?: EnvironmentFingerprint;
|
|
132
|
-
triage?: {
|
|
133
|
-
symptoms: string[];
|
|
134
|
-
likelyCauses: string[];
|
|
135
|
-
verificationSteps?: string[];
|
|
136
|
-
fixSteps?: string[];
|
|
137
|
-
gotchas?: string[];
|
|
138
|
-
};
|
|
139
|
-
antiPattern?: MemoryRecord["antiPattern"];
|
|
140
|
-
}
|
|
141
|
-
interface SaveLearningRequest {
|
|
142
|
-
agentId: string;
|
|
143
|
-
sessionId?: string;
|
|
144
|
-
taskId?: string;
|
|
145
|
-
learnings: LearningCandidate[];
|
|
146
|
-
policy?: {
|
|
147
|
-
minConfidence?: number;
|
|
148
|
-
requireVerificationSteps?: boolean;
|
|
149
|
-
maxItems?: number;
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
interface SaveLearningResult {
|
|
153
|
-
saved: Array<{
|
|
154
|
-
id: string;
|
|
155
|
-
kind: MemoryKind;
|
|
156
|
-
title: string;
|
|
157
|
-
deduped: boolean;
|
|
158
|
-
}>;
|
|
159
|
-
rejected: Array<{
|
|
160
|
-
title: string;
|
|
161
|
-
reason: string;
|
|
162
|
-
}>;
|
|
163
|
-
}
|
|
164
|
-
interface MemoryServiceConfig {
|
|
165
|
-
neo4j: {
|
|
166
|
-
uri: string;
|
|
167
|
-
username: string;
|
|
168
|
-
password: string;
|
|
169
|
-
database?: string;
|
|
170
|
-
};
|
|
171
|
-
vectorIndex?: string;
|
|
172
|
-
fulltextIndex?: string;
|
|
173
|
-
halfLifeSeconds?: number;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
declare class MemoryService {
|
|
177
|
-
private client;
|
|
178
|
-
private vectorIndex;
|
|
179
|
-
private fulltextIndex;
|
|
180
|
-
private halfLifeSeconds;
|
|
181
|
-
private cyUpsertMemory;
|
|
182
|
-
private cyUpsertCase;
|
|
183
|
-
private cyRetrieveBundle;
|
|
184
|
-
private cyFeedbackBatch;
|
|
185
|
-
private cyFeedbackCoUsed;
|
|
186
|
-
private cyGetRecallEdges;
|
|
187
|
-
constructor(cfg: MemoryServiceConfig);
|
|
188
|
-
init(): Promise<void>;
|
|
189
|
-
close(): Promise<void>;
|
|
190
|
-
private ensureEnvHash;
|
|
191
|
-
/**
|
|
192
|
-
* Save a distilled memory (semantic/procedural/episodic) with exact dedupe by contentHash.
|
|
193
|
-
* NOTE: This package intentionally does not store "full answers" as semantic/procedural.
|
|
194
|
-
*/
|
|
195
|
-
upsertMemory(l: LearningCandidate & {
|
|
196
|
-
id?: string;
|
|
197
|
-
}): Promise<{
|
|
198
|
-
id: string;
|
|
199
|
-
deduped: boolean;
|
|
200
|
-
}>;
|
|
201
|
-
/**
|
|
202
|
-
* Upsert an episodic Case (case-based reasoning) that links symptoms + env + resolved_by + negative memories.
|
|
203
|
-
*/
|
|
204
|
-
upsertCase(c: CaseRecord): Promise<string>;
|
|
205
|
-
/**
|
|
206
|
-
* Retrieve a ContextBundle with separate Fix and Do-not-do sections, using case-based reasoning.
|
|
207
|
-
* The key idea: match cases by symptoms + env similarity, then pull linked memories.
|
|
208
|
-
*/
|
|
209
|
-
retrieveContextBundle(args: RetrieveContextArgs): Promise<ContextBundle>;
|
|
210
|
-
/**
|
|
211
|
-
* Reinforce/degrade agent->memory association weights using a single batched Cypher query.
|
|
212
|
-
* This supports mid-run retrieval by making feedback cheap and frequent.
|
|
213
|
-
*/
|
|
214
|
-
feedback(fb: MemoryFeedback): Promise<void>;
|
|
215
|
-
/**
|
|
216
|
-
* Save distilled learnings discovered during a task.
|
|
217
|
-
* Enforces quality gates and stores negative memories explicitly.
|
|
218
|
-
* Automatically creates a Case if learnings have triage.symptoms.
|
|
219
|
-
*/
|
|
220
|
-
saveLearnings(req: SaveLearningRequest): Promise<SaveLearningResult>;
|
|
221
|
-
}
|
|
222
|
-
declare function createMemoryService(cfg: MemoryServiceConfig): Promise<MemoryService>;
|
|
223
|
-
|
|
224
|
-
interface Neo4jClientConfig {
|
|
225
|
-
uri: string;
|
|
226
|
-
username: string;
|
|
227
|
-
password: string;
|
|
228
|
-
database?: string;
|
|
229
|
-
}
|
|
230
|
-
declare class Neo4jClient {
|
|
231
|
-
private driver;
|
|
232
|
-
private database?;
|
|
233
|
-
constructor(cfg: Neo4jClientConfig);
|
|
234
|
-
session(mode?: "READ" | "WRITE"): Session;
|
|
235
|
-
close(): Promise<void>;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
declare function ensureSchema(client: Neo4jClient): Promise<void>;
|
|
239
|
-
|
|
240
|
-
declare function sha256Hex(s: string): string;
|
|
241
|
-
declare function canonicaliseForHash(title: string, content: string, tags: string[]): string;
|
|
242
|
-
declare function newId(prefix: string): string;
|
|
243
|
-
declare function normaliseSymptom(s: string): string;
|
|
244
|
-
declare function envHash(env: EnvironmentFingerprint): string;
|
|
245
|
-
|
|
246
|
-
export { type BetaEdge, type CaseRecord, type ContextBundle, type ContextMemoryBase, type ContextMemorySummary, type DistilledInvariant, type EnvironmentFingerprint, type FeedbackMetrics, type LearningCandidate, type MemoryFeedback, type MemoryKind, type MemoryPolarity, type MemoryRecord, MemoryService, type MemoryServiceConfig, Neo4jClient, type Neo4jClientConfig, type RetrieveContextArgs, type SaveLearningRequest, type SaveLearningResult, canonicaliseForHash, createMemoryService, ensureSchema, envHash, newId, normaliseSymptom, sha256Hex };
|