neoagent 2.4.2-beta.5 → 2.4.2-beta.7

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.
Files changed (44) hide show
  1. package/README.md +48 -18
  2. package/flutter_app/lib/main_controller.dart +0 -1
  3. package/flutter_app/lib/main_models.dart +28 -0
  4. package/flutter_app/lib/main_operations.dart +1 -1
  5. package/flutter_app/lib/main_settings.dart +9 -14
  6. package/flutter_app/lib/main_shared.dart +0 -9
  7. package/flutter_app/lib/main_unified.dart +0 -2
  8. package/flutter_app/lib/src/backend_client.dart +8 -0
  9. package/package.json +1 -1
  10. package/server/db/database.js +112 -0
  11. package/server/http/routes.js +1 -0
  12. package/server/public/.last_build_id +1 -1
  13. package/server/public/flutter_bootstrap.js +2 -2
  14. package/server/public/main.dart.js +10083 -10063
  15. package/server/routes/mcp.js +9 -0
  16. package/server/routes/memory.js +35 -2
  17. package/server/routes/runtime.js +7 -1
  18. package/server/routes/settings.js +34 -1
  19. package/server/routes/skills.js +42 -0
  20. package/server/routes/task_webhooks.js +65 -0
  21. package/server/services/ai/engine.js +525 -24
  22. package/server/services/ai/learning.js +56 -8
  23. package/server/services/ai/providers/anthropic.js +40 -8
  24. package/server/services/ai/providers/google.js +10 -0
  25. package/server/services/ai/providers/openai.js +3 -15
  26. package/server/services/ai/providers/openaiCompatible.js +11 -0
  27. package/server/services/ai/repetitionGuard.js +60 -0
  28. package/server/services/ai/systemPrompt.js +52 -16
  29. package/server/services/ai/taskAnalysis.js +15 -6
  30. package/server/services/ai/toolEvidence.js +3 -1
  31. package/server/services/ai/toolRunner.js +43 -1
  32. package/server/services/ai/toolSelector.js +92 -46
  33. package/server/services/ai/tools.js +89 -9
  34. package/server/services/ai/usage.js +114 -0
  35. package/server/services/manager.js +34 -0
  36. package/server/services/memory/manager.js +170 -4
  37. package/server/services/security/capability_audit.js +107 -0
  38. package/server/services/tasks/adapters/index.js +1 -0
  39. package/server/services/tasks/adapters/webhook.js +14 -0
  40. package/server/services/tasks/runtime.js +1 -1
  41. package/server/services/tasks/webhooks.js +146 -0
  42. package/server/services/workspace/code_navigation.js +194 -0
  43. package/server/services/workspace/structured_data.js +93 -0
  44. package/server/utils/cloud-security.js +24 -2
@@ -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));
@@ -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
 
@@ -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
 
@@ -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;