neoagent 2.4.2-beta.5 → 2.4.2-beta.6
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 +48 -18
- package/flutter_app/lib/main_models.dart +28 -0
- package/flutter_app/lib/main_settings.dart +9 -0
- package/flutter_app/lib/src/backend_client.dart +8 -0
- package/package.json +1 -1
- package/server/db/database.js +112 -0
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +10069 -10049
- package/server/routes/mcp.js +9 -0
- package/server/routes/memory.js +35 -2
- package/server/routes/runtime.js +7 -1
- package/server/routes/settings.js +34 -1
- package/server/routes/skills.js +42 -0
- package/server/routes/task_webhooks.js +65 -0
- package/server/services/ai/engine.js +525 -24
- package/server/services/ai/learning.js +56 -8
- package/server/services/ai/providers/anthropic.js +40 -8
- package/server/services/ai/providers/google.js +10 -0
- package/server/services/ai/providers/openai.js +3 -15
- package/server/services/ai/providers/openaiCompatible.js +11 -0
- package/server/services/ai/repetitionGuard.js +60 -0
- package/server/services/ai/systemPrompt.js +52 -16
- package/server/services/ai/taskAnalysis.js +15 -6
- package/server/services/ai/toolEvidence.js +3 -1
- package/server/services/ai/toolRunner.js +43 -1
- package/server/services/ai/toolSelector.js +92 -46
- package/server/services/ai/tools.js +89 -9
- package/server/services/ai/usage.js +114 -0
- package/server/services/manager.js +34 -0
- package/server/services/memory/manager.js +170 -4
- package/server/services/security/capability_audit.js +107 -0
- package/server/services/tasks/adapters/index.js +1 -0
- package/server/services/tasks/adapters/webhook.js +14 -0
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/tasks/webhooks.js +146 -0
- package/server/services/workspace/code_navigation.js +194 -0
- package/server/services/workspace/structured_data.js +93 -0
- package/server/utils/cloud-security.js +24 -2
package/server/routes/mcp.js
CHANGED
|
@@ -20,6 +20,15 @@ function getTrustedPostMessageOrigin(req) {
|
|
|
20
20
|
|
|
21
21
|
router.use(requireAuth);
|
|
22
22
|
|
|
23
|
+
router.get('/audit', (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
26
|
+
res.json(req.app.locals.capabilityAuditService.auditMcp(req.session.userId, { agentId }));
|
|
27
|
+
} catch (err) {
|
|
28
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
23
32
|
// List configured MCP servers
|
|
24
33
|
router.get('/', (req, res) => {
|
|
25
34
|
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
package/server/routes/memory.js
CHANGED
|
@@ -226,6 +226,39 @@ router.get('/entities', (req, res) => {
|
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
router.get('/facts', (req, res) => {
|
|
230
|
+
const mm = req.app.locals.memoryManager;
|
|
231
|
+
const userId = req.session.userId;
|
|
232
|
+
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
233
|
+
try {
|
|
234
|
+
res.json(mm.listFacts(userId, {
|
|
235
|
+
agentId,
|
|
236
|
+
status: req.query.status || '',
|
|
237
|
+
subject: req.query.subject || '',
|
|
238
|
+
predicate: req.query.predicate || '',
|
|
239
|
+
validAt: req.query.validAt || '',
|
|
240
|
+
knownAt: req.query.knownAt || '',
|
|
241
|
+
limit: req.query.limit,
|
|
242
|
+
}));
|
|
243
|
+
} catch (err) {
|
|
244
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
router.post('/facts/reconcile', (req, res) => {
|
|
249
|
+
const mm = req.app.locals.memoryManager;
|
|
250
|
+
const userId = req.session.userId;
|
|
251
|
+
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
252
|
+
try {
|
|
253
|
+
res.json(mm.reconcileFacts(userId, {
|
|
254
|
+
agentId,
|
|
255
|
+
minimumConfidence: req.body?.minimumConfidence,
|
|
256
|
+
}));
|
|
257
|
+
} catch (err) {
|
|
258
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
229
262
|
router.post('/knowledge-views/materialize', (req, res) => {
|
|
230
263
|
const mm = req.app.locals.memoryManager;
|
|
231
264
|
const userId = req.session.userId;
|
|
@@ -424,7 +457,7 @@ router.post('/transfer-import', transferImportLimiter, async (req, res) => {
|
|
|
424
457
|
if (applyCoreMemory && parsed.coreEntries) {
|
|
425
458
|
for (const [key, value] of Object.entries(parsed.coreEntries)) {
|
|
426
459
|
if (!key || key === 'active_context') continue;
|
|
427
|
-
mm.updateCore(userId, key, value, { agentId });
|
|
460
|
+
mm.updateCore(userId, key, value, { agentId, confirmed: true });
|
|
428
461
|
coreUpdatedCount += 1;
|
|
429
462
|
}
|
|
430
463
|
}
|
|
@@ -456,7 +489,7 @@ router.put('/core/:key', (req, res) => {
|
|
|
456
489
|
if (req.params.key === 'active_context') {
|
|
457
490
|
return res.status(400).json({ error: 'active_context is no longer a supported core memory key' });
|
|
458
491
|
}
|
|
459
|
-
mm.updateCore(userId, req.params.key, value, { agentId });
|
|
492
|
+
mm.updateCore(userId, req.params.key, value, { agentId, confirmed: true });
|
|
460
493
|
res.json({ success: true });
|
|
461
494
|
});
|
|
462
495
|
|
package/server/routes/runtime.js
CHANGED
|
@@ -5,7 +5,13 @@ const express = require('express');
|
|
|
5
5
|
const router = express.Router();
|
|
6
6
|
|
|
7
7
|
router.get('/config', (req, res) => {
|
|
8
|
-
res.json({
|
|
8
|
+
res.json({
|
|
9
|
+
analytics: {
|
|
10
|
+
enabled: false,
|
|
11
|
+
provider: null,
|
|
12
|
+
requiresConsent: true,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
9
15
|
});
|
|
10
16
|
|
|
11
17
|
module.exports = router;
|
|
@@ -410,6 +410,23 @@ router.get('/token-usage/summary', (req, res) => {
|
|
|
410
410
|
return acc;
|
|
411
411
|
}, { count: 0, system: 0, tools: 0, history: 0, recall: 0, replay: 0 });
|
|
412
412
|
|
|
413
|
+
const normalizedUsage = db.prepare(`
|
|
414
|
+
SELECT
|
|
415
|
+
COALESCE(SUM(input_tokens), 0) AS inputTokens,
|
|
416
|
+
COALESCE(SUM(output_tokens), 0) AS outputTokens,
|
|
417
|
+
COALESCE(SUM(reasoning_tokens), 0) AS reasoningTokens,
|
|
418
|
+
COALESCE(SUM(cached_read_tokens), 0) AS cachedReadTokens,
|
|
419
|
+
COALESCE(SUM(cache_write_tokens), 0) AS cacheWriteTokens,
|
|
420
|
+
COALESCE(SUM(total_tokens), 0) AS totalTokens,
|
|
421
|
+
SUM(estimated_cost_usd) AS estimatedCostUsd,
|
|
422
|
+
COUNT(estimated_cost_usd) AS pricedCallCount,
|
|
423
|
+
COUNT(*) AS callCount
|
|
424
|
+
FROM agent_model_usage
|
|
425
|
+
WHERE user_id = ? AND agent_id = ?
|
|
426
|
+
`).get(userId, agentId);
|
|
427
|
+
const cacheEligibleTokens = Number(normalizedUsage?.inputTokens || 0)
|
|
428
|
+
+ Number(normalizedUsage?.cachedReadTokens || 0);
|
|
429
|
+
|
|
413
430
|
res.json({
|
|
414
431
|
totals: {
|
|
415
432
|
totalTokens: Number(totals?.totalTokens || 0),
|
|
@@ -429,7 +446,23 @@ router.get('/token-usage/summary', (req, res) => {
|
|
|
429
446
|
toolReplayTokens: Math.round(metricTotals.replay / metricTotals.count)
|
|
430
447
|
},
|
|
431
448
|
latest: parsedMetrics[0] || null
|
|
432
|
-
}
|
|
449
|
+
},
|
|
450
|
+
modelUsage: {
|
|
451
|
+
inputTokens: Number(normalizedUsage?.inputTokens || 0),
|
|
452
|
+
outputTokens: Number(normalizedUsage?.outputTokens || 0),
|
|
453
|
+
reasoningTokens: Number(normalizedUsage?.reasoningTokens || 0),
|
|
454
|
+
cachedReadTokens: Number(normalizedUsage?.cachedReadTokens || 0),
|
|
455
|
+
cacheWriteTokens: Number(normalizedUsage?.cacheWriteTokens || 0),
|
|
456
|
+
totalTokens: Number(normalizedUsage?.totalTokens || 0),
|
|
457
|
+
estimatedCostUsd: Number(normalizedUsage?.pricedCallCount || 0) > 0
|
|
458
|
+
? Number(normalizedUsage.estimatedCostUsd)
|
|
459
|
+
: null,
|
|
460
|
+
pricedCallCount: Number(normalizedUsage?.pricedCallCount || 0),
|
|
461
|
+
callCount: Number(normalizedUsage?.callCount || 0),
|
|
462
|
+
cacheHitRatio: cacheEligibleTokens > 0
|
|
463
|
+
? Number(normalizedUsage?.cachedReadTokens || 0) / cacheEligibleTokens
|
|
464
|
+
: 0,
|
|
465
|
+
},
|
|
433
466
|
});
|
|
434
467
|
});
|
|
435
468
|
|
package/server/routes/skills.js
CHANGED
|
@@ -2,6 +2,7 @@ const express = require('express');
|
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { requireAuth } = require('../middleware/auth');
|
|
4
4
|
const { sanitizeError } = require('../utils/security');
|
|
5
|
+
const db = require('../db/database');
|
|
5
6
|
const {
|
|
6
7
|
getSkillRunner,
|
|
7
8
|
serializeInstalledSkill,
|
|
@@ -62,6 +63,47 @@ router.get('/', async (req, res) => {
|
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
65
|
|
|
66
|
+
router.get('/metrics/summary', (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const rows = db.prepare(
|
|
69
|
+
`SELECT skill_name, invocation_count, success_count, failure_count,
|
|
70
|
+
correction_count, total_tokens, last_used_at
|
|
71
|
+
FROM skill_metrics WHERE user_id = ?
|
|
72
|
+
ORDER BY invocation_count DESC, skill_name ASC`
|
|
73
|
+
).all(req.session.userId);
|
|
74
|
+
res.json(rows.map((row) => {
|
|
75
|
+
const invocations = Number(row.invocation_count || 0);
|
|
76
|
+
const successes = Number(row.success_count || 0);
|
|
77
|
+
const failures = Number(row.failure_count || 0);
|
|
78
|
+
let recommendation = 'keep';
|
|
79
|
+
if (invocations >= 5 && failures / invocations >= 0.4) recommendation = 'improve_or_disable';
|
|
80
|
+
else if (invocations >= 10 && successes === 0) recommendation = 'disable_or_delete';
|
|
81
|
+
else if (invocations === 0) recommendation = 'review_unused';
|
|
82
|
+
return {
|
|
83
|
+
skillName: row.skill_name,
|
|
84
|
+
invocationCount: invocations,
|
|
85
|
+
successCount: successes,
|
|
86
|
+
failureCount: failures,
|
|
87
|
+
correctionCount: Number(row.correction_count || 0),
|
|
88
|
+
totalTokens: Number(row.total_tokens || 0),
|
|
89
|
+
successRate: invocations > 0 ? successes / invocations : null,
|
|
90
|
+
lastUsedAt: row.last_used_at,
|
|
91
|
+
recommendation,
|
|
92
|
+
};
|
|
93
|
+
}));
|
|
94
|
+
} catch (err) {
|
|
95
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
router.get('/audit/summary', (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
res.json(req.app.locals.capabilityAuditService.auditSkills());
|
|
102
|
+
} catch (err) {
|
|
103
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
65
107
|
router.get('/:name', async (req, res) => {
|
|
66
108
|
try {
|
|
67
109
|
const runner = await getSkillRunner(req.app);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const { requireAuth } = require('../middleware/auth');
|
|
5
|
+
const { sanitizeError } = require('../utils/security');
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
function parseTaskId(value) {
|
|
10
|
+
const taskId = Number.parseInt(value, 10);
|
|
11
|
+
if (!Number.isInteger(taskId) || taskId <= 0) throw new Error('Invalid task id.');
|
|
12
|
+
return taskId;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
router.post('/:taskId/deliver', async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await req.app.locals.taskWebhookService.deliver(
|
|
18
|
+
parseTaskId(req.params.taskId),
|
|
19
|
+
req.headers,
|
|
20
|
+
req.rawBody || JSON.stringify(req.body || {}),
|
|
21
|
+
req.body || {},
|
|
22
|
+
);
|
|
23
|
+
res.status(202).json(result);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
router.use(requireAuth);
|
|
30
|
+
|
|
31
|
+
router.get('/:taskId', (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
res.json(req.app.locals.taskWebhookService.getConfiguration(
|
|
34
|
+
req.session.userId,
|
|
35
|
+
parseTaskId(req.params.taskId),
|
|
36
|
+
));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
router.post('/:taskId/rotate', (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
res.json(req.app.locals.taskWebhookService.rotateSecret(
|
|
45
|
+
req.session.userId,
|
|
46
|
+
parseTaskId(req.params.taskId),
|
|
47
|
+
));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
router.get('/:taskId/deliveries', (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
res.json(req.app.locals.taskWebhookService.listDeliveries(
|
|
56
|
+
req.session.userId,
|
|
57
|
+
parseTaskId(req.params.taskId),
|
|
58
|
+
req.query?.limit,
|
|
59
|
+
));
|
|
60
|
+
} catch (err) {
|
|
61
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
module.exports = router;
|