kiku-agent 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.md +69 -12
  2. package/package.json +5 -12
  3. package/server/agent/agentCore.js +319 -46
  4. package/server/agent/appPaths.js +31 -0
  5. package/server/agent/audioPlayer.js +339 -12
  6. package/server/agent/autoContext.js +193 -0
  7. package/server/agent/catalogEntityResolver.js +170 -28
  8. package/server/agent/clarifyingQuestions.js +137 -35
  9. package/server/agent/cli.js +177 -48
  10. package/server/agent/cloudAdapters.js +116 -0
  11. package/server/agent/commandRegistry.js +409 -80
  12. package/server/agent/commands.js +1056 -125
  13. package/server/agent/config.js +1088 -690
  14. package/server/agent/contextCompactor.js +213 -21
  15. package/server/agent/cpuMetrics.js +199 -0
  16. package/server/agent/engineService.js +591 -0
  17. package/server/agent/eval/AGENTS.md +228 -0
  18. package/server/agent/eval/analyze.py +684 -0
  19. package/server/agent/eval/gates.json +15 -0
  20. package/server/agent/eval/modules/analysis/compare.js +198 -0
  21. package/server/agent/eval/modules/analysis/gates.js +72 -0
  22. package/server/agent/eval/modules/analysis/judge.js +70 -0
  23. package/server/agent/eval/modules/core/cli.js +453 -0
  24. package/server/agent/eval/modules/core/config.js +60 -0
  25. package/server/agent/eval/modules/core/store.js +236 -0
  26. package/server/agent/eval/modules/evaluation/cases.js +689 -0
  27. package/server/agent/eval/modules/evaluation/runner.js +288 -0
  28. package/server/agent/eval/modules/evaluation/scoring.js +363 -0
  29. package/server/agent/eval/modules/evaluation/worker.js +122 -0
  30. package/server/agent/eval/modules/reporting/benchmark.js +265 -0
  31. package/server/agent/eval/modules/reporting/benchmarkPng.js +487 -0
  32. package/server/agent/eval/modules/reporting/dashboard.js +1406 -0
  33. package/server/agent/eval/modules/server/evalApi.js +1099 -0
  34. package/server/agent/eval/modules/ui/evalUi.js +4799 -0
  35. package/server/agent/eval/modules/utils/smokes.js +119 -0
  36. package/server/agent/eventBus.js +1 -0
  37. package/server/agent/ink/main.jsx +196 -191
  38. package/server/agent/jobManager.js +11 -1
  39. package/server/agent/lanDiscovery.js +721 -23
  40. package/server/agent/llmClient.js +317 -71
  41. package/server/agent/lmStudioWakeup.js +517 -65
  42. package/server/agent/locationService.js +797 -0
  43. package/server/agent/mcpQuizData.js +509 -0
  44. package/server/agent/memory/contextAllocator.js +19 -7
  45. package/server/agent/memory.js +3 -1
  46. package/server/agent/modelCompatibility.js +352 -0
  47. package/server/agent/modelDefaults.js +1 -0
  48. package/server/agent/modelManager.js +46 -2
  49. package/server/agent/newsWatch.js +9 -3
  50. package/server/agent/processManager.js +229 -0
  51. package/server/agent/prompts.js +174 -8
  52. package/server/agent/routingInterceptor.js +9 -1
  53. package/server/agent/sanitizer.js +41 -6
  54. package/server/agent/searchQueryPlanner.js +202 -0
  55. package/server/agent/session.js +59 -4
  56. package/server/agent/tests/agentBubbleSystem.test.js +65 -0
  57. package/server/agent/tests/agentFollowUps.test.js +253 -0
  58. package/server/agent/tests/agentMusicControl.test.js +245 -0
  59. package/server/agent/tests/apiRouter.test.js +108 -0
  60. package/server/agent/tests/architectureFixes.test.js +81 -0
  61. package/server/agent/tests/architectureRefactoring.test.js +194 -0
  62. package/server/agent/tests/autoContext.test.js +176 -0
  63. package/server/agent/tests/autoResearch.test.js +64 -0
  64. package/server/agent/tests/bilibiliEmbed.test.js +296 -0
  65. package/server/agent/tests/catalogSync.test.js +130 -0
  66. package/server/agent/tests/chatHardening.test.js +121 -0
  67. package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
  68. package/server/agent/tests/citations.test.js +180 -0
  69. package/server/agent/tests/clarificationAnalytics.test.js +219 -0
  70. package/server/agent/tests/clarifyingQuestions.test.js +627 -0
  71. package/server/agent/tests/cliAutomation.test.js +158 -0
  72. package/server/agent/tests/cloudProviders.test.js +322 -0
  73. package/server/agent/tests/commandInput.test.js +24 -0
  74. package/server/agent/tests/commands.test.js +506 -0
  75. package/server/agent/tests/contextIntelligence.test.js +209 -0
  76. package/server/agent/tests/conversationSafety.test.js +53 -0
  77. package/server/agent/tests/cpuMetrics.test.js +101 -0
  78. package/server/agent/tests/decisionAndSearchEnhancements.test.js +167 -0
  79. package/server/agent/tests/dramaAnswerQuality.test.js +73 -0
  80. package/server/agent/tests/dynamicCatalogIngestion.test.js +285 -0
  81. package/server/agent/tests/dynamicRoutingAndSearch.test.js +92 -0
  82. package/server/agent/tests/edgeCasesScenarios.test.js +131 -0
  83. package/server/agent/tests/engineService.test.js +436 -0
  84. package/server/agent/tests/engineUnload.test.js +94 -0
  85. package/server/agent/tests/entertainmentMetrics.test.js +126 -0
  86. package/server/agent/tests/envBootstrap.test.js +87 -0
  87. package/server/agent/tests/envCommand.test.js +284 -0
  88. package/server/agent/tests/errorHandling.test.js +80 -0
  89. package/server/agent/tests/errors.test.js +13 -0
  90. package/server/agent/tests/evalAnalyze.test.js +54 -0
  91. package/server/agent/tests/evalCommand.test.js +54 -0
  92. package/server/agent/tests/evalCorpus.test.js +126 -0
  93. package/server/agent/tests/evalHarness.test.js +62 -0
  94. package/server/agent/tests/evalJudgeContext.test.js +38 -0
  95. package/server/agent/tests/evalMetrics.test.js +320 -0
  96. package/server/agent/tests/evalStore.test.js +190 -0
  97. package/server/agent/tests/evalStudio.test.js +179 -0
  98. package/server/agent/tests/feedbackLearning.test.js +186 -0
  99. package/server/agent/tests/fetchApi.test.js +99 -0
  100. package/server/agent/tests/firstRun.test.js +44 -0
  101. package/server/agent/tests/gemma4eOptimization.test.js +95 -0
  102. package/server/agent/tests/hardwareTiers.test.js +206 -0
  103. package/server/agent/tests/hybridWebSearch.test.js +194 -0
  104. package/server/agent/tests/judgeService.test.js +251 -0
  105. package/server/agent/tests/lanDiscovery.test.js +324 -0
  106. package/server/agent/tests/lanInterfaces.test.js +510 -0
  107. package/server/agent/tests/lmStudioWakeup.test.js +278 -0
  108. package/server/agent/tests/locationTool.test.js +278 -0
  109. package/server/agent/tests/mcpClient.test.js +201 -0
  110. package/server/agent/tests/memoryRoute.test.js +173 -0
  111. package/server/agent/tests/memoryUiNormalization.test.js +75 -0
  112. package/server/agent/tests/modelCompatibility.test.js +148 -0
  113. package/server/agent/tests/modelManager.test.js +141 -0
  114. package/server/agent/tests/multilingualScenarios.test.js +69 -0
  115. package/server/agent/tests/musicControlUiBridge.test.js +113 -0
  116. package/server/agent/tests/newCommands.test.js +347 -0
  117. package/server/agent/tests/newCommandsTui.test.js +141 -0
  118. package/server/agent/tests/newTools.test.js +113 -0
  119. package/server/agent/tests/newsIntelligence.test.js +178 -0
  120. package/server/agent/tests/nextGenArchitecture.test.js +101 -0
  121. package/server/agent/tests/offlineInput.test.js +217 -0
  122. package/server/agent/tests/openDomainPrompt.test.js +197 -0
  123. package/server/agent/tests/openDomainSearch.test.js +121 -0
  124. package/server/agent/tests/packagingGuard.test.js +57 -0
  125. package/server/agent/tests/presentation.test.js +61 -0
  126. package/server/agent/tests/processManager.test.js +144 -0
  127. package/server/agent/tests/providerPersistence.test.js +44 -0
  128. package/server/agent/tests/queryBenchmarks.test.js +83 -0
  129. package/server/agent/tests/queryDetails.test.js +126 -0
  130. package/server/agent/tests/quickActionsAndModals.test.js +180 -0
  131. package/server/agent/tests/realWorldValidation.test.js +391 -0
  132. package/server/agent/tests/resilienceAndConcurrency.test.js +117 -0
  133. package/server/agent/tests/runtimeWatchdog.test.js +32 -0
  134. package/server/agent/tests/sanitizer.test.js +37 -0
  135. package/server/agent/tests/scenariosData.js +378 -0
  136. package/server/agent/tests/schemaRouting.test.js +54 -0
  137. package/server/agent/tests/searchQueryPlanner.test.js +108 -0
  138. package/server/agent/tests/securityAuditRemediation.test.js +221 -0
  139. package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
  140. package/server/agent/tests/securityHardening.test.js +146 -0
  141. package/server/agent/tests/securitySanitizer.test.js +38 -0
  142. package/server/agent/tests/sessionAndCopy.test.js +267 -0
  143. package/server/agent/tests/sidebarControls.test.js +135 -0
  144. package/server/agent/tests/statefulMemory.test.js +277 -0
  145. package/server/agent/tests/subagent.test.js +216 -0
  146. package/server/agent/tests/subagentProgress.test.js +109 -0
  147. package/server/agent/tests/subagentTextFallback.test.js +115 -0
  148. package/server/agent/tests/suggestions.test.js +199 -0
  149. package/server/agent/tests/suggestionsTui.test.js +251 -0
  150. package/server/agent/tests/telemetryAndProfiles.test.js +179 -0
  151. package/server/agent/tests/terminalImage.test.js +80 -0
  152. package/server/agent/tests/terminalMarkdown.test.js +166 -0
  153. package/server/agent/tests/terminalText.test.js +133 -0
  154. package/server/agent/tests/textProtocolLoop.test.js +197 -0
  155. package/server/agent/tests/textToolParsing.test.js +187 -0
  156. package/server/agent/tests/theme.test.js +26 -0
  157. package/server/agent/tests/toolRegistryParity.test.js +53 -0
  158. package/server/agent/tests/toolRoutingRegressions.test.js +160 -0
  159. package/server/agent/tests/tools.test.js +259 -0
  160. package/server/agent/tests/unifiedEndpoints.test.js +159 -0
  161. package/server/agent/tests/v110Features.test.js +262 -0
  162. package/server/agent/tests/versionBump.test.js +69 -0
  163. package/server/agent/tests/vramEstimator.test.js +595 -0
  164. package/server/agent/tests/webChatDocs.test.js +199 -0
  165. package/server/agent/tests/webChatInferenceControl.test.js +298 -0
  166. package/server/agent/tests/webChatInspection.test.js +551 -0
  167. package/server/agent/tests/webChatMcp.test.js +94 -0
  168. package/server/agent/tests/webChatMemory.test.js +112 -0
  169. package/server/agent/tests/webChatSecurity.test.js +169 -0
  170. package/server/agent/tests/webExtract.test.js +284 -0
  171. package/server/agent/tests/webSources.test.js +281 -0
  172. package/server/agent/textToolProtocol.js +173 -46
  173. package/server/agent/toolRouter.js +318 -44
  174. package/server/agent/tools.js +877 -96
  175. package/server/agent/ui.js +39 -34
  176. package/server/agent/vramService.js +35 -54
  177. package/server/agent/webSearch.js +441 -162
  178. package/server/agent/websearch/orchestrator.js +15 -5
  179. package/server/agent/websearch/resilience/cache.js +9 -2
  180. package/server/agent/websearch/security/ssrf.js +41 -10
  181. package/server/agent/youtubeAuth.js +5 -20
  182. package/server/apiRouter.js +5558 -4422
  183. package/server/eval/judgeRubrics.js +110 -0
  184. package/server/eval/judgeService.js +290 -0
  185. package/server/eval/runBenchmark.js +182 -0
  186. package/server/mcp/data/discography.js +2 -2
  187. package/server/mcp/examples/antigravity.example.json +17 -0
  188. package/server/mcp/examples/librechat.example.yaml +16 -0
  189. package/server/mcp/handler.js +8 -4
  190. package/server/mcp/security.js +220 -21
  191. package/server/mcp/server.js +2 -1
  192. package/server/mcp/tests/connection.test.js +162 -0
  193. package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
  194. package/server/mcp/tools.js +7 -1
  195. package/server/supabase_migration_prefix.sql +102 -0
  196. package/server/supabase_schema.sql +63 -0
  197. package/server/web-chat/core/connection.js +179 -218
  198. package/server/web-chat/core/conversationContext.js +9 -2
  199. package/server/web-chat/core/directCatalogEngine.js +52 -28
  200. package/server/web-chat/core/dramaKnowledge.js +33 -22
  201. package/server/web-chat/core/followUpEngine.js +735 -81
  202. package/server/web-chat/core/futureEventGuard.js +5 -1
  203. package/server/web-chat/core/musicControlAction.js +70 -0
  204. package/server/web-chat/core/security.js +32 -21
  205. package/server/web-chat/core/toolInspector.js +6 -6
  206. package/server/web-chat/core/urlGuard.js +45 -0
  207. package/server/web-chat/router.js +204 -157
  208. package/server/web-chat/routes/chatRoute.js +885 -488
  209. package/server/web-chat/routes/engineRoute.js +270 -0
  210. package/server/web-chat/routes/exportRoute.js +1 -1
  211. package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
  212. package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
  213. package/server/web-chat/routes/mcpRoute.js +386 -0
  214. package/server/web-chat/routes/memoryRoute.js +253 -0
  215. package/server/web-chat/routes/providerRoute.js +49 -16
  216. package/server/web-chat/routes/staticRoute.js +306 -17
  217. package/server/web-chat/routes/statusRoute.js +194 -73
  218. package/server/web-chat/routes/suggestRoute.js +11 -3
  219. package/server/web-chat/routes/verifyRoute.js +26 -4
  220. package/server/web-chat/server.js +7 -6
  221. package/server/web-chat/ui/docsBuilder.js +3558 -0
  222. package/server/web-chat/ui/htmlBuilder.js +12 -0
  223. package/server/web-chat/ui/scripts/client.js +3401 -1302
  224. package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
  225. package/server/web-chat/ui/settingsBuilder.js +7870 -0
  226. package/server/web-chat/ui/styles/lan-card.css +165 -0
  227. package/server/web-chat/ui/styles/main.css +4289 -447
  228. package/server/web-chat/ui/sw.js +64 -0
  229. package/server/web-chat/ui/templates/body.html +407 -281
  230. package/src/data/awardsData.js +146 -146
  231. package/src/data/musicVideoData.js +6 -4
  232. package/src/lib/vram/estimator.js +195 -19
