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,225 @@
1
+ #!/usr/bin/env python3
2
+ """fetch_quality.py — 抓取结果的质量信号计算(fetch_v3 第三级)。
3
+
4
+ 从 fetch_v3 拆出:本模块只做纯信号计算(来源分类 / 页面类型 / 质量分 /
5
+ 内容安全),不参与抓取调度,也不持有外部状态。独立成模块可让 fetch_v3
6
+ 聚焦降级链调度,避免文件持续膨胀。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from typing import Any
13
+ from urllib.parse import urlparse
14
+
15
+ # 判定为 article 的最小正文字数阈值唯一来源在 content_signals.MIN_ARTICLE_CHARS
16
+ # (detect_page_type 的 markdown-only 回退使用),此处不再复制常量防漂移。
17
+
18
+
19
+
20
+ def assess(result: dict) -> dict:
21
+ """公开入口:为抓取结果补充质量信号并返回同一 dict。"""
22
+ return _assess_quality(result)
23
+
24
+
25
+ def _assess_quality(result: dict) -> dict:
26
+ """计算内容质量信号(内联 content_signals 的核心逻辑)。"""
27
+ url = result.get("url", "")
28
+ content = result.get("content", "")
29
+ html = result.get("html", "")
30
+
31
+ # source_type + is_official
32
+ source_type, is_official = _classify_domain(url)
33
+
34
+ # page_type
35
+ page_type = _detect_page_type(html, content, url)
36
+
37
+ # quality_score
38
+ quality_score = _compute_quality(content, html)
39
+
40
+ # content_ok
41
+ content_ok = quality_score > 0.25 and len(content) > 80
42
+
43
+ # is_stale (简化:无日期信息时保守判定)
44
+ is_stale = False
45
+ content_age_days = -1
46
+
47
+ # 内容安全:注入检测 + 清洗(任何抓取内容先过安全引擎)
48
+ security = {}
49
+ if content:
50
+ try:
51
+ from content_security import scrub_to_dict
52
+ security = scrub_to_dict(content)
53
+ except Exception:
54
+ security = {}
55
+
56
+ result.update({
57
+ "quality_breakdown": quality_breakdown(content, html),
58
+ "content_ok": content_ok,
59
+ "page_type": page_type,
60
+ "source_type": source_type,
61
+ "is_official": is_official,
62
+ "is_stale": is_stale,
63
+ "content_age_days": content_age_days,
64
+ "quality_score": quality_score,
65
+ "content_security": security,
66
+ })
67
+ return result
68
+
69
+
70
+ def _classify_domain(url: str) -> tuple[str, bool]:
71
+ """快速域名分类。"""
72
+ try:
73
+ host = urlparse(url).netloc.lower().split(":")[0]
74
+ except Exception:
75
+ return "unknown", False
76
+
77
+ if host.endswith(".gov") or ".gov." in host:
78
+ return "gov", True
79
+ if host.endswith(".edu") or host.endswith(".ac.uk"):
80
+ return "edu", True
81
+ if "github.com" in host or host.endswith(".github.io"):
82
+ return "github", True
83
+ if host.startswith("docs.") or host.startswith("developer."):
84
+ return "docs-site", True
85
+ if "stackoverflow" in host or "stackexchange" in host:
86
+ return "qa", False
87
+ if any(m in host for m in ("forum", "community", "discourse")):
88
+ return "forum", False
89
+ if host in ("reddit.com", "www.reddit.com", "old.reddit.com"):
90
+ return "forum", False
91
+ if any(host == d or host.endswith("." + d) for d in (
92
+ "nytimes.com", "bbc.com", "reuters.com", "theguardian.com",
93
+ "bloomberg.com", "techcrunch.com", "theverge.com",
94
+ )):
95
+ return "news", False
96
+ return "unknown", False
97
+
98
+
99
+ def _detect_page_type(html: str, content: str, url: str = "") -> str:
100
+ """检测页面结构类型。
101
+
102
+ 此前本模块内联了一套独立实现(朴素正则、不认 URL),与
103
+ content_signals.detect_page_type 并存且行为不同——两套判定是真实的
104
+ 维护风险。现统一委托给 content_signals(标记更全、支持 URL 感知的
105
+ list 判定、带 confidence),本模块只做字符串适配。
106
+
107
+ 注意 page_type 目前只是报告字段,不参与 TTL / 质量分计算:
108
+ - TTL 由 evidence_loop.ttl_for_fetch_result 取 source_type,而
109
+ _classify_domain 对任意 URL 都返回非空值(保底 "unknown"),
110
+ `source_type or page_type` 恒短路,page_type 永不参与 TTL 判定;
111
+ - _compute_quality(content, html) 不读 page_type。
112
+ """
113
+ try:
114
+ from content_signals import detect_page_type
115
+ return detect_page_type(html, url, content).get("page_type", "unknown")
116
+ except Exception:
117
+ # content_signals 不可用时不做静默误判:宁可 unknown 也不猜类型
118
+ return "unknown"
119
+
120
+
121
+ # 评分口径版本:公式一改就要动它。
122
+ #
123
+ # 质量分随结果写进缓存(TTL 最长 86400s),公式变更后旧条目里的分数会**静默
124
+ # 沿用**——调用方看到的是已经不再成立的口径,而且无从分辨。这与正文产出口径
125
+ # 变化的情形同构(见 cache.FETCH_PIPELINE_VERSION);区别是评分不需要重新联网,
126
+ # 用条目里已有的 content 就地重算即可,所以版本记在条目里而不是并进缓存键。
127
+ QUALITY_FORMULA_VERSION = 3
128
+
129
+
130
+ # CJK 统一表意文字 + 日文假名 + 谚文:这些文字之间不靠空格分词,
131
+ # `content.split()` 会把整段中文算成个位数个「词」。
132
+ _CJK_RE = re.compile(r"[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff"
133
+ r"\u3040-\u30ff\uac00-\ud7af]")
134
+ # 折算比:中文约 1.6 字/词,取 0.5(即 2 字折 1 词)偏保守——宁可让中文
135
+ # 晚一点饱和,也不系统性高估。
136
+ _CJK_WORDS_PER_CHAR = 0.5
137
+
138
+
139
+ def effective_words(content: str) -> int:
140
+ """有效词数:拉丁文按空白切词,CJK 按字折算。
141
+
142
+ 为什么不能直接用 `content.split()`:它按空白切分,而中文没有词间空格——
143
+ 实测同一体量的页面,英文得 1,220「词」,中文只有 85,导致四项加权里的
144
+ word_count 项(权重 0.4)对中文几乎是零分(0.07 vs 0.40),中文内容总分
145
+ 被系统性压低约 0.3。后果不是显示难看,而是**中文信息源在筛选时被更激进地
146
+ 判为低质**——而这项判断本该与语种无关。
147
+ """
148
+ if not content:
149
+ return 0
150
+ cjk = len(_CJK_RE.findall(content))
151
+ latin = len(_CJK_RE.sub(" ", content).split())
152
+ return latin + int(cjk * _CJK_WORDS_PER_CHAR)
153
+
154
+
155
+ def quality_breakdown(content: str, html: str = "") -> dict:
156
+ """质量分的**分项依据**:每个分量得了多少、为什么。
157
+
158
+ 只给一个总分时,调用方无法分辨「分低是因为太短」还是「因为全是导航链接」,
159
+ 而这两者的处置完全不同。四项加权分与结构修正项各自落出来,并指出拖累
160
+ 总分的那一项,结论才可用。
161
+
162
+ 已知残留偏差:density 项量的是「非空白字符占比」,而中文词间本就不写空格,
163
+ 因此中文在这一项上天然接近满分,英文约 0.85,折算到总分实测 +0.04~0.06 的系统差(等有效词数下)。
164
+ 量级比修复前的词数偏差(−0.29)小约六倍,且方向不再惩罚任一语种,
165
+ 故不动它——真正的密度指标需要语种感知的分母,那是另一件事。
166
+ """
167
+ if not content:
168
+ return {"total": 0.0, "version": QUALITY_FORMULA_VERSION,
169
+ "terms": {}, "bonus": 0.0,
170
+ "inputs": {"chars": 0, "word_count": 0, "cjk_chars": 0, "text_density": 0.0,
171
+ "has_structure": False, "html": bool(html)},
172
+ "drag": "empty"}
173
+ cjk_chars = len(_CJK_RE.findall(content))
174
+ word_count = effective_words(content)
175
+ text_density = len(content.replace(" ", "").replace("\n", "")) / max(len(content), 1)
176
+ has_structure = bool(re.search(r'[.!?。!?].{10,}[.!?。!?]', content))
177
+
178
+ terms = {
179
+ "word_count": 0.4 * min(word_count / 500, 1.0),
180
+ "density": 0.3 * text_density,
181
+ "structure": 0.2 * (1.0 if has_structure else 0.0),
182
+ "length": 0.1 * (1.0 if len(content) > 1000 else 0.0),
183
+ }
184
+ score = min(1.0, sum(terms.values()))
185
+
186
+ bonus = 0.0
187
+ if html:
188
+ low = html.lower()
189
+ if re.search(r'<(article|main)\b', low):
190
+ bonus += 0.08 # 语义主内容标签:最强信号
191
+ p_count = low.count("<p")
192
+ if p_count >= 5:
193
+ bonus += 0.05 # 真实文章由 <p> 组成
194
+ elif p_count >= 2:
195
+ bonus += 0.02
196
+ a_count = low.count("<a ")
197
+ if a_count > word_count / 10:
198
+ bonus -= 0.05 # 链接密度过高 → 更像列表页
199
+ bonus = max(-0.1, min(0.15, bonus))
200
+
201
+ # 拖累项:离自己的满分最远的那个分量(bonus 为负时归因到 structure,因为
202
+ # 结构修正是唯一会因链接密度扣分的地方)
203
+ weights = {"word_count": 0.4, "density": 0.3, "structure": 0.2, "length": 0.1}
204
+ drag = max(weights, key=lambda k: weights[k] - terms[k])
205
+ if bonus < 0:
206
+ drag = "structure"
207
+
208
+ return {
209
+ "total": round(min(1.0, max(0.0, score + bonus)), 2),
210
+ "version": QUALITY_FORMULA_VERSION,
211
+ "terms": {k: round(v, 3) for k, v in terms.items()},
212
+ "bonus": round(bonus, 3),
213
+ "inputs": {"chars": len(content), "word_count": word_count,
214
+ "cjk_chars": cjk_chars,
215
+ "text_density": round(text_density, 3),
216
+ "has_structure": has_structure, "html": bool(html)},
217
+ "drag": drag if score + bonus < 0.85 else None,
218
+ }
219
+
220
+
221
+ def _compute_quality(content: str, html: str) -> float:
222
+ """质量评分(0-1)。分项依据见 quality_breakdown——公式只此一处,避免分叉。"""
223
+ return quality_breakdown(content, html)["total"]
224
+
225
+
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+ """fetch_render_tinyfish.py — TinyFish 直连渲染层(fetch_v3 第二级A)。
3
+
4
+ 从 fetch_v3 拆出:该层自包含(直连 HTTP、不依赖 fetch_v3 内部状态),
5
+ 独立成模块可让 fetch_v3 保持在主链调度职责上,避免文件继续膨胀。
6
+
7
+ 定位:markdown-only 渲染。返回 clean markdown 作为 content,不产 raw html
8
+ ——因此调用方若需要原始 HTML(爬取提取链接)必须显式跳过本层。
9
+ 失败一律返回 success=False,由 fetch_v3 主链继续降级到 Wayback/浏览器。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import os
16
+ import urllib.request
17
+ from net_proxy import open_url # 出口调度唯一入口(issue #13 同类修复)
18
+
19
+ # fetch_method 标识。fetch_v3 主链用它判断是否已命中渲染层
20
+ # (命中则不再冷启动本地 Chrome)。集中为常量避免拼写漂移。
21
+ TINYFISH_METHOD = "tinyfish"
22
+
23
+ _ENDPOINT = "https://api.fetch.tinyfish.ai"
24
+ _MAX_ERR_CHARS = 100
25
+
26
+
27
+ def _result(url: str, *, success: bool, error: str | None = None,
28
+ content: str = "", title: str = "") -> dict:
29
+ """统一的渲染层结果构造(消除重复的 dict 字面量)。"""
30
+ return {
31
+ "url": url,
32
+ "content": content,
33
+ "html": "", # 本层只产 markdown,无 raw html
34
+ "title": title,
35
+ "length": len(content),
36
+ "success": success,
37
+ "error": error,
38
+ "fetch_method": TINYFISH_METHOD,
39
+ }
40
+
41
+
42
+ def enabled() -> bool:
43
+ """渲染层开关:ARGO_FETCH_TINYFISH=0 关闭,默认开启。"""
44
+ try:
45
+ from engine_env import env_flag
46
+ return env_flag("ARGO_FETCH_TINYFISH")
47
+ except ImportError:
48
+ return os.environ.get("ARGO_FETCH_TINYFISH", "1").strip().lower() not in (
49
+ "0", "false", "no", "off")
50
+
51
+
52
+ def _api_key() -> str:
53
+ """引擎密钥统一走 engine_env:os.environ 优先 + ~/.config/argo/env 热读保底,
54
+ 与 search 引擎的 {TINYFISH_API_KEY} 占位符解析同一来源——否则会出现
55
+ 「search 能用、fetch 渲染层静默禁用」的分裂(密钥只写在 env 文件时)。"""
56
+ try:
57
+ from engine_env import get_env
58
+ return get_env("TINYFISH_API_KEY")
59
+ except ImportError:
60
+ # engine_env 不可达时仍要认两套名字(推荐名 ARGO_ 前缀 / 历史裸名),
61
+ # 与 engine_env._name_variants 判断一致——否则这条保底会把「悄悄关掉」
62
+ # 从「env 文件」搬到「os.environ」再演一次。
63
+ return (os.environ.get("ARGO_TINYFISH_API_KEY")
64
+ or os.environ.get("TINYFISH_API_KEY", ""))
65
+
66
+
67
+ def fetch(url: str, max_chars: int = 8000, timeout: float = 8.0) -> dict:
68
+ """TinyFish 免费渲染(直连 HTTP,无外部 CLI 依赖)。
69
+
70
+ POST https://api.fetch.tinyfish.ai,认证用 X-API-Key
71
+ (读 TINYFISH_API_KEY 环境变量)。
72
+ key 缺失或端点失败时返回非 success,由上层继续走 Wayback/浏览器。
73
+ """
74
+ api_key = _api_key().strip()
75
+ if not api_key:
76
+ return _result(url, success=False,
77
+ error="TINYFISH_API_KEY not configured")
78
+
79
+ body = json.dumps({"urls": [url], "format": "markdown"}).encode("utf-8")
80
+ req = urllib.request.Request(
81
+ _ENDPOINT,
82
+ data=body,
83
+ headers={"X-API-Key": api_key, "Content-Type": "application/json"},
84
+ method="POST",
85
+ )
86
+ try:
87
+ with open_url(req, timeout=timeout) as resp:
88
+ raw = resp.read().decode("utf-8")
89
+ data = json.loads(raw)
90
+ except Exception as e:
91
+ return _result(url, success=False, error=str(e)[:_MAX_ERR_CHARS])
92
+
93
+ results = data.get("results") or []
94
+ # 真实 /fetch 返回形如 {"results":[...], "errors":[...]};errors 非空时应
95
+ # 优先透出,避免「取不到却报 empty」这种模糊错误掩盖真实原因。
96
+ errors = data.get("errors") or []
97
+ err_msg = ""
98
+ if errors:
99
+ e0 = errors[0]
100
+ err_msg = (e0.get("message") or e0.get("error")
101
+ if isinstance(e0, dict) else str(e0))
102
+
103
+ # 畸形 payload 守卫:results 为空或首项非 dict 时按失败处理(第三方返回
104
+ # 结构不受我方控制,任何形状都不能让异常逃出 fetch() 的 success=False 契约)。
105
+ item = results[0] if results and isinstance(results[0], dict) else {}
106
+ if not item:
107
+ return _result(url, success=False,
108
+ error=(err_msg or "tinyfish empty result")[:_MAX_ERR_CHARS])
109
+
110
+ text = (item.get("text") or "").strip()
111
+ if not text:
112
+ return _result(url, success=False,
113
+ error=(err_msg or "tinyfish empty content")[:_MAX_ERR_CHARS])
114
+
115
+ content = text[:max_chars]
116
+ return _result(url, success=True, content=content,
117
+ title=item.get("title") or "")