argo-search 1.0.1 → 2.9.7

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 (463) hide show
  1. package/LICENSE +201 -21
  2. package/README.en.md +822 -0
  3. package/README.es.md +864 -0
  4. package/README.ja.md +864 -0
  5. package/README.ko.md +864 -0
  6. package/README.md +867 -320
  7. package/SKILL.md +135 -0
  8. package/assets/readme/hero.svg +121 -0
  9. package/assets/readme/made-with-beautify.svg +10 -0
  10. package/assets/readme/proof-routes.svg +73 -0
  11. package/assets/readme/settings-keys.png +0 -0
  12. package/assets/readme/settings-tools.png +0 -0
  13. package/assets/readme/why-better.svg +104 -0
  14. package/assets/readme/workflow.svg +83 -0
  15. package/backends/domain_profiles.json +2308 -96
  16. package/backends/engine_registry.yaml +3314 -503
  17. package/backends/query_synonyms_cn.json +35 -0
  18. package/backends/quota_profiles.json +2409 -161
  19. package/backends/source_types_cn.json +83 -0
  20. package/backends/text_lexicon.json +44 -0
  21. package/bin/argo +703 -0
  22. package/bin/argo.js +71 -39
  23. package/config.yaml +4976 -363
  24. package/cordis.patch.yml +21 -0
  25. package/data/countries.json +40 -0
  26. package/data/lang_matrix/lang_capability.json +842 -0
  27. package/data/lang_matrix/matrix_2026-09-10.json +11921 -0
  28. package/data/regions_cn.json +1 -0
  29. package/docs/ADDING_NEW_ENGINE.md +184 -0
  30. package/docs/ARGO_INTRO.md +124 -0
  31. package/docs/DATA_SOURCE_GAPS.md +81 -0
  32. package/docs/DESIGN_LOCAL_DATA_FUSION.md +90 -0
  33. package/docs/ENGINE_CATALOG.md +564 -0
  34. package/docs/MCP_SETUP.md +106 -0
  35. package/docs/PLATFORM_PATHS.md +111 -0
  36. package/docs/RELEASE_NOTES_v2.8.0.md +71 -0
  37. package/docs/RELEASE_NOTES_v2.8.2.md +63 -0
  38. package/docs/RELEASE_NOTES_v2.8.3.md +55 -0
  39. package/docs/RELEASE_NOTES_v2.8.4.md +79 -0
  40. package/docs/RELEASE_NOTES_v2.8.5.md +102 -0
  41. package/docs/RELEASE_NOTES_v2.8.6.md +66 -0
  42. package/docs/RELEASE_NOTES_v2.8.7.md +65 -0
  43. package/docs/RELEASE_NOTES_v2.8.8.md +67 -0
  44. package/docs/RELEASE_NOTES_v2.8.9.md +67 -0
  45. package/docs/RELEASE_NOTES_v2.9.0.md +64 -0
  46. package/docs/SEARCH_ARCHIVE.md +115 -0
  47. package/docs/assets/architecture.svg +104 -0
  48. package/docs/assets/hero.svg +98 -0
  49. package/docs/engines/bangumi.md +27 -0
  50. package/docs/engines/biorxiv.md +27 -0
  51. package/docs/engines/clawhub.md +27 -0
  52. package/docs/engines/crt_sh.md +27 -0
  53. package/docs/engines/datacite.md +27 -0
  54. package/docs/engines/deps_dev.md +27 -0
  55. package/docs/engines/douban_movie.md +27 -0
  56. package/docs/engines/endoflife.md +27 -0
  57. package/docs/engines/flk_law.md +27 -0
  58. package/docs/engines/fxtwitter.md +27 -0
  59. package/docs/engines/google_news.md +27 -0
  60. package/docs/engines/gov_regulations.md +27 -0
  61. package/docs/engines/guqian.md +27 -0
  62. package/docs/engines/hackernews.md +27 -0
  63. package/docs/engines/iplant.md +27 -0
  64. package/docs/engines/keenable.md +27 -0
  65. package/docs/engines/met_museum.md +27 -0
  66. package/docs/engines/nasa_images.md +27 -0
  67. package/docs/engines/netease_music.md +27 -0
  68. package/docs/engines/npm.md +27 -0
  69. package/docs/engines/nvd.md +27 -0
  70. package/docs/engines/openreview.md +27 -0
  71. package/docs/engines/openstd.md +27 -0
  72. package/docs/engines/parallel_free.md +27 -0
  73. package/docs/engines/people_daily.md +27 -0
  74. package/docs/engines/realtime_index.md +27 -0
  75. package/docs/engines/sec_edgar.md +27 -0
  76. package/docs/engines/seltz.md +27 -0
  77. package/docs/engines/skillsmp.md +27 -0
  78. package/docs/engines/sspai.md +27 -0
  79. package/docs/engines/std_samr.md +27 -0
  80. package/docs/engines/train.md +27 -0
  81. package/docs/engines/un_comtrade.md +27 -0
  82. package/docs/engines/wikisource.md +27 -0
  83. package/docs/engines/zdic.md +27 -0
  84. package/docs/engines/zenodo.md +27 -0
  85. package/docs/engines/zhihu_hot_app.md +27 -0
  86. package/docs//344/270/272/344/273/200/344/271/210/351/200/211/346/213/251argo.md +176 -0
  87. package/engines/README.md +10 -0
  88. package/engines/_template_cli.yaml +39 -0
  89. package/engines/_template_http.yaml +37 -0
  90. package/engines/plugins/_template_plugin.py +50 -0
  91. package/engines/specs/_cisa_kev.yaml +54 -0
  92. package/engines/specs/_osv.yaml +63 -0
  93. package/engines/specs/aljazeera.yaml +43 -0
  94. package/engines/specs/antara.yaml +43 -0
  95. package/engines/specs/artic.yaml +20 -0
  96. package/engines/specs/aviation_weather.yaml +39 -0
  97. package/engines/specs/bangumi.yaml +21 -0
  98. package/engines/specs/biorxiv.yaml +24 -0
  99. package/engines/specs/carbon_intensity.yaml +32 -0
  100. package/engines/specs/cinii.yaml +44 -0
  101. package/engines/specs/clawhub.yaml +49 -0
  102. package/engines/specs/cleveland.yaml +19 -0
  103. package/engines/specs/cn_ai_news.yaml +43 -0
  104. package/engines/specs/core.yaml +35 -0
  105. package/engines/specs/crt_sh.yaml +38 -0
  106. package/engines/specs/ct24.yaml +43 -0
  107. package/engines/specs/datacite.yaml +27 -0
  108. package/engines/specs/datagov_uk.yaml +41 -0
  109. package/engines/specs/ddgs_images.yaml +45 -0
  110. package/engines/specs/deepseek_search.yaml +74 -0
  111. package/engines/specs/deezer.yaml +20 -0
  112. package/engines/specs/deps_dev.yaml +21 -0
  113. package/engines/specs/digup.yaml +36 -0
  114. package/engines/specs/doi.yaml +39 -0
  115. package/engines/specs/douban_movie.yaml +22 -0
  116. package/engines/specs/dw_news.yaml +47 -0
  117. package/engines/specs/egov_law.yaml +21 -0
  118. package/engines/specs/elpais.yaml +43 -0
  119. package/engines/specs/endoflife.yaml +21 -0
  120. package/engines/specs/energy_charts.yaml +21 -0
  121. package/engines/specs/factcheck_org.yaml +52 -0
  122. package/engines/specs/faz.yaml +43 -0
  123. package/engines/specs/federal_register.yaml +57 -0
  124. package/engines/specs/firecrawl.yaml +53 -0
  125. package/engines/specs/flk_law.yaml +24 -0
  126. package/engines/specs/folha.yaml +43 -0
  127. package/engines/specs/france24.yaml +44 -0
  128. package/engines/specs/full_fact.yaml +47 -0
  129. package/engines/specs/fxtwitter.yaml +31 -0
  130. package/engines/specs/gbfs_nyc.yaml +32 -0
  131. package/engines/specs/gdacs.yaml +21 -0
  132. package/engines/specs/gdelt.yaml +64 -0
  133. package/engines/specs/google_news.yaml +22 -0
  134. package/engines/specs/gov_regulations.yaml +38 -0
  135. package/engines/specs/guardian_rss.yaml +59 -0
  136. package/engines/specs/hurriyet.yaml +43 -0
  137. package/engines/specs/iplant.yaml +25 -0
  138. package/engines/specs/jikan.yaml +24 -0
  139. package/engines/specs/jolpica.yaml +20 -0
  140. package/engines/specs/k10plus.yaml +20 -0
  141. package/engines/specs/keenable.yaml +44 -0
  142. package/engines/specs/lefigaro.yaml +43 -0
  143. package/engines/specs/listenbrainz.yaml +19 -0
  144. package/engines/specs/matichon.yaml +43 -0
  145. package/engines/specs/met_museum.yaml +21 -0
  146. package/engines/specs/nasa_images.yaml +45 -0
  147. package/engines/specs/netease_music.yaml +38 -0
  148. package/engines/specs/nhk.yaml +43 -0
  149. package/engines/specs/nhtsa_vpic.yaml +20 -0
  150. package/engines/specs/noaa_swpc.yaml +20 -0
  151. package/engines/specs/nvd.yaml +38 -0
  152. package/engines/specs/obis.yaml +20 -0
  153. package/engines/specs/opencitations.yaml +52 -0
  154. package/engines/specs/openf1.yaml +21 -0
  155. package/engines/specs/openfoodfacts.yaml +45 -0
  156. package/engines/specs/openligadb.yaml +20 -0
  157. package/engines/specs/openreview.yaml +37 -0
  158. package/engines/specs/openstd.yaml +21 -0
  159. package/engines/specs/people_daily.yaml +22 -0
  160. package/engines/specs/realtime_index.yaml +34 -0
  161. package/engines/specs/ror.yaml +20 -0
  162. package/engines/specs/satnogs.yaml +20 -0
  163. package/engines/specs/sec_edgar.yaml +46 -0
  164. package/engines/specs/skillsmp.yaml +50 -0
  165. package/engines/specs/sogou_weixin_browser.yaml +30 -0
  166. package/engines/specs/soilgrids.yaml +25 -0
  167. package/engines/specs/sspai.yaml +36 -0
  168. package/engines/specs/stackexchange.yaml +46 -0
  169. package/engines/specs/std_samr.yaml +21 -0
  170. package/engines/specs/tass.yaml +43 -0
  171. package/engines/specs/tle_mirror.yaml +21 -0
  172. package/engines/specs/train.yaml +37 -0
  173. package/engines/specs/tvmaze.yaml +20 -0
  174. package/engines/specs/twitter_syndication.yaml +31 -0
  175. package/engines/specs/un_comtrade.yaml +23 -0
  176. package/engines/specs/unpaywall.yaml +54 -0
  177. package/engines/specs/vnpress.yaml +43 -0
  178. package/engines/specs/weather.yaml +32 -0
  179. package/engines/specs/who_don.yaml +21 -0
  180. package/engines/specs/who_gho.yaml +20 -0
  181. package/engines/specs/wikimedia_commons.yaml +34 -0
  182. package/engines/specs/wikisource.yaml +22 -0
  183. package/engines/specs/worms.yaml +19 -0
  184. package/engines/specs/x_trending.yaml +34 -0
  185. package/engines/specs/yna.yaml +43 -0
  186. package/engines/specs/ynet.yaml +43 -0
  187. package/engines/specs/zbmath.yaml +40 -0
  188. package/engines/specs/zdic.yaml +22 -0
  189. package/engines/specs/zenodo.yaml +27 -0
  190. package/engines/specs/zhihu_hot_app.yaml +35 -0
  191. package/mcp/clients.yaml +74 -0
  192. package/package.json +51 -12
  193. package/packages/dsh-plugin/README.md +134 -0
  194. package/packages/dsh-plugin/cordis.patch.yml +48 -0
  195. package/packages/dsh-plugin/dsh/client.js +1244 -0
  196. package/packages/dsh-plugin/dsh/index.js +2290 -0
  197. package/packages/dsh-plugin/dsh/native-tools.mjs +743 -0
  198. package/packages/dsh-plugin/package.json +38 -0
  199. package/packages/dsh-plugin/test/client.test.js +232 -0
  200. package/packages/dsh-plugin/test/config.test.js +30 -0
  201. package/packages/dsh-plugin/test/index.test.js +1338 -0
  202. package/references/query-templates.md +96 -0
  203. package/references/query-templates.yaml +118 -0
  204. package/scripts/ab_eval.py +219 -0
  205. package/scripts/ab_eval_p0p1.py +266 -0
  206. package/scripts/adaptive.py +271 -70
  207. package/scripts/answer.py +154 -0
  208. package/scripts/archive_run.py +591 -0
  209. package/scripts/argo_engine_registry.py +172 -0
  210. package/scripts/argo_paths.py +728 -0
  211. package/scripts/article.py +136 -0
  212. package/scripts/arxiv_source.py +201 -0
  213. package/scripts/badcase_ledger.py +252 -0
  214. package/scripts/batch_probe.py +241 -0
  215. package/scripts/benchmark.py +19 -0
  216. package/scripts/bounded_run.py +144 -0
  217. package/scripts/browser_auth.py +323 -0
  218. package/scripts/burstiness_detector.py +147 -0
  219. package/scripts/cache.py +1078 -67
  220. package/scripts/cache_guard.py +271 -0
  221. package/scripts/cache_key_vdom.py +56 -0
  222. package/scripts/cache_similarity.py +170 -0
  223. package/scripts/caliber_isolation.py +195 -0
  224. package/scripts/calibrate_lowquality.py +210 -0
  225. package/scripts/candidate_envelope.py +366 -0
  226. package/scripts/chrome_cdp.py +871 -0
  227. package/scripts/circuit_breaker.py +389 -0
  228. package/scripts/citations.py +334 -0
  229. package/scripts/cite_cli.py +59 -0
  230. package/scripts/cjk_tokens.py +95 -0
  231. package/scripts/clarify.py +47 -24
  232. package/scripts/cli_io.py +240 -0
  233. package/scripts/config.py +683 -45
  234. package/scripts/config_cache.py +402 -0
  235. package/scripts/conn_pool.py +220 -0
  236. package/scripts/content_security.py +685 -0
  237. package/scripts/content_signals.py +730 -0
  238. package/scripts/crawl.py +74 -35
  239. package/scripts/daemon.py +866 -0
  240. package/scripts/digup_bridge.py +214 -0
  241. package/scripts/engine_admission.py +366 -0
  242. package/scripts/engine_dispatch.py +1019 -0
  243. package/scripts/engine_env.py +534 -0
  244. package/scripts/engine_failure.py +411 -0
  245. package/scripts/engine_families.py +732 -0
  246. package/scripts/engine_policy.py +218 -0
  247. package/scripts/engine_requires.py +190 -0
  248. package/scripts/engine_status.py +322 -0
  249. package/scripts/engine_validate.py +638 -0
  250. package/scripts/engines.py +778 -294
  251. package/scripts/engines_base.py +1521 -0
  252. package/scripts/engines_builders.py +298 -0
  253. package/scripts/engines_builders_batch10.py +159 -0
  254. package/scripts/engines_builders_batch11.py +174 -0
  255. package/scripts/engines_builders_batch12.py +103 -0
  256. package/scripts/engines_builders_batch13.py +340 -0
  257. package/scripts/engines_builders_batch9.py +1468 -0
  258. package/scripts/engines_builders_cn.py +2283 -0
  259. package/scripts/engines_builders_data.py +2923 -0
  260. package/scripts/engines_builders_data_macro.py +775 -0
  261. package/scripts/engines_builders_feeds.py +255 -0
  262. package/scripts/engines_builders_intl.py +1239 -0
  263. package/scripts/engines_builders_search.py +440 -0
  264. package/scripts/engines_builders_tech.py +1360 -0
  265. package/scripts/evidence.py +588 -162
  266. package/scripts/evidence_loop.py +592 -0
  267. package/scripts/evidence_tier.py +298 -0
  268. package/scripts/except_sets.py +25 -0
  269. package/scripts/extract.py +55 -16
  270. package/scripts/fact_align.py +207 -0
  271. package/scripts/fact_crosscheck.py +230 -0
  272. package/scripts/fetch.py +25 -12
  273. package/scripts/fetch_browser.py +87 -0
  274. package/scripts/fetch_jsrun.py +122 -0
  275. package/scripts/fetch_quality.py +225 -0
  276. package/scripts/fetch_render_tinyfish.py +117 -0
  277. package/scripts/fetch_v3.py +1779 -0
  278. package/scripts/focus_extract.py +350 -0
  279. package/scripts/fulltext_store.py +169 -0
  280. package/scripts/fxtwitter_api.py +325 -0
  281. package/scripts/gen_engine_catalog.py +364 -0
  282. package/scripts/gen_native_tools.py +91 -0
  283. package/scripts/gen_routing_snapshot.py +184 -0
  284. package/scripts/health_check.py +140 -402
  285. package/scripts/health_probe.py +23 -8
  286. package/scripts/hot_state.py +134 -0
  287. package/scripts/http_client.py +1053 -0
  288. package/scripts/image_ops.py +430 -0
  289. package/scripts/image_vision/vision_probe +0 -0
  290. package/scripts/image_vision/vision_probe.swift +126 -0
  291. package/scripts/install.ps1 +148 -0
  292. package/scripts/install.sh +206 -0
  293. package/scripts/job.py +1186 -0
  294. package/scripts/jump_resolver.py +235 -0
  295. package/scripts/lang_capability.py +150 -0
  296. package/scripts/lang_detect.py +289 -0
  297. package/scripts/lang_matrix_probe.py +329 -0
  298. package/scripts/lang_pref.py +311 -0
  299. package/scripts/link_source.py +340 -0
  300. package/scripts/local_engines.py +321 -0
  301. package/scripts/local_image.py +970 -0
  302. package/scripts/local_seek.py +366 -0
  303. package/scripts/macro_countries.py +82 -0
  304. package/scripts/matrix_search_eval.py +1105 -0
  305. package/scripts/mcp_diag.py +8 -3
  306. package/scripts/mcp_handlers.py +1074 -0
  307. package/scripts/mcp_handlers_surface.py +157 -0
  308. package/scripts/mcp_launch.sh +122 -0
  309. package/scripts/mcp_server.py +118 -417
  310. package/scripts/mcp_setup.py +407 -0
  311. package/scripts/mcp_tools.py +330 -0
  312. package/scripts/mcp_transport.py +191 -0
  313. package/scripts/min_version_scan.py +335 -0
  314. package/scripts/net_proxy.py +258 -0
  315. package/scripts/network_aware.py +152 -0
  316. package/scripts/paper.py +274 -0
  317. package/scripts/pdf_extract.py +518 -0
  318. package/scripts/phrase_filter.py +66 -0
  319. package/scripts/plan.py +422 -0
  320. package/scripts/postprocess_signals.py +150 -0
  321. package/scripts/pre-commit +48 -0
  322. package/scripts/query_enhance.py +157 -0
  323. package/scripts/query_operators.py +196 -0
  324. package/scripts/query_rewriter.py +116 -7
  325. package/scripts/query_signals.py +300 -0
  326. package/scripts/query_templates.py +167 -0
  327. package/scripts/query_understanding.py +411 -0
  328. package/scripts/query_variants.py +298 -0
  329. package/scripts/quota.py +488 -76
  330. package/scripts/rank_signals.py +775 -0
  331. package/scripts/ranking_eval.py +319 -0
  332. package/scripts/readability_extract.py +433 -0
  333. package/scripts/recompute.py +415 -0
  334. package/scripts/recovery.py +616 -0
  335. package/scripts/recovery_l0.py +160 -0
  336. package/scripts/redskill/redskill_engine.py +332 -0
  337. package/scripts/regression_p0p1.py +318 -0
  338. package/scripts/replay_eval.py +640 -0
  339. package/scripts/rerank_tfidf.py +225 -0
  340. package/scripts/research.py +591 -403
  341. package/scripts/research_cli.py +361 -0
  342. package/scripts/research_dossier.py +492 -0
  343. package/scripts/research_expand.py +146 -0
  344. package/scripts/research_gates.py +272 -0
  345. package/scripts/research_report.py +209 -0
  346. package/scripts/research_strategy.py +37 -0
  347. package/scripts/research_work_packages.py +195 -0
  348. package/scripts/result_lang.py +311 -0
  349. package/scripts/robots_guard.py +259 -0
  350. package/scripts/route.py +921 -254
  351. package/scripts/route_bangs.py +40 -0
  352. package/scripts/route_cache.py +225 -0
  353. package/scripts/route_combo.py +910 -0
  354. package/scripts/route_domains.py +328 -0
  355. package/scripts/route_lang.py +605 -0
  356. package/scripts/route_log.py +63 -0
  357. package/scripts/route_policy.py +250 -0
  358. package/scripts/rrf_classic.py +66 -0
  359. package/scripts/screenshot.py +73 -0
  360. package/scripts/search.py +897 -405
  361. package/scripts/search_attribution.py +88 -0
  362. package/scripts/search_benchmark.py +407 -0
  363. package/scripts/search_cli.py +627 -0
  364. package/scripts/search_entry.py +389 -0
  365. package/scripts/search_output.py +606 -0
  366. package/scripts/search_pipeline.py +1077 -0
  367. package/scripts/search_rank.py +991 -0
  368. package/scripts/search_types.py +27 -12
  369. package/scripts/seek_locator.py +68 -0
  370. package/scripts/semantic_evidence.py +215 -0
  371. package/scripts/serp_guard.py +228 -0
  372. package/scripts/serp_kill_log.py +51 -0
  373. package/scripts/single_flight.py +120 -0
  374. package/scripts/social_engines/__init__.py +10 -0
  375. package/scripts/social_engines/bilibili_engine.py +131 -0
  376. package/scripts/social_engines/hackernews_engine.py +113 -0
  377. package/scripts/social_engines/reddit_engine.py +172 -0
  378. package/scripts/social_engines/twitter_engine.py +397 -0
  379. package/scripts/social_engines/v2ex_engine.py +101 -0
  380. package/scripts/social_engines/weibo_engine.py +128 -0
  381. package/scripts/social_engines/xiaohongshu_engine.py +137 -0
  382. package/scripts/social_engines/zhihu_engine.py +139 -0
  383. package/scripts/social_research.py +201 -0
  384. package/scripts/sogou_weixin_browser.py +318 -0
  385. package/scripts/source_ledger.py +249 -0
  386. package/scripts/stage_timing.py +58 -0
  387. package/scripts/stats_cli.py +95 -0
  388. package/scripts/straggler_policy.py +60 -0
  389. package/scripts/stylometry_detector.py +231 -0
  390. package/scripts/sync_backends.py +353 -0
  391. package/scripts/sync_installs.py +29 -0
  392. package/scripts/template_cli.py +127 -0
  393. package/scripts/text_lexicon.py +72 -0
  394. package/scripts/tfidf_router.py +109 -37
  395. package/scripts/time_utils.py +233 -0
  396. package/scripts/topic_research_profiles.py +643 -0
  397. package/scripts/train.py +737 -0
  398. package/scripts/tweet.py +596 -0
  399. package/scripts/url_canon.py +217 -0
  400. package/scripts/url_optimize.py +56 -0
  401. package/scripts/url_safety.py +236 -0
  402. package/scripts/usage_log.py +116 -0
  403. package/scripts/usage_log_clean.py +113 -0
  404. package/scripts/v2ex_nodes.py +311 -0
  405. package/scripts/variant_recall.py +188 -0
  406. package/scripts/watch.py +205 -0
  407. package/scripts/wechat_article.py +258 -0
  408. package/scripts/wide_research.py +402 -0
  409. package/scripts/wx.py +349 -0
  410. package/scripts/x_article_fetch.py +141 -0
  411. package/scripts/x_search.py +234 -0
  412. package/scripts/x_trending.py +189 -0
  413. package/scripts/yaml_load.py +67 -0
  414. package/sub-skills/ego-search/SKILL.md +253 -0
  415. package/sub-skills/ego-search/references/browser-runtime.md +43 -0
  416. package/sub-skills/ego-search/references/install.md +67 -0
  417. package/sub-skills/ego-search/references/original-ego-upgrade.md +51 -0
  418. package/sub-skills/ego-search/scripts/ego_search.py +800 -0
  419. package/sub-skills/ego-search/scripts/merge.py +226 -0
  420. package/sub-skills/ego-search/scripts/quality.py +64 -0
  421. package/sub-skills/ego-search/scripts/runtime.py +172 -0
  422. package/sub-skills/ego-search/scripts/safety.py +109 -0
  423. package/sub-skills/ego-search/scripts/serp_spec.py +109 -0
  424. package/sub-skills/ego-search/scripts/webbridge_adapter.py +374 -0
  425. package/sub-skills/ego-search/tests/test_query_normalize.py +68 -0
  426. package/sub-skills/js-run/SKILL.md +67 -0
  427. package/sub-skills/js-run/scripts/jsrun.py +112 -0
  428. package/sub-skills/js-run/scripts/shim.js +188 -0
  429. package/sub-skills/js-run/tests/fixtures/challenge_like.js +27 -0
  430. package/sub-skills/js-run/tests/test_jsrun.py +165 -0
  431. package/sub-skills/local-search/SKILL.md +50 -53
  432. package/sub-skills/local-search/bm25.py +162 -0
  433. package/sub-skills/local-search/config.yaml +152 -55
  434. package/sub-skills/local-search/engine_registry.py +91 -26
  435. package/sub-skills/local-search/{health_check.py → local_health_check.py} +140 -10
  436. package/sub-skills/local-search/local_search_adapter.py +13 -2
  437. package/sub-skills/local-search/parse_maps.yaml +40 -42
  438. package/sub-skills/local-search/search_v3.py +1182 -65
  439. package/sub-skills/local-search/smart_router.py +47 -7
  440. package/sub-skills/local-seek/SKILL.md +110 -0
  441. package/sub-skills/local-seek/config/domains.yaml +20 -0
  442. package/sub-skills/local-seek/references/auto-behaviors.md +24 -0
  443. package/sub-skills/local-seek/references/fd-mdfind-recipes.md +62 -0
  444. package/sub-skills/local-seek/references/git-integration.md +54 -0
  445. package/sub-skills/local-seek/references/remote-handoff.md +46 -0
  446. package/sub-skills/local-seek/references/rg-recipes.md +96 -0
  447. package/sub-skills/local-seek/references/strategies.md +52 -0
  448. package/sub-skills/local-seek/references/structural-search.md +59 -0
  449. package/sub-skills/local-seek/scripts/eval_rank.py +216 -0
  450. package/sub-skills/local-seek/scripts/eval_seek.py +224 -0
  451. package/sub-skills/local-seek/scripts/seek.py +1465 -0
  452. package/sub-skills/local-seek/scripts/seek_frecency.py +126 -0
  453. package/sub-skills/local-seek/scripts/seek_fts.py +430 -0
  454. package/sub-skills/local-seek/scripts/seek_rank.py +193 -0
  455. package/sub-skills/local-seek/tests/test_content_rank.py +222 -0
  456. package/sub-skills/local-seek/tests/test_determinism_20261006.py +145 -0
  457. package/sub-skills/local-seek/tests/test_noise_tier.py +101 -0
  458. package/sub-skills/local-seek/tests/test_pcre2_probe.py +136 -0
  459. package/sub-skills/local-seek/tests/test_rank_pinyin.py +77 -0
  460. package/sub-skills/local-seek/tests/test_regression_20261003.py +169 -0
  461. package/sub-skills/local-seek/tests/test_regression_20261006.py +146 -0
  462. package/sub-skills/local-seek/tests/test_seek_degradation.py +111 -0
  463. package/sub-skills/local-seek/tests/test_seek_fts.py +313 -0
