neoagent 3.2.1-beta.2 → 3.2.1-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/extensions/chrome-browser/background.mjs +318 -88
- package/extensions/chrome-browser/http.mjs +136 -0
- package/extensions/chrome-browser/protocol.mjs +511 -89
- package/flutter_app/lib/main_chat.dart +118 -739
- package/flutter_app/lib/main_controller.dart +29 -20
- package/flutter_app/lib/main_models.dart +3 -0
- package/flutter_app/lib/main_operations.dart +334 -321
- package/flutter_app/lib/main_settings.dart +4 -3
- package/flutter_app/lib/main_shared.dart +14 -11
- package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
- package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
- package/flutter_app/windows/runner/flutter_window.cpp +143 -32
- package/lib/manager.js +106 -89
- package/lib/schema_migrations.js +67 -13
- package/package.json +20 -13
- package/runtime/paths.js +49 -5
- package/server/guest_agent.js +52 -30
- package/server/http/middleware.js +24 -0
- package/server/http/routes.js +4 -6
- package/server/public/.last_build_id +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 +30803 -30805
- package/server/routes/admin.js +1 -1
- package/server/routes/android.js +30 -34
- package/server/routes/browser.js +23 -15
- package/server/routes/desktop.js +18 -1
- package/server/routes/integrations.js +5 -1
- package/server/routes/memory.js +1 -0
- package/server/routes/settings.js +16 -5
- package/server/routes/social_reach.js +12 -3
- package/server/routes/social_video.js +4 -0
- package/server/services/ai/compaction.js +7 -2
- package/server/services/ai/history.js +44 -5
- package/server/services/ai/integrated_tools/http_request.js +8 -0
- package/server/services/ai/loop/agent_engine_core.js +347 -162
- package/server/services/ai/loop/callbacks.js +1 -0
- package/server/services/ai/loop/completion_judge.js +12 -0
- package/server/services/ai/loop/conversation_loop.js +348 -242
- package/server/services/ai/loop/messaging_delivery.js +129 -57
- package/server/services/ai/loop/model_call_guard.js +91 -0
- package/server/services/ai/loop/model_io.js +8 -41
- package/server/services/ai/loop/tool_dispatch.js +19 -8
- package/server/services/ai/loopPolicy.js +24 -19
- package/server/services/ai/model_discovery.js +227 -0
- package/server/services/ai/model_identity.js +71 -0
- package/server/services/ai/models.js +67 -162
- package/server/services/ai/providerRetry.js +17 -59
- package/server/services/ai/provider_selector.js +111 -0
- package/server/services/ai/providers/anthropic.js +2 -2
- package/server/services/ai/providers/claudeCode.js +15 -28
- package/server/services/ai/providers/githubCopilot.js +36 -16
- package/server/services/ai/providers/google.js +23 -5
- package/server/services/ai/providers/grok.js +4 -3
- package/server/services/ai/providers/grokOauth.js +13 -22
- package/server/services/ai/providers/nvidia.js +10 -5
- package/server/services/ai/providers/ollama.js +102 -82
- package/server/services/ai/providers/ollama_stream.js +142 -0
- package/server/services/ai/providers/openai.js +39 -5
- package/server/services/ai/providers/openaiCodex.js +11 -4
- package/server/services/ai/providers/openrouter.js +29 -7
- package/server/services/ai/providers/provider_error.js +36 -0
- package/server/services/ai/settings.js +26 -2
- package/server/services/ai/taskAnalysis.js +5 -1
- package/server/services/ai/terminal_reply.js +45 -0
- package/server/services/ai/toolEvidence.js +58 -29
- package/server/services/ai/tools.js +124 -111
- package/server/services/android/controller.js +770 -237
- package/server/services/android/process.js +140 -0
- package/server/services/android/sdk_download.js +143 -0
- package/server/services/android/uia.js +6 -5
- package/server/services/artifacts/store.js +24 -0
- package/server/services/browser/controller.js +736 -385
- package/server/services/browser/extension/gateway.js +40 -16
- package/server/services/browser/extension/protocol.js +12 -1
- package/server/services/browser/extension/provider.js +59 -47
- package/server/services/browser/extension/registry.js +155 -34
- package/server/services/cli/executor.js +62 -9
- package/server/services/desktop/gateway.js +41 -4
- package/server/services/desktop/protocol.js +3 -0
- package/server/services/desktop/provider.js +39 -42
- package/server/services/desktop/registry.js +137 -52
- package/server/services/integrations/figma/provider.js +78 -12
- package/server/services/integrations/github/common.js +11 -6
- package/server/services/integrations/github/provider.js +52 -53
- package/server/services/integrations/google/provider.js +55 -19
- package/server/services/integrations/home_assistant/network.js +17 -20
- package/server/services/integrations/home_assistant/provider.js +7 -5
- package/server/services/integrations/home_assistant/tools.js +17 -5
- package/server/services/integrations/http.js +51 -0
- package/server/services/integrations/manager.js +158 -53
- package/server/services/integrations/microsoft/provider.js +80 -13
- package/server/services/integrations/neoarchive/provider.js +55 -29
- package/server/services/integrations/neorecall/client.js +17 -10
- package/server/services/integrations/neorecall/provider.js +20 -11
- package/server/services/integrations/notion/provider.js +16 -13
- package/server/services/integrations/oauth_provider.js +115 -51
- package/server/services/integrations/slack/provider.js +98 -9
- package/server/services/integrations/spotify/provider.js +67 -71
- package/server/services/integrations/trello/provider.js +21 -7
- package/server/services/integrations/weather/provider.js +18 -12
- package/server/services/integrations/whatsapp/provider.js +76 -16
- package/server/services/manager.js +87 -1
- package/server/services/memory/embedding_index.js +20 -8
- package/server/services/memory/embeddings.js +151 -90
- package/server/services/memory/ingestion.js +50 -9
- package/server/services/memory/ingestion_documents.js +13 -3
- package/server/services/memory/manager.js +52 -19
- package/server/services/messaging/automation.js +84 -9
- package/server/services/messaging/http_platforms.js +33 -13
- package/server/services/messaging/inbound_queue.js +78 -24
- package/server/services/messaging/inbound_store.js +224 -0
- package/server/services/messaging/manager.js +326 -51
- package/server/services/messaging/typing_keepalive.js +5 -2
- package/server/services/network/http.js +210 -0
- package/server/services/network/safe_request.js +307 -0
- package/server/services/runtime/backends/local-vm.js +214 -66
- package/server/services/runtime/manager.js +17 -12
- package/server/services/social_reach/channels/github.js +10 -4
- package/server/services/social_reach/channels/reddit.js +4 -4
- package/server/services/social_reach/channels/rss.js +2 -2
- package/server/services/social_reach/channels/social_video.js +12 -7
- package/server/services/social_reach/channels/v2ex.js +21 -8
- package/server/services/social_reach/channels/x.js +2 -2
- package/server/services/social_reach/channels/xueqiu.js +5 -5
- package/server/services/social_reach/service.js +9 -6
- package/server/services/social_reach/utils.js +65 -14
- package/server/services/social_video/service.js +160 -50
- package/server/services/tasks/integration_runtime.js +18 -8
- package/server/services/tasks/runtime.js +39 -4
- package/server/services/voice/agentBridge.js +17 -4
- package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
- package/server/services/voice/liveSession.js +31 -0
- package/server/services/voice/openaiSpeech.js +33 -8
- package/server/services/voice/providers.js +233 -151
- package/server/services/voice/runtimeManager.js +118 -20
- package/server/services/voice/turnRunner.js +6 -0
- package/server/services/wearable/firmware_manifest.js +51 -13
- package/server/services/wearable/service.js +1 -0
- package/server/utils/abort.js +96 -0
- package/server/utils/cloud-security.js +110 -3
- package/server/utils/files.js +31 -0
- package/server/utils/image_payload.js +95 -0
- package/server/utils/retry.js +107 -0
package/server/routes/admin.js
CHANGED
|
@@ -1025,7 +1025,7 @@ router.put('/api/config/rate-limits', requireAdminAuth, express.json(), (req, re
|
|
|
1025
1025
|
router.get('/api/models', requireAdminAuth, async (req, res) => {
|
|
1026
1026
|
const { getSupportedModels } = require('../services/ai/models');
|
|
1027
1027
|
try {
|
|
1028
|
-
const models = await getSupportedModels(null, null);
|
|
1028
|
+
const models = await getSupportedModels(null, null, { signal: req.signal });
|
|
1029
1029
|
const disabledStr = process.env.NEOAGENT_DISABLED_MODELS || '';
|
|
1030
1030
|
const disabledModels = disabledStr ? disabledStr.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
1031
1031
|
res.json({ models, disabledModels });
|
package/server/routes/android.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const express = require('express');
|
|
2
4
|
const fs = require('fs');
|
|
3
5
|
const path = require('path');
|
|
@@ -6,8 +8,7 @@ const router = express.Router();
|
|
|
6
8
|
const { DATA_DIR } = require('../../runtime/paths');
|
|
7
9
|
const { requireAuth } = require('../middleware/auth');
|
|
8
10
|
const { sanitizeError } = require('../utils/security');
|
|
9
|
-
const {
|
|
10
|
-
const { getRuntimeValidation } = require('../services/runtime/validation');
|
|
11
|
+
const { validateAndroidIntentUrl } = require('../utils/cloud-security');
|
|
11
12
|
|
|
12
13
|
router.use(requireAuth);
|
|
13
14
|
|
|
@@ -52,18 +53,6 @@ async function getAndroidController(req) {
|
|
|
52
53
|
throw new Error('Android controller is unavailable.');
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
function getAndroidStatusSnapshot(req) {
|
|
56
|
-
const runtimeValidation = getRuntimeValidation(req.app?.locals?.runtimeManager);
|
|
57
|
-
const ready = Boolean(runtimeValidation?.ready);
|
|
58
|
-
return {
|
|
59
|
-
bootstrapped: false,
|
|
60
|
-
canBootstrap: ready,
|
|
61
|
-
devices: [],
|
|
62
|
-
lastStartError: ready ? null : (runtimeValidation?.issues?.[0] || 'VM runtime is not ready.'),
|
|
63
|
-
runtimeReady: ready,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
56
|
function handleAndroidAction(action) {
|
|
68
57
|
return async (req, res) => {
|
|
69
58
|
try {
|
|
@@ -79,45 +68,49 @@ function handleAndroidAction(action) {
|
|
|
79
68
|
router.get('/status', async (req, res) => {
|
|
80
69
|
try {
|
|
81
70
|
const controller = await getAndroidController(req);
|
|
82
|
-
res.json(await controller.getStatus(
|
|
71
|
+
res.json(await controller.getStatus({ signal: req.signal }));
|
|
83
72
|
} catch (err) {
|
|
84
73
|
res.status(500).json({ error: sanitizeError(err) });
|
|
85
74
|
}
|
|
86
75
|
});
|
|
87
76
|
|
|
88
77
|
router.post('/start', handleAndroidAction((controller, req) =>
|
|
89
|
-
controller.requestStartEmulator(req.body || {})));
|
|
78
|
+
controller.requestStartEmulator({ ...(req.body || {}), signal: req.signal })));
|
|
90
79
|
|
|
91
|
-
router.post('/stop', handleAndroidAction((controller) => controller.stopEmulator()));
|
|
80
|
+
router.post('/stop', handleAndroidAction((controller, req) => controller.stopEmulator({ signal: req.signal })));
|
|
92
81
|
|
|
93
|
-
router.get('/devices', handleAndroidAction(async (controller) => ({
|
|
94
|
-
devices: await controller.listDevices(),
|
|
82
|
+
router.get('/devices', handleAndroidAction(async (controller, req) => ({
|
|
83
|
+
devices: await controller.listDevices({ signal: req.signal }),
|
|
95
84
|
})));
|
|
96
85
|
|
|
97
86
|
router.post('/screenshot', handleAndroidAction((controller, req) =>
|
|
98
|
-
controller.screenshot(req.body || {})));
|
|
87
|
+
controller.screenshot({ ...(req.body || {}), signal: req.signal })));
|
|
99
88
|
|
|
100
89
|
router.post('/observe', handleAndroidAction((controller, req) =>
|
|
101
|
-
controller.observe(req.body || {})));
|
|
90
|
+
controller.observe({ ...(req.body || {}), signal: req.signal })));
|
|
102
91
|
|
|
103
92
|
router.post('/ui-dump', handleAndroidAction((controller, req) =>
|
|
104
|
-
controller.dumpUi(req.body || {})));
|
|
93
|
+
controller.dumpUi({ ...(req.body || {}), signal: req.signal })));
|
|
105
94
|
|
|
106
95
|
router.get('/apps', handleAndroidAction((controller, req) =>
|
|
107
|
-
controller.listApps({ includeSystem: req.query.includeSystem === 'true' })));
|
|
96
|
+
controller.listApps({ includeSystem: req.query.includeSystem === 'true', signal: req.signal })));
|
|
108
97
|
|
|
109
98
|
router.post('/open-app', handleAndroidAction((controller, req) =>
|
|
110
|
-
controller.openApp(req.body || {})));
|
|
99
|
+
controller.openApp({ ...(req.body || {}), signal: req.signal })));
|
|
111
100
|
|
|
112
101
|
router.post('/open-intent', async (req, res) => {
|
|
113
102
|
try {
|
|
114
103
|
const body = req.body || {};
|
|
115
104
|
const intentUrl = body.data || body.url || body.uri;
|
|
116
|
-
if (
|
|
105
|
+
if (
|
|
106
|
+
intentUrl
|
|
107
|
+
&& typeof intentUrl === 'string'
|
|
108
|
+
&& !(await validateAndroidIntentUrl(intentUrl, { signal: req.signal })).allowed
|
|
109
|
+
) {
|
|
117
110
|
return res.status(403).json({ error: 'This URL is not permitted.' });
|
|
118
111
|
}
|
|
119
112
|
const controller = await getAndroidController(req);
|
|
120
|
-
const result = await controller.openIntent(body);
|
|
113
|
+
const result = await controller.openIntent({ ...body, signal: req.signal });
|
|
121
114
|
res.json(result);
|
|
122
115
|
} catch (err) {
|
|
123
116
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -125,22 +118,22 @@ router.post('/open-intent', async (req, res) => {
|
|
|
125
118
|
});
|
|
126
119
|
|
|
127
120
|
router.post('/tap', handleAndroidAction((controller, req) =>
|
|
128
|
-
controller.tap(req.body || {})));
|
|
121
|
+
controller.tap({ ...(req.body || {}), signal: req.signal })));
|
|
129
122
|
|
|
130
123
|
router.post('/long-press', handleAndroidAction((controller, req) =>
|
|
131
|
-
controller.longPress(req.body || {})));
|
|
124
|
+
controller.longPress({ ...(req.body || {}), signal: req.signal })));
|
|
132
125
|
|
|
133
126
|
router.post('/type', handleAndroidAction((controller, req) =>
|
|
134
|
-
controller.type(req.body || {})));
|
|
127
|
+
controller.type({ ...(req.body || {}), signal: req.signal })));
|
|
135
128
|
|
|
136
129
|
router.post('/swipe', handleAndroidAction((controller, req) =>
|
|
137
|
-
controller.swipe(req.body || {})));
|
|
130
|
+
controller.swipe({ ...(req.body || {}), signal: req.signal })));
|
|
138
131
|
|
|
139
132
|
router.post('/press-key', handleAndroidAction((controller, req) =>
|
|
140
|
-
controller.pressKey(req.body || {})));
|
|
133
|
+
controller.pressKey({ ...(req.body || {}), signal: req.signal })));
|
|
141
134
|
|
|
142
135
|
router.post('/wait-for', handleAndroidAction((controller, req) =>
|
|
143
|
-
controller.waitFor(req.body || {})));
|
|
136
|
+
controller.waitFor({ ...(req.body || {}), signal: req.signal })));
|
|
144
137
|
|
|
145
138
|
router.post('/install-apk', (req, res) => {
|
|
146
139
|
androidApkUpload.single('apk')(req, res, async (uploadError) => {
|
|
@@ -162,7 +155,7 @@ router.post('/install-apk', (req, res) => {
|
|
|
162
155
|
|
|
163
156
|
try {
|
|
164
157
|
const controller = await getAndroidController(req);
|
|
165
|
-
const result = await controller.installApk({ apkPath: uploadedApkPath });
|
|
158
|
+
const result = await controller.installApk({ apkPath: uploadedApkPath, signal: req.signal });
|
|
166
159
|
res.json({
|
|
167
160
|
...result,
|
|
168
161
|
filename: req.file.originalname,
|
|
@@ -176,6 +169,9 @@ router.post('/install-apk', (req, res) => {
|
|
|
176
169
|
});
|
|
177
170
|
});
|
|
178
171
|
|
|
179
|
-
router.post('/shell', handleAndroidAction((controller, req) => controller.shell(
|
|
172
|
+
router.post('/shell', handleAndroidAction((controller, req) => controller.shell({
|
|
173
|
+
...(req.body || {}),
|
|
174
|
+
signal: req.signal,
|
|
175
|
+
})));
|
|
180
176
|
|
|
181
177
|
module.exports = router;
|
package/server/routes/browser.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const express = require('express');
|
|
2
4
|
const router = express.Router();
|
|
3
5
|
const { requireAuth } = require('../middleware/auth');
|
|
4
6
|
const { sanitizeError } = require('../utils/security');
|
|
5
|
-
const {
|
|
7
|
+
const { validateCloudUrlWithDns } = require('../utils/cloud-security');
|
|
6
8
|
const { getRuntimeValidation } = require('../services/runtime/validation');
|
|
7
9
|
|
|
8
10
|
router.use(requireAuth);
|
|
@@ -11,7 +13,9 @@ async function getBrowserController(req) {
|
|
|
11
13
|
const runtimeManager = req.app?.locals?.runtimeManager;
|
|
12
14
|
if (runtimeManager && typeof runtimeManager.getBrowserProviderForUser === 'function') {
|
|
13
15
|
const start = Date.now();
|
|
14
|
-
const runtimeController = await runtimeManager.getBrowserProviderForUser(req.session?.userId
|
|
16
|
+
const runtimeController = await runtimeManager.getBrowserProviderForUser(req.session?.userId, {
|
|
17
|
+
signal: req.signal,
|
|
18
|
+
});
|
|
15
19
|
const duration = Date.now() - start;
|
|
16
20
|
if (duration > 1000) {
|
|
17
21
|
console.log(`[HTTP] Browser controller acquired for user ${req.session?.userId} in ${duration}ms`);
|
|
@@ -73,7 +77,7 @@ router.get('/cookies', async (req, res) => {
|
|
|
73
77
|
if (typeof bc.getCookies !== 'function') {
|
|
74
78
|
return res.status(501).json({ error: 'Cookie export is unavailable for this browser provider.' });
|
|
75
79
|
}
|
|
76
|
-
const result = await bc.getCookies();
|
|
80
|
+
const result = await bc.getCookies({ signal: req.signal });
|
|
77
81
|
res.json(result);
|
|
78
82
|
} catch (err) {
|
|
79
83
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -84,7 +88,7 @@ router.get('/cookies', async (req, res) => {
|
|
|
84
88
|
router.post('/launch', async (req, res) => {
|
|
85
89
|
try {
|
|
86
90
|
const bc = await getBrowserController(req);
|
|
87
|
-
await bc.launch(req.body || {});
|
|
91
|
+
await bc.launch({ ...(req.body || {}), signal: req.signal });
|
|
88
92
|
res.json({ success: true });
|
|
89
93
|
} catch (err) {
|
|
90
94
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -97,12 +101,12 @@ router.post('/navigate', async (req, res) => {
|
|
|
97
101
|
const { url, waitFor } = req.body;
|
|
98
102
|
if (!url) return res.status(400).json({ error: 'url required' });
|
|
99
103
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (bc.providerType === 'vm' && !validateCloudUrl(url).allowed) {
|
|
104
|
+
const urlValidation = await validateCloudUrlWithDns(url, { signal: req.signal });
|
|
105
|
+
if (!urlValidation.allowed) {
|
|
103
106
|
return res.status(403).json({ error: 'This URL is not permitted.' });
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
const bc = await getBrowserController(req);
|
|
106
110
|
const result = await bc.navigate(url, {
|
|
107
111
|
waitUntil: waitFor || req.body?.waitUntil || 'domcontentloaded',
|
|
108
112
|
waitFor,
|
|
@@ -110,6 +114,7 @@ router.post('/navigate', async (req, res) => {
|
|
|
110
114
|
fullPage: req.body?.fullPage === true,
|
|
111
115
|
referrerMode: req.body?.referrerMode,
|
|
112
116
|
challengeRetry: req.body?.challengeRetry,
|
|
117
|
+
signal: req.signal,
|
|
113
118
|
});
|
|
114
119
|
res.json(result);
|
|
115
120
|
} catch (err) {
|
|
@@ -121,7 +126,7 @@ router.post('/navigate', async (req, res) => {
|
|
|
121
126
|
router.post('/screenshot', async (req, res) => {
|
|
122
127
|
try {
|
|
123
128
|
const bc = await getBrowserController(req);
|
|
124
|
-
const result = await bc.screenshot(req.body || {});
|
|
129
|
+
const result = await bc.screenshot({ ...(req.body || {}), signal: req.signal });
|
|
125
130
|
res.json(result);
|
|
126
131
|
} catch (err) {
|
|
127
132
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -133,7 +138,7 @@ router.post('/click', async (req, res) => {
|
|
|
133
138
|
try {
|
|
134
139
|
const { selector, text } = req.body;
|
|
135
140
|
const bc = await getBrowserController(req);
|
|
136
|
-
const result = await bc.click(selector, text, req.body?.screenshot !== false);
|
|
141
|
+
const result = await bc.click(selector, text, req.body?.screenshot !== false, { signal: req.signal });
|
|
137
142
|
res.json(result);
|
|
138
143
|
} catch (err) {
|
|
139
144
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -147,7 +152,7 @@ router.post('/click-point', async (req, res) => {
|
|
|
147
152
|
return res.status(400).json({ error: 'x and y required' });
|
|
148
153
|
}
|
|
149
154
|
const bc = await getBrowserController(req);
|
|
150
|
-
const result = await bc.clickPoint(x, y, req.body?.screenshot !== false);
|
|
155
|
+
const result = await bc.clickPoint(x, y, req.body?.screenshot !== false, { signal: req.signal });
|
|
151
156
|
res.json(result);
|
|
152
157
|
} catch (err) {
|
|
153
158
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -161,7 +166,7 @@ router.post('/mouse-move', async (req, res) => {
|
|
|
161
166
|
return res.status(400).json({ error: 'x and y required' });
|
|
162
167
|
}
|
|
163
168
|
const bc = await getBrowserController(req);
|
|
164
|
-
const result = await bc.hoverPoint(x, y, req.body || {});
|
|
169
|
+
const result = await bc.hoverPoint(x, y, { ...(req.body || {}), signal: req.signal });
|
|
165
170
|
res.json(result);
|
|
166
171
|
} catch (err) {
|
|
167
172
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -179,6 +184,7 @@ router.post('/fill', async (req, res) => {
|
|
|
179
184
|
clear: req.body?.clear !== false,
|
|
180
185
|
pressEnter: req.body?.pressEnter === true,
|
|
181
186
|
screenshot: req.body?.screenshot !== false,
|
|
187
|
+
signal: req.signal,
|
|
182
188
|
});
|
|
183
189
|
res.json(result);
|
|
184
190
|
} catch (err) {
|
|
@@ -193,6 +199,7 @@ router.post('/type-text', async (req, res) => {
|
|
|
193
199
|
const result = await bc.typeText(String(text || ''), {
|
|
194
200
|
pressEnter: req.body?.pressEnter === true,
|
|
195
201
|
screenshot: req.body?.screenshot !== false,
|
|
202
|
+
signal: req.signal,
|
|
196
203
|
});
|
|
197
204
|
res.json(result);
|
|
198
205
|
} catch (err) {
|
|
@@ -205,7 +212,7 @@ router.post('/press-key', async (req, res) => {
|
|
|
205
212
|
const { key } = req.body || {};
|
|
206
213
|
if (!key) return res.status(400).json({ error: 'key required' });
|
|
207
214
|
const bc = await getBrowserController(req);
|
|
208
|
-
const result = await bc.pressKey(key, req.body?.screenshot !== false);
|
|
215
|
+
const result = await bc.pressKey(key, req.body?.screenshot !== false, { signal: req.signal });
|
|
209
216
|
res.json(result);
|
|
210
217
|
} catch (err) {
|
|
211
218
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -219,6 +226,7 @@ router.post('/scroll', async (req, res) => {
|
|
|
219
226
|
req.body?.deltaX ?? 0,
|
|
220
227
|
req.body?.deltaY ?? 0,
|
|
221
228
|
req.body?.screenshot !== false,
|
|
229
|
+
{ signal: req.signal },
|
|
222
230
|
);
|
|
223
231
|
res.json(result);
|
|
224
232
|
} catch (err) {
|
|
@@ -230,7 +238,7 @@ router.post('/scroll', async (req, res) => {
|
|
|
230
238
|
router.post('/extract', async (req, res) => {
|
|
231
239
|
try {
|
|
232
240
|
const bc = await getBrowserController(req);
|
|
233
|
-
const result = await bc.extractContent(req.body || {});
|
|
241
|
+
const result = await bc.extractContent({ ...(req.body || {}), signal: req.signal });
|
|
234
242
|
res.json(result);
|
|
235
243
|
} catch (err) {
|
|
236
244
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -252,7 +260,7 @@ router.post('/execute', async (req, res) => {
|
|
|
252
260
|
}
|
|
253
261
|
|
|
254
262
|
const bc = await getBrowserController(req);
|
|
255
|
-
const result = await bc.executeJS(code);
|
|
263
|
+
const result = await bc.executeJS(code, { signal: req.signal });
|
|
256
264
|
res.json({ result });
|
|
257
265
|
} catch (err) {
|
|
258
266
|
res.status(500).json({ error: sanitizeError(err) });
|
|
@@ -263,7 +271,7 @@ router.post('/execute', async (req, res) => {
|
|
|
263
271
|
router.post('/close', async (req, res) => {
|
|
264
272
|
try {
|
|
265
273
|
const bc = await getBrowserController(req);
|
|
266
|
-
await bc.closeBrowser();
|
|
274
|
+
await bc.closeBrowser({ signal: req.signal });
|
|
267
275
|
res.json({ success: true });
|
|
268
276
|
} catch (err) {
|
|
269
277
|
res.status(500).json({ error: sanitizeError(err) });
|
package/server/routes/desktop.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const express = require('express');
|
|
2
4
|
const router = express.Router();
|
|
3
5
|
const { requireAuth } = require('../middleware/auth');
|
|
@@ -144,6 +146,7 @@ router.post('/screenshot', (req, res) =>
|
|
|
144
146
|
handleDesktopAction(req, res, (provider, request) => provider.screenshot({
|
|
145
147
|
...(request.body || {}),
|
|
146
148
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
149
|
+
signal: request.signal,
|
|
147
150
|
})));
|
|
148
151
|
|
|
149
152
|
router.post('/observe', (req, res) =>
|
|
@@ -151,6 +154,7 @@ router.post('/observe', (req, res) =>
|
|
|
151
154
|
...(request.body || {}),
|
|
152
155
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
153
156
|
includeTree: parseOptionalBoolean(request.body?.includeTree, false),
|
|
157
|
+
signal: request.signal,
|
|
154
158
|
})));
|
|
155
159
|
|
|
156
160
|
router.post('/click', (req, res) =>
|
|
@@ -160,6 +164,7 @@ router.post('/click', (req, res) =>
|
|
|
160
164
|
{
|
|
161
165
|
...(request.body || {}),
|
|
162
166
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
167
|
+
signal: request.signal,
|
|
163
168
|
},
|
|
164
169
|
)));
|
|
165
170
|
|
|
@@ -170,6 +175,7 @@ router.post('/mouse-move', (req, res) =>
|
|
|
170
175
|
{
|
|
171
176
|
...(request.body || {}),
|
|
172
177
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
178
|
+
signal: request.signal,
|
|
173
179
|
},
|
|
174
180
|
)));
|
|
175
181
|
|
|
@@ -182,6 +188,7 @@ router.post('/drag', (req, res) =>
|
|
|
182
188
|
x2: parseFiniteNumber(request.body?.x2, 'x2', { min: -100000, max: 100000 }),
|
|
183
189
|
y2: parseFiniteNumber(request.body?.y2, 'y2', { min: -100000, max: 100000 }),
|
|
184
190
|
durationMs: Math.round(parseFiniteNumber(request.body?.durationMs ?? 280, 'durationMs', { min: 20, max: 5000 })),
|
|
191
|
+
signal: request.signal,
|
|
185
192
|
})));
|
|
186
193
|
|
|
187
194
|
router.post('/scroll', (req, res) =>
|
|
@@ -190,6 +197,7 @@ router.post('/scroll', (req, res) =>
|
|
|
190
197
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
191
198
|
deltaX: Math.round(parseFiniteNumber(request.body?.deltaX ?? 0, 'deltaX', { min: -5000, max: 5000 })),
|
|
192
199
|
deltaY: Math.round(parseFiniteNumber(request.body?.deltaY ?? 0, 'deltaY', { min: -5000, max: 5000 })),
|
|
200
|
+
signal: request.signal,
|
|
193
201
|
})));
|
|
194
202
|
|
|
195
203
|
router.post('/type-text', (req, res) =>
|
|
@@ -199,6 +207,7 @@ router.post('/type-text', (req, res) =>
|
|
|
199
207
|
...(request.body || {}),
|
|
200
208
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
201
209
|
pressEnter: parseOptionalBoolean(request.body?.pressEnter, false),
|
|
210
|
+
signal: request.signal,
|
|
202
211
|
},
|
|
203
212
|
)));
|
|
204
213
|
|
|
@@ -208,6 +217,7 @@ router.post('/press-key', (req, res) =>
|
|
|
208
217
|
{
|
|
209
218
|
...(request.body || {}),
|
|
210
219
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
220
|
+
signal: request.signal,
|
|
211
221
|
},
|
|
212
222
|
)));
|
|
213
223
|
|
|
@@ -216,11 +226,15 @@ router.post('/launch-app', (req, res) =>
|
|
|
216
226
|
...(request.body || {}),
|
|
217
227
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
218
228
|
app: parseRequiredString(request.body?.app, 'app', MAX_APP_LENGTH),
|
|
229
|
+
signal: request.signal,
|
|
219
230
|
})));
|
|
220
231
|
|
|
221
232
|
router.get('/displays', (req, res) =>
|
|
222
233
|
handleDesktopAction(req, res, (provider, request) =>
|
|
223
|
-
provider.listDisplays(
|
|
234
|
+
provider.listDisplays({
|
|
235
|
+
...parseTypedDisplayQuery(request.query),
|
|
236
|
+
signal: request.signal,
|
|
237
|
+
})));
|
|
224
238
|
|
|
225
239
|
router.post('/select-display', (req, res) =>
|
|
226
240
|
handleDesktopAction(req, res, (provider, request) => provider.selectDisplay(
|
|
@@ -228,6 +242,7 @@ router.post('/select-display', (req, res) =>
|
|
|
228
242
|
{
|
|
229
243
|
...(request.body || {}),
|
|
230
244
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
245
|
+
signal: request.signal,
|
|
231
246
|
},
|
|
232
247
|
)));
|
|
233
248
|
|
|
@@ -239,12 +254,14 @@ router.post('/pause-device', (req, res) =>
|
|
|
239
254
|
handleDesktopAction(req, res, (provider, request) => provider.pauseDevice(
|
|
240
255
|
parseRequiredString(request.body?.deviceId, 'deviceId', 256),
|
|
241
256
|
parseOptionalBoolean(request.body?.paused, true),
|
|
257
|
+
{ signal: request.signal },
|
|
242
258
|
)));
|
|
243
259
|
|
|
244
260
|
router.post('/tree', (req, res) =>
|
|
245
261
|
handleDesktopAction(req, res, (provider, request) => provider.getAccessibilityTree({
|
|
246
262
|
...(request.body || {}),
|
|
247
263
|
deviceId: parseDeviceId(request.body?.deviceId),
|
|
264
|
+
signal: request.signal,
|
|
248
265
|
})));
|
|
249
266
|
|
|
250
267
|
module.exports = router;
|
|
@@ -101,7 +101,7 @@ router.get('/oauth/callback', async (req, res) => {
|
|
|
101
101
|
if (!manager) {
|
|
102
102
|
throw new Error('Official integration manager is not available on app.locals.integrationManager.');
|
|
103
103
|
}
|
|
104
|
-
const result = await manager.finishOAuth(state, code);
|
|
104
|
+
const result = await manager.finishOAuth(state, code, { signal: req.signal });
|
|
105
105
|
const payload = JSON.stringify({
|
|
106
106
|
type: 'integration_oauth_success',
|
|
107
107
|
provider: result.provider,
|
|
@@ -320,6 +320,7 @@ router.post('/:provider/connect', async (req, res) => {
|
|
|
320
320
|
{
|
|
321
321
|
appKey: req.body?.appId,
|
|
322
322
|
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
323
|
+
signal: req.signal,
|
|
323
324
|
},
|
|
324
325
|
);
|
|
325
326
|
res.json(result);
|
|
@@ -340,6 +341,7 @@ router.post('/:provider/disconnect', async (req, res) => {
|
|
|
340
341
|
{
|
|
341
342
|
connectionId: req.body?.connectionId,
|
|
342
343
|
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
344
|
+
signal: req.signal,
|
|
343
345
|
},
|
|
344
346
|
);
|
|
345
347
|
res.json(result);
|
|
@@ -429,6 +431,7 @@ router.put('/:provider/config', async (req, res) => {
|
|
|
429
431
|
userId: req.session.userId,
|
|
430
432
|
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
431
433
|
config: req.body?.config || req.body || {},
|
|
434
|
+
signal: req.signal,
|
|
432
435
|
});
|
|
433
436
|
res.json({
|
|
434
437
|
provider: provider.key,
|
|
@@ -458,6 +461,7 @@ router.delete('/:provider/config', async (req, res) => {
|
|
|
458
461
|
await provider.clearUserConfig({
|
|
459
462
|
userId: req.session.userId,
|
|
460
463
|
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
464
|
+
signal: req.signal,
|
|
461
465
|
});
|
|
462
466
|
res.json({
|
|
463
467
|
provider: provider.key,
|
package/server/routes/memory.js
CHANGED
|
@@ -198,6 +198,7 @@ router.post('/ingestion/documents', async (req, res) => {
|
|
|
198
198
|
connectionId: req.body.connectionId,
|
|
199
199
|
sourceAccount: req.body.sourceAccount,
|
|
200
200
|
metadata: normalizeMetadata(req.body.metadata),
|
|
201
|
+
signal: req.signal,
|
|
201
202
|
});
|
|
202
203
|
res.json(result);
|
|
203
204
|
} catch (err) {
|
|
@@ -40,6 +40,14 @@ const AGENT_SETTING_KEYS = new Set([
|
|
|
40
40
|
'cost_mode',
|
|
41
41
|
'chat_history_window',
|
|
42
42
|
'tool_replay_budget_chars',
|
|
43
|
+
'tool_replay_budget_file_chars',
|
|
44
|
+
'tool_replay_budget_browser_chars',
|
|
45
|
+
'tool_replay_budget_command_chars',
|
|
46
|
+
'max_iterations',
|
|
47
|
+
'max_consecutive_read_only_iterations',
|
|
48
|
+
'max_consecutive_tool_failures',
|
|
49
|
+
'max_model_failure_recoveries',
|
|
50
|
+
'compaction_threshold',
|
|
43
51
|
'subagent_max_iterations',
|
|
44
52
|
'subagent_max_children_per_run',
|
|
45
53
|
'assistant_behavior_notes',
|
|
@@ -194,16 +202,19 @@ async function resetEnvBackedSettingValue(req, key) {
|
|
|
194
202
|
// Get supported models metadata
|
|
195
203
|
router.get('/meta/models', async (req, res) => {
|
|
196
204
|
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
197
|
-
const models = await getSupportedModels(req.session.userId, agentId);
|
|
205
|
+
const models = await getSupportedModels(req.session.userId, agentId, { signal: req.signal });
|
|
198
206
|
res.json({ models });
|
|
199
207
|
});
|
|
200
208
|
|
|
201
209
|
router.get('/meta/ai-providers', async (req, res) => {
|
|
202
210
|
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
const providers = await getProviderHealthCatalog(req.session.userId, agentId, {
|
|
212
|
+
signal: req.signal,
|
|
213
|
+
});
|
|
214
|
+
const models = await getSupportedModels(req.session.userId, agentId, {
|
|
215
|
+
providerCatalog: providers,
|
|
216
|
+
signal: req.signal,
|
|
217
|
+
});
|
|
207
218
|
|
|
208
219
|
const modelCounts = models.reduce((acc, model) => {
|
|
209
220
|
acc[model.provider] = (acc[model.provider] || 0) + 1;
|
|
@@ -25,7 +25,7 @@ router.get('/status', async (req, res) => {
|
|
|
25
25
|
if (!service || typeof service.getStatus !== 'function') {
|
|
26
26
|
return res.status(503).json({ error: 'Social reach service is unavailable.' });
|
|
27
27
|
}
|
|
28
|
-
return res.json(await service.getStatus(req.session.userId));
|
|
28
|
+
return res.json(await service.getStatus(req.session.userId, { signal: req.signal }));
|
|
29
29
|
} catch (error) {
|
|
30
30
|
return sendError(res, error);
|
|
31
31
|
}
|
|
@@ -37,7 +37,11 @@ router.post('/read', async (req, res) => {
|
|
|
37
37
|
if (!service || typeof service.read !== 'function') {
|
|
38
38
|
return res.status(503).json({ error: 'Social reach service is unavailable.' });
|
|
39
39
|
}
|
|
40
|
-
return res.json(await service.read(
|
|
40
|
+
return res.json(await service.read(
|
|
41
|
+
req.session.userId,
|
|
42
|
+
req.body || {},
|
|
43
|
+
{ signal: req.signal },
|
|
44
|
+
));
|
|
41
45
|
} catch (error) {
|
|
42
46
|
return sendError(res, error);
|
|
43
47
|
}
|
|
@@ -49,7 +53,11 @@ router.post('/search', async (req, res) => {
|
|
|
49
53
|
if (!service || typeof service.search !== 'function') {
|
|
50
54
|
return res.status(503).json({ error: 'Social reach service is unavailable.' });
|
|
51
55
|
}
|
|
52
|
-
return res.json(await service.search(
|
|
56
|
+
return res.json(await service.search(
|
|
57
|
+
req.session.userId,
|
|
58
|
+
req.body || {},
|
|
59
|
+
{ signal: req.signal },
|
|
60
|
+
));
|
|
53
61
|
} catch (error) {
|
|
54
62
|
return sendError(res, error);
|
|
55
63
|
}
|
|
@@ -67,6 +75,7 @@ router.post('/cookies/import', async (req, res) => {
|
|
|
67
75
|
}
|
|
68
76
|
return res.json(await service.importCookiesFromExtension(req.session.userId, platform, {
|
|
69
77
|
tokenId: req.body?.tokenId || req.body?.token_id || null,
|
|
78
|
+
signal: req.signal,
|
|
70
79
|
}));
|
|
71
80
|
} catch (error) {
|
|
72
81
|
return sendError(res, error);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const express = require('express');
|
|
2
4
|
const { requireAuth } = require('../middleware/auth');
|
|
3
5
|
const { getAgentIdFromRequest, resolveAgentId } = require('../services/agents/manager');
|
|
@@ -18,6 +20,7 @@ router.get('/health', async (req, res) => {
|
|
|
18
20
|
}
|
|
19
21
|
const health = await service.getHealthStatus({
|
|
20
22
|
forceRefresh: req.query?.refresh === '1' || req.query?.refresh === 'true',
|
|
23
|
+
signal: req.signal,
|
|
21
24
|
});
|
|
22
25
|
return res.json(health);
|
|
23
26
|
} catch (error) {
|
|
@@ -46,6 +49,7 @@ router.post('/extract', async (req, res) => {
|
|
|
46
49
|
includeFrame: req.body?.include_frame !== false,
|
|
47
50
|
forceStt: req.body?.force_stt === true,
|
|
48
51
|
agentId,
|
|
52
|
+
signal: req.signal,
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
if (result?.setup?.ready === false) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
async function compact(messages, provider, model, contextWindow = null) {
|
|
1
|
+
async function compact(messages, provider, model, contextWindow = null, options = {}) {
|
|
2
2
|
const systemMsg = messages.find(m => m.role === 'system');
|
|
3
3
|
const nonSystem = messages.filter(m => m.role !== 'system');
|
|
4
4
|
const beforeTokens = estimateTokenCount(messages);
|
|
@@ -26,7 +26,11 @@ async function compact(messages, provider, model, contextWindow = null) {
|
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
|
-
const response = await provider.chat(summaryPrompt, [], {
|
|
29
|
+
const response = await provider.chat(summaryPrompt, [], {
|
|
30
|
+
model,
|
|
31
|
+
maxTokens: 1600,
|
|
32
|
+
signal: options.signal,
|
|
33
|
+
});
|
|
30
34
|
const summary = response.content || 'Previous conversation context (summary unavailable).';
|
|
31
35
|
|
|
32
36
|
const compactedMessages = [];
|
|
@@ -44,6 +48,7 @@ async function compact(messages, provider, model, contextWindow = null) {
|
|
|
44
48
|
}
|
|
45
49
|
return compactedMessages;
|
|
46
50
|
} catch (err) {
|
|
51
|
+
if (options.signal?.aborted) throw err;
|
|
47
52
|
console.error('Compaction failed:', err.message);
|
|
48
53
|
const trimmed = [];
|
|
49
54
|
if (systemMsg) trimmed.push(systemMsg);
|