neoagent 3.2.1-beta.1 → 3.2.1-beta.11

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 (198) 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 +654 -90
  4. package/flutter_app/lib/main_chat.dart +118 -739
  5. package/flutter_app/lib/main_controller.dart +157 -24
  6. package/flutter_app/lib/main_integrations.dart +607 -8
  7. package/flutter_app/lib/main_models.dart +7 -2
  8. package/flutter_app/lib/main_operations.dart +334 -370
  9. package/flutter_app/lib/main_security.dart +266 -112
  10. package/flutter_app/lib/main_settings.dart +342 -3
  11. package/flutter_app/lib/main_shared.dart +14 -11
  12. package/flutter_app/lib/src/backend_client.dart +97 -0
  13. package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
  14. package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
  15. package/flutter_app/windows/runner/flutter_window.cpp +143 -32
  16. package/landing/index.html +3 -1
  17. package/lib/manager.js +106 -89
  18. package/lib/schema_migrations.js +115 -13
  19. package/package.json +30 -15
  20. package/runtime/paths.js +49 -5
  21. package/server/db/database.js +2 -2
  22. package/server/guest-agent.cli.package.json +13 -0
  23. package/server/guest_agent.js +85 -40
  24. package/server/http/middleware.js +24 -0
  25. package/server/http/routes.js +12 -6
  26. package/server/public/.last_build_id +1 -1
  27. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  28. package/server/public/flutter_bootstrap.js +2 -2
  29. package/server/public/main.dart.js +81930 -80509
  30. package/server/routes/admin.js +1 -1
  31. package/server/routes/android.js +30 -34
  32. package/server/routes/behavior.js +80 -0
  33. package/server/routes/browser.js +23 -15
  34. package/server/routes/desktop.js +18 -1
  35. package/server/routes/integrations.js +107 -1
  36. package/server/routes/memory.js +1 -0
  37. package/server/routes/settings.js +20 -5
  38. package/server/routes/social_reach.js +12 -3
  39. package/server/routes/social_video.js +4 -0
  40. package/server/services/agents/manager.js +1 -1
  41. package/server/services/ai/capabilityHealth.js +62 -96
  42. package/server/services/ai/compaction.js +7 -2
  43. package/server/services/ai/history.js +45 -6
  44. package/server/services/ai/integrated_tools/http_request.js +8 -0
  45. package/server/services/ai/loop/agent_engine_core.js +452 -176
  46. package/server/services/ai/loop/blank_recovery.js +5 -4
  47. package/server/services/ai/loop/callbacks.js +1 -0
  48. package/server/services/ai/loop/completion_judge.js +291 -8
  49. package/server/services/ai/loop/conversation_loop.js +515 -342
  50. package/server/services/ai/loop/messaging_delivery.js +176 -57
  51. package/server/services/ai/loop/model_call_guard.js +91 -0
  52. package/server/services/ai/loop/model_io.js +20 -45
  53. package/server/services/ai/loop/progress_classification.js +2 -0
  54. package/server/services/ai/loop/tool_dispatch.js +19 -8
  55. package/server/services/ai/loopPolicy.js +48 -21
  56. package/server/services/ai/messagingFallback.js +17 -17
  57. package/server/services/ai/model_discovery.js +227 -0
  58. package/server/services/ai/model_failure_cache.js +108 -0
  59. package/server/services/ai/model_identity.js +71 -0
  60. package/server/services/ai/models.js +68 -163
  61. package/server/services/ai/providerRetry.js +17 -59
  62. package/server/services/ai/provider_selector.js +166 -0
  63. package/server/services/ai/providers/anthropic.js +2 -2
  64. package/server/services/ai/providers/claudeCode.js +21 -33
  65. package/server/services/ai/providers/githubCopilot.js +41 -20
  66. package/server/services/ai/providers/google.js +135 -97
  67. package/server/services/ai/providers/grok.js +4 -3
  68. package/server/services/ai/providers/grokOauth.js +19 -27
  69. package/server/services/ai/providers/nvidia.js +10 -5
  70. package/server/services/ai/providers/ollama.js +111 -84
  71. package/server/services/ai/providers/ollama_stream.js +142 -0
  72. package/server/services/ai/providers/openai.js +39 -5
  73. package/server/services/ai/providers/openaiCodex.js +11 -4
  74. package/server/services/ai/providers/openrouter.js +29 -7
  75. package/server/services/ai/providers/provider_error.js +36 -0
  76. package/server/services/ai/settings.js +26 -2
  77. package/server/services/ai/systemPrompt.js +32 -123
  78. package/server/services/ai/taskAnalysis.js +104 -11
  79. package/server/services/ai/toolEvidence.js +256 -29
  80. package/server/services/ai/tools.js +248 -117
  81. package/server/services/android/controller.js +770 -237
  82. package/server/services/android/process.js +140 -0
  83. package/server/services/android/sdk_download.js +143 -0
  84. package/server/services/android/uia.js +6 -5
  85. package/server/services/artifacts/store.js +24 -0
  86. package/server/services/behavior/config.js +251 -0
  87. package/server/services/behavior/defaults.js +68 -0
  88. package/server/services/behavior/delivery.js +176 -0
  89. package/server/services/behavior/index.js +43 -0
  90. package/server/services/behavior/model_client.js +35 -0
  91. package/server/services/behavior/modules/agent_identity.js +37 -0
  92. package/server/services/behavior/modules/channel_style.js +28 -0
  93. package/server/services/behavior/modules/index.js +29 -0
  94. package/server/services/behavior/modules/norms.js +101 -0
  95. package/server/services/behavior/modules/persona.js +48 -0
  96. package/server/services/behavior/modules/persona_prompt.js +33 -0
  97. package/server/services/behavior/modules/social_memory.js +86 -0
  98. package/server/services/behavior/modules/social_observability.js +94 -0
  99. package/server/services/behavior/modules/social_signals.js +41 -0
  100. package/server/services/behavior/modules/theory_of_mind.js +110 -0
  101. package/server/services/behavior/modules/turn_taking.js +237 -0
  102. package/server/services/behavior/pipeline.js +285 -0
  103. package/server/services/behavior/registry.js +78 -0
  104. package/server/services/behavior/signals.js +107 -0
  105. package/server/services/behavior/state.js +99 -0
  106. package/server/services/behavior/system_prompt.js +75 -0
  107. package/server/services/browser/controller.js +843 -385
  108. package/server/services/browser/extension/gateway.js +40 -16
  109. package/server/services/browser/extension/protocol.js +15 -1
  110. package/server/services/browser/extension/provider.js +71 -47
  111. package/server/services/browser/extension/registry.js +155 -34
  112. package/server/services/cli/executor.js +62 -9
  113. package/server/services/credentials/bitwarden_cli.js +322 -0
  114. package/server/services/credentials/broker.js +594 -0
  115. package/server/services/desktop/gateway.js +41 -4
  116. package/server/services/desktop/protocol.js +3 -0
  117. package/server/services/desktop/provider.js +39 -42
  118. package/server/services/desktop/registry.js +137 -52
  119. package/server/services/integrations/bitwarden/constants.js +14 -0
  120. package/server/services/integrations/bitwarden/provider.js +197 -0
  121. package/server/services/integrations/bitwarden/snapshot.js +65 -0
  122. package/server/services/integrations/figma/provider.js +78 -12
  123. package/server/services/integrations/github/common.js +11 -6
  124. package/server/services/integrations/github/provider.js +52 -53
  125. package/server/services/integrations/google/provider.js +55 -19
  126. package/server/services/integrations/home_assistant/network.js +17 -20
  127. package/server/services/integrations/home_assistant/provider.js +7 -5
  128. package/server/services/integrations/home_assistant/tools.js +17 -5
  129. package/server/services/integrations/http.js +51 -0
  130. package/server/services/integrations/manager.js +159 -53
  131. package/server/services/integrations/microsoft/provider.js +80 -13
  132. package/server/services/integrations/neoarchive/provider.js +55 -29
  133. package/server/services/integrations/neorecall/client.js +17 -10
  134. package/server/services/integrations/neorecall/provider.js +20 -11
  135. package/server/services/integrations/notion/provider.js +16 -13
  136. package/server/services/integrations/oauth_provider.js +115 -51
  137. package/server/services/integrations/registry.js +2 -0
  138. package/server/services/integrations/slack/provider.js +98 -9
  139. package/server/services/integrations/spotify/provider.js +67 -71
  140. package/server/services/integrations/trello/provider.js +21 -7
  141. package/server/services/integrations/weather/provider.js +18 -12
  142. package/server/services/integrations/whatsapp/provider.js +76 -16
  143. package/server/services/manager.js +110 -1
  144. package/server/services/memory/embedding_index.js +20 -8
  145. package/server/services/memory/embeddings.js +151 -90
  146. package/server/services/memory/ingestion.js +50 -9
  147. package/server/services/memory/ingestion_documents.js +13 -3
  148. package/server/services/memory/manager.js +66 -52
  149. package/server/services/messaging/access_policy.js +10 -6
  150. package/server/services/messaging/automation.js +240 -34
  151. package/server/services/messaging/discord.js +11 -1
  152. package/server/services/messaging/formatting_guides.js +5 -4
  153. package/server/services/messaging/http_platforms.js +37 -16
  154. package/server/services/messaging/inbound_queue.js +143 -28
  155. package/server/services/messaging/inbound_store.js +257 -0
  156. package/server/services/messaging/manager.js +344 -51
  157. package/server/services/messaging/telegram.js +10 -1
  158. package/server/services/messaging/typing_keepalive.js +5 -2
  159. package/server/services/messaging/whatsapp.js +33 -14
  160. package/server/services/network/http.js +210 -0
  161. package/server/services/network/safe_request.js +307 -0
  162. package/server/services/runtime/backends/local-vm.js +227 -67
  163. package/server/services/runtime/docker-vm-manager.js +9 -0
  164. package/server/services/runtime/guest_bootstrap.js +30 -4
  165. package/server/services/runtime/guest_image.js +43 -12
  166. package/server/services/runtime/manager.js +77 -23
  167. package/server/services/runtime/validation.js +7 -6
  168. package/server/services/security/tool_categories.js +6 -0
  169. package/server/services/social_reach/channels/github.js +10 -4
  170. package/server/services/social_reach/channels/reddit.js +4 -4
  171. package/server/services/social_reach/channels/rss.js +2 -2
  172. package/server/services/social_reach/channels/social_video.js +13 -8
  173. package/server/services/social_reach/channels/v2ex.js +21 -8
  174. package/server/services/social_reach/channels/x.js +2 -2
  175. package/server/services/social_reach/channels/xueqiu.js +5 -5
  176. package/server/services/social_reach/service.js +9 -6
  177. package/server/services/social_reach/utils.js +65 -14
  178. package/server/services/social_video/captions.js +2 -2
  179. package/server/services/social_video/service.js +343 -68
  180. package/server/services/tasks/integration_runtime.js +18 -8
  181. package/server/services/tasks/runtime.js +39 -4
  182. package/server/services/voice/agentBridge.js +17 -4
  183. package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
  184. package/server/services/voice/liveSession.js +31 -0
  185. package/server/services/voice/message.js +1 -1
  186. package/server/services/voice/openaiSpeech.js +33 -8
  187. package/server/services/voice/providers.js +233 -151
  188. package/server/services/voice/runtime.js +2 -2
  189. package/server/services/voice/runtimeManager.js +118 -20
  190. package/server/services/voice/turnRunner.js +6 -0
  191. package/server/services/wearable/firmware_manifest.js +51 -13
  192. package/server/services/wearable/service.js +1 -0
  193. package/server/utils/abort.js +96 -0
  194. package/server/utils/cloud-security.js +110 -3
  195. package/server/utils/files.js +31 -0
  196. package/server/utils/image_payload.js +95 -0
  197. package/server/utils/logger.js +19 -0
  198. 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 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,11 +195,204 @@ 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
