neoagent 2.4.1-beta.19 → 2.4.1-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/docs/getting-started.md +9 -3
- package/flutter_app/assets/branding/app_icon_light_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_64.png +0 -0
- package/flutter_app/assets/branding/tray_icon_light_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +3 -0
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_account_settings.dart +9 -33
- package/flutter_app/lib/main_app_shell.dart +237 -197
- package/flutter_app/lib/main_controller.dart +0 -25
- package/flutter_app/lib/main_devices.dart +2 -0
- package/flutter_app/lib/main_models.dart +144 -0
- package/flutter_app/lib/main_operations.dart +150 -19
- package/flutter_app/lib/main_shared.dart +642 -195
- package/flutter_app/lib/main_theme.dart +2 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +3 -1
- package/flutter_app/lib/src/security/password_strength.dart +84 -0
- package/flutter_app/lib/src/theme/palette.dart +15 -15
- package/flutter_app/pubspec.yaml +3 -0
- package/flutter_app/web/favicon_light.svg +3 -0
- package/flutter_app/web/icons/Icon-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-512-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512-light.png +0 -0
- package/lib/manager.js +282 -81
- package/package.json +17 -3
- package/server/config/origins.js +3 -1
- package/server/db/database.js +73 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/assets/branding/app_icon_light_256.png +0 -0
- package/server/public/assets/assets/branding/app_icon_light_512.png +0 -0
- package/server/public/assets/assets/branding/tray_icon_light_template.png +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/favicon_light.svg +3 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/icons/Icon-192-light.png +0 -0
- package/server/public/icons/Icon-512-light.png +0 -0
- package/server/public/icons/Icon-maskable-192-light.png +0 -0
- package/server/public/icons/Icon-maskable-512-light.png +0 -0
- package/server/public/main.dart.js +68769 -68268
- package/server/routes/agent_profiles.js +3 -0
- package/server/routes/memory.js +22 -1
- package/server/services/account/password_policy.js +6 -1
- package/server/services/memory/intelligence.js +181 -0
- package/server/services/memory/manager.js +475 -25
- package/server/utils/security.js +3 -0
- package/server/services/memory/openhuman_uplift.test.js +0 -98
- package/server/utils/version.test.js +0 -39
|
@@ -41,6 +41,7 @@ router.put('/:id', (req, res) => {
|
|
|
41
41
|
try {
|
|
42
42
|
res.json(updateAgent(req.session.userId, req.params.id, req.body || {}));
|
|
43
43
|
} catch (err) {
|
|
44
|
+
if (err.message === 'Agent not found.') return res.status(404).json({ error: err.message });
|
|
44
45
|
res.status(400).json({ error: sanitizeError(err) });
|
|
45
46
|
}
|
|
46
47
|
});
|
|
@@ -49,6 +50,7 @@ router.post('/:id/default', (req, res) => {
|
|
|
49
50
|
try {
|
|
50
51
|
res.json(setDefaultAgent(req.session.userId, req.params.id));
|
|
51
52
|
} catch (err) {
|
|
53
|
+
if (err.message === 'Agent not found.') return res.status(404).json({ error: err.message });
|
|
52
54
|
res.status(400).json({ error: sanitizeError(err) });
|
|
53
55
|
}
|
|
54
56
|
});
|
|
@@ -57,6 +59,7 @@ router.delete('/:id', (req, res) => {
|
|
|
57
59
|
try {
|
|
58
60
|
res.json(archiveAgent(req.session.userId, req.params.id));
|
|
59
61
|
} catch (err) {
|
|
62
|
+
if (err.message === 'Agent not found.') return res.status(404).json({ error: err.message });
|
|
60
63
|
res.status(400).json({ error: sanitizeError(err) });
|
|
61
64
|
}
|
|
62
65
|
});
|
package/server/routes/memory.js
CHANGED
|
@@ -111,13 +111,19 @@ router.get('/', (req, res) => {
|
|
|
111
111
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
112
112
|
const coreMemory = { ...(mm.getCoreMemory(userId, { agentId }) || {}) };
|
|
113
113
|
delete coreMemory.active_context;
|
|
114
|
+
const knowledgeViews = mm.listKnowledgeViews(userId, { agentId, limit: 12 });
|
|
114
115
|
res.json({
|
|
115
116
|
agentId,
|
|
116
117
|
assistantBehaviorNotes: mm.getAssistantBehaviorNotes(userId, { agentId }),
|
|
117
118
|
assistantSelfState: mm.getAssistantSelfState(userId, { agentId }),
|
|
118
119
|
dailyLogs: mm.listDailyLogs(7, userId),
|
|
119
120
|
apiKeys: Object.keys(mm.readApiKeys(userId)),
|
|
120
|
-
coreMemory
|
|
121
|
+
coreMemory,
|
|
122
|
+
stats: mm.getMemoryStats(userId, { agentId }),
|
|
123
|
+
entities: mm.listEntities(userId, { agentId, limit: 12 }),
|
|
124
|
+
knowledgeViews,
|
|
125
|
+
ingestionOverview: mm.getIngestionOverview(userId, { agentId }),
|
|
126
|
+
recentKnowledgeChanges: mm.listRecentKnowledgeChanges(userId, { agentId, limit: 8 }),
|
|
121
127
|
});
|
|
122
128
|
});
|
|
123
129
|
|
|
@@ -204,6 +210,21 @@ router.get('/knowledge-views', (req, res) => {
|
|
|
204
210
|
}
|
|
205
211
|
});
|
|
206
212
|
|
|
213
|
+
router.get('/entities', (req, res) => {
|
|
214
|
+
const mm = req.app.locals.memoryManager;
|
|
215
|
+
const userId = req.session.userId;
|
|
216
|
+
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
217
|
+
try {
|
|
218
|
+
res.json(mm.listEntities(userId, {
|
|
219
|
+
agentId,
|
|
220
|
+
query: req.query.query || null,
|
|
221
|
+
limit: req.query.limit,
|
|
222
|
+
}));
|
|
223
|
+
} catch (err) {
|
|
224
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
207
228
|
router.post('/knowledge-views/materialize', (req, res) => {
|
|
208
229
|
const mm = req.app.locals.memoryManager;
|
|
209
230
|
const userId = req.session.userId;
|
|
@@ -113,7 +113,12 @@ function evaluatePasswordStrength(password, context = {}) {
|
|
|
113
113
|
label,
|
|
114
114
|
length,
|
|
115
115
|
hasMinimumLength: length >= MIN_PASSWORD_LENGTH,
|
|
116
|
-
isAcceptable: length >= MIN_PASSWORD_LENGTH
|
|
116
|
+
isAcceptable: length >= MIN_PASSWORD_LENGTH
|
|
117
|
+
&& score >= MIN_PASSWORD_SCORE
|
|
118
|
+
&& !containsPersonalInfo
|
|
119
|
+
&& !usesCommonPattern
|
|
120
|
+
&& !sequential
|
|
121
|
+
&& !repeatedRuns,
|
|
117
122
|
feedback,
|
|
118
123
|
};
|
|
119
124
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
const ENTITY_KIND_PATTERNS = [
|
|
6
|
+
['email', /^[^\s@]+@[^\s@]+\.[^\s@]+$/],
|
|
7
|
+
['url', /^https?:\/\//i],
|
|
8
|
+
['version', /^v?\d+(?:\.\d+){1,3}$/i],
|
|
9
|
+
['identifier', /^[a-z0-9][a-z0-9_-]{2,}$/i],
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
function clamp(value, min, max, fallback) {
|
|
13
|
+
const number = Number(value);
|
|
14
|
+
return Number.isFinite(number) ? Math.max(min, Math.min(max, number)) : fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function stableHash(value) {
|
|
18
|
+
return crypto
|
|
19
|
+
.createHash('sha256')
|
|
20
|
+
.update(String(value || '').trim().toLowerCase())
|
|
21
|
+
.digest('hex');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function canonicalEntityKey(name) {
|
|
25
|
+
return String(name || '')
|
|
26
|
+
.trim()
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^\p{L}\p{N}._@:/+-]+/gu, ' ')
|
|
29
|
+
.replace(/\s+/g, ' ')
|
|
30
|
+
.slice(0, 160);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function classifyEntity(name) {
|
|
34
|
+
const value = String(name || '').trim();
|
|
35
|
+
for (const [kind, pattern] of ENTITY_KIND_PATTERNS) {
|
|
36
|
+
if (pattern.test(value)) return kind;
|
|
37
|
+
}
|
|
38
|
+
if (/^[A-Z][A-Z0-9_-]{2,}$/.test(value)) return 'acronym';
|
|
39
|
+
if (/\.(js|ts|dart|py|json|md|yaml|yml|sql|rs|go|java|kt|swift|css|html)$/i.test(value)) {
|
|
40
|
+
return 'file';
|
|
41
|
+
}
|
|
42
|
+
return 'concept';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function uniqueByKey(items, getKey) {
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const item of items) {
|
|
49
|
+
const key = getKey(item);
|
|
50
|
+
if (!key || seen.has(key)) continue;
|
|
51
|
+
seen.add(key);
|
|
52
|
+
result.push(item);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function extractEntities(text, { maxEntities = 16 } = {}) {
|
|
58
|
+
const raw = String(text || '');
|
|
59
|
+
const candidates = [];
|
|
60
|
+
|
|
61
|
+
for (const match of raw.matchAll(/[^\s@]+@[^\s@]+\.[^\s@]+/g)) {
|
|
62
|
+
candidates.push(match[0]);
|
|
63
|
+
}
|
|
64
|
+
for (const match of raw.matchAll(/https?:\/\/[^\s)]+/gi)) {
|
|
65
|
+
candidates.push(match[0].replace(/[.,;]+$/g, ''));
|
|
66
|
+
}
|
|
67
|
+
for (const match of raw.matchAll(/\b[A-Z][\p{L}\p{N}_+./:-]*(?:\s+[A-Z][\p{L}\p{N}_+./:-]*){0,4}\b/gu)) {
|
|
68
|
+
const value = match[0].trim();
|
|
69
|
+
if (value.length > 2) candidates.push(value);
|
|
70
|
+
}
|
|
71
|
+
for (const match of raw.matchAll(/\b[\p{L}\p{N}_./-]+\.(?:js|ts|dart|py|json|md|yaml|yml|sql|rs|go|java|kt|swift|css|html)\b/giu)) {
|
|
72
|
+
candidates.push(match[0]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return uniqueByKey(
|
|
76
|
+
candidates
|
|
77
|
+
.map((name) => {
|
|
78
|
+
const normalized = String(name || '').trim().replace(/\s+/g, ' ');
|
|
79
|
+
const key = canonicalEntityKey(normalized);
|
|
80
|
+
if (!key || key.length < 2) return null;
|
|
81
|
+
return {
|
|
82
|
+
key,
|
|
83
|
+
name: normalized.slice(0, 160),
|
|
84
|
+
kind: classifyEntity(normalized),
|
|
85
|
+
};
|
|
86
|
+
})
|
|
87
|
+
.filter(Boolean),
|
|
88
|
+
(entity) => entity.key,
|
|
89
|
+
).slice(0, maxEntities);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function extractKeywords(text, { maxKeywords = 24 } = {}) {
|
|
93
|
+
const counts = new Map();
|
|
94
|
+
const tokens = String(text || '')
|
|
95
|
+
.toLowerCase()
|
|
96
|
+
.match(/[\p{L}\p{N}_-]{5,}/gu) || [];
|
|
97
|
+
for (const token of tokens) {
|
|
98
|
+
if (/^\d+$/.test(token)) continue;
|
|
99
|
+
counts.set(token, (counts.get(token) || 0) + 1);
|
|
100
|
+
}
|
|
101
|
+
return [...counts.entries()]
|
|
102
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
103
|
+
.slice(0, maxKeywords)
|
|
104
|
+
.map(([keyword]) => keyword);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function splitSentences(text) {
|
|
108
|
+
return String(text || '')
|
|
109
|
+
.split(/(?<=[.!?])\s+|\n+/u)
|
|
110
|
+
.map((part) => part.trim())
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildFacts({ content, category, sourceRef, metadata } = {}) {
|
|
115
|
+
const sentences = splitSentences(content);
|
|
116
|
+
const entities = extractEntities(content);
|
|
117
|
+
const keywords = extractKeywords(content, { maxKeywords: 10 });
|
|
118
|
+
const subject = entities[0]?.name || String(category || 'memory').replace(/_/g, ' ');
|
|
119
|
+
const predicate = String(category || 'episodic').replace(/_/g, ' ');
|
|
120
|
+
const sourceType = sourceRef?.sourceType || metadata?.sourceType || null;
|
|
121
|
+
|
|
122
|
+
const factTexts = sentences.length
|
|
123
|
+
? sentences.slice(0, 6)
|
|
124
|
+
: [String(content || '').trim()].filter(Boolean);
|
|
125
|
+
|
|
126
|
+
return factTexts.map((text, index) => ({
|
|
127
|
+
subject: String(subject || 'memory').slice(0, 180),
|
|
128
|
+
predicate: index === 0 ? predicate : 'detail',
|
|
129
|
+
object: text.slice(0, 900),
|
|
130
|
+
category,
|
|
131
|
+
confidence: sourceType === 'llm_import' ? 0.74 : 0.68,
|
|
132
|
+
metadata: {
|
|
133
|
+
keywords,
|
|
134
|
+
sourceType,
|
|
135
|
+
extractedBy: 'local_memory_intelligence',
|
|
136
|
+
},
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function summarizeForPrompt(memory) {
|
|
141
|
+
const entities = Array.isArray(memory.entities) ? memory.entities : [];
|
|
142
|
+
const content = String(memory.content || '').replace(/\s+/g, ' ').trim();
|
|
143
|
+
const entitySuffix = entities.length
|
|
144
|
+
? ` (${entities.slice(0, 4).map((entity) => entity.name || entity).join(', ')})`
|
|
145
|
+
: '';
|
|
146
|
+
return `${content}${entitySuffix}`.slice(0, 900);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function rankFuse(rank, weight = 1) {
|
|
150
|
+
if (!Number.isFinite(rank) || rank < 0) return 0;
|
|
151
|
+
return weight / (60 + rank + 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function scoreMemoryCandidate({
|
|
155
|
+
semanticRank = -1,
|
|
156
|
+
lexicalRank = -1,
|
|
157
|
+
entityRank = -1,
|
|
158
|
+
baseScore = 0,
|
|
159
|
+
importance = 5,
|
|
160
|
+
accessCount = 0,
|
|
161
|
+
freshness = 1,
|
|
162
|
+
} = {}) {
|
|
163
|
+
const fused = (
|
|
164
|
+
rankFuse(semanticRank, 1.0) +
|
|
165
|
+
rankFuse(lexicalRank, 0.85) +
|
|
166
|
+
rankFuse(entityRank, 0.95)
|
|
167
|
+
) * 20;
|
|
168
|
+
const quality = 0.15 + clamp(importance, 1, 10, 5) / 22;
|
|
169
|
+
const usage = Math.min(0.08, Math.log1p(Math.max(0, Number(accessCount) || 0)) / 50);
|
|
170
|
+
return Math.max(baseScore, fused + quality + usage) * freshness;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
buildFacts,
|
|
175
|
+
canonicalEntityKey,
|
|
176
|
+
extractEntities,
|
|
177
|
+
extractKeywords,
|
|
178
|
+
scoreMemoryCandidate,
|
|
179
|
+
stableHash,
|
|
180
|
+
summarizeForPrompt,
|
|
181
|
+
};
|