@@ -1,5 +1,5 @@
1
1
  import { toolsSchema } from './tools.js';
2
- import { resolveQueryEntities, detectPlatforms, detectPromotionIntent } from './catalogEntityResolver.js';
2
+ import { resolveQueryEntities, detectPlatforms, detectPromotionIntent, classifyQueryDomain, isQueryInDomain, classifyQuerySpectrum } from './catalogEntityResolver.js';
3
3
 
4
4
  export const TOOL_CLUSTERS = Object.freeze({
5
5
  CATALOG: [
@@ -22,6 +22,13 @@ export const TOOL_CLUSTERS = Object.freeze({
22
22
  'stop_music',
23
23
  'pause_music',
24
24
  'resume_music',
25
+ 'mute_music',
26
+ 'unmute_music',
27
+ 'set_volume',
28
+ 'seek_music',
29
+ 'set_repeat_mode',
30
+ 'set_shuffle_mode',
31
+ 'toggle_video_mode',
25
32
  'next_track',
26
33
  'prev_track',
27
34
  'add_to_queue',
@@ -51,6 +58,9 @@ export const TOOL_CLUSTERS = Object.freeze({
51
58
  'search_chinese_web',
52
59
  'search_bilibili',
53
60
  'search_web_images',
61
+ 'get_user_location',
62
+ 'search_location_map',
63
+ 'get_weather',
54
64
  ],
55
65
  SYSTEM: [
56
66
  'get_system_stats',
@@ -63,6 +73,56 @@ export const TOOL_CLUSTERS = Object.freeze({
63
73
  ]
64
74
  });
65
75
 
76
+ const TOOL_CLUSTER_MAP = new Map();
77
+ for (const [cluster, tools] of Object.entries(TOOL_CLUSTERS)) {
78
+ for (const t of tools) {
79
+ TOOL_CLUSTER_MAP.set(t, cluster);
80
+ }
81
+ }
82
+
83
+ function getToolCluster(toolName) {
84
+ if (TOOL_CLUSTER_MAP.has(toolName)) return TOOL_CLUSTER_MAP.get(toolName);
85
+ if (toolName && toolName.startsWith('mcp_')) return 'SYSTEM';
86
+ return 'CATALOG';
87
+ }
88
+
89
+ export const DEFAULT_OPEN_DOMAIN_TOOLS = Object.freeze([
90
+ 'get_user_location',
91
+ 'search_location_map',
92
+ 'get_weather',
93
+ 'web_search',
94
+ 'web_extract',
95
+ 'fetch_webpage',
96
+ 'search_encyclopedia',
97
+ 'search_news',
98
+ 'fetch_json',
99
+ 'global_search',
100
+ ]);
101
+
102
+ export const OPEN_DOMAIN_WEB_TOOLS = Object.freeze([
103
+ 'web_search',
104
+ 'web_extract',
105
+ 'fetch_webpage',
106
+ 'search_encyclopedia',
107
+ 'search_news',
108
+ 'fetch_json',
109
+ 'fetch_api',
110
+ 'fetch_rss',
111
+ 'search_chinese_web',
112
+ 'search_bilibili',
113
+ 'search_youtube',
114
+ 'search_web_images',
115
+ 'search_music_platforms',
116
+ 'get_youtube_transcript',
117
+ ]);
118
+
119
+ const CATALOG_INTENT_TYPES = new Set([
120
+ 'WORKS',
121
+ 'PROFILE_AWARDS_BRANDS',
122
+ 'TIMELINE_ERA',
123
+ 'MEDIA',
124
+ ]);
125
+
66
126
  const DEFAULT_UNIVERSAL_TOOLS = Object.freeze([
67
127
  'global_search',
68
128
  'get_profile_and_fandom',
@@ -105,13 +165,13 @@ export function expandActiveToolsWithDependencies(selectedTools, allToolsSchema)
105
165
  const INTENT_PATTERNS = [
106
166
  {
107
167
  type: 'MEDIA',
108
- regex: /(เพลง|ร้อง|ฟัง|เอ็มวี|\bmv\b|\bmusics?\b|\bsongs?\b|\bsingles?\b|\bsolos?\b|\balbums?\b|\bep\b|\bdiscography\b|\bdiscs?\b|\btracks?\b|\btunes?\b|\bmelod(?:y|ies)\b|\bplay\b|\bqueues?\b|\baudios?\b|\bsounds?\b|\blisten\b|\bstages?\b|\bdances?\b|\bdancing\b|\bconcerts?\b|\bost\b|\bbilibili\b|b站|\bqqmusic\b|\bspotify\b|(?<!เครื่อง)ดนตรี|อัลบั้ม|ซิงเกิล|โซโล่|歌曲|单曲|首发|舞台|演唱|คลิป|วิดีโอ|\bvideos?\b)/i,
168
+ regex: /(เพลง|ร้อง|ฟัง(?!ก์|กชัน)|เอ็มวี|\bmv\b|\bmusics?\b|\bsongs?\b|\bsingles?\b|\bsolos?\b|\balbums?\b|\bep\b|\bdiscography\b|\bdiscs?\b|\btracks?\b|\btunes?\b|\bmelod(?:y|ies)\b|\bplay\b|\bqueues?\b|\baudios?\b|\bsounds?\b|\blisten\b|\bstages?\b|\bdances?\b|\bdancing\b|\bconcerts?\b|\bost\b|\bbilibili\b|b站|\bqqmusic\b|\bspotify\b|(?<!เครื่อง)ดนตรี|อัลบั้ม|ซิงเกิล|โซโล่|歌曲|单曲|首发|舞台|演唱|คลิป|วิดีโอ|\bvideos?\b|หยุดเพลง|พักเพลง|เล่นต่อ|ข้ามเพลง|เพลงถัดไป|เพลงก่อนหน้า|(?<!เ)ปิดเพลง|ปรับเสียง|เพิ่มเสียง|ลดเสียง|(?<!เ)ปิดเสียง|เปิดเสียง|\bmute\b|\bunmute\b|\bvolume\b|\bpause\b|\bresume\b|\bnext\b|\bprev\b|\bseek\b|\bshuffle\b|\brepeat\b|\bloop\b|วนซ้ำ|สุ่มเพลง|กรอเพลง|วินาทีที่|นาทีที่|เปิด\s*mv|ดู\s*mv)/i,
109
169
  clusters: ['MEDIA'],
110
- essentialTools: ['search_discography', 'get_music_videos', 'get_media_links', 'play_music', 'get_queue', 'search_youtube', 'get_current_track', 'get_music_status', 'search_bilibili', 'search_music_platforms', 'get_gallery_photos']
170
+ essentialTools: ['search_discography', 'get_music_videos', 'search_bilibili', 'search_music_platforms', 'search_youtube', 'get_media_links', 'play_music', 'get_current_track', 'get_music_status', 'get_queue', 'get_gallery_photos']
111
171
  },
112
172
  {
113
173
  type: 'WORKS',
114
- regex: /(ละคร|ซีรีส์|หนัง|ภาพยนตร์|บทบาท|ตัวละคร|ตอน|ช่อง|ฉาย|\bdramas?\b|\bseries\b|\bmovies?\b|\bfilms?\b|\broles?\b|\bcharacters?\b|\bsynops(?:is|es)\b|\bactors?\b|\btv\b|\bworks?\b|\bbroadcast(?:s|ing)?\b|\bweibo\b|影视|剧|新剧|角色|饰演)/i,
174
+ regex: /(ละคร|ซีรีส์|หนัง|ภาพยนตร์|บทบาท|ตัวละคร|ตอน|ช่อง|ฉาย|\bdramas?\b|\bseries\b|\bmovies?\b|\bfilms?\b|\broles?\b|\bcharacters?\b|\bsynops(?:is|es)\b|\bactors?\b|\btv\b|\bworks?\b|\bbroadcast(?:s|ing)?\b|影视|剧|新剧|角色|饰演)/i,
115
175
  clusters: ['CATALOG'],
116
176
  essentialTools: ['search_works', 'get_work_detail', 'get_character_roles', 'global_search', 'get_career_timeline', 'get_gallery_photos']
117
177
  },
@@ -129,20 +189,59 @@ const INTENT_PATTERNS = [
129
189
  },
130
190
  {
131
191
  type: 'RESEARCH_VERIFY',
132
- regex: /(ข่าว|ล่าสุด|จริงไหม|ข่าวลือ|สืบ|ตรวจสอบ|สัญญา|ศาล|\bstar48\b|\bsiba\b|\bnews\b|\bupdates?\b|\blatest\b|\brecent(?:ly)?\b|\brumors?\b|\bverify\b|\bclaims?\b|\bdisputes?\b|\blegal\b|\blawsuits?\b|\bcontracts?\b|\btruth\b|新闻|最新消息|动态|热搜|资讯|爆料|近况|百科|百度百科|维基|\bencyclopedia\b|\bbaike\b|\bwikipedia?\b)/i,
192
+ regex: /(ข่าว|ล่าสุด|ใหม่ล่าสุด|ตอนนี้|ช่วงนี้|ปัจจุบัน|ปีนี้|ตารางงาน|ความเคลื่อนไหว|จริงไหม|ข่าวลือ|สืบ|ตรวจสอบ|สัญญา|ศาล|\bstar48\b|\bsiba\b|\bnews\b|\bupdates?\b|\blatest\b|\brecent(?:ly)?\b|\brumors?\b|\bverify\b|\bclaims?\b|\bdisputes?\b|\blegal\b|\blawsuits?\b|\bcontracts?\b|\btruth\b|\bweibo\b|เว่ยป๋อ|微博|超话|\bxiaohongshu\b|เสี่ยวหงซู|小红书|\bdouyin\b|โต่วอิน|抖音|路透|开机|新剧|行程|\bcurrently\b|\bnowadays\b|\bthese days\b|新闻|最新消息|动态|热搜|资讯|爆料|近况|百科|百度百科|维基|\bencyclopedia\b|\bbaike\b|\bwikipedia?\b)/i,
133
193
  clusters: ['RESEARCH'],
134
- essentialTools: ['verify_claim', 'web_search', 'web_extract', 'fetch_webpage', 'run_subagent', 'run_deep_research', 'global_search', 'search_news', 'search_encyclopedia', 'search_chinese_web', 'get_gallery_photos']
194
+ essentialTools: ['verify_claim', 'web_search', 'web_extract', 'fetch_webpage', 'run_subagent', 'run_deep_research', 'global_search', 'search_news', 'search_encyclopedia', 'search_chinese_web']
135
195
  },
136
196
  {
137
197
  type: 'SYSTEM_SKILL',
138
- regex: /(\bskills?\b|\blearn(?:ing|ed)?\b|\bteach(?:ing|er)?\b|\btui\b|\bsystems?\b|\bstats\b|\bstatus\b|\bmemory\b|\bprefs?\b|\bfiles?\b|\bdirs?\b|\bdirector(?:y|ies)\b|สถิติ|จำนวน|ระบบ|สอน)/i,
198
+ regex: /(?:\bskills?\b|\bmanage_skill\b|\btui\b|\bsystems?\b|\bstats\b|\bstatus\b|\bmemory\b|\bprefs?\b|\bfiles?\b|\bdirs?\b|\bdirector(?:y|ies)\b|สถิติระบบ|สถานะระบบ|ระบบไฟล์|สอน\s*(?:kiku|บอต|ai|ระบบ|เธอ|คุณ|น้องจวี)|เรียนรู้ทักษะ|จัดการสกิล|\bteach\s*(?:kiku|the\s*bot|the\s*ai|the\s*system|you)\b|\blearn\s*(?:skill|preference|instruction)\b)/i,
139
199
  clusters: ['SYSTEM'],
140
200
  essentialTools: ['manage_skill', 'get_system_stats', 'read_local_file', 'list_directory']
201
+ },
202
+ {
203
+ type: 'LOCATION_WEATHER',
204
+ regex: /(สภาพอากาศ|พยากรณ์อากาศ|อากาศ|ฝนตก|อุณหภูมิ|กี่องศา|ร่ม|แดด|หนาว|ร้อน|พายุ|แถวนี้|ใกล้ฉัน|ที่นี่|พิกัด|ตำแหน่ง|\bweather\b|\btemperature\b|\bforecast\b|\brain\b|\bclimate\b|\bnearby\b|\baround here\b|\bhere\b|\blocation\b|\bcoords?\b|\bgeolocation\b)/i,
205
+ clusters: ['RESEARCH'],
206
+ essentialTools: ['get_weather', 'get_user_location', 'search_location_map', 'web_search', 'fetch_json', 'fetch_webpage']
207
+ },
208
+ {
209
+ type: 'MAP_NAVIGATION',
210
+ regex: /(แผนที่|ขอแผนที่|ปักหมุด|นำทาง|ทางไป|อยู่ตรงไหน|อยู่ที่ไหน|พิกัดของ|เปิดแมพ|แมพ|\bmap of\b|\bmap\b|\bosm\b|\bopenstreetmap\b|\bdirections?\b|\bnavigation\b|\bwhere is\b)/i,
211
+ clusters: ['RESEARCH'],
212
+ essentialTools: ['search_location_map', 'get_user_location', 'web_search']
141
213
  }
142
214
  ];
143
215
 
144
216
  const MAX_ROUTED_TOOLS = 15;
145
217
 
218
+ export function calculateSearchEscalationScore(prompt = '') {
219
+ const text = String(prompt || '');
220
+ let score = 0;
221
+
222
+ // Factor 1: Recency and temporal live markers
223
+ if (/(?:ล่าสุด|ใหม่ล่าสุด|ตอนนี้|ช่วงนี้|ปัจจุบัน|ปีนี้|ปี\s*(?:202[4-9]|256[7-9])|\b202[4-9]\b|latest|newest|recent(?:ly)?|current(?:ly)?|today|nowadays|these days|最新|近期|现在|近况)/i.test(text)) {
224
+ score += 15;
225
+ }
226
+
227
+ // Factor 2: Social media, live events, ongoing production
228
+ if (/(?:\bweibo\b|เว่ยป๋อ|微博|超话|\bxiaohongshu\b|เสี่ยวหงซู|小红书|\bdouyin\b|โต่วอิน|抖音|路透|开机|新剧|行程|ตารางงาน|งานอีเวนต์|อีเวนต์|แฟนมีต|แฟนมีตติ้ง|คอนเสิร์ต|ไลฟ์สด|\blive\b|直播)/i.test(text)) {
229
+ score += 15;
230
+ }
231
+
232
+ // Factor 3: News, verification, rumors, controversies
233
+ if (/(?:ข่าว|ข่าวลือ|จริงไหม|สืบ|อัปเดต|ความเคลื่อนไหว|สัญญากับ|\bstar48\b|\bsiba\b|\bnews\b|\brumor(?:s)?\b|\bupdates?\b|\bdispute|\blawsuit|\bcontroversy|新闻|资讯|动态|热搜|爆料)/i.test(text)) {
234
+ score += 20;
235
+ }
236
+
237
+ // Factor 4: Inquiring about external presence / web / internet
238
+ if (/(?:ค้นหาเว็บ|ค้นหาในเน็ต|เสิร์ช|กูเกิล|baidu|ไป่ตู้|เน็ต|อินเทอร์เน็ต|\bweb\b|\bsearch\b|\bgoogle\b|\bbaidu\b|上网查|网上|搜索)/i.test(text)) {
239
+ score += 15;
240
+ }
241
+
242
+ return Math.min(score, 60);
243
+ }
244
+
146
245
  export class DynamicToolRouter {
147
246
  constructor(allTools = toolsSchema) {
148
247
  this.allTools = Array.isArray(allTools) ? allTools : toolsSchema;
@@ -158,48 +257,56 @@ export class DynamicToolRouter {
158
257
  const text = String(prompt || '');
159
258
  const matched = [];
160
259
  const matchedTypes = new Set();
260
+ const resolution = resolveQueryEntities(text);
261
+ const intentScores = new Map();
161
262
 
263
+ const addScore = (type, score) => {
264
+ intentScores.set(type, (intentScores.get(type) || 0) + score);
265
+ };
162
266
 
163
267
  for (const pattern of INTENT_PATTERNS) {
164
268
  if (pattern.regex.test(text)) {
165
- matched.push(pattern);
166
- matchedTypes.add(pattern.type);
269
+ addScore(pattern.type, 3);
167
270
  }
168
271
  }
169
272
 
170
-
171
- const resolution = resolveQueryEntities(text);
172
-
173
- if ((resolution.hasBrand || resolution.isPromotion) && !matchedTypes.has('PROFILE_AWARDS_BRANDS')) {
174
- const brandPattern = INTENT_PATTERNS.find(p => p.type === 'PROFILE_AWARDS_BRANDS');
175
- if (brandPattern) {
176
- matched.push(brandPattern);
177
- matchedTypes.add('PROFILE_AWARDS_BRANDS');
178
- }
273
+ if (/(?:หยุดเพลง|พักเพลง|เล่นต่อ|ข้ามเพลง|เพลงถัดไป|เพลงก่อนหน้า|(?<!เ)ปิดเพลง|ปรับเสียง|เพิ่มเสียง|ลดเสียง|(?<!เ)ปิดเสียง|เปิดเสียง|\bmute\b|\bunmute\b|\bvolume\b|\bpause\b|\bresume\b|\bnext\b|\bprev\b|\bseek\b|\bshuffle\b|\brepeat\b|\bloop\b|วนซ้ำ|สุ่มเพลง|กรอเพลง|วินาทีที่|นาทีที่|เปิด\s*mv|ดู\s*mv|toggle_video)/i.test(text)) {
274
+ addScore('MEDIA', 5);
179
275
  }
276
+ if (resolution.hasSong) addScore('MEDIA', 4);
277
+ const mediaPlatforms = resolution.platforms.filter(p => ['bilibili', 'youtube', 'qqmusic', 'douyin'].includes(p));
278
+ if (mediaPlatforms.length > 0) addScore('MEDIA', 3);
180
279
 
181
- if (resolution.hasWork && !matchedTypes.has('WORKS')) {
182
- const workPattern = INTENT_PATTERNS.find(p => p.type === 'WORKS');
183
- if (workPattern) {
184
- matched.push(workPattern);
185
- matchedTypes.add('WORKS');
186
- }
187
- }
280
+ if (resolution.hasWork) addScore('WORKS', 4);
281
+
282
+ if (resolution.hasBrand || resolution.isPromotion) addScore('PROFILE_AWARDS_BRANDS', 4);
283
+ if (resolution.hasAward || resolution.isAwardIntent) addScore('PROFILE_AWARDS_BRANDS', 5);
188
284
 
189
- if (resolution.hasSong && !matchedTypes.has('MEDIA')) {
190
- const mediaPattern = INTENT_PATTERNS.find(p => p.type === 'MEDIA');
191
- if (mediaPattern) {
192
- matched.push(mediaPattern);
193
- matchedTypes.add('MEDIA');
285
+ const sortedIntents = [...intentScores.entries()]
286
+ .filter(([, score]) => score >= 3)
287
+ .sort((a, b) => b[1] - a[1]);
288
+
289
+ for (const [type, score] of sortedIntents) {
290
+ const p = INTENT_PATTERNS.find(item => item.type === type);
291
+ if (p) {
292
+ matched.push({ ...p, score });
293
+ matchedTypes.add(type);
194
294
  }
195
295
  }
196
296
 
197
- if (resolution.platforms.length > 0 && !matchedTypes.has('MEDIA')) {
198
- const mediaPattern = INTENT_PATTERNS.find(p => p.type === 'MEDIA');
199
- if (mediaPattern) {
200
- matched.push(mediaPattern);
201
- matchedTypes.add('MEDIA');
202
- }
297
+ const spectrum = classifyQuerySpectrum(text);
298
+ const hasCatalogSignal = resolution.matchedEntities.length > 0;
299
+ const offTopicEnforced = spectrum.verdict === 'OPEN_DOMAIN' && !hasCatalogSignal;
300
+ if (spectrum.verdict === 'OPEN_DOMAIN' || spectrum.bias < 0.4) {
301
+ matched.push({
302
+ type: 'OPEN_DOMAIN',
303
+ regex: /.*/,
304
+ clusters: ['RESEARCH'],
305
+ essentialTools: offTopicEnforced ? [...OPEN_DOMAIN_WEB_TOOLS] : ['web_search', 'web_extract', 'fetch_webpage', 'search_encyclopedia', 'search_news'],
306
+ score: offTopicEnforced ? 6 : Math.max(1, Math.round((1 - spectrum.bias) * 5)),
307
+ ...(offTopicEnforced ? { enforced: true } : {})
308
+ });
309
+ matchedTypes.add('OPEN_DOMAIN');
203
310
  }
204
311
 
205
312
  return matched;
@@ -245,8 +352,22 @@ export class DynamicToolRouter {
245
352
 
246
353
  const matchedIntents = this.detectIntents(prompt);
247
354
  const resolution = resolveQueryEntities(prompt);
355
+ const searchEscalationScore = calculateSearchEscalationScore(prompt);
356
+ const openIntent = matchedIntents.find(i => i.type === 'OPEN_DOMAIN');
357
+ const forceOpenDomain = Boolean(openIntent && openIntent.enforced)
358
+ && resolution.matchedEntities.length === 0;
359
+
360
+ if (searchEscalationScore >= 15) {
361
+ selectedNames.add('search_chinese_web');
362
+ selectedNames.add('search_news');
363
+ selectedNames.add('web_search');
364
+ selectedNames.add('web_extract');
365
+ highPriorityTools.add('search_chinese_web');
366
+ highPriorityTools.add('search_news');
367
+ highPriorityTools.add('web_search');
368
+ }
369
+
248
370
 
249
-
250
371
  for (const entity of resolution.matchedEntities) {
251
372
  for (const toolName of (entity.recommendedTools || [])) {
252
373
  selectedNames.add(toolName);
@@ -254,35 +375,135 @@ export class DynamicToolRouter {
254
375
  }
255
376
  }
256
377
 
257
-
378
+ for (const toolName of (resolution.recommendedTools || [])) {
379
+ selectedNames.add(toolName);
380
+ highPriorityTools.add(toolName);
381
+ }
382
+
258
383
  if (resolution.hasBrand || resolution.isPromotion) {
259
384
  selectedNames.add('get_brand_endorsements');
260
385
  highPriorityTools.add('get_brand_endorsements');
261
386
  }
262
387
 
388
+ if (resolution.hasAward || resolution.isAwardIntent) {
389
+ selectedNames.add('get_awards');
390
+ highPriorityTools.add('get_awards');
391
+ }
392
+
263
393
  if (resolution.platforms.includes('bilibili')) {
264
394
  selectedNames.add('search_bilibili');
265
- selectedNames.add('get_media_links');
266
395
  highPriorityTools.add('search_bilibili');
396
+ if (!forceOpenDomain) {
397
+ selectedNames.add('get_media_links');
398
+ highPriorityTools.add('get_media_links');
399
+ }
267
400
  }
268
401
 
269
402
  if (resolution.platforms.includes('youtube')) {
270
403
  selectedNames.add('search_youtube');
271
- selectedNames.add('get_media_links');
272
404
  highPriorityTools.add('search_youtube');
405
+ if (!forceOpenDomain) {
406
+ selectedNames.add('get_media_links');
407
+ highPriorityTools.add('get_media_links');
408
+ }
409
+ }
410
+
411
+ if (/(เปิดเพลง|เล่นเพลง|\bplay(?:_music)?\b)/i.test(prompt)) {
412
+ selectedNames.add('play_music');
413
+ highPriorityTools.add('play_music');
414
+ }
415
+ if (/(ปรับเสียง|เพิ่มเสียง|ลดเสียง|\bvolume\b)/i.test(prompt)) {
416
+ selectedNames.add('set_volume');
417
+ highPriorityTools.add('set_volume');
418
+ }
419
+ if (/(?:(?<!เ)ปิดเสียง|เปิดเสียง|\bmute\b|\bunmute\b)/i.test(prompt)) {
420
+ selectedNames.add('mute_music');
421
+ selectedNames.add('unmute_music');
422
+ highPriorityTools.add('mute_music');
423
+ highPriorityTools.add('unmute_music');
424
+ }
425
+ if (/(กรอเพลง|วินาทีที่|นาทีที่|\bseek\b)/i.test(prompt)) {
426
+ selectedNames.add('seek_music');
427
+ highPriorityTools.add('seek_music');
428
+ }
429
+ if (/(วนซ้ำ|\brepeat\b|\bloop\b)/i.test(prompt)) {
430
+ selectedNames.add('set_repeat_mode');
431
+ highPriorityTools.add('set_repeat_mode');
432
+ }
433
+ if (/(สุ่มเพลง|\bshuffle\b)/i.test(prompt)) {
434
+ selectedNames.add('set_shuffle_mode');
435
+ highPriorityTools.add('set_shuffle_mode');
436
+ }
437
+ if (/(เปิด\s*mv|ดู\s*mv|สลับ\s*mv|toggle_video)/i.test(prompt)) {
438
+ selectedNames.add('toggle_video_mode');
439
+ highPriorityTools.add('toggle_video_mode');
440
+ }
441
+ if (/(หยุดเพลง|พักเพลง|\bpause\b)/i.test(prompt)) {
442
+ selectedNames.add('pause_music');
443
+ highPriorityTools.add('pause_music');
444
+ }
445
+ if (/(เล่นต่อ|\bresume\b)/i.test(prompt)) {
446
+ selectedNames.add('resume_music');
447
+ highPriorityTools.add('resume_music');
448
+ }
449
+ if (/(ข้ามเพลง|เพลงถัดไป|\bnext\b)/i.test(prompt)) {
450
+ selectedNames.add('next_track');
451
+ highPriorityTools.add('next_track');
452
+ }
453
+ if (/(เพลงก่อนหน้า|\bprev\b)/i.test(prompt)) {
454
+ selectedNames.add('prev_track');
455
+ highPriorityTools.add('prev_track');
456
+ }
457
+ if (/(?:(?<!เ)ปิดเพลง|เลิกฟังเพลง|\bstop_music\b)/i.test(prompt)) {
458
+ selectedNames.add('stop_music');
459
+ highPriorityTools.add('stop_music');
460
+ }
461
+ if (/(สภาพอากาศ|พยากรณ์อากาศ|อากาศ|ฝนตก|อุณหภูมิ|กี่องศา|ร่ม|แดด|หนาว|ร้อน|พายุ|แถวนี้|ใกล้ฉัน|ที่นี่|พิกัด|ตำแหน่ง|\bweather\b|\btemperature\b|\bforecast\b|\brain\b|\bclimate\b|\bnearby\b|\baround here\b|\bhere\b|\blocation\b|\bcoords?\b|\bgeolocation\b)/i.test(prompt)) {
462
+ selectedNames.add('get_weather');
463
+ highPriorityTools.add('get_weather');
464
+ selectedNames.add('get_user_location');
465
+ highPriorityTools.add('get_user_location');
466
+ selectedNames.add('web_search');
467
+ highPriorityTools.add('web_search');
468
+ }
469
+ if (/(แผนที่|ขอแผนที่|ปักหมุด|นำทาง|ทางไป|อยู่ตรงไหน|อยู่ที่ไหน|พิกัดของ|เปิดแมพ|แมพ|\bmap of\b|\bmap\b|\bosm\b|\bopenstreetmap\b|\bdirections?\b|\bnavigation\b|\bwhere is\b)/i.test(prompt)) {
470
+ selectedNames.add('search_location_map');
471
+ highPriorityTools.add('search_location_map');
472
+ selectedNames.add('get_user_location');
273
473
  }
274
474
 
275
475
  if (matchedIntents.length === 0) {
276
- for (const name of DEFAULT_UNIVERSAL_TOOLS) {
476
+ const isDomain = isQueryInDomain(prompt) || /^(?:สวัสดี|hello|hi|hey|หวัดดี|ดีครับ|ดีค่ะ)/i.test(prompt);
477
+ const fallbackTools = isDomain ? DEFAULT_UNIVERSAL_TOOLS : DEFAULT_OPEN_DOMAIN_TOOLS;
478
+ for (const name of fallbackTools) {
277
479
  selectedNames.add(name);
278
480
  }
279
481
  } else {
482
+ const isOpenDomain = matchedIntents.some(i => i.type === 'OPEN_DOMAIN');
280
483
  for (const intent of matchedIntents) {
484
+ if (forceOpenDomain && CATALOG_INTENT_TYPES.has(intent.type)) {
485
+ continue;
486
+ }
487
+ if (isOpenDomain && !resolution.hasWork && !resolution.hasBrand && (intent.type === 'WORKS' || intent.type === 'PROFILE_AWARDS_BRANDS')) {
488
+ continue;
489
+ }
281
490
  for (const toolName of intent.essentialTools) {
282
491
  selectedNames.add(toolName);
283
492
  }
284
493
  }
285
- selectedNames.add('global_search');
494
+ if (isOpenDomain) {
495
+ highPriorityTools.add('web_search');
496
+ highPriorityTools.add('search_encyclopedia');
497
+ }
498
+ if (forceOpenDomain) {
499
+ for (const name of OPEN_DOMAIN_WEB_TOOLS) {
500
+ selectedNames.add(name);
501
+ highPriorityTools.add(name);
502
+ }
503
+ }
504
+ if (!forceOpenDomain) {
505
+ selectedNames.add('global_search');
506
+ }
286
507
  selectedNames.add('web_search');
287
508
  selectedNames.add('web_extract');
288
509
  }
@@ -313,13 +534,17 @@ export class DynamicToolRouter {
313
534
  }
314
535
 
315
536
  for (const intent of matchedIntents) {
537
+ if (forceOpenDomain && CATALOG_INTENT_TYPES.has(intent.type)) continue;
316
538
  for (const toolName of intent.essentialTools) {
317
539
  addToolByName(toolName);
318
540
  }
319
541
  }
320
542
 
321
543
  if (matchedIntents.length > 0) {
322
- for (const name of ['global_search', 'web_search', 'web_extract']) {
544
+ const guaranteed = forceOpenDomain
545
+ ? ['web_search', 'web_extract', 'search_encyclopedia', 'search_news']
546
+ : ['global_search', 'web_search', 'web_extract'];
547
+ for (const name of guaranteed) {
323
548
  addToolByName(name);
324
549
  }
325
550
  }
@@ -352,7 +577,56 @@ export class DynamicToolRouter {
352
577
  }
353
578
 
354
579
  if (bundled.length > MAX_ROUTED_TOOLS && !options.showAll) {
355
- return bundled.slice(0, MAX_ROUTED_TOOLS);
580
+ const priority = (toolName) => {
581
+ if (pinnedTools.some(t => t.function?.name === toolName)) return 100;
582
+ let score = 0;
583
+ if (highPriorityTools.has(toolName)) score += 20;
584
+ if (selectedNames.has(toolName)) score += 10;
585
+ if (searchEscalationScore > 0 && ['search_chinese_web', 'search_news', 'web_search'].includes(toolName)) {
586
+ score += Math.min(Math.round(searchEscalationScore * 0.4), 12);
587
+ }
588
+ for (let i = 0; i < matchedIntents.length; i++) {
589
+ const intent = matchedIntents[i];
590
+ const rank = intent.essentialTools ? intent.essentialTools.indexOf(toolName) : -1;
591
+ if (rank !== -1) {
592
+ score += Math.max(1, 15 - i * 3 - rank);
593
+ }
594
+ }
595
+ return score;
596
+ };
597
+
598
+ const ranked = [...bundled].sort((a, b) => {
599
+ const nameA = a.function?.name || a.name;
600
+ const nameB = b.function?.name || b.name;
601
+ return priority(nameB) - priority(nameA);
602
+ });
603
+
604
+ const kept = [];
605
+ const perCluster = new Map();
606
+ const overflow = [];
607
+
608
+ for (const tool of ranked) {
609
+ const name = tool.function?.name || tool.name;
610
+ if (pinnedTools.some(t => (t.function?.name || t.name) === name)) {
611
+ kept.push(tool);
612
+ continue;
613
+ }
614
+ const cluster = getToolCluster(name);
615
+ const count = perCluster.get(cluster) || 0;
616
+ if (count < 7) {
617
+ kept.push(tool);
618
+ perCluster.set(cluster, count + 1);
619
+ if (kept.length >= MAX_ROUTED_TOOLS) break;
620
+ } else {
621
+ overflow.push(tool);
622
+ }
623
+ }
624
+
625
+ while (kept.length < MAX_ROUTED_TOOLS && overflow.length > 0) {
626
+ kept.push(overflow.shift());
627
+ }
628
+
629
+ return kept;
356
630
  }
357
631
 
358
632
  return bundled;