+ function clampResearchText(value, maxChars = 220) {
204
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
205
+ if (!text) return '';
206
+ if (text.length <= maxChars) return text;
207
+ return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
208
+ }
209
+
210
+ function uniqueResearchTokens(values = [], { limit = 12 } = {}) {
211
+ const seen = new Set();
212
+ const tokens = [];
213
+ for (const value of values) {
214
+ const token = String(value || '').replace(/\s+/g, ' ').trim();
215
+ if (!token) continue;
216
+ const key = token.toLowerCase();
217
+ if (seen.has(key)) continue;
218
+ seen.add(key);
219
+ tokens.push(token);
220
+ if (tokens.length >= limit) break;
221
+ }
222
+ return tokens;
223
+ }
224
+
225
+ // Research targets come from structured task analysis / goal contract only.
226
+ // Do not NLP-extract entity names from free text.
227
+ function collectResearchTargets(analysis = null, goalContext = null) {
228
+ return uniqueResearchTokens([
229
+ ...(Array.isArray(analysis?.research_targets) ? analysis.research_targets : []),
230
+ ...(Array.isArray(goalContext?.researchTargets) ? goalContext.researchTargets : []),
231
+ ], { limit: 8 });
232
+ }
233
+
234
+ function resolveResearchIntensity(analysis = null, goalContext = null) {
235
+ const declaredDepth = String(
236
+ analysis?.research_depth
237
+ || analysis?.researchDepth
238
+ || '',
239
+ ).trim().toLowerCase();
240
+ const declaredTargets = collectResearchTargets(analysis, goalContext);
241
+ if (
242
+ ['none', 'light', 'deep'].includes(declaredDepth)
243
+ && !(declaredDepth === 'none' && declaredTargets.length > 0)
244
+ ) {
245
+ return declaredDepth;
246
+ }
247
+
248
+ const mode = String(analysis?.mode || '').trim().toLowerCase();
249
+ const complexity = String(
250
+ goalContext?.effectiveComplexity
251
+ || analysis?.complexity
252
+ || '',
253
+ ).trim().toLowerCase();
254
+ const autonomyLevel = String(
255
+ goalContext?.effectiveAutonomyLevel
256
+ || analysis?.autonomy_level
257
+ || '',
258
+ ).trim().toLowerCase();
259
+ const completionConfidence = String(
260
+ goalContext?.effectiveCompletionConfidence
261
+ || analysis?.completion_confidence_required
262
+ || '',
263
+ ).trim().toLowerCase();
264
+ const verificationNeed = String(analysis?.verification_need || '').trim().toLowerCase();
265
+ const freshnessRisk = String(analysis?.freshness_risk || '').trim().toLowerCase();
266
+ const planningDepth = String(analysis?.planning_depth || '').trim().toLowerCase();
267
+ const targets = declaredTargets;
268
+
269
+ if (mode === 'direct_answer' && verificationNeed === 'none' && freshnessRisk === 'none') {
270
+ return 'none';
271
+ }
272
+
273
+ const needsExternalEvidence = (
274
+ targets.length > 0
275
+ || freshnessRisk === 'possible'
276
+ || freshnessRisk === 'high'
277
+ );
278
+ if (!needsExternalEvidence) {
279
+ return 'none';
280
+ }
281
+
282
+ const deepSignals = [
283
+ mode === 'plan_execute',
284
+ complexity === 'complex',
285
+ autonomyLevel === 'high',
286
+ completionConfidence === 'high',
287
+ verificationNeed === 'required',
288
+ freshnessRisk === 'high',
289
+ planningDepth === 'deep',
290
+ targets.length >= 2,
291
+ ].filter(Boolean).length;
292
+
293
+ if (deepSignals >= 2 || targets.length >= 2 || verificationNeed === 'required' || freshnessRisk === 'high') {
294
+ return 'deep';
295
+ }
296
+
297
+ return 'light';
298
+ }
299
+
300
+ function isSuccessfulResearchExecution(item = {}) {
301
+ if (!item || item.ok !== true) return false;
302
+ if (!isSubstantiveProgressToolName(item.toolName)) return false;
303
+ if (item.evidenceSource === 'messaging') return false;
304
+ return Boolean(item.evidenceRelevant || item.dependsOnOutput || item.stateChanged);
305
+ }
306
+
307
+ function selectResearchEvidenceCandidates(toolExecutions = [], maxItems = 80) {
308
+ const candidates = (Array.isArray(toolExecutions) ? toolExecutions : [])
309
+ .map((execution, index) => ({ execution, evidenceIndex: index + 1 }))
310
+ .filter(({ execution }) => isSuccessfulResearchExecution(execution));
311
+ if (candidates.length <= maxItems) return candidates;
312
+
313
+ const firstCount = Math.floor(maxItems / 4);
314
+ return [
315
+ ...candidates.slice(0, firstCount),
316
+ ...candidates.slice(-(maxItems - firstCount)),
317
+ ];
318
+ }
319
+
320
+ function summarizeResearchEvidenceCatalog(toolExecutions = [], maxItems = 80) {
321
+ return selectResearchEvidenceCandidates(toolExecutions, maxItems)
322
+ .map(({ execution, evidenceIndex }) => (
323
+ `E${evidenceIndex}. ${execution.toolName} [${execution.evidenceSource || 'tool'}] :: ${clampRunContext(execution.summary || '', 160)}`
324
+ ))
325
+ .join('\n');
326
+ }
327
+
328
+ function researchExecutionSignature(execution = {}) {
329
+ return JSON.stringify([
330
+ String(execution.toolName || '').trim(),
331
+ execution.input && typeof execution.input === 'object' ? execution.input : {},
332
+ ]);
333
+ }
334
+
335
+ function assessResearchAdequacy({
336
+ analysis = null,
337
+ goalContext = null,
338
+ toolExecutions = [],
339
+ } = {}) {
340
+ const intensity = resolveResearchIntensity(analysis, goalContext);
341
+ const targets = collectResearchTargets(analysis, goalContext);
342
+
343
+ const successful = (Array.isArray(toolExecutions) ? toolExecutions : [])
344
+ .filter(isSuccessfulResearchExecution);
345
+ const uniqueEvidenceCandidateCount = new Set(
346
+ successful.map((item) => researchExecutionSignature(item)),
347
+ ).size;
348
+ const structurallyReady = intensity === 'none' || uniqueEvidenceCandidateCount > 0;
349
+ const missing = structurallyReady
350
+ ? []
351
+ : ['No successful source-bearing tool evidence is available for semantic review.'];
352
+ const nextActions = [];
353
+ if (!structurallyReady) {
354
+ nextActions.push('Gather source-backed evidence before requesting completion.');
355
+ } else if (intensity !== 'none') {
356
+ nextActions.push('Have the completion judge map each requested target to concrete evidence entries and assess source quality.');
357
+ }
358
+
359
+ return {
360
+ intensity,
361
+ adequate: structurallyReady,
362
+ structurallyReady,
363
+ semanticReviewRequired: intensity !== 'none',
364
+ evidenceCandidateCount: successful.length,
365
+ uniqueEvidenceCandidateCount,
366
+ targets,
367
+ coveredTargets: [],
368
+ primaryCoveredTargets: [],
369
+ uncoveredTargets: intensity === 'none' ? [] : targets,
370
+ primaryUncoveredTargets: intensity === 'none' ? [] : targets,
371
+ missing,
372
+ nextActions,
373
+ reason: structurallyReady
374
+ ? (intensity === 'none'
375
+ ? 'No research burden for this run.'
376
+ : 'Source candidates exist; the AI completion judge must still validate target coverage and source quality.')
377
+ : clampResearchText(missing.join(' ') || 'Research evidence is still incomplete.', 320),
378
+ };
379
+ }
380
+
381
+ function formatResearchAdequacyGuidance(assessment = null) {
382
+ if (!assessment || assessment.intensity === 'none') return '';
383
+ const lines = [
384
+ assessment.structurallyReady
385
+ ? 'Research self-check: source candidates exist, but semantic target coverage is not inferred from tool names, arguments, or token overlap.'
386
+ : 'Research self-check: no successful source evidence is available yet.',
387
+ assessment.reason ? `Gap: ${assessment.reason}` : '',
388
+ assessment.nextActions?.length
389
+ ? `Next safe steps:\n- ${assessment.nextActions.join('\n- ')}`
390
+ : '',
391
+ 'The completion judge must explicitly map every requested target to supporting evidence and judge whether that evidence is primary, secondary, or merely contextual.',
392
+ ];
393
+ return lines.filter(Boolean).join('\n');
394
+ }
395
+
175
396
  function isSubstantiveProgressEvidence(item = {}) {
176
397
  if (!isSubstantiveProgressToolName(item.toolName)) return false;
177
398
  if (item.evidenceSource === 'messaging') return false;
@@ -246,12 +467,18 @@ function buildAutonomousRecoveryContext({ err, toolExecutions = [], tools = [],
246
467
  module.exports = {
247
468
  classifyToolExecution,
248
469
  deriveEvidenceSource,
470
+ assessResearchAdequacy,
471
+ formatResearchAdequacyGuidance,
249
472
  gatheredNewEvidence,
250
473
  isSubstantiveProgressEvidence,
251
474
  isSubstantiveProgressToolName,
475
+ resolveResearchIntensity,
476
+ selectResearchEvidenceCandidates,
477
+ summarizeResearchEvidenceCatalog,
252
478
  summarizeProgressToolExecutions,
253
479
  summarizeToolExecutions,
254
480
  summarizeAvailableTools,
255
481
  inferToolFailureMessage,
256
482
  buildAutonomousRecoveryContext,
483
+ resolveDeclaredToolAccess,
257
484
  };