@@ -0,0 +1,389 @@
1
+ #!/usr/bin/env python3
2
+ """search_entry.py — 搜索的入口两段:准备(prepare)与调度(dispatch)。
3
+
4
+ 与 search_pipeline 的分工(四个阶段合起来就是一次搜索的全过程):
5
+
6
+ prepare 请求侧整理 + 缓存查询(命中即返回存档响应,不再打网络)
7
+ dispatch 引擎编排(并发/串行、重试、熔断、结局分类)——实现在 engine_dispatch
8
+ postprocess 融合 + 13 个加工阶段(search_pipeline)
9
+ finalize 缓存写 + 记账 + 对外响应(search_pipeline)
10
+
11
+ ## 为什么把 prepare/dispatch 单独成模块
12
+
13
+ 它们是**唯一**会打网络、也是唯一依赖「可打桩入口」的两段:测试靠
14
+ `patch.object(search, "engine_search" / "get_engines" / "_missing_env_for" /
15
+ "_FAST_TOTAL_BUDGET_S" / "_PRIMARY_GRACE_S")` 换掉引擎与预算。这些入口与常量
16
+ **按值传入**(`_SearchHooks`),不让本模块反向 import search——直连绑定会让补丁
17
+ 静默失效(假引擎不被调用、真网络被打开)。
18
+
19
+ execute_search 因此只剩编排:prepare → (命中则返回)→ dispatch → postprocess →
20
+ finalize,五行。
21
+
22
+ ## 边界
23
+
24
+ 本模块不 import http_client / engines_base;网络只经由传入的 hooks 与
25
+ engine_dispatch。
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import time
31
+ from dataclasses import dataclass, field, replace
32
+ from typing import Any, Callable, Optional
33
+
34
+ from engine_dispatch import run_dispatch
35
+ from usage_log import emit as _emit_usage
36
+ from search_output import _collect_errors
37
+ from search_pipeline import _SearchRequest, _SearchRun
38
+ from search_types import Stage
39
+ from stage_timing import StageTiming, tick as _tick, tock as _tock
40
+ from time_utils import (
41
+ is_time_capable as _is_time_capable,
42
+ normalize_time_window as _normalize_time_window,
43
+ sort_results_by_time as _sort_results,
44
+ )
45
+
46
+
47
+ def _log(message: str) -> None:
48
+ """默认静默的调试出口;仅在真正需要记录时才引入 logging。
49
+
50
+ 与 search_output._log 同一范式。本模块的 prepare 曾有两处**函数内**裸
51
+ `import logging`:CPython 在编译期就把 `logging` 定为 prepare 的局部名,
52
+ 任何早于它们的分支再加一处 `logging.getLogger(...)`,另外两处立刻编译成
53
+ LOAD_FAST → UnboundLocalError,而它们全在 except 处理器内,处理器再抛会
54
+ 顶替掉原本被 fail-open 吞掉的错误。模块级不留绑定:既无局部遮蔽,也不把
55
+ logging→traceback→dataclasses→inspect(实测 21 ms)拖进 import search
56
+ 的必经之路。
57
+ """
58
+ import logging
59
+ logging.getLogger("unified_search").debug(message)
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class _SearchHooks:
64
+ """可打桩的入口与常量(按值传入,见模块 docstring)。"""
65
+
66
+ engine_search: Any
67
+ available_engines: Any
68
+ get_cost_factor: Any
69
+ get_engines: Any
70
+ get_execution_config: Any
71
+ missing_env_for: Any
72
+ classify_outcome: Any
73
+ note_quota_exhausted: Any
74
+ per_engine_budget_s: float
75
+ fast_budget_s: float
76
+ auto_budget_s: float
77
+ primary_grace_s: float
78
+ straggler_grace_s: float
79
+ serial_stagger_s: float
80
+ deep_budget_s: float | None = None
81
+ # 尾宽分层函数(可选,默认 None = 固定尾宽):dataclass 默认值字段只能
82
+ # 排在无默认值字段之后,故放在末尾。
83
+ straggler_tail_fn: Callable[[str, float], float] | None = None
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class _Prepared:
88
+ """准备阶段的产出——两种结局显式分开,不用哨兵值:
89
+
90
+ - 命中缓存:`cached` 非空(调用方直接返回它,不再打网络),req/run 为空;
91
+ - 未命中:`req`/`run` 就位,交给 dispatch。
92
+ """
93
+
94
+ cached: dict[str, Any] | None = None
95
+ req: _SearchRequest | None = None
96
+ run: _SearchRun | None = None
97
+
98
+
99
+
100
+ def prepare(query: str, decision: dict[str, Any], max_results: int,
101
+ timeout: int, depth: str, cache: Any, skip_cache: bool,
102
+ *, mode: str = "auto", since: str | None = None,
103
+ until: str | None = None, sort: str = "relevance",
104
+ on_progress: Optional[Callable[[Stage, dict[str, Any]], None]] = None,
105
+ engine_domain: str | None = None,
106
+ engine_sub_domain: str | None = None,
107
+ timing: StageTiming | None = None,
108
+ hooks: _SearchHooks) -> _Prepared:
109
+ """请求侧整理 + 缓存查询;命中时 `cached` 非空(调用方直接返回它)。
110
+
111
+ 段内顺序即契约:查询理解 → 词形规范化 → 进度事件 → 网络感知超时 →
112
+ 时间窗归一化 → 缓存键 → 缓存查询。
113
+ """
114
+ domain = decision.get("domain") or "general"
115
+ engine_label = decision.get("engine", "auto")
116
+ engines_combo = decision.get("engines_combo", decision.get("engines", [engine_label]))
117
+ # 防御:过滤空引擎名(空串会在 registry 查无 → 空结果 → 熔断空键 '')。
118
+ # 全空时保底 anysearch,防止下方 engines[0] IndexError(route 层已保证
119
+ # combo 非空,此处仅防畸形 decision 直接调用 execute_search)。
120
+ engines = [e for e in engines_combo if e] or ["anysearch"]
121
+ parallel = decision.get("parallel", False) and len(engines) > 1
122
+
123
+ # P0-001:查询理解 — clean_query 用于检索,exclude_terms 用于融合后过滤
124
+ exclude_terms: list[str] = []
125
+ retrieval_query = query
126
+ qu = None
127
+ try:
128
+ from query_understanding import _understand_cached as understand
129
+ qu = understand(query)
130
+ exclude_terms = qu.exclude_terms
131
+ # 查询操作符 -词/-"短语" 的排除项汇合(2026-10-17 query_operators):
132
+ # 与否定理解共用同一「融合后过滤」通道,去重保序。
133
+ _op_excl = ((decision or {}).get("operators") or {}).get("exclude_terms") or []
134
+ for t in _op_excl:
135
+ if t and t not in exclude_terms:
136
+ exclude_terms.append(t)
137
+ # 仅当去否定片段后仍有实义内容时才替换检索词,避免空检索
138
+ if qu.clean_query and qu.clean_query.strip():
139
+ retrieval_query = qu.clean_query
140
+ except ImportError:
141
+ pass # query_understanding 不可用
142
+ except Exception as e:
143
+ _log(f"查询理解跳过: {type(e).__name__}")
144
+
145
+ # 词形规范化:全角→半角、拆斜杠、压多余空格(提升精确源命中,治型号/日期分隔符)
146
+ try:
147
+ from query_enhance import normalize_query
148
+ retrieval_query = normalize_query(retrieval_query)
149
+ except ImportError:
150
+ pass
151
+
152
+ if on_progress:
153
+ on_progress(Stage.START, {"query": query})
154
+
155
+ # 网络环境感知:慢网放大超时预算(避免误杀),快网收紧(更快响应)
156
+ _eff_timeout = timeout
157
+ try:
158
+ from network_aware import adjusted_timeout, network_profile
159
+ _eff_timeout = adjusted_timeout(timeout, engines)
160
+ if _eff_timeout != timeout:
161
+ _log(
162
+ f"网络感知超时: {timeout}s → {_eff_timeout}s "
163
+ f"({network_profile(engines).get('network')})",
164
+ )
165
+ except ImportError:
166
+ pass
167
+
168
+ # 时间窗归一化:下推/缓存键用归一化 ISO(相对值转绝对日期),
169
+ # 后过滤用 epoch 秒;非法输入保持原样下推、不参与后过滤。
170
+ since_iso, until_iso, since_ts, until_ts = _normalize_time_window(since, until)
171
+
172
+ # 缓存键的引擎维度取**请求侧身份**(用户点名的引擎,或 auto),不取
173
+ # 路由出来的 engines_combo。
174
+ #
175
+ # 为什么(2026-09-19 实测):combo 是决策结果,会被 adaptive 学习器按上一次
176
+ # 搜索的成败逐次改写。拿结果当键就是「用缓存让缓存失效」——同一查询连跑
177
+ # 两次,进键的引擎串从 `anysearch+octen` 变成 `exa+octen`、从 `byted+...`
178
+ # 变成 `local_bing+...`,20 条样本里 6 条重复查询(30%)因此白跑一遍网络,
179
+ # 而查询、域、档位全都没变。
180
+ #
181
+ # 语义边界:用户点名 `--engine pypi` 时键里就是 pypi(显式约束必须隔离,
182
+ # 这正是 v2.4.2 那条修复要保的东西);`auto` 时键里是 auto——「用自动路由
183
+ # 搜这个查询」本身就是请求,具体挑了哪几个引擎是实现细节,由 TTL 兜住
184
+ # 时效,并原样保留在缓存载荷里供追溯。
185
+ cache_engine_key = decision.get("engine_request") or "auto"
186
+ # 引擎级垂直域并入缓存键:--domain/--sub_domain 改变了发给引擎的请求,
187
+ # 同 query 在不同 domain 下的结果集不同。不隔离就会把「不限域」的结果
188
+ # 当成「限定金融域」的答案发回去——而这两个开关此前连请求都没带上,
189
+ # 静默退化成通用搜索(同一个坑的另一半)。
190
+ if engine_domain:
191
+ cache_engine_key += f"|ed={engine_domain}"
192
+ if engine_sub_domain:
193
+ cache_engine_key += f"|esd={engine_sub_domain}"
194
+ # 查询操作符并入缓存键:clean_query 是键的主体,若不把指令本身入键,
195
+ # 受限查询与无约束查询会互串缓存(site: 最危险——用户写了约束却可能
196
+ # 命中未过滤的旧条目)。after/before 已由下方 since/until 隔离。
197
+ try:
198
+ from query_operators import operator_cache_suffix
199
+ cache_engine_key += operator_cache_suffix(
200
+ (decision or {}).get("operators"))
201
+ except ImportError:
202
+ pass
203
+ # 时间窗并入缓存键:同一 query 不同 since/until 不串缓存;
204
+ # 用归一化 ISO(7d 与等价绝对日期共享缓存;相对窗跨天自然过期不串旧数据)。
205
+ # 仅当组合内含带时间能力引擎时隔离:无时间字段引擎忽略时间窗、结果相同,
206
+ # 隔离只会降低命中率(7d/30d 查 octen/anysearch 命中同一缓存)。
207
+ time_aware = any(_is_time_capable(e) for e in engines)
208
+ if since_iso and time_aware:
209
+ cache_engine_key += f"|since={since_iso}"
210
+ if until_iso and time_aware:
211
+ cache_engine_key += f"|until={until_iso}"
212
+
213
+ if on_progress:
214
+ on_progress(Stage.ROUTING, {"domain": domain, "engine": engine_label, "engines": engines})
215
+
216
+ # combo 缓存命中(含 depth + 柔性命中)
217
+ if not skip_cache:
218
+ t_cache_start = time.time()
219
+ _tk_cache = _tick(timing)
220
+ hit = cache.get(query, cache_engine_key, max_results, domain=domain,
221
+ mode=mode, depth=depth)
222
+ _tock(timing, "cache_lookup", _tk_cache)
223
+ if hit:
224
+ cache_elapsed = int((time.time() - t_cache_start) * 1000)
225
+ if on_progress:
226
+ on_progress(Stage.CACHE_HIT, {"cache_level": hit.get("_cache_level", "L?")})
227
+ tfidf_scores = decision.get("tfidf_scores", [])
228
+ if tfidf_scores and all(s.get("score", 0) == 0 for s in tfidf_scores):
229
+ tfidf_scores = []
230
+ # 排序在缓存读出后、返回前:缓存内容保持 score 序,sort 只改展示顺序
231
+ hit_results = _sort_results(hit.get("results", []), sort)
232
+ # 命中时一律报**产出这批结果的那次运行**的组合与理由,而不是本次
233
+ # 路由的。此前两者恒等(组合就在缓存键里),这条区分不存在;键改成
234
+ # 请求身份后,同一查询两次运行可以路由到不同组合,而本次路由根本
235
+ # 没执行——报它等于报一个没跑过的计划,还会与同样来自缓存的
236
+ # engines_used / engine_outcomes 自相矛盾。
237
+ cached_combo = (hit.get("engines_combo") or hit.get("engines")
238
+ or engines)
239
+ _hit = {
240
+ "query": query, "engine": (cached_combo or engines)[0],
241
+ "engines": cached_combo,
242
+ "engines_combo": cached_combo, "cached": True,
243
+ "cache_level": hit.get("_cache_level", "L?"),
244
+ "domain": domain, "elapsed_ms": cache_elapsed,
245
+ "tfidf_scores": tfidf_scores,
246
+ "route_reason": hit.get("route_reason") or decision.get("reason"),
247
+ "login_hint": decision.get("login_hint"),
248
+ "results": hit_results,
249
+ "count": len(hit_results),
250
+ "engines_used": hit.get("engines_used") or engines,
251
+ "mode": mode, "depth": depth,
252
+ "reranker": "skipped_cache",
253
+ "engine_outcomes": hit.get("engine_outcomes") or [],
254
+ "errors": _collect_errors({}, hit.get("engine_outcomes") or []),
255
+ "time_filtered": 0,
256
+ }
257
+ # 软命中披露:L2 语义命中返回的是**另一条查询**的载荷,不标出来
258
+ # 就与精确命中无法区分——调用方会以为这就是本查询的缓存。同样遵循
259
+ # 「不适用就整个键缺席」(见下方漏斗注释):精确命中下这三个键不
260
+ # 存在,不写成 null。
261
+ if hit.get("_semantic_hit"):
262
+ _hit["semantic_hit"] = True
263
+ _hit["semantic_query"] = hit.get("_semantic_query")
264
+ _hit["semantic_similarity"] = hit.get("_semantic_similarity")
265
+ # 缓存命中时漏斗记账沿用存档值(它描述的是上一次真实抓取)。
266
+ # 存档里没有(该条写入于引入漏斗之前)就**整个键缺席**,不写成
267
+ # null——null 会被读成「漏斗算出来是空」,而缺席只表示「这次没有
268
+ # 这个数据」。两种档位(默认/agent)必须同一形态,否则同一件事
269
+ # 有两种表述。
270
+ if hit.get("funnel") is not None:
271
+ _hit["funnel"] = hit["funnel"]
272
+ if timing is not None:
273
+ _hit["timing"] = timing.summary()
274
+ return _Prepared(cached=_hit)
275
+ try:
276
+ from circuit_breaker import get_breaker
277
+ breaker = get_breaker()
278
+ except ImportError:
279
+ breaker = None
280
+
281
+ return _Prepared(req=_SearchRequest(
282
+ query=query, decision=decision, engines=engines,
283
+ engines_combo=engines_combo, domain=domain, mode=mode, depth=depth,
284
+ timeout=timeout, max_results=max_results, retrieval_query=retrieval_query,
285
+ parallel=parallel, eff_timeout=_eff_timeout,
286
+ exclude_terms=exclude_terms, qu=qu, since_iso=since_iso,
287
+ until_iso=until_iso, since_ts=since_ts, until_ts=until_ts,
288
+ time_aware=time_aware, skip_cache=skip_cache, timing=timing,
289
+ on_progress=on_progress, sort=sort, cache=cache,
290
+ engine_label=engine_label, cache_engine_key=cache_engine_key,
291
+ emit_usage_log=_emit_usage, breaker=breaker,
292
+ engine_domain=engine_domain, engine_sub_domain=engine_sub_domain,
293
+ ), run=_SearchRun(raw_results={}, engine_outcomes=[], merged=[]))
294
+
295
+
296
+ def dispatch(req: _SearchRequest, run: _SearchRun, hooks: _SearchHooks) -> _SearchRun:
297
+ """引擎编排:打网络、记账、返回填好 dispatch 字段的运行状态。
298
+
299
+ run_dispatch 的入口与常量按值传入(见模块 docstring)。
300
+ """
301
+ query = req.query
302
+ decision = req.decision
303
+ engines = req.engines
304
+ domain = req.domain
305
+ mode, depth = req.mode, req.depth
306
+ max_results, timeout = req.max_results, req.timeout
307
+ skip_cache, cache = req.skip_cache, req.cache
308
+ since_iso, until_iso = req.since_iso, req.until_iso
309
+ timing, on_progress = req.timing, req.on_progress
310
+ retrieval_query = req.retrieval_query
311
+ parallel = req.parallel
312
+ _eff_timeout = req.eff_timeout
313
+ if on_progress:
314
+ on_progress(Stage.SEARCHING, {"engines": engines})
315
+
316
+ t0 = time.time()
317
+ # 单调钟基准:预算窗不随 NTP 跳变失真(engine_dispatch 整套换钟,见其垫片注释)
318
+ t0_mono = time.monotonic()
319
+ _tk_dispatch = _tick(timing)
320
+ # 配额批次在这里建、在融合后的 D6 补搜之后才 flush:中间所有 _ingest
321
+ # (含补搜)都要记进同一批,提前 flush 会让补搜引擎的记账落不了盘。
322
+ #
323
+ # import 放在函数内:quota 模块在 import 期就 ensure_state_dir()(mkdir)。
324
+ # 放模块级意味着每次 argo 调用——含纯缓存命中、根本不建批次的路径——
325
+ # 都要碰一次文件系统。批量对象只可能在本函数被创建,没理由让不建批次的
326
+ # 路径付这笔钱。
327
+ from quota import _QuotaBatch
328
+ quota_batch = _QuotaBatch()
329
+ # 引擎编排(并发/串行调度、重试、熔断、结局分类)整段在 engine_dispatch。
330
+ # 入口与常量**按值传入**而非让那边直接 import search:测试靠
331
+ # patch.object(search, "engine_search" / "get_engines" / "_missing_env_for" /
332
+ # "_FAST_TOTAL_BUDGET_S" / "_PRIMARY_GRACE_S") 换掉它们,直连绑定会让补丁
333
+ # 静默失效(假引擎不被调用、真网络被打开)。
334
+ _dispatch = run_dispatch(
335
+ query=query, retrieval_query=retrieval_query, engines=engines,
336
+ decision=decision, parallel=parallel,
337
+ domain=domain, mode=mode, depth=depth,
338
+ max_results=max_results, timeout=timeout, net_timeout=_eff_timeout,
339
+ skip_cache=skip_cache, cache=cache, breaker=req.breaker,
340
+ since_iso=since_iso, until_iso=until_iso, t0=t0, t0_mono=t0_mono,
341
+ engine_search=hooks.engine_search,
342
+ get_engines_fn=hooks.get_engines,
343
+ get_execution_config_fn=hooks.get_execution_config,
344
+ missing_env_for=hooks.missing_env_for,
345
+ classify_outcome=hooks.classify_outcome,
346
+ quota_batch=quota_batch,
347
+ note_quota_exhausted=hooks.note_quota_exhausted,
348
+ per_engine_budget_s=hooks.per_engine_budget_s,
349
+ fast_budget_s=hooks.fast_budget_s,
350
+ auto_budget_s=hooks.auto_budget_s,
351
+ deep_budget_s=hooks.deep_budget_s,
352
+ primary_grace_s=hooks.primary_grace_s,
353
+ straggler_grace_s=hooks.straggler_grace_s,
354
+ straggler_tail_fn=hooks.straggler_tail_fn,
355
+ serial_stagger_s=hooks.serial_stagger_s,
356
+ engine_domain=req.engine_domain,
357
+ engine_sub_domain=req.engine_sub_domain,
358
+ )
359
+ raw_results = _dispatch.raw_results
360
+ engine_outcomes = _dispatch.engine_outcomes
361
+ engine_latency = _dispatch.engine_latency
362
+ wasted_ms = _dispatch.wasted_ms
363
+ useful_ms = _dispatch.useful_ms
364
+ early_stopped = _dispatch.early_stopped
365
+ budget_used_ms = _dispatch.budget_used_ms
366
+ budget_total_ms = _dispatch.budget_total_ms
367
+ # 融合后的 D6 补搜要用同一套「跑单引擎 + 入账」,钩子由此取回
368
+ _run_one = _dispatch.run_one
369
+ _ingest = _dispatch.ingest
370
+
371
+ elapsed = int((time.time() - t0) * 1000)
372
+ _tock(timing, "dispatch", _tk_dispatch)
373
+ _tk_fusion = _tick(timing)
374
+ return replace(
375
+ run,
376
+ raw_results=raw_results,
377
+ engine_outcomes=engine_outcomes,
378
+ engine_latency=engine_latency,
379
+ wasted_ms=wasted_ms,
380
+ useful_ms=useful_ms,
381
+ early_stopped=early_stopped,
382
+ budget_used_ms=budget_used_ms,
383
+ budget_total_ms=budget_total_ms,
384
+ elapsed=elapsed,
385
+ tk_fusion=_tk_fusion,
386
+ quota_batch=quota_batch,
387
+ run_one=_run_one,
388
+ ingest=_ingest,
389
+ )