@jshookmcp/jshook 0.2.8 → 0.3.0
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/README.md +36 -5
- package/README.zh.md +36 -5
- package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-CqGDXmfc.mjs} +160 -54
- package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
- package/dist/ConsoleMonitor-DykL3IAw.mjs +2269 -0
- package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-ETyy0xyo.mjs} +1 -1
- package/dist/DetailedDataManager-HT49OrvF.mjs +217 -0
- package/dist/EventBus-DFKvADm3.mjs +141 -0
- package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +153 -0
- package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-BDMsY2Dz.mjs} +27 -13
- package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-BN4UQWnX.mjs} +1 -1
- package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
- package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-DruMgsgj.mjs} +21 -21
- package/dist/HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs +566 -0
- package/dist/InstrumentationSession-DLH0vd-z.mjs +244 -0
- package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CMtviNW_.mjs} +3 -3
- package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-ITgb_NMi.mjs} +81 -78
- package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-CiL7Z3ey.mjs} +50 -21
- package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +20 -56
- package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
- package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-DMQ44gen.mjs} +16 -16
- package/dist/PageController-BPJNqqBN.mjs +431 -0
- package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
- package/dist/PrerequisiteError-TuyZIs6n.mjs +20 -0
- package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
- package/dist/ResponseBuilder-CJXWmWNw.mjs +143 -0
- package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +269 -0
- package/dist/ScriptManager-ZuWD-0Jg.mjs +3003 -0
- package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-D-z0umeT.mjs} +2 -2
- package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
- package/dist/ToolCatalog-5OJdMiF0.mjs +582 -0
- package/dist/ToolError-jh9whhMd.mjs +15 -0
- package/dist/ToolProbe-DbCFGyrg.mjs +45 -0
- package/dist/ToolRegistry-B9krbTtI.mjs +180 -0
- package/dist/ToolRouter.policy-BGDAGyeH.mjs +344 -0
- package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
- package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-C2kjj0ze.mjs} +19 -13
- package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-CKrGOTpo.mjs} +3 -3
- package/dist/WorkflowEngine-DJ6M4opp.mjs +569 -0
- package/dist/analysis-BHeJW2Nb.mjs +1234 -0
- package/dist/antidebug-BRKeyt27.mjs +1081 -0
- package/dist/artifactRetention-CPXkUJXp.mjs +598 -0
- package/dist/artifacts-DkfosXH3.mjs +59 -0
- package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
- package/dist/betterSqlite3-DLSBZodi.mjs +74 -0
- package/dist/binary-instrument--V3MAhJ4.mjs +971 -0
- package/dist/bind-helpers-ClV34xdn.mjs +42 -0
- package/dist/boringssl-inspector-Bo_LOLaS.mjs +180 -0
- package/dist/browser-Dx3_S2cG.mjs +4369 -0
- package/dist/capabilities-CcHlvWgK.mjs +33 -0
- package/dist/concurrency-Drev_Vz9.mjs +41 -0
- package/dist/{constants-CCvsN80K.mjs → constants-CDZLOoVv.mjs} +105 -48
- package/dist/coordination-DgItD9DL.mjs +259 -0
- package/dist/debugger-RS3RSAqs.mjs +1288 -0
- package/dist/definitions-BEoYofW5.mjs +47 -0
- package/dist/definitions-BRaefg3u.mjs +365 -0
- package/dist/definitions-BbkvZkiv.mjs +96 -0
- package/dist/definitions-BtWSHJ3o.mjs +17 -0
- package/dist/definitions-C1gCHO0i.mjs +43 -0
- package/dist/definitions-CDOg_b-l.mjs +138 -0
- package/dist/definitions-CVPD9hzZ.mjs +54 -0
- package/dist/definitions-Cea8Lgl7.mjs +94 -0
- package/dist/definitions-DAgIyjxM.mjs +10 -0
- package/dist/definitions-DJA27nsL.mjs +66 -0
- package/dist/definitions-DKPFU3LW.mjs +25 -0
- package/dist/definitions-DPRpZQ96.mjs +47 -0
- package/dist/definitions-DUE5gmdn.mjs +18 -0
- package/dist/definitions-DYVjOtxa.mjs +26 -0
- package/dist/definitions-DcYLVLCo.mjs +37 -0
- package/dist/definitions-Pp5LI2H4.mjs +27 -0
- package/dist/definitions-j9KdHVNR.mjs +14 -0
- package/dist/definitions-uzkjBwa7.mjs +258 -0
- package/dist/definitions-va-AnLuQ.mjs +28 -0
- package/dist/encoding-DJeqHmpd.mjs +1079 -0
- package/dist/evidence-graph-bridge-DcYizFk2.mjs +136 -0
- package/dist/{factory-CibqTNC8.mjs → factory-C90tBff6.mjs} +41 -56
- package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
- package/dist/graphql-CoHrhweh.mjs +1197 -0
- package/dist/handlers-4jmR0nMs.mjs +898 -0
- package/dist/handlers-BAHPxcch.mjs +789 -0
- package/dist/handlers-BOs9b907.mjs +2600 -0
- package/dist/handlers-BWXEy6ef.mjs +917 -0
- package/dist/handlers-Bndn6QvE.mjs +111 -0
- package/dist/handlers-BqC4bD4s.mjs +681 -0
- package/dist/handlers-BtYq60bM2.mjs +276 -0
- package/dist/handlers-BzgcB4iv.mjs +799 -0
- package/dist/handlers-CRyRWj2b.mjs +859 -0
- package/dist/handlers-CVv2H1uq.mjs +592 -0
- package/dist/handlers-Dl5a7JS4.mjs +572 -0
- package/dist/handlers-Dx2d7jt7.mjs +2537 -0
- package/dist/handlers-Dz9PYsCa.mjs +2805 -0
- package/dist/handlers-HujRKC3b.mjs +661 -0
- package/dist/handlers.impl-XWXkQfyi.mjs +807 -0
- package/dist/hooks-B1B8NRHL.mjs +898 -0
- package/dist/index.mjs +491 -259
- package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
- package/dist/maintenance-PRMkLVRW.mjs +835 -0
- package/dist/manifest-67Bok-Si.mjs +58 -0
- package/dist/manifest-6lNTMZAB2.mjs +87 -0
- package/dist/manifest-B2duEHiH.mjs +90 -0
- package/dist/manifest-B6EY9Vm8.mjs +57 -0
- package/dist/manifest-B6nKSbyY.mjs +95 -0
- package/dist/manifest-BL8AQNPF.mjs +106 -0
- package/dist/manifest-BSZvJJmV.mjs +47 -0
- package/dist/manifest-BU7qzUyX.mjs +418 -0
- package/dist/manifest-Bl62e8WK.mjs +49 -0
- package/dist/manifest-Bo5cXjdt.mjs +82 -0
- package/dist/manifest-BpS4gtUK.mjs +1347 -0
- package/dist/manifest-Bv65_e2W.mjs +101 -0
- package/dist/manifest-BytNIF4Z.mjs +117 -0
- package/dist/manifest-C-xtsjS3.mjs +81 -0
- package/dist/manifest-CDYl7OhA.mjs +66 -0
- package/dist/manifest-CRZ3xmkD.mjs +61 -0
- package/dist/manifest-CoW6u4Tp.mjs +132 -0
- package/dist/manifest-Cq5zN_8A.mjs +50 -0
- package/dist/manifest-D7YZM_2e.mjs +194 -0
- package/dist/manifest-DE_VrAeQ.mjs +314 -0
- package/dist/manifest-DGsXSCpT.mjs +39 -0
- package/dist/manifest-DJ2vfEuW.mjs +156 -0
- package/dist/manifest-DPXDYhEu.mjs +80 -0
- package/dist/manifest-Dd4fQb0a.mjs +322 -0
- package/dist/manifest-Deq6opGg.mjs +223 -0
- package/dist/manifest-DfJTafJK.mjs +37 -0
- package/dist/manifest-DgOdgN_j.mjs +50 -0
- package/dist/manifest-DlbMW4v4.mjs +47 -0
- package/dist/manifest-DmVfbH0w.mjs +374 -0
- package/dist/manifest-Dog6Ddjr.mjs +109 -0
- package/dist/manifest-DvgU5FWb.mjs +58 -0
- package/dist/manifest-HsfDBs7j.mjs +50 -0
- package/dist/manifest-I8oQHvCG.mjs +186 -0
- package/dist/manifest-NvH_a-av.mjs +786 -0
- package/dist/manifest-cEJU1v0Z.mjs +129 -0
- package/dist/manifest-wOl5XLB12.mjs +112 -0
- package/dist/modules-tZozf0LQ.mjs +10635 -0
- package/dist/mojo-ipc-DXNEXEqb.mjs +640 -0
- package/dist/network-CPVvwvFg.mjs +3852 -0
- package/dist/{artifacts-BbdOMET5.mjs → outputPaths-um7lCRY3.mjs} +219 -216
- package/dist/parse-args-B4cY5Vx5.mjs +39 -0
- package/dist/platform-CYeFoTWp.mjs +2161 -0
- package/dist/process-BTbgcVc6.mjs +1306 -0
- package/dist/proxy-r8YN6nP1.mjs +192 -0
- package/dist/registry-Bl8ZQW61.mjs +34 -0
- package/dist/response-CWhh2aLo.mjs +34 -0
- package/dist/server/plugin-api.mjs +2 -2
- package/dist/shared-state-board-BoZnSoj-.mjs +586 -0
- package/dist/sourcemap-BIDHUVXy.mjs +934 -0
- package/dist/ssrf-policy-Dsqd-DTX.mjs +166 -0
- package/dist/streaming-Dal6utPp.mjs +725 -0
- package/dist/tool-builder-BHJp32mV.mjs +186 -0
- package/dist/transform-DRVgGG90.mjs +1011 -0
- package/dist/types-Bx92KJfT.mjs +4 -0
- package/dist/wasm-BYx5UOeG.mjs +1044 -0
- package/dist/webcrack-Be0_FccV.mjs +747 -0
- package/dist/workflow-BpuKEtvn.mjs +725 -0
- package/package.json +82 -49
- package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
- package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
- package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
- package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
- package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
- package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-nnMvEohD.mjs} +0 -0
- package/dist/{types-BBjOqye-.mjs → types-DDBWs9UP.mjs} +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as logger } from "./logger-
|
|
3
|
-
import { $t as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
3
|
+
import { $t as PREDICTIVE_CONFIDENCE_THRESHOLD, An as SEARCH_CACHE_VECTOR_WEIGHT_TOLERANCE, Bn as SEARCH_RECENCY_WINDOW_MS, Ct as MCP_HTTP_KEEPALIVE_TIMEOUT_MS, Dn as SEARCH_AFFINITY_TOP_N, En as SEARCH_AFFINITY_BOOST_FACTOR, Fn as SEARCH_PARAM_TOKEN_WEIGHT, Gn as SEARCH_TIER_PENALTY, Hn as SEARCH_RRF_K, In as SEARCH_PREFIX_MATCH_MULTIPLIER, Jn as SEARCH_TIER_PENALTY_WORKFLOW, Kn as SEARCH_TIER_PENALTY_FULL, Ln as SEARCH_QUERY_CACHE_CAPACITY, M as COMPOUND_LONG_WINDOW_MS, Mn as SEARCH_DOMAIN_HUB_BOOST_MULTIPLIER, Nn as SEARCH_DOMAIN_HUB_THRESHOLD, On as SEARCH_BM25_B, Pn as SEARCH_EXACT_NAME_MATCH_MULTIPLIER, Qn as SEARCH_VECTOR_COSINE_WEIGHT, Rn as SEARCH_RECENCY_MAX_BOOST, St as MCP_HTTP_HEADERS_TIMEOUT_MS, Tn as SEARCH_AFFINITY_BASE_WEIGHT, Un as SEARCH_RRF_RESCALE_FACTOR, Vn as SEARCH_RRF_BM25_BLEND, Wn as SEARCH_SYNONYM_EXPANSION_LIMIT, Xn as SEARCH_TRIGRAM_WEIGHT, Yn as SEARCH_TRIGRAM_THRESHOLD, Zn as SEARCH_VECTOR_BM25_SKIP_THRESHOLD, a as ACTIVATION_TTL_MINUTES, ar as SEARCH_WORKFLOW_DOMAIN_BOOST_MULTIPLIER, bt as MCP_COMPACT_SCHEMA, dn as RUNTIME_ERROR_WINDOW_MS, dt as HTTP_RATE_LIMIT_MAX_IPS, en as PREDICTIVE_DECAY_FACTOR, er as SEARCH_VECTOR_ENABLED, h as AUTOPRUNE_MANUAL_INACTIVITY_MS, i as ACTIVATION_EVENT_HISTORY_MAX, j as COMPOUND_EVENT_WINDOW_MS, jn as SEARCH_COVERAGE_PRECISION_FACTOR, kn as SEARCH_BM25_K1, m as AUTOPRUNE_CHECK_INTERVAL_MS, n as ACTIVATION_COMPOUND_EVAL_EVERY, nn as PREDICTIVE_MAX_SECOND_ORDER_KEYS, nr as SEARCH_VECTOR_LEARN_TOP_N, or as SHUTDOWN_TIMEOUT_MS, p as AUTOPRUNE_AUTO_INACTIVITY_MS, qn as SEARCH_TIER_PENALTY_SEARCH, r as ACTIVATION_COOLDOWN_MS, rr as SEARCH_VECTOR_LEARN_UP, t as ACTIVATION_BOOST_WINDOW_MS, tn as PREDICTIVE_MAX_HISTORY, tr as SEARCH_VECTOR_LEARN_DOWN, un as RUNTIME_ERROR_THRESHOLD, ut as HTTP_CLEANUP_INTERVAL_MS, wt as MCP_HTTP_REQUEST_TIMEOUT_MS, xt as MCP_HTTP_FORCE_CLOSE_TIMEOUT_MS, zn as SEARCH_RECENCY_TRACKER_MAX } from "./constants-CDZLOoVv.mjs";
|
|
4
|
+
import { a as TokenBudgetManager, i as UnifiedCacheManager, n as getArtifactRetentionConfig, r as startArtifactRetentionScheduler, t as cleanupArtifacts } from "./artifactRetention-CPXkUJXp.mjs";
|
|
5
|
+
import { t as DetailedDataManager } from "./DetailedDataManager-HT49OrvF.mjs";
|
|
6
|
+
import { r as asTextResponse, t as asErrorResponse } from "./response-CWhh2aLo.mjs";
|
|
7
|
+
import { a as getToolsForProfile, c as ensureAllDomainsLoaded, d as getAllKnownDomains, f as getAllManifests, h as getLoaderMetadata, i as getToolsByDomains, l as ensureDomainLoaded, m as initRegistry, n as getProfileDomains, o as parseToolDomains, p as getAllRegistrations, r as getToolDomain, s as buildHandlerMapFromRegistry, t as allTools, u as getAllDomains } from "./ToolCatalog-5OJdMiF0.mjs";
|
|
8
|
+
import { t as ToolError } from "./ToolError-jh9whhMd.mjs";
|
|
9
|
+
import { t as ProcessRegistry } from "./ProcessRegistry-zGg12QbE.mjs";
|
|
10
|
+
import { c as getConfig, d as DEFAULT_SEARCH_CONFIG, u as validateConfig } from "./outputPaths-um7lCRY3.mjs";
|
|
11
|
+
import { i as reloadExtensions, n as ensureWorkflowsLoaded, r as listExtensions } from "./ExtensionManager-BDMsY2Dz.mjs";
|
|
12
|
+
import { _ as isBrowserOrNetworkTask, a as buildWorkflowToolSequence, b as matchWorkflowRoute, c as getAvailableToolNames, d as getToolDomainFromContext, f as getToolInputSchema, g as detectWorkflowIntent, h as validateToolNameArray, i as buildWorkflowRouteRecommendation, l as getRoutingState, m as normalizeToolName, n as buildRouteMatchMetadata, o as getEffectivePrerequisites, p as isToolActive, r as buildStatelessComputeRecommendations, s as rerankResultsForContext, t as buildPresetRecommendations, u as getToolDescription, v as isMaintenanceTask, y as isStatelessComputeTask } from "./ToolRouter.policy-BGDAGyeH.mjs";
|
|
13
|
+
import { i as generateAntiDebugBypass, r as getInjectionInstructions } from "./HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs";
|
|
14
|
+
import { n as createServerEventBus } from "./EventBus-DFKvADm3.mjs";
|
|
7
15
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
16
|
import { CompleteRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
9
17
|
import { promises } from "fs";
|
|
10
18
|
import { join } from "path";
|
|
11
19
|
import { createHash } from "crypto";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import { ZodError, z } from "zod";
|
|
14
|
-
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
15
|
-
import { readFileSync as readFileSync$1 } from "node:fs";
|
|
16
20
|
import { createServer } from "node:http";
|
|
21
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
17
22
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
23
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
19
24
|
import { Worker } from "worker_threads";
|
|
25
|
+
import { ZodError, z } from "zod";
|
|
26
|
+
import { readFileSync as readFileSync$1 } from "node:fs";
|
|
27
|
+
import { dirname, resolve } from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
20
30
|
//#region src/utils/cache.ts
|
|
21
31
|
var CacheManager = class {
|
|
22
32
|
config;
|
|
@@ -298,7 +308,7 @@ var ToolExecutionRouter = class {
|
|
|
298
308
|
* appends `_tabContext` metadata to responses so the LLM always knows which page
|
|
299
309
|
* it is operating on, preventing silent context drift.
|
|
300
310
|
*
|
|
301
|
-
* Additionally, tracks consecutive identical tool calls and injects `
|
|
311
|
+
* Additionally, tracks consecutive identical tool calls and injects `repeatWarning`
|
|
302
312
|
* when the same tool is called ≥ MAX_CONSECUTIVE_REPEATS times in a row, helping
|
|
303
313
|
* break LLM degeneration loops (e.g. stealth_inject called 5× instead of page_navigate).
|
|
304
314
|
*/
|
|
@@ -336,15 +346,21 @@ const REPEAT_GUARD_EXCLUDES = new Set([
|
|
|
336
346
|
const DOMAIN_ALTERNATIVES = new Map([
|
|
337
347
|
["stealth", [
|
|
338
348
|
"page_navigate",
|
|
339
|
-
"
|
|
340
|
-
"stealth_verify"
|
|
349
|
+
"page_evaluate",
|
|
350
|
+
"stealth_verify",
|
|
351
|
+
"page_screenshot"
|
|
341
352
|
]],
|
|
342
353
|
["page", [
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
354
|
+
"browser_jsdom_parse",
|
|
355
|
+
"js_bundle_search",
|
|
356
|
+
"network_get_requests",
|
|
357
|
+
"page_screenshot"
|
|
358
|
+
]],
|
|
359
|
+
["console", [
|
|
360
|
+
"page_evaluate",
|
|
361
|
+
"console_get_logs",
|
|
362
|
+
"page_screenshot"
|
|
346
363
|
]],
|
|
347
|
-
["console", ["page_evaluate", "page_screenshot"]],
|
|
348
364
|
["network", ["network_get_requests", "page_navigate"]],
|
|
349
365
|
["captcha", ["captcha_wait", "page_screenshot"]],
|
|
350
366
|
["ai_hook", [
|
|
@@ -352,8 +368,8 @@ const DOMAIN_ALTERNATIVES = new Map([
|
|
|
352
368
|
"page_evaluate",
|
|
353
369
|
"ai_hook_inject"
|
|
354
370
|
]],
|
|
355
|
-
["instrumentation", ["
|
|
356
|
-
["evidence", ["
|
|
371
|
+
["instrumentation", ["instrumentation_session", "instrumentation_artifact"]],
|
|
372
|
+
["evidence", ["evidence_query", "evidence_chain"]]
|
|
357
373
|
]);
|
|
358
374
|
var ToolCallContextGuard = class {
|
|
359
375
|
/** Memoize prefix-match results — tool names repeat heavily across calls. */
|
|
@@ -404,7 +420,7 @@ var ToolCallContextGuard = class {
|
|
|
404
420
|
if (!this.isContextSensitive(toolName)) return response;
|
|
405
421
|
if (response.isError) return response;
|
|
406
422
|
const provider = this.getProvider();
|
|
407
|
-
if (!provider) return response;
|
|
423
|
+
if (!provider || typeof provider.getContextMeta !== "function") return response;
|
|
408
424
|
const meta = provider.getContextMeta();
|
|
409
425
|
if (!meta.pageId && meta.tabIndex === null) return response;
|
|
410
426
|
const content = response.content;
|
|
@@ -446,12 +462,17 @@ var ToolCallContextGuard = class {
|
|
|
446
462
|
return raw.replace(/\}\s*$/, `,"_tabContext":${compactJson}}`);
|
|
447
463
|
}
|
|
448
464
|
/**
|
|
449
|
-
* Inject a `
|
|
465
|
+
* Inject a `repeatWarning` into the response when a tool call loop is detected.
|
|
450
466
|
* Splices into JSON text content if possible, or appends a new text entry.
|
|
451
467
|
*/
|
|
452
468
|
injectRepeatWarning(toolName, response) {
|
|
453
469
|
const prefix = toolName.split("_")[0] ?? "";
|
|
454
|
-
const suggestions = (
|
|
470
|
+
const suggestions = (toolName === "page_evaluate" ? [
|
|
471
|
+
"browser_jsdom_parse",
|
|
472
|
+
"js_bundle_search",
|
|
473
|
+
"network_get_requests",
|
|
474
|
+
"page_screenshot"
|
|
475
|
+
] : DOMAIN_ALTERNATIVES.get(prefix) ?? ["page_navigate", "page_evaluate"]).filter((t) => t !== toolName);
|
|
455
476
|
const warning = {
|
|
456
477
|
detected: true,
|
|
457
478
|
consecutiveCount: this.consecutiveCount,
|
|
@@ -467,7 +488,7 @@ var ToolCallContextGuard = class {
|
|
|
467
488
|
try {
|
|
468
489
|
const parsed = JSON.parse(raw);
|
|
469
490
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
470
|
-
parsed.
|
|
491
|
+
parsed.repeatWarning = warning;
|
|
471
492
|
firstText.text = JSON.stringify(parsed, null, 2);
|
|
472
493
|
return;
|
|
473
494
|
}
|
|
@@ -475,7 +496,7 @@ var ToolCallContextGuard = class {
|
|
|
475
496
|
}
|
|
476
497
|
content.push({
|
|
477
498
|
type: "text",
|
|
478
|
-
text: JSON.stringify({
|
|
499
|
+
text: JSON.stringify({ repeatWarning: warning }, null, 2)
|
|
479
500
|
});
|
|
480
501
|
}
|
|
481
502
|
};
|
|
@@ -1024,6 +1045,16 @@ async function closeServer(ctx) {
|
|
|
1024
1045
|
if (ctx.shutdownStarted) return ctx.shutdownPromise ?? Promise.resolve();
|
|
1025
1046
|
ctx.shutdownStarted = true;
|
|
1026
1047
|
ctx.shutdownPromise = (async () => {
|
|
1048
|
+
const getInst = typeof ctx.getDomainInstance === "function" ? ctx.getDomainInstance.bind(ctx) : null;
|
|
1049
|
+
if (getInst) {
|
|
1050
|
+
const scheduler = getInst("snapshotScheduler");
|
|
1051
|
+
if (scheduler) try {
|
|
1052
|
+
await scheduler.flushAll();
|
|
1053
|
+
scheduler.dispose();
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
logger.warn("snapshot flush on shutdown failed:", error);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1027
1058
|
for (const [, entry] of ctx.domainTtlEntries) clearTimeout(entry.timer);
|
|
1028
1059
|
ctx.domainTtlEntries.clear();
|
|
1029
1060
|
ctx.detailedData.shutdown();
|
|
@@ -1762,7 +1793,8 @@ var EmbeddingEngine = class {
|
|
|
1762
1793
|
}
|
|
1763
1794
|
ensureWorker() {
|
|
1764
1795
|
if (this.worker) return;
|
|
1765
|
-
|
|
1796
|
+
const workerPath = new URL("./EmbeddingWorker.js", import.meta.url);
|
|
1797
|
+
this.worker = new Worker(workerPath);
|
|
1766
1798
|
if (typeof this.worker.unref === "function") this.worker.unref();
|
|
1767
1799
|
ProcessRegistry.register(this.worker);
|
|
1768
1800
|
this.worker.on("message", (msg) => {
|
|
@@ -1872,7 +1904,7 @@ var TrigramIndex = class TrigramIndex {
|
|
|
1872
1904
|
* @param threshold Minimum Jaccard similarity to include in results (default 0.3).
|
|
1873
1905
|
* @returns Map from document index to similarity score (0–1).
|
|
1874
1906
|
*/
|
|
1875
|
-
search(query, threshold =
|
|
1907
|
+
search(query, threshold = SEARCH_TRIGRAM_THRESHOLD) {
|
|
1876
1908
|
const queryTrigrams = TrigramIndex.extractTrigrams(query);
|
|
1877
1909
|
if (queryTrigrams.size === 0) return /* @__PURE__ */ new Map();
|
|
1878
1910
|
const results = /* @__PURE__ */ new Map();
|
|
@@ -2029,6 +2061,13 @@ const PARAM_DESCRIPTION_STOP_WORDS = new Set([
|
|
|
2029
2061
|
* and tool metadata for indexing.
|
|
2030
2062
|
*/
|
|
2031
2063
|
const QueryNormalizer = {
|
|
2064
|
+
/**
|
|
2065
|
+
* Extract parameter names and description tokens from a tool's inputSchema.
|
|
2066
|
+
* Handles both simple flat properties and nested object properties.
|
|
2067
|
+
*
|
|
2068
|
+
* @param inputSchema The tool's inputSchema object
|
|
2069
|
+
* @returns Array of normalized tokens from parameter names and descriptions
|
|
2070
|
+
*/
|
|
2032
2071
|
extractParamTokens(inputSchema) {
|
|
2033
2072
|
const tokens = [];
|
|
2034
2073
|
if (!inputSchema || typeof inputSchema !== "object") return tokens;
|
|
@@ -2050,6 +2089,13 @@ const QueryNormalizer = {
|
|
|
2050
2089
|
}
|
|
2051
2090
|
return tokens;
|
|
2052
2091
|
},
|
|
2092
|
+
/**
|
|
2093
|
+
* Extract a short description from a full tool description.
|
|
2094
|
+
* Takes the first sentence and truncates to 120 characters if needed.
|
|
2095
|
+
*
|
|
2096
|
+
* @param description The full tool description
|
|
2097
|
+
* @returns A shortened description suitable for display
|
|
2098
|
+
*/
|
|
2053
2099
|
extractShortDescription(description) {
|
|
2054
2100
|
if (!description) return "";
|
|
2055
2101
|
const firstSentence = description.match(/^[^.!?\n]+[.!?]?/);
|
|
@@ -2059,28 +2105,27 @@ const QueryNormalizer = {
|
|
|
2059
2105
|
}
|
|
2060
2106
|
return description.length > 120 ? description.slice(0, 117) + "..." : description;
|
|
2061
2107
|
},
|
|
2108
|
+
/**
|
|
2109
|
+
* Check if a query contains CJK (Chinese, Japanese, Korean) characters.
|
|
2110
|
+
*
|
|
2111
|
+
* @param query The search query
|
|
2112
|
+
* @returns true if the query contains CJK characters
|
|
2113
|
+
*/
|
|
2062
2114
|
containsCJK(query) {
|
|
2063
2115
|
return /[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(query);
|
|
2064
2116
|
},
|
|
2117
|
+
/**
|
|
2118
|
+
* Normalize a tool name for comparison (lowercase, underscores to hyphens/spaces).
|
|
2119
|
+
*
|
|
2120
|
+
* @param query The search query
|
|
2121
|
+
* @returns Normalized query string
|
|
2122
|
+
*/
|
|
2065
2123
|
normalizeToolName(query) {
|
|
2066
2124
|
return query.toLowerCase().replace(/[\s-]+/g, "_");
|
|
2067
2125
|
}
|
|
2068
2126
|
};
|
|
2069
2127
|
//#endregion
|
|
2070
|
-
//#region src/server/search/
|
|
2071
|
-
/**
|
|
2072
|
-
* Hybrid BM25 + RRF multi-signal tool search engine for progressive tool discovery.
|
|
2073
|
-
*
|
|
2074
|
-
* Enhancements:
|
|
2075
|
-
* - BM25 keyword scoring with synonym-expanded queries
|
|
2076
|
-
* - TF-IDF cosine similarity as an independent RRF signal
|
|
2077
|
-
* - Trigram fuzzy matching for typo tolerance
|
|
2078
|
-
* - RRF (Reciprocal Rank Fusion) combining all signals
|
|
2079
|
-
* - Tool affinity graph with prefix-group expansion (§4.1.4 dependency hull)
|
|
2080
|
-
* - Query category adaptive domain weights (§4.1.3 task-type encoding)
|
|
2081
|
-
* - Parameter name indexing for schema-aware search
|
|
2082
|
-
* - LRU query result cache (§4.3 CSAPC cross-session caching)
|
|
2083
|
-
*/
|
|
2128
|
+
//#region src/server/search/ToolSearchEngine.helpers.ts
|
|
2084
2129
|
function findDelimitedIndex(haystack, needle, wordChar) {
|
|
2085
2130
|
if (!needle) return -1;
|
|
2086
2131
|
let idx = haystack.indexOf(needle);
|
|
@@ -2094,6 +2139,93 @@ function findDelimitedIndex(haystack, needle, wordChar) {
|
|
|
2094
2139
|
}
|
|
2095
2140
|
return -1;
|
|
2096
2141
|
}
|
|
2142
|
+
function buildAffinityGraph(docs) {
|
|
2143
|
+
const graph = /* @__PURE__ */ new Map();
|
|
2144
|
+
const prefixGroups = /* @__PURE__ */ new Map();
|
|
2145
|
+
for (let i = 0; i < docs.length; i++) {
|
|
2146
|
+
const name = docs[i].name;
|
|
2147
|
+
const underscoreIdx = name.indexOf("_");
|
|
2148
|
+
if (underscoreIdx <= 0) continue;
|
|
2149
|
+
const prefix = name.slice(0, underscoreIdx);
|
|
2150
|
+
const group = prefixGroups.get(prefix) ?? [];
|
|
2151
|
+
group.push(i);
|
|
2152
|
+
prefixGroups.set(prefix, group);
|
|
2153
|
+
}
|
|
2154
|
+
for (const [, members] of prefixGroups) {
|
|
2155
|
+
if (members.length < 2 || members.length > 15) continue;
|
|
2156
|
+
const affinityWeight = SEARCH_AFFINITY_BASE_WEIGHT / Math.sqrt(members.length);
|
|
2157
|
+
for (const src of members) {
|
|
2158
|
+
const edges = graph.get(src) ?? [];
|
|
2159
|
+
for (const dst of members) if (dst !== src) edges.push({
|
|
2160
|
+
docIndex: dst,
|
|
2161
|
+
weight: affinityWeight
|
|
2162
|
+
});
|
|
2163
|
+
graph.set(src, edges);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return graph;
|
|
2167
|
+
}
|
|
2168
|
+
function rankByScores(scores) {
|
|
2169
|
+
const entries = [];
|
|
2170
|
+
for (let i = 0; i < scores.length; i++) if (scores[i] > 0) entries.push({
|
|
2171
|
+
idx: i,
|
|
2172
|
+
score: scores[i]
|
|
2173
|
+
});
|
|
2174
|
+
entries.sort((a, b) => b.score - a.score);
|
|
2175
|
+
const ranked = /* @__PURE__ */ new Map();
|
|
2176
|
+
for (let rank = 0; rank < entries.length; rank++) ranked.set(entries[rank].idx, rank);
|
|
2177
|
+
return ranked;
|
|
2178
|
+
}
|
|
2179
|
+
function rankByMap(scoreMap) {
|
|
2180
|
+
const entries = [...scoreMap.entries()].toSorted((a, b) => b[1] - a[1]);
|
|
2181
|
+
const ranked = /* @__PURE__ */ new Map();
|
|
2182
|
+
for (let rank = 0; rank < entries.length; rank++) ranked.set(entries[rank][0], rank);
|
|
2183
|
+
return ranked;
|
|
2184
|
+
}
|
|
2185
|
+
function blendRrfIntoScores(scores, rrfScores) {
|
|
2186
|
+
for (let i = 0; i < scores.length; i++) {
|
|
2187
|
+
const rrfScore = rrfScores[i];
|
|
2188
|
+
if (rrfScore <= 0) continue;
|
|
2189
|
+
const bm25Original = scores[i];
|
|
2190
|
+
const rrfRescaled = rrfScore * SEARCH_RRF_RESCALE_FACTOR;
|
|
2191
|
+
const blend = SEARCH_RRF_BM25_BLEND;
|
|
2192
|
+
scores[i] = Math.max(bm25Original, rrfRescaled * blend) + rrfRescaled * blend;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
function applyGraphExpansionToScores(options) {
|
|
2196
|
+
const { scores, docs, affinityGraph } = options;
|
|
2197
|
+
const scored = [];
|
|
2198
|
+
for (let i = 0; i < scores.length; i++) if (scores[i] > 0) scored.push({
|
|
2199
|
+
idx: i,
|
|
2200
|
+
score: scores[i]
|
|
2201
|
+
});
|
|
2202
|
+
if (scored.length === 0) return;
|
|
2203
|
+
scored.sort((a, b) => b.score - a.score);
|
|
2204
|
+
if (affinityGraph.size > 0) {
|
|
2205
|
+
const limit = Math.min(SEARCH_AFFINITY_TOP_N, scored.length);
|
|
2206
|
+
for (let rank = 0; rank < limit; rank++) {
|
|
2207
|
+
const { idx, score } = scored[rank];
|
|
2208
|
+
const neighbors = affinityGraph.get(idx);
|
|
2209
|
+
if (!neighbors) continue;
|
|
2210
|
+
const rankDecay = 1 / (1 + rank);
|
|
2211
|
+
for (const { docIndex, weight } of neighbors) if (scores[docIndex] > 0) scores[docIndex] += score * weight * rankDecay * SEARCH_AFFINITY_BOOST_FACTOR;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
if (SEARCH_DOMAIN_HUB_THRESHOLD > 0 && scored.length >= SEARCH_DOMAIN_HUB_THRESHOLD) {
|
|
2215
|
+
const top10 = scored.slice(0, 10);
|
|
2216
|
+
const domainCounts = /* @__PURE__ */ new Map();
|
|
2217
|
+
for (const { idx } of top10) {
|
|
2218
|
+
const domain = docs[idx].domain;
|
|
2219
|
+
if (domain) domainCounts.set(domain, (domainCounts.get(domain) ?? 0) + 1);
|
|
2220
|
+
}
|
|
2221
|
+
for (const [domain, count] of domainCounts) {
|
|
2222
|
+
if (count < SEARCH_DOMAIN_HUB_THRESHOLD) continue;
|
|
2223
|
+
for (let i = 0; i < docs.length; i++) if (scores[i] > 0 && docs[i].domain === domain) scores[i] *= SEARCH_DOMAIN_HUB_BOOST_MULTIPLIER;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
//#endregion
|
|
2228
|
+
//#region src/server/search/ToolSearchEngineImpl.ts
|
|
2097
2229
|
function buildSearchCacheKey(query, topK, visibleDomains) {
|
|
2098
2230
|
if (!visibleDomains || visibleDomains.size === 0) return `${query}\0${topK}`;
|
|
2099
2231
|
return `${query}\0${topK}\0${[...visibleDomains].toSorted().join("|")}`;
|
|
@@ -2134,8 +2266,6 @@ var ToolSearchEngine = class {
|
|
|
2134
2266
|
domainOverrides;
|
|
2135
2267
|
domainScoreMultipliers;
|
|
2136
2268
|
toolScoreMultipliers;
|
|
2137
|
-
/** IDF values per term, used for TF-IDF cosine computation. */
|
|
2138
|
-
idfMap;
|
|
2139
2269
|
/** Name → doc index for O(1) lookup during affinity expansion. */
|
|
2140
2270
|
docNameIndex = /* @__PURE__ */ new Map();
|
|
2141
2271
|
/** Prefix-group affinity graph: docIndex → neighbor edges. */
|
|
@@ -2164,7 +2294,8 @@ var ToolSearchEngine = class {
|
|
|
2164
2294
|
this.toolScoreMultipliers = toolScoreMultipliers;
|
|
2165
2295
|
this.docCount = source.length;
|
|
2166
2296
|
this.bm25Scorer = new BM25ScorerImpl(searchConfig);
|
|
2167
|
-
|
|
2297
|
+
const vectorEnabled = searchConfig?.vectorEnabled ?? SEARCH_VECTOR_ENABLED;
|
|
2298
|
+
this.embeddingEngine = vectorEnabled ? new EmbeddingEngine() : null;
|
|
2168
2299
|
this.feedbackTracker = new FeedbackTracker(searchConfig);
|
|
2169
2300
|
this.intentBoost = new IntentBoostImpl(searchConfig?.intentToolBoostRules);
|
|
2170
2301
|
let totalLength = 0;
|
|
@@ -2193,9 +2324,7 @@ var ToolSearchEngine = class {
|
|
|
2193
2324
|
length: allTokens.length,
|
|
2194
2325
|
nameTokens,
|
|
2195
2326
|
nameTokenSet,
|
|
2196
|
-
nameTokenCount: nameTokenSet.size
|
|
2197
|
-
tfidfWeights: /* @__PURE__ */ new Map(),
|
|
2198
|
-
tfidfMagnitude: 0
|
|
2327
|
+
nameTokenCount: nameTokenSet.size
|
|
2199
2328
|
};
|
|
2200
2329
|
this.docs.push(doc);
|
|
2201
2330
|
this.docNameIndex.set(tool.name, i);
|
|
@@ -2252,31 +2381,30 @@ var ToolSearchEngine = class {
|
|
|
2252
2381
|
}
|
|
2253
2382
|
this.avgDocLength = this.docCount > 0 ? totalLength / this.docCount : 1;
|
|
2254
2383
|
this.sortedKeys = [...this.invertedIndex.keys()].toSorted();
|
|
2255
|
-
|
|
2256
|
-
for (const [term, postings] of this.invertedIndex) idfMap.set(term, Math.log(1 + this.docCount / postings.length));
|
|
2257
|
-
this.idfMap = idfMap;
|
|
2258
|
-
for (let i = 0; i < this.docCount; i++) {
|
|
2259
|
-
const doc = this.docs[i];
|
|
2260
|
-
const rawTf = /* @__PURE__ */ new Map();
|
|
2261
|
-
for (const token of doc.tokens) rawTf.set(token, (rawTf.get(token) ?? 0) + 1);
|
|
2262
|
-
const tfidfWeights = /* @__PURE__ */ new Map();
|
|
2263
|
-
let magnitudeSq = 0;
|
|
2264
|
-
for (const [term, tf] of rawTf) {
|
|
2265
|
-
const idf = idfMap.get(term) ?? 0;
|
|
2266
|
-
const w = (1 + Math.log(tf)) * idf;
|
|
2267
|
-
tfidfWeights.set(term, w);
|
|
2268
|
-
magnitudeSq += w * w;
|
|
2269
|
-
}
|
|
2270
|
-
doc.tfidfWeights = tfidfWeights;
|
|
2271
|
-
doc.tfidfMagnitude = Math.sqrt(magnitudeSq);
|
|
2272
|
-
}
|
|
2273
|
-
this.affinityGraph = this.buildAffinityGraph();
|
|
2384
|
+
this.affinityGraph = buildAffinityGraph(this.docs);
|
|
2274
2385
|
this.trigramIndex = new TrigramIndex(this.docs.map((d) => d.name));
|
|
2275
2386
|
this.queryCache = new LRUCache(SEARCH_QUERY_CACHE_CAPACITY);
|
|
2276
2387
|
}
|
|
2277
|
-
async search(query, topK = 10, activeToolNames, visibleDomains) {
|
|
2278
|
-
|
|
2388
|
+
async search(query, topK = 10, activeToolNames, visibleDomains, profile) {
|
|
2389
|
+
let queryTokens = this.bm25Scorer.tokenise(query);
|
|
2279
2390
|
if (queryTokens.length === 0) return [];
|
|
2391
|
+
if (queryTokens.length > 6) {
|
|
2392
|
+
const inVocab = queryTokens.filter((t) => this.invertedIndex.has(t));
|
|
2393
|
+
if (inVocab.length >= 3) {
|
|
2394
|
+
const scored = inVocab.map((t) => {
|
|
2395
|
+
const df = this.invertedIndex.get(t).length;
|
|
2396
|
+
return {
|
|
2397
|
+
token: t,
|
|
2398
|
+
idf: Math.log((this.docCount - df + .5) / (df + .5) + 1)
|
|
2399
|
+
};
|
|
2400
|
+
});
|
|
2401
|
+
scored.sort((a, b) => b.idf - a.idf);
|
|
2402
|
+
const kept = new Set(scored.slice(0, 6).map((s) => s.token));
|
|
2403
|
+
queryTokens = queryTokens.filter((t) => kept.has(t));
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
const synonymTokens = this.bm25Scorer.tokenise(queryTokens.join(" "), { expandSynonyms: true }).filter((t) => !queryTokens.includes(t));
|
|
2407
|
+
queryTokens.push(...synonymTokens);
|
|
2280
2408
|
const explicitToolMention = (() => {
|
|
2281
2409
|
const lower = query.toLowerCase();
|
|
2282
2410
|
if (!/(?:\b(?:call|use|run|invoke|execute)\b|调用|执行|使用|运行)/i.test(lower)) return null;
|
|
@@ -2347,11 +2475,10 @@ var ToolSearchEngine = class {
|
|
|
2347
2475
|
const toolMultiplier = this.toolScoreMultipliers?.get(doc.name) ?? 1;
|
|
2348
2476
|
if (toolMultiplier !== 1) scores[i] *= toolMultiplier;
|
|
2349
2477
|
}
|
|
2350
|
-
this.
|
|
2351
|
-
this.applyDomainHubExpansion(scores);
|
|
2478
|
+
this.applyGraphExpansion(scores);
|
|
2352
2479
|
this.applyIntentBonusBand(scores, intentToolBonuses);
|
|
2353
2480
|
this.applyRecencyBoost(scores);
|
|
2354
|
-
this.applyTierPenalty(scores, visibleDomains);
|
|
2481
|
+
this.applyTierPenalty(scores, visibleDomains, profile);
|
|
2355
2482
|
if (explicitToolMention) {
|
|
2356
2483
|
const explicitIdx = this.docNameIndex.get(explicitToolMention);
|
|
2357
2484
|
if (explicitIdx !== void 0) {
|
|
@@ -2421,9 +2548,9 @@ var ToolSearchEngine = class {
|
|
|
2421
2548
|
* tier. The penalty is a soft multiplier in [0, 1]; 1 disables the feature.
|
|
2422
2549
|
* Tools without a resolved domain are left untouched.
|
|
2423
2550
|
*/
|
|
2424
|
-
applyTierPenalty(scores, visibleDomains) {
|
|
2551
|
+
applyTierPenalty(scores, visibleDomains, profile) {
|
|
2425
2552
|
if (!visibleDomains || visibleDomains.size === 0) return;
|
|
2426
|
-
const penalty = SEARCH_TIER_PENALTY;
|
|
2553
|
+
const penalty = profile ? profile === "full" ? SEARCH_TIER_PENALTY_FULL : profile === "workflow" ? SEARCH_TIER_PENALTY_WORKFLOW : SEARCH_TIER_PENALTY_SEARCH : SEARCH_TIER_PENALTY;
|
|
2427
2554
|
if (penalty >= 1 || penalty <= 0) return;
|
|
2428
2555
|
for (let i = 0; i < this.docCount; i++) {
|
|
2429
2556
|
if (scores[i] <= 0) continue;
|
|
@@ -2496,96 +2623,41 @@ var ToolSearchEngine = class {
|
|
|
2496
2623
|
* independently contribute — a document with BM25=0 but high trigram
|
|
2497
2624
|
* similarity can still surface.
|
|
2498
2625
|
*/
|
|
2499
|
-
async applyRRFFusion(
|
|
2626
|
+
async applyRRFFusion(_queryTokens, query, scores) {
|
|
2500
2627
|
const k = SEARCH_RRF_K;
|
|
2501
2628
|
const trigramWeight = SEARCH_TRIGRAM_WEIGHT;
|
|
2502
|
-
const
|
|
2503
|
-
const
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2629
|
+
const bm25Ranked = rankByScores(scores);
|
|
2630
|
+
const trigramRanked = rankByMap(this.trigramIndex.search(query, SEARCH_TRIGRAM_THRESHOLD));
|
|
2631
|
+
let vectorScores;
|
|
2632
|
+
let vectorRanked;
|
|
2633
|
+
if (SEARCH_VECTOR_BM25_SKIP_THRESHOLD > 0 && bm25Ranked.size > 0) {
|
|
2634
|
+
const topBm25Idx = [...bm25Ranked.entries()].find(([, r]) => r === 0)?.[0];
|
|
2635
|
+
if ((topBm25Idx !== void 0 ? scores[topBm25Idx] : 0) >= SEARCH_VECTOR_BM25_SKIP_THRESHOLD) {
|
|
2636
|
+
vectorScores = /* @__PURE__ */ new Map();
|
|
2637
|
+
vectorRanked = /* @__PURE__ */ new Map();
|
|
2638
|
+
} else {
|
|
2639
|
+
vectorScores = await this.computeVectorCosineScores(query);
|
|
2640
|
+
vectorRanked = rankByMap(vectorScores);
|
|
2641
|
+
}
|
|
2642
|
+
} else {
|
|
2643
|
+
vectorScores = await this.computeVectorCosineScores(query);
|
|
2644
|
+
vectorRanked = rankByMap(vectorScores);
|
|
2514
2645
|
}
|
|
2646
|
+
const ranking = /* @__PURE__ */ new Map();
|
|
2647
|
+
for (const [docIdx, rank] of vectorRanked) ranking.set(this.docs[docIdx].name, rank);
|
|
2648
|
+
this.feedbackTracker.recordVectorRanking(ranking);
|
|
2649
|
+
const fusedRrfScores = new Float64Array(this.docCount);
|
|
2515
2650
|
for (let i = 0; i < this.docCount; i++) {
|
|
2516
2651
|
let rrfScore = 0;
|
|
2517
2652
|
const bm25Rank = bm25Ranked.get(i);
|
|
2518
2653
|
if (bm25Rank !== void 0) rrfScore += 1 / (k + bm25Rank);
|
|
2519
|
-
const cosineRank = cosineRanked.get(i);
|
|
2520
|
-
if (cosineRank !== void 0 && tfidfWeight > 0) rrfScore += tfidfWeight * (1 / (k + cosineRank));
|
|
2521
2654
|
const trigramRank = trigramRanked.get(i);
|
|
2522
2655
|
if (trigramRank !== void 0 && trigramWeight > 0) rrfScore += trigramWeight * (1 / (k + trigramRank));
|
|
2523
2656
|
const vectorRank = vectorRanked.get(i);
|
|
2524
2657
|
if (vectorRank !== void 0 && this.feedbackTracker.getVectorWeight() > 0) rrfScore += this.feedbackTracker.getVectorWeight() * (1 / (k + vectorRank));
|
|
2525
|
-
|
|
2526
|
-
const bm25Original = scores[i];
|
|
2527
|
-
const rrfRescaled = rrfScore * SEARCH_RRF_RESCALE_FACTOR;
|
|
2528
|
-
const blend = SEARCH_RRF_BM25_BLEND;
|
|
2529
|
-
scores[i] = Math.max(bm25Original, rrfRescaled * blend) + rrfRescaled * blend;
|
|
2530
|
-
}
|
|
2658
|
+
fusedRrfScores[i] = rrfScore;
|
|
2531
2659
|
}
|
|
2532
|
-
|
|
2533
|
-
/**
|
|
2534
|
-
* Compute raw TF-IDF cosine similarity scores for each document.
|
|
2535
|
-
* Returns Map<docIndex, cosineScore>.
|
|
2536
|
-
*/
|
|
2537
|
-
computeTfidfCosineScores(queryTokens) {
|
|
2538
|
-
const queryTf = /* @__PURE__ */ new Map();
|
|
2539
|
-
for (const token of queryTokens) queryTf.set(token, (queryTf.get(token) ?? 0) + 1);
|
|
2540
|
-
const queryWeights = /* @__PURE__ */ new Map();
|
|
2541
|
-
let queryMagSq = 0;
|
|
2542
|
-
for (const [term, tf] of queryTf) {
|
|
2543
|
-
const idf = this.idfMap.get(term) ?? 0;
|
|
2544
|
-
if (idf === 0) continue;
|
|
2545
|
-
const w = (1 + Math.log(tf)) * idf;
|
|
2546
|
-
queryWeights.set(term, w);
|
|
2547
|
-
queryMagSq += w * w;
|
|
2548
|
-
}
|
|
2549
|
-
if (queryMagSq === 0) return /* @__PURE__ */ new Map();
|
|
2550
|
-
const queryMagnitude = Math.sqrt(queryMagSq);
|
|
2551
|
-
const results = /* @__PURE__ */ new Map();
|
|
2552
|
-
for (let i = 0; i < this.docCount; i++) {
|
|
2553
|
-
const doc = this.docs[i];
|
|
2554
|
-
if (doc.tfidfMagnitude === 0) continue;
|
|
2555
|
-
let dot = 0;
|
|
2556
|
-
for (const [term, qw] of queryWeights) {
|
|
2557
|
-
const dw = doc.tfidfWeights.get(term);
|
|
2558
|
-
if (dw !== void 0) dot += qw * dw;
|
|
2559
|
-
}
|
|
2560
|
-
if (dot <= 0) continue;
|
|
2561
|
-
results.set(i, dot / (queryMagnitude * doc.tfidfMagnitude));
|
|
2562
|
-
}
|
|
2563
|
-
return results;
|
|
2564
|
-
}
|
|
2565
|
-
/**
|
|
2566
|
-
* Rank documents by Float64Array scores.
|
|
2567
|
-
* Returns Map<docIndex, rank> (0-based, lower = better).
|
|
2568
|
-
*/
|
|
2569
|
-
rankByScores(scores) {
|
|
2570
|
-
const entries = [];
|
|
2571
|
-
for (let i = 0; i < scores.length; i++) if (scores[i] > 0) entries.push({
|
|
2572
|
-
idx: i,
|
|
2573
|
-
score: scores[i]
|
|
2574
|
-
});
|
|
2575
|
-
entries.sort((a, b) => b.score - a.score);
|
|
2576
|
-
const ranked = /* @__PURE__ */ new Map();
|
|
2577
|
-
for (let rank = 0; rank < entries.length; rank++) ranked.set(entries[rank].idx, rank);
|
|
2578
|
-
return ranked;
|
|
2579
|
-
}
|
|
2580
|
-
/**
|
|
2581
|
-
* Rank documents by a score map.
|
|
2582
|
-
* Returns Map<docIndex, rank> (0-based, lower = better).
|
|
2583
|
-
*/
|
|
2584
|
-
rankByMap(scoreMap) {
|
|
2585
|
-
const entries = [...scoreMap.entries()].toSorted((a, b) => b[1] - a[1]);
|
|
2586
|
-
const ranked = /* @__PURE__ */ new Map();
|
|
2587
|
-
for (let rank = 0; rank < entries.length; rank++) ranked.set(entries[rank][0], rank);
|
|
2588
|
-
return ranked;
|
|
2660
|
+
blendRrfIntoScores(scores, fusedRrfScores);
|
|
2589
2661
|
}
|
|
2590
2662
|
/**
|
|
2591
2663
|
* Lazy-compute and cache tool description embeddings.
|
|
@@ -2674,82 +2746,16 @@ var ToolSearchEngine = class {
|
|
|
2674
2746
|
}
|
|
2675
2747
|
/**
|
|
2676
2748
|
* Build prefix-group affinity graph (§4.1.4 dependency hull).
|
|
2677
|
-
* Tools sharing a name prefix (e.g. "breakpoint_set"
|
|
2749
|
+
* Tools sharing a name prefix (e.g. legacy "breakpoint_set"/"breakpoint_list"
|
|
2750
|
+
* or unified families such as "memory_*")
|
|
2678
2751
|
* form an affinity group with mutual edges.
|
|
2679
2752
|
*/
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
const underscoreIdx = name.indexOf("_");
|
|
2686
|
-
if (underscoreIdx <= 0) continue;
|
|
2687
|
-
const prefix = name.slice(0, underscoreIdx);
|
|
2688
|
-
const group = prefixGroups.get(prefix) ?? [];
|
|
2689
|
-
group.push(i);
|
|
2690
|
-
prefixGroups.set(prefix, group);
|
|
2691
|
-
}
|
|
2692
|
-
for (const [, members] of prefixGroups) {
|
|
2693
|
-
if (members.length < 2 || members.length > 15) continue;
|
|
2694
|
-
const affinityWeight = SEARCH_AFFINITY_BASE_WEIGHT / Math.sqrt(members.length);
|
|
2695
|
-
for (const src of members) {
|
|
2696
|
-
const edges = graph.get(src) ?? [];
|
|
2697
|
-
for (const dst of members) if (dst !== src) edges.push({
|
|
2698
|
-
docIndex: dst,
|
|
2699
|
-
weight: affinityWeight
|
|
2700
|
-
});
|
|
2701
|
-
graph.set(src, edges);
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
return graph;
|
|
2705
|
-
}
|
|
2706
|
-
/**
|
|
2707
|
-
* Boost affinity neighbors of top results (§4.1.4).
|
|
2708
|
-
* For each of the top-N scored documents, add a fraction of its score
|
|
2709
|
-
* to its prefix-group neighbors, encouraging co-retrieval.
|
|
2710
|
-
*/
|
|
2711
|
-
applyAffinityExpansion(scores) {
|
|
2712
|
-
if (this.affinityGraph.size === 0) return;
|
|
2713
|
-
const topN = SEARCH_AFFINITY_TOP_N;
|
|
2714
|
-
const scored = [];
|
|
2715
|
-
for (let i = 0; i < this.docCount; i++) if (scores[i] > 0) scored.push({
|
|
2716
|
-
idx: i,
|
|
2717
|
-
score: scores[i]
|
|
2718
|
-
});
|
|
2719
|
-
scored.sort((a, b) => b.score - a.score);
|
|
2720
|
-
const boostFactor = SEARCH_AFFINITY_BOOST_FACTOR;
|
|
2721
|
-
const limit = Math.min(topN, scored.length);
|
|
2722
|
-
for (let rank = 0; rank < limit; rank++) {
|
|
2723
|
-
const { idx, score } = scored[rank];
|
|
2724
|
-
const neighbors = this.affinityGraph.get(idx);
|
|
2725
|
-
if (!neighbors) continue;
|
|
2726
|
-
const rankDecay = 1 / (1 + rank);
|
|
2727
|
-
for (const { docIndex, weight } of neighbors) if (scores[docIndex] > 0) scores[docIndex] += score * weight * rankDecay * boostFactor;
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
/**
|
|
2731
|
-
* Domain hub expansion (§4.1.4): when a domain is heavily represented
|
|
2732
|
-
* in top results, slightly boost remaining tools from that domain.
|
|
2733
|
-
*/
|
|
2734
|
-
applyDomainHubExpansion(scores) {
|
|
2735
|
-
const threshold = SEARCH_DOMAIN_HUB_THRESHOLD;
|
|
2736
|
-
if (threshold <= 0) return;
|
|
2737
|
-
const scored = [];
|
|
2738
|
-
for (let i = 0; i < this.docCount; i++) if (scores[i] > 0) scored.push({
|
|
2739
|
-
idx: i,
|
|
2740
|
-
score: scores[i]
|
|
2753
|
+
applyGraphExpansion(scores) {
|
|
2754
|
+
applyGraphExpansionToScores({
|
|
2755
|
+
scores,
|
|
2756
|
+
docs: this.docs,
|
|
2757
|
+
affinityGraph: this.affinityGraph
|
|
2741
2758
|
});
|
|
2742
|
-
if (scored.length < threshold) return;
|
|
2743
|
-
scored.sort((a, b) => b.score - a.score);
|
|
2744
|
-
const top10 = scored.slice(0, 10);
|
|
2745
|
-
const domainCounts = /* @__PURE__ */ new Map();
|
|
2746
|
-
for (const { idx } of top10) {
|
|
2747
|
-
const domain = this.docs[idx].domain;
|
|
2748
|
-
if (domain) domainCounts.set(domain, (domainCounts.get(domain) ?? 0) + 1);
|
|
2749
|
-
}
|
|
2750
|
-
for (const [domain, count] of domainCounts) if (count >= threshold) {
|
|
2751
|
-
for (let i = 0; i < this.docCount; i++) if (scores[i] > 0 && this.docs[i].domain === domain) scores[i] *= SEARCH_DOMAIN_HUB_BOOST_MULTIPLIER;
|
|
2752
|
-
}
|
|
2753
2759
|
}
|
|
2754
2760
|
};
|
|
2755
2761
|
//#endregion
|
|
@@ -2788,18 +2794,22 @@ function getVisibleDomainsForTier(ctx) {
|
|
|
2788
2794
|
}
|
|
2789
2795
|
return visible;
|
|
2790
2796
|
}
|
|
2797
|
+
function getBaseTier(ctx) {
|
|
2798
|
+
return ctx.baseTier;
|
|
2799
|
+
}
|
|
2791
2800
|
function getExtensionDomainMap(ctx) {
|
|
2792
2801
|
const map = /* @__PURE__ */ new Map();
|
|
2793
2802
|
for (const record of ctx.extensionToolsByName.values()) map.set(record.name, record.domain);
|
|
2794
2803
|
return map;
|
|
2795
2804
|
}
|
|
2796
|
-
function getCombinedTools(ctx) {
|
|
2805
|
+
async function getCombinedTools(ctx) {
|
|
2806
|
+
await ensureAllDomainsLoaded();
|
|
2797
2807
|
const tools = new Map(allTools.map((tool) => [tool.name, tool]));
|
|
2798
2808
|
for (const record of ctx.extensionToolsByName.values()) tools.set(record.name, record.tool);
|
|
2799
2809
|
return [...tools.values()];
|
|
2800
2810
|
}
|
|
2801
|
-
function getToolByName(ctx) {
|
|
2802
|
-
return new Map(getCombinedTools(ctx).map((tool) => [tool.name, tool]));
|
|
2811
|
+
async function getToolByName(ctx) {
|
|
2812
|
+
return new Map((await getCombinedTools(ctx)).map((tool) => [tool.name, tool]));
|
|
2803
2813
|
}
|
|
2804
2814
|
const searchEngineCache = /* @__PURE__ */ new WeakMap();
|
|
2805
2815
|
/**
|
|
@@ -2812,11 +2822,12 @@ function buildSearchSignature(ctx) {
|
|
|
2812
2822
|
extParts.sort();
|
|
2813
2823
|
return [ctx.extensionWorkflowRuntimeById.size, extParts.join("|")].join("::");
|
|
2814
2824
|
}
|
|
2815
|
-
function getSearchEngine(ctx) {
|
|
2825
|
+
async function getSearchEngine(ctx) {
|
|
2826
|
+
await ensureAllDomainsLoaded();
|
|
2816
2827
|
const signature = buildSearchSignature(ctx);
|
|
2817
2828
|
const cached = searchEngineCache.get(ctx);
|
|
2818
2829
|
if (cached?.signature === signature) return cached.engine;
|
|
2819
|
-
const tools = getCombinedTools(ctx);
|
|
2830
|
+
const tools = await getCombinedTools(ctx);
|
|
2820
2831
|
const extensionDomains = getExtensionDomainMap(ctx);
|
|
2821
2832
|
const domainScoreMultipliers = /* @__PURE__ */ new Map();
|
|
2822
2833
|
const toolScoreMultipliers = /* @__PURE__ */ new Map();
|
|
@@ -2833,14 +2844,14 @@ function getSearchEngine(ctx) {
|
|
|
2833
2844
|
});
|
|
2834
2845
|
return engine;
|
|
2835
2846
|
}
|
|
2836
|
-
/** Generate domain summary description
|
|
2847
|
+
/** Generate domain summary description. Uses metadata when not all domains are loaded. */
|
|
2837
2848
|
function buildDomainDescription(ctx) {
|
|
2838
2849
|
const groups = {};
|
|
2839
2850
|
for (const r of getAllRegistrations()) groups[r.domain] = (groups[r.domain] ?? 0) + 1;
|
|
2840
2851
|
for (const record of ctx.extensionToolsByName.values()) groups[record.domain] = (groups[record.domain] ?? 0) + 1;
|
|
2841
|
-
const
|
|
2842
|
-
const
|
|
2843
|
-
return `Search ${
|
|
2852
|
+
const loadedCount = getAllRegistrations().length;
|
|
2853
|
+
const extensionCount = ctx.extensionToolsByName.size;
|
|
2854
|
+
return `Search ${loadedCount + extensionCount} tools across ${Object.keys(groups).length} capability domains. This includes built-in tools plus any loaded plugin/workflow tools (${extensionCount} currently loaded). In search-tier sessions, call this before assuming a capability is unavailable. Use activate_tools for exact matches, activate_domain for an entire domain. Domains: ${Object.entries(groups).toSorted((a, b) => b[1] - a[1]).map(([domain, count]) => `${domain} (${count})`).join(" | ")}. Query tip: before searching, distill your intent into key concepts (action verb + target + domain). Pass distilled keywords, not full sentences — the search engine works on token matching, not semantic understanding.`;
|
|
2844
2855
|
}
|
|
2845
2856
|
//#endregion
|
|
2846
2857
|
//#region src/server/MCPServer.search.handlers.domain.ts
|
|
@@ -2853,12 +2864,13 @@ async function handleActivateDomain(ctx, args) {
|
|
|
2853
2864
|
success: false,
|
|
2854
2865
|
error: "domain must be a non-empty string"
|
|
2855
2866
|
}));
|
|
2856
|
-
const validDomains = new Set(
|
|
2867
|
+
const validDomains = new Set(getAllKnownDomains());
|
|
2857
2868
|
for (const record of ctx.extensionToolsByName.values()) validDomains.add(record.domain);
|
|
2858
2869
|
if (!validDomains.has(domain)) return asTextResponse(JSON.stringify({
|
|
2859
2870
|
success: false,
|
|
2860
2871
|
error: `Unknown domain "${domain}". Valid: ${[...validDomains].join(", ")}`
|
|
2861
2872
|
}));
|
|
2873
|
+
await ensureDomainLoaded(domain);
|
|
2862
2874
|
const ttlMinutes = typeof args.ttlMinutes === "number" ? args.ttlMinutes : ACTIVATION_TTL_MINUTES;
|
|
2863
2875
|
const domainTools = [...getToolsByDomains([domain]), ...[...ctx.extensionToolsByName.values()].filter((record) => record.domain === domain).map((record) => record.tool)];
|
|
2864
2876
|
const activeNames = getActiveToolNames(ctx);
|
|
@@ -3416,7 +3428,8 @@ var ActivationController = class {
|
|
|
3416
3428
|
this.boostRules = [...DEFAULT_BOOST_RULES, ...customRules].toSorted((a, b) => b.priority - a.priority);
|
|
3417
3429
|
this.compoundEngine = new CompoundConditionEngine();
|
|
3418
3430
|
this.predictiveBooster = new PredictiveBooster();
|
|
3419
|
-
|
|
3431
|
+
const baseDomains = new Set(getProfileDomains(ctx.baseTier));
|
|
3432
|
+
this.autoPruner = new AutoPruner(eventBus, baseDomains, (domain) => {
|
|
3420
3433
|
logger.info(`[ActivationController] Auto-pruning domain "${domain}"`);
|
|
3421
3434
|
});
|
|
3422
3435
|
this.subscribe();
|
|
@@ -3648,10 +3661,27 @@ function handleToolError(toolName, error) {
|
|
|
3648
3661
|
logger.error(`Tool execution failed: ${toolName}`, error);
|
|
3649
3662
|
throw new McpError(ErrorCode.InternalError, `Execution Failed in ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3650
3663
|
}
|
|
3664
|
+
function stripParamDescriptions(schema) {
|
|
3665
|
+
const clone = { ...schema };
|
|
3666
|
+
if (clone.properties) {
|
|
3667
|
+
const props = {};
|
|
3668
|
+
for (const [key, val] of Object.entries(clone.properties)) {
|
|
3669
|
+
const { description: _d, ...rest } = val;
|
|
3670
|
+
props[key] = stripParamDescriptions(rest);
|
|
3671
|
+
}
|
|
3672
|
+
clone.properties = props;
|
|
3673
|
+
}
|
|
3674
|
+
if (clone.items && typeof clone.items === "object") {
|
|
3675
|
+
const { description: _d, ...rest } = clone.items;
|
|
3676
|
+
clone.items = stripParamDescriptions(rest);
|
|
3677
|
+
}
|
|
3678
|
+
if (clone.additionalProperties && typeof clone.additionalProperties === "object" && !Array.isArray(clone.additionalProperties)) clone.additionalProperties = stripParamDescriptions(clone.additionalProperties);
|
|
3679
|
+
return clone;
|
|
3680
|
+
}
|
|
3651
3681
|
function registerSingleTool(ctx, toolDef) {
|
|
3652
3682
|
const builtTool = toolDef;
|
|
3653
|
-
if (builtTool.
|
|
3654
|
-
const rawSchema = toolDef.inputSchema;
|
|
3683
|
+
if (builtTool.autocompleteHandlers) ctx.toolAutocompleteHandlers.set(toolDef.name, builtTool.autocompleteHandlers);
|
|
3684
|
+
const rawSchema = MCP_COMPACT_SCHEMA && toolDef.inputSchema ? stripParamDescriptions(toolDef.inputSchema) : toolDef.inputSchema;
|
|
3655
3685
|
const shape = rawSchema && typeof rawSchema === "object" ? buildZodShape(rawSchema) : {};
|
|
3656
3686
|
const description = toolDef.description ?? toolDef.name;
|
|
3657
3687
|
if (Object.keys(shape).length > 0) {
|
|
@@ -3668,7 +3698,7 @@ function registerSingleTool(ctx, toolDef) {
|
|
|
3668
3698
|
}
|
|
3669
3699
|
});
|
|
3670
3700
|
if (builtTool.execution) {
|
|
3671
|
-
const sdkInternalMap = ctx.server.
|
|
3701
|
+
const sdkInternalMap = ctx.server.registeredTools;
|
|
3672
3702
|
if (sdkInternalMap && sdkInternalMap[toolDef.name]) sdkInternalMap[toolDef.name].execution = builtTool.execution;
|
|
3673
3703
|
}
|
|
3674
3704
|
return registeredTool;
|
|
@@ -3683,7 +3713,7 @@ function registerSingleTool(ctx, toolDef) {
|
|
|
3683
3713
|
}
|
|
3684
3714
|
});
|
|
3685
3715
|
if (builtTool.execution) {
|
|
3686
|
-
const sdkInternalMap = ctx.server.
|
|
3716
|
+
const sdkInternalMap = ctx.server.registeredTools;
|
|
3687
3717
|
if (sdkInternalMap && sdkInternalMap[toolDef.name]) sdkInternalMap[toolDef.name].execution = builtTool.execution;
|
|
3688
3718
|
}
|
|
3689
3719
|
return registeredTool;
|
|
@@ -3737,7 +3767,7 @@ async function routeToolRequest(request, ctx, searchEngine) {
|
|
|
3737
3767
|
const availableToolNames = getAvailableToolNames(ctx);
|
|
3738
3768
|
const routeMatch = matchWorkflowRoute(task, ctx);
|
|
3739
3769
|
let presetPlannedToolNames = null;
|
|
3740
|
-
const searchResults = await searchEngine.search(task, maxRecommendations * 2, activeNames, visibleDomains);
|
|
3770
|
+
const searchResults = await searchEngine.search(task, maxRecommendations * 2, activeNames, visibleDomains, getBaseTier(ctx));
|
|
3741
3771
|
let finalResults = [];
|
|
3742
3772
|
if (routeMatch?.workflow.route.kind === "preset") {
|
|
3743
3773
|
const presetTools = buildPresetRecommendations(routeMatch, routingState, ctx, availableToolNames);
|
|
@@ -3749,17 +3779,25 @@ async function routeToolRequest(request, ctx, searchEngine) {
|
|
|
3749
3779
|
const workflowResult = buildWorkflowRouteRecommendation(routeMatch, ctx);
|
|
3750
3780
|
finalResults = [workflowResult, ...searchResults.filter((result) => result.name !== workflowResult.name)];
|
|
3751
3781
|
} else if (workflow) {
|
|
3782
|
+
const statelessWorkflow = isStatelessComputeTask(task);
|
|
3752
3783
|
const workflowSequence = buildWorkflowToolSequence(workflow, routingState, availableToolNames);
|
|
3753
3784
|
const workflowTools = workflowSequence.map((name, index) => ({
|
|
3754
3785
|
name,
|
|
3755
3786
|
domain: getToolDomainFromContext(name, ctx),
|
|
3756
3787
|
shortDescription: searchResults.find((r) => r.name === name)?.shortDescription ?? ctx.extensionToolsByName.get(name)?.tool.description ?? "",
|
|
3757
|
-
score: workflow.priority - index * .01,
|
|
3788
|
+
score: (statelessWorkflow ? 90 : workflow.priority) - index * .01,
|
|
3758
3789
|
isActive: isToolActive(name, ctx)
|
|
3759
3790
|
}));
|
|
3760
3791
|
const workflowNames = new Set(workflowSequence);
|
|
3761
3792
|
const otherResults = searchResults.filter((result) => !workflowNames.has(result.name));
|
|
3762
3793
|
finalResults = [...workflowTools, ...otherResults];
|
|
3794
|
+
} else if (task && !isBrowserOrNetworkTask(task, workflow) && !isMaintenanceTask(task)) {
|
|
3795
|
+
const statelessRecommendations = buildStatelessComputeRecommendations(task, ctx, availableToolNames);
|
|
3796
|
+
if (statelessRecommendations.length > 0) {
|
|
3797
|
+
const statelessNames = new Set(statelessRecommendations.map((tool) => tool.name));
|
|
3798
|
+
const otherResults = searchResults.filter((result) => !statelessNames.has(result.name));
|
|
3799
|
+
finalResults = [...statelessRecommendations, ...otherResults];
|
|
3800
|
+
} else finalResults = [...searchResults];
|
|
3763
3801
|
} else finalResults = [...searchResults];
|
|
3764
3802
|
const dedupedResults = [];
|
|
3765
3803
|
const seenNames = /* @__PURE__ */ new Set();
|
|
@@ -3904,10 +3942,10 @@ async function routeToolRequest(request, ctx, searchEngine) {
|
|
|
3904
3942
|
async function handleSearchTools(ctx, args) {
|
|
3905
3943
|
const query = args.query;
|
|
3906
3944
|
const topK = args.top_k ?? 10;
|
|
3907
|
-
const engine = getSearchEngine(ctx);
|
|
3945
|
+
const engine = await getSearchEngine(ctx);
|
|
3908
3946
|
const activeNames = getActiveToolNames(ctx);
|
|
3909
3947
|
const visibleDomains = getVisibleDomainsForTier(ctx);
|
|
3910
|
-
const results = await engine.search(query, topK, activeNames, visibleDomains);
|
|
3948
|
+
const results = await engine.search(query, topK, activeNames, visibleDomains, getBaseTier(ctx));
|
|
3911
3949
|
const topResult = results[0];
|
|
3912
3950
|
const topTool = topResult ? describeTool(topResult.name, ctx) : null;
|
|
3913
3951
|
const topExampleArgs = topTool ? generateExampleArgs(topTool.inputSchema) : void 0;
|
|
@@ -3934,12 +3972,14 @@ async function handleSearchTools(ctx, args) {
|
|
|
3934
3972
|
exampleArgs: topExampleArgs,
|
|
3935
3973
|
description: `Call ${topResult.name} directly. Use describe_tool("${topResult.name}") only if you need the full schema.`
|
|
3936
3974
|
});
|
|
3975
|
+
const baseHint = "For guided tool discovery with workflow detection, use route_tool instead. Use activate_tools to enable specific tools, activate_domain for entire domains.";
|
|
3976
|
+
const refinementHint = results.length < 3 ? " Few results — try distilling your query to key concepts (e.g. \"hook fetch\" instead of \"how to intercept fetch requests\")." : "";
|
|
3937
3977
|
const response = {
|
|
3938
3978
|
query,
|
|
3939
3979
|
resultCount: results.length,
|
|
3940
3980
|
results,
|
|
3941
3981
|
nextActions: searchNextActions,
|
|
3942
|
-
hint:
|
|
3982
|
+
hint: baseHint + refinementHint
|
|
3943
3983
|
};
|
|
3944
3984
|
return asTextResponse(JSON.stringify(response, null, 2));
|
|
3945
3985
|
}
|
|
@@ -3957,6 +3997,7 @@ async function notifyToolListChanged(ctx, changed) {
|
|
|
3957
3997
|
}
|
|
3958
3998
|
}
|
|
3959
3999
|
async function activateToolNames(ctx, names) {
|
|
4000
|
+
await ensureAllDomainsLoaded();
|
|
3960
4001
|
const activeNames = getActiveToolNames(ctx);
|
|
3961
4002
|
const activated = [];
|
|
3962
4003
|
const alreadyActive = [];
|
|
@@ -3967,7 +4008,7 @@ async function activateToolNames(ctx, names) {
|
|
|
3967
4008
|
alreadyActive.push(name);
|
|
3968
4009
|
continue;
|
|
3969
4010
|
}
|
|
3970
|
-
const toolDef = getToolByName(ctx).get(name);
|
|
4011
|
+
const toolDef = (await getToolByName(ctx)).get(name);
|
|
3971
4012
|
if (!toolDef) {
|
|
3972
4013
|
notFound.push(name);
|
|
3973
4014
|
continue;
|
|
@@ -4067,7 +4108,7 @@ async function handleRouteTool(ctx, args) {
|
|
|
4067
4108
|
success: false,
|
|
4068
4109
|
error: "task must be a non-empty string"
|
|
4069
4110
|
}));
|
|
4070
|
-
const engine = getSearchEngine(ctx);
|
|
4111
|
+
const engine = await getSearchEngine(ctx);
|
|
4071
4112
|
const autoActivate = context?.autoActivate === true;
|
|
4072
4113
|
let response = populateCallCommands(await routeToolRequest({
|
|
4073
4114
|
task,
|
|
@@ -4142,6 +4183,7 @@ function buildCallToolMetadata(wasAutoActivated, activatedTools) {
|
|
|
4142
4183
|
};
|
|
4143
4184
|
}
|
|
4144
4185
|
function attachCallToolMetadata(response, metadata) {
|
|
4186
|
+
if (!response?.content || !Array.isArray(response.content)) return response;
|
|
4145
4187
|
return {
|
|
4146
4188
|
...response,
|
|
4147
4189
|
content: response.content.map((item) => {
|
|
@@ -4181,7 +4223,7 @@ async function handleCallTool(ctx, args) {
|
|
|
4181
4223
|
try {
|
|
4182
4224
|
const response = await ctx.executeToolWithTracking(name, toolArgs);
|
|
4183
4225
|
try {
|
|
4184
|
-
getSearchEngine(ctx).recordToolCallFeedback(name, "");
|
|
4226
|
+
(await getSearchEngine(ctx)).recordToolCallFeedback(name, "");
|
|
4185
4227
|
} catch {}
|
|
4186
4228
|
return attachCallToolMetadata(response, callMetadata);
|
|
4187
4229
|
} catch (error) {
|
|
@@ -4206,7 +4248,7 @@ function buildMetaToolDefinitions(ctx) {
|
|
|
4206
4248
|
properties: {
|
|
4207
4249
|
query: {
|
|
4208
4250
|
type: "string",
|
|
4209
|
-
description: "
|
|
4251
|
+
description: "Before calling, distill your intent into 2-5 key concepts: what action, on what target, in which domain. Pass only those distilled keywords — not the original user request."
|
|
4210
4252
|
},
|
|
4211
4253
|
top_k: {
|
|
4212
4254
|
type: "number",
|
|
@@ -4237,7 +4279,7 @@ function buildMetaToolDefinitions(ctx) {
|
|
|
4237
4279
|
},
|
|
4238
4280
|
autoActivate: {
|
|
4239
4281
|
type: "boolean",
|
|
4240
|
-
description: "Whether to auto-activate recommended tools (default:
|
|
4282
|
+
description: "Whether to auto-activate recommended tools (default: false)"
|
|
4241
4283
|
},
|
|
4242
4284
|
maxRecommendations: {
|
|
4243
4285
|
type: "number",
|
|
@@ -4293,7 +4335,7 @@ function buildMetaToolDefinitions(ctx) {
|
|
|
4293
4335
|
},
|
|
4294
4336
|
{
|
|
4295
4337
|
name: "activate_domain",
|
|
4296
|
-
description: `Activate all tools in a domain at once. Domains: ${[...getAllDomains()].join(", ")}. Use
|
|
4338
|
+
description: `Activate all tools in a domain at once. Domains: ${[...getAllDomains()].join(", ")}. Use reload_extensions first to include external plugin/workflow domains.`,
|
|
4297
4339
|
inputSchema: {
|
|
4298
4340
|
type: "object",
|
|
4299
4341
|
properties: {
|
|
@@ -4312,7 +4354,7 @@ function buildMetaToolDefinitions(ctx) {
|
|
|
4312
4354
|
},
|
|
4313
4355
|
{
|
|
4314
4356
|
name: "call_tool",
|
|
4315
|
-
description: "Execute
|
|
4357
|
+
description: "Execute an already-active tool by name. Use this when activate_tools/activate_domain registered a tool but your client did not refresh its tool list. Does not auto-activate inactive tools.",
|
|
4316
4358
|
inputSchema: {
|
|
4317
4359
|
type: "object",
|
|
4318
4360
|
properties: {
|
|
@@ -4482,7 +4524,164 @@ Generally, you have three options to deploy hooks:
|
|
|
4482
4524
|
}] }));
|
|
4483
4525
|
}
|
|
4484
4526
|
//#endregion
|
|
4527
|
+
//#region src/server/persistence/RuntimeSnapshotScheduler.ts
|
|
4528
|
+
var RuntimeSnapshotScheduler = class {
|
|
4529
|
+
sources = [];
|
|
4530
|
+
debounceTimer = null;
|
|
4531
|
+
periodicTimer = null;
|
|
4532
|
+
debounceMs;
|
|
4533
|
+
periodicMs;
|
|
4534
|
+
disposed = false;
|
|
4535
|
+
started = false;
|
|
4536
|
+
constructor(options) {
|
|
4537
|
+
this.debounceMs = options?.debounceMs ?? 2e3;
|
|
4538
|
+
this.periodicMs = options?.periodicMs ?? 3e4;
|
|
4539
|
+
}
|
|
4540
|
+
register(filePath, source) {
|
|
4541
|
+
const existing = this.sources.find((entry) => entry.filePath === filePath || entry.source === source);
|
|
4542
|
+
if (existing) {
|
|
4543
|
+
if (existing.filePath !== filePath || existing.source !== source) logger.warn(`skipping conflicting snapshot registration for ${filePath}`);
|
|
4544
|
+
return;
|
|
4545
|
+
}
|
|
4546
|
+
const entry = {
|
|
4547
|
+
source,
|
|
4548
|
+
filePath
|
|
4549
|
+
};
|
|
4550
|
+
this.sources.push(entry);
|
|
4551
|
+
if (this.started) this.restoreOne(entry).catch((err) => logger.warn(`snapshot restore failed for ${entry.filePath}:`, err));
|
|
4552
|
+
}
|
|
4553
|
+
async start() {
|
|
4554
|
+
if (this.started || this.disposed) return;
|
|
4555
|
+
this.started = true;
|
|
4556
|
+
await this.restoreAll();
|
|
4557
|
+
if (this.disposed || this.periodicTimer) return;
|
|
4558
|
+
this.periodicTimer = setInterval(() => {
|
|
4559
|
+
this.scheduleFlush().catch((err) => logger.warn("periodic snapshot failed:", err));
|
|
4560
|
+
}, this.periodicMs);
|
|
4561
|
+
}
|
|
4562
|
+
notifyDirty() {
|
|
4563
|
+
if (this.disposed) return;
|
|
4564
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
4565
|
+
this.debounceTimer = setTimeout(() => {
|
|
4566
|
+
this.scheduleFlush().catch((err) => logger.warn("debounce snapshot failed:", err));
|
|
4567
|
+
}, this.debounceMs);
|
|
4568
|
+
}
|
|
4569
|
+
async flushAll() {
|
|
4570
|
+
if (this.debounceTimer) {
|
|
4571
|
+
clearTimeout(this.debounceTimer);
|
|
4572
|
+
this.debounceTimer = null;
|
|
4573
|
+
}
|
|
4574
|
+
await this.writeDirtySources();
|
|
4575
|
+
}
|
|
4576
|
+
dispose() {
|
|
4577
|
+
this.disposed = true;
|
|
4578
|
+
if (this.debounceTimer) {
|
|
4579
|
+
clearTimeout(this.debounceTimer);
|
|
4580
|
+
this.debounceTimer = null;
|
|
4581
|
+
}
|
|
4582
|
+
if (this.periodicTimer) {
|
|
4583
|
+
clearInterval(this.periodicTimer);
|
|
4584
|
+
this.periodicTimer = null;
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
async restoreAll() {
|
|
4588
|
+
for (const entry of this.sources) await this.restoreOne(entry);
|
|
4589
|
+
}
|
|
4590
|
+
async restoreOne(entry) {
|
|
4591
|
+
try {
|
|
4592
|
+
const data = await readFile(entry.filePath, "utf-8");
|
|
4593
|
+
const parsed = JSON.parse(data);
|
|
4594
|
+
entry.source.restoreSnapshot(parsed);
|
|
4595
|
+
logger.info(`restored snapshot from ${entry.filePath}`);
|
|
4596
|
+
} catch {}
|
|
4597
|
+
}
|
|
4598
|
+
async scheduleFlush() {
|
|
4599
|
+
await this.writeDirtySources();
|
|
4600
|
+
}
|
|
4601
|
+
async writeDirtySources() {
|
|
4602
|
+
for (const entry of this.sources) {
|
|
4603
|
+
if (!entry.source.isPersistDirty()) continue;
|
|
4604
|
+
try {
|
|
4605
|
+
await this.writeSnapshot(entry);
|
|
4606
|
+
} catch (err) {
|
|
4607
|
+
logger.warn(`snapshot write failed for ${entry.filePath}:`, err);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
async writeSnapshot(entry) {
|
|
4612
|
+
await mkdir(dirname(entry.filePath), { recursive: true });
|
|
4613
|
+
const data = JSON.stringify(entry.source.exportSnapshot());
|
|
4614
|
+
const tmpPath = entry.filePath + ".tmp";
|
|
4615
|
+
await writeFile(tmpPath, data, "utf-8");
|
|
4616
|
+
await rename(tmpPath, entry.filePath);
|
|
4617
|
+
entry.source.markPersisted();
|
|
4618
|
+
}
|
|
4619
|
+
};
|
|
4620
|
+
function getStateDir(baseDir) {
|
|
4621
|
+
return resolve(baseDir, ".jshookmcp", "state");
|
|
4622
|
+
}
|
|
4623
|
+
//#endregion
|
|
4485
4624
|
//#region src/server/MCPServer.ts
|
|
4625
|
+
function shouldCollectExecutionMetrics() {
|
|
4626
|
+
return process.env.E2E_COLLECT_PERFORMANCE === "1";
|
|
4627
|
+
}
|
|
4628
|
+
function captureExecutionMetricMemory() {
|
|
4629
|
+
const memory = process.memoryUsage();
|
|
4630
|
+
return {
|
|
4631
|
+
source: "server",
|
|
4632
|
+
rssBytes: memory.rss,
|
|
4633
|
+
privateBytes: null,
|
|
4634
|
+
virtualBytes: null,
|
|
4635
|
+
heapUsedBytes: memory.heapUsed,
|
|
4636
|
+
heapTotalBytes: memory.heapTotal,
|
|
4637
|
+
externalBytes: memory.external,
|
|
4638
|
+
arrayBuffersBytes: memory.arrayBuffers
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
function buildExecutionMetrics(startedAt, startTime, timeoutMs, cpuStart, memoryBefore) {
|
|
4642
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4643
|
+
const cpuUsage = process.cpuUsage(cpuStart);
|
|
4644
|
+
const memoryAfter = captureExecutionMetricMemory();
|
|
4645
|
+
return {
|
|
4646
|
+
source: "server",
|
|
4647
|
+
startedAt,
|
|
4648
|
+
finishedAt,
|
|
4649
|
+
elapsedMs: Number((performance.now() - startTime).toFixed(2)),
|
|
4650
|
+
timeoutMs,
|
|
4651
|
+
serverPid: process.pid,
|
|
4652
|
+
cpuUserMicros: cpuUsage.user,
|
|
4653
|
+
cpuSystemMicros: cpuUsage.system,
|
|
4654
|
+
memoryBefore,
|
|
4655
|
+
memoryAfter,
|
|
4656
|
+
memoryDelta: {
|
|
4657
|
+
rssBytes: memoryAfter.rssBytes - memoryBefore.rssBytes,
|
|
4658
|
+
privateBytes: null,
|
|
4659
|
+
virtualBytes: null,
|
|
4660
|
+
heapUsedBytes: memoryAfter.heapUsedBytes - memoryBefore.heapUsedBytes,
|
|
4661
|
+
heapTotalBytes: memoryAfter.heapTotalBytes - memoryBefore.heapTotalBytes,
|
|
4662
|
+
externalBytes: memoryAfter.externalBytes - memoryBefore.externalBytes,
|
|
4663
|
+
arrayBuffersBytes: memoryAfter.arrayBuffersBytes - memoryBefore.arrayBuffersBytes
|
|
4664
|
+
}
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
function appendExecutionMetrics(response, metrics) {
|
|
4668
|
+
const content = response.content;
|
|
4669
|
+
if (!Array.isArray(content)) return response;
|
|
4670
|
+
const firstText = content.find((entry) => typeof entry === "object" && entry !== null && entry.type === "text" && typeof entry.text === "string");
|
|
4671
|
+
if (!firstText) return response;
|
|
4672
|
+
try {
|
|
4673
|
+
const parsed = JSON.parse(firstText.text);
|
|
4674
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return response;
|
|
4675
|
+
const record = parsed;
|
|
4676
|
+
if (!("_executionMetrics" in record)) {
|
|
4677
|
+
record._executionMetrics = metrics;
|
|
4678
|
+
firstText.text = JSON.stringify(record);
|
|
4679
|
+
}
|
|
4680
|
+
} catch {
|
|
4681
|
+
return response;
|
|
4682
|
+
}
|
|
4683
|
+
return response;
|
|
4684
|
+
}
|
|
4486
4685
|
var MCPServer = class {
|
|
4487
4686
|
config;
|
|
4488
4687
|
server;
|
|
@@ -4538,12 +4737,32 @@ var MCPServer = class {
|
|
|
4538
4737
|
this.enabledDomains = this.resolveEnabledDomains(this.selectedTools);
|
|
4539
4738
|
const depsEntries = [];
|
|
4540
4739
|
const manifests = getAllManifests();
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4740
|
+
const loadedByDomain = new Map(manifests.map((m) => [m.domain, m]));
|
|
4741
|
+
const allMeta = getLoaderMetadata();
|
|
4742
|
+
if (!Array.isArray(allMeta)) logger.warn("[MCPServer] getLoaderMetadata returned non-array, skipping domain proxy setup");
|
|
4743
|
+
else for (const meta of allMeta) {
|
|
4744
|
+
const loaded = loadedByDomain.get(meta.domain);
|
|
4745
|
+
if (loaded) {
|
|
4746
|
+
depsEntries.push([meta.depKey, createDomainProxy(this, meta.domain, `${meta.domain}:${meta.depKey}`, () => loaded.ensure(this))]);
|
|
4747
|
+
if (loaded.secondaryDepKeys) {
|
|
4748
|
+
for (const key of loaded.secondaryDepKeys) if (!depsEntries.some(([k]) => k === key)) depsEntries.push([key, createDomainProxy(this, meta.domain, `${meta.domain}:${key}`, async () => {
|
|
4749
|
+
await loaded.ensure(this);
|
|
4750
|
+
return this[key];
|
|
4751
|
+
})]);
|
|
4752
|
+
}
|
|
4753
|
+
} else {
|
|
4754
|
+
depsEntries.push([meta.depKey, createDomainProxy(this, meta.domain, `${meta.domain}:${meta.depKey}`, async () => {
|
|
4755
|
+
const manifest = await ensureDomainLoaded(meta.domain);
|
|
4756
|
+
if (!manifest) throw new Error(`Failed to load domain ${meta.domain}`);
|
|
4757
|
+
return manifest.ensure(this);
|
|
4758
|
+
})]);
|
|
4759
|
+
for (const key of meta.secondaryDepKeys) if (!depsEntries.some(([k]) => k === key)) depsEntries.push([key, createDomainProxy(this, meta.domain, `${meta.domain}:${key}`, async () => {
|
|
4760
|
+
const manifest = await ensureDomainLoaded(meta.domain);
|
|
4761
|
+
if (!manifest) throw new Error(`Failed to load domain ${meta.domain}`);
|
|
4762
|
+
await manifest.ensure(this);
|
|
4763
|
+
return this[key];
|
|
4764
|
+
})]);
|
|
4765
|
+
}
|
|
4547
4766
|
}
|
|
4548
4767
|
this.handlerDeps = Object.fromEntries(depsEntries);
|
|
4549
4768
|
const selectedToolNames = new Set(this.selectedTools.map((t) => t.name));
|
|
@@ -4580,6 +4799,11 @@ var MCPServer = class {
|
|
|
4580
4799
|
this.samplingBridge = new LLMSamplingBridge(this.server);
|
|
4581
4800
|
this.elicitationBridge = new ElicitationBridge(this.server);
|
|
4582
4801
|
this.setDomainInstance("activationController", new ActivationController(this.eventBus, this));
|
|
4802
|
+
const stateDir = getStateDir(process.cwd());
|
|
4803
|
+
const snapshotScheduler = new RuntimeSnapshotScheduler();
|
|
4804
|
+
this.setDomainInstance("snapshotScheduler", snapshotScheduler);
|
|
4805
|
+
this.setDomainInstance("snapshotStateDir", stateDir);
|
|
4806
|
+
snapshotScheduler.start().catch((err) => logger.warn("snapshot scheduler start failed:", err));
|
|
4583
4807
|
this.eventBus.on("tool:progress", async (payload) => {
|
|
4584
4808
|
try {
|
|
4585
4809
|
await this.server.server.notification({
|
|
@@ -4658,7 +4882,7 @@ var MCPServer = class {
|
|
|
4658
4882
|
}
|
|
4659
4883
|
this.cacheRegistrationPromise = (async () => {
|
|
4660
4884
|
try {
|
|
4661
|
-
const { createCacheAdapters } = await import("./CacheAdapters-
|
|
4885
|
+
const { createCacheAdapters } = await import("./CacheAdapters-jJFy20G-.mjs");
|
|
4662
4886
|
const codeCache = this.collector.getCache();
|
|
4663
4887
|
const codeCompressor = this.collector.getCompressor();
|
|
4664
4888
|
const adapters = createCacheAdapters(this.detailedData, codeCache, codeCompressor);
|
|
@@ -4679,6 +4903,12 @@ var MCPServer = class {
|
|
|
4679
4903
|
}
|
|
4680
4904
|
async executeToolWithTracking(name, args) {
|
|
4681
4905
|
let timeoutTimer;
|
|
4906
|
+
const timeoutMs = 3e4;
|
|
4907
|
+
const collectExecutionMetrics = shouldCollectExecutionMetrics();
|
|
4908
|
+
const executionStartedAt = collectExecutionMetrics ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
4909
|
+
const executionStartTime = collectExecutionMetrics ? performance.now() : 0;
|
|
4910
|
+
const executionCpuStart = collectExecutionMetrics ? process.cpuUsage() : null;
|
|
4911
|
+
const executionMemoryBefore = collectExecutionMetrics ? captureExecutionMetricMemory() : null;
|
|
4682
4912
|
try {
|
|
4683
4913
|
timeoutTimer = setTimeout(() => {
|
|
4684
4914
|
try {
|
|
@@ -4687,7 +4917,7 @@ var MCPServer = class {
|
|
|
4687
4917
|
} catch {
|
|
4688
4918
|
logger.warn(`Telemetry Alert [ERR-03]: Tool execution hung (>30s) for '${name}'.`);
|
|
4689
4919
|
}
|
|
4690
|
-
},
|
|
4920
|
+
}, timeoutMs);
|
|
4691
4921
|
timeoutTimer.unref();
|
|
4692
4922
|
let response;
|
|
4693
4923
|
try {
|
|
@@ -4696,7 +4926,8 @@ var MCPServer = class {
|
|
|
4696
4926
|
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
4697
4927
|
}
|
|
4698
4928
|
this.contextGuard.recordCall(name);
|
|
4699
|
-
|
|
4929
|
+
let enriched = this.contextGuard.enrichResponse(name, response);
|
|
4930
|
+
if (collectExecutionMetrics && executionStartedAt && executionCpuStart && executionMemoryBefore) enriched = appendExecutionMetrics(enriched, buildExecutionMetrics(executionStartedAt, executionStartTime, timeoutMs, executionCpuStart, executionMemoryBefore));
|
|
4700
4931
|
try {
|
|
4701
4932
|
this.tokenBudget.recordToolCall(name, args, enriched);
|
|
4702
4933
|
} catch (trackingError) {
|
|
@@ -4910,7 +5141,8 @@ async function main() {
|
|
|
4910
5141
|
if (cleanup.removedFiles > 0) logger.info(`[artifacts] Startup cleanup removed ${cleanup.removedFiles} files (${cleanup.removedBytes} bytes)`);
|
|
4911
5142
|
}
|
|
4912
5143
|
logger.info("Creating MCP server instance...");
|
|
4913
|
-
|
|
5144
|
+
const explicitProfile = (process.env.MCP_TOOL_PROFILE ?? "").trim().toLowerCase();
|
|
5145
|
+
await initRegistry(explicitProfile === "full" || explicitProfile === "workflow" || explicitProfile === "search" ? explicitProfile : "search");
|
|
4914
5146
|
const server = new MCPServer(config);
|
|
4915
5147
|
const stopArtifactRetentionScheduler = startArtifactRetentionScheduler();
|
|
4916
5148
|
const recoveryWindowMs = Math.max(1e3, RUNTIME_ERROR_WINDOW_MS);
|