neoagent 3.2.1-beta.0 → 3.2.1-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agent-run-lifecycle.md +10 -0
- package/extensions/chrome-browser/background.mjs +318 -88
- package/extensions/chrome-browser/http.mjs +136 -0
- package/extensions/chrome-browser/protocol.mjs +654 -90
- package/flutter_app/lib/main_chat.dart +118 -739
- package/flutter_app/lib/main_controller.dart +111 -20
- package/flutter_app/lib/main_integrations.dart +607 -8
- package/flutter_app/lib/main_models.dart +3 -0
- package/flutter_app/lib/main_operations.dart +334 -321
- package/flutter_app/lib/main_security.dart +266 -112
- package/flutter_app/lib/main_settings.dart +4 -3
- package/flutter_app/lib/main_shared.dart +14 -11
- package/flutter_app/lib/src/backend_client.dart +78 -0
- 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/landing/index.html +3 -1
- package/lib/manager.js +106 -89
- package/lib/schema_migrations.js +145 -13
- package/package.json +30 -15
- package/runtime/paths.js +49 -5
- package/server/db/database.js +4 -4
- package/server/guest-agent.cli.package.json +13 -0
- package/server/guest_agent.js +85 -40
- package/server/http/middleware.js +24 -0
- package/server/http/routes.js +11 -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 +2 -2
- package/server/public/main.dart.js +73083 -72209
- package/server/routes/admin.js +1 -1
- package/server/routes/agents.js +35 -2
- 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 +107 -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/agents/manager.js +1 -1
- package/server/services/ai/capabilityHealth.js +62 -96
- package/server/services/ai/compaction.js +7 -2
- package/server/services/ai/history.js +45 -6
- package/server/services/ai/integrated_tools/http_request.js +8 -0
- package/server/services/ai/loop/agent_engine_core.js +496 -166
- package/server/services/ai/loop/blank_recovery.js +5 -4
- package/server/services/ai/loop/callbacks.js +1 -0
- package/server/services/ai/loop/completion_judge.js +121 -5
- package/server/services/ai/loop/conversation_loop.js +620 -340
- package/server/services/ai/loop/lifecycle.js +108 -0
- 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 +48 -56
- package/server/services/ai/loop/progress_classification.js +2 -0
- package/server/services/ai/loop/tool_dispatch.js +28 -8
- package/server/services/ai/loopPolicy.js +48 -21
- package/server/services/ai/messagingFallback.js +17 -17
- package/server/services/ai/model_discovery.js +227 -0
- package/server/services/ai/model_failure_cache.js +108 -0
- package/server/services/ai/model_identity.js +71 -0
- package/server/services/ai/models.js +68 -163
- package/server/services/ai/providerRetry.js +17 -59
- package/server/services/ai/provider_selector.js +166 -0
- package/server/services/ai/providers/anthropic.js +4 -4
- 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 +135 -97
- package/server/services/ai/providers/grok.js +6 -5
- package/server/services/ai/providers/grokOauth.js +19 -27
- package/server/services/ai/providers/nvidia.js +12 -7
- package/server/services/ai/providers/ollama.js +114 -86
- package/server/services/ai/providers/ollama_stream.js +142 -0
- package/server/services/ai/providers/openai.js +41 -7
- package/server/services/ai/providers/openaiCodex.js +13 -4
- package/server/services/ai/providers/openrouter.js +31 -9
- package/server/services/ai/providers/provider_error.js +36 -0
- package/server/services/ai/settings.js +26 -2
- package/server/services/ai/systemPrompt.js +19 -12
- package/server/services/ai/taskAnalysis.js +58 -10
- package/server/services/ai/terminal_reply.js +18 -0
- package/server/services/ai/toolEvidence.js +350 -29
- package/server/services/ai/tools.js +190 -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 +843 -385
- package/server/services/browser/extension/gateway.js +40 -16
- package/server/services/browser/extension/protocol.js +15 -1
- package/server/services/browser/extension/provider.js +71 -47
- package/server/services/browser/extension/registry.js +155 -34
- package/server/services/cli/executor.js +62 -9
- package/server/services/credentials/bitwarden_cli.js +322 -0
- package/server/services/credentials/broker.js +594 -0
- 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/bitwarden/constants.js +14 -0
- package/server/services/integrations/bitwarden/provider.js +197 -0
- package/server/services/integrations/bitwarden/snapshot.js +65 -0
- 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 +159 -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/registry.js +2 -0
- 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 +110 -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 +85 -10
- package/server/services/messaging/formatting_guides.js +7 -4
- 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/messaging/whatsapp.js +22 -14
- 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 +227 -67
- package/server/services/runtime/docker-vm-manager.js +9 -0
- package/server/services/runtime/guest_bootstrap.js +30 -4
- package/server/services/runtime/guest_image.js +43 -12
- package/server/services/runtime/manager.js +77 -23
- package/server/services/runtime/validation.js +7 -6
- package/server/services/security/tool_categories.js +6 -0
- 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/message.js +1 -1
- package/server/services/voice/openaiSpeech.js +33 -8
- package/server/services/voice/providers.js +233 -151
- package/server/services/voice/runtime.js +2 -2
- 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
|
@@ -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
|
|
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
|
|
106
|
+
const declaredAccess = resolveDeclaredToolAccess(toolDefinition, toolArgs);
|
|
107
|
+
const evidenceSource = declaredAccess ? 'integration' : deriveEvidenceSource(name);
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
||
|
|
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,11 +195,300 @@ 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
|
}
|
|
174
202
|
|
|
203
|
+
const PRIMARY_RESEARCH_SOURCES = new Set([
|
|
204
|
+
'browser',
|
|
205
|
+
'http',
|
|
206
|
+
'integration',
|
|
207
|
+
'files',
|
|
208
|
+
'command',
|
|
209
|
+
'mcp',
|
|
210
|
+
'android',
|
|
211
|
+
'data',
|
|
212
|
+
'vision',
|
|
213
|
+
'skills',
|
|
214
|
+
'subagent',
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
const SECONDARY_RESEARCH_SOURCES = new Set([
|
|
218
|
+
'search',
|
|
219
|
+
'memory',
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
// External/source-backed tools only. Local file/edit/shell work must not
|
|
223
|
+
// inherit a research burden just because inspection tools are available.
|
|
224
|
+
const RESEARCH_TOOL_HINTS = new Set([
|
|
225
|
+
'web_search',
|
|
226
|
+
'http_request',
|
|
227
|
+
'browser_navigate',
|
|
228
|
+
'browser_open',
|
|
229
|
+
'browser_click',
|
|
230
|
+
'browser_type',
|
|
231
|
+
'browser_snapshot',
|
|
232
|
+
'browser_evaluate',
|
|
233
|
+
'analyze_image',
|
|
234
|
+
'spawn_subagent',
|
|
235
|
+
'delegate_to_agent',
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
function clampResearchText(value, maxChars = 220) {
|
|
239
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
240
|
+
if (!text) return '';
|
|
241
|
+
if (text.length <= maxChars) return text;
|
|
242
|
+
return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function uniqueResearchTokens(values = [], { limit = 12 } = {}) {
|
|
246
|
+
const seen = new Set();
|
|
247
|
+
const tokens = [];
|
|
248
|
+
for (const value of values) {
|
|
249
|
+
const token = String(value || '').replace(/\s+/g, ' ').trim();
|
|
250
|
+
if (!token) continue;
|
|
251
|
+
const key = token.toLowerCase();
|
|
252
|
+
if (seen.has(key)) continue;
|
|
253
|
+
seen.add(key);
|
|
254
|
+
tokens.push(token);
|
|
255
|
+
if (tokens.length >= limit) break;
|
|
256
|
+
}
|
|
257
|
+
return tokens;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Research targets come from structured task analysis / goal contract only.
|
|
261
|
+
// Do not NLP-extract entity names from free text.
|
|
262
|
+
function collectResearchTargets(analysis = null, goalContext = null) {
|
|
263
|
+
return uniqueResearchTokens([
|
|
264
|
+
...(Array.isArray(analysis?.research_targets) ? analysis.research_targets : []),
|
|
265
|
+
...(Array.isArray(goalContext?.researchTargets) ? goalContext.researchTargets : []),
|
|
266
|
+
], { limit: 8 });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function hasResearchToolHint(analysis = null) {
|
|
270
|
+
const tools = Array.isArray(analysis?.suggested_tools) ? analysis.suggested_tools : [];
|
|
271
|
+
return tools.some((name) => {
|
|
272
|
+
const tool = String(name || '').trim().toLowerCase();
|
|
273
|
+
if (!tool) return false;
|
|
274
|
+
if (RESEARCH_TOOL_HINTS.has(tool)) return true;
|
|
275
|
+
// Tool-schema identity only: family prefixes, not free-text phrase matching.
|
|
276
|
+
return tool.startsWith('browser_') || tool.startsWith('mcp_');
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function resolveResearchIntensity(analysis = null, goalContext = null) {
|
|
281
|
+
const mode = String(analysis?.mode || '').trim().toLowerCase();
|
|
282
|
+
const complexity = String(
|
|
283
|
+
goalContext?.effectiveComplexity
|
|
284
|
+
|| analysis?.complexity
|
|
285
|
+
|| '',
|
|
286
|
+
).trim().toLowerCase();
|
|
287
|
+
const autonomyLevel = String(
|
|
288
|
+
goalContext?.effectiveAutonomyLevel
|
|
289
|
+
|| analysis?.autonomy_level
|
|
290
|
+
|| '',
|
|
291
|
+
).trim().toLowerCase();
|
|
292
|
+
const completionConfidence = String(
|
|
293
|
+
goalContext?.effectiveCompletionConfidence
|
|
294
|
+
|| analysis?.completion_confidence_required
|
|
295
|
+
|| '',
|
|
296
|
+
).trim().toLowerCase();
|
|
297
|
+
const verificationNeed = String(analysis?.verification_need || '').trim().toLowerCase();
|
|
298
|
+
const freshnessRisk = String(analysis?.freshness_risk || '').trim().toLowerCase();
|
|
299
|
+
const planningDepth = String(analysis?.planning_depth || '').trim().toLowerCase();
|
|
300
|
+
const targets = collectResearchTargets(analysis, goalContext);
|
|
301
|
+
const researchToolHint = hasResearchToolHint(analysis);
|
|
302
|
+
|
|
303
|
+
if (mode === 'direct_answer' && verificationNeed === 'none' && freshnessRisk === 'none') {
|
|
304
|
+
return 'none';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// External/source-backed work only. Pure local implementation tasks must not
|
|
308
|
+
// inherit a research burden just because they are complex or multi-step.
|
|
309
|
+
const needsExternalEvidence = (
|
|
310
|
+
targets.length > 0
|
|
311
|
+
|| researchToolHint
|
|
312
|
+
|| freshnessRisk === 'possible'
|
|
313
|
+
|| freshnessRisk === 'high'
|
|
314
|
+
|| verificationNeed === 'required'
|
|
315
|
+
);
|
|
316
|
+
if (!needsExternalEvidence) {
|
|
317
|
+
return 'none';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const deepSignals = [
|
|
321
|
+
mode === 'plan_execute',
|
|
322
|
+
complexity === 'complex',
|
|
323
|
+
autonomyLevel === 'high',
|
|
324
|
+
completionConfidence === 'high',
|
|
325
|
+
verificationNeed === 'required',
|
|
326
|
+
freshnessRisk === 'high',
|
|
327
|
+
planningDepth === 'deep',
|
|
328
|
+
targets.length >= 2,
|
|
329
|
+
].filter(Boolean).length;
|
|
330
|
+
|
|
331
|
+
if (deepSignals >= 2 || targets.length >= 2 || verificationNeed === 'required' || freshnessRisk === 'high') {
|
|
332
|
+
return 'deep';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return 'light';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function isSuccessfulResearchExecution(item = {}) {
|
|
339
|
+
if (!item || item.ok !== true) return false;
|
|
340
|
+
if (!isSubstantiveProgressToolName(item.toolName)) return false;
|
|
341
|
+
if (item.evidenceSource === 'messaging') return false;
|
|
342
|
+
return Boolean(item.evidenceRelevant || item.dependsOnOutput || item.stateChanged);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function isPrimaryResearchSource(source = '') {
|
|
346
|
+
return PRIMARY_RESEARCH_SOURCES.has(String(source || '').trim().toLowerCase());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function isSecondaryResearchSource(source = '') {
|
|
350
|
+
return SECONDARY_RESEARCH_SOURCES.has(String(source || '').trim().toLowerCase());
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function executionMentionsTarget(execution = {}, target = '') {
|
|
354
|
+
const needle = String(target || '').trim().toLowerCase();
|
|
355
|
+
if (!needle) return false;
|
|
356
|
+
const haystack = [
|
|
357
|
+
execution.summary,
|
|
358
|
+
execution.toolName,
|
|
359
|
+
execution.evidenceSource,
|
|
360
|
+
JSON.stringify(execution.input || {}),
|
|
361
|
+
].join(' ').toLowerCase();
|
|
362
|
+
if (haystack.includes(needle)) return true;
|
|
363
|
+
|
|
364
|
+
const tokens = needle
|
|
365
|
+
.split(/[^a-z0-9+]+/i)
|
|
366
|
+
.map((token) => token.trim())
|
|
367
|
+
.filter((token) => token.length >= 3);
|
|
368
|
+
if (tokens.length === 0) return false;
|
|
369
|
+
const matched = tokens.filter((token) => haystack.includes(token)).length;
|
|
370
|
+
return matched >= Math.min(2, tokens.length);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function assessResearchAdequacy({
|
|
374
|
+
analysis = null,
|
|
375
|
+
goalContext = null,
|
|
376
|
+
toolExecutions = [],
|
|
377
|
+
} = {}) {
|
|
378
|
+
const intensity = resolveResearchIntensity(analysis, goalContext);
|
|
379
|
+
const targets = collectResearchTargets(analysis, goalContext);
|
|
380
|
+
|
|
381
|
+
const successful = (Array.isArray(toolExecutions) ? toolExecutions : [])
|
|
382
|
+
.filter(isSuccessfulResearchExecution);
|
|
383
|
+
const primary = successful.filter((item) => isPrimaryResearchSource(item.evidenceSource));
|
|
384
|
+
const secondary = successful.filter((item) => isSecondaryResearchSource(item.evidenceSource));
|
|
385
|
+
const coveredTargets = targets.filter((target) => (
|
|
386
|
+
successful.some((item) => executionMentionsTarget(item, target))
|
|
387
|
+
));
|
|
388
|
+
const primaryCoveredTargets = targets.filter((target) => (
|
|
389
|
+
primary.some((item) => executionMentionsTarget(item, target))
|
|
390
|
+
));
|
|
391
|
+
const uncoveredTargets = targets.filter((target) => !coveredTargets.includes(target));
|
|
392
|
+
const primaryUncoveredTargets = targets.filter((target) => !primaryCoveredTargets.includes(target));
|
|
393
|
+
|
|
394
|
+
const requiredPrimarySources = intensity === 'deep'
|
|
395
|
+
? (targets.length > 0 ? Math.min(4, targets.length) : 2)
|
|
396
|
+
: intensity === 'light'
|
|
397
|
+
? 1
|
|
398
|
+
: 0;
|
|
399
|
+
const requiredSecondarySources = intensity === 'deep' ? 1 : 0;
|
|
400
|
+
const requiredTargetCoverage = intensity === 'deep'
|
|
401
|
+
? targets.length
|
|
402
|
+
: intensity === 'light'
|
|
403
|
+
? Math.min(targets.length, 1)
|
|
404
|
+
: 0;
|
|
405
|
+
const requiredPrimaryTargetCoverage = intensity === 'deep'
|
|
406
|
+
? targets.length
|
|
407
|
+
: 0;
|
|
408
|
+
|
|
409
|
+
const missing = [];
|
|
410
|
+
if (primary.length < requiredPrimarySources) {
|
|
411
|
+
missing.push(
|
|
412
|
+
`Need ${requiredPrimarySources} primary source open/fetch/inspect step(s); have ${primary.length}.`,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
if (secondary.length < requiredSecondarySources && primary.length < requiredPrimarySources) {
|
|
416
|
+
missing.push('Need at least one search lead before finishing deep research.');
|
|
417
|
+
}
|
|
418
|
+
if (targets.length > 0 && coveredTargets.length < requiredTargetCoverage) {
|
|
419
|
+
missing.push(
|
|
420
|
+
`Need evidence for: ${uncoveredTargets.slice(0, 4).join('; ') || targets.slice(0, 4).join('; ')}.`,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (targets.length > 0 && primaryCoveredTargets.length < requiredPrimaryTargetCoverage) {
|
|
424
|
+
missing.push(
|
|
425
|
+
`Need primary-source evidence for: ${primaryUncoveredTargets.slice(0, 4).join('; ') || targets.slice(0, 4).join('; ')}.`,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
if (intensity === 'deep' && primary.length === 0 && secondary.length > 0) {
|
|
429
|
+
missing.push('Search snippets alone are not enough; open primary sources for the key claims.');
|
|
430
|
+
}
|
|
431
|
+
if (intensity === 'light' && primary.length === 0 && secondary.length === 0) {
|
|
432
|
+
missing.push('Need at least one successful search or primary-source check before finishing.');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const adequate = intensity === 'none' ? true : missing.length === 0;
|
|
436
|
+
const nextActions = [];
|
|
437
|
+
if (!adequate) {
|
|
438
|
+
if (primaryUncoveredTargets.length > 0) {
|
|
439
|
+
nextActions.push(
|
|
440
|
+
`Open or fetch primary sources for each remaining target: ${primaryUncoveredTargets.slice(0, 4).join('; ')}.`,
|
|
441
|
+
);
|
|
442
|
+
} else if (uncoveredTargets.length > 0) {
|
|
443
|
+
nextActions.push(
|
|
444
|
+
`Research each remaining target separately: ${uncoveredTargets.slice(0, 4).join('; ')}.`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
if (primary.length < requiredPrimarySources) {
|
|
448
|
+
nextActions.push('Open or fetch primary pages/docs for the remaining claims instead of guessing from memory or snippets.');
|
|
449
|
+
}
|
|
450
|
+
if (secondary.length === 0 && intensity === 'deep') {
|
|
451
|
+
nextActions.push('Run targeted searches, then open the strongest sources.');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
intensity,
|
|
457
|
+
adequate,
|
|
458
|
+
requiredPrimarySources,
|
|
459
|
+
requiredSecondarySources,
|
|
460
|
+
requiredTargetCoverage,
|
|
461
|
+
requiredPrimaryTargetCoverage,
|
|
462
|
+
primarySourceCount: primary.length,
|
|
463
|
+
secondarySourceCount: secondary.length,
|
|
464
|
+
targets,
|
|
465
|
+
coveredTargets,
|
|
466
|
+
primaryCoveredTargets,
|
|
467
|
+
uncoveredTargets,
|
|
468
|
+
primaryUncoveredTargets,
|
|
469
|
+
missing,
|
|
470
|
+
nextActions,
|
|
471
|
+
reason: adequate
|
|
472
|
+
? (intensity === 'none'
|
|
473
|
+
? 'No research burden for this run.'
|
|
474
|
+
: 'Research evidence covers the requested targets.')
|
|
475
|
+
: clampResearchText(missing.join(' ') || 'Research evidence is still incomplete.', 320),
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function formatResearchAdequacyGuidance(assessment = null) {
|
|
480
|
+
if (!assessment || assessment.adequate !== false) return '';
|
|
481
|
+
const lines = [
|
|
482
|
+
'Research self-check: evidence is still incomplete for this run.',
|
|
483
|
+
assessment.reason ? `Gap: ${assessment.reason}` : '',
|
|
484
|
+
assessment.nextActions?.length
|
|
485
|
+
? `Next safe steps:\n- ${assessment.nextActions.join('\n- ')}`
|
|
486
|
+
: '',
|
|
487
|
+
'Do not complete with memory, guesses, or a partial comparison while these gaps remain. Gather the missing evidence first, or return a blocker that names exactly what could not be verified.',
|
|
488
|
+
];
|
|
489
|
+
return lines.filter(Boolean).join('\n');
|
|
490
|
+
}
|
|
491
|
+
|
|
175
492
|
function isSubstantiveProgressEvidence(item = {}) {
|
|
176
493
|
if (!isSubstantiveProgressToolName(item.toolName)) return false;
|
|
177
494
|
if (item.evidenceSource === 'messaging') return false;
|
|
@@ -246,12 +563,16 @@ function buildAutonomousRecoveryContext({ err, toolExecutions = [], tools = [],
|
|
|
246
563
|
module.exports = {
|
|
247
564
|
classifyToolExecution,
|
|
248
565
|
deriveEvidenceSource,
|
|
566
|
+
assessResearchAdequacy,
|
|
567
|
+
formatResearchAdequacyGuidance,
|
|
249
568
|
gatheredNewEvidence,
|
|
250
569
|
isSubstantiveProgressEvidence,
|
|
251
570
|
isSubstantiveProgressToolName,
|
|
571
|
+
resolveResearchIntensity,
|
|
252
572
|
summarizeProgressToolExecutions,
|
|
253
573
|
summarizeToolExecutions,
|
|
254
574
|
summarizeAvailableTools,
|
|
255
575
|
inferToolFailureMessage,
|
|
256
576
|
buildAutonomousRecoveryContext,
|
|
577
|
+
resolveDeclaredToolAccess,
|
|
257
578
|
};
|