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.
Files changed (144) hide show
  1. package/extensions/chrome-browser/background.mjs +318 -88
  2. package/extensions/chrome-browser/http.mjs +136 -0
  3. package/extensions/chrome-browser/protocol.mjs +511 -89
  4. package/flutter_app/lib/main_chat.dart +118 -739
  5. package/flutter_app/lib/main_controller.dart +29 -20
  6. package/flutter_app/lib/main_models.dart +3 -0
  7. package/flutter_app/lib/main_operations.dart +334 -321
  8. package/flutter_app/lib/main_settings.dart +4 -3
  9. package/flutter_app/lib/main_shared.dart +14 -11
  10. package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
  11. package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
  12. package/flutter_app/windows/runner/flutter_window.cpp +143 -32
  13. package/lib/manager.js +106 -89
  14. package/lib/schema_migrations.js +67 -13
  15. package/package.json +20 -13
  16. package/runtime/paths.js +49 -5
  17. package/server/guest_agent.js +52 -30
  18. package/server/http/middleware.js +24 -0
  19. package/server/http/routes.js +4 -6
  20. package/server/public/.last_build_id +1 -1
  21. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  22. package/server/public/flutter_bootstrap.js +1 -1
  23. package/server/public/main.dart.js +30803 -30805
  24. package/server/routes/admin.js +1 -1
  25. package/server/routes/android.js +30 -34
  26. package/server/routes/browser.js +23 -15
  27. package/server/routes/desktop.js +18 -1
  28. package/server/routes/integrations.js +5 -1
  29. package/server/routes/memory.js +1 -0
  30. package/server/routes/settings.js +16 -5
  31. package/server/routes/social_reach.js +12 -3
  32. package/server/routes/social_video.js +4 -0
  33. package/server/services/ai/compaction.js +7 -2
  34. package/server/services/ai/history.js +44 -5
  35. package/server/services/ai/integrated_tools/http_request.js +8 -0
  36. package/server/services/ai/loop/agent_engine_core.js +358 -162
  37. package/server/services/ai/loop/callbacks.js +1 -0
  38. package/server/services/ai/loop/completion_judge.js +12 -0
  39. package/server/services/ai/loop/conversation_loop.js +348 -242
  40. package/server/services/ai/loop/messaging_delivery.js +129 -57
  41. package/server/services/ai/loop/model_call_guard.js +91 -0
  42. package/server/services/ai/loop/model_io.js +20 -45
  43. package/server/services/ai/loop/tool_dispatch.js +19 -8
  44. package/server/services/ai/loopPolicy.js +24 -19
  45. package/server/services/ai/model_discovery.js +227 -0
  46. package/server/services/ai/model_identity.js +71 -0
  47. package/server/services/ai/models.js +67 -162
  48. package/server/services/ai/providerRetry.js +17 -59
  49. package/server/services/ai/provider_selector.js +111 -0
  50. package/server/services/ai/providers/anthropic.js +2 -2
  51. package/server/services/ai/providers/claudeCode.js +21 -33
  52. package/server/services/ai/providers/githubCopilot.js +41 -20
  53. package/server/services/ai/providers/google.js +23 -5
  54. package/server/services/ai/providers/grok.js +4 -3
  55. package/server/services/ai/providers/grokOauth.js +19 -27
  56. package/server/services/ai/providers/nvidia.js +10 -5
  57. package/server/services/ai/providers/ollama.js +111 -84
  58. package/server/services/ai/providers/ollama_stream.js +142 -0
  59. package/server/services/ai/providers/openai.js +39 -5
  60. package/server/services/ai/providers/openaiCodex.js +11 -4
  61. package/server/services/ai/providers/openrouter.js +29 -7
  62. package/server/services/ai/providers/provider_error.js +36 -0
  63. package/server/services/ai/settings.js +26 -2
  64. package/server/services/ai/taskAnalysis.js +5 -1
  65. package/server/services/ai/terminal_reply.js +45 -0
  66. package/server/services/ai/toolEvidence.js +58 -29
  67. package/server/services/ai/tools.js +124 -111
  68. package/server/services/android/controller.js +770 -237
  69. package/server/services/android/process.js +140 -0
  70. package/server/services/android/sdk_download.js +143 -0
  71. package/server/services/android/uia.js +6 -5
  72. package/server/services/artifacts/store.js +24 -0
  73. package/server/services/browser/controller.js +736 -385
  74. package/server/services/browser/extension/gateway.js +40 -16
  75. package/server/services/browser/extension/protocol.js +12 -1
  76. package/server/services/browser/extension/provider.js +59 -47
  77. package/server/services/browser/extension/registry.js +155 -34
  78. package/server/services/cli/executor.js +62 -9
  79. package/server/services/desktop/gateway.js +41 -4
  80. package/server/services/desktop/protocol.js +3 -0
  81. package/server/services/desktop/provider.js +39 -42
  82. package/server/services/desktop/registry.js +137 -52
  83. package/server/services/integrations/figma/provider.js +78 -12
  84. package/server/services/integrations/github/common.js +11 -6
  85. package/server/services/integrations/github/provider.js +52 -53
  86. package/server/services/integrations/google/provider.js +55 -19
  87. package/server/services/integrations/home_assistant/network.js +17 -20
  88. package/server/services/integrations/home_assistant/provider.js +7 -5
  89. package/server/services/integrations/home_assistant/tools.js +17 -5
  90. package/server/services/integrations/http.js +51 -0
  91. package/server/services/integrations/manager.js +158 -53
  92. package/server/services/integrations/microsoft/provider.js +80 -13
  93. package/server/services/integrations/neoarchive/provider.js +55 -29
  94. package/server/services/integrations/neorecall/client.js +17 -10
  95. package/server/services/integrations/neorecall/provider.js +20 -11
  96. package/server/services/integrations/notion/provider.js +16 -13
  97. package/server/services/integrations/oauth_provider.js +115 -51
  98. package/server/services/integrations/slack/provider.js +98 -9
  99. package/server/services/integrations/spotify/provider.js +67 -71
  100. package/server/services/integrations/trello/provider.js +21 -7
  101. package/server/services/integrations/weather/provider.js +18 -12
  102. package/server/services/integrations/whatsapp/provider.js +76 -16
  103. package/server/services/manager.js +87 -1
  104. package/server/services/memory/embedding_index.js +20 -8
  105. package/server/services/memory/embeddings.js +151 -90
  106. package/server/services/memory/ingestion.js +50 -9
  107. package/server/services/memory/ingestion_documents.js +13 -3
  108. package/server/services/memory/manager.js +52 -19
  109. package/server/services/messaging/automation.js +84 -9
  110. package/server/services/messaging/http_platforms.js +33 -13
  111. package/server/services/messaging/inbound_queue.js +78 -24
  112. package/server/services/messaging/inbound_store.js +224 -0
  113. package/server/services/messaging/manager.js +326 -51
  114. package/server/services/messaging/typing_keepalive.js +5 -2
  115. package/server/services/network/http.js +210 -0
  116. package/server/services/network/safe_request.js +307 -0
  117. package/server/services/runtime/backends/local-vm.js +214 -66
  118. package/server/services/runtime/manager.js +17 -12
  119. package/server/services/social_reach/channels/github.js +10 -4
  120. package/server/services/social_reach/channels/reddit.js +4 -4
  121. package/server/services/social_reach/channels/rss.js +2 -2
  122. package/server/services/social_reach/channels/social_video.js +12 -7
  123. package/server/services/social_reach/channels/v2ex.js +21 -8
  124. package/server/services/social_reach/channels/x.js +2 -2
  125. package/server/services/social_reach/channels/xueqiu.js +5 -5
  126. package/server/services/social_reach/service.js +9 -6
  127. package/server/services/social_reach/utils.js +65 -14
  128. package/server/services/social_video/service.js +160 -50
  129. package/server/services/tasks/integration_runtime.js +18 -8
  130. package/server/services/tasks/runtime.js +39 -4
  131. package/server/services/voice/agentBridge.js +17 -4
  132. package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
  133. package/server/services/voice/liveSession.js +31 -0
  134. package/server/services/voice/openaiSpeech.js +33 -8
  135. package/server/services/voice/providers.js +233 -151
  136. package/server/services/voice/runtimeManager.js +118 -20
  137. package/server/services/voice/turnRunner.js +6 -0
  138. package/server/services/wearable/firmware_manifest.js +51 -13
  139. package/server/services/wearable/service.js +1 -0
  140. package/server/utils/abort.js +96 -0
  141. package/server/utils/cloud-security.js +110 -3
  142. package/server/utils/files.js +31 -0
  143. package/server/utils/image_payload.js +95 -0
  144. package/server/utils/retry.js +107 -0
