neoagent 2.1.17-beta.19 → 2.1.17-beta.20

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": "2.1.17-beta.19",
3
+ "version": "2.1.17-beta.20",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"425cfb54d01a9472b3e81d9e76fd63a4a44cfb
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "4027054955" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "753467176" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -87,7 +87,9 @@ router.post('/', (req, res) => {
87
87
  const device = manager.registerDevice(req.session.userId, macAddress, protocol, name);
88
88
  res.status(201).json(device);
89
89
  } catch (err) {
90
- res.status(500).json({ error: sanitizeError(err) });
90
+ const message = sanitizeError(err);
91
+ const isClientError = /unsupported wearable protocol|required/i.test(message);
92
+ res.status(isClientError ? 400 : 500).json({ error: message });
91
93
  }
92
94
  });
93
95
 
@@ -54,14 +54,24 @@ class GrokProvider extends BaseProvider {
54
54
 
55
55
  async *stream(messages, tools = [], options = {}) {
56
56
  const model = options.model || 'grok-4-1-fast-reasoning';
57
- const params = { ...this._buildParams(model, messages, tools, options), stream: true };
57
+ const params = {
58
+ ...this._buildParams(model, messages, tools, options),
59
+ stream: true,
60
+ stream_options: { include_usage: true }
61
+ };
58
62
 
59
63
  const stream = await this.client.chat.completions.create(params);
60
64
 
61
65
  let toolCalls = [];
62
66
  let content = '';
67
+ let finalUsage = null;
63
68
 
64
69
  for await (const chunk of stream) {
70
+ if (chunk.usage && (!chunk.choices || chunk.choices.length === 0)) {
71
+ finalUsage = this.#normalizeUsage(chunk.usage);
72
+ continue;
73
+ }
74
+
65
75
  const delta = chunk.choices[0]?.delta;
66
76
  if (!delta) continue;
67
77
 
@@ -83,19 +93,28 @@ class GrokProvider extends BaseProvider {
83
93
 
84
94
  const finishReason = chunk.choices[0]?.finish_reason;
85
95
  if (finishReason === 'tool_calls' || (finishReason === 'stop' && toolCalls.length > 0)) {
86
- yield { type: 'tool_calls', toolCalls, content };
96
+ yield {
97
+ type: 'tool_calls',
98
+ toolCalls,
99
+ content,
100
+ usage: this.#normalizeUsage(chunk.usage) || finalUsage
101
+ };
87
102
  return;
88
103
  }
89
104
  if (finishReason === 'stop') {
90
- yield { type: 'done', content };
105
+ yield {
106
+ type: 'done',
107
+ content,
108
+ usage: this.#normalizeUsage(chunk.usage) || finalUsage
109
+ };
91
110
  return;
92
111
  }
93
112
  }
94
113
 
95
114
  if (toolCalls.length > 0) {
96
- yield { type: 'tool_calls', toolCalls, content };
115
+ yield { type: 'tool_calls', toolCalls, content, usage: finalUsage };
97
116
  } else {
98
- yield { type: 'done', content };
117
+ yield { type: 'done', content, usage: finalUsage };
99
118
  }
100
119
  }
101
120
 
@@ -110,7 +129,18 @@ class GrokProvider extends BaseProvider {
110
129
  function: { name: tc.function.name, arguments: tc.function.arguments }
111
130
  })) || [],
112
131
  finishReason: choice.finish_reason,
113
- usage: response.usage
132
+ usage: this.#normalizeUsage(response.usage)
133
+ };
134
+ }
135
+
136
+ #normalizeUsage(usage) {
137
+ if (!usage) {
138
+ return null;
139
+ }
140
+ return {
141
+ promptTokens: usage.prompt_tokens ?? usage.promptTokens ?? 0,
142
+ completionTokens: usage.completion_tokens ?? usage.completionTokens ?? 0,
143
+ totalTokens: usage.total_tokens ?? usage.totalTokens ?? 0,
114
144
  };
115
145
  }
116
146
 
@@ -97,19 +97,25 @@ function withOwnershipLock(work) {
97
97
  if (!fs.existsSync(OWNERSHIP_FILE)) {
98
98
  fs.writeFileSync(OWNERSHIP_FILE, '{}');
99
99
  }
100
- const release = lockfile.lockSync(OWNERSHIP_FILE, {
101
- realpath: false,
102
- retries: {
103
- retries: 3,
104
- minTimeout: 20,
105
- maxTimeout: 120,
106
- },
107
- });
108
- try {
109
- return work();
110
- } finally {
111
- release();
100
+ let lastError = null;
101
+ for (let attempt = 0; attempt < 3; attempt += 1) {
102
+ try {
103
+ const release = lockfile.lockSync(OWNERSHIP_FILE, {
104
+ realpath: false,
105
+ });
106
+ try {
107
+ return work();
108
+ } finally {
109
+ release();
110
+ }
111
+ } catch (err) {
112
+ lastError = err;
113
+ if (attempt === 2) {
114
+ break;
115
+ }
116
+ }
112
117
  }
118
+ throw lastError;
113
119
  }
114
120
 
115
121
  function normalizeOwnerKey(userId) {
@@ -399,10 +399,6 @@ class RecordingManager {
399
399
  }
400
400
 
401
401
  async processSession(userId, sessionId) {
402
- if (!isDeepgramConfigured()) {
403
- throw new Error('DEEPGRAM_API_KEY is not configured.');
404
- }
405
-
406
402
  const session = db.prepare(`
407
403
  SELECT *
408
404
  FROM recording_sessions
@@ -438,6 +434,10 @@ class RecordingManager {
438
434
  let maxDuration = 0;
439
435
 
440
436
  try {
437
+ if (!isDeepgramConfigured()) {
438
+ throw new Error('DEEPGRAM_API_KEY is not configured.');
439
+ }
440
+
441
441
  for (const source of sources) {
442
442
  const sourceMetadata = this.#parseJson(source.metadata_json, {});
443
443
  const chunks = db.prepare(`