neoagent 2.1.17-beta.2 → 2.1.17-beta.3
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
package/server/http/routes.js
CHANGED
|
@@ -25,25 +25,21 @@ function registerApiRoutes(app) {
|
|
|
25
25
|
const handler = require(route.modulePath);
|
|
26
26
|
if (route.basePath) {
|
|
27
27
|
app.use(route.basePath, handler);
|
|
28
|
-
console.log(`[HTTP] Registered API route ${route.basePath} -> ${route.modulePath}`);
|
|
29
28
|
} else {
|
|
30
29
|
app.use(handler);
|
|
31
|
-
console.log(`[HTTP] Registered API route ${route.modulePath}`);
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
setupTelnyxWebhook(app);
|
|
36
|
-
console.log('[HTTP] Registered Telnyx webhook');
|
|
37
34
|
|
|
38
35
|
app.get('/api/health', requireAuth, (req, res) => {
|
|
39
36
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
40
37
|
});
|
|
41
|
-
console.log('[HTTP] Registered API route /api/health');
|
|
42
38
|
|
|
43
39
|
app.get('/api/version', requireAuth, (req, res) => {
|
|
44
40
|
res.json(getVersionInfo());
|
|
45
41
|
});
|
|
46
|
-
console.log(
|
|
42
|
+
console.log(`[HTTP] Registered ${routeRegistry.length + 3} routes`);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
module.exports = {
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"052f31d115eceda8cbff1b3481fcde4330c4ae
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "3731763910" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -154,6 +154,24 @@ function buildForcedFinalReplyPrompt(triggerSource) {
|
|
|
154
154
|
return 'Tool work is finished. Write the final user-facing reply now. Do not call tools.';
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
function buildBlankMessagingReplyPrompt(attempt) {
|
|
158
|
+
if (attempt <= 1) {
|
|
159
|
+
return 'You must produce one non-empty plain-text reply for the external messaging user right now. Do not call tools. Do not use markdown. Briefly summarize what you did or what blocked you.';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return 'Your previous reply was empty. Return one non-empty plain-text message now. Do not call tools. Do not use markdown. If needed, apologize briefly and explain the blocker in one or two sentences.';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildDeterministicMessagingFallback({ failedStepCount, stepIndex }) {
|
|
166
|
+
if (failedStepCount > 0) {
|
|
167
|
+
return 'I ran into an issue while working on that, so I do not have a clean final result yet. Ask me for a summary and I will explain what I found.';
|
|
168
|
+
}
|
|
169
|
+
if (stepIndex > 0) {
|
|
170
|
+
return 'I worked on that, but I could not produce a clean summary before finishing. Ask me for a summary and I will condense what I found into one message.';
|
|
171
|
+
}
|
|
172
|
+
return 'I could not generate a proper reply just now, but I am still here. Please send the request again and I will try once more.';
|
|
173
|
+
}
|
|
174
|
+
|
|
157
175
|
function clampRunContext(text, maxChars) {
|
|
158
176
|
const value = normalizeOutgoingMessage(text);
|
|
159
177
|
if (!value) return '';
|
|
@@ -208,6 +226,56 @@ class AgentEngine {
|
|
|
208
226
|
return buildSystemPrompt(userId, context, memoryManager);
|
|
209
227
|
}
|
|
210
228
|
|
|
229
|
+
async recoverBlankMessagingReply({
|
|
230
|
+
userId,
|
|
231
|
+
runId,
|
|
232
|
+
messages,
|
|
233
|
+
provider,
|
|
234
|
+
model,
|
|
235
|
+
providerName,
|
|
236
|
+
options,
|
|
237
|
+
stepIndex,
|
|
238
|
+
failedStepCount
|
|
239
|
+
}) {
|
|
240
|
+
const attempts = 2;
|
|
241
|
+
let recoveredContent = '';
|
|
242
|
+
let totalTokens = 0;
|
|
243
|
+
|
|
244
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
245
|
+
console.warn(
|
|
246
|
+
`[Run ${shortenRunId(runId)}] blank_reply_recovery attempt=${attempt} model=${model}`
|
|
247
|
+
);
|
|
248
|
+
const response = await provider.chat(
|
|
249
|
+
sanitizeConversationMessages([
|
|
250
|
+
...messages,
|
|
251
|
+
{
|
|
252
|
+
role: 'system',
|
|
253
|
+
content: buildBlankMessagingReplyPrompt(attempt)
|
|
254
|
+
}
|
|
255
|
+
]),
|
|
256
|
+
[],
|
|
257
|
+
{
|
|
258
|
+
model,
|
|
259
|
+
reasoningEffort: this.getReasoningEffort(providerName, options)
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
totalTokens += response.usage?.totalTokens || 0;
|
|
263
|
+
recoveredContent = sanitizeModelOutput(response.content || '', { model });
|
|
264
|
+
if (normalizeOutgoingMessage(recoveredContent)) {
|
|
265
|
+
console.info(
|
|
266
|
+
`[Run ${shortenRunId(runId)}] blank_reply_recovery succeeded attempt=${attempt}`
|
|
267
|
+
);
|
|
268
|
+
return { content: recoveredContent, tokens: totalTokens, recovered: true };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const fallback = buildDeterministicMessagingFallback({ failedStepCount, stepIndex });
|
|
273
|
+
console.warn(
|
|
274
|
+
`[Run ${shortenRunId(runId)}] blank_reply_recovery fallback=deterministic`
|
|
275
|
+
);
|
|
276
|
+
return { content: fallback, tokens: totalTokens, recovered: true };
|
|
277
|
+
}
|
|
278
|
+
|
|
211
279
|
getAvailableTools(app, options = {}) {
|
|
212
280
|
const { getAvailableTools } = require('./tools');
|
|
213
281
|
return getAvailableTools(app, options);
|
|
@@ -551,6 +619,7 @@ class AgentEngine {
|
|
|
551
619
|
let totalTokens = 0;
|
|
552
620
|
let lastContent = '';
|
|
553
621
|
let stepIndex = 0;
|
|
622
|
+
let failedStepCount = 0;
|
|
554
623
|
let promptMetrics = {};
|
|
555
624
|
|
|
556
625
|
try {
|
|
@@ -767,6 +836,7 @@ class AgentEngine {
|
|
|
767
836
|
);
|
|
768
837
|
} catch (err) {
|
|
769
838
|
toolResult = { error: err.message };
|
|
839
|
+
failedStepCount++;
|
|
770
840
|
this.detachProcessFromRun(runId, toolResult?.pid);
|
|
771
841
|
db.prepare('UPDATE agent_steps SET status = ?, error = ?, completed_at = datetime(\'now\') WHERE id = ?')
|
|
772
842
|
.run('failed', err.message, stepId);
|
|
@@ -849,6 +919,30 @@ class AgentEngine {
|
|
|
849
919
|
|
|
850
920
|
const runMeta = this.activeRuns.get(runId);
|
|
851
921
|
const messagingSent = runMeta?.messagingSent || false;
|
|
922
|
+
|
|
923
|
+
if (triggerSource === 'messaging' && !normalizeOutgoingMessage(lastContent) && !messagingSent) {
|
|
924
|
+
const recovered = await this.recoverBlankMessagingReply({
|
|
925
|
+
userId,
|
|
926
|
+
runId,
|
|
927
|
+
messages,
|
|
928
|
+
provider,
|
|
929
|
+
model,
|
|
930
|
+
providerName,
|
|
931
|
+
options,
|
|
932
|
+
stepIndex,
|
|
933
|
+
failedStepCount
|
|
934
|
+
});
|
|
935
|
+
lastContent = recovered.content;
|
|
936
|
+
totalTokens += recovered.tokens || 0;
|
|
937
|
+
if (normalizeOutgoingMessage(lastContent)) {
|
|
938
|
+
messages.push({ role: 'assistant', content: lastContent });
|
|
939
|
+
if (conversationId) {
|
|
940
|
+
db.prepare('INSERT INTO conversation_messages (conversation_id, role, content, tokens) VALUES (?, ?, ?, ?)')
|
|
941
|
+
.run(conversationId, 'assistant', lastContent, recovered.tokens || 0);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
852
946
|
const sentMessageText = joinSentMessages(runMeta?.sentMessages);
|
|
853
947
|
const finalResponseText = lastContent.trim() ? lastContent : sentMessageText;
|
|
854
948
|
const lastSentMessage = normalizeOutgoingMessage(
|