@@ -12,7 +12,7 @@ const AI_PROVIDER_DEFINITIONS = Object.freeze({
12
12
  openai: {
13
13
  id: 'openai',
14
14
  label: 'OpenAI',
15
- description: 'GPT-5 and GPT-4.1 models for fast general work and reasoning.',
15
+ description: 'Current GPT models for general work, coding, and reasoning.',
16
16
  envKey: 'OPENAI_API_KEY',
17
17
  supportsApiKey: true,
18
18
  supportsBaseUrl: true,
@@ -148,11 +148,19 @@ function createDefaultAiSettings() {
148
148
  cost_mode: 'balanced_auto',
149
149
  chat_history_window: 20,
150
150
  tool_replay_budget_chars: 6000,
151
+ tool_replay_budget_file_chars: null,
152
+ tool_replay_budget_browser_chars: null,
153
+ tool_replay_budget_command_chars: null,
154
+ max_iterations: null,
155
+ max_consecutive_read_only_iterations: null,
156
+ max_consecutive_tool_failures: null,
157
+ max_model_failure_recoveries: null,
158
+ compaction_threshold: null,
151
159
  subagent_max_iterations: 6,
152
160
  subagent_max_children_per_run: 10,
153
161
  assistant_behavior_notes: '',
154
162
  auto_skill_learning: false,
155
- fallback_model_id: 'gpt-5-nano',
163
+ fallback_model_id: 'openai::gpt-5-nano',
156
164
  smarter_model_selector: true,
157
165
  enabled_models: [],
158
166
  default_chat_model: 'auto',
@@ -179,6 +187,14 @@ function parseSettingValue(value) {
179
187
  }
180
188
  }
181
189
 
190
+ function normalizeOptionalNumber(value, min, max, { integer = false } = {}) {
191
+ if (value == null || value === '') return null;
192
+ const parsed = Number(value);
193
+ if (!Number.isFinite(parsed)) return null;
194
+ const normalized = integer ? Math.floor(parsed) : parsed;
195
+ return Math.min(Math.max(normalized, min), max);
196
+ }
197
+
182
198
  function normalizeProviderConfigs(rawConfigs) {
183
199
  const defaults = createDefaultProviderConfigs();
184
200
  const parsed = rawConfigs && typeof rawConfigs === 'object' && !Array.isArray(rawConfigs)
@@ -325,6 +341,14 @@ function getAiSettings(userId, agentId = null) {
325
341
 
326
342
  settings.chat_history_window = Math.max(6, Math.min(Number(settings.chat_history_window) || DEFAULT_AI_SETTINGS.chat_history_window, 40));
327
343
  settings.tool_replay_budget_chars = Math.max(1200, Math.min(Number(settings.tool_replay_budget_chars) || DEFAULT_AI_SETTINGS.tool_replay_budget_chars, 12000));
344
+ settings.tool_replay_budget_file_chars = normalizeOptionalNumber(settings.tool_replay_budget_file_chars, 500, 500_000, { integer: true });
345
+ settings.tool_replay_budget_browser_chars = normalizeOptionalNumber(settings.tool_replay_budget_browser_chars, 500, 500_000, { integer: true });
346
+ settings.tool_replay_budget_command_chars = normalizeOptionalNumber(settings.tool_replay_budget_command_chars, 500, 500_000, { integer: true });
347
+ settings.max_iterations = normalizeOptionalNumber(settings.max_iterations, 1, 400, { integer: true });
348
+ settings.max_consecutive_read_only_iterations = normalizeOptionalNumber(settings.max_consecutive_read_only_iterations, 3, 25, { integer: true });
349
+ settings.max_consecutive_tool_failures = normalizeOptionalNumber(settings.max_consecutive_tool_failures, 1, 50, { integer: true });
350
+ settings.max_model_failure_recoveries = normalizeOptionalNumber(settings.max_model_failure_recoveries, 0, 10, { integer: true });
351
+ settings.compaction_threshold = normalizeOptionalNumber(settings.compaction_threshold, 0.1, 1);
328
352
  settings.subagent_max_iterations = Math.max(2, Math.min(Number(settings.subagent_max_iterations) || DEFAULT_AI_SETTINGS.subagent_max_iterations, 12));
329
353
  settings.subagent_max_children_per_run = Math.max(
330
354
  1,
@@ -17,6 +17,7 @@ const TASK_ANALYSIS_CONFIDENCE_DEFAULT = 0.55;
17
17
  const VERIFICATION_CONFIDENCE_VERIFIED = 0.85;
18
18
  const VERIFICATION_CONFIDENCE_DEFAULT = 0.5;
19
19
  const JSON_ONLY_RESPONSE_RULE = 'Return JSON only. No markdown, no prose, no code fences.';
20
+ const { isDeferredWorkReply } = require('./terminal_reply');
20
21
  const ANALYSIS_SCHEMA_EXAMPLE = {
21
22
  mode: 'execute',
22
23
  needs_verification: true,
@@ -256,7 +257,10 @@ function isDirectAnswerEligibleAnalysis(analysis) {
256
257
  planningDepth: analysis.planning_depth,
257
258
  });
258
259
 
259
- return promotedMode === 'direct_answer' && !analysis.needs_subagents && Boolean(draftReply);
260
+ return promotedMode === 'direct_answer'
261
+ && !analysis.needs_subagents
262
+ && Boolean(draftReply)
263
+ && !isDeferredWorkReply(draftReply);
260
264
  }
261
265
 
262
266
  function extractJsonCandidate(text) {
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ function normalizeReply(content) {
4
+ return String(content || '')
5
+ .replace(/[\u2018\u2019]/g, "'")
6
+ .replace(/^[\s>*_`#-]+/, '')
7
+ .replace(/\s+/g, ' ')
8
+ .trim()
9
+ .toLowerCase();
10
+ }
11
+
12
+ function hasExternalBlocker(text) {
13
+ return /\b(?:blocked|cannot|can't|could not|couldn't|unable to|do not have access|don't have access|missing (?:access|credentials|permission|information)|need you to|requires? your|waiting for your|please (?:provide|send|choose|confirm|authorize|approve))\b/.test(text);
14
+ }
15
+
16
+ /**
17
+ * Detect replies that only announce or promise work which has not happened yet.
18
+ * This is intentionally conservative and supplements the model completion judge;
19
+ * it is a deterministic last line of defence against terminating after a status
20
+ * phrase such as "I'm working on it" or "let me check".
21
+ */
22
+ function isDeferredWorkReply(content) {
23
+ const text = normalizeReply(content);
24
+ if (!text || hasExternalBlocker(text)) return false;
25
+
26
+ const acknowledgement = '(?:(?:sure|okay|ok|alright|absolutely|of course|got it|understood)[,.!]?\\s+)?';
27
+ const taskTarget = '(?:it|this|that|your\\s+(?:request|task|issue)|the\\s+(?:request|task|issue|problem|code|logs?|repository|repo|build|tests?))';
28
+ const activeWork = `(?:working\\s+(?:on|through)\\s+${taskTarget}|checking(?:\\s+${taskTarget})?|looking\\s+(?:into|at)\\s+${taskTarget}|investigating\\s+${taskTarget}|reviewing\\s+${taskTarget}|researching\\s+${taskTarget}|testing\\s+${taskTarget}|debugging\\s+${taskTarget}|running\\s+${taskTarget}|processing\\s+${taskTarget}|handling\\s+${taskTarget}|starting\\s+${taskTarget}|continuing\\s+${taskTarget})`;
29
+ const promisedWork = '(?:check|look\\s+into|investigate|review|research|test|debug|run|work\\s+on|handle|start|continue|fix|send|create|update|delete|install|restart|deploy|publish|do\\s+that|take\\s+care\\s+of)';
30
+ const patterns = [
31
+ new RegExp(`^${acknowledgement}i(?:'m| am)\\s+(?:(?:still|currently|now|already)\\s+)?${activeWork}\\b`),
32
+ new RegExp(`^${acknowledgement}i(?:'ll| will)\\s+(?:now\\s+)?${promisedWork}\\b`),
33
+ new RegExp(`^${acknowledgement}(?:let me|allow me to)\\s+${promisedWork}\\b`),
34
+ new RegExp(`^${acknowledgement}(?:i(?:'m| am)\\s+going to)\\s+${promisedWork}\\b`),
35
+ /^(?:(?:please )?give me\s+(?:(?:a|one|another)\s+)?(?:moment|minute|bit)|hang tight|please wait|one moment|bear with me)\b/,
36
+ /^(?:working on it|checking now|on it)[.!…]*$/,
37
+ /\bi(?:'ll| will)\s+(?:get back to you|update you|report back|let you know|keep you posted)\b/,
38
+ /\b(?:i(?:'ll| will)\s+follow up|stay tuned)\b/,
39
+ ];
40
+ return patterns.some((pattern) => pattern.test(text));
41
+ }
42
+
43
+ module.exports = {
44
+ isDeferredWorkReply,
45
+ };
@@ -34,30 +34,50 @@ const EVIDENCE_SOURCE_RULES = [
34
34
  { source: 'subagent', match: (name) => name.includes('subagent') },
35
35
  ];
36
36
 
37
+ const STATE_CHANGING_DEVICE_TOOLS = new Set([
38
+ 'android_install_apk',
39
+ 'android_long_press',
40
+ 'android_open_app',
41
+ 'android_open_intent',
42
+ 'android_press_key',
43
+ 'android_start_emulator',
44
+ 'android_stop_emulator',
45
+ 'android_swipe',
46
+ 'android_tap',
47
+ 'android_type',
48
+ 'desktop_click',
49
+ 'desktop_drag',
50
+ 'desktop_launch_app',
51
+ 'desktop_press_key',
52
+ 'desktop_scroll',
53
+ 'desktop_select_device',
54
+ 'desktop_type',
55
+ ]);
56
+
37
57
  function deriveEvidenceSource(name) {
38
58
  const rule = EVIDENCE_SOURCE_RULES.find((entry) => entry.match(name));
39
59
  return rule ? rule.source : 'tool';
40
60
  }
41
61
 
42
- function classifyToolExecution(toolName, toolArgs = {}, result, errorMessage = '') {
62
+ function resolveDeclaredToolAccess(toolDefinition, toolArgs = {}) {
63
+ const access = String(toolDefinition?.access || '').trim().toLowerCase();
64
+ if (access === 'read' || access === 'write') return access;
65
+ if (access !== 'dynamic_http_method') return null;
66
+
67
+ const method = String(toolArgs?.method || toolArgs?.http_method || 'GET')
68
+ .trim()
69
+ .toUpperCase();
70
+ return ['GET', 'HEAD', 'OPTIONS'].includes(method) ? 'read' : 'write';
71
+ }
72
+
73
+ function classifyToolExecution(
74
+ toolName,
75
+ toolArgs = {},
76
+ result,
77
+ errorMessage = '',
78
+ toolDefinition = null,
79
+ ) {
43
80
  const name = String(toolName || '');
44
- const evidenceRelevantPrefixes = ['browser_', 'android_'];
45
- const evidenceRelevantExact = new Set([
46
- 'web_search',
47
- 'http_request',
48
- 'read_file',
49
- 'read_files',
50
- 'search_files',
51
- 'list_directory',
52
- 'code_navigate',
53
- 'query_structured_data',
54
- 'session_search',
55
- 'memory_recall',
56
- 'analyze_image',
57
- 'read_health_data',
58
- 'list_tasks',
59
- 'wait_subagent',
60
- ]);
61
81
  const stateChangingExact = new Set([
62
82
  'execute_command',
63
83
  'write_file',
@@ -79,22 +99,30 @@ function classifyToolExecution(toolName, toolArgs = {}, result, errorMessage = '
79
99
  'mcp_add_server',
80
100
  'mcp_remove_server',
81
101
  'spawn_subagent',
102
+ 'delegate_to_agent',
82
103
  'cancel_subagent',
83
104
  ]);
84
105
 
85
- const evidenceSource = deriveEvidenceSource(name);
106
+ const declaredAccess = resolveDeclaredToolAccess(toolDefinition, toolArgs);
107
+ const evidenceSource = declaredAccess ? 'integration' : deriveEvidenceSource(name);
86
108
 
87
- const evidenceRelevant = evidenceRelevantExact.has(name)
88
- || evidenceRelevantPrefixes.some((prefix) => name.startsWith(prefix));
89
- const stateChanged = (
90
- name === 'execute_command'
91
- ? !isClearlyReadOnlyShellCommand(toolArgs?.command || '')
92
- : stateChangingExact.has(name)
93
- )
109
+ // Any successful, substantive tool result can advance the run. This default
110
+ // is deliberate: MCP, skills, and newly added integrations must not become
111
+ // invisible to the churn guard just because their names were not added to a
112
+ // central allow-list. Repetition detection still rejects unchanged retries.
113
+ const evidenceRelevant = isSubstantiveProgressToolName(name);
114
+ let directStateChange;
115
+ if (name === 'execute_command' || name === 'android_shell') {
116
+ directStateChange = !isClearlyReadOnlyShellCommand(toolArgs?.command || '');
117
+ } else {
118
+ directStateChange = declaredAccess === 'write'
119
+ || STATE_CHANGING_DEVICE_TOOLS.has(name)
120
+ || stateChangingExact.has(name);
121
+ }
122
+ const stateChanged = directStateChange
94
123
  || (name.startsWith('github_') && isProgressToolCall(name, toolArgs))
95
124
  || (name === 'http_request' && isProgressToolCall(name, toolArgs))
96
- || name.startsWith('android_')
97
- || ['browser_click', 'browser_type', 'browser_evaluate'].includes(name);
125
+ || ['browser_click', 'browser_evaluate', 'browser_navigate', 'browser_type'].includes(name);
98
126
 
99
127
  let normalizedError = String(errorMessage || result?.error || '').trim();
100
128
  if (!normalizedError && name === 'execute_command' && result && typeof result === 'object') {
@@ -167,7 +195,7 @@ function gatheredNewEvidence(execution, repetitionObservation = null) {
167
195
  function isSubstantiveProgressToolName(toolName = '') {
168
196
  const name = String(toolName || '').trim();
169
197
  if (!name) return false;
170
- if (name === 'send_message' || name === 'send_interim_update' || name === 'make_call') return false;
198
+ if (name === 'send_message' || name === 'send_interim_update' || name === 'make_call' || name === 'notify_user') return false;
171
199
  if (name === 'think' || name === 'activate_tools' || name === 'task_complete') return false;
172
200
  return true;
173
201
  }
@@ -254,4 +282,5 @@ module.exports = {
254
282
  summarizeAvailableTools,
255
283
  inferToolFailureMessage,
256
284
  buildAutonomousRecoveryContext,
285
+ resolveDeclaredToolAccess,
257
286
  };