stellar-memory 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/LICENSE +21 -0
- package/README.md +362 -0
- package/dist/api/routes/analytics.d.ts +15 -0
- package/dist/api/routes/analytics.js +131 -0
- package/dist/api/routes/analytics.js.map +1 -0
- package/dist/api/routes/conflicts.d.ts +12 -0
- package/dist/api/routes/conflicts.js +67 -0
- package/dist/api/routes/conflicts.js.map +1 -0
- package/dist/api/routes/consolidation.d.ts +11 -0
- package/dist/api/routes/consolidation.js +63 -0
- package/dist/api/routes/consolidation.js.map +1 -0
- package/dist/api/routes/constellation.d.ts +4 -0
- package/dist/api/routes/constellation.js +84 -0
- package/dist/api/routes/constellation.js.map +1 -0
- package/dist/api/routes/memories.d.ts +4 -0
- package/dist/api/routes/memories.js +219 -0
- package/dist/api/routes/memories.js.map +1 -0
- package/dist/api/routes/observations.d.ts +10 -0
- package/dist/api/routes/observations.js +42 -0
- package/dist/api/routes/observations.js.map +1 -0
- package/dist/api/routes/orbit.d.ts +4 -0
- package/dist/api/routes/orbit.js +71 -0
- package/dist/api/routes/orbit.js.map +1 -0
- package/dist/api/routes/projects.d.ts +15 -0
- package/dist/api/routes/projects.js +121 -0
- package/dist/api/routes/projects.js.map +1 -0
- package/dist/api/routes/scan.d.ts +4 -0
- package/dist/api/routes/scan.js +403 -0
- package/dist/api/routes/scan.js.map +1 -0
- package/dist/api/routes/sun.d.ts +4 -0
- package/dist/api/routes/sun.js +43 -0
- package/dist/api/routes/sun.js.map +1 -0
- package/dist/api/routes/system.d.ts +4 -0
- package/dist/api/routes/system.js +70 -0
- package/dist/api/routes/system.js.map +1 -0
- package/dist/api/routes/temporal.d.ts +13 -0
- package/dist/api/routes/temporal.js +82 -0
- package/dist/api/routes/temporal.js.map +1 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.js +99 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/websocket.d.ts +53 -0
- package/dist/api/websocket.js +168 -0
- package/dist/api/websocket.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +10 -0
- package/dist/cli/init.js +163 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/engine/analytics.d.ts +93 -0
- package/dist/engine/analytics.js +437 -0
- package/dist/engine/analytics.js.map +1 -0
- package/dist/engine/conflict.d.ts +54 -0
- package/dist/engine/conflict.js +322 -0
- package/dist/engine/conflict.js.map +1 -0
- package/dist/engine/consolidation.d.ts +83 -0
- package/dist/engine/consolidation.js +368 -0
- package/dist/engine/consolidation.js.map +1 -0
- package/dist/engine/constellation.d.ts +66 -0
- package/dist/engine/constellation.js +382 -0
- package/dist/engine/constellation.js.map +1 -0
- package/dist/engine/corona.d.ts +53 -0
- package/dist/engine/corona.js +181 -0
- package/dist/engine/corona.js.map +1 -0
- package/dist/engine/embedding.d.ts +44 -0
- package/dist/engine/embedding.js +168 -0
- package/dist/engine/embedding.js.map +1 -0
- package/dist/engine/gravity.d.ts +63 -0
- package/dist/engine/gravity.js +121 -0
- package/dist/engine/gravity.js.map +1 -0
- package/dist/engine/multiproject.d.ts +75 -0
- package/dist/engine/multiproject.js +241 -0
- package/dist/engine/multiproject.js.map +1 -0
- package/dist/engine/observation.d.ts +82 -0
- package/dist/engine/observation.js +357 -0
- package/dist/engine/observation.js.map +1 -0
- package/dist/engine/orbit.d.ts +91 -0
- package/dist/engine/orbit.js +249 -0
- package/dist/engine/orbit.js.map +1 -0
- package/dist/engine/planet.d.ts +64 -0
- package/dist/engine/planet.js +432 -0
- package/dist/engine/planet.js.map +1 -0
- package/dist/engine/procedural.d.ts +71 -0
- package/dist/engine/procedural.js +259 -0
- package/dist/engine/procedural.js.map +1 -0
- package/dist/engine/quality.d.ts +48 -0
- package/dist/engine/quality.js +245 -0
- package/dist/engine/quality.js.map +1 -0
- package/dist/engine/repository.d.ts +79 -0
- package/dist/engine/repository.js +13 -0
- package/dist/engine/repository.js.map +1 -0
- package/dist/engine/sun.d.ts +61 -0
- package/dist/engine/sun.js +240 -0
- package/dist/engine/sun.js.map +1 -0
- package/dist/engine/temporal.d.ts +67 -0
- package/dist/engine/temporal.js +283 -0
- package/dist/engine/temporal.js.map +1 -0
- package/dist/engine/types.d.ts +179 -0
- package/dist/engine/types.js +27 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/connector-registry.d.ts +20 -0
- package/dist/mcp/connector-registry.js +35 -0
- package/dist/mcp/connector-registry.js.map +1 -0
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +242 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/daemon-tool.d.ts +16 -0
- package/dist/mcp/tools/daemon-tool.js +58 -0
- package/dist/mcp/tools/daemon-tool.js.map +1 -0
- package/dist/mcp/tools/ingestion-tools.d.ts +20 -0
- package/dist/mcp/tools/ingestion-tools.js +34 -0
- package/dist/mcp/tools/ingestion-tools.js.map +1 -0
- package/dist/mcp/tools/memory-tools.d.ts +122 -0
- package/dist/mcp/tools/memory-tools.js +1037 -0
- package/dist/mcp/tools/memory-tools.js.map +1 -0
- package/dist/scanner/cloud/github.d.ts +34 -0
- package/dist/scanner/cloud/github.js +260 -0
- package/dist/scanner/cloud/github.js.map +1 -0
- package/dist/scanner/cloud/google-drive.d.ts +30 -0
- package/dist/scanner/cloud/google-drive.js +289 -0
- package/dist/scanner/cloud/google-drive.js.map +1 -0
- package/dist/scanner/cloud/notion.d.ts +33 -0
- package/dist/scanner/cloud/notion.js +231 -0
- package/dist/scanner/cloud/notion.js.map +1 -0
- package/dist/scanner/cloud/slack.d.ts +38 -0
- package/dist/scanner/cloud/slack.js +282 -0
- package/dist/scanner/cloud/slack.js.map +1 -0
- package/dist/scanner/cloud/types.d.ts +73 -0
- package/dist/scanner/cloud/types.js +9 -0
- package/dist/scanner/cloud/types.js.map +1 -0
- package/dist/scanner/index.d.ts +35 -0
- package/dist/scanner/index.js +420 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/local/filesystem.d.ts +33 -0
- package/dist/scanner/local/filesystem.js +203 -0
- package/dist/scanner/local/filesystem.js.map +1 -0
- package/dist/scanner/local/git.d.ts +24 -0
- package/dist/scanner/local/git.js +161 -0
- package/dist/scanner/local/git.js.map +1 -0
- package/dist/scanner/local/parsers/code.d.ts +3 -0
- package/dist/scanner/local/parsers/code.js +127 -0
- package/dist/scanner/local/parsers/code.js.map +1 -0
- package/dist/scanner/local/parsers/index.d.ts +11 -0
- package/dist/scanner/local/parsers/index.js +24 -0
- package/dist/scanner/local/parsers/index.js.map +1 -0
- package/dist/scanner/local/parsers/json-parser.d.ts +3 -0
- package/dist/scanner/local/parsers/json-parser.js +117 -0
- package/dist/scanner/local/parsers/json-parser.js.map +1 -0
- package/dist/scanner/local/parsers/markdown.d.ts +3 -0
- package/dist/scanner/local/parsers/markdown.js +120 -0
- package/dist/scanner/local/parsers/markdown.js.map +1 -0
- package/dist/scanner/local/parsers/text.d.ts +3 -0
- package/dist/scanner/local/parsers/text.js +41 -0
- package/dist/scanner/local/parsers/text.js.map +1 -0
- package/dist/scanner/metadata-scanner.d.ts +67 -0
- package/dist/scanner/metadata-scanner.js +356 -0
- package/dist/scanner/metadata-scanner.js.map +1 -0
- package/dist/scanner/types.d.ts +47 -0
- package/dist/scanner/types.js +19 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/service/daemon.d.ts +23 -0
- package/dist/service/daemon.js +105 -0
- package/dist/service/daemon.js.map +1 -0
- package/dist/service/scheduler.d.ts +73 -0
- package/dist/service/scheduler.js +281 -0
- package/dist/service/scheduler.js.map +1 -0
- package/dist/storage/database.d.ts +10 -0
- package/dist/storage/database.js +265 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/queries.d.ts +85 -0
- package/dist/storage/queries.js +865 -0
- package/dist/storage/queries.js.map +1 -0
- package/dist/storage/sqlite-repository.d.ts +32 -0
- package/dist/storage/sqlite-repository.js +68 -0
- package/dist/storage/sqlite-repository.js.map +1 -0
- package/dist/storage/vec.d.ts +62 -0
- package/dist/storage/vec.js +111 -0
- package/dist/storage/vec.js.map +1 -0
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +60 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/time.d.ts +21 -0
- package/dist/utils/time.js +42 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/tokenizer.d.ts +13 -0
- package/dist/utils/tokenizer.js +46 -0
- package/dist/utils/tokenizer.js.map +1 -0
- package/package.json +77 -0
- package/scripts/check-node.mjs +36 -0
- package/scripts/setup.mjs +157 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* procedural.ts — Procedural Memory ("Navigation Rules")
|
|
3
|
+
*
|
|
4
|
+
* Procedural memories are behavioral rules learned from patterns in other
|
|
5
|
+
* memories. They represent "how we do things here" — conventions, workflows,
|
|
6
|
+
* and recurring solutions extracted from observed repetition.
|
|
7
|
+
*
|
|
8
|
+
* Key behaviors:
|
|
9
|
+
* - Pattern detection: group memories by tags, find groups with 3+ members
|
|
10
|
+
* - Rule creation: procedural memories start with high impact (0.9)
|
|
11
|
+
* - Slow decay: procedural memories decay at 30% of the normal rate
|
|
12
|
+
* - Sun integration: top 5 rules appear in a dedicated section
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
15
|
+
import { getMemoriesByProject, insertMemory } from '../storage/queries.js';
|
|
16
|
+
import { getConfig } from '../utils/config.js';
|
|
17
|
+
import { recencyScore, frequencyScore, importanceToDistance, } from './orbit.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Pattern detection
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Group memories by individual tag. Only groups with 3+ memories are kept —
|
|
23
|
+
* anything below that threshold is not a stable enough pattern to act on.
|
|
24
|
+
*/
|
|
25
|
+
function findRepeatedPatterns(memories) {
|
|
26
|
+
const tagGroups = new Map();
|
|
27
|
+
for (const memory of memories) {
|
|
28
|
+
const tags = Array.isArray(memory.tags) ? memory.tags : [];
|
|
29
|
+
for (const tag of tags) {
|
|
30
|
+
if (!tagGroups.has(tag))
|
|
31
|
+
tagGroups.set(tag, []);
|
|
32
|
+
tagGroups.get(tag).push(memory);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Only keep groups with 3+ memories
|
|
36
|
+
return new Map([...tagGroups].filter(([, mems]) => mems.length >= 3));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the most representative keywords from a group of memories.
|
|
40
|
+
* Uses the most common non-trivial words across all content.
|
|
41
|
+
*/
|
|
42
|
+
function extractGroupKeywords(memories) {
|
|
43
|
+
const stopWords = new Set([
|
|
44
|
+
'the', 'a', 'an', 'is', 'it', 'in', 'on', 'at', 'to', 'for',
|
|
45
|
+
'of', 'and', 'or', 'but', 'with', 'from', 'that', 'this', 'was',
|
|
46
|
+
'are', 'be', 'been', 'been', 'by', 'as', 'not', 'use', 'used',
|
|
47
|
+
]);
|
|
48
|
+
const wordCount = new Map();
|
|
49
|
+
for (const mem of memories) {
|
|
50
|
+
const words = (mem.content + ' ' + mem.summary)
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
.split(/[\s,.\-:;()\[\]{}'"!?/\\]+/)
|
|
53
|
+
.filter(w => w.length > 3 && !stopWords.has(w));
|
|
54
|
+
for (const w of words) {
|
|
55
|
+
wordCount.set(w, (wordCount.get(w) ?? 0) + 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return [...wordCount.entries()]
|
|
59
|
+
.sort((a, b) => b[1] - a[1])
|
|
60
|
+
.slice(0, 5)
|
|
61
|
+
.map(([w]) => w);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Determine if a tag group shares a consistent memory type (majority rules).
|
|
65
|
+
*/
|
|
66
|
+
function dominantType(memories) {
|
|
67
|
+
const typeCounts = new Map();
|
|
68
|
+
for (const m of memories) {
|
|
69
|
+
typeCounts.set(m.type, (typeCounts.get(m.type) ?? 0) + 1);
|
|
70
|
+
}
|
|
71
|
+
const dominant = [...typeCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
72
|
+
if (!dominant)
|
|
73
|
+
return null;
|
|
74
|
+
// Return the dominant type only if it represents at least 60% of the group
|
|
75
|
+
return dominant[1] / memories.length >= 0.6 ? dominant[0] : null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Detect patterns in the memory corpus that are strong enough to warrant
|
|
79
|
+
* a procedural rule.
|
|
80
|
+
*
|
|
81
|
+
* Returns one candidate per tag-group that has 3+ members, describing the
|
|
82
|
+
* observed pattern and suggesting a concrete rule.
|
|
83
|
+
*/
|
|
84
|
+
export function detectProceduralPattern(memories, project) {
|
|
85
|
+
const groups = findRepeatedPatterns(memories);
|
|
86
|
+
const results = [];
|
|
87
|
+
for (const [tag, groupMemories] of groups) {
|
|
88
|
+
const keywords = extractGroupKeywords(groupMemories);
|
|
89
|
+
const type = dominantType(groupMemories);
|
|
90
|
+
const frequency = groupMemories.length;
|
|
91
|
+
let pattern;
|
|
92
|
+
let suggestedRule;
|
|
93
|
+
if (type === 'error') {
|
|
94
|
+
pattern = `Repeated error pattern tagged "${tag}" (${frequency} occurrences)`;
|
|
95
|
+
suggestedRule = keywords.length > 0
|
|
96
|
+
? `When encountering ${tag}-related issues: check ${keywords.slice(0, 3).join(', ')}`
|
|
97
|
+
: `When encountering ${tag}-related issues: review established fix pattern`;
|
|
98
|
+
}
|
|
99
|
+
else if (type === 'decision') {
|
|
100
|
+
pattern = `Recurring decision pattern tagged "${tag}" (${frequency} occurrences)`;
|
|
101
|
+
suggestedRule = keywords.length > 0
|
|
102
|
+
? `For ${tag} decisions: consistently apply ${keywords.slice(0, 3).join(', ')}`
|
|
103
|
+
: `For ${tag} decisions: follow established decision criteria`;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
pattern = `Repeated context tagged "${tag}" across ${frequency} memories`;
|
|
107
|
+
suggestedRule = keywords.length > 0
|
|
108
|
+
? `In ${project}: ${tag} work consistently involves ${keywords.slice(0, 3).join(', ')}`
|
|
109
|
+
: `In ${project}: ${tag} is a key recurring theme`;
|
|
110
|
+
}
|
|
111
|
+
results.push({ pattern, frequency, suggestedRule });
|
|
112
|
+
}
|
|
113
|
+
// Sort by frequency descending — strongest patterns first
|
|
114
|
+
return results.sort((a, b) => b.frequency - a.frequency);
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Procedural memory creation
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
/**
|
|
120
|
+
* Create a procedural memory from a learned rule and its supporting evidence.
|
|
121
|
+
*
|
|
122
|
+
* Procedural memories:
|
|
123
|
+
* - Use type 'procedural'
|
|
124
|
+
* - Start with high impact (0.9) so they orbit close to the sun
|
|
125
|
+
* - Include both the rule and the evidence that generated it
|
|
126
|
+
* - Are tagged with 'procedural' plus terms extracted from the rule
|
|
127
|
+
*/
|
|
128
|
+
export function createProceduralMemory(rule, evidence, project) {
|
|
129
|
+
const config = getConfig();
|
|
130
|
+
const impact = 0.9;
|
|
131
|
+
const content = `Rule: ${rule}\nEvidence: ${evidence.join(', ')}`;
|
|
132
|
+
const summary = rule.length > 80 ? rule.slice(0, 80).trimEnd() + '…' : rule;
|
|
133
|
+
// Extract meaningful terms from the rule text for tags
|
|
134
|
+
const ruleWords = rule
|
|
135
|
+
.toLowerCase()
|
|
136
|
+
.split(/[\s,.:;()\-]+/)
|
|
137
|
+
.filter(w => w.length > 3);
|
|
138
|
+
const tags = ['procedural', ...new Set(ruleWords.slice(0, 4))];
|
|
139
|
+
// Compute initial importance (new procedural starts strong)
|
|
140
|
+
const now = new Date().toISOString();
|
|
141
|
+
const rec = recencyScore(null, now, config.decayHalfLifeHours);
|
|
142
|
+
const freq = frequencyScore(0, config.frequencySaturationPoint);
|
|
143
|
+
const rel = 0.5; // reasonable default for new procedural memory
|
|
144
|
+
const total = Math.min(1.0, config.weights.recency * rec +
|
|
145
|
+
config.weights.frequency * freq +
|
|
146
|
+
config.weights.impact * impact +
|
|
147
|
+
config.weights.relevance * rel);
|
|
148
|
+
const distance = importanceToDistance(total);
|
|
149
|
+
const contentHash = createHash('sha256').update(content).digest('hex');
|
|
150
|
+
return insertMemory({
|
|
151
|
+
id: randomUUID(),
|
|
152
|
+
project,
|
|
153
|
+
content,
|
|
154
|
+
summary,
|
|
155
|
+
type: 'procedural',
|
|
156
|
+
tags,
|
|
157
|
+
distance,
|
|
158
|
+
importance: total,
|
|
159
|
+
velocity: 0,
|
|
160
|
+
impact,
|
|
161
|
+
access_count: 0,
|
|
162
|
+
last_accessed_at: null,
|
|
163
|
+
metadata: { evidence_count: evidence.length },
|
|
164
|
+
content_hash: contentHash,
|
|
165
|
+
created_at: now,
|
|
166
|
+
updated_at: now,
|
|
167
|
+
deleted_at: null,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Retrieval
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* Get all procedural memories for a project, sorted by importance descending.
|
|
175
|
+
* These feed into the sun content formatter and the suggest logic.
|
|
176
|
+
*/
|
|
177
|
+
export function getProceduralMemories(project) {
|
|
178
|
+
const all = getMemoriesByProject(project);
|
|
179
|
+
return all
|
|
180
|
+
.filter(m => m.type === 'procedural')
|
|
181
|
+
.sort((a, b) => b.importance - a.importance);
|
|
182
|
+
}
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Sun content formatting
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
/**
|
|
187
|
+
* Format procedural memories as a concise "Navigation Rules" section
|
|
188
|
+
* suitable for inclusion in the sun resource content.
|
|
189
|
+
*
|
|
190
|
+
* At most 5 rules are shown (most important first). Each rule is rendered
|
|
191
|
+
* as a single numbered line extracted from the content (the "Rule: ..." part).
|
|
192
|
+
*/
|
|
193
|
+
export function formatProceduralSection(memories) {
|
|
194
|
+
if (memories.length === 0)
|
|
195
|
+
return '';
|
|
196
|
+
const top = memories.slice(0, 5);
|
|
197
|
+
const lines = top.map((m, i) => {
|
|
198
|
+
// Extract just the rule line from "Rule: ...\nEvidence: ..."
|
|
199
|
+
const match = m.content.match(/^Rule:\s*(.+?)(\n|$)/);
|
|
200
|
+
const rule = match ? match[1].trim() : m.summary;
|
|
201
|
+
return ` ${i + 1}. ${rule}`;
|
|
202
|
+
});
|
|
203
|
+
return `Navigation Rules:\n${lines.join('\n')}`;
|
|
204
|
+
}
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Decay multiplier
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
/**
|
|
209
|
+
* Procedural memories are hard-won knowledge and should be highly durable.
|
|
210
|
+
* Return 0.3 so that the effective half-life is ~3.3x longer than normal.
|
|
211
|
+
*
|
|
212
|
+
* Used by orbit.ts when calculating recency score for procedural memories:
|
|
213
|
+
* effectiveHalfLife = baseHalfLife / getProceduralDecayMultiplier()
|
|
214
|
+
*/
|
|
215
|
+
export function getProceduralDecayMultiplier() {
|
|
216
|
+
return 0.3;
|
|
217
|
+
}
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Rule suggestion
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
/**
|
|
222
|
+
* Analyse the last 50 memories and suggest concrete procedural rules.
|
|
223
|
+
*
|
|
224
|
+
* Returns candidates sorted by confidence (highest first). Confidence is
|
|
225
|
+
* the proportion of the tag-group that supports a consistent pattern.
|
|
226
|
+
*
|
|
227
|
+
* Skips rules that already exist as procedural memories (by rule text match).
|
|
228
|
+
*/
|
|
229
|
+
export function suggestRules(recentMemories) {
|
|
230
|
+
const sample = recentMemories.slice(0, 50);
|
|
231
|
+
const groups = findRepeatedPatterns(sample);
|
|
232
|
+
const results = [];
|
|
233
|
+
for (const [tag, groupMemories] of groups) {
|
|
234
|
+
const total = groupMemories.length;
|
|
235
|
+
const type = dominantType(groupMemories);
|
|
236
|
+
const keywords = extractGroupKeywords(groupMemories);
|
|
237
|
+
// Confidence: how consistently the group points in one direction
|
|
238
|
+
const confidence = Math.min(1.0, (total - 2) / 10 + (type ? 0.2 : 0));
|
|
239
|
+
let rule;
|
|
240
|
+
if (type === 'error') {
|
|
241
|
+
rule = keywords.length > 0
|
|
242
|
+
? `When facing ${tag} errors: address ${keywords.slice(0, 2).join(' and ')}`
|
|
243
|
+
: `Document and track ${tag} error resolutions systematically`;
|
|
244
|
+
}
|
|
245
|
+
else if (type === 'decision') {
|
|
246
|
+
rule = keywords.length > 0
|
|
247
|
+
? `For ${tag} decisions: apply ${keywords.slice(0, 2).join(' and ')} criteria`
|
|
248
|
+
: `Maintain consistent decision criteria for ${tag}`;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
rule = keywords.length > 0
|
|
252
|
+
? `${tag} work follows pattern: ${keywords.slice(0, 3).join(', ')}`
|
|
253
|
+
: `Track ${tag} as a key recurring theme`;
|
|
254
|
+
}
|
|
255
|
+
results.push({ rule, confidence, evidence: groupMemories });
|
|
256
|
+
}
|
|
257
|
+
return results.sort((a, b) => b.confidence - a.confidence);
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=procedural.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedural.js","sourceRoot":"","sources":["../../src/engine/procedural.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quality.ts — Memory Quality Scoring ("Planet Grading")
|
|
3
|
+
*
|
|
4
|
+
* Grades each memory on four dimensions:
|
|
5
|
+
*
|
|
6
|
+
* - SPECIFICITY : How much concrete detail does the content contain?
|
|
7
|
+
* - ACTIONABILITY : Can someone act on this? Does it say *how*?
|
|
8
|
+
* - UNIQUENESS : Is this information available in other memories?
|
|
9
|
+
* - FRESHNESS : How recently was this memory verified / accessed?
|
|
10
|
+
*
|
|
11
|
+
* The overall score is a weighted average. Low-quality memories are pushed
|
|
12
|
+
* farther from the sun via qualityOrbitAdjustment().
|
|
13
|
+
*
|
|
14
|
+
* All scoring is purely algorithmic — no LLM calls.
|
|
15
|
+
*/
|
|
16
|
+
import type { Memory, QualityScore } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Calculate the quality score for a single memory.
|
|
19
|
+
*
|
|
20
|
+
* If allMemories is provided, uniqueness is computed against those peers.
|
|
21
|
+
* Without it, uniqueness defaults to 0.5 (neutral).
|
|
22
|
+
*/
|
|
23
|
+
export declare function calculateQuality(memory: Memory, allMemories?: Memory[]): QualityScore;
|
|
24
|
+
/**
|
|
25
|
+
* Score every non-deleted memory in a project and persist the results.
|
|
26
|
+
* Returns aggregate stats.
|
|
27
|
+
*/
|
|
28
|
+
export declare function scoreAllMemories(project: string): {
|
|
29
|
+
scored: number;
|
|
30
|
+
avgQuality: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Generate feedback tips for a newly stored memory.
|
|
34
|
+
*
|
|
35
|
+
* Returns null when the memory is already high quality (overall >= 0.7).
|
|
36
|
+
* Otherwise returns up to 2 actionable tips (in Korean, matching project locale).
|
|
37
|
+
*/
|
|
38
|
+
export declare function getQualityFeedback(quality: QualityScore): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Return a distance multiplier based on quality.
|
|
41
|
+
*
|
|
42
|
+
* Low-quality memories drift outward to make room for high-quality ones.
|
|
43
|
+
* - quality >= 0.7 : 1.0 (no adjustment)
|
|
44
|
+
* - quality 0.4–0.7: 1.2 (slightly further)
|
|
45
|
+
* - quality < 0.4 : 1.5 (noticeably further)
|
|
46
|
+
*/
|
|
47
|
+
export declare function qualityOrbitAdjustment(quality: number): number;
|
|
48
|
+
//# sourceMappingURL=quality.d.ts.map
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quality.ts — Memory Quality Scoring ("Planet Grading")
|
|
3
|
+
*
|
|
4
|
+
* Grades each memory on four dimensions:
|
|
5
|
+
*
|
|
6
|
+
* - SPECIFICITY : How much concrete detail does the content contain?
|
|
7
|
+
* - ACTIONABILITY : Can someone act on this? Does it say *how*?
|
|
8
|
+
* - UNIQUENESS : Is this information available in other memories?
|
|
9
|
+
* - FRESHNESS : How recently was this memory verified / accessed?
|
|
10
|
+
*
|
|
11
|
+
* The overall score is a weighted average. Low-quality memories are pushed
|
|
12
|
+
* farther from the sun via qualityOrbitAdjustment().
|
|
13
|
+
*
|
|
14
|
+
* All scoring is purely algorithmic — no LLM calls.
|
|
15
|
+
*/
|
|
16
|
+
import { getMemoriesByProject, updateQualityScore } from '../storage/queries.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Named-entity extraction
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Extract named entities and technical identifiers from a block of text.
|
|
22
|
+
*
|
|
23
|
+
* Looks for:
|
|
24
|
+
* - PascalCase / camelCase identifiers
|
|
25
|
+
* - kebab-case and snake_case identifiers (length > 4)
|
|
26
|
+
* - Semver / version numbers (v1.2.3, ^2.0, ~3)
|
|
27
|
+
* - File paths (/foo/bar, ./src/index.ts, C:\...)
|
|
28
|
+
* - URLs (http://, https://)
|
|
29
|
+
*/
|
|
30
|
+
function extractNamedEntities(text) {
|
|
31
|
+
const found = [];
|
|
32
|
+
// PascalCase / camelCase
|
|
33
|
+
const camelPascal = text.match(/\b[A-Z][a-zA-Z0-9]{2,}\b|\b[a-z][a-z0-9]*[A-Z][a-zA-Z0-9]*\b/g);
|
|
34
|
+
if (camelPascal)
|
|
35
|
+
found.push(...camelPascal);
|
|
36
|
+
// kebab-case and snake_case (require at least one separator and 2+ segments)
|
|
37
|
+
const kebabSnake = text.match(/\b[a-z][a-z0-9]*(?:[-_][a-z0-9]+){1,}\b/g);
|
|
38
|
+
if (kebabSnake)
|
|
39
|
+
found.push(...kebabSnake.filter(t => t.length > 4));
|
|
40
|
+
// Version numbers
|
|
41
|
+
const versions = text.match(/\bv?\d+\.\d+(?:\.\d+)?(?:-[a-z0-9.]+)?\b|\^?\d+\.\d+|\~\d+\.\d+/gi);
|
|
42
|
+
if (versions)
|
|
43
|
+
found.push(...versions);
|
|
44
|
+
// File paths (Unix, Windows, relative)
|
|
45
|
+
const paths = text.match(/(?:\/[\w.\-]+){2,}|(?:\.\/)[\w.\-/]+|[A-Za-z]:\\[\w.\\\-]+/g);
|
|
46
|
+
if (paths)
|
|
47
|
+
found.push(...paths);
|
|
48
|
+
// URLs
|
|
49
|
+
const urls = text.match(/https?:\/\/[^\s)>"']+/g);
|
|
50
|
+
if (urls)
|
|
51
|
+
found.push(...urls);
|
|
52
|
+
return [...new Set(found)];
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Specificity
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
function scoreSpecificity(memory) {
|
|
58
|
+
const text = memory.content + ' ' + memory.summary;
|
|
59
|
+
const namedEntities = extractNamedEntities(text);
|
|
60
|
+
const hasNumbers = /\b\d+(?:\.\d+)?\b/.test(text);
|
|
61
|
+
const hasPaths = /(?:\/[\w.\-]+){2,}|(?:\.\/)[\w.\-/]+|[A-Za-z]:\\[\w.\\\-]+/.test(text);
|
|
62
|
+
const contentLength = memory.content.length;
|
|
63
|
+
return Math.min(1.0, namedEntities.length * 0.1 +
|
|
64
|
+
(hasNumbers ? 0.2 : 0) +
|
|
65
|
+
(hasPaths ? 0.2 : 0) +
|
|
66
|
+
Math.min(0.3, contentLength / 500));
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Actionability
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
const ACTION_VERBS_EN = [
|
|
72
|
+
'use', 'run', 'install', 'configure', 'fix', 'add', 'remove', 'update',
|
|
73
|
+
'set', 'enable', 'disable', 'check', 'ensure', 'avoid', 'always', 'never',
|
|
74
|
+
'prefer', 'call', 'import', 'export', 'create', 'delete', 'replace',
|
|
75
|
+
];
|
|
76
|
+
const ACTION_VERBS_KO = [
|
|
77
|
+
'사용', '실행', '설치', '수정', '추가', '삭제', '확인', '설정', '적용',
|
|
78
|
+
'피해야', '항상', '절대', '선호', '호출', '가져오기', '내보내기',
|
|
79
|
+
];
|
|
80
|
+
const INSTRUCTION_PATTERNS = [
|
|
81
|
+
/\bstep \d+\b/i,
|
|
82
|
+
/\b\d+\.\s+[A-Z]/, // numbered list "1. Do something"
|
|
83
|
+
/\bfirst\b.{0,30}\bthen\b/i,
|
|
84
|
+
/\bmake sure\b/i,
|
|
85
|
+
/\bdon['']t\b/i,
|
|
86
|
+
/\b하면\s/, // Korean conditional
|
|
87
|
+
/\b다음\s+단계\b/, // Korean "next step"
|
|
88
|
+
];
|
|
89
|
+
const CONDITIONAL_PATTERNS = [
|
|
90
|
+
/\bif\b.{1,60}\bthen\b/i,
|
|
91
|
+
/\bwhen\b.{1,60},\s/i,
|
|
92
|
+
/\bunless\b/i,
|
|
93
|
+
/\botherwise\b/i,
|
|
94
|
+
/\b이면\b/, // Korean conditional
|
|
95
|
+
/\b경우에\b/,
|
|
96
|
+
];
|
|
97
|
+
function scoreActionability(memory) {
|
|
98
|
+
const text = memory.content.toLowerCase();
|
|
99
|
+
const hasActionVerbEn = ACTION_VERBS_EN.some(v => text.includes(v));
|
|
100
|
+
const hasActionVerbKo = ACTION_VERBS_KO.some(v => memory.content.includes(v));
|
|
101
|
+
const hasActionVerb = hasActionVerbEn || hasActionVerbKo;
|
|
102
|
+
const hasInstruction = INSTRUCTION_PATTERNS.some(p => p.test(memory.content));
|
|
103
|
+
const hasConditional = CONDITIONAL_PATTERNS.some(p => p.test(memory.content));
|
|
104
|
+
return ((hasActionVerb ? 0.4 : 0) +
|
|
105
|
+
(hasInstruction ? 0.3 : 0) +
|
|
106
|
+
(hasConditional ? 0.3 : 0));
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Uniqueness
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
/**
|
|
112
|
+
* Estimate similarity between two memories using tag overlap and
|
|
113
|
+
* summary keyword overlap as a lightweight proxy.
|
|
114
|
+
*
|
|
115
|
+
* Returns a value in [0, 1] where 1.0 = identical.
|
|
116
|
+
*/
|
|
117
|
+
function estimateSimilarity(a, b) {
|
|
118
|
+
const tagsA = new Set(Array.isArray(a.tags) ? a.tags : []);
|
|
119
|
+
const tagsB = new Set(Array.isArray(b.tags) ? b.tags : []);
|
|
120
|
+
const tagIntersection = [...tagsA].filter(t => tagsB.has(t)).length;
|
|
121
|
+
const tagUnion = new Set([...tagsA, ...tagsB]).size;
|
|
122
|
+
const tagOverlap = tagUnion > 0 ? tagIntersection / tagUnion : 0;
|
|
123
|
+
// Summary keyword overlap
|
|
124
|
+
const wordsA = new Set(a.summary.toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
125
|
+
const wordsB = new Set(b.summary.toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
126
|
+
const wordIntersection = [...wordsA].filter(w => wordsB.has(w)).length;
|
|
127
|
+
const wordUnion = new Set([...wordsA, ...wordsB]).size;
|
|
128
|
+
const wordOverlap = wordUnion > 0 ? wordIntersection / wordUnion : 0;
|
|
129
|
+
// Weighted combination: tags carry more signal than summary keywords
|
|
130
|
+
return tagOverlap * 0.6 + wordOverlap * 0.4;
|
|
131
|
+
}
|
|
132
|
+
function scoreUniqueness(memory, allMemories) {
|
|
133
|
+
if (!allMemories || allMemories.length === 0)
|
|
134
|
+
return 0.5;
|
|
135
|
+
const others = allMemories.filter(m => m.id !== memory.id);
|
|
136
|
+
if (others.length === 0)
|
|
137
|
+
return 1.0;
|
|
138
|
+
const maxSimilarity = Math.max(...others.map(o => estimateSimilarity(memory, o)));
|
|
139
|
+
return Math.max(0.0, 1.0 - maxSimilarity);
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Freshness
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
function scoreFreshness(memory) {
|
|
145
|
+
const ref = memory.last_accessed_at ?? memory.created_at;
|
|
146
|
+
if (!ref)
|
|
147
|
+
return 0.1;
|
|
148
|
+
const normalized = /[Zz]$|[+-]\d{2}:\d{2}$/.test(ref) ? ref : ref + 'Z';
|
|
149
|
+
const refMs = new Date(normalized).getTime();
|
|
150
|
+
if (isNaN(refMs))
|
|
151
|
+
return 0.1;
|
|
152
|
+
const hoursSince = (Date.now() - refMs) / (1000 * 60 * 60);
|
|
153
|
+
if (hoursSince <= 24)
|
|
154
|
+
return 1.0;
|
|
155
|
+
if (hoursSince <= 168)
|
|
156
|
+
return 0.7; // 1 week
|
|
157
|
+
if (hoursSince <= 720)
|
|
158
|
+
return 0.4; // 1 month
|
|
159
|
+
return 0.2;
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Public API
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
/**
|
|
165
|
+
* Calculate the quality score for a single memory.
|
|
166
|
+
*
|
|
167
|
+
* If allMemories is provided, uniqueness is computed against those peers.
|
|
168
|
+
* Without it, uniqueness defaults to 0.5 (neutral).
|
|
169
|
+
*/
|
|
170
|
+
export function calculateQuality(memory, allMemories) {
|
|
171
|
+
const specificity = scoreSpecificity(memory);
|
|
172
|
+
const actionability = scoreActionability(memory);
|
|
173
|
+
const uniqueness = scoreUniqueness(memory, allMemories);
|
|
174
|
+
const freshness = scoreFreshness(memory);
|
|
175
|
+
const overall = 0.30 * specificity +
|
|
176
|
+
0.25 * actionability +
|
|
177
|
+
0.25 * uniqueness +
|
|
178
|
+
0.20 * freshness;
|
|
179
|
+
return {
|
|
180
|
+
overall: Math.min(1.0, Math.max(0.0, overall)),
|
|
181
|
+
specificity: Math.min(1.0, Math.max(0.0, specificity)),
|
|
182
|
+
actionability: Math.min(1.0, Math.max(0.0, actionability)),
|
|
183
|
+
uniqueness: Math.min(1.0, Math.max(0.0, uniqueness)),
|
|
184
|
+
freshness: Math.min(1.0, Math.max(0.0, freshness)),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Score every non-deleted memory in a project and persist the results.
|
|
189
|
+
* Returns aggregate stats.
|
|
190
|
+
*/
|
|
191
|
+
export function scoreAllMemories(project) {
|
|
192
|
+
const memories = getMemoriesByProject(project);
|
|
193
|
+
if (memories.length === 0)
|
|
194
|
+
return { scored: 0, avgQuality: 0 };
|
|
195
|
+
let totalQuality = 0;
|
|
196
|
+
for (const memory of memories) {
|
|
197
|
+
const score = calculateQuality(memory, memories);
|
|
198
|
+
updateQualityScore(memory.id, score.overall);
|
|
199
|
+
totalQuality += score.overall;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
scored: memories.length,
|
|
203
|
+
avgQuality: totalQuality / memories.length,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Generate feedback tips for a newly stored memory.
|
|
208
|
+
*
|
|
209
|
+
* Returns null when the memory is already high quality (overall >= 0.7).
|
|
210
|
+
* Otherwise returns up to 2 actionable tips (in Korean, matching project locale).
|
|
211
|
+
*/
|
|
212
|
+
export function getQualityFeedback(quality) {
|
|
213
|
+
if (quality.overall >= 0.7)
|
|
214
|
+
return null;
|
|
215
|
+
const tips = [];
|
|
216
|
+
if (quality.specificity < 0.3) {
|
|
217
|
+
tips.push('이 기억을 더 구체적으로 작성하면 좋겠습니다. 구체적인 이름, 버전, 경로 등을 포함해주세요.');
|
|
218
|
+
}
|
|
219
|
+
if (quality.actionability < 0.3) {
|
|
220
|
+
tips.push('어떻게 행동해야 하는지 구체적인 단계를 포함하면 더 유용합니다.');
|
|
221
|
+
}
|
|
222
|
+
if (quality.uniqueness < 0.3) {
|
|
223
|
+
tips.push('유사한 기억이 이미 존재합니다. 기존 기억을 업데이트하는 것을 고려해주세요.');
|
|
224
|
+
}
|
|
225
|
+
if (tips.length === 0)
|
|
226
|
+
return null;
|
|
227
|
+
// Return at most 2 tips
|
|
228
|
+
return tips.slice(0, 2).join(' ');
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Return a distance multiplier based on quality.
|
|
232
|
+
*
|
|
233
|
+
* Low-quality memories drift outward to make room for high-quality ones.
|
|
234
|
+
* - quality >= 0.7 : 1.0 (no adjustment)
|
|
235
|
+
* - quality 0.4–0.7: 1.2 (slightly further)
|
|
236
|
+
* - quality < 0.4 : 1.5 (noticeably further)
|
|
237
|
+
*/
|
|
238
|
+
export function qualityOrbitAdjustment(quality) {
|
|
239
|
+
if (quality >= 0.7)
|
|
240
|
+
return 1.0;
|
|
241
|
+
if (quality >= 0.4)
|
|
242
|
+
return 1.2;
|
|
243
|
+
return 1.5;
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/engine/quality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEjF,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,yBAAyB;IACzB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;IAChG,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;IAE5C,6EAA6E;IAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC1E,IAAI,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAEpE,kBAAkB;IAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACjG,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAEtC,uCAAuC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACxF,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAEhC,OAAO;IACP,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAClD,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IAE9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,SAAS,gBAAgB,CAAC,MAAc;IACtC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC;IAEnD,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,4DAA4D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAE5C,OAAO,IAAI,CAAC,GAAG,CACb,GAAG,EACH,aAAa,CAAC,MAAM,GAAG,GAAG;QACxB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,GAAG,GAAG,CAAC,CACrC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,eAAe,GAAG;IACtB,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IACtE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO;IACzE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS;CACpE,CAAC;AAEF,MAAM,eAAe,GAAG;IACtB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IACpD,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;CAC9C,CAAC;AAEF,MAAM,oBAAoB,GAAG;IAC3B,eAAe;IACf,iBAAiB,EAAe,kCAAkC;IAClE,2BAA2B;IAC3B,gBAAgB;IAChB,eAAe;IACf,QAAQ,EAAuB,qBAAqB;IACpD,aAAa,EAAgB,qBAAqB;CACnD,CAAC;AAEF,MAAM,oBAAoB,GAAG;IAC3B,wBAAwB;IACxB,qBAAqB;IACrB,aAAa;IACb,gBAAgB;IAChB,QAAQ,EAAuB,qBAAqB;IACpD,SAAS;CACV,CAAC;AAEF,SAAS,kBAAkB,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAE1C,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,MAAM,aAAa,GAAG,eAAe,IAAI,eAAe,CAAC;IAEzD,MAAM,cAAc,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9E,MAAM,cAAc,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAE9E,OAAO,CACL,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAC3B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE3D,MAAM,eAAe,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjE,0BAA0B;IAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAC/D,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAC/D,CAAC;IACF,MAAM,gBAAgB,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACvE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,MAAM,WAAW,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAErE,qEAAqE;IACrE,OAAO,UAAU,GAAG,GAAG,GAAG,WAAW,GAAG,GAAG,CAAC;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,WAAsB;IAC7D,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAEzD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAEpC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,CAAC,CAAC;AAC5C,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,IAAI,MAAM,CAAC,UAAU,CAAC;IACzD,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IAErB,MAAM,UAAU,GAAG,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;IACxE,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7C,IAAI,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAE7B,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAE3D,IAAI,UAAU,IAAI,EAAE;QAAI,OAAO,GAAG,CAAC;IACnC,IAAI,UAAU,IAAI,GAAG;QAAG,OAAO,GAAG,CAAC,CAAE,SAAS;IAC9C,IAAI,UAAU,IAAI,GAAG;QAAG,OAAO,GAAG,CAAC,CAAE,UAAU;IAC/C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,WAAsB;IAEtB,MAAM,WAAW,GAAK,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,UAAU,GAAM,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAO,cAAc,CAAC,MAAM,CAAC,CAAC;IAE7C,MAAM,OAAO,GACX,IAAI,GAAG,WAAW;QAClB,IAAI,GAAG,aAAa;QACpB,IAAI,GAAG,UAAU;QACjB,IAAI,GAAG,SAAS,CAAC;IAEnB,OAAO;QACL,OAAO,EAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAClD,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACtD,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC1D,UAAU,EAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACrD,SAAS,EAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;KACrD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe;IAEf,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAE/D,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjD,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC;IAChC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,YAAY,GAAG,QAAQ,CAAC,MAAM;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,IAAI,OAAO,CAAC,OAAO,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,IAAI,OAAO,CAAC,WAAW,GAAG,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CACP,sDAAsD,CACvD,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CACP,qCAAqC,CACtC,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CACP,4CAA4C,CAC7C,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,wBAAwB;IACxB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,IAAI,OAAO,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,OAAO,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAC/B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/repository.ts — MemoryRepository abstraction
|
|
3
|
+
*
|
|
4
|
+
* Defines the storage contract for memories and sun state.
|
|
5
|
+
* The engine and use-case layers depend only on this interface,
|
|
6
|
+
* making the concrete storage backend swappable (SQLite today,
|
|
7
|
+
* Python v1 or a remote API tomorrow).
|
|
8
|
+
*
|
|
9
|
+
* Dependency rule: this file lives in the engine layer and MUST NOT
|
|
10
|
+
* import from storage/, infrastructure, or framework packages.
|
|
11
|
+
*/
|
|
12
|
+
import type { Memory, MemoryType, SunState, OrbitChange } from './types.js';
|
|
13
|
+
export interface InsertMemoryData {
|
|
14
|
+
id?: string;
|
|
15
|
+
project: string;
|
|
16
|
+
content: string;
|
|
17
|
+
summary?: string;
|
|
18
|
+
type?: MemoryType;
|
|
19
|
+
tags?: string[];
|
|
20
|
+
distance?: number;
|
|
21
|
+
importance?: number;
|
|
22
|
+
velocity?: number;
|
|
23
|
+
impact?: number;
|
|
24
|
+
access_count?: number;
|
|
25
|
+
last_accessed_at?: string | null;
|
|
26
|
+
metadata?: Record<string, unknown>;
|
|
27
|
+
source?: string | null;
|
|
28
|
+
source_path?: string | null;
|
|
29
|
+
source_hash?: string | null;
|
|
30
|
+
content_hash?: string | null;
|
|
31
|
+
created_at?: string;
|
|
32
|
+
updated_at?: string;
|
|
33
|
+
deleted_at?: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface MemoryRepository {
|
|
36
|
+
/** Persist a new memory and return the fully-resolved record. */
|
|
37
|
+
insert(data: InsertMemoryData): Memory;
|
|
38
|
+
/** Fetch a single memory by its UUID. Returns undefined if not found. */
|
|
39
|
+
getById(id: string): Memory | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Fetch multiple memories by a list of IDs in a single round-trip.
|
|
42
|
+
* Soft-deleted memories are excluded.
|
|
43
|
+
*/
|
|
44
|
+
getByIds(ids: string[]): Memory[];
|
|
45
|
+
/**
|
|
46
|
+
* Fetch all non-deleted memories for a project, ordered by distance ASC.
|
|
47
|
+
*/
|
|
48
|
+
getByProject(project: string): Memory[];
|
|
49
|
+
/**
|
|
50
|
+
* Find a non-deleted memory with the given content hash (SHA-256).
|
|
51
|
+
* Used for content-level deduplication in createMemory().
|
|
52
|
+
*/
|
|
53
|
+
getByContentHash(project: string, hash: string): Memory | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Full-text search over memories in a project.
|
|
56
|
+
* Implementations may use FTS5, Postgres full-text, or any other backend.
|
|
57
|
+
*/
|
|
58
|
+
search(project: string, query: string, limit: number): Memory[];
|
|
59
|
+
/** Increment access_count and refresh last_accessed_at. */
|
|
60
|
+
updateAccess(id: string): void;
|
|
61
|
+
/** Persist new orbital position after a physics recalculation. */
|
|
62
|
+
updateOrbit(id: string, distance: number, importance: number, velocity: number): void;
|
|
63
|
+
/** Soft-delete a memory (sets deleted_at; keeps the row for audit trails). */
|
|
64
|
+
softDelete(id: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Return up to `limit` non-deleted memories for the project ordered by
|
|
67
|
+
* distance ASC (closest to the sun first).
|
|
68
|
+
*/
|
|
69
|
+
getNearestMemories(project: string, limit: number): Memory[];
|
|
70
|
+
/** Retrieve the sun (working context) for a project. */
|
|
71
|
+
getSunState(project: string): SunState | undefined;
|
|
72
|
+
/** Insert or update the sun state for a project. */
|
|
73
|
+
upsertSunState(state: SunState): void;
|
|
74
|
+
/** Append an orbit-change event for audit / analytics. */
|
|
75
|
+
insertOrbitLog(change: OrbitChange): void;
|
|
76
|
+
/** Remove orbit log entries older than `daysOld` days. */
|
|
77
|
+
cleanupOrbitLog(daysOld: number): void;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=repository.d.ts.map
|