neoagent 2.2.1-beta.6 → 2.2.1-beta.8
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/docs/automation.md +2 -2
- package/docs/capabilities.md +7 -10
- package/docs/hardware.md +4 -7
- package/docs/index.md +7 -7
- package/docs/integrations.md +1 -1
- package/docs/migration.md +238 -0
- package/docs/operations.md +1 -1
- package/docs/why-neoagent.md +2 -2
- package/lib/manager.js +99 -2
- package/lib/migrations.js +409 -0
- package/package.json +1 -1
- package/server/catalog_sources/store-bundles/skills/productivity/migration/SKILL.md +173 -0
- package/server/db/database.js +76 -61
- package/server/http/routes.js +1 -2
- package/server/public/assets/AssetManifest.json +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +75830 -74189
- package/server/routes/auth.js +13 -5
- package/server/routes/integrations.js +22 -0
- package/server/routes/messaging.js +41 -5
- package/server/routes/settings.js +1 -0
- package/server/routes/{scheduler.js → tasks.js} +31 -29
- package/server/routes/widgets.js +7 -7
- package/server/services/ai/capabilityHealth.js +4 -4
- package/server/services/ai/engine.js +9 -9
- package/server/services/ai/systemPrompt.js +7 -7
- package/server/services/ai/taskAnalysis.js +3 -3
- package/server/services/ai/toolResult.js +6 -8
- package/server/services/ai/tools.js +62 -95
- package/server/services/commands/router.js +14 -6
- package/server/services/integrations/google/provider.js +20 -2
- package/server/services/integrations/manager.js +79 -8
- package/server/services/integrations/whatsapp/provider.js +23 -1
- package/server/services/manager.js +14 -14
- package/server/services/memory/manager.js +7 -7
- package/server/services/memory/policy.js +1 -1
- package/server/services/messaging/access_policy.js +703 -0
- package/server/services/messaging/access_policy.test.js +228 -0
- package/server/services/messaging/automation.js +32 -95
- package/server/services/messaging/base.js +39 -0
- package/server/services/messaging/discord.js +61 -46
- package/server/services/messaging/formatting_guides.js +0 -4
- package/server/services/messaging/http_platforms.js +178 -15
- package/server/services/messaging/manager.js +136 -71
- package/server/services/messaging/telegram.js +54 -40
- package/server/services/messaging/telnyx.js +43 -14
- package/server/services/messaging/whatsapp.js +27 -0
- package/server/services/tasks/adapters/gmail_message_received.js +36 -0
- package/server/services/tasks/adapters/index.js +10 -0
- package/server/services/tasks/adapters/outlook_email_received.js +38 -0
- package/server/services/tasks/adapters/schedule.js +57 -0
- package/server/services/tasks/adapters/slack_message_received.js +39 -0
- package/server/services/tasks/adapters/teams_message_received.js +39 -0
- package/server/services/tasks/adapters/whatsapp_personal_message_received.js +42 -0
- package/server/services/tasks/integration_runtime.js +260 -0
- package/server/services/tasks/runtime.js +539 -0
- package/server/services/{scheduler/cron_utils.js → tasks/schedule_utils.js} +2 -0
- package/server/services/tasks/security.js +60 -0
- package/server/services/tasks/task_repository.js +162 -0
- package/server/services/tasks/trigger_registry.js +29 -0
- package/server/services/tasks/utils.js +45 -0
- package/server/services/websocket.js +1 -1
- package/server/services/widgets/service.js +37 -25
- package/server/routes/wearable_device.js +0 -147
- package/server/services/messaging/waveshare_wearable.js +0 -40
- package/server/services/scheduler/cron.js +0 -580
- package/server/services/wearables/device_auth.js +0 -228
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { resolveAgentId } = require('../agents/manager');
|
|
4
|
+
const { normalizeJsonObject } = require('./utils');
|
|
5
|
+
|
|
6
|
+
const POLLED_TRIGGER_TYPES = Object.freeze([
|
|
7
|
+
'gmail_message_received',
|
|
8
|
+
'outlook_email_received',
|
|
9
|
+
'slack_message_received',
|
|
10
|
+
'teams_message_received',
|
|
11
|
+
'whatsapp_personal_message_received',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
function sortByTimestamp(left, right) {
|
|
15
|
+
return String(left.timestamp).localeCompare(String(right.timestamp));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function fetchTriggerRows({ integrationManager, userId, agentId, triggerType, config }) {
|
|
19
|
+
if (!integrationManager) return [];
|
|
20
|
+
const scopedAgentId = resolveAgentId(userId, agentId);
|
|
21
|
+
const connectionArg = {
|
|
22
|
+
connection_id: config.connectionId,
|
|
23
|
+
account_email: config.accountEmail || undefined,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (triggerType === 'gmail_message_received') {
|
|
27
|
+
const queryParts = [];
|
|
28
|
+
if (config.query) queryParts.push(config.query);
|
|
29
|
+
if (config.unreadOnly) queryParts.push('is:unread');
|
|
30
|
+
const result = await integrationManager.executeTool(userId, 'google_workspace_gmail_api_request', {
|
|
31
|
+
...connectionArg,
|
|
32
|
+
method: 'GET',
|
|
33
|
+
path: '/gmail/v1/users/me/messages',
|
|
34
|
+
query: {
|
|
35
|
+
maxResults: 20,
|
|
36
|
+
q: queryParts.join(' ').trim() || undefined,
|
|
37
|
+
},
|
|
38
|
+
}, scopedAgentId);
|
|
39
|
+
const messages = Array.isArray(result?.messages) ? result.messages : [];
|
|
40
|
+
return messages
|
|
41
|
+
.map((item) => ({
|
|
42
|
+
fingerprint: `gmail:${config.connectionId}:${item.id}`,
|
|
43
|
+
timestamp: new Date().toISOString(),
|
|
44
|
+
context: { triggerEvent: { provider: 'gmail', messageId: item.id, threadId: item.threadId || null } },
|
|
45
|
+
}))
|
|
46
|
+
.sort((left, right) => left.fingerprint.localeCompare(right.fingerprint));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (triggerType === 'outlook_email_received') {
|
|
50
|
+
const filters = [];
|
|
51
|
+
const escapedQuery = String(config.query || '').replace(/"/g, '\\"');
|
|
52
|
+
if (config.unreadOnly) filters.push('isRead eq false');
|
|
53
|
+
const result = await integrationManager.executeTool(userId, 'microsoft_365_outlook_graph_request', {
|
|
54
|
+
...connectionArg,
|
|
55
|
+
method: 'GET',
|
|
56
|
+
path: config.folderId
|
|
57
|
+
? `/v1.0/me/mailFolders/${encodeURIComponent(config.folderId)}/messages`
|
|
58
|
+
: '/v1.0/me/messages',
|
|
59
|
+
query: {
|
|
60
|
+
'$top': 20,
|
|
61
|
+
...(filters.length ? { '$filter': filters.join(' and ') } : {}),
|
|
62
|
+
...(escapedQuery ? { '$search': `"${escapedQuery}"` } : {}),
|
|
63
|
+
},
|
|
64
|
+
}, scopedAgentId);
|
|
65
|
+
const messages = Array.isArray(result?.value) ? result.value : [];
|
|
66
|
+
return messages
|
|
67
|
+
.map((item) => ({
|
|
68
|
+
fingerprint: `outlook:${config.connectionId}:${item.id}`,
|
|
69
|
+
timestamp: item.receivedDateTime || new Date().toISOString(),
|
|
70
|
+
context: {
|
|
71
|
+
triggerEvent: {
|
|
72
|
+
provider: 'outlook',
|
|
73
|
+
messageId: item.id,
|
|
74
|
+
subject: item.subject || '',
|
|
75
|
+
from: item.from?.emailAddress?.address || null,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
}))
|
|
79
|
+
.sort(sortByTimestamp);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (triggerType === 'slack_message_received') {
|
|
83
|
+
const result = await integrationManager.executeTool(userId, 'slack_get_conversation_history', {
|
|
84
|
+
...connectionArg,
|
|
85
|
+
channel: config.channel,
|
|
86
|
+
limit: 20,
|
|
87
|
+
}, scopedAgentId);
|
|
88
|
+
const messages = Array.isArray(result?.result?.messages)
|
|
89
|
+
? result.result.messages
|
|
90
|
+
: Array.isArray(result?.messages)
|
|
91
|
+
? result.messages
|
|
92
|
+
: [];
|
|
93
|
+
return messages
|
|
94
|
+
.filter((item) => !config.sender || String(item.user || '') === String(config.sender))
|
|
95
|
+
.map((item) => ({
|
|
96
|
+
fingerprint: `slack:${config.connectionId}:${config.channel}:${item.ts}`,
|
|
97
|
+
timestamp: item.ts || new Date().toISOString(),
|
|
98
|
+
context: {
|
|
99
|
+
triggerEvent: {
|
|
100
|
+
provider: 'slack',
|
|
101
|
+
channel: config.channel,
|
|
102
|
+
sender: item.user || null,
|
|
103
|
+
messageId: item.client_msg_id || item.ts,
|
|
104
|
+
content: item.text || '',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}))
|
|
108
|
+
.sort(sortByTimestamp);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (triggerType === 'teams_message_received') {
|
|
112
|
+
const result = await integrationManager.executeTool(userId, 'microsoft_365_teams_graph_request', {
|
|
113
|
+
...connectionArg,
|
|
114
|
+
method: 'GET',
|
|
115
|
+
path: `/v1.0/me/chats/${encodeURIComponent(config.chatId)}/messages`,
|
|
116
|
+
query: { '$top': 20 },
|
|
117
|
+
}, scopedAgentId);
|
|
118
|
+
const messages = Array.isArray(result?.value) ? result.value : [];
|
|
119
|
+
return messages
|
|
120
|
+
.filter((item) => {
|
|
121
|
+
const sender = item.from?.user?.id || item.from?.application?.id || '';
|
|
122
|
+
return !config.sender || String(sender) === String(config.sender);
|
|
123
|
+
})
|
|
124
|
+
.map((item) => ({
|
|
125
|
+
fingerprint: `teams:${config.connectionId}:${config.chatId}:${item.id}`,
|
|
126
|
+
timestamp: item.createdDateTime || new Date().toISOString(),
|
|
127
|
+
context: {
|
|
128
|
+
triggerEvent: {
|
|
129
|
+
provider: 'teams',
|
|
130
|
+
chatId: config.chatId,
|
|
131
|
+
messageId: item.id,
|
|
132
|
+
sender: item.from?.user?.id || null,
|
|
133
|
+
content: item.body?.content || '',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}))
|
|
137
|
+
.sort(sortByTimestamp);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (triggerType === 'whatsapp_personal_message_received') {
|
|
141
|
+
const result = await integrationManager.executeTool(userId, 'whatsapp_personal_get_messages', {
|
|
142
|
+
...connectionArg,
|
|
143
|
+
chat_id: config.chatId,
|
|
144
|
+
limit: 25,
|
|
145
|
+
}, scopedAgentId);
|
|
146
|
+
const messages = Array.isArray(result?.messages) ? result.messages : [];
|
|
147
|
+
return messages
|
|
148
|
+
.filter((item) => item && item.fromMe !== true)
|
|
149
|
+
.filter((item) => !config.sender || String(item.senderTag || item.sender || '') === String(config.sender))
|
|
150
|
+
.filter((item) => !(config.ignoreGroups && String(item.chatId || '').endsWith('@g.us')))
|
|
151
|
+
.map((item) => ({
|
|
152
|
+
fingerprint: `whatsapp:${config.connectionId}:${item.id}`,
|
|
153
|
+
timestamp: item.timestamp || new Date().toISOString(),
|
|
154
|
+
context: {
|
|
155
|
+
triggerEvent: {
|
|
156
|
+
provider: 'whatsapp_personal',
|
|
157
|
+
chatId: item.chatId,
|
|
158
|
+
messageId: item.id,
|
|
159
|
+
sender: item.sender || null,
|
|
160
|
+
senderTag: item.senderTag || null,
|
|
161
|
+
content: item.text || '',
|
|
162
|
+
isGroup: String(item.chatId || '').endsWith('@g.us'),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
}))
|
|
166
|
+
.sort(sortByTimestamp);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function pollIntegrationTask(runtime, task) {
|
|
173
|
+
const config = normalizeJsonObject(task.trigger_config);
|
|
174
|
+
const rows = await fetchTriggerRows({
|
|
175
|
+
integrationManager: runtime.integrationManager,
|
|
176
|
+
userId: task.user_id,
|
|
177
|
+
agentId: task.agent_id,
|
|
178
|
+
triggerType: task.trigger_type,
|
|
179
|
+
config,
|
|
180
|
+
});
|
|
181
|
+
if (!rows.length) return;
|
|
182
|
+
|
|
183
|
+
const existingFingerprint = String(task.last_trigger_fingerprint || '');
|
|
184
|
+
const latestFingerprint = rows[rows.length - 1].fingerprint;
|
|
185
|
+
if (!existingFingerprint) {
|
|
186
|
+
runtime.taskRepository.markTaskTriggerCheckpoint(task.id, latestFingerprint);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const startIndex = rows.findIndex((row) => row.fingerprint === existingFingerprint);
|
|
191
|
+
const pending = startIndex >= 0 ? rows.slice(startIndex + 1) : rows.slice(-1);
|
|
192
|
+
for (const row of pending) {
|
|
193
|
+
await runtime.fireTaskFromTrigger(task.id, task.user_id, row);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function createWhatsappTriggerPayload(event) {
|
|
198
|
+
return {
|
|
199
|
+
fingerprint: `whatsapp:${event.connectionId}:${event.messageId}`,
|
|
200
|
+
timestamp: event.timestamp,
|
|
201
|
+
context: {
|
|
202
|
+
triggerEvent: {
|
|
203
|
+
provider: 'whatsapp_personal',
|
|
204
|
+
chatId: event.chatId,
|
|
205
|
+
sender: event.sender,
|
|
206
|
+
senderTag: event.senderTag || null,
|
|
207
|
+
messageId: event.messageId,
|
|
208
|
+
content: event.text || '',
|
|
209
|
+
isGroup: event.isGroup === true,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function matchesWhatsappTaskEvent(task, event) {
|
|
216
|
+
const config = normalizeJsonObject(task.trigger_config);
|
|
217
|
+
if (String(config.connectionId || '') !== String(event.connectionId || '')) return false;
|
|
218
|
+
if (config.chatId && String(config.chatId) !== String(event.chatId)) return false;
|
|
219
|
+
if (config.sender && String(config.sender) !== String(event.senderTag || event.sender || '')) return false;
|
|
220
|
+
if (config.ignoreGroups && event.isGroup) return false;
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function attachIntegrationEventSources(runtime) {
|
|
225
|
+
const cleanups = [];
|
|
226
|
+
const provider = runtime.integrationManager?.getProvider?.('whatsapp_personal');
|
|
227
|
+
if (provider && typeof provider.on === 'function') {
|
|
228
|
+
const listener = async (event) => {
|
|
229
|
+
const tasks = runtime.taskRepository.listEnabledWhatsappEventTasks(event.userId, event.agentId);
|
|
230
|
+
for (const task of tasks) {
|
|
231
|
+
if (!matchesWhatsappTaskEvent(task, event)) continue;
|
|
232
|
+
await runtime.fireTaskFromTrigger(task.id, task.user_id, createWhatsappTriggerPayload(event)).catch((error) => {
|
|
233
|
+
const logger = runtime.logger?.error || console.error;
|
|
234
|
+
logger('[Tasks] Failed to fire WhatsApp task trigger', {
|
|
235
|
+
taskId: task.id,
|
|
236
|
+
userId: task.user_id,
|
|
237
|
+
agentId: event.agentId,
|
|
238
|
+
error: error?.message || String(error),
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
provider.on('message', listener);
|
|
244
|
+
cleanups.push(() => {
|
|
245
|
+
if (typeof provider.off === 'function') {
|
|
246
|
+
provider.off('message', listener);
|
|
247
|
+
} else if (typeof provider.removeListener === 'function') {
|
|
248
|
+
provider.removeListener('message', listener);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return cleanups;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = {
|
|
256
|
+
POLLED_TRIGGER_TYPES,
|
|
257
|
+
attachIntegrationEventSources,
|
|
258
|
+
fetchTriggerRows,
|
|
259
|
+
pollIntegrationTask,
|
|
260
|
+
};
|