neoagent 2.4.1-beta.36 → 2.4.1-beta.37

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.
@@ -186,7 +186,7 @@ router.post('/:id/abort', (req, res) => {
186
186
  const run = db.prepare('SELECT id FROM agent_runs WHERE id = ? AND user_id = ?').get(req.params.id, req.session.userId);
187
187
  if (!run) return res.status(404).json({ error: 'Run not found' });
188
188
  const engine = req.app.locals.agentEngine;
189
- engine.abort(req.params.id);
189
+ engine.abort(req.params.id, { userId: req.session.userId });
190
190
  res.json({ success: true });
191
191
  } catch (err) {
192
192
  res.status(500).json({ error: sanitizeError(err) });
@@ -129,7 +129,9 @@ function readAuthenticatedUser(req) {
129
129
  if (!user) {
130
130
  try {
131
131
  req.session.destroy(() => {});
132
- } catch (_) {}
132
+ } catch (destroyError) {
133
+ console.warn('Auth stale-session cleanup failed:', destroyError.message);
134
+ }
133
135
  return null;
134
136
  }
135
137
  return user;
@@ -2,21 +2,24 @@
2
2
 
3
3
  const express = require('express');
4
4
  const db = require('../db/database');
5
+ const { buildFtsQuery } = require('../db/ftsQuery');
6
+ const { requireAuth } = require('../middleware/auth');
5
7
  const { getErrorMessage } = require('../services/bootstrap_helpers');
6
8
 
7
9
  const router = express.Router();
8
10
 
11
+ router.use(requireAuth);
12
+
9
13
  router.get('/search', (req, res) => {
10
14
  const { q, limit = 50, offset = 0 } = req.query;
11
-
12
- if (!req.user || !req.user.id) {
13
- return res.status(401).json({ error: 'Unauthorized' });
14
- }
15
+ const userId = req.session.userId;
15
16
 
16
17
  try {
17
18
  let results = [];
18
- if (q) {
19
- // Full text search
19
+ const ftsQuery = q ? buildFtsQuery(q) : null;
20
+ if (ftsQuery) {
21
+ // Full text search. buildFtsQuery sanitizes user input so FTS5 operator
22
+ // characters (hyphens, AND/OR/NOT) don't throw and 500 the request.
20
23
  results = db.prepare(`
21
24
  SELECT s.id, s.timestamp, s.app_name, s.text_content
22
25
  FROM screen_history_fts fts
@@ -24,7 +27,10 @@ router.get('/search', (req, res) => {
24
27
  WHERE screen_history_fts MATCH ? AND s.user_id = ?
25
28
  ORDER BY s.timestamp DESC
26
29
  LIMIT ? OFFSET ?
27
- `).all(q, req.user.id, Number(limit), Number(offset));
30
+ `).all(ftsQuery, userId, Number(limit), Number(offset));
31
+ } else if (q) {
32
+ // Query had no usable search tokens — return no matches rather than error.
33
+ results = [];
28
34
  } else {
29
35
  // Recent history
30
36
  results = db.prepare(`
@@ -33,7 +39,7 @@ router.get('/search', (req, res) => {
33
39
  WHERE user_id = ?
34
40
  ORDER BY timestamp DESC
35
41
  LIMIT ? OFFSET ?
36
- `).all(req.user.id, Number(limit), Number(offset));
42
+ `).all(userId, Number(limit), Number(offset));
37
43
  }
38
44
 
39
45
  res.json({ results });
@@ -13,10 +13,10 @@ router.post('/geofence', async (req, res) => {
13
13
  const { label, latitude, longitude, radius_meters, action } = req.body;
14
14
 
15
15
  try {
16
- const userRow = db.prepare('SELECT id FROM users WHERE id = ?').get(req.user.id);
16
+ const userRow = db.prepare('SELECT id FROM users WHERE id = ?').get(req.session.userId);
17
17
  if (!userRow) return res.status(401).json({ error: 'Unauthorized' });
18
18
 
19
- console.log(`[Triggers] Geofence entered: ${label} by user ${req.user.id}`);
19
+ console.log(`[Triggers] Geofence entered: ${label} by user ${req.session.userId}`);
20
20
 
21
21
  res.json({ success: true, message: 'Geofence trigger processed' });
22
22
  } catch (err) {
@@ -29,14 +29,14 @@ router.post('/geofence', async (req, res) => {
29
29
  Promise.resolve().then(() => {
30
30
  const agentEngine = req.app.locals.agentEngine;
31
31
  if (!agentEngine) return;
32
- const defaultAgentId = db.prepare('SELECT id FROM agents WHERE user_id = ? ORDER BY is_default DESC LIMIT 1').get(req.user.id)?.id;
32
+ const defaultAgentId = db.prepare('SELECT id FROM agents WHERE user_id = ? ORDER BY is_default DESC LIMIT 1').get(req.session.userId)?.id;
33
33
  if (!defaultAgentId) return;
34
34
  const prompt = [
35
35
  `A geofence event was triggered.`,
36
36
  `Location label: ${label || 'unknown'}`,
37
37
  action ? `Suggested action: ${action}` : 'Check if there are any active reminders or tasks related to this location.',
38
38
  ].join('\n');
39
- return agentEngine.run(req.user.id, prompt, {
39
+ return agentEngine.run(req.session.userId, prompt, {
40
40
  agentId: defaultAgentId,
41
41
  triggerSource: 'tasks',
42
42
  context: { source: 'geofence', label, latitude, longitude, radius_meters },
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ // Decides whether the agent may accept a `task_complete` signal, balancing the
4
+ // model's self-reported confidence against the confidence the run requires.
5
+
6
+ function normalizeCompletionConfidence(value) {
7
+ const normalized = String(value || '').trim().toLowerCase();
8
+ if (normalized === 'high' || normalized === 'medium' || normalized === 'low') return normalized;
9
+ return 'medium';
10
+ }
11
+
12
+ function completionConfidenceRank(value) {
13
+ const normalized = normalizeCompletionConfidence(value);
14
+ if (normalized === 'high') return 3;
15
+ if (normalized === 'medium') return 2;
16
+ return 1;
17
+ }
18
+
19
+ function shouldAcceptTaskComplete({ confidence, requiredConfidence, iteration, maxIterations }) {
20
+ const required = normalizeCompletionConfidence(requiredConfidence || 'medium');
21
+ const actual = normalizeCompletionConfidence(confidence || 'medium');
22
+ if (completionConfidenceRank(actual) >= completionConfidenceRank(required)) {
23
+ return { accept: true, reason: '' };
24
+ }
25
+
26
+ const iterationsRemaining = Math.max(0, Number(maxIterations || 0) - Number(iteration || 0));
27
+ if (iterationsRemaining <= 1) {
28
+ return {
29
+ accept: true,
30
+ reason: `Accepted ${actual}-confidence completion at the iteration limit; final verifier will calibrate the answer.`,
31
+ };
32
+ }
33
+
34
+ return {
35
+ accept: false,
36
+ reason: `Completion confidence "${actual}" is below required "${required}". Continue with verification, recovery, or a narrower truthful result before completing.`,
37
+ };
38
+ }
39
+
40
+ module.exports = {
41
+ normalizeCompletionConfidence,
42
+ completionConfidenceRank,
43
+ shouldAcceptTaskComplete,
44
+ };