neoagent 1.4.5 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -11,7 +11,8 @@ const PACKS = {
11
11
  images: ['generate_image', 'analyze_image'],
12
12
  tables: ['generate_table', 'generate_graph'],
13
13
  subagents: ['spawn_subagent'],
14
- mcpAdmin: ['mcp_add_server', 'mcp_list_servers', 'mcp_remove_server']
14
+ mcpAdmin: ['mcp_add_server', 'mcp_list_servers', 'mcp_remove_server'],
15
+ health: ['read_health_data']
15
16
  };
16
17
 
17
18
  function containsAny(text, patterns) {
@@ -100,6 +101,10 @@ function detectRequestedPacks(task = '', options = {}) {
100
101
  packs.add('images');
101
102
  }
102
103
 
104
+ if (containsAny(text, [/\bhealth\b/, /\bfitness\b/, /\bsteps\b/, /\bsleep\b/, /\bheart rate\b/, /\bworkout\b/, /\bblood\b/, /\bsamsung health\b/, /\bhealth connect\b/])) {
105
+ packs.add('health');
106
+ }
107
+
103
108
  return packs;
104
109
  }
105
110
 
@@ -581,6 +581,17 @@ function getAvailableTools(app, options = {}) {
581
581
  },
582
582
  required: ['image_path']
583
583
  }
584
+ },
585
+ {
586
+ name: 'read_health_data',
587
+ description: 'Read the user\'s synced mobile health data (like steps, heart rate, sleep). If you omit metric_type, it returns a summary of all available metrics. If you provide a metric_type, it returns the most recent historical records for that metric.',
588
+ parameters: {
589
+ type: 'object',
590
+ properties: {
591
+ metric_type: { type: 'string', description: 'The specific metric to query, e.g. "Steps", "HeartRate", "SleepSession". Optional.' },
592
+ limit: { type: 'number', description: 'Maximum number of recent records to return if metric_type is specified (default 50, max 200).' }
593
+ }
594
+ }
584
595
  }
585
596
  ];
586
597
 
@@ -801,6 +812,12 @@ async function executeTool(toolName, args, context, engine) {
801
812
  return { success: true, key: args.key, message: 'Core memory updated' };
802
813
  }
803
814
 
815
+ case 'read_health_data': {
816
+ const { readHealthData } = require('../health/ingestion');
817
+ const result = readHealthData(userId, args.metric_type, args.limit);
818
+ return result;
819
+ }
820
+
804
821
  case 'memory_write': {
805
822
  const { MemoryManager } = require('../memory/manager');
806
823
  const mm = new MemoryManager();
@@ -169,7 +169,42 @@ function getHealthSyncStatus(userId) {
169
169
  };
170
170
  }
171
171
 
172
+ function readHealthData(userId, metricType, limit = 50) {
173
+ if (!metricType) {
174
+ const metrics = db.prepare(`
175
+ SELECT metric_type, COUNT(*) AS sample_count, MAX(COALESCE(end_time, recorded_at, start_time)) AS last_seen_at
176
+ FROM health_metric_samples
177
+ WHERE user_id = ?
178
+ GROUP BY metric_type
179
+ ORDER BY metric_type ASC
180
+ `).all(userId);
181
+ return { metrics };
182
+ }
183
+
184
+ const samples = db.prepare(`
185
+ SELECT
186
+ start_time, end_time, recorded_at,
187
+ numeric_value, text_value, unit,
188
+ source_app_id, source_device,
189
+ payload_json
190
+ FROM health_metric_samples
191
+ WHERE user_id = ? AND metric_type = ?
192
+ ORDER BY COALESCE(end_time, recorded_at, start_time) DESC
193
+ LIMIT ?
194
+ `).all(userId, metricType, limit);
195
+
196
+ return {
197
+ metricType,
198
+ samples: samples.map(s => ({
199
+ ...s,
200
+ payload: s.payload_json ? JSON.parse(s.payload_json) : null,
201
+ payload_json: undefined
202
+ }))
203
+ };
204
+ }
205
+
172
206
  module.exports = {
173
207
  getHealthSyncStatus,
174
208
  ingestHealthSync,
209
+ readHealthData,
175
210
  };
@@ -107,8 +107,8 @@ async function startServices(app, io) {
107
107
  : '';
108
108
 
109
109
  const prompt = isVoiceCall
110
- ? `You are on a live phone call. The caller (${msg.senderName || msg.sender}) said:\n<caller_speech>\n${msg.content}\n</caller_speech>\n\nRespond via send_message with platform="telnyx" and to="${msg.chatId}".`
111
- : `You received a ${msg.platform} message from ${msg.senderName || msg.sender} (chat: ${msg.chatId}):\n<external_message>\n${msg.content}\n</external_message>${mediaNote}${discordContext}${sttNote}\n\nReply via send_message with platform="${msg.platform}" and to="${msg.chatId}".`;
110
+ ? `You are on a live phone call. The caller (${msg.senderName || msg.sender}) said:\n<caller_speech>\n${msg.content}\n</caller_speech>\n\nRespond via send_message with platform="telnyx" and to="${msg.chatId}". Note: once you successfully send your voice response, end the tool loop and output exactly [NO RESPONSE] as your final text.`
111
+ : `You received a ${msg.platform} message from ${msg.senderName || msg.sender} (chat: ${msg.chatId}):\n<external_message>\n${msg.content}\n</external_message>${mediaNote}${discordContext}${sttNote}\n\nReply via send_message with platform="${msg.platform}" and to="${msg.chatId}". Note: once you successfully send your reply, end the tool loop and output exactly [NO RESPONSE] as your final text to prevent double-notifications.`;
112
112
 
113
113
  let convRow = db.prepare(
114
114
  'SELECT id FROM conversations WHERE user_id = ? AND platform = ? AND platform_chat_id = ?'