memory-lucia 2.0.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/NPM-PUBLISH-GUIDE.md +169 -0
- package/PUBLISH-STEPS.md +102 -0
- package/README.md +64 -0
- package/SKILL.md +120 -0
- package/api/index.js +255 -0
- package/database/init.js +137 -0
- package/modules/decision.js +267 -0
- package/modules/evolution.js +286 -0
- package/modules/learning.js +267 -0
- package/modules/priority.js +174 -0
- package/modules/version.js +286 -0
- package/package.json +35 -0
- package/publish-with-otp.bat +13 -0
- package/publish.bat +17 -0
- package/publish.sh +20 -0
- package/scripts/init-memory.js +30 -0
package/api/index.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory V2.0 Unified API
|
|
3
|
+
* Single entry point for all memory operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const PriorityModule = require('../modules/priority');
|
|
7
|
+
const LearningModule = require('../modules/learning');
|
|
8
|
+
const DecisionModule = require('../modules/decision');
|
|
9
|
+
const EvolutionModule = require('../modules/evolution');
|
|
10
|
+
const VersionManager = require('../modules/version');
|
|
11
|
+
const MemoryDatabase = require('../database/init');
|
|
12
|
+
|
|
13
|
+
class MemoryAPI {
|
|
14
|
+
constructor(dbPath = './memory-v2.db') {
|
|
15
|
+
this.dbPath = dbPath;
|
|
16
|
+
this.db = new MemoryDatabase(dbPath);
|
|
17
|
+
this.modules = {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize API and all modules
|
|
22
|
+
*/
|
|
23
|
+
async init() {
|
|
24
|
+
await this.db.init();
|
|
25
|
+
await this.db.runSchema();
|
|
26
|
+
|
|
27
|
+
// Initialize modules
|
|
28
|
+
this.modules = {
|
|
29
|
+
priority: new PriorityModule(this.db),
|
|
30
|
+
learning: new LearningModule(this.db),
|
|
31
|
+
decision: new DecisionModule(this.db),
|
|
32
|
+
evolution: new EvolutionModule(this.db),
|
|
33
|
+
version: new VersionManager(this.db)
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
console.log('✅ Memory V2.0 API initialized');
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// =========================================
|
|
41
|
+
// PRIORITY API
|
|
42
|
+
// =========================================
|
|
43
|
+
|
|
44
|
+
async analyzePriority(message, context = {}) {
|
|
45
|
+
return await this.modules.priority.analyze(message, context);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async storePriority(messageId, conversationId, analysis) {
|
|
49
|
+
return await this.modules.priority.store(messageId, conversationId, analysis);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getHighPriority(limit = 20) {
|
|
53
|
+
return await this.modules.priority.getHighPriority(limit);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// =========================================
|
|
57
|
+
// LEARNING API
|
|
58
|
+
// =========================================
|
|
59
|
+
|
|
60
|
+
async detectLearningIntent(message) {
|
|
61
|
+
return await this.modules.learning.detectIntent(message);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async startLearning(messageId, conversationId, message) {
|
|
65
|
+
const intent = await this.detectLearningIntent(message);
|
|
66
|
+
if (!intent.isLearning) {
|
|
67
|
+
return { success: false, error: 'No learning intent detected' };
|
|
68
|
+
}
|
|
69
|
+
return await this.modules.learning.createLearning(messageId, conversationId, intent, message);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async updateLearningProgress(learningId, progressData) {
|
|
73
|
+
return await this.modules.learning.updateProgress(learningId, progressData);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async addMilestone(learningId, milestone) {
|
|
77
|
+
return await this.modules.learning.addMilestone(learningId, milestone);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getActiveLearning(limit = 20) {
|
|
81
|
+
return await this.modules.learning.getActiveLearning(limit);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async getWeeklyLearningReport() {
|
|
85
|
+
return await this.modules.learning.generateWeeklyReport();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// =========================================
|
|
89
|
+
// DECISION API
|
|
90
|
+
// =========================================
|
|
91
|
+
|
|
92
|
+
async recordDecision(messageId, conversationId, decisionData) {
|
|
93
|
+
return await this.modules.decision.createDecision(messageId, conversationId, decisionData);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async updateDecisionOutcome(decisionId, outcomeData) {
|
|
97
|
+
return await this.modules.decision.updateOutcome(decisionId, outcomeData);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async getPendingDecisions(limit = 20) {
|
|
101
|
+
return await this.modules.decision.getPendingDecisions(limit);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getDueReviews(days = 7) {
|
|
105
|
+
return await this.modules.decision.getDueReviews(days);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getDecisionStats() {
|
|
109
|
+
return await this.modules.decision.getStats();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async analyzeDecisionPatterns() {
|
|
113
|
+
return await this.modules.decision.analyzePatterns();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =========================================
|
|
117
|
+
// EVOLUTION API
|
|
118
|
+
// =========================================
|
|
119
|
+
|
|
120
|
+
async recordSkillUsage(skillName, category, result = 'success', metrics = {}) {
|
|
121
|
+
return await this.modules.evolution.recordUsage(skillName, category, result, metrics);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async getSkill(skillName) {
|
|
125
|
+
return await this.modules.evolution.getSkill(skillName);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async getTopSkills(limit = 10) {
|
|
129
|
+
return await this.modules.evolution.getTopSkills(limit);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async getSkillGrowthReport(days = 30) {
|
|
133
|
+
return await this.modules.evolution.generateGrowthReport(days);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async suggestSkillsToPractice() {
|
|
137
|
+
return await this.modules.evolution.suggestPractice();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// =========================================
|
|
141
|
+
// VERSION API
|
|
142
|
+
// =========================================
|
|
143
|
+
|
|
144
|
+
async createBackup(description = '') {
|
|
145
|
+
return await this.modules.version.createBackup(description, 'full');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async listVersions() {
|
|
149
|
+
return await this.modules.version.listVersions();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async rollbackToVersion(versionId) {
|
|
153
|
+
return await this.modules.version.rollbackToVersion(versionId);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getBackupStats() {
|
|
157
|
+
return await this.modules.version.getBackupStats();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// =========================================
|
|
161
|
+
// UNIFIED QUERY API
|
|
162
|
+
// =========================================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Search across all memory modules
|
|
166
|
+
*/
|
|
167
|
+
async search(query, options = {}) {
|
|
168
|
+
const results = {
|
|
169
|
+
query,
|
|
170
|
+
timestamp: new Date().toISOString(),
|
|
171
|
+
modules: {}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Search priorities
|
|
175
|
+
if (options.modules?.includes('priority') || !options.modules) {
|
|
176
|
+
results.modules.priority = await this.modules.priority.searchByPriorityRange();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Search learning
|
|
180
|
+
if (options.modules?.includes('learning') || !options.modules) {
|
|
181
|
+
results.modules.learning = await this.modules.learning.getByTopic(query);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Search decisions
|
|
185
|
+
if (options.modules?.includes('decision') || !options.modules) {
|
|
186
|
+
results.modules.decisions = await this.modules.decision.getSimilarDecisions(query);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get comprehensive dashboard data
|
|
194
|
+
*/
|
|
195
|
+
async getDashboard() {
|
|
196
|
+
return {
|
|
197
|
+
priority: {
|
|
198
|
+
high: await this.modules.priority.getHighPriority(5),
|
|
199
|
+
stats: await this.modules.priority.getStats()
|
|
200
|
+
},
|
|
201
|
+
learning: {
|
|
202
|
+
active: await this.modules.learning.getActiveLearning(5),
|
|
203
|
+
weekly: await this.modules.learning.generateWeeklyReport()
|
|
204
|
+
},
|
|
205
|
+
decisions: {
|
|
206
|
+
pending: await this.modules.decision.getPendingDecisions(5),
|
|
207
|
+
dueReviews: await this.modules.decision.getDueReviews(7)
|
|
208
|
+
},
|
|
209
|
+
evolution: {
|
|
210
|
+
top: await this.modules.evolution.getTopSkills(5),
|
|
211
|
+
needsPractice: await this.modules.evolution.suggestPractice()
|
|
212
|
+
},
|
|
213
|
+
version: {
|
|
214
|
+
stats: await this.modules.version.getBackupStats()
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Health check
|
|
221
|
+
*/
|
|
222
|
+
async health() {
|
|
223
|
+
return await this.db.healthCheck();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Close database connection
|
|
228
|
+
*/
|
|
229
|
+
async close() {
|
|
230
|
+
await this.db.close();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// CLI usage
|
|
235
|
+
if (require.main === module) {
|
|
236
|
+
const api = new MemoryAPI();
|
|
237
|
+
|
|
238
|
+
(async () => {
|
|
239
|
+
try {
|
|
240
|
+
await api.init();
|
|
241
|
+
|
|
242
|
+
// Example usage
|
|
243
|
+
console.log('\n📊 Dashboard:');
|
|
244
|
+
const dashboard = await api.getDashboard();
|
|
245
|
+
console.log(JSON.stringify(dashboard, null, 2));
|
|
246
|
+
|
|
247
|
+
await api.close();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
console.error('❌ Error:', err);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
})();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = MemoryAPI;
|
package/database/init.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory V2.0 Database Initialization
|
|
3
|
+
* Extends Lossless Claw with personal modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const sqlite3 = require('sqlite3').verbose();
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class MemoryDatabase {
|
|
11
|
+
constructor(dbPath = './memory-v2.db') {
|
|
12
|
+
this.dbPath = dbPath;
|
|
13
|
+
this.db = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async init() {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
this.db = new sqlite3.Database(this.dbPath, (err) => {
|
|
19
|
+
if (err) {
|
|
20
|
+
console.error('Error opening database:', err);
|
|
21
|
+
reject(err);
|
|
22
|
+
} else {
|
|
23
|
+
console.log('✅ Connected to Memory V2.0 database');
|
|
24
|
+
resolve();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async runSchema() {
|
|
31
|
+
const schemaPath = path.join(__dirname, 'schema.sql');
|
|
32
|
+
const schema = fs.readFileSync(schemaPath, 'utf8');
|
|
33
|
+
|
|
34
|
+
// Split schema into individual statements
|
|
35
|
+
const statements = schema
|
|
36
|
+
.split(';')
|
|
37
|
+
.map(s => s.trim())
|
|
38
|
+
.filter(s => s.length > 0);
|
|
39
|
+
|
|
40
|
+
for (const statement of statements) {
|
|
41
|
+
await this.run(statement);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('✅ Database schema initialized');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async run(sql, params = []) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
this.db.run(sql, params, function(err) {
|
|
50
|
+
if (err) {
|
|
51
|
+
console.error('SQL Error:', err);
|
|
52
|
+
reject(err);
|
|
53
|
+
} else {
|
|
54
|
+
resolve({ id: this.lastID, changes: this.changes });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async get(sql, params = []) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
this.db.get(sql, params, (err, row) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
reject(err);
|
|
65
|
+
} else {
|
|
66
|
+
resolve(row);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async all(sql, params = []) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
this.db.all(sql, params, (err, rows) => {
|
|
75
|
+
if (err) {
|
|
76
|
+
reject(err);
|
|
77
|
+
} else {
|
|
78
|
+
resolve(rows);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async close() {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
this.db.close((err) => {
|
|
87
|
+
if (err) {
|
|
88
|
+
reject(err);
|
|
89
|
+
} else {
|
|
90
|
+
console.log('✅ Database connection closed');
|
|
91
|
+
resolve();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Health check
|
|
98
|
+
async healthCheck() {
|
|
99
|
+
try {
|
|
100
|
+
const tables = await this.all(
|
|
101
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'memory_%'"
|
|
102
|
+
);
|
|
103
|
+
return {
|
|
104
|
+
status: 'healthy',
|
|
105
|
+
tables: tables.map(t => t.name),
|
|
106
|
+
timestamp: new Date().toISOString()
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return {
|
|
110
|
+
status: 'unhealthy',
|
|
111
|
+
error: err.message,
|
|
112
|
+
timestamp: new Date().toISOString()
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// CLI usage
|
|
119
|
+
if (require.main === module) {
|
|
120
|
+
const db = new MemoryDatabase();
|
|
121
|
+
|
|
122
|
+
(async () => {
|
|
123
|
+
try {
|
|
124
|
+
await db.init();
|
|
125
|
+
await db.runSchema();
|
|
126
|
+
const health = await db.healthCheck();
|
|
127
|
+
console.log('\n📊 Database Health:', health);
|
|
128
|
+
await db.close();
|
|
129
|
+
console.log('\n🎉 Memory V2.0 database initialized successfully!');
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error('❌ Initialization failed:', err);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = MemoryDatabase;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision Module
|
|
3
|
+
* Track decisions, outcomes, and reviews
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class DecisionModule {
|
|
7
|
+
constructor(db) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
this.decisionTypes = ['framework', 'vendor', 'migration', 'publish', 'spend', 'other'];
|
|
10
|
+
|
|
11
|
+
// Keywords for automatic decision type detection
|
|
12
|
+
this.typeKeywords = {
|
|
13
|
+
framework: ['framework', 'library', 'architecture', 'stack', 'technology'],
|
|
14
|
+
vendor: ['vendor', 'provider', 'service', 'platform', 'tool'],
|
|
15
|
+
migration: ['migrate', 'upgrade', 'update', 'move', 'transfer', 'switch'],
|
|
16
|
+
publish: ['publish', 'release', 'deploy', 'launch', 'ship'],
|
|
17
|
+
spend: ['budget', 'cost', 'price', 'purchase', 'buy', 'invest', 'spend'],
|
|
18
|
+
other: ['decide', 'choice', 'option', 'select', 'pick']
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Detect decision type from message
|
|
24
|
+
*/
|
|
25
|
+
detectType(message) {
|
|
26
|
+
const content = message.toLowerCase();
|
|
27
|
+
const scores = {};
|
|
28
|
+
|
|
29
|
+
for (const [type, keywords] of Object.entries(this.typeKeywords)) {
|
|
30
|
+
scores[type] = 0;
|
|
31
|
+
for (const keyword of keywords) {
|
|
32
|
+
if (content.includes(keyword)) {
|
|
33
|
+
scores[type] += 1;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Find highest score
|
|
39
|
+
let detectedType = 'other';
|
|
40
|
+
let maxScore = 0;
|
|
41
|
+
|
|
42
|
+
for (const [type, score] of Object.entries(scores)) {
|
|
43
|
+
if (score > maxScore) {
|
|
44
|
+
maxScore = score;
|
|
45
|
+
detectedType = type;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
type: detectedType,
|
|
51
|
+
confidence: maxScore > 0 ? Math.min(maxScore / 3, 1) : 0,
|
|
52
|
+
scores
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extract decision question
|
|
58
|
+
*/
|
|
59
|
+
extractQuestion(message) {
|
|
60
|
+
// Look for question patterns
|
|
61
|
+
const patterns = [
|
|
62
|
+
/(?:should|need to|want to|decide|choose)\s+(.+?)(?:\?|\.|$)/i,
|
|
63
|
+
/which\s+(.+?)(?:\?|\.|$)/i,
|
|
64
|
+
/what\s+(.+?)(?:\?|\.|$)/i,
|
|
65
|
+
/how\s+(.+?)(?:\?|\.|$)/i
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
for (const pattern of patterns) {
|
|
69
|
+
const match = message.match(pattern);
|
|
70
|
+
if (match) {
|
|
71
|
+
return match[1].trim();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallback: first sentence
|
|
76
|
+
const sentences = message.split(/[.!?]+/);
|
|
77
|
+
return sentences[0]?.trim() || message.substring(0, 100);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create new decision record
|
|
82
|
+
*/
|
|
83
|
+
async createDecision(messageId, conversationId, decisionData) {
|
|
84
|
+
const sql = `
|
|
85
|
+
INSERT INTO memory_decisions
|
|
86
|
+
(message_id, conversation_id, decision_type, decision_question,
|
|
87
|
+
decision_context, options_considered, chosen_option, rationale,
|
|
88
|
+
confidence_level, expected_outcome, review_date)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
// Auto-detect type if not provided
|
|
93
|
+
const typeDetection = decisionData.type
|
|
94
|
+
? { type: decisionData.type, confidence: 1 }
|
|
95
|
+
: this.detectType(decisionData.question || decisionData.context);
|
|
96
|
+
|
|
97
|
+
// Calculate review date (default: 30 days)
|
|
98
|
+
const reviewDate = new Date();
|
|
99
|
+
reviewDate.setDate(reviewDate.getDate() + (decisionData.reviewDays || 30));
|
|
100
|
+
|
|
101
|
+
const result = await this.db.run(sql, [
|
|
102
|
+
messageId,
|
|
103
|
+
conversationId,
|
|
104
|
+
typeDetection.type,
|
|
105
|
+
decisionData.question || this.extractQuestion(decisionData.context),
|
|
106
|
+
decisionData.context,
|
|
107
|
+
JSON.stringify(decisionData.options || []),
|
|
108
|
+
decisionData.chosenOption,
|
|
109
|
+
decisionData.rationale,
|
|
110
|
+
decisionData.confidence || 3,
|
|
111
|
+
decisionData.expectedOutcome,
|
|
112
|
+
reviewDate.toISOString()
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
decisionId: result.id,
|
|
118
|
+
type: typeDetection.type,
|
|
119
|
+
reviewDate: reviewDate.toISOString()
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Update decision outcome
|
|
125
|
+
*/
|
|
126
|
+
async updateOutcome(decisionId, outcomeData) {
|
|
127
|
+
const sql = `
|
|
128
|
+
UPDATE memory_decisions
|
|
129
|
+
SET outcome_status = ?, actual_outcome = ?, updated_at = CURRENT_TIMESTAMP
|
|
130
|
+
WHERE id = ?
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
await this.db.run(sql, [
|
|
134
|
+
outcomeData.status,
|
|
135
|
+
outcomeData.description,
|
|
136
|
+
decisionId
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
return { success: true, decisionId };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Schedule review
|
|
144
|
+
*/
|
|
145
|
+
async scheduleReview(decisionId, reviewDate) {
|
|
146
|
+
const sql = `
|
|
147
|
+
UPDATE memory_decisions
|
|
148
|
+
SET review_date = ?, updated_at = CURRENT_TIMESTAMP
|
|
149
|
+
WHERE id = ?
|
|
150
|
+
`;
|
|
151
|
+
|
|
152
|
+
await this.db.run(sql, [reviewDate, decisionId]);
|
|
153
|
+
return { success: true };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Complete review
|
|
158
|
+
*/
|
|
159
|
+
async completeReview(decisionId, reviewData) {
|
|
160
|
+
const sql = `
|
|
161
|
+
UPDATE memory_decisions
|
|
162
|
+
SET reviewed_at = CURRENT_TIMESTAMP, review_notes = ?, updated_at = CURRENT_TIMESTAMP
|
|
163
|
+
WHERE id = ?
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
await this.db.run(sql, [reviewData.notes, decisionId]);
|
|
167
|
+
return { success: true };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get pending decisions
|
|
172
|
+
*/
|
|
173
|
+
async getPendingDecisions(limit = 20) {
|
|
174
|
+
const sql = `SELECT * FROM v_pending_decisions LIMIT ?`;
|
|
175
|
+
return await this.db.all(sql, [limit]);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get decisions by type
|
|
180
|
+
*/
|
|
181
|
+
async getByType(type, limit = 20) {
|
|
182
|
+
const sql = `
|
|
183
|
+
SELECT * FROM memory_decisions
|
|
184
|
+
WHERE decision_type = ?
|
|
185
|
+
ORDER BY created_at DESC
|
|
186
|
+
LIMIT ?
|
|
187
|
+
`;
|
|
188
|
+
return await this.db.all(sql, [type, limit]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get decisions needing review
|
|
193
|
+
*/
|
|
194
|
+
async getDueReviews(days = 7) {
|
|
195
|
+
const sql = `
|
|
196
|
+
SELECT * FROM memory_decisions
|
|
197
|
+
WHERE outcome_status = 'pending'
|
|
198
|
+
AND review_date <= datetime('now', '+${days} days')
|
|
199
|
+
AND reviewed_at IS NULL
|
|
200
|
+
ORDER BY review_date ASC
|
|
201
|
+
`;
|
|
202
|
+
return await this.db.all(sql);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get decision statistics
|
|
207
|
+
*/
|
|
208
|
+
async getStats() {
|
|
209
|
+
const sql = `
|
|
210
|
+
SELECT
|
|
211
|
+
decision_type,
|
|
212
|
+
outcome_status,
|
|
213
|
+
COUNT(*) as count
|
|
214
|
+
FROM memory_decisions
|
|
215
|
+
GROUP BY decision_type, outcome_status
|
|
216
|
+
`;
|
|
217
|
+
return await this.db.all(sql);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Analyze decision patterns
|
|
222
|
+
*/
|
|
223
|
+
async analyzePatterns() {
|
|
224
|
+
const sql = `
|
|
225
|
+
SELECT
|
|
226
|
+
decision_type,
|
|
227
|
+
AVG(confidence_level) as avg_confidence,
|
|
228
|
+
COUNT(CASE WHEN outcome_status = 'successful' THEN 1 END) as success_count,
|
|
229
|
+
COUNT(CASE WHEN outcome_status = 'failed' THEN 1 END) as failure_count,
|
|
230
|
+
COUNT(*) as total
|
|
231
|
+
FROM memory_decisions
|
|
232
|
+
WHERE outcome_status != 'pending'
|
|
233
|
+
GROUP BY decision_type
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
const patterns = await this.db.all(sql);
|
|
237
|
+
|
|
238
|
+
return patterns.map(p => ({
|
|
239
|
+
...p,
|
|
240
|
+
success_rate: p.total > 0 ? (p.success_count / p.total * 100).toFixed(2) : 0
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get similar past decisions
|
|
246
|
+
*/
|
|
247
|
+
async getSimilarDecisions(question, limit = 5) {
|
|
248
|
+
const keywords = question.toLowerCase().split(/\s+/).filter(w => w.length > 3);
|
|
249
|
+
|
|
250
|
+
if (keywords.length === 0) return [];
|
|
251
|
+
|
|
252
|
+
const conditions = keywords.map(() => 'decision_question LIKE ?').join(' OR ');
|
|
253
|
+
const sql = `
|
|
254
|
+
SELECT * FROM memory_decisions
|
|
255
|
+
WHERE ${conditions}
|
|
256
|
+
ORDER BY created_at DESC
|
|
257
|
+
LIMIT ?
|
|
258
|
+
`;
|
|
259
|
+
|
|
260
|
+
const params = keywords.map(k => `%${k}%`);
|
|
261
|
+
params.push(limit);
|
|
262
|
+
|
|
263
|
+
return await this.db.all(sql, params);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = DecisionModule;
|