@devflow-tools/memory-engine 0.13.3 → 0.14.1
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/dist/consolidator.d.ts +13 -0
- package/dist/consolidator.d.ts.map +1 -0
- package/dist/consolidator.js +184 -0
- package/dist/consolidator.js.map +1 -0
- package/dist/event-grouper.d.ts +13 -0
- package/dist/event-grouper.d.ts.map +1 -0
- package/dist/event-grouper.js +178 -0
- package/dist/event-grouper.js.map +1 -0
- package/dist/graph-store.d.ts.map +1 -1
- package/dist/graph-store.js +2 -4
- package/dist/graph-store.js.map +1 -1
- package/dist/hybrid-search.d.ts +7 -4
- package/dist/hybrid-search.d.ts.map +1 -1
- package/dist/hybrid-search.js +69 -8
- package/dist/hybrid-search.js.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/l2-evaluator.d.ts +31 -0
- package/dist/l2-evaluator.d.ts.map +1 -0
- package/dist/l2-evaluator.js +103 -0
- package/dist/l2-evaluator.js.map +1 -0
- package/dist/llm-provider.d.ts +9 -0
- package/dist/llm-provider.d.ts.map +1 -0
- package/dist/llm-provider.js +37 -0
- package/dist/llm-provider.js.map +1 -0
- package/dist/memory-engine.d.ts +27 -4
- package/dist/memory-engine.d.ts.map +1 -1
- package/dist/memory-engine.js +275 -182
- package/dist/memory-engine.js.map +1 -1
- package/dist/memory-gate.d.ts +116 -0
- package/dist/memory-gate.d.ts.map +1 -0
- package/dist/memory-gate.js +385 -0
- package/dist/memory-gate.js.map +1 -0
- package/dist/memory-store.d.ts +234 -32
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/memory-store.js +729 -142
- package/dist/memory-store.js.map +1 -1
- package/dist/project-validator.d.ts +10 -0
- package/dist/project-validator.d.ts.map +1 -0
- package/dist/project-validator.js +36 -0
- package/dist/project-validator.js.map +1 -0
- package/dist/session-summarizer.d.ts +10 -0
- package/dist/session-summarizer.d.ts.map +1 -0
- package/dist/session-summarizer.js +197 -0
- package/dist/session-summarizer.js.map +1 -0
- package/dist/signal-buffer.d.ts +42 -0
- package/dist/signal-buffer.d.ts.map +1 -0
- package/dist/signal-buffer.js +104 -0
- package/dist/signal-buffer.js.map +1 -0
- package/dist/signal-detector.d.ts +39 -0
- package/dist/signal-detector.d.ts.map +1 -0
- package/dist/signal-detector.js +239 -0
- package/dist/signal-detector.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MemoryStore } from './memory-store.js';
|
|
2
|
+
export interface ConsolidatorResult {
|
|
3
|
+
consolidated: number;
|
|
4
|
+
skipped: number;
|
|
5
|
+
provider: string;
|
|
6
|
+
latencyMs: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function consolidate(store: MemoryStore, projectRoot: string, options?: {
|
|
9
|
+
minObservations?: number;
|
|
10
|
+
minSessions?: number;
|
|
11
|
+
maxClusters?: number;
|
|
12
|
+
}): Promise<ConsolidatorResult>;
|
|
13
|
+
//# sourceMappingURL=consolidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../src/consolidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0BrD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACA,OAAO,CAAC,kBAAkB,CAAC,CAuK7B"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { callLLM } from './llm-provider.js';
|
|
2
|
+
// ── Prompt ──────────────────────────────────────────────
|
|
3
|
+
const CONSOLIDATOR_SYSTEM = `You are a knowledge consolidator for DevFlow.
|
|
4
|
+
Given multiple observations about the same concept from different coding sessions,
|
|
5
|
+
produce ONE consolidated memory entry.
|
|
6
|
+
|
|
7
|
+
Output JSON:
|
|
8
|
+
{
|
|
9
|
+
"type": "semantic" | "pattern" | "constraint" | "preference" | "lesson",
|
|
10
|
+
"title": "≤80 chars",
|
|
11
|
+
"content": "1-2 sentence consolidated knowledge",
|
|
12
|
+
"confidence": 0.0-1.0,
|
|
13
|
+
"supersedes_observation_ids": ["obs:...", ...]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Rules:
|
|
17
|
+
- Merge complementary facts; discard contradictory outliers
|
|
18
|
+
- Higher confidence when observations are consistent across sessions
|
|
19
|
+
- If observations disagree, pick the majority view and note uncertainty in content
|
|
20
|
+
- Return ONLY the JSON object`;
|
|
21
|
+
// ── Consolidator ────────────────────────────────────────
|
|
22
|
+
export async function consolidate(store, projectRoot, options) {
|
|
23
|
+
const start = Date.now();
|
|
24
|
+
const minObs = options?.minObservations ?? 3;
|
|
25
|
+
const minSess = options?.minSessions ?? 2;
|
|
26
|
+
// 1. Get concept clusters from unconsolidated observations
|
|
27
|
+
const clusters = store.getClusterCandidates(projectRoot, minObs, minSess);
|
|
28
|
+
if (clusters.length === 0) {
|
|
29
|
+
console.log('[Consolidator] No clusters found (no concept meets cross-session threshold)');
|
|
30
|
+
return { consolidated: 0, skipped: 0, provider: 'none', latencyMs: Date.now() - start };
|
|
31
|
+
}
|
|
32
|
+
console.log('[Consolidator] Clusters formed', {
|
|
33
|
+
count: clusters.length,
|
|
34
|
+
clusters: clusters.map(c => ({
|
|
35
|
+
concept: c.concept,
|
|
36
|
+
observationCount: c.observationCount,
|
|
37
|
+
sessionCount: c.sessionCount,
|
|
38
|
+
})),
|
|
39
|
+
});
|
|
40
|
+
// 2. Fetch full observation data for each cluster
|
|
41
|
+
const clusterData = clusters.map(c => ({
|
|
42
|
+
concept: c.concept,
|
|
43
|
+
observationCount: c.observationCount,
|
|
44
|
+
sessionCount: c.sessionCount,
|
|
45
|
+
observations: c.observationIds
|
|
46
|
+
.map(id => store.getObservation(id))
|
|
47
|
+
.filter(Boolean),
|
|
48
|
+
}));
|
|
49
|
+
// 3. LLM aggregation (1 call for all clusters)
|
|
50
|
+
let provider = 'rules';
|
|
51
|
+
let consolidated = 0;
|
|
52
|
+
try {
|
|
53
|
+
const userPrompt = buildConsolidationPrompt(clusterData);
|
|
54
|
+
const response = await callLLM(CONSOLIDATOR_SYSTEM, userPrompt);
|
|
55
|
+
provider = response.provider;
|
|
56
|
+
if (response.provider !== 'rules') {
|
|
57
|
+
const results = JSON.parse(response.content);
|
|
58
|
+
const entries = Array.isArray(results) ? results : [results];
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
if (!entry.type || !entry.content)
|
|
61
|
+
continue;
|
|
62
|
+
// 4. Write memory
|
|
63
|
+
const memId = `mem:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
|
|
64
|
+
store.add({
|
|
65
|
+
id: memId,
|
|
66
|
+
type: entry.type,
|
|
67
|
+
content: entry.content,
|
|
68
|
+
title: entry.title,
|
|
69
|
+
source: 'consolidation',
|
|
70
|
+
status: 'active',
|
|
71
|
+
confidence: entry.confidence ?? 0.7,
|
|
72
|
+
});
|
|
73
|
+
// 5. Link observations to memory
|
|
74
|
+
const obsIds = entry.supersedes_observation_ids ?? [];
|
|
75
|
+
for (const obsId of obsIds) {
|
|
76
|
+
store.linkObservationToMemory(obsId, memId);
|
|
77
|
+
}
|
|
78
|
+
// 6. Add evidence
|
|
79
|
+
store.addEvidence({
|
|
80
|
+
memoryId: memId,
|
|
81
|
+
type: 'consolidation',
|
|
82
|
+
weight: entry.confidence ?? 0.7,
|
|
83
|
+
sourceObservationIds: obsIds,
|
|
84
|
+
summary: `Consolidated from ${obsIds.length} observations about "${entry.title ?? entry.type}"`,
|
|
85
|
+
});
|
|
86
|
+
// 7. Audit
|
|
87
|
+
store.recordHistory({
|
|
88
|
+
memoryId: memId,
|
|
89
|
+
event: 'ADD',
|
|
90
|
+
newContent: entry.content,
|
|
91
|
+
reason: 'consolidation',
|
|
92
|
+
});
|
|
93
|
+
consolidated++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Rule fallback: pick highest-importance observation per cluster as representative
|
|
98
|
+
for (const cluster of clusterData) {
|
|
99
|
+
const best = cluster.observations.reduce((a, b) => (a?.importance ?? 0) >= (b?.importance ?? 0) ? a : b);
|
|
100
|
+
if (!best)
|
|
101
|
+
continue;
|
|
102
|
+
const memId = `mem:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
|
|
103
|
+
store.add({
|
|
104
|
+
id: memId,
|
|
105
|
+
type: 'semantic',
|
|
106
|
+
content: best.narrative || best.title,
|
|
107
|
+
title: best.title,
|
|
108
|
+
source: 'consolidation',
|
|
109
|
+
status: 'active',
|
|
110
|
+
confidence: best.confidence,
|
|
111
|
+
});
|
|
112
|
+
store.linkObservationToMemory(best.id, memId);
|
|
113
|
+
store.addEvidence({
|
|
114
|
+
memoryId: memId,
|
|
115
|
+
type: 'consolidation',
|
|
116
|
+
weight: best.confidence,
|
|
117
|
+
sourceObservationIds: [best.id],
|
|
118
|
+
summary: `Rule-based consolidation from observation about "${cluster.concept}"`,
|
|
119
|
+
});
|
|
120
|
+
store.recordHistory({
|
|
121
|
+
memoryId: memId,
|
|
122
|
+
event: 'ADD',
|
|
123
|
+
newContent: best.narrative ?? best.title,
|
|
124
|
+
reason: 'consolidation_rule_fallback',
|
|
125
|
+
});
|
|
126
|
+
consolidated++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.warn('[Consolidator] Consolidation failed', { error: err.message });
|
|
132
|
+
// Rule fallback on error
|
|
133
|
+
for (const cluster of clusterData) {
|
|
134
|
+
const best = cluster.observations.reduce((a, b) => (a?.importance ?? 0) >= (b?.importance ?? 0) ? a : b);
|
|
135
|
+
if (!best)
|
|
136
|
+
continue;
|
|
137
|
+
const memId = `mem:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
|
|
138
|
+
store.add({
|
|
139
|
+
id: memId,
|
|
140
|
+
type: 'semantic',
|
|
141
|
+
content: best.narrative || best.title,
|
|
142
|
+
title: best.title,
|
|
143
|
+
source: 'consolidation',
|
|
144
|
+
status: 'active',
|
|
145
|
+
confidence: best.confidence,
|
|
146
|
+
});
|
|
147
|
+
store.linkObservationToMemory(best.id, memId);
|
|
148
|
+
store.addEvidence({
|
|
149
|
+
memoryId: memId,
|
|
150
|
+
type: 'consolidation',
|
|
151
|
+
weight: best.confidence,
|
|
152
|
+
sourceObservationIds: [best.id],
|
|
153
|
+
summary: `Error-fallback consolidation from observation about "${cluster.concept}"`,
|
|
154
|
+
});
|
|
155
|
+
store.recordHistory({
|
|
156
|
+
memoryId: memId,
|
|
157
|
+
event: 'ADD',
|
|
158
|
+
newContent: best.narrative ?? best.title,
|
|
159
|
+
reason: 'consolidation_error_fallback',
|
|
160
|
+
});
|
|
161
|
+
consolidated++;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const latencyMs = Date.now() - start;
|
|
165
|
+
console.log('[Consolidator] Consolidation complete', {
|
|
166
|
+
consolidated,
|
|
167
|
+
skipped: clusters.length - consolidated,
|
|
168
|
+
provider,
|
|
169
|
+
latencyMs,
|
|
170
|
+
});
|
|
171
|
+
return { consolidated, skipped: clusters.length - consolidated, provider, latencyMs };
|
|
172
|
+
}
|
|
173
|
+
// ── Helpers ─────────────────────────────────────────────
|
|
174
|
+
function buildConsolidationPrompt(clusters) {
|
|
175
|
+
const parts = clusters.map((c, i) => {
|
|
176
|
+
const obsText = c.observations
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.map(o => ` - [${o.type}] ${o.title}: ${o.narrative || 'no narrative'} (importance: ${o.importance}, session: ${o.session_id}, id: ${o.id})`)
|
|
179
|
+
.join('\n');
|
|
180
|
+
return `Cluster ${i + 1}: Concept "${c.concept}" (${c.observationCount} observations across ${c.sessionCount} sessions)\n${obsText}`;
|
|
181
|
+
});
|
|
182
|
+
return `Consolidate the following observation clusters into memory entries. Return a JSON array with one entry per cluster.\n\n${parts.join('\n\n')}`;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=consolidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator.js","sourceRoot":"","sources":["../src/consolidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EventRow } from './memory-store.js';
|
|
2
|
+
export interface EventGroup {
|
|
3
|
+
id: string;
|
|
4
|
+
events: EventRow[];
|
|
5
|
+
primaryTool: string;
|
|
6
|
+
hasError: boolean;
|
|
7
|
+
hasFix: boolean;
|
|
8
|
+
signalTypes: string[];
|
|
9
|
+
summary: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function groupEvents(events: EventRow[]): EventGroup[];
|
|
12
|
+
export declare function triageGroups(groups: EventGroup[], maxGroups?: number): EventGroup[];
|
|
13
|
+
//# sourceMappingURL=event-grouper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-grouper.d.ts","sourceRoot":"","sources":["../src/event-grouper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKlD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAeD,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,CA4D5D;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,SAAS,SAAI,GAAG,UAAU,EAAE,CAqC9E"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// ── Constants ──────────────────────────────────────────
|
|
2
|
+
const LOW_VALUE_TOOLS = new Set([
|
|
3
|
+
'Glob', 'Grep',
|
|
4
|
+
]);
|
|
5
|
+
const LOW_VALUE_FIRST_WORDS = new Set([
|
|
6
|
+
'ls', 'cat', 'grep', 'echo', 'cd', 'pwd', 'which',
|
|
7
|
+
'head', 'tail', 'wc', 'date',
|
|
8
|
+
]);
|
|
9
|
+
// ── Event Grouper ──────────────────────────────────────
|
|
10
|
+
export function groupEvents(events) {
|
|
11
|
+
const groups = [];
|
|
12
|
+
let currentGroup = [];
|
|
13
|
+
let groupIdx = 0;
|
|
14
|
+
for (let i = 0; i < events.length; i++) {
|
|
15
|
+
const e = events[i];
|
|
16
|
+
// Start new group on context boundaries
|
|
17
|
+
if (currentGroup.length > 0) {
|
|
18
|
+
const prev = currentGroup[currentGroup.length - 1];
|
|
19
|
+
const timeGap = e.created_at - prev.created_at;
|
|
20
|
+
const isNewGroup = timeGap > 120_000 || // >2 min gap
|
|
21
|
+
isContextSwitch(prev, e) ||
|
|
22
|
+
currentGroup.length >= 10; // max 10 events per group
|
|
23
|
+
if (isNewGroup) {
|
|
24
|
+
const group = buildGroup(currentGroup, groupIdx++);
|
|
25
|
+
if (group)
|
|
26
|
+
groups.push(group);
|
|
27
|
+
currentGroup = [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
currentGroup.push(e);
|
|
31
|
+
// Error events create their own micro-group (error + follow-up fix)
|
|
32
|
+
if (isErrorEvent(e) && currentGroup.length > 1) {
|
|
33
|
+
// Take error + next few events as a fix attempt group
|
|
34
|
+
const fixGroup = [e];
|
|
35
|
+
let j = i + 1;
|
|
36
|
+
while (j < events.length && j <= i + 5) {
|
|
37
|
+
const next = events[j];
|
|
38
|
+
if (isRelatedFix(e, next) || j - i <= 1) {
|
|
39
|
+
fixGroup.push(next);
|
|
40
|
+
j++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (fixGroup.length > 1) {
|
|
47
|
+
const group = buildGroup(fixGroup, groupIdx++);
|
|
48
|
+
if (group)
|
|
49
|
+
groups.push(group);
|
|
50
|
+
currentGroup = []; // reset current
|
|
51
|
+
i = j - 1; // skip consumed events
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Flush remaining
|
|
56
|
+
if (currentGroup.length > 0) {
|
|
57
|
+
const group = buildGroup(currentGroup, groupIdx++);
|
|
58
|
+
if (group)
|
|
59
|
+
groups.push(group);
|
|
60
|
+
}
|
|
61
|
+
console.log('[EventGrouper] Groups formed', {
|
|
62
|
+
eventCount: events.length,
|
|
63
|
+
groupCount: groups.length,
|
|
64
|
+
});
|
|
65
|
+
return groups;
|
|
66
|
+
}
|
|
67
|
+
// ── Triage ─────────────────────────────────────────────
|
|
68
|
+
export function triageGroups(groups, maxGroups = 8) {
|
|
69
|
+
const before = groups.length;
|
|
70
|
+
// Filter: remove pure-query groups
|
|
71
|
+
const filtered = groups.filter(g => {
|
|
72
|
+
// Keep groups with errors
|
|
73
|
+
if (g.hasError)
|
|
74
|
+
return true;
|
|
75
|
+
// Keep groups with fixes
|
|
76
|
+
if (g.hasFix)
|
|
77
|
+
return true;
|
|
78
|
+
// Remove groups that are only low-value tools
|
|
79
|
+
const onlyLowValue = g.events.every(e => {
|
|
80
|
+
if (LOW_VALUE_TOOLS.has(e.tool))
|
|
81
|
+
return true;
|
|
82
|
+
if (e.tool === 'Bash' && e.command) {
|
|
83
|
+
const firstWord = e.command.split(' ')[0] ?? '';
|
|
84
|
+
return LOW_VALUE_FIRST_WORDS.has(firstWord);
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
});
|
|
88
|
+
return !onlyLowValue;
|
|
89
|
+
});
|
|
90
|
+
// Sort: groups with errors/signals first, then by size desc
|
|
91
|
+
filtered.sort((a, b) => {
|
|
92
|
+
const aPriority = (a.hasError ? 2 : 0) + (a.hasFix ? 1 : 0) + a.signalTypes.length;
|
|
93
|
+
const bPriority = (b.hasError ? 2 : 0) + (b.hasFix ? 1 : 0) + b.signalTypes.length;
|
|
94
|
+
if (bPriority !== aPriority)
|
|
95
|
+
return bPriority - aPriority;
|
|
96
|
+
return b.events.length - a.events.length;
|
|
97
|
+
});
|
|
98
|
+
const result = filtered.slice(0, maxGroups);
|
|
99
|
+
console.log('[EventGrouper] Groups triaged', {
|
|
100
|
+
inputCount: before,
|
|
101
|
+
outputCount: result.length,
|
|
102
|
+
discardedCount: before - result.length,
|
|
103
|
+
});
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
// ── Helpers ────────────────────────────────────────────
|
|
107
|
+
function buildGroup(events, idx) {
|
|
108
|
+
if (events.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
const tools = events.map(e => e.tool);
|
|
111
|
+
const primaryTool = mostFrequent(tools);
|
|
112
|
+
const hasError = events.some(e => e.exit_code !== null && e.exit_code !== undefined && e.exit_code !== 0);
|
|
113
|
+
const hasFix = events.some(e => e.exit_code === 0) && hasError;
|
|
114
|
+
// Generate short summary from the events
|
|
115
|
+
const summaryEvents = events
|
|
116
|
+
.filter(e => e.tool !== 'Grep' && e.tool !== 'Glob')
|
|
117
|
+
.slice(0, 3);
|
|
118
|
+
const summary = summaryEvents
|
|
119
|
+
.map(e => {
|
|
120
|
+
const tool = e.tool === 'Bash' ? (e.command?.split(' ')[0] ?? 'bash') : e.tool;
|
|
121
|
+
return e.exit_code !== 0 ? `${tool}(error)` : tool;
|
|
122
|
+
})
|
|
123
|
+
.join(' → ');
|
|
124
|
+
return {
|
|
125
|
+
id: `group:${idx}`,
|
|
126
|
+
events,
|
|
127
|
+
primaryTool,
|
|
128
|
+
hasError,
|
|
129
|
+
hasFix,
|
|
130
|
+
signalTypes: [],
|
|
131
|
+
summary: summary || `${events.length} tool calls with ${primaryTool}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function isErrorEvent(e) {
|
|
135
|
+
return e.exit_code !== null && e.exit_code !== undefined && e.exit_code !== 0;
|
|
136
|
+
}
|
|
137
|
+
function isContextSwitch(prev, next) {
|
|
138
|
+
const BASH = 'Bash';
|
|
139
|
+
const FILE_TOOLS = new Set(['Read', 'Edit', 'Write']);
|
|
140
|
+
// Bash → File read is continuation
|
|
141
|
+
if (prev.tool === BASH && FILE_TOOLS.has(next.tool))
|
|
142
|
+
return false;
|
|
143
|
+
// File read → Edit is continuation
|
|
144
|
+
if (FILE_TOOLS.has(prev.tool) && FILE_TOOLS.has(next.tool))
|
|
145
|
+
return false;
|
|
146
|
+
// Agent spawn → anything is new context
|
|
147
|
+
if (prev.tool === 'Agent')
|
|
148
|
+
return true;
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
function isRelatedFix(errorEvent, nextEvent) {
|
|
152
|
+
// Same tool/command base
|
|
153
|
+
if (errorEvent.tool === nextEvent.tool)
|
|
154
|
+
return true;
|
|
155
|
+
if (errorEvent.command && nextEvent.command) {
|
|
156
|
+
const errBase = errorEvent.command.split(' ')[0];
|
|
157
|
+
const nextBase = nextEvent.command.split(' ')[0];
|
|
158
|
+
if (errBase === nextBase)
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
function mostFrequent(arr) {
|
|
164
|
+
const counts = new Map();
|
|
165
|
+
for (const item of arr) {
|
|
166
|
+
counts.set(item, (counts.get(item) ?? 0) + 1);
|
|
167
|
+
}
|
|
168
|
+
let best = arr[0] ?? 'unknown';
|
|
169
|
+
let bestCount = 0;
|
|
170
|
+
for (const [item, count] of counts) {
|
|
171
|
+
if (count > bestCount) {
|
|
172
|
+
best = item;
|
|
173
|
+
bestCount = count;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return best;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=event-grouper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-grouper.js","sourceRoot":"","sources":["../src/event-grouper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-store.d.ts","sourceRoot":"","sources":["../src/graph-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"graph-store.d.ts","sourceRoot":"","sources":["../src/graph-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAgBrD,qBAAa,UAAU;IACT,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,WAAW;IAEhC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBjD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,SAAI,GAAG,MAAM,EAAE;CA8B/D"}
|
package/dist/graph-store.js
CHANGED
|
@@ -11,7 +11,6 @@ function extractEntities(text) {
|
|
|
11
11
|
entities.push(...compounds);
|
|
12
12
|
return [...new Set(entities)];
|
|
13
13
|
}
|
|
14
|
-
const RELATED_RELATIONS = ['related_to', 'similar_to', 'same_category', 'same_scope'];
|
|
15
14
|
export class GraphStore {
|
|
16
15
|
store;
|
|
17
16
|
constructor(store) {
|
|
@@ -28,9 +27,8 @@ export class GraphStore {
|
|
|
28
27
|
for (const match of matches) {
|
|
29
28
|
if (match.id === memoryId)
|
|
30
29
|
continue;
|
|
31
|
-
|
|
32
|
-
this.store.addEdge(
|
|
33
|
-
this.store.addEdge(match.id, memoryId, relation, 0.7);
|
|
30
|
+
this.store.addEdge(memoryId, match.id, 'related_to', 0.7);
|
|
31
|
+
this.store.addEdge(match.id, memoryId, 'related_to', 0.7);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
catch { /* non-fatal */ }
|
package/dist/graph-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-store.js","sourceRoot":"","sources":["../src/graph-store.ts"],"names":[],"mappings":"AAEA,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,IAAI,EAAE,CAAC;IACjE,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"graph-store.js","sourceRoot":"","sources":["../src/graph-store.ts"],"names":[],"mappings":"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"}
|
package/dist/hybrid-search.d.ts
CHANGED
|
@@ -8,16 +8,18 @@ export interface SearchOptions {
|
|
|
8
8
|
}
|
|
9
9
|
export interface SearchResultEntry {
|
|
10
10
|
id: string;
|
|
11
|
-
|
|
11
|
+
type: string;
|
|
12
|
+
category: string;
|
|
13
|
+
title: string;
|
|
12
14
|
content: string;
|
|
13
15
|
originalText?: string;
|
|
14
|
-
source:
|
|
15
|
-
status:
|
|
16
|
+
source: string;
|
|
17
|
+
status: string;
|
|
16
18
|
confidence: number;
|
|
19
|
+
strength: number;
|
|
17
20
|
scope: string;
|
|
18
21
|
sessionId?: string;
|
|
19
22
|
createdAt: number;
|
|
20
|
-
approvedAt?: number;
|
|
21
23
|
}
|
|
22
24
|
export interface SearchResult {
|
|
23
25
|
entry: SearchResultEntry;
|
|
@@ -28,6 +30,7 @@ export declare class HybridSearch {
|
|
|
28
30
|
private embedder;
|
|
29
31
|
private engine;
|
|
30
32
|
constructor(store: MemoryStore, embedder: EmbeddingProvider, engine?: MemoryEngine);
|
|
33
|
+
private rerankWithLLM;
|
|
31
34
|
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
32
35
|
}
|
|
33
36
|
//# sourceMappingURL=hybrid-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid-search.d.ts","sourceRoot":"","sources":["../src/hybrid-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"hybrid-search.d.ts","sourceRoot":"","sources":["../src/hybrid-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAwDD,qBAAa,YAAY;IAIrB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,MAAM,CAAsB;gBAG1B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,iBAAiB,EACnC,MAAM,CAAC,EAAE,YAAY;YAKT,aAAa;IA0CrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA4HlF"}
|
package/dist/hybrid-search.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { callLLM } from './llm-provider.js';
|
|
2
|
+
const DEFAULT_WEIGHTS = { bm25: 0.3, vector: 0.4, graph: 0.2, retention: 0.1 };
|
|
2
3
|
const RRF_K = 60;
|
|
3
4
|
function rrfFusion(results, k) {
|
|
4
5
|
const scored = [];
|
|
@@ -22,18 +23,21 @@ function rrfFusion(results, k) {
|
|
|
22
23
|
return scored;
|
|
23
24
|
}
|
|
24
25
|
function rowToEntry(row) {
|
|
26
|
+
const mappedStatus = row.status === 'active' ? 'approved' : row.status;
|
|
25
27
|
return {
|
|
26
28
|
id: row.id,
|
|
27
|
-
|
|
29
|
+
type: row.type,
|
|
30
|
+
category: row.type,
|
|
31
|
+
title: row.title,
|
|
28
32
|
content: row.content,
|
|
29
33
|
originalText: row.original_text ?? undefined,
|
|
30
34
|
source: row.source,
|
|
31
|
-
status:
|
|
35
|
+
status: mappedStatus,
|
|
32
36
|
confidence: row.confidence,
|
|
37
|
+
strength: row.strength,
|
|
33
38
|
scope: row.scope,
|
|
34
39
|
sessionId: row.session_id ?? undefined,
|
|
35
40
|
createdAt: row.created_at,
|
|
36
|
-
approvedAt: row.approved_at ?? undefined,
|
|
37
41
|
};
|
|
38
42
|
}
|
|
39
43
|
const MIN_EFFECTIVE_CONFIDENCE = 0.3;
|
|
@@ -46,6 +50,40 @@ export class HybridSearch {
|
|
|
46
50
|
this.embedder = embedder;
|
|
47
51
|
this.engine = engine ?? null;
|
|
48
52
|
}
|
|
53
|
+
async rerankWithLLM(query, fused, docs) {
|
|
54
|
+
const candidates = fused.slice(0, Math.min(fused.length, 20));
|
|
55
|
+
if (candidates.length <= 1)
|
|
56
|
+
return fused;
|
|
57
|
+
const candidateList = candidates.map((c, i) => {
|
|
58
|
+
const doc = docs.get(c.id);
|
|
59
|
+
return `${i + 1}. [${c.id}] ${doc?.entry.title ?? 'Untitled'}\n ${(doc?.entry.content ?? '').slice(0, 200)}`;
|
|
60
|
+
}).join('\n\n');
|
|
61
|
+
const systemPrompt = 'You are a search relevance ranker. Return only the memory IDs in ranked order, one per line.';
|
|
62
|
+
const userMessage = `Query: "${query}"\n\nCandidates:\n${candidateList}\n\nRank these candidates by relevance to the query. Return only IDs, one per line:`;
|
|
63
|
+
try {
|
|
64
|
+
const response = await callLLM(systemPrompt, userMessage);
|
|
65
|
+
if (response.provider === 'rules')
|
|
66
|
+
return fused;
|
|
67
|
+
const llmRanked = new Map();
|
|
68
|
+
for (const line of response.content.split('\n')) {
|
|
69
|
+
const idMatch = line.match(/(mem:[a-zA-Z0-9:_-]+)/);
|
|
70
|
+
if (idMatch) {
|
|
71
|
+
llmRanked.set(idMatch[1], llmRanked.size);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (llmRanked.size === 0)
|
|
75
|
+
return fused;
|
|
76
|
+
const rest = fused.slice(candidates.length);
|
|
77
|
+
const ranked = candidates
|
|
78
|
+
.filter(c => llmRanked.has(c.id))
|
|
79
|
+
.sort((a, b) => llmRanked.get(a.id) - llmRanked.get(b.id));
|
|
80
|
+
const unranked = candidates.filter(c => !llmRanked.has(c.id));
|
|
81
|
+
return [...ranked, ...unranked, ...rest];
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return fused;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
49
87
|
async search(query, options = {}) {
|
|
50
88
|
const limit = options.limit ?? 10;
|
|
51
89
|
const fetchLimit = limit * 3;
|
|
@@ -64,7 +102,8 @@ export class HybridSearch {
|
|
|
64
102
|
try {
|
|
65
103
|
const simpleEntities = query.split(/\s+/).filter(w => w.length > 3);
|
|
66
104
|
for (const entity of simpleEntities.slice(0, 5)) {
|
|
67
|
-
const
|
|
105
|
+
const entityEmbedding = await this.embedder.embed(entity);
|
|
106
|
+
const entityMatches = this.store.searchVector(entityEmbedding, 3);
|
|
68
107
|
for (const { id } of entityMatches) {
|
|
69
108
|
if (!graphIds.has(id)) {
|
|
70
109
|
graphIds.add(id);
|
|
@@ -101,9 +140,26 @@ export class HybridSearch {
|
|
|
101
140
|
docs.get(id)?.ranks.set('graph', graphRank++);
|
|
102
141
|
}
|
|
103
142
|
}
|
|
143
|
+
// Retention signal (4th RRF channel, weight 0.1)
|
|
144
|
+
try {
|
|
145
|
+
const retentionRanked = [];
|
|
146
|
+
for (const [id] of docs) {
|
|
147
|
+
const retention = this.store.getRetentionScore(id);
|
|
148
|
+
if (retention) {
|
|
149
|
+
retentionRanked.push({ id, score: retention.score });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
retentionRanked.sort((a, b) => b.score - a.score);
|
|
153
|
+
for (let i = 0; i < retentionRanked.length; i++) {
|
|
154
|
+
docs.get(retentionRanked[i].id)?.ranks.set('retention', i + 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch { /* retention lookup failure is non-fatal */ }
|
|
104
158
|
const fused = rrfFusion(docs, RRF_K);
|
|
159
|
+
// LLM rerank (only when LLM is available)
|
|
160
|
+
const ranked = await this.rerankWithLLM(query, fused, docs);
|
|
105
161
|
const sessionCount = new Map();
|
|
106
|
-
const diverse = new Set(
|
|
162
|
+
const diverse = new Set(ranked.map(r => {
|
|
107
163
|
const doc = docs.get(r.id);
|
|
108
164
|
const sid = doc?.entry.sessionId;
|
|
109
165
|
if (sid) {
|
|
@@ -115,13 +171,13 @@ export class HybridSearch {
|
|
|
115
171
|
return r.id;
|
|
116
172
|
}).filter(Boolean));
|
|
117
173
|
const results = [];
|
|
118
|
-
for (const { id, score } of
|
|
174
|
+
for (const { id, score } of ranked) {
|
|
119
175
|
if (!diverse.has(id))
|
|
120
176
|
continue;
|
|
121
177
|
const doc = docs.get(id);
|
|
122
178
|
if (!doc)
|
|
123
179
|
continue;
|
|
124
|
-
if (options.categories?.length && !options.categories.includes(doc.entry.
|
|
180
|
+
if (options.categories?.length && !options.categories.includes(doc.entry.type))
|
|
125
181
|
continue;
|
|
126
182
|
if (options.scope && doc.entry.scope !== options.scope)
|
|
127
183
|
continue;
|
|
@@ -140,6 +196,11 @@ export class HybridSearch {
|
|
|
140
196
|
}
|
|
141
197
|
}
|
|
142
198
|
results.push({ entry: doc.entry, score: Math.round(score * 10000) / 10000 });
|
|
199
|
+
// Record access so confidence decay slows with use
|
|
200
|
+
try {
|
|
201
|
+
this.store.incrementAccess(doc.entry.id);
|
|
202
|
+
}
|
|
203
|
+
catch { /* non-critical */ }
|
|
143
204
|
if (results.length >= limit)
|
|
144
205
|
break;
|
|
145
206
|
}
|