neoagent 3.2.1-beta.1 → 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 +358 -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 +20 -45
- 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 +21 -33
- package/server/services/ai/providers/githubCopilot.js +41 -20
- 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 +19 -27
- package/server/services/ai/providers/nvidia.js +10 -5
- package/server/services/ai/providers/ollama.js +111 -84
- 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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { analyzeImageForUser } = require('./imageAnalysis');
|
|
4
|
-
const {
|
|
4
|
+
const { validateCloudUrlWithDns } = require('../../utils/cloud-security');
|
|
5
|
+
const { isAbortError } = require('../../utils/abort');
|
|
6
|
+
const { fetchResponseText } = require('../network/http');
|
|
5
7
|
const db = require('../../db/database');
|
|
6
8
|
const { DATA_DIR } = require('../../../runtime/paths');
|
|
7
9
|
const { isMainAgent } = require('../agents/manager');
|
|
@@ -10,11 +12,13 @@ const {
|
|
|
10
12
|
normalizeOutgoingMessageForPlatform,
|
|
11
13
|
} = require('../messaging/formatting_guides');
|
|
12
14
|
const { INTERIM_KINDS, normalizeInterimKind } = require('./interim');
|
|
15
|
+
const { isDeferredWorkReply } = require('./terminal_reply');
|
|
13
16
|
const { normalizeWhatsAppId } = require('../../utils/whatsapp');
|
|
14
17
|
const {
|
|
15
18
|
executeIntegratedTool,
|
|
16
19
|
getIntegratedToolDefinitions,
|
|
17
20
|
} = require('./integrated_tools');
|
|
21
|
+
const { executeHttpRequest } = require('./integrated_tools/http_request');
|
|
18
22
|
|
|
19
23
|
function compactText(text, maxChars = 120) {
|
|
20
24
|
const str = String(text || '').replace(/\s+/g, ' ').trim();
|
|
@@ -34,6 +38,14 @@ function compactToolDefinition(tool, options = {}) {
|
|
|
34
38
|
}
|
|
35
39
|
};
|
|
36
40
|
|
|
41
|
+
// Keep execution-only access metadata on the internal tool definition. The
|
|
42
|
+
// provider adapters serialize only name/description/parameters, so this is
|
|
43
|
+
// never sent as part of an LLM tool schema. The loop uses it to distinguish
|
|
44
|
+
// official-integration reads from writes without hard-coding provider names.
|
|
45
|
+
if (typeof tool.access === 'string' && tool.access.trim()) {
|
|
46
|
+
compact.access = tool.access.trim().toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
if (options.includeDescriptions) {
|
|
38
50
|
compact.description = compactText(tool.description, 320);
|
|
39
51
|
}
|
|
@@ -878,7 +890,7 @@ function getAvailableTools(app, options = {}) {
|
|
|
878
890
|
},
|
|
879
891
|
{
|
|
880
892
|
name: 'send_message',
|
|
881
|
-
description: `Send a message on a connected messaging platform. Supports WhatsApp (text/media), Telnyx Voice (phone calls — TTS), Discord, Telegram, Slack, Google Chat, Microsoft Teams, Matrix, Signal, iMessage/BlueBubbles, IRC, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, WeChat, WebChat, and configurable webhook bridges. ${buildSendMessageFormattingReference()} For WhatsApp: use media_path to attach files. Use content "[NO RESPONSE]" only when the user explicitly asked for silence/no reply, or when a background task intentionally decides no user-visible update is needed with purpose="no_response". For background task or schedule runs, set purpose to final_result, blocker, or no_response.`,
|
|
893
|
+
description: `Send a final message on a connected messaging platform. Use send_interim_update, not this tool, for an ongoing status reply to the originating chat. Supports WhatsApp (text/media), Telnyx Voice (phone calls — TTS), Discord, Telegram, Slack, Google Chat, Microsoft Teams, Matrix, Signal, iMessage/BlueBubbles, IRC, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, WeChat, WebChat, and configurable webhook bridges. ${buildSendMessageFormattingReference()} For WhatsApp: use media_path to attach files. Use content "[NO RESPONSE]" only when the user explicitly asked for silence/no reply, or when a background task intentionally decides no user-visible update is needed with purpose="no_response". For background task or schedule runs, set purpose to final_result, blocker, or no_response.`,
|
|
882
894
|
parameters: {
|
|
883
895
|
type: 'object',
|
|
884
896
|
properties: {
|
|
@@ -1632,7 +1644,8 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1632
1644
|
taskId,
|
|
1633
1645
|
widgetId,
|
|
1634
1646
|
deliveryState = null,
|
|
1635
|
-
allowMultipleProactiveMessages = false
|
|
1647
|
+
allowMultipleProactiveMessages = false,
|
|
1648
|
+
signal = null,
|
|
1636
1649
|
} = context;
|
|
1637
1650
|
const runtime = () => app?.locals?.runtimeManager || engine.runtimeManager || null;
|
|
1638
1651
|
const bc = async () => {
|
|
@@ -1641,7 +1654,10 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1641
1654
|
const backend = typeof manager.getActiveBrowserBackend === 'function'
|
|
1642
1655
|
? await Promise.resolve(manager.getActiveBrowserBackend(userId))
|
|
1643
1656
|
: 'vm';
|
|
1644
|
-
return {
|
|
1657
|
+
return {
|
|
1658
|
+
provider: await manager.getBrowserProviderForUser(userId, { signal }),
|
|
1659
|
+
backend,
|
|
1660
|
+
};
|
|
1645
1661
|
}
|
|
1646
1662
|
throw new Error('Browser provider is unavailable. VM runtime is required.');
|
|
1647
1663
|
};
|
|
@@ -1672,7 +1688,13 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1672
1688
|
|
|
1673
1689
|
const integrationManager = integrations();
|
|
1674
1690
|
if (integrationManager) {
|
|
1675
|
-
const integrationResult = await integrationManager.executeTool(
|
|
1691
|
+
const integrationResult = await integrationManager.executeTool(
|
|
1692
|
+
userId,
|
|
1693
|
+
toolName,
|
|
1694
|
+
args,
|
|
1695
|
+
agentId,
|
|
1696
|
+
{ signal },
|
|
1697
|
+
);
|
|
1676
1698
|
if (
|
|
1677
1699
|
integrationResult &&
|
|
1678
1700
|
typeof integrationResult === 'object' &&
|
|
@@ -1724,6 +1746,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1724
1746
|
stdinInput: args.stdin_input,
|
|
1725
1747
|
pty: args.pty === true,
|
|
1726
1748
|
inputs: Array.isArray(args.inputs) ? args.inputs : [],
|
|
1749
|
+
signal,
|
|
1727
1750
|
};
|
|
1728
1751
|
if (typeof runtimeManager.executeCliCommand === 'function') {
|
|
1729
1752
|
return await runtimeManager.executeCliCommand(userId, args.command, execOptions);
|
|
@@ -1736,7 +1759,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1736
1759
|
}
|
|
1737
1760
|
|
|
1738
1761
|
case 'browser_navigate': {
|
|
1739
|
-
const urlCheck =
|
|
1762
|
+
const urlCheck = await validateCloudUrlWithDns(args.url, { signal });
|
|
1740
1763
|
if (!urlCheck.allowed) return { error: 'URL is not allowed: blocked scheme or private/internal network address.' };
|
|
1741
1764
|
const { provider, backend } = await bc();
|
|
1742
1765
|
if (!provider) return { error: 'Browser controller not available' };
|
|
@@ -1745,14 +1768,15 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1745
1768
|
waitFor: args.waitFor,
|
|
1746
1769
|
fullPage: args.fullPage,
|
|
1747
1770
|
referrerMode: args.referrerMode,
|
|
1748
|
-
challengeRetry: args.challengeRetry
|
|
1771
|
+
challengeRetry: args.challengeRetry,
|
|
1772
|
+
signal,
|
|
1749
1773
|
}), backend };
|
|
1750
1774
|
}
|
|
1751
1775
|
|
|
1752
1776
|
case 'browser_click': {
|
|
1753
1777
|
const { provider, backend } = await bc();
|
|
1754
1778
|
if (!provider) return { error: 'Browser controller not available' };
|
|
1755
|
-
return { ...await provider.click(args.selector, args.text, args.screenshot !== false), backend };
|
|
1779
|
+
return { ...await provider.click(args.selector, args.text, args.screenshot !== false, { signal }), backend };
|
|
1756
1780
|
}
|
|
1757
1781
|
|
|
1758
1782
|
case 'browser_type': {
|
|
@@ -1760,20 +1784,21 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1760
1784
|
if (!provider) return { error: 'Browser controller not available' };
|
|
1761
1785
|
return { ...await provider.type(args.selector, args.text, {
|
|
1762
1786
|
clear: args.clear !== false,
|
|
1763
|
-
pressEnter: args.pressEnter
|
|
1787
|
+
pressEnter: args.pressEnter,
|
|
1788
|
+
signal,
|
|
1764
1789
|
}), backend };
|
|
1765
1790
|
}
|
|
1766
1791
|
|
|
1767
1792
|
case 'browser_extract': {
|
|
1768
1793
|
const { provider, backend } = await bc();
|
|
1769
1794
|
if (!provider) return { error: 'Browser controller not available' };
|
|
1770
|
-
return { ...await provider.extract(args.selector, args.attribute, args.all), backend };
|
|
1795
|
+
return { ...await provider.extract(args.selector, args.attribute, args.all, { signal }), backend };
|
|
1771
1796
|
}
|
|
1772
1797
|
|
|
1773
1798
|
case 'browser_screenshot': {
|
|
1774
1799
|
const { provider, backend } = await bc();
|
|
1775
1800
|
if (!provider) return { error: 'Browser controller not available' };
|
|
1776
|
-
return { ...await provider.screenshot({ fullPage: args.fullPage, selector: args.selector }), backend };
|
|
1801
|
+
return { ...await provider.screenshot({ fullPage: args.fullPage, selector: args.selector, signal }), backend };
|
|
1777
1802
|
}
|
|
1778
1803
|
|
|
1779
1804
|
case 'browser_evaluate': {
|
|
@@ -1781,13 +1806,13 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1781
1806
|
if (!provider) return { error: 'Browser controller not available' };
|
|
1782
1807
|
const script = args.script ?? args.javascript;
|
|
1783
1808
|
if (!script) return { error: 'browser_evaluate requires a "script" argument' };
|
|
1784
|
-
return { ...await provider.evaluate(script), backend };
|
|
1809
|
+
return { ...await provider.evaluate(script, { signal }), backend };
|
|
1785
1810
|
}
|
|
1786
1811
|
|
|
1787
1812
|
case 'android_start_emulator': {
|
|
1788
1813
|
const controller = await ac();
|
|
1789
1814
|
if (!controller) return { error: 'Android controller not available' };
|
|
1790
|
-
return await controller.startEmulator(args || {});
|
|
1815
|
+
return await controller.startEmulator({ ...(args || {}), signal });
|
|
1791
1816
|
}
|
|
1792
1817
|
|
|
1793
1818
|
case 'desktop_list_devices': {
|
|
@@ -1814,6 +1839,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1814
1839
|
return await controller.observe({
|
|
1815
1840
|
deviceId: args.device_id,
|
|
1816
1841
|
includeTree: args.includeTree === true,
|
|
1842
|
+
signal,
|
|
1817
1843
|
});
|
|
1818
1844
|
}
|
|
1819
1845
|
|
|
@@ -1823,6 +1849,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1823
1849
|
return await controller.clickPoint(args.x, args.y, {
|
|
1824
1850
|
deviceId: args.device_id,
|
|
1825
1851
|
button: args.button,
|
|
1852
|
+
signal,
|
|
1826
1853
|
});
|
|
1827
1854
|
}
|
|
1828
1855
|
|
|
@@ -1836,6 +1863,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1836
1863
|
x2: args.x2,
|
|
1837
1864
|
y2: args.y2,
|
|
1838
1865
|
durationMs: args.durationMs,
|
|
1866
|
+
signal,
|
|
1839
1867
|
});
|
|
1840
1868
|
}
|
|
1841
1869
|
|
|
@@ -1846,6 +1874,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1846
1874
|
deviceId: args.device_id,
|
|
1847
1875
|
deltaX: args.deltaX,
|
|
1848
1876
|
deltaY: args.deltaY,
|
|
1877
|
+
signal,
|
|
1849
1878
|
});
|
|
1850
1879
|
}
|
|
1851
1880
|
|
|
@@ -1855,6 +1884,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1855
1884
|
return await controller.typeText(args.text, {
|
|
1856
1885
|
deviceId: args.device_id,
|
|
1857
1886
|
pressEnter: args.pressEnter === true,
|
|
1887
|
+
signal,
|
|
1858
1888
|
});
|
|
1859
1889
|
}
|
|
1860
1890
|
|
|
@@ -1863,6 +1893,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1863
1893
|
if (!controller) return { error: 'Desktop provider not available' };
|
|
1864
1894
|
return await controller.pressKey(args.key, {
|
|
1865
1895
|
deviceId: args.device_id,
|
|
1896
|
+
signal,
|
|
1866
1897
|
});
|
|
1867
1898
|
}
|
|
1868
1899
|
|
|
@@ -1872,6 +1903,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1872
1903
|
return await controller.launchApp({
|
|
1873
1904
|
deviceId: args.device_id,
|
|
1874
1905
|
app: args.app,
|
|
1906
|
+
signal,
|
|
1875
1907
|
});
|
|
1876
1908
|
}
|
|
1877
1909
|
|
|
@@ -1880,112 +1912,111 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
1880
1912
|
if (!controller) return { error: 'Desktop provider not available' };
|
|
1881
1913
|
return await controller.getAccessibilityTree({
|
|
1882
1914
|
deviceId: args.device_id,
|
|
1915
|
+
signal,
|
|
1883
1916
|
});
|
|
1884
1917
|
}
|
|
1885
1918
|
|
|
1886
1919
|
case 'android_stop_emulator': {
|
|
1887
1920
|
const controller = await ac();
|
|
1888
1921
|
if (!controller) return { error: 'Android controller not available' };
|
|
1889
|
-
return await controller.stopEmulator();
|
|
1922
|
+
return await controller.stopEmulator({ signal });
|
|
1890
1923
|
}
|
|
1891
1924
|
|
|
1892
1925
|
case 'android_list_devices': {
|
|
1893
1926
|
const controller = await ac();
|
|
1894
1927
|
if (!controller) return { error: 'Android controller not available' };
|
|
1895
|
-
return { devices: await controller.listDevices() };
|
|
1928
|
+
return { devices: await controller.listDevices({ signal }) };
|
|
1896
1929
|
}
|
|
1897
1930
|
|
|
1898
1931
|
case 'android_open_app': {
|
|
1899
1932
|
const controller = await ac();
|
|
1900
1933
|
if (!controller) return { error: 'Android controller not available' };
|
|
1901
|
-
return await controller.openApp(args || {});
|
|
1934
|
+
return await controller.openApp({ ...(args || {}), signal });
|
|
1902
1935
|
}
|
|
1903
1936
|
|
|
1904
1937
|
case 'android_open_intent': {
|
|
1905
1938
|
const controller = await ac();
|
|
1906
1939
|
if (!controller) return { error: 'Android controller not available' };
|
|
1907
|
-
return await controller.openIntent(args || {});
|
|
1940
|
+
return await controller.openIntent({ ...(args || {}), signal });
|
|
1908
1941
|
}
|
|
1909
1942
|
|
|
1910
1943
|
case 'android_tap': {
|
|
1911
1944
|
const controller = await ac();
|
|
1912
1945
|
if (!controller) return { error: 'Android controller not available' };
|
|
1913
|
-
return await controller.tap(args || {});
|
|
1946
|
+
return await controller.tap({ ...(args || {}), signal });
|
|
1914
1947
|
}
|
|
1915
1948
|
|
|
1916
1949
|
case 'android_long_press': {
|
|
1917
1950
|
const controller = await ac();
|
|
1918
1951
|
if (!controller) return { error: 'Android controller not available' };
|
|
1919
|
-
return await controller.longPress(args || {});
|
|
1952
|
+
return await controller.longPress({ ...(args || {}), signal });
|
|
1920
1953
|
}
|
|
1921
1954
|
|
|
1922
1955
|
case 'android_type': {
|
|
1923
1956
|
const controller = await ac();
|
|
1924
1957
|
if (!controller) return { error: 'Android controller not available' };
|
|
1925
|
-
return await controller.type(args || {});
|
|
1958
|
+
return await controller.type({ ...(args || {}), signal });
|
|
1926
1959
|
}
|
|
1927
1960
|
|
|
1928
1961
|
case 'android_swipe': {
|
|
1929
1962
|
const controller = await ac();
|
|
1930
1963
|
if (!controller) return { error: 'Android controller not available' };
|
|
1931
|
-
return await controller.swipe(args || {});
|
|
1964
|
+
return await controller.swipe({ ...(args || {}), signal });
|
|
1932
1965
|
}
|
|
1933
1966
|
|
|
1934
1967
|
case 'android_press_key': {
|
|
1935
1968
|
const controller = await ac();
|
|
1936
1969
|
if (!controller) return { error: 'Android controller not available' };
|
|
1937
|
-
return await controller.pressKey(args || {});
|
|
1970
|
+
return await controller.pressKey({ ...(args || {}), signal });
|
|
1938
1971
|
}
|
|
1939
1972
|
|
|
1940
1973
|
case 'android_wait_for': {
|
|
1941
1974
|
const controller = await ac();
|
|
1942
1975
|
if (!controller) return { error: 'Android controller not available' };
|
|
1943
|
-
return await controller.waitFor(args || {});
|
|
1976
|
+
return await controller.waitFor({ ...(args || {}), signal });
|
|
1944
1977
|
}
|
|
1945
1978
|
|
|
1946
1979
|
case 'android_observe': {
|
|
1947
1980
|
const controller = await ac();
|
|
1948
1981
|
if (!controller) return { error: 'Android controller not available' };
|
|
1949
|
-
return await controller.observe(args || {});
|
|
1982
|
+
return await controller.observe({ ...(args || {}), signal });
|
|
1950
1983
|
}
|
|
1951
1984
|
|
|
1952
1985
|
case 'android_dump_ui': {
|
|
1953
1986
|
const controller = await ac();
|
|
1954
1987
|
if (!controller) return { error: 'Android controller not available' };
|
|
1955
|
-
return await controller.dumpUi(args || {});
|
|
1988
|
+
return await controller.dumpUi({ ...(args || {}), signal });
|
|
1956
1989
|
}
|
|
1957
1990
|
|
|
1958
1991
|
case 'android_screenshot': {
|
|
1959
1992
|
const controller = await ac();
|
|
1960
1993
|
if (!controller) return { error: 'Android controller not available' };
|
|
1961
|
-
return await controller.screenshot(args || {});
|
|
1994
|
+
return await controller.screenshot({ ...(args || {}), signal });
|
|
1962
1995
|
}
|
|
1963
1996
|
|
|
1964
1997
|
case 'android_list_apps': {
|
|
1965
1998
|
const controller = await ac();
|
|
1966
1999
|
if (!controller) return { error: 'Android controller not available' };
|
|
1967
|
-
return await controller.listApps(args || {});
|
|
2000
|
+
return await controller.listApps({ ...(args || {}), signal });
|
|
1968
2001
|
}
|
|
1969
2002
|
|
|
1970
2003
|
case 'android_install_apk': {
|
|
1971
2004
|
const controller = await ac();
|
|
1972
2005
|
if (!controller) return { error: 'Android controller not available' };
|
|
1973
|
-
return await controller.installApk(args || {});
|
|
2006
|
+
return await controller.installApk({ ...(args || {}), signal });
|
|
1974
2007
|
}
|
|
1975
2008
|
|
|
1976
2009
|
case 'android_shell': {
|
|
1977
2010
|
const controller = await ac();
|
|
1978
2011
|
if (!controller) return { error: 'Android controller not available' };
|
|
1979
|
-
return await controller.shell(args || {});
|
|
2012
|
+
return await controller.shell({ ...(args || {}), signal });
|
|
1980
2013
|
}
|
|
1981
2014
|
|
|
1982
2015
|
case 'web_search': {
|
|
1983
2016
|
const apiKey = process.env.BRAVE_SEARCH_API_KEY;
|
|
1984
2017
|
if (!apiKey) return { error: 'BRAVE_SEARCH_API_KEY is not configured' };
|
|
1985
2018
|
|
|
1986
|
-
const controller = new AbortController();
|
|
1987
2019
|
const timeoutMs = 20000;
|
|
1988
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1989
2020
|
|
|
1990
2021
|
try {
|
|
1991
2022
|
const limit = Math.max(1, Math.min(Number(args.count) || 5, 10));
|
|
@@ -2000,15 +2031,20 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2000
2031
|
if (args.search_lang) params.set('search_lang', String(args.search_lang).toLowerCase());
|
|
2001
2032
|
if (args.freshness) params.set('freshness', args.freshness);
|
|
2002
2033
|
|
|
2003
|
-
const
|
|
2034
|
+
const { response, text } = await fetchResponseText(
|
|
2035
|
+
`https://api.search.brave.com/res/v1/web/search?${params.toString()}`,
|
|
2036
|
+
{
|
|
2004
2037
|
headers: {
|
|
2005
2038
|
Accept: 'application/json',
|
|
2006
2039
|
'X-Subscription-Token': apiKey
|
|
2007
2040
|
},
|
|
2008
|
-
|
|
2009
|
-
|
|
2041
|
+
maxResponseBytes: 2 * 1024 * 1024,
|
|
2042
|
+
serviceName: 'Brave Search',
|
|
2043
|
+
signal,
|
|
2044
|
+
timeoutMs,
|
|
2045
|
+
},
|
|
2046
|
+
);
|
|
2010
2047
|
|
|
2011
|
-
const text = await res.text();
|
|
2012
2048
|
let data = null;
|
|
2013
2049
|
try {
|
|
2014
2050
|
data = JSON.parse(text);
|
|
@@ -2016,9 +2052,9 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2016
2052
|
data = null;
|
|
2017
2053
|
}
|
|
2018
2054
|
|
|
2019
|
-
if (!
|
|
2055
|
+
if (!response.ok) {
|
|
2020
2056
|
return {
|
|
2021
|
-
error: `Brave Search API request failed with status ${
|
|
2057
|
+
error: `Brave Search API request failed with status ${response.status}`,
|
|
2022
2058
|
details: data || text.slice(0, 1000)
|
|
2023
2059
|
};
|
|
2024
2060
|
}
|
|
@@ -2040,10 +2076,11 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2040
2076
|
results
|
|
2041
2077
|
};
|
|
2042
2078
|
} catch (err) {
|
|
2043
|
-
if (err
|
|
2079
|
+
if (isAbortError(err, signal)) throw err;
|
|
2080
|
+
if (err.code === 'HTTP_TIMEOUT') {
|
|
2081
|
+
return { error: `Brave Search API request timed out after ${timeoutMs}ms` };
|
|
2082
|
+
}
|
|
2044
2083
|
return { error: err.message };
|
|
2045
|
-
} finally {
|
|
2046
|
-
clearTimeout(timer);
|
|
2047
2084
|
}
|
|
2048
2085
|
}
|
|
2049
2086
|
|
|
@@ -2051,7 +2088,13 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2051
2088
|
const { MemoryManager } = require('../memory/manager');
|
|
2052
2089
|
const mm = new MemoryManager();
|
|
2053
2090
|
const content = typeof args.content === 'string' ? args.content : args.value;
|
|
2054
|
-
const id = await mm.saveMemory(
|
|
2091
|
+
const id = await mm.saveMemory(
|
|
2092
|
+
userId,
|
|
2093
|
+
content,
|
|
2094
|
+
args.category || 'episodic',
|
|
2095
|
+
args.importance || 5,
|
|
2096
|
+
{ agentId, signal },
|
|
2097
|
+
);
|
|
2055
2098
|
if (!id) {
|
|
2056
2099
|
return {
|
|
2057
2100
|
success: true,
|
|
@@ -2065,7 +2108,12 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2065
2108
|
case 'memory_recall': {
|
|
2066
2109
|
const { MemoryManager } = require('../memory/manager');
|
|
2067
2110
|
const mm = new MemoryManager();
|
|
2068
|
-
const results = await mm.recallMemory(
|
|
2111
|
+
const results = await mm.recallMemory(
|
|
2112
|
+
userId,
|
|
2113
|
+
args.query,
|
|
2114
|
+
args.limit || 6,
|
|
2115
|
+
{ agentId, signal },
|
|
2116
|
+
);
|
|
2069
2117
|
if (!results.length) return { results: [], message: 'Nothing found' };
|
|
2070
2118
|
return { results };
|
|
2071
2119
|
}
|
|
@@ -2198,6 +2246,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2198
2246
|
includeFrame: args.include_frame !== false,
|
|
2199
2247
|
forceStt: args.force_stt === true,
|
|
2200
2248
|
agentId,
|
|
2249
|
+
signal,
|
|
2201
2250
|
});
|
|
2202
2251
|
}
|
|
2203
2252
|
|
|
@@ -2206,7 +2255,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2206
2255
|
if (!service || typeof service.getStatus !== 'function') {
|
|
2207
2256
|
return { error: 'Social reach service is unavailable.' };
|
|
2208
2257
|
}
|
|
2209
|
-
return await service.getStatus(userId);
|
|
2258
|
+
return await service.getStatus(userId, { signal });
|
|
2210
2259
|
}
|
|
2211
2260
|
|
|
2212
2261
|
case 'social_reach_read': {
|
|
@@ -2214,7 +2263,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2214
2263
|
if (!service || typeof service.read !== 'function') {
|
|
2215
2264
|
return { error: 'Social reach service is unavailable.' };
|
|
2216
2265
|
}
|
|
2217
|
-
return await service.read(userId, args || {});
|
|
2266
|
+
return await service.read(userId, args || {}, { signal });
|
|
2218
2267
|
}
|
|
2219
2268
|
|
|
2220
2269
|
case 'social_reach_search': {
|
|
@@ -2222,7 +2271,7 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2222
2271
|
if (!service || typeof service.search !== 'function') {
|
|
2223
2272
|
return { error: 'Social reach service is unavailable.' };
|
|
2224
2273
|
}
|
|
2225
|
-
return await service.search(userId, args || {});
|
|
2274
|
+
return await service.search(userId, args || {}, { signal });
|
|
2226
2275
|
}
|
|
2227
2276
|
|
|
2228
2277
|
case 'memory_write': {
|
|
@@ -2305,6 +2354,23 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2305
2354
|
stripNoResponseMarker: false
|
|
2306
2355
|
});
|
|
2307
2356
|
const suppressReply = normalizedMessage === '[NO RESPONSE]';
|
|
2357
|
+
const originDelivery = isOriginMessagingDelivery({
|
|
2358
|
+
triggerSource,
|
|
2359
|
+
source: context.source,
|
|
2360
|
+
chatId: context.chatId,
|
|
2361
|
+
platform: args.platform,
|
|
2362
|
+
to: args.to,
|
|
2363
|
+
});
|
|
2364
|
+
if (
|
|
2365
|
+
!suppressReply
|
|
2366
|
+
&& triggerSource === 'messaging'
|
|
2367
|
+
&& originDelivery
|
|
2368
|
+
&& isDeferredWorkReply(normalizedMessage)
|
|
2369
|
+
) {
|
|
2370
|
+
return {
|
|
2371
|
+
error: 'send_message cannot end the run with a promise or progress-only reply. Continue the work, or use send_interim_update for a factual interim update.',
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2308
2374
|
if (isProactiveTrigger(triggerSource)) {
|
|
2309
2375
|
const proactiveValidation = validateProactiveSendMessageArgs({
|
|
2310
2376
|
purpose: args.purpose,
|
|
@@ -2360,24 +2426,22 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2360
2426
|
};
|
|
2361
2427
|
}
|
|
2362
2428
|
|
|
2429
|
+
if (triggerSource === 'messaging' && originDelivery) {
|
|
2430
|
+
await engine?.stopMessagingProgressSupervisor?.(runId);
|
|
2431
|
+
}
|
|
2363
2432
|
const sendResult = await manager.sendMessage(userId, args.platform, args.to, args.content, {
|
|
2364
2433
|
agentId,
|
|
2365
2434
|
mediaPath: args.media_path,
|
|
2366
2435
|
runId,
|
|
2367
|
-
persistConversation: triggerSource === 'schedule' || triggerSource === 'tasks'
|
|
2436
|
+
persistConversation: triggerSource === 'schedule' || triggerSource === 'tasks',
|
|
2437
|
+
signal,
|
|
2368
2438
|
});
|
|
2369
2439
|
// Track that the agent explicitly sent a message during this run
|
|
2370
2440
|
if (
|
|
2371
2441
|
!suppressReply
|
|
2372
2442
|
&& sendResult?.success === true
|
|
2373
2443
|
&& sendResult?.suppressed !== true
|
|
2374
|
-
&&
|
|
2375
|
-
triggerSource,
|
|
2376
|
-
source: context.source,
|
|
2377
|
-
chatId: context.chatId,
|
|
2378
|
-
platform: args.platform,
|
|
2379
|
-
to: args.to,
|
|
2380
|
-
})
|
|
2444
|
+
&& originDelivery
|
|
2381
2445
|
) {
|
|
2382
2446
|
markProactiveMessageSent({ runState, deliveryState, content: normalizedMessage });
|
|
2383
2447
|
if (runState && triggerSource === 'messaging') {
|
|
@@ -2557,66 +2621,14 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2557
2621
|
}
|
|
2558
2622
|
|
|
2559
2623
|
case 'http_request': {
|
|
2560
|
-
let parsedUrl;
|
|
2561
|
-
try { parsedUrl = new URL(args.url); } catch {
|
|
2562
|
-
return { error: 'Invalid URL' };
|
|
2563
|
-
}
|
|
2564
|
-
const scheme = parsedUrl.protocol.replace(/:$/, '').toLowerCase();
|
|
2565
|
-
if (!['http', 'https'].includes(scheme)) {
|
|
2566
|
-
return { error: 'URL scheme not allowed. Only http and https are permitted.' };
|
|
2567
|
-
}
|
|
2568
|
-
const h = parsedUrl.hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
2569
|
-
if (h === 'localhost' || h === '127.0.0.1' || h === '::1' || h.endsWith('.localhost')) {
|
|
2570
|
-
return { error: 'Loopback addresses are not permitted.' };
|
|
2571
|
-
}
|
|
2572
|
-
const allowPrivate = process.env.NEOAGENT_HTTP_ALLOW_PRIVATE !== 'false';
|
|
2573
|
-
if (!allowPrivate && isPrivateHost(parsedUrl.hostname)) {
|
|
2574
|
-
return { error: 'Private/internal network addresses are not permitted.' };
|
|
2575
|
-
}
|
|
2576
|
-
const controller = new AbortController();
|
|
2577
|
-
const timeoutMs = args.timeout_ms || 30000;
|
|
2578
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2579
2624
|
try {
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
};
|
|
2585
|
-
if (args.body && ['POST', 'PUT', 'PATCH'].includes(options.method)) {
|
|
2586
|
-
options.body = args.body;
|
|
2587
|
-
if (!options.headers['Content-Type']) {
|
|
2588
|
-
options.headers['Content-Type'] = 'application/json';
|
|
2589
|
-
}
|
|
2590
|
-
}
|
|
2591
|
-
const res = await fetch(args.url, options);
|
|
2592
|
-
const MAX_BODY = 512 * 1024;
|
|
2593
|
-
const reader = res.body.getReader();
|
|
2594
|
-
const chunks = [];
|
|
2595
|
-
let total = 0;
|
|
2596
|
-
let truncated = false;
|
|
2597
|
-
while (true) {
|
|
2598
|
-
const { done, value } = await reader.read();
|
|
2599
|
-
if (done) break;
|
|
2600
|
-
total += value.length;
|
|
2601
|
-
if (total > MAX_BODY) {
|
|
2602
|
-
const take = MAX_BODY - (total - value.length);
|
|
2603
|
-
if (take > 0) chunks.push(value.slice(0, take));
|
|
2604
|
-
truncated = true;
|
|
2605
|
-
break;
|
|
2606
|
-
}
|
|
2607
|
-
chunks.push(value);
|
|
2608
|
-
}
|
|
2609
|
-
const text = Buffer.concat(chunks.map(c => Buffer.from(c))).toString('utf-8');
|
|
2610
|
-
return {
|
|
2611
|
-
status: res.status,
|
|
2612
|
-
headers: Object.fromEntries(res.headers.entries()),
|
|
2613
|
-
body: truncated ? text + '\n...[truncated]' : text,
|
|
2614
|
-
};
|
|
2625
|
+
return await executeHttpRequest(args, {
|
|
2626
|
+
allowPrivate: process.env.NEOAGENT_HTTP_ALLOW_PRIVATE === 'true',
|
|
2627
|
+
signal,
|
|
2628
|
+
});
|
|
2615
2629
|
} catch (err) {
|
|
2616
|
-
if (err
|
|
2630
|
+
if (isAbortError(err, signal)) throw err;
|
|
2617
2631
|
return { error: err.message };
|
|
2618
|
-
} finally {
|
|
2619
|
-
clearTimeout(timer);
|
|
2620
2632
|
}
|
|
2621
2633
|
}
|
|
2622
2634
|
|
|
@@ -2762,7 +2774,8 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2762
2774
|
const sendResult = await manager.sendMessage(userId, target.platform, target.to, message, {
|
|
2763
2775
|
agentId,
|
|
2764
2776
|
runId,
|
|
2765
|
-
persistConversation: true
|
|
2777
|
+
persistConversation: true,
|
|
2778
|
+
signal,
|
|
2766
2779
|
});
|
|
2767
2780
|
if (taskId && taskConfig && (taskConfig.notifyPlatform !== target.platform || taskConfig.notifyTo !== target.to)) {
|
|
2768
2781
|
taskConfig.notifyPlatform = target.platform;
|