@researai/deepscientist 1.5.16 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (896) hide show
  1. package/AGENTS.md +309 -130
  2. package/AISB/catalog/aisb.b1.agentic_coding.yaml +244 -0
  3. package/AISB/catalog/aisb.b10.climate_earth.yaml +235 -0
  4. package/AISB/catalog/aisb.b11.model_efficiency.yaml +231 -0
  5. package/AISB/catalog/aisb.b12.embodied_ai.yaml +238 -0
  6. package/AISB/catalog/aisb.b2.agent_systems.yaml +229 -0
  7. package/AISB/catalog/aisb.b3.self_evolving_rl.yaml +237 -0
  8. package/AISB/catalog/aisb.b4.lm_reasoning.yaml +240 -0
  9. package/AISB/catalog/aisb.b5.math_proof.yaml +235 -0
  10. package/AISB/catalog/aisb.b6.research_process.yaml +243 -0
  11. package/AISB/catalog/aisb.b7.multimodal_fusion.yaml +232 -0
  12. package/AISB/catalog/aisb.b8.lifesci_drug.yaml +275 -0
  13. package/AISB/catalog/aisb.b9.material_science.yaml +237 -0
  14. package/AISB/catalog/aisb.t3.001_savvy.yaml +159 -0
  15. package/AISB/catalog/aisb.t3.001_savvy.zh.yaml +121 -0
  16. package/AISB/catalog/aisb.t3.002_pinet.yaml +189 -0
  17. package/AISB/catalog/aisb.t3.002_pinet.zh.yaml +130 -0
  18. package/AISB/catalog/aisb.t3.004_decentralattn.yaml +184 -0
  19. package/AISB/catalog/aisb.t3.004_decentralattn.zh.yaml +153 -0
  20. package/AISB/catalog/aisb.t3.005_tsae.yaml +193 -0
  21. package/AISB/catalog/aisb.t3.005_tsae.zh.yaml +139 -0
  22. package/AISB/catalog/aisb.t3.006_physense.yaml +194 -0
  23. package/AISB/catalog/aisb.t3.006_physense.zh.yaml +118 -0
  24. package/AISB/catalog/aisb.t3.007_reasoningiqa.yaml +169 -0
  25. package/AISB/catalog/aisb.t3.007_reasoningiqa.zh.yaml +133 -0
  26. package/AISB/catalog/aisb.t3.008_meanflows.yaml +188 -0
  27. package/AISB/catalog/aisb.t3.008_meanflows.zh.yaml +140 -0
  28. package/AISB/catalog/aisb.t3.009_scoremissing.yaml +179 -0
  29. package/AISB/catalog/aisb.t3.009_scoremissing.zh.yaml +119 -0
  30. package/AISB/catalog/aisb.t3.010_suitabilityfilter.yaml +221 -0
  31. package/AISB/catalog/aisb.t3.010_suitabilityfilter.zh.yaml +141 -0
  32. package/AISB/catalog/aisb.t3.011_osd.yaml +206 -0
  33. package/AISB/catalog/aisb.t3.011_osd.zh.yaml +163 -0
  34. package/AISB/catalog/aisb.t3.012_efficientqat.yaml +206 -0
  35. package/AISB/catalog/aisb.t3.012_efficientqat.zh.yaml +159 -0
  36. package/AISB/catalog/aisb.t3.013_appl.yaml +152 -0
  37. package/AISB/catalog/aisb.t3.013_appl.zh.yaml +126 -0
  38. package/AISB/catalog/aisb.t3.014_piguard.yaml +207 -0
  39. package/AISB/catalog/aisb.t3.014_piguard.zh.yaml +164 -0
  40. package/AISB/catalog/aisb.t3.015_frspec.yaml +209 -0
  41. package/AISB/catalog/aisb.t3.015_frspec.zh.yaml +163 -0
  42. package/AISB/catalog/aisb.t3.016_mathfusion.yaml +166 -0
  43. package/AISB/catalog/aisb.t3.016_mathfusion.zh.yaml +145 -0
  44. package/AISB/catalog/aisb.t3.017_multimodalglp.yaml +171 -0
  45. package/AISB/catalog/aisb.t3.017_multimodalglp.zh.yaml +122 -0
  46. package/AISB/catalog/aisb.t3.018_cotsynth.yaml +206 -0
  47. package/AISB/catalog/aisb.t3.018_cotsynth.zh.yaml +162 -0
  48. package/AISB/catalog/aisb.t3.019_dyscaleut.yaml +211 -0
  49. package/AISB/catalog/aisb.t3.019_dyscaleut.zh.yaml +148 -0
  50. package/AISB/catalog/aisb.t3.020_aristotle.yaml +173 -0
  51. package/AISB/catalog/aisb.t3.020_aristotle.zh.yaml +119 -0
  52. package/AISB/catalog/aisb.t3.021_tokenrecycling.yaml +160 -0
  53. package/AISB/catalog/aisb.t3.021_tokenrecycling.zh.yaml +129 -0
  54. package/AISB/catalog/aisb.t3.022_chainofreasoning.yaml +204 -0
  55. package/AISB/catalog/aisb.t3.022_chainofreasoning.zh.yaml +161 -0
  56. package/AISB/catalog/aisb.t3.023_guidedembed.yaml +211 -0
  57. package/AISB/catalog/aisb.t3.023_guidedembed.zh.yaml +189 -0
  58. package/AISB/catalog/aisb.t3.024_outputcentric.yaml +148 -0
  59. package/AISB/catalog/aisb.t3.024_outputcentric.zh.yaml +131 -0
  60. package/AISB/catalog/aisb.t3.025_deeper.yaml +143 -0
  61. package/AISB/catalog/aisb.t3.025_deeper.zh.yaml +116 -0
  62. package/AISB/catalog/aisb.t3.026_gartkg.yaml +195 -0
  63. package/AISB/catalog/aisb.t3.026_gartkg.zh.yaml +127 -0
  64. package/AISB/catalog/aisb.t3.027_citeeval.yaml +182 -0
  65. package/AISB/catalog/aisb.t3.027_citeeval.zh.yaml +135 -0
  66. package/AISB/catalog/aisb.t3.028_sbam.yaml +206 -0
  67. package/AISB/catalog/aisb.t3.028_sbam.zh.yaml +166 -0
  68. package/AISB/catalog/aisb.t3.029_cdqgeoembed.yaml +224 -0
  69. package/AISB/catalog/aisb.t3.029_cdqgeoembed.zh.yaml +142 -0
  70. package/AISB/catalog/aisb.t3.030_processrm.yaml +211 -0
  71. package/AISB/catalog/aisb.t3.030_processrm.zh.yaml +166 -0
  72. package/AISB/catalog/aisb.t3.031_circuitstability.yaml +172 -0
  73. package/AISB/catalog/aisb.t3.031_circuitstability.zh.yaml +134 -0
  74. package/AISB/catalog/aisb.t3.032_ptsolver.yaml +169 -0
  75. package/AISB/catalog/aisb.t3.032_ptsolver.zh.yaml +135 -0
  76. package/AISB/catalog/aisb.t3.033_gcse.yaml +144 -0
  77. package/AISB/catalog/aisb.t3.033_gcse.zh.yaml +126 -0
  78. package/AISB/catalog/aisb.t3.034_ensemblewm.yaml +183 -0
  79. package/AISB/catalog/aisb.t3.034_ensemblewm.zh.yaml +146 -0
  80. package/AISB/catalog/aisb.t3.035_moralvalueswa.yaml +207 -0
  81. package/AISB/catalog/aisb.t3.035_moralvalueswa.zh.yaml +165 -0
  82. package/AISB/catalog/aisb.t3.036_weakstrongpref.yaml +210 -0
  83. package/AISB/catalog/aisb.t3.036_weakstrongpref.zh.yaml +194 -0
  84. package/AISB/catalog/aisb.t3.037_dementiamask.yaml +172 -0
  85. package/AISB/catalog/aisb.t3.037_dementiamask.zh.yaml +132 -0
  86. package/AISB/catalog/aisb.t3.038_tinysam.yaml +284 -0
  87. package/AISB/catalog/aisb.t3.038_tinysam.zh.yaml +240 -0
  88. package/AISB/catalog/aisb.t3.039_calf.yaml +224 -0
  89. package/AISB/catalog/aisb.t3.039_calf.zh.yaml +194 -0
  90. package/AISB/catalog/aisb.t3.040_graniteguardian.yaml +199 -0
  91. package/AISB/catalog/aisb.t3.040_graniteguardian.zh.yaml +174 -0
  92. package/AISB/catalog/aisb.t3.041_amdm.yaml +149 -0
  93. package/AISB/catalog/aisb.t3.041_amdm.zh.yaml +137 -0
  94. package/AISB/catalog/aisb.t3.042_xpatch.yaml +216 -0
  95. package/AISB/catalog/aisb.t3.042_xpatch.zh.yaml +182 -0
  96. package/AISB/catalog/aisb.t3.043_vhm.yaml +268 -0
  97. package/AISB/catalog/aisb.t3.043_vhm.zh.yaml +193 -0
  98. package/AISB/catalog/aisb.t3.044_rgvi.yaml +224 -0
  99. package/AISB/catalog/aisb.t3.044_rgvi.zh.yaml +176 -0
  100. package/AISB/catalog/aisb.t3.045_pslstm.yaml +203 -0
  101. package/AISB/catalog/aisb.t3.045_pslstm.zh.yaml +179 -0
  102. package/AISB/catalog/aisb.t3.046_nonstatts.yaml +208 -0
  103. package/AISB/catalog/aisb.t3.046_nonstatts.zh.yaml +194 -0
  104. package/AISB/catalog/aisb.t3.047_timepfn.yaml +156 -0
  105. package/AISB/catalog/aisb.t3.047_timepfn.zh.yaml +124 -0
  106. package/AISB/catalog/aisb.t3.048_proxyspex.yaml +148 -0
  107. package/AISB/catalog/aisb.t3.048_proxyspex.zh.yaml +125 -0
  108. package/AISB/catalog/aisb.t3.049_hogwildinference.yaml +183 -0
  109. package/AISB/catalog/aisb.t3.049_hogwildinference.zh.yaml +138 -0
  110. package/AISB/catalog/aisb.t3.050_causalpfn.yaml +214 -0
  111. package/AISB/catalog/aisb.t3.050_causalpfn.zh.yaml +190 -0
  112. package/AISB/catalog/aisb.t3.051_flashtp.yaml +169 -0
  113. package/AISB/catalog/aisb.t3.051_flashtp.zh.yaml +124 -0
  114. package/AISB/catalog/aisb.t3.052_nsdiff.yaml +155 -0
  115. package/AISB/catalog/aisb.t3.052_nsdiff.zh.yaml +138 -0
  116. package/AISB/catalog/aisb.t3.053_k2vae.yaml +158 -0
  117. package/AISB/catalog/aisb.t3.053_k2vae.zh.yaml +132 -0
  118. package/AISB/catalog/aisb.t3.054_timebase.yaml +178 -0
  119. package/AISB/catalog/aisb.t3.054_timebase.zh.yaml +158 -0
  120. package/AISB/catalog/aisb.t3.055_csbrain.yaml +238 -0
  121. package/AISB/catalog/aisb.t3.055_csbrain.zh.yaml +184 -0
  122. package/AISB/catalog/aisb.t3.056_infosam.yaml +224 -0
  123. package/AISB/catalog/aisb.t3.056_infosam.zh.yaml +189 -0
  124. package/AISB/catalog/aisb.t3.057_mdreid.yaml +129 -0
  125. package/AISB/catalog/aisb.t3.057_mdreid.zh.yaml +117 -0
  126. package/AISB/catalog/aisb.t3.058_mindglitch.yaml +171 -0
  127. package/AISB/catalog/aisb.t3.058_mindglitch.zh.yaml +145 -0
  128. package/AISB/catalog/aisb.t3.059_selfsupervised.yaml +154 -0
  129. package/AISB/catalog/aisb.t3.059_selfsupervised.zh.yaml +125 -0
  130. package/AISB/catalog/aisb.t3.060_iaggad.yaml +121 -0
  131. package/AISB/catalog/aisb.t3.060_iaggad.zh.yaml +100 -0
  132. package/AISB/catalog/aisb.t3.061_hsgkn.yaml +136 -0
  133. package/AISB/catalog/aisb.t3.061_hsgkn.zh.yaml +113 -0
  134. package/AISB/catalog/aisb.t3.062_visionts.yaml +237 -0
  135. package/AISB/catalog/aisb.t3.062_visionts.zh.yaml +216 -0
  136. package/AISB/catalog/aisb.t3.063_tsrag.yaml +162 -0
  137. package/AISB/catalog/aisb.t3.063_tsrag.zh.yaml +138 -0
  138. package/AISB/catalog/aisb.t3.064_pir.yaml +221 -0
  139. package/AISB/catalog/aisb.t3.064_pir.zh.yaml +197 -0
  140. package/AISB/catalog/aisb.t3.065_proteinbinding.yaml +234 -0
  141. package/AISB/catalog/aisb.t3.065_proteinbinding.zh.yaml +167 -0
  142. package/AISB/catalog/aisb.t3.066_tropicalattention.yaml +267 -0
  143. package/AISB/catalog/aisb.t3.066_tropicalattention.zh.yaml +229 -0
  144. package/AISB/catalog/aisb.t3.067_kanad.yaml +193 -0
  145. package/AISB/catalog/aisb.t3.067_kanad.zh.yaml +167 -0
  146. package/AISB/catalog/aisb.t3.068_sempo.yaml +187 -0
  147. package/AISB/catalog/aisb.t3.068_sempo.zh.yaml +148 -0
  148. package/AISB/catalog/aisb.t3.069_treehfd.yaml +129 -0
  149. package/AISB/catalog/aisb.t3.069_treehfd.zh.yaml +111 -0
  150. package/AISB/catalog/aisb.t3.070_certifiedunlearning.yaml +224 -0
  151. package/AISB/catalog/aisb.t3.070_certifiedunlearning.zh.yaml +171 -0
  152. package/AISB/catalog/aisb.t3.071_neuralmjd.yaml +142 -0
  153. package/AISB/catalog/aisb.t3.071_neuralmjd.zh.yaml +120 -0
  154. package/AISB/catalog/aisb.t3.072_fedgmt.yaml +181 -0
  155. package/AISB/catalog/aisb.t3.072_fedgmt.zh.yaml +158 -0
  156. package/AISB/catalog/aisb.t3.073_rld.yaml +161 -0
  157. package/AISB/catalog/aisb.t3.073_rld.zh.yaml +129 -0
  158. package/AISB/catalog/aisb.t3.074_lsvi.yaml +163 -0
  159. package/AISB/catalog/aisb.t3.074_lsvi.zh.yaml +129 -0
  160. package/AISB/catalog/aisb.t3.075_treeslicedentropy.yaml +201 -0
  161. package/AISB/catalog/aisb.t3.075_treeslicedentropy.zh.yaml +148 -0
  162. package/AISB/catalog/aisb.t3.076_aanet.yaml +169 -0
  163. package/AISB/catalog/aisb.t3.076_aanet.zh.yaml +129 -0
  164. package/AISB/catalog/aisb.t3.077_cmnn.yaml +199 -0
  165. package/AISB/catalog/aisb.t3.077_cmnn.zh.yaml +165 -0
  166. package/AISB/catalog/aisb.t3.078_conformalanomaly.yaml +146 -0
  167. package/AISB/catalog/aisb.t3.078_conformalanomaly.zh.yaml +117 -0
  168. package/AISB/catalog/aisb.t3.079_dpfkmeans.yaml +131 -0
  169. package/AISB/catalog/aisb.t3.079_dpfkmeans.zh.yaml +104 -0
  170. package/AISB/catalog/aisb.t3.080_latentscorereweight.yaml +169 -0
  171. package/AISB/catalog/aisb.t3.080_latentscorereweight.zh.yaml +123 -0
  172. package/AISB/catalog/aisb.t3.081_qmamba.yaml +150 -0
  173. package/AISB/catalog/aisb.t3.081_qmamba.zh.yaml +117 -0
  174. package/AISB/catalog/aisb.t3.082_onlinellmrouting.yaml +160 -0
  175. package/AISB/catalog/aisb.t3.082_onlinellmrouting.zh.yaml +133 -0
  176. package/AISB/catalog/aisb.t3.083_starformer.yaml +178 -0
  177. package/AISB/catalog/aisb.t3.083_starformer.zh.yaml +140 -0
  178. package/AISB/catalog/aisb.t3.084_ift.yaml +139 -0
  179. package/AISB/catalog/aisb.t3.084_ift.zh.yaml +111 -0
  180. package/AISB/catalog/aisb.t3.085_neuralsurv.yaml +183 -0
  181. package/AISB/catalog/aisb.t3.085_neuralsurv.zh.yaml +143 -0
  182. package/AISB/catalog/aisb.t3.086_stella.yaml +197 -0
  183. package/AISB/catalog/aisb.t3.086_stella.zh.yaml +142 -0
  184. package/AISB/catalog/aisb.t3.087_moses.yaml +167 -0
  185. package/AISB/catalog/aisb.t3.087_moses.zh.yaml +132 -0
  186. package/AISB/catalog/aisb.t3.088_channelnorm.yaml +140 -0
  187. package/AISB/catalog/aisb.t3.088_channelnorm.zh.yaml +109 -0
  188. package/AISB/catalog/aisb.t3.089_causalvelocity.yaml +730 -0
  189. package/AISB/catalog/aisb.t3.089_causalvelocity.zh.yaml +668 -0
  190. package/AISB/catalog/aisb.t3.090_rstib.yaml +144 -0
  191. package/AISB/catalog/aisb.t3.090_rstib.zh.yaml +109 -0
  192. package/AISB/catalog/aisb.t3.091_timeawarecausal.yaml +132 -0
  193. package/AISB/catalog/aisb.t3.091_timeawarecausal.zh.yaml +107 -0
  194. package/AISB/catalog/aisb.t3.092_kmeanslocalopt.yaml +138 -0
  195. package/AISB/catalog/aisb.t3.092_kmeanslocalopt.zh.yaml +110 -0
  196. package/AISB/catalog/aisb.t3.093_fedwmsam.yaml +134 -0
  197. package/AISB/catalog/aisb.t3.093_fedwmsam.zh.yaml +106 -0
  198. package/AISB/catalog/aisb.t3.094_boundre.yaml +147 -0
  199. package/AISB/catalog/aisb.t3.094_boundre.zh.yaml +114 -0
  200. package/AISB/catalog/aisb.t3.095_fastfeaturecp.yaml +153 -0
  201. package/AISB/catalog/aisb.t3.095_fastfeaturecp.zh.yaml +118 -0
  202. package/AISB/catalog/aisb.t3.096_m3svm.yaml +189 -0
  203. package/AISB/catalog/aisb.t3.096_m3svm.zh.yaml +149 -0
  204. package/AISB/catalog/aisb.t3.097_wassersteintl.yaml +212 -0
  205. package/AISB/catalog/aisb.t3.097_wassersteintl.zh.yaml +169 -0
  206. package/AISB/catalog/aisb.t3.098_xmahalanobis.yaml +171 -0
  207. package/AISB/catalog/aisb.t3.098_xmahalanobis.zh.yaml +127 -0
  208. package/AISB/catalog/aisb.t3.099_ollalanding.yaml +248 -0
  209. package/AISB/catalog/aisb.t3.099_ollalanding.zh.yaml +182 -0
  210. package/AISB/catalog/aisb.t3.100_invmissingdata.yaml +179 -0
  211. package/AISB/catalog/aisb.t3.100_invmissingdata.zh.yaml +150 -0
  212. package/AISB/catalog/aisb.t3.101_acia.yaml +164 -0
  213. package/AISB/catalog/aisb.t3.101_acia.zh.yaml +109 -0
  214. package/AISB/catalog/aisb.t3.102_stochasticff.yaml +178 -0
  215. package/AISB/catalog/aisb.t3.102_stochasticff.zh.yaml +130 -0
  216. package/AISB/catalog/aisb.t3.103_qdcp.yaml +150 -0
  217. package/AISB/catalog/aisb.t3.103_qdcp.zh.yaml +116 -0
  218. package/AISB/catalog/aisb.t3.104_balancedactiveinf.yaml +137 -0
  219. package/AISB/catalog/aisb.t3.104_balancedactiveinf.zh.yaml +104 -0
  220. package/AISB/catalog/aisb.t3.105_binaryclasseval.yaml +161 -0
  221. package/AISB/catalog/aisb.t3.105_binaryclasseval.zh.yaml +130 -0
  222. package/AISB/image/001_aisb.t3.001_savvy.jpg +0 -0
  223. package/AISB/image/002_aisb.t3.002_pinet.jpg +0 -0
  224. package/AISB/image/003_aisb.t3.003_dmsqd.jpg +0 -0
  225. package/AISB/image/004_aisb.t3.004_decentralattn.jpg +0 -0
  226. package/AISB/image/005_aisb.t3.005_tsae.jpg +0 -0
  227. package/AISB/image/006_aisb.t3.006_physense.jpg +0 -0
  228. package/AISB/image/007_aisb.t3.007_reasoningiqa.jpg +0 -0
  229. package/AISB/image/008_aisb.t3.008_meanflows.jpg +0 -0
  230. package/AISB/image/009_aisb.t3.009_scoremissing.jpg +0 -0
  231. package/AISB/image/010_aisb.t3.010_suitabilityfilter.jpg +0 -0
  232. package/AISB/image/011_aisb.t3.011_osd.jpg +0 -0
  233. package/AISB/image/012_aisb.t3.012_efficientqat.jpg +0 -0
  234. package/AISB/image/013_aisb.t3.013_appl.jpg +0 -0
  235. package/AISB/image/014_aisb.t3.014_piguard.jpg +0 -0
  236. package/AISB/image/015_aisb.t3.015_frspec.jpg +0 -0
  237. package/AISB/image/016_aisb.t3.016_mathfusion.jpg +0 -0
  238. package/AISB/image/017_aisb.t3.017_multimodalglp.jpg +0 -0
  239. package/AISB/image/018_aisb.t3.018_cotsynth.jpg +0 -0
  240. package/AISB/image/019_aisb.t3.019_dyscaleut.jpg +0 -0
  241. package/AISB/image/020_aisb.t3.020_aristotle.jpg +0 -0
  242. package/AISB/image/021_aisb.t3.021_tokenrecycling.jpg +0 -0
  243. package/AISB/image/022_aisb.t3.022_chainofreasoning.jpg +0 -0
  244. package/AISB/image/023_aisb.t3.023_guidedembed.jpg +0 -0
  245. package/AISB/image/024_aisb.t3.024_outputcentric.jpg +0 -0
  246. package/AISB/image/025_aisb.t3.025_deeper.jpg +0 -0
  247. package/AISB/image/026_aisb.t3.026_gartkg.jpg +0 -0
  248. package/AISB/image/027_aisb.t3.027_citeeval.jpg +0 -0
  249. package/AISB/image/028_aisb.t3.028_sbam.jpg +0 -0
  250. package/AISB/image/029_aisb.t3.029_cdqgeoembed.jpg +0 -0
  251. package/AISB/image/030_aisb.t3.030_processrm.jpg +0 -0
  252. package/AISB/image/031_aisb.t3.031_circuitstability.jpg +0 -0
  253. package/AISB/image/032_aisb.t3.032_ptsolver.jpg +0 -0
  254. package/AISB/image/033_aisb.t3.033_gcse.jpg +0 -0
  255. package/AISB/image/034_aisb.t3.034_ensemblewm.jpg +0 -0
  256. package/AISB/image/035_aisb.t3.035_moralvalueswa.jpg +0 -0
  257. package/AISB/image/036_aisb.t3.036_weakstrongpref.jpg +0 -0
  258. package/AISB/image/037_aisb.t3.037_dementiamask.jpg +0 -0
  259. package/AISB/image/038_aisb.t3.038_tinysam.jpg +0 -0
  260. package/AISB/image/039_aisb.t3.039_calf.jpg +0 -0
  261. package/AISB/image/040_aisb.t3.040_graniteguardian.jpg +0 -0
  262. package/AISB/image/041_aisb.t3.041_amdm.jpg +0 -0
  263. package/AISB/image/042_aisb.t3.042_xpatch.jpg +0 -0
  264. package/AISB/image/043_aisb.t3.043_vhm.jpg +0 -0
  265. package/AISB/image/044_aisb.t3.044_rgvi.jpg +0 -0
  266. package/AISB/image/045_aisb.t3.045_pslstm.jpg +0 -0
  267. package/AISB/image/046_aisb.t3.046_nonstatts.jpg +0 -0
  268. package/AISB/image/047_aisb.t3.047_timepfn.jpg +0 -0
  269. package/AISB/image/048_aisb.t3.048_proxyspex.jpg +0 -0
  270. package/AISB/image/049_aisb.t3.049_hogwildinference.jpg +0 -0
  271. package/AISB/image/050_aisb.t3.050_causalpfn.jpg +0 -0
  272. package/AISB/image/051_aisb.t3.051_flashtp.jpg +0 -0
  273. package/AISB/image/052_aisb.t3.052_nsdiff.jpg +0 -0
  274. package/AISB/image/053_aisb.t3.053_k2vae.jpg +0 -0
  275. package/AISB/image/054_aisb.t3.054_timebase.jpg +0 -0
  276. package/AISB/image/055_aisb.t3.055_csbrain.jpg +0 -0
  277. package/AISB/image/056_aisb.t3.056_infosam.jpg +0 -0
  278. package/AISB/image/057_aisb.t3.057_mdreid.jpg +0 -0
  279. package/AISB/image/058_aisb.t3.058_mindglitch.jpg +0 -0
  280. package/AISB/image/059_aisb.t3.059_selfsupervised.jpg +0 -0
  281. package/AISB/image/060_aisb.t3.060_iaggad.jpg +0 -0
  282. package/AISB/image/061_aisb.t3.061_hsgkn.jpg +0 -0
  283. package/AISB/image/062_aisb.t3.062_visionts.jpg +0 -0
  284. package/AISB/image/063_aisb.t3.063_tsrag.jpg +0 -0
  285. package/AISB/image/064_aisb.t3.064_pir.jpg +0 -0
  286. package/AISB/image/065_aisb.t3.065_proteinbinding.jpg +0 -0
  287. package/AISB/image/066_aisb.t3.066_tropicalattention.jpg +0 -0
  288. package/AISB/image/067_aisb.t3.067_kanad.jpg +0 -0
  289. package/AISB/image/068_aisb.t3.068_sempo.jpg +0 -0
  290. package/AISB/image/069_aisb.t3.069_treehfd.jpg +0 -0
  291. package/AISB/image/070_aisb.t3.070_certifiedunlearning.jpg +0 -0
  292. package/AISB/image/071_aisb.t3.071_neuralmjd.jpg +0 -0
  293. package/AISB/image/072_aisb.t3.072_fedgmt.jpg +0 -0
  294. package/AISB/image/073_aisb.t3.073_rld.jpg +0 -0
  295. package/AISB/image/074_aisb.t3.074_lsvi.jpg +0 -0
  296. package/AISB/image/075_aisb.t3.075_treeslicedentropy.jpg +0 -0
  297. package/AISB/image/076_aisb.t3.076_aanet.jpg +0 -0
  298. package/AISB/image/077_aisb.t3.077_cmnn.jpg +0 -0
  299. package/AISB/image/078_aisb.t3.078_conformalanomaly.jpg +0 -0
  300. package/AISB/image/079_aisb.t3.079_dpfkmeans.jpg +0 -0
  301. package/AISB/image/080_aisb.t3.080_latentscorereweight.jpg +0 -0
  302. package/AISB/image/081_aisb.t3.081_qmamba.jpg +0 -0
  303. package/AISB/image/082_aisb.t3.082_onlinellmrouting.jpg +0 -0
  304. package/AISB/image/083_aisb.t3.083_starformer.jpg +0 -0
  305. package/AISB/image/084_aisb.t3.084_ift.jpg +0 -0
  306. package/AISB/image/085_aisb.t3.085_neuralsurv.jpg +0 -0
  307. package/AISB/image/086_aisb.t3.086_stella.jpg +0 -0
  308. package/AISB/image/087_aisb.t3.087_moses.jpg +0 -0
  309. package/AISB/image/088_aisb.t3.088_channelnorm.jpg +0 -0
  310. package/AISB/image/089_aisb.t3.089_causalvelocity.jpg +0 -0
  311. package/AISB/image/090_aisb.t3.090_rstib.jpg +0 -0
  312. package/AISB/image/091_aisb.t3.091_timeawarecausal.jpg +0 -0
  313. package/AISB/image/092_aisb.t3.092_kmeanslocalopt.jpg +0 -0
  314. package/AISB/image/093_aisb.t3.093_fedwmsam.jpg +0 -0
  315. package/AISB/image/094_aisb.t3.094_boundre.jpg +0 -0
  316. package/AISB/image/095_aisb.t3.095_fastfeaturecp.jpg +0 -0
  317. package/AISB/image/096_aisb.t3.096_m3svm.jpg +0 -0
  318. package/AISB/image/097_aisb.t3.097_wassersteintl.jpg +0 -0
  319. package/AISB/image/098_aisb.t3.098_xmahalanobis.jpg +0 -0
  320. package/AISB/image/099_aisb.t3.099_ollalanding.jpg +0 -0
  321. package/AISB/image/100_aisb.t3.100_invmissingdata.jpg +0 -0
  322. package/AISB/image/101_aisb.t3.101_acia.jpg +0 -0
  323. package/AISB/image/102_aisb.t3.102_stochasticff.jpg +0 -0
  324. package/AISB/image/103_aisb.t3.103_qdcp.jpg +0 -0
  325. package/AISB/image/104_aisb.t3.104_balancedactiveinf.jpg +0 -0
  326. package/AISB/image/105_aisb.t3.105_binaryclasseval.jpg +0 -0
  327. package/AISB/image/106_aisb.t1.reasoning_lite.jpg +0 -0
  328. package/AISB/image/107_aisb.t2.paper_audit.jpg +0 -0
  329. package/AISB/image/108_aisb.t3.multi_gpu_search.jpg +0 -0
  330. package/AISB/image/109_aisb.t3.tdc_admet.jpg +0 -0
  331. package/AISB/image/aisb.b1.agentic_coding.svg +16 -0
  332. package/AISB/image/aisb.b10.climate_earth.svg +16 -0
  333. package/AISB/image/aisb.b11.model_efficiency.svg +16 -0
  334. package/AISB/image/aisb.b12.embodied_ai.svg +16 -0
  335. package/AISB/image/aisb.b2.agent_systems.svg +16 -0
  336. package/AISB/image/aisb.b3.self_evolving_rl.svg +16 -0
  337. package/AISB/image/aisb.b4.lm_reasoning.svg +16 -0
  338. package/AISB/image/aisb.b5.math_proof.svg +16 -0
  339. package/AISB/image/aisb.b6.research_process.svg +16 -0
  340. package/AISB/image/aisb.b7.multimodal_fusion.svg +16 -0
  341. package/AISB/image/aisb.b8.lifesci_drug.svg +16 -0
  342. package/AISB/image/aisb.b9.material_science.svg +16 -0
  343. package/README.md +196 -32
  344. package/bin/ds.js +924 -66
  345. package/docs/en/00_QUICK_START.md +195 -18
  346. package/docs/en/01_SETTINGS_REFERENCE.md +468 -96
  347. package/docs/en/02_START_RESEARCH_GUIDE.md +26 -5
  348. package/docs/en/03_QQ_CONNECTOR_GUIDE.md +14 -3
  349. package/docs/en/04_LINGZHU_CONNECTOR_GUIDE.md +2 -0
  350. package/docs/en/05_TUI_GUIDE.md +171 -2
  351. package/docs/en/07_MEMORY_AND_MCP.md +38 -2
  352. package/docs/en/09_DOCTOR.md +78 -7
  353. package/docs/en/10_WEIXIN_CONNECTOR_GUIDE.md +38 -1
  354. package/docs/en/11_LICENSE_AND_RISK.md +4 -0
  355. package/docs/en/12_GUIDED_WORKFLOW_TOUR.md +15 -0
  356. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +9 -0
  357. package/docs/en/15_CODEX_PROVIDER_SETUP.md +624 -180
  358. package/docs/en/16_TELEGRAM_CONNECTOR_GUIDE.md +14 -0
  359. package/docs/en/17_WHATSAPP_CONNECTOR_GUIDE.md +14 -0
  360. package/docs/en/18_FEISHU_CONNECTOR_GUIDE.md +14 -0
  361. package/docs/en/21_LOCAL_MODEL_BACKENDS_GUIDE.md +386 -0
  362. package/docs/en/22_BENCHSTORE_YAML_REFERENCE.md +469 -0
  363. package/docs/en/23_BENCHSTORE_GITHUB_RELEASES_SPEC.md +316 -0
  364. package/docs/en/24_CLAUDE_CODE_PROVIDER_SETUP.md +469 -0
  365. package/docs/en/25_OPENCODE_PROVIDER_SETUP.md +653 -0
  366. package/docs/en/26_CITATION_AND_ATTRIBUTION.md +119 -0
  367. package/docs/en/27_KIMI_CODE_PROVIDER_SETUP.md +180 -0
  368. package/docs/en/28_DISCORD_CONNECTOR_GUIDE.md +61 -0
  369. package/docs/en/29_SLACK_CONNECTOR_GUIDE.md +60 -0
  370. package/docs/en/30_SETTINGS_CONTROL_CENTER_GUIDE.md +371 -0
  371. package/docs/en/{19_LOCAL_BROWSER_AUTH.md → 31_LOCAL_BROWSER_AUTH.md} +1 -1
  372. package/docs/en/32_WINDOWS_WSL2_DEPLOYMENT_GUIDE.md +273 -0
  373. package/docs/en/33_WORKSPACE_EXPLORER_QA.md +121 -0
  374. package/docs/en/91_DEVELOPMENT.md +266 -0
  375. package/docs/en/99_ACKNOWLEDGEMENTS.md +24 -19
  376. package/docs/en/README.md +48 -7
  377. package/docs/images/admin/admin-connectors-health-en.png +0 -0
  378. package/docs/images/admin/admin-controllers-en.png +0 -0
  379. package/docs/images/admin/admin-diagnostics-en.png +0 -0
  380. package/docs/images/admin/admin-errors-en.png +0 -0
  381. package/docs/images/admin/admin-issues-en.png +0 -0
  382. package/docs/images/admin/admin-logs-en.png +0 -0
  383. package/docs/images/admin/admin-quest-detail-en.png +0 -0
  384. package/docs/images/admin/admin-quests-en.png +0 -0
  385. package/docs/images/admin/admin-repairs-en.png +0 -0
  386. package/docs/images/admin/admin-runtime-en.png +0 -0
  387. package/docs/images/admin/admin-search-en.png +0 -0
  388. package/docs/images/admin/admin-stats-en.png +0 -0
  389. package/docs/images/admin/admin-summary-en.png +0 -0
  390. package/docs/images/connectors/connector-discord-en.png +0 -0
  391. package/docs/images/connectors/connector-feishu-en.png +0 -0
  392. package/docs/images/connectors/connector-lingzhu-en.png +0 -0
  393. package/docs/images/connectors/connector-qq-en.png +0 -0
  394. package/docs/images/connectors/connector-slack-en.png +0 -0
  395. package/docs/images/connectors/connector-telegram-en.png +0 -0
  396. package/docs/images/connectors/connector-weixin-en.png +0 -0
  397. package/docs/images/connectors/connector-whatsapp-en.png +0 -0
  398. package/docs/images/settings/settings-baselines-en.png +0 -0
  399. package/docs/images/settings/settings-config-en.png +0 -0
  400. package/docs/images/settings/settings-connectors-overview-en.png +0 -0
  401. package/docs/images/settings/settings-deepxiv-en.png +0 -0
  402. package/docs/images/settings/settings-mcp-servers-en.png +0 -0
  403. package/docs/images/settings/settings-plugins-en.png +0 -0
  404. package/docs/images/settings/settings-runners-en.png +0 -0
  405. package/docs/zh/00_QUICK_START.md +142 -18
  406. package/docs/zh/01_SETTINGS_REFERENCE.md +219 -98
  407. package/docs/zh/02_START_RESEARCH_GUIDE.md +26 -5
  408. package/docs/zh/05_TUI_GUIDE.md +171 -2
  409. package/docs/zh/07_MEMORY_AND_MCP.md +29 -2
  410. package/docs/zh/09_DOCTOR.md +54 -8
  411. package/docs/zh/10_WEIXIN_CONNECTOR_GUIDE.md +24 -1
  412. package/docs/zh/11_LICENSE_AND_RISK.md +4 -0
  413. package/docs/zh/12_GUIDED_WORKFLOW_TOUR.md +15 -0
  414. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +9 -0
  415. package/docs/zh/15_CODEX_PROVIDER_SETUP.md +552 -181
  416. package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +384 -0
  417. package/docs/zh/22_BENCHSTORE_YAML_REFERENCE.md +459 -0
  418. package/docs/zh/23_BENCHSTORE_GITHUB_RELEASES_SPEC.md +287 -0
  419. package/docs/zh/23_CLAUDE_RUNNER_GUIDE.md +103 -0
  420. package/docs/zh/24_CLAUDE_CODE_PROVIDER_SETUP.md +460 -0
  421. package/docs/zh/25_OPENCODE_PROVIDER_SETUP.md +660 -0
  422. package/docs/zh/26_CITATION_AND_ATTRIBUTION.md +102 -0
  423. package/docs/zh/27_KIMI_CODE_PROVIDER_SETUP.md +51 -0
  424. package/docs/zh/{19_LOCAL_BROWSER_AUTH.md → 31_LOCAL_BROWSER_AUTH.md} +1 -1
  425. package/docs/zh/32_WINDOWS_WSL2_DEPLOYMENT_GUIDE.md +264 -0
  426. package/docs/zh/33_WORKSPACE_EXPLORER_QA.md +127 -0
  427. package/docs/zh/99_ACKNOWLEDGEMENTS.md +23 -19
  428. package/docs/zh/README.md +33 -7
  429. package/install.sh +168 -20
  430. package/package.json +5 -1
  431. package/pyproject.toml +2 -1
  432. package/src/deepscientist/__init__.py +1 -1
  433. package/src/deepscientist/acp/envelope.py +13 -0
  434. package/src/deepscientist/admin/__init__.py +3 -0
  435. package/src/deepscientist/admin/charts.py +681 -0
  436. package/src/deepscientist/admin/logs.py +119 -0
  437. package/src/deepscientist/admin/repairs.py +217 -0
  438. package/src/deepscientist/admin/service.py +1310 -0
  439. package/src/deepscientist/admin/system_info.py +700 -0
  440. package/src/deepscientist/admin/tasks.py +465 -0
  441. package/src/deepscientist/admin/tool_metrics.py +600 -0
  442. package/src/deepscientist/artifact/guidance.py +8 -4
  443. package/src/deepscientist/artifact/schemas.py +115 -0
  444. package/src/deepscientist/artifact/service.py +4268 -260
  445. package/src/deepscientist/bash_exec/monitor.py +30 -3
  446. package/src/deepscientist/bash_exec/service.py +134 -1
  447. package/src/deepscientist/benchstore/__init__.py +4 -0
  448. package/src/deepscientist/benchstore/prompt_builder.py +224 -0
  449. package/src/deepscientist/benchstore/service.py +1716 -0
  450. package/src/deepscientist/bridges/connectors.py +8 -2
  451. package/src/deepscientist/channels/weixin_ilink.py +8 -1
  452. package/src/deepscientist/cli.py +92 -17
  453. package/src/deepscientist/codex_cli_compat.py +187 -74
  454. package/src/deepscientist/config/models.py +82 -11
  455. package/src/deepscientist/config/service.py +1077 -93
  456. package/src/deepscientist/connector/weixin_support.py +48 -17
  457. package/src/deepscientist/daemon/api/handlers.py +827 -235
  458. package/src/deepscientist/daemon/api/router.py +81 -1
  459. package/src/deepscientist/daemon/app.py +1512 -85
  460. package/src/deepscientist/diagnostics/__init__.py +6 -0
  461. package/src/deepscientist/diagnostics/runner_failures.py +277 -0
  462. package/src/deepscientist/doctor.py +407 -56
  463. package/src/deepscientist/evidence_packets.py +590 -0
  464. package/src/deepscientist/home.py +52 -4
  465. package/src/deepscientist/kimi_cli_compat.py +50 -0
  466. package/src/deepscientist/latex_runtime.py +2 -2
  467. package/src/deepscientist/mcp/context.py +2 -0
  468. package/src/deepscientist/mcp/schemas.py +114 -0
  469. package/src/deepscientist/mcp/server.py +1566 -126
  470. package/src/deepscientist/memory/service.py +203 -16
  471. package/src/deepscientist/process_control.py +8 -1
  472. package/src/deepscientist/prompts/builder.py +850 -88
  473. package/src/deepscientist/quest/__init__.py +2 -2
  474. package/src/deepscientist/quest/layout.py +12 -1
  475. package/src/deepscientist/quest/node_traces.py +10 -0
  476. package/src/deepscientist/quest/service.py +1852 -161
  477. package/src/deepscientist/quest/stage_views.py +1 -1
  478. package/src/deepscientist/runners/__init__.py +18 -0
  479. package/src/deepscientist/runners/base.py +89 -1
  480. package/src/deepscientist/runners/builtins.py +13 -1
  481. package/src/deepscientist/runners/claude.py +391 -0
  482. package/src/deepscientist/runners/codex.py +480 -35
  483. package/src/deepscientist/runners/codex_telemetry.py +127 -0
  484. package/src/deepscientist/runners/kimi.py +334 -0
  485. package/src/deepscientist/runners/metadata.py +68 -0
  486. package/src/deepscientist/runners/opencode.py +414 -0
  487. package/src/deepscientist/runners/runtime_overrides.py +100 -0
  488. package/src/deepscientist/runners/simple_cli.py +538 -0
  489. package/src/deepscientist/runtime_storage.py +303 -0
  490. package/src/deepscientist/shared.py +80 -16
  491. package/src/deepscientist/skills/installer.py +37 -0
  492. package/src/deepscientist/skills/registry.py +2 -0
  493. package/src/deepscientist/tinytex.py +2 -2
  494. package/src/deepscientist/tui.py +10 -3
  495. package/src/prompts/benchstore/system.md +77 -0
  496. package/src/prompts/connectors/qq.md +33 -2
  497. package/src/prompts/connectors/weixin.md +208 -23
  498. package/src/prompts/contracts/admin_ops.md +74 -0
  499. package/src/prompts/contracts/admin_ops_knowledge.md +138 -0
  500. package/src/prompts/contracts/shared_interaction.md +5 -10
  501. package/src/prompts/start_setup/system.md +422 -0
  502. package/src/prompts/system.md +411 -304
  503. package/src/prompts/system_copilot.md +89 -0
  504. package/src/skills/analysis-campaign/SKILL.md +239 -578
  505. package/src/skills/analysis-campaign/references/artifact-flow-examples.md +102 -0
  506. package/src/skills/analysis-campaign/references/boundary-cases.md +98 -0
  507. package/src/skills/analysis-campaign/references/campaign-checklist-template.md +39 -24
  508. package/src/skills/analysis-campaign/references/campaign-design.md +26 -10
  509. package/src/skills/analysis-campaign/references/campaign-plan-template.md +53 -54
  510. package/src/skills/analysis-campaign/references/operational-guidance.md +97 -0
  511. package/src/skills/analysis-campaign/references/writing-facing-slice-examples.md +10 -20
  512. package/src/skills/baseline/SKILL.md +183 -461
  513. package/src/skills/baseline/references/artifact-flow-examples.md +106 -0
  514. package/src/skills/baseline/references/artifact-payload-examples.md +1 -1
  515. package/src/skills/baseline/references/baseline-checklist-template.md +27 -35
  516. package/src/skills/baseline/references/baseline-plan-template.md +37 -76
  517. package/src/skills/baseline/references/boundary-cases.md +86 -0
  518. package/src/skills/baseline/references/codebase-audit-checklist.md +2 -6
  519. package/src/skills/baseline/references/comparability-contract.md +7 -12
  520. package/src/skills/baseline/references/operational-guidance.md +56 -0
  521. package/src/skills/baseline/references/route-selection.md +5 -25
  522. package/src/skills/decision/SKILL.md +113 -306
  523. package/src/skills/decision/references/checkpoint-memory-template.md +47 -0
  524. package/src/skills/decision/references/operational-guidance.md +94 -0
  525. package/src/skills/decision/references/research-route-criteria.md +7 -8
  526. package/src/skills/decision/references/strategic-decision-template.md +13 -26
  527. package/src/skills/experiment/SKILL.md +132 -670
  528. package/src/skills/experiment/references/execution-playbook.md +374 -0
  529. package/src/skills/experiment/references/main-experiment-checklist-template.md +26 -2
  530. package/src/skills/experiment/references/main-experiment-plan-template.md +28 -17
  531. package/src/skills/experiment/references/operational-guidance.md +108 -0
  532. package/src/skills/finalize/SKILL.md +62 -0
  533. package/src/skills/finalize/references/checkpoint-memory-template.md +49 -0
  534. package/src/skills/finalize/references/resume-packet-template.md +7 -0
  535. package/src/skills/idea/SKILL.md +228 -15
  536. package/src/skills/idea/references/controlled-brainstorming-playbook.md +78 -0
  537. package/src/skills/idea/references/current-board-packet-template.md +61 -0
  538. package/src/skills/idea/references/high-value-idea-sourcing.md +119 -0
  539. package/src/skills/idea/references/idea-generation-playbook.md +21 -0
  540. package/src/skills/idea/references/idea-thinking-flow.md +6 -0
  541. package/src/skills/idea/references/literature-survey-template.md +3 -0
  542. package/src/skills/idea/references/objective-contract-template.md +54 -0
  543. package/src/skills/idea/references/outline-seeding-example.md +56 -0
  544. package/src/skills/idea/references/pre-idea-draft-template.md +105 -0
  545. package/src/skills/idea/references/related-work-playbook.md +75 -2
  546. package/src/skills/idea/references/research-history-playbook.md +114 -0
  547. package/src/skills/idea/references/selection-gate.md +58 -6
  548. package/src/skills/intake-audit/SKILL.md +43 -2
  549. package/src/skills/intake-audit/references/state-audit-template.md +10 -0
  550. package/src/skills/nature-data/SKILL.md +128 -0
  551. package/src/skills/nature-data/UPSTREAM_LICENSE.txt +21 -0
  552. package/src/skills/nature-data/agents/openai.yaml +4 -0
  553. package/src/skills/nature-data/references/chinese-author-alignment.md +84 -0
  554. package/src/skills/nature-data/references/fair-metadata-checklist.md +105 -0
  555. package/src/skills/nature-data/references/policy-principles.md +103 -0
  556. package/src/skills/nature-data/references/repository-and-identifiers.md +96 -0
  557. package/src/skills/nature-data/references/source-basis.md +54 -0
  558. package/src/skills/nature-data/references/statement-patterns.md +153 -0
  559. package/src/skills/nature-figure/SKILL.md +197 -0
  560. package/src/skills/nature-figure/UPSTREAM_LICENSE.txt +21 -0
  561. package/src/skills/nature-figure/agents/openai.yaml +4 -0
  562. package/src/skills/nature-figure/evals/evals.json +37 -0
  563. package/src/skills/nature-figure/references/api.md +428 -0
  564. package/src/skills/nature-figure/references/backend-selection.md +100 -0
  565. package/src/skills/nature-figure/references/chart-types.md +281 -0
  566. package/src/skills/nature-figure/references/common-patterns.md +349 -0
  567. package/src/skills/nature-figure/references/design-theory.md +436 -0
  568. package/src/skills/nature-figure/references/figure-contract.md +93 -0
  569. package/src/skills/nature-figure/references/nature-2026-observations.md +112 -0
  570. package/src/skills/nature-figure/references/qa-contract.md +119 -0
  571. package/src/skills/nature-figure/references/r-template-index.md +66 -0
  572. package/src/skills/nature-figure/references/r-workflow.md +161 -0
  573. package/src/skills/nature-figure/references/tutorials.md +250 -0
  574. package/src/skills/nature-paper2ppt/SKILL.md +507 -0
  575. package/src/skills/nature-paper2ppt/UPSTREAM_LICENSE.txt +21 -0
  576. package/src/skills/nature-paper2ppt/agents/openai.yaml +4 -0
  577. package/src/skills/nature-polishing/SKILL.md +385 -0
  578. package/src/skills/nature-polishing/UPSTREAM_LICENSE.txt +21 -0
  579. package/src/skills/nature-polishing/agents/openai.yaml +4 -0
  580. package/src/skills/nature-polishing/references/phrasebank-playbook.md +162 -0
  581. package/src/skills/nature-polishing/references/section-moves.md +240 -0
  582. package/src/skills/nature-polishing/references/style-guardrails.md +94 -0
  583. package/src/skills/nature-polishing/references/writing-strategy.md +148 -0
  584. package/src/skills/optimize/SKILL.md +177 -1568
  585. package/src/skills/optimize/references/brief-shaping-playbook.md +95 -0
  586. package/src/skills/optimize/references/candidate-board-template.md +13 -0
  587. package/src/skills/optimize/references/candidate-ranking-template.md +51 -0
  588. package/src/skills/optimize/references/codegen-route-playbook.md +50 -0
  589. package/src/skills/optimize/references/debug-response-template.md +29 -0
  590. package/src/skills/optimize/references/frontier-review-template.md +32 -0
  591. package/src/skills/optimize/references/fusion-playbook.md +36 -0
  592. package/src/skills/optimize/references/method-brief-template.md +73 -0
  593. package/src/skills/optimize/references/operational-guidance.md +621 -0
  594. package/src/skills/optimize/references/optimization-memory-template.md +30 -0
  595. package/src/skills/optimize/references/optimize-checklist-template.md +18 -0
  596. package/src/skills/optimize/references/plateau-response-playbook.md +28 -0
  597. package/src/skills/optimize/references/prompt-patterns.md +49 -0
  598. package/src/skills/paper-outline/SKILL.md +227 -0
  599. package/src/skills/paper-outline/references/outline-patterns.md +87 -0
  600. package/src/skills/paper-plot/SKILL.md +79 -0
  601. package/src/skills/paper-plot/agents/openai.yaml +4 -0
  602. package/src/skills/paper-plot/references/bar_grouped_hatch.md +96 -0
  603. package/src/skills/paper-plot/references/bar_paired_delta.md +72 -0
  604. package/src/skills/paper-plot/references/line_confidence_band.md +75 -0
  605. package/src/skills/paper-plot/references/line_loss_with_inset.md +65 -0
  606. package/src/skills/paper-plot/references/line_training_curve.md +44 -0
  607. package/src/skills/paper-plot/references/radar_dual_series.md +59 -0
  608. package/src/skills/paper-plot/references/scatter_broken_axis.md +59 -0
  609. package/src/skills/paper-plot/references/scatter_tsne_cluster.md +72 -0
  610. package/src/skills/paper-plot/scripts/bar_memevolve.py +109 -0
  611. package/src/skills/paper-plot/scripts/bar_spice.py +166 -0
  612. package/src/skills/paper-plot/scripts/line_aime.py +94 -0
  613. package/src/skills/paper-plot/scripts/line_loss_inset.py +157 -0
  614. package/src/skills/paper-plot/scripts/line_selfdistill.py +168 -0
  615. package/src/skills/paper-plot/scripts/radar_dora.py +151 -0
  616. package/src/skills/paper-plot/scripts/scatter_break.py +169 -0
  617. package/src/skills/paper-plot/scripts/scatter_tsne.py +133 -0
  618. package/src/skills/rebuttal/SKILL.md +9 -0
  619. package/src/skills/references/tool-usage-by-stage.md +438 -0
  620. package/src/skills/review/SKILL.md +105 -7
  621. package/src/skills/science/PROVENANCE.md +44 -0
  622. package/src/skills/science/SKILL.md +137 -0
  623. package/src/skills/science/references/artifact-science-tool.md +110 -0
  624. package/src/skills/science/references/claim-type-discipline.md +56 -0
  625. package/src/skills/science/references/domain-index.md +422 -0
  626. package/src/skills/science/references/hpc-via-bash-exec.md +42 -0
  627. package/src/skills/science/references/package-check-playbook.md +64 -0
  628. package/src/skills/science/references/package-index.min.json +3616 -0
  629. package/src/skills/science/references/packages/abinit.md +80 -0
  630. package/src/skills/science/references/packages/acts.md +73 -0
  631. package/src/skills/science/references/packages/aiida-core.md +80 -0
  632. package/src/skills/science/references/packages/alamode.md +80 -0
  633. package/src/skills/science/references/packages/amuse.md +88 -0
  634. package/src/skills/science/references/packages/anndata.md +88 -0
  635. package/src/skills/science/references/packages/arbor.md +80 -0
  636. package/src/skills/science/references/packages/arc.md +73 -0
  637. package/src/skills/science/references/packages/astropy.md +88 -0
  638. package/src/skills/science/references/packages/astroquery.md +88 -0
  639. package/src/skills/science/references/packages/atomate2.md +80 -0
  640. package/src/skills/science/references/packages/atomsmltr.md +73 -0
  641. package/src/skills/science/references/packages/awkward.md +73 -0
  642. package/src/skills/science/references/packages/batman.md +88 -0
  643. package/src/skills/science/references/packages/biopython.md +88 -0
  644. package/src/skills/science/references/packages/bloqade.md +73 -0
  645. package/src/skills/science/references/packages/brian2.md +73 -0
  646. package/src/skills/science/references/packages/bullet3.md +73 -0
  647. package/src/skills/science/references/packages/calculix.md +80 -0
  648. package/src/skills/science/references/packages/cantera.md +73 -0
  649. package/src/skills/science/references/packages/cavity-md-ipi.md +80 -0
  650. package/src/skills/science/references/packages/ccdproc.md +88 -0
  651. package/src/skills/science/references/packages/celerite2.md +88 -0
  652. package/src/skills/science/references/packages/cellrank.md +73 -0
  653. package/src/skills/science/references/packages/cesm.md +80 -0
  654. package/src/skills/science/references/packages/chemicals.md +73 -0
  655. package/src/skills/science/references/packages/chempy.md +73 -0
  656. package/src/skills/science/references/packages/cirq.md +73 -0
  657. package/src/skills/science/references/packages/coffea.md +73 -0
  658. package/src/skills/science/references/packages/cp2k.md +88 -0
  659. package/src/skills/science/references/packages/custodian.md +80 -0
  660. package/src/skills/science/references/packages/dart.md +73 -0
  661. package/src/skills/science/references/packages/datamol.md +88 -0
  662. package/src/skills/science/references/packages/dd4hep.md +73 -0
  663. package/src/skills/science/references/packages/dealii.md +80 -0
  664. package/src/skills/science/references/packages/deepchem.md +88 -0
  665. package/src/skills/science/references/packages/delphes.md +73 -0
  666. package/src/skills/science/references/packages/devito.md +80 -0
  667. package/src/skills/science/references/packages/dftb.md +88 -0
  668. package/src/skills/science/references/packages/dftd4.md +88 -0
  669. package/src/skills/science/references/packages/dftk-jl.md +80 -0
  670. package/src/skills/science/references/packages/dolfinx.md +80 -0
  671. package/src/skills/science/references/packages/drake.md +73 -0
  672. package/src/skills/science/references/packages/dumux.md +73 -0
  673. package/src/skills/science/references/packages/elk.md +80 -0
  674. package/src/skills/science/references/packages/elmerfem.md +80 -0
  675. package/src/skills/science/references/packages/enzo-e.md +88 -0
  676. package/src/skills/science/references/packages/espresso.md +80 -0
  677. package/src/skills/science/references/packages/exoplanet.md +88 -0
  678. package/src/skills/science/references/packages/fairroot.md +73 -0
  679. package/src/skills/science/references/packages/fbpic.md +80 -0
  680. package/src/skills/science/references/packages/fdtdbath-meep.md +80 -0
  681. package/src/skills/science/references/packages/geant4.md +73 -0
  682. package/src/skills/science/references/packages/geosx.md +80 -0
  683. package/src/skills/science/references/packages/gprmax.md +80 -0
  684. package/src/skills/science/references/packages/gromacs.md +80 -0
  685. package/src/skills/science/references/packages/gwaslab.md +73 -0
  686. package/src/skills/science/references/packages/gz-sim.md +73 -0
  687. package/src/skills/science/references/packages/hail.md +88 -0
  688. package/src/skills/science/references/packages/hiphive.md +80 -0
  689. package/src/skills/science/references/packages/hoomd-blue.md +80 -0
  690. package/src/skills/science/references/packages/itensor.md +73 -0
  691. package/src/skills/science/references/packages/itensors-jl.md +73 -0
  692. package/src/skills/science/references/packages/jdftx.md +73 -0
  693. package/src/skills/science/references/packages/jobflow.md +80 -0
  694. package/src/skills/science/references/packages/kadanoffbaym-jl.md +73 -0
  695. package/src/skills/science/references/packages/kite.md +80 -0
  696. package/src/skills/science/references/packages/kratos.md +80 -0
  697. package/src/skills/science/references/packages/kwant.md +73 -0
  698. package/src/skills/science/references/packages/lammps.md +80 -0
  699. package/src/skills/science/references/packages/lightkurve.md +88 -0
  700. package/src/skills/science/references/packages/limix.md +73 -0
  701. package/src/skills/science/references/packages/maxwelllink.md +80 -0
  702. package/src/skills/science/references/packages/mcdc.md +73 -0
  703. package/src/skills/science/references/packages/meep.md +80 -0
  704. package/src/skills/science/references/packages/mfem.md +80 -0
  705. package/src/skills/science/references/packages/mitgcm.md +73 -0
  706. package/src/skills/science/references/packages/modflow6.md +73 -0
  707. package/src/skills/science/references/packages/molecool.md +73 -0
  708. package/src/skills/science/references/packages/mom6.md +73 -0
  709. package/src/skills/science/references/packages/moose.md +80 -0
  710. package/src/skills/science/references/packages/mpas-model.md +73 -0
  711. package/src/skills/science/references/packages/mujoco.md +73 -0
  712. package/src/skills/science/references/packages/mumax3.md +73 -0
  713. package/src/skills/science/references/packages/nekrs.md +80 -0
  714. package/src/skills/science/references/packages/nessi.md +73 -0
  715. package/src/skills/science/references/packages/nest-simulator.md +73 -0
  716. package/src/skills/science/references/packages/netket.md +73 -0
  717. package/src/skills/science/references/packages/neuron.md +73 -0
  718. package/src/skills/science/references/packages/nextflow.md +88 -0
  719. package/src/skills/science/references/packages/nwchem.md +88 -0
  720. package/src/skills/science/references/packages/openbabel.md +88 -0
  721. package/src/skills/science/references/packages/openems.md +80 -0
  722. package/src/skills/science/references/packages/openff-toolkit.md +88 -0
  723. package/src/skills/science/references/packages/openfoam-dev.md +80 -0
  724. package/src/skills/science/references/packages/openmc.md +73 -0
  725. package/src/skills/science/references/packages/openmm.md +80 -0
  726. package/src/skills/science/references/packages/openmoc.md +73 -0
  727. package/src/skills/science/references/packages/openmx.md +80 -0
  728. package/src/skills/science/references/packages/opensees.md +80 -0
  729. package/src/skills/science/references/packages/opensn.md +80 -0
  730. package/src/skills/science/references/packages/opm-simulators.md +73 -0
  731. package/src/skills/science/references/packages/oqupy.md +73 -0
  732. package/src/skills/science/references/packages/packmol.md +80 -0
  733. package/src/skills/science/references/packages/palabos.md +80 -0
  734. package/src/skills/science/references/packages/parflow.md +80 -0
  735. package/src/skills/science/references/packages/pennylane.md +88 -0
  736. package/src/skills/science/references/packages/perceval.md +73 -0
  737. package/src/skills/science/references/packages/phono3py.md +73 -0
  738. package/src/skills/science/references/packages/phonopy.md +73 -0
  739. package/src/skills/science/references/packages/photutils.md +88 -0
  740. package/src/skills/science/references/packages/picongpu.md +80 -0
  741. package/src/skills/science/references/packages/plink-ng.md +88 -0
  742. package/src/skills/science/references/packages/precice.md +73 -0
  743. package/src/skills/science/references/packages/psc.md +80 -0
  744. package/src/skills/science/references/packages/psi4.md +88 -0
  745. package/src/skills/science/references/packages/pybinding.md +73 -0
  746. package/src/skills/science/references/packages/pyfr.md +80 -0
  747. package/src/skills/science/references/packages/pyhf.md +73 -0
  748. package/src/skills/science/references/packages/pyiron_base.md +80 -0
  749. package/src/skills/science/references/packages/pylcp.md +73 -0
  750. package/src/skills/science/references/packages/pylith.md +80 -0
  751. package/src/skills/science/references/packages/pynbody.md +88 -0
  752. package/src/skills/science/references/packages/pysam.md +88 -0
  753. package/src/skills/science/references/packages/pyscf.md +88 -0
  754. package/src/skills/science/references/packages/q-e.md +73 -0
  755. package/src/skills/science/references/packages/qibo.md +73 -0
  756. package/src/skills/science/references/packages/qiskit.md +73 -0
  757. package/src/skills/science/references/packages/quantica-jl.md +73 -0
  758. package/src/skills/science/references/packages/quantumoptics-jl.md +73 -0
  759. package/src/skills/science/references/packages/quimb.md +73 -0
  760. package/src/skills/science/references/packages/qulacs.md +73 -0
  761. package/src/skills/science/references/packages/qutip.md +73 -0
  762. package/src/skills/science/references/packages/rdkit.md +88 -0
  763. package/src/skills/science/references/packages/rmg-py.md +73 -0
  764. package/src/skills/science/references/packages/root.md +73 -0
  765. package/src/skills/science/references/packages/scanpy.md +88 -0
  766. package/src/skills/science/references/packages/scikit-allel.md +88 -0
  767. package/src/skills/science/references/packages/scikit-bio.md +88 -0
  768. package/src/skills/science/references/packages/scqubits.md +73 -0
  769. package/src/skills/science/references/packages/scuff-em.md +80 -0
  770. package/src/skills/science/references/packages/scvi-tools.md +73 -0
  771. package/src/skills/science/references/packages/seissol.md +73 -0
  772. package/src/skills/science/references/packages/sfepy.md +80 -0
  773. package/src/skills/science/references/packages/sisl.md +73 -0
  774. package/src/skills/science/references/packages/smilei.md +80 -0
  775. package/src/skills/science/references/packages/snakemake.md +88 -0
  776. package/src/skills/science/references/packages/specfem3d-globe.md +80 -0
  777. package/src/skills/science/references/packages/specutils.md +88 -0
  778. package/src/skills/science/references/packages/spglib.md +80 -0
  779. package/src/skills/science/references/packages/squidpy.md +88 -0
  780. package/src/skills/science/references/packages/starry.md +88 -0
  781. package/src/skills/science/references/packages/strawberryfields.md +73 -0
  782. package/src/skills/science/references/packages/su2.md +80 -0
  783. package/src/skills/science/references/packages/sunny-jl.md +73 -0
  784. package/src/skills/science/references/packages/sw4.md +73 -0
  785. package/src/skills/science/references/packages/swift.md +88 -0
  786. package/src/skills/science/references/packages/tdnegf.md +73 -0
  787. package/src/skills/science/references/packages/tenpy.md +73 -0
  788. package/src/skills/science/references/packages/thermo.md +73 -0
  789. package/src/skills/science/references/packages/tkwant.md +73 -0
  790. package/src/skills/science/references/packages/tvb-root.md +73 -0
  791. package/src/skills/science/references/packages/uproot5.md +73 -0
  792. package/src/skills/science/references/packages/vampire.md +80 -0
  793. package/src/skills/science/references/packages/wannier_tools.md +73 -0
  794. package/src/skills/science/references/packages/warpx.md +80 -0
  795. package/src/skills/science/references/packages/wrf.md +73 -0
  796. package/src/skills/science/references/packages/xtb.md +88 -0
  797. package/src/skills/science/references/packages/yt.md +73 -0
  798. package/src/skills/science/references/science-task-brief-template.md +71 -0
  799. package/src/skills/scout/SKILL.md +83 -425
  800. package/src/skills/scout/references/literature-scout-template.md +5 -24
  801. package/src/skills/scout/references/operational-guidance.md +191 -0
  802. package/src/skills/scout/references/paper-triage-playbook.md +11 -35
  803. package/src/skills/write/SKILL.md +744 -1246
  804. package/src/skills/write/references/experiments_analysis_patterns.md +129 -0
  805. package/src/skills/write/references/oral_package_patterns.md +252 -0
  806. package/src/skills/write/references/oral_writing_principles.md +291 -0
  807. package/src/skills/write/references/section_rewrite_checklist.md +234 -0
  808. package/src/tui/dist/app/AppContainer.js +1314 -27
  809. package/src/tui/dist/components/Composer.js +26 -1
  810. package/src/tui/dist/components/ConfigScreen.js +2 -1
  811. package/src/tui/dist/components/InputPrompt.js +25 -9
  812. package/src/tui/dist/components/MainContent.js +18 -3
  813. package/src/tui/dist/components/QuestScreen.js +3 -2
  814. package/src/tui/dist/components/UtilityScreen.js +37 -0
  815. package/src/tui/dist/hooks/useSafeInput.js +10 -0
  816. package/src/tui/dist/index.js +13 -1
  817. package/src/tui/dist/layouts/DefaultAppLayout.js +11 -8
  818. package/src/tui/dist/lib/api.js +89 -1
  819. package/src/tui/package.json +1 -1
  820. package/src/ui/dist/assets/{AnalysisPlugin-DnSm0GZn.js → AnalysisPlugin-CA94NGmI.js} +1 -1
  821. package/src/ui/dist/assets/CliPlugin-DHBzphZU.js +79 -0
  822. package/src/ui/dist/assets/CodeEditorPlugin-BOFwD2rn.js +2 -0
  823. package/src/ui/dist/assets/{CodeViewerPlugin-itb0tltR.js → CodeViewerPlugin-CqDpgjik.js} +4 -4
  824. package/src/ui/dist/assets/{DocViewerPlugin-DqKkiCI6.js → DocViewerPlugin-UDBgt8-4.js} +3 -3
  825. package/src/ui/dist/assets/GitCommitViewerPlugin-BmHtZ0bZ.js +6 -0
  826. package/src/ui/dist/assets/{GitDiffViewerPlugin-DxL2ezFG.js → GitDiffViewerPlugin-CAxjNorQ.js} +2 -2
  827. package/src/ui/dist/assets/{GitSnapshotViewer-B_RQm1YZ.js → GitSnapshotViewer-CweA6VON.js} +2 -2
  828. package/src/ui/dist/assets/{ImageViewerPlugin-tHqlXY3n.js → ImageViewerPlugin-C8wHGvGN.js} +5 -5
  829. package/src/ui/dist/assets/LabPlugin-COyyLUol.js +32 -0
  830. package/src/ui/dist/assets/{LatexPlugin-B495DTXC.js → LatexPlugin-BQjAaA5J.js} +4 -4
  831. package/src/ui/dist/assets/{MarkdownViewerPlugin-DG28-61B.js → MarkdownViewerPlugin-Dy1NE2dI.js} +3 -3
  832. package/src/ui/dist/assets/{MarketplacePlugin-BiOGT-Kj.js → MarketplacePlugin-DMIZtEJ2.js} +2 -2
  833. package/src/ui/dist/assets/NotebookEditor-CFHMq_Qt.js +91 -0
  834. package/src/ui/dist/assets/{NotebookEditor-CVsj8h_T.js → NotebookEditor-WFyd8Ybt.js} +23 -23
  835. package/src/ui/dist/assets/{PdfLoader-CASDQmxJ.js → PdfLoader-CLE5u5TS.js} +3 -3
  836. package/src/ui/dist/assets/{PdfMarkdownPlugin-BFhwoKsY.js → PdfMarkdownPlugin-_iNK_H83.js} +1 -1
  837. package/src/ui/dist/assets/PdfViewerPlugin-DgWsbInT.js +22 -0
  838. package/src/ui/dist/assets/SearchPlugin-DrZmn5iw.js +11 -0
  839. package/src/ui/dist/assets/{TextViewerPlugin-CB4DYfWO.js → TextViewerPlugin-D1-T3aC7.js} +4 -4
  840. package/src/ui/dist/assets/branding/runner-claude.svg +107 -0
  841. package/src/ui/dist/assets/branding/runner-codex.svg +10 -0
  842. package/src/ui/dist/assets/branding/runner-kimi.svg +14 -0
  843. package/src/ui/dist/assets/branding/runner-opencode.svg +7 -0
  844. package/src/ui/dist/assets/cli-store-CoZ-x5Ip.js +1 -0
  845. package/src/ui/dist/assets/{code-DLC6G24T.js → code-DbsmSd3Y.js} +1 -1
  846. package/src/ui/dist/assets/file-diff-panel-DsvyRz47.js +1 -0
  847. package/src/ui/dist/assets/{wrap-text-CwMn-iqb.js → file-jump-queue-DeQBikaw.js} +3 -3
  848. package/src/ui/dist/assets/{file-socket-Cu4Qln7Y.js → file-socket-DA5XIx88.js} +1 -1
  849. package/src/ui/dist/assets/fonts/ds-fonts.css +50 -4
  850. package/src/ui/dist/assets/images/deepxiv/register-guide.png +0 -0
  851. package/src/ui/dist/assets/index-39vY9LmZ.js +1 -0
  852. package/src/ui/dist/assets/{index-wQ7RIIRd.js → index-BsO46tJA.js} +1 -1
  853. package/src/ui/dist/assets/index-CHzJ2xtB.js +3530 -0
  854. package/src/ui/dist/assets/index-DH-zxoZ3.css +33 -0
  855. package/src/ui/dist/assets/{plugin-notebook-HbW2K-1c.js → plugin-notebook-JRhysCqj.js} +2 -2
  856. package/src/ui/dist/assets/{project-sync-CsX08Qno.js → project-sync-DPmWKmKD.js} +1 -1
  857. package/src/ui/dist/assets/{zoom-out-R-GWEhzS.js → zoom-out-DAukFWen.js} +3 -3
  858. package/src/ui/dist/index.html +3 -3
  859. package/src/skills/analysis-campaign/references/artifact-orchestration.md +0 -58
  860. package/src/skills/baseline/references/memory-playbook.md +0 -40
  861. package/src/skills/baseline/references/publishable-baseline-package.md +0 -30
  862. package/src/skills/write/references/outline-evidence-contract-example.md +0 -107
  863. package/src/skills/write/references/paper-experiment-matrix-template.md +0 -131
  864. package/src/skills/write/references/paper-section-playbook.md +0 -64
  865. package/src/skills/write/references/reviewer-first-writing.md +0 -64
  866. package/src/skills/write/references/revision-checklist.md +0 -70
  867. package/src/skills/write/references/section-contracts.md +0 -82
  868. package/src/skills/write/references/sentence-level-proofing.md +0 -49
  869. package/src/ui/dist/assets/AiManusChatView-COFACy7V.js +0 -204
  870. package/src/ui/dist/assets/CliPlugin-CvwCmDQ5.js +0 -109
  871. package/src/ui/dist/assets/CodeEditorPlugin-cOqSa0xq.js +0 -2
  872. package/src/ui/dist/assets/GitCommitViewerPlugin-DVgNHBCS.js +0 -1
  873. package/src/ui/dist/assets/LabCopilotPanel-ClMbq5Yu.js +0 -14
  874. package/src/ui/dist/assets/LabPlugin-L_SuE8ow.js +0 -22
  875. package/src/ui/dist/assets/NotebookEditor-C-4Kt1p9.js +0 -81
  876. package/src/ui/dist/assets/PdfViewerPlugin-DcOzU9vd.js +0 -17
  877. package/src/ui/dist/assets/SearchPlugin-CHj7M58O.js +0 -16
  878. package/src/ui/dist/assets/VNCViewer-CjlbyCB3.js +0 -11
  879. package/src/ui/dist/assets/bot-CFkZY-JP.js +0 -6
  880. package/src/ui/dist/assets/chevron-up-Dq5ofbht.js +0 -6
  881. package/src/ui/dist/assets/file-content-Dv4LoZec.js +0 -1
  882. package/src/ui/dist/assets/file-diff-panel-Denq-lC3.js +0 -1
  883. package/src/ui/dist/assets/file-jump-queue-DA-SdG__.js +0 -1
  884. package/src/ui/dist/assets/git-commit-horizontal-BUh6G52n.js +0 -6
  885. package/src/ui/dist/assets/image-B9HUUddG.js +0 -6
  886. package/src/ui/dist/assets/index-B2B1sg-M.js +0 -1
  887. package/src/ui/dist/assets/index-Cgla8biy.css +0 -33
  888. package/src/ui/dist/assets/index-DRyx7vAc.js +0 -1
  889. package/src/ui/dist/assets/index-Gbl53BNp.js +0 -2496
  890. package/src/ui/dist/assets/pdf-effect-queue-ZtnHFCAi.js +0 -6
  891. package/src/ui/dist/assets/popover-DL6h35vr.js +0 -1
  892. package/src/ui/dist/assets/select-DvmXt1yY.js +0 -11
  893. package/src/ui/dist/assets/sigma-7jpXazui.js +0 -6
  894. package/src/ui/dist/assets/trash-xA7kFt8i.js +0 -11
  895. package/src/ui/dist/assets/useCliAccess-DsMwDjOp.js +0 -1
  896. package/src/ui/dist/assets/useFileDiffOverlay-FuhcnKiw.js +0 -1
@@ -2,19 +2,29 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import os
5
+ import shlex
6
+ import shutil
5
7
  import subprocess
6
8
  import tempfile
7
9
  from copy import deepcopy
8
10
  from pathlib import Path
9
11
  from urllib.error import URLError
12
+ from urllib.parse import urlencode
10
13
  from urllib.request import Request
11
14
 
12
15
  from ..codex_cli_compat import (
16
+ active_provider_metadata_from_home,
13
17
  adapt_profile_only_provider_config,
18
+ chat_wire_compatible_codex_version,
19
+ codex_cli_version,
20
+ format_codex_cli_version,
14
21
  materialize_codex_runtime_home,
22
+ missing_provider_env_key,
23
+ missing_provider_env_key_from_text,
15
24
  normalize_codex_reasoning_effort,
16
- provider_profile_metadata_from_home,
25
+ provider_base_url_looks_local,
17
26
  )
27
+ from ..kimi_cli_compat import materialize_kimi_runtime_home
18
28
  from ..connector.connector_profiles import PROFILEABLE_CONNECTOR_NAMES, list_connector_profiles, normalize_connector_config
19
29
  from ..connector_runtime import build_discovered_target, infer_connector_transport
20
30
  from ..home import repo_root
@@ -43,8 +53,9 @@ from ..connector.qq_profiles import (
43
53
  )
44
54
  from ..connector.weixin_support import normalize_weixin_base_url, normalize_weixin_cdn_base_url
45
55
  from ..network import urlopen_with_proxy as urlopen
56
+ from ..runners.metadata import get_runner_metadata, list_builtin_runner_names
46
57
  from ..runners.runtime_overrides import apply_codex_runtime_overrides, apply_runners_runtime_overrides
47
- from ..shared import read_json, read_text, read_yaml, resolve_runner_binary, run_command, sha256_text, utc_now, which, write_text, write_yaml
58
+ from ..shared import ensure_utf8_subprocess_env, read_json, read_text, read_yaml, resolve_runner_binary, run_command, sha256_text, utc_now, utf8_text_subprocess_kwargs, which, write_json, write_text, write_yaml
48
59
  from .models import (
49
60
  CONFIG_NAMES,
50
61
  OPTIONAL_CONFIG_NAMES,
@@ -205,31 +216,83 @@ class ConfigManager:
205
216
  return prepared
206
217
 
207
218
  def _invalidate_codex_bootstrap_state_if_runner_changed(self, previous: dict, current: dict) -> None:
208
- previous_codex = previous.get("codex") if isinstance(previous.get("codex"), dict) else {}
209
- current_codex = current.get("codex") if isinstance(current.get("codex"), dict) else {}
210
- tracked_keys = (
211
- "binary",
212
- "config_dir",
213
- "profile",
214
- "model",
215
- "model_reasoning_effort",
216
- "approval_policy",
217
- "sandbox_mode",
218
- "env",
219
- )
220
- if all(previous_codex.get(key) == current_codex.get(key) for key in tracked_keys):
219
+ tracked_keys_by_runner = {
220
+ "codex": (
221
+ "binary",
222
+ "config_dir",
223
+ "profile",
224
+ "model",
225
+ "model_reasoning_effort",
226
+ "approval_policy",
227
+ "sandbox_mode",
228
+ "env",
229
+ "mcp_tool_timeout_sec",
230
+ ),
231
+ "claude": (
232
+ "binary",
233
+ "config_dir",
234
+ "model",
235
+ "permission_mode",
236
+ "mcp_timeout_ms",
237
+ "mcp_tool_timeout_ms",
238
+ "env",
239
+ ),
240
+ "kimi": (
241
+ "binary",
242
+ "config_dir",
243
+ "model",
244
+ "agent",
245
+ "thinking",
246
+ "yolo",
247
+ "mcp_tool_timeout_ms",
248
+ "env",
249
+ ),
250
+ "opencode": (
251
+ "binary",
252
+ "config_dir",
253
+ "model",
254
+ "permission_mode",
255
+ "default_agent",
256
+ "variant",
257
+ "mcp_timeout_ms",
258
+ "env",
259
+ ),
260
+ }
261
+ changed_runners: list[str] = []
262
+ for runner_name, tracked_keys in tracked_keys_by_runner.items():
263
+ previous_runner = previous.get(runner_name) if isinstance(previous.get(runner_name), dict) else {}
264
+ current_runner = current.get(runner_name) if isinstance(current.get(runner_name), dict) else {}
265
+ if all(previous_runner.get(key) == current_runner.get(key) for key in tracked_keys):
266
+ continue
267
+ changed_runners.append(runner_name)
268
+ if not changed_runners:
221
269
  return
222
270
  config = self.load_named_normalized("config")
223
271
  bootstrap = config.get("bootstrap") if isinstance(config.get("bootstrap"), dict) else {}
224
- bootstrap["codex_ready"] = False
225
- bootstrap["codex_last_checked_at"] = utc_now()
226
- bootstrap["codex_last_result"] = {
227
- "ok": False,
228
- "summary": "Codex runner configuration changed. A new startup probe is required.",
229
- "warnings": [],
230
- "errors": [],
231
- "guidance": [],
232
- }
272
+ runner_readiness = bootstrap.get("runner_readiness") if isinstance(bootstrap.get("runner_readiness"), dict) else {}
273
+ checked_at = utc_now()
274
+ for runner_name in changed_runners:
275
+ try:
276
+ runner_label = get_runner_metadata(runner_name).label
277
+ except KeyError:
278
+ runner_label = runner_name
279
+ summary = f"{runner_label} runner configuration changed. A new startup probe is required."
280
+ runner_readiness[runner_name] = {
281
+ "ready": False,
282
+ "last_checked_at": checked_at,
283
+ "last_result": {
284
+ "ok": False,
285
+ "summary": summary,
286
+ "warnings": [],
287
+ "errors": [],
288
+ "guidance": [],
289
+ },
290
+ }
291
+ bootstrap["runner_readiness"] = runner_readiness
292
+ codex_state = runner_readiness.get("codex") if isinstance(runner_readiness.get("codex"), dict) else {}
293
+ bootstrap["codex_ready"] = bool(codex_state.get("ready", False))
294
+ bootstrap["codex_last_checked_at"] = codex_state.get("last_checked_at")
295
+ bootstrap["codex_last_result"] = codex_state.get("last_result") if isinstance(codex_state.get("last_result"), dict) else {}
233
296
  config["bootstrap"] = bootstrap
234
297
  self.save_named_text("config", self.render_named_payload("config", config))
235
298
 
@@ -470,12 +533,12 @@ The **Test** button checks:
470
533
 
471
534
  This is a safe local smoke test.
472
535
 
473
- ## Codex startup gate
536
+ ## Runner startup gate
474
537
 
475
- - `bootstrap.codex_ready` starts as `false`
476
- - the launcher runs a real Codex hello probe before first daemon start
477
- - once Codex answers correctly, DeepScientist flips this flag to `true`
478
- - if the probe fails, DeepScientist writes the failure summary back into config and blocks startup
538
+ - the launcher runs a real hello probe for the selected runner before first daemon start
539
+ - `codex`, `claude`, `kimi`, and `opencode` can each record readiness under `bootstrap.runner_readiness`
540
+ - if the selected runner fails, DeepScientist writes the failure summary back into config and blocks only that requested startup path
541
+ - you can start with the default runner first, then configure or switch Claude Code / Kimi Code / OpenCode from Settings
479
542
 
480
543
  ## Figure and chart style policy
481
544
 
@@ -491,15 +554,16 @@ This page edits `{home_text}/config/runners.yaml`.
491
554
  ## Recommended v1 choice
492
555
 
493
556
  - keep `codex.enabled: true`
494
- - keep `claude.enabled: false`
495
- - `claude` remains TODO / reserved in the current open-source release and is not runnable yet
557
+ - enable whichever runners you actually plan to use (`codex`, `claude`, `kimi`, `opencode`)
558
+ - keep the others disabled if their local CLI or credentials are not ready yet
496
559
  - set `codex.profile` only when your Codex CLI uses a named provider profile such as `m27`
497
560
  - when you launch DeepScientist ad hoc with a provider profile, you can also use `ds --codex-profile <name>`
498
561
  - when you want a one-off Codex binary override, you can also use `ds --codex /absolute/path/to/codex`
499
562
  - keep `codex.model_reasoning_effort: xhigh` unless you explicitly want a lighter default
500
563
  - keep `codex.retry_on_failure: true` so transient Codex failures can resume automatically
501
- - keep retry timing near `10s / 6x / 1800s max` so Codex backs off exponentially and the last retry waits about 30 minutes
502
- - DeepScientist hard-limits one turn to at most `5` total attempts, even if the config says more
564
+ - keep retry timing near `10s / 6x / 1800s max` so Codex backs off exponentially and the final retries sit at the 30-minute cap
565
+ - DeepScientist hard-limits one turn to at most `7` total attempts, even if the config says more
566
+ - one-off diagnostics can target a runner without permanently enabling it: `ds doctor --runner claude`, `ds doctor --runner kimi`, or `ds doctor --runner opencode`
503
567
 
504
568
  ## Test behavior
505
569
 
@@ -507,7 +571,7 @@ The **Test** button checks:
507
571
 
508
572
  - whether the configured runner binaries are on PATH
509
573
  - whether disabled runners are intentionally skipped
510
- - for Codex, it also runs a real hello probe so login problems, profile misconfiguration, and first-run setup issues surface before quest execution
574
+ - for enabled runners, it also runs a real hello probe so login problems, profile misconfiguration, and first-run setup issues surface before quest execution
511
575
  - it does not simulate the full failure/retry loop, so use quest runtime logs when debugging recovery behavior
512
576
  """
513
577
  if name == "plugins":
@@ -601,21 +665,159 @@ Use **Test** when the file exposes runtime dependencies.
601
665
  rendered = self.render_named_payload(name, payload)
602
666
  return self.test_named_text(name, rendered, live=live, delivery_targets=delivery_targets)
603
667
 
604
- def probe_codex_bootstrap(self, *, persist: bool = False, payload: dict | None = None) -> dict:
605
- runners_payload = payload if isinstance(payload, dict) else self.load_named_normalized("runners")
606
- codex_payload = runners_payload.get("codex") if isinstance(runners_payload.get("codex"), dict) else {}
607
- result = self._probe_codex_runner(codex_payload)
668
+ def test_deepxiv_payload(self, payload: dict | None = None) -> dict:
669
+ normalized = self._normalize_named_payload("config", payload if isinstance(payload, dict) else self.load_named_normalized("config"))
670
+ literature = normalized.get("literature") if isinstance(normalized.get("literature"), dict) else {}
671
+ deepxiv = literature.get("deepxiv") if isinstance(literature.get("deepxiv"), dict) else {}
672
+ base_url = str(deepxiv.get("base_url") or "https://data.rag.ac.cn").strip() or "https://data.rag.ac.cn"
673
+ direct_token = str(deepxiv.get("token") or "").strip()
674
+ token_env_name = str(deepxiv.get("token_env") or "").strip()
675
+ env_token = str(os.environ.get(token_env_name) or "").strip() if token_env_name else ""
676
+ resolved_token = direct_token or env_token
677
+ query = "transformers"
678
+ result_size = max(1, int(deepxiv.get("default_result_size") or 10))
679
+ preview_characters = max(200, int(deepxiv.get("preview_characters") or 1200))
680
+ request_timeout_seconds = max(3, int(deepxiv.get("request_timeout_seconds") or 20))
681
+ details = {
682
+ "base_url": base_url,
683
+ "query": query,
684
+ "result_size": result_size,
685
+ "preview_characters": preview_characters,
686
+ "request_timeout_seconds": request_timeout_seconds,
687
+ "token_source": "direct_token" if direct_token else ("env" if env_token else "missing"),
688
+ "token_env": token_env_name or None,
689
+ }
690
+ if not resolved_token:
691
+ return {
692
+ "ok": False,
693
+ "summary": "DeepXiv test failed: token is missing.",
694
+ "warnings": [],
695
+ "errors": ["Provide a DeepXiv token before running the test."],
696
+ "details": details,
697
+ "results": [],
698
+ "preview": "",
699
+ }
700
+ url = f"{base_url.rstrip('/')}/arxiv/?{urlencode({'type': 'retrieve', 'query': query, 'size': str(result_size)})}"
701
+ request = Request(
702
+ url,
703
+ headers={
704
+ "Accept": "application/json",
705
+ "Authorization": f"Bearer {resolved_token}",
706
+ "User-Agent": "DeepScientist/DeepXivTest",
707
+ },
708
+ )
709
+ try:
710
+ with urlopen(request, timeout=request_timeout_seconds) as response: # noqa: S310
711
+ response_text = response.read().decode("utf-8", errors="replace")
712
+ except Exception as exc:
713
+ details["request_url"] = url
714
+ return {
715
+ "ok": False,
716
+ "summary": "DeepXiv test request failed.",
717
+ "warnings": [],
718
+ "errors": [str(exc)],
719
+ "details": details,
720
+ "results": [],
721
+ "preview": "",
722
+ }
723
+ try:
724
+ parsed = json.loads(response_text)
725
+ except json.JSONDecodeError:
726
+ preview = response_text[:preview_characters].rstrip()
727
+ if len(response_text) > preview_characters:
728
+ preview = f"{preview}\n...[truncated]"
729
+ details["request_url"] = url
730
+ return {
731
+ "ok": False,
732
+ "summary": "DeepXiv test returned invalid JSON.",
733
+ "warnings": [],
734
+ "errors": ["DeepXiv returned invalid JSON."],
735
+ "details": details,
736
+ "results": [],
737
+ "preview": preview,
738
+ }
739
+ results = parsed.get("results") if isinstance(parsed.get("results"), list) else []
740
+ if not results and isinstance(parsed.get("result"), list):
741
+ results = parsed.get("result") or []
742
+ total = parsed.get("total")
743
+ if total is None:
744
+ total = parsed.get("total_count")
745
+ preview_payload = {
746
+ "total": total,
747
+ "took": parsed.get("took"),
748
+ "results": results[: min(3, len(results))],
749
+ }
750
+ preview = json.dumps(preview_payload, ensure_ascii=False, indent=2)
751
+ if len(preview) > preview_characters:
752
+ preview = f"{preview[:preview_characters].rstrip()}\n...[truncated]"
753
+ details.update(
754
+ {
755
+ "request_url": url,
756
+ "total": total,
757
+ "result_count": len(results),
758
+ "first_title": str((results[0] or {}).get("title") or "").strip() if results else None,
759
+ }
760
+ )
761
+ ok = len(results) > 0
762
+ return {
763
+ "ok": ok,
764
+ "summary": "DeepXiv returned search results for `transformers`." if ok else "DeepXiv returned no search results for `transformers`.",
765
+ "warnings": [],
766
+ "errors": [] if ok else ["No results were returned for `transformers`."],
767
+ "details": details,
768
+ "results": results[:5],
769
+ "preview": preview,
770
+ }
771
+
772
+ def probe_runner_bootstrap(self, runner_name: str, *, persist: bool = False, payload: dict | None = None) -> dict:
773
+ normalized_runner = str(runner_name or "codex").strip().lower() or "codex"
774
+ runners_payload = payload if isinstance(payload, dict) else self.load_runners_config()
775
+ runner_payload = runners_payload.get(normalized_runner) if isinstance(runners_payload.get(normalized_runner), dict) else {}
776
+ if normalized_runner == "codex":
777
+ result = self._probe_codex_runner(runner_payload)
778
+ elif normalized_runner == "claude":
779
+ result = self._probe_claude_runner(runner_payload)
780
+ elif normalized_runner == "kimi":
781
+ result = self._probe_kimi_runner(runner_payload)
782
+ elif normalized_runner == "opencode":
783
+ result = self._probe_opencode_runner(runner_payload)
784
+ else:
785
+ raise KeyError(f"Unknown runner `{normalized_runner}`.")
608
786
  if persist:
609
- self._persist_codex_bootstrap_result(result)
787
+ self._persist_runner_bootstrap_result(normalized_runner, result)
610
788
  return result
611
789
 
612
- def codex_bootstrap_state(self) -> dict:
790
+ def runner_bootstrap_state(self, runner_name: str) -> dict:
791
+ normalized_runner = str(runner_name or "codex").strip().lower() or "codex"
613
792
  config = self.load_named_normalized("config")
614
793
  bootstrap = config.get("bootstrap") if isinstance(config.get("bootstrap"), dict) else {}
794
+ runner_readiness = bootstrap.get("runner_readiness") if isinstance(bootstrap.get("runner_readiness"), dict) else {}
795
+ runner_state = runner_readiness.get(normalized_runner) if isinstance(runner_readiness.get(normalized_runner), dict) else {}
796
+ if normalized_runner == "codex" and not runner_state:
797
+ runner_state = {
798
+ "ready": bool(bootstrap.get("codex_ready", False)),
799
+ "last_checked_at": bootstrap.get("codex_last_checked_at"),
800
+ "last_result": bootstrap.get("codex_last_result") if isinstance(bootstrap.get("codex_last_result"), dict) else {},
801
+ }
615
802
  return {
616
- "codex_ready": bool(bootstrap.get("codex_ready", False)),
617
- "codex_last_checked_at": bootstrap.get("codex_last_checked_at"),
618
- "codex_last_result": bootstrap.get("codex_last_result") if isinstance(bootstrap.get("codex_last_result"), dict) else {},
803
+ "runner": normalized_runner,
804
+ "ready": bool(runner_state.get("ready", False)),
805
+ "last_checked_at": runner_state.get("last_checked_at"),
806
+ "last_result": runner_state.get("last_result") if isinstance(runner_state.get("last_result"), dict) else {},
807
+ }
808
+
809
+ def runner_readiness_map(self) -> dict[str, dict[str, Any]]:
810
+ return {name: self.runner_bootstrap_state(name) for name in list_builtin_runner_names()}
811
+
812
+ def probe_codex_bootstrap(self, *, persist: bool = False, payload: dict | None = None) -> dict:
813
+ return self.probe_runner_bootstrap("codex", persist=persist, payload=payload)
814
+
815
+ def codex_bootstrap_state(self) -> dict:
816
+ state = self.runner_bootstrap_state("codex")
817
+ return {
818
+ "codex_ready": bool(state.get("ready")),
819
+ "codex_last_checked_at": state.get("last_checked_at"),
820
+ "codex_last_result": state.get("last_result") if isinstance(state.get("last_result"), dict) else {},
619
821
  }
620
822
 
621
823
  def git_readiness(self) -> dict:
@@ -695,7 +897,8 @@ Use **Test** when the file exposes runtime dependencies.
695
897
 
696
898
  def _test_runners_payload(self, payload: dict, *, live: bool) -> dict:
697
899
  items = []
698
- for name, config in payload.items():
900
+ normalized_payload = apply_runners_runtime_overrides(self._normalize_named_payload("runners", payload))
901
+ for name, config in normalized_payload.items():
699
902
  if not isinstance(config, dict):
700
903
  continue
701
904
  enabled = bool(config.get("enabled", False))
@@ -718,8 +921,8 @@ Use **Test** when the file exposes runtime dependencies.
718
921
  "live_probe_executed": False,
719
922
  },
720
923
  }
721
- if enabled and name == "codex" and exists and live:
722
- probe = self._probe_codex_runner(config)
924
+ if enabled and exists and live:
925
+ probe = self.probe_runner_bootstrap(name, persist=False, payload=normalized_payload)
723
926
  item["ok"] = bool(probe.get("ok"))
724
927
  item["warnings"] = [*warnings, *list(probe.get("warnings") or [])]
725
928
  item["errors"] = list(probe.get("errors") or [])
@@ -1215,7 +1418,7 @@ Use **Test** when the file exposes runtime dependencies.
1215
1418
  def _codex_runner_env(config: dict) -> dict[str, str]:
1216
1419
  raw_env = config.get("env")
1217
1420
  if not isinstance(raw_env, dict):
1218
- return {}
1421
+ return ensure_utf8_subprocess_env({})
1219
1422
  resolved: dict[str, str] = {}
1220
1423
  for key, value in raw_env.items():
1221
1424
  env_key = str(key or "").strip()
@@ -1225,7 +1428,7 @@ Use **Test** when the file exposes runtime dependencies.
1225
1428
  if env_value == "":
1226
1429
  continue
1227
1430
  resolved[env_key] = env_value
1228
- return resolved
1431
+ return ensure_utf8_subprocess_env(resolved)
1229
1432
 
1230
1433
  def _prepare_codex_probe_home(
1231
1434
  self,
@@ -1267,34 +1470,233 @@ Use **Test** when the file exposes runtime dependencies.
1267
1470
  ]
1268
1471
  )
1269
1472
  else:
1270
- guidance.append("Run `codex --login` (or `codex`) once and finish authentication before starting DeepScientist.")
1473
+ guidance.append("Run `codex login` (or just `codex`) once and finish authentication before starting DeepScientist.")
1271
1474
  guidance.append(
1272
1475
  "If you use a custom Codex path, either set `runners.codex.binary` or launch with `ds --codex /absolute/path/to/codex`."
1273
1476
  )
1274
1477
  return guidance
1275
1478
 
1276
- def _codex_probe_failure_guidance(self, config: dict) -> tuple[list[str], list[str]]:
1479
+ @staticmethod
1480
+ def _provider_profile_probe_hints(metadata: dict[str, object]) -> list[str]:
1481
+ base_url = str(metadata.get("base_url") or "").strip().lower()
1482
+ model = str(metadata.get("model") or "").strip().lower()
1483
+ provider = str(metadata.get("provider") or "").strip().lower()
1484
+ if "dashscope.aliyuncs.com" not in base_url and "bailian" not in provider and "qwen" not in model:
1485
+ return []
1486
+ if "coding.dashscope.aliyuncs.com" not in base_url:
1487
+ return [
1488
+ "Alibaba Bailian's generic DashScope / Qwen platform API is not supported by the Codex-backed DeepScientist path.",
1489
+ "If you want to use Qwen here, switch the profile to the Bailian Coding Plan endpoint: `https://coding.dashscope.aliyuncs.com/v1`.",
1490
+ ]
1491
+ return [
1492
+ "For Qwen on Alibaba Bailian, only the Coding Plan endpoint is supported here; do not switch back to the generic Bailian / DashScope Qwen API.",
1493
+ ]
1494
+
1495
+ @staticmethod
1496
+ def _local_provider_probe_hints(metadata: dict[str, object]) -> list[str]:
1497
+ base_url = str(metadata.get("base_url") or "").strip()
1498
+ wire_api = str(metadata.get("wire_api") or "").strip().lower()
1499
+ requires_openai_auth = metadata.get("requires_openai_auth")
1500
+ if not base_url:
1501
+ return []
1502
+ is_local_provider = provider_base_url_looks_local(base_url)
1503
+ if requires_openai_auth is not False and not is_local_provider:
1504
+ return []
1505
+ hints = [
1506
+ f"Verify the local provider directly: `curl {base_url}/models`.",
1507
+ f"Then verify the Responses API explicitly: `curl {base_url}/responses ...`.",
1508
+ "Latest Codex CLI requires `wire_api = \"responses\"`; chat-only provider configs are no longer accepted.",
1509
+ "If `/v1/chat/completions` works but `/v1/responses` fails, that backend is not currently compatible with the latest Codex runner.",
1510
+ "If the backend is chat-only and you still want to test it through Codex, try `@openai/codex@0.57.0` with top-level `model_provider` / `model` plus `wire_api = \"chat\"`.",
1511
+ "For local model backends, vLLM is the safest path. Ollama only works when its `/v1/responses` endpoint works; chat-only SGLang deployments will fail with the latest Codex.",
1512
+ ]
1513
+ if requires_openai_auth is not False:
1514
+ hints.insert(
1515
+ 0,
1516
+ "For local or self-hosted providers, add `requires_openai_auth = false` so DeepScientist can remove conflicting `OPENAI_*` auth variables.",
1517
+ )
1518
+ if not wire_api:
1519
+ hints.insert(0, "Your current provider config does not declare `wire_api`; set `wire_api = \"responses\"` first.")
1520
+ elif wire_api != "responses":
1521
+ hints.insert(0, f"Your current provider config uses `wire_api = \"{wire_api}\"`; switch it to `wire_api = \"responses\"` first.")
1522
+ return hints
1523
+
1524
+ @staticmethod
1525
+ def _codex_direct_hello_probe_command(*, profile: str = "") -> str:
1526
+ profile_args = f" --profile {shlex.quote(profile)}" if profile else ""
1527
+ return (
1528
+ "printf 'Reply with exactly HELLO.' | "
1529
+ f"codex --search{profile_args} exec --json --cd /tmp --skip-git-repo-check -"
1530
+ )
1531
+
1532
+ @staticmethod
1533
+ def _missing_provider_env_guidance(
1534
+ *,
1535
+ profile: str,
1536
+ env_key: str,
1537
+ metadata: dict[str, object],
1538
+ ) -> list[str]:
1539
+ guidance = [
1540
+ f"Set `runners.codex.env.{env_key}` in `~/DeepScientist/config/runners.yaml`, or export `{env_key}` before launching `ds`.",
1541
+ ]
1542
+ if provider_base_url_looks_local(str(metadata.get("base_url") or "").strip()):
1543
+ guidance.append(
1544
+ f"If `{env_key}` is only a placeholder for a local OpenAI-compatible backend, any non-empty value such as `1234` is usually enough."
1545
+ )
1546
+ if metadata.get("requires_openai_auth") is not False:
1547
+ guidance.append(
1548
+ "Also add `requires_openai_auth = false` to that local provider profile so DeepScientist can remove conflicting `OPENAI_*` auth variables."
1549
+ )
1550
+ guidance.append(
1551
+ "Before retrying DeepScientist, run a real request such as "
1552
+ f"`{ConfigManager._codex_direct_hello_probe_command(profile=profile)}` and verify it returns `HELLO`."
1553
+ )
1554
+ return guidance
1555
+
1556
+ @staticmethod
1557
+ def _chat_wire_probe_version_block(
1558
+ metadata: dict[str, object],
1559
+ *,
1560
+ resolved_binary: str,
1561
+ ) -> tuple[tuple[int, int, int] | None, dict[str, object] | None]:
1562
+ wire_api = str(metadata.get("wire_api") or "").strip().lower()
1563
+ if wire_api != "chat":
1564
+ return None, None
1565
+ detected_version = codex_cli_version(str(resolved_binary or ""))
1566
+ required_version = chat_wire_compatible_codex_version()
1567
+ if detected_version == required_version:
1568
+ return detected_version, None
1569
+ required_text = format_codex_cli_version(required_version)
1570
+ detected_text = format_codex_cli_version(detected_version)
1571
+ errors = [
1572
+ "This provider uses `wire_api = \"chat\"`, but DeepScientist only probes chat-mode providers with `codex-cli 0.57.0`.",
1573
+ ]
1574
+ if detected_text:
1575
+ errors.append(f"Detected Codex CLI version: `{detected_text}`.")
1576
+ else:
1577
+ errors.append("DeepScientist could not determine the active Codex CLI version from the configured binary.")
1578
+ guidance = [
1579
+ "Install `npm install -g @openai/codex@0.57.0`, or point DeepScientist at a dedicated `0.57.0` binary with `ds --codex /absolute/path/to/codex`.",
1580
+ "If you want to stay on a newer Codex CLI, switch the provider/backend to `wire_api = \"responses\"` instead.",
1581
+ "For chat-mode fallback configs, keep the compatible top-level `model_provider` / `model` entries in `~/.codex/config.toml`.",
1582
+ ]
1583
+ return (
1584
+ detected_version,
1585
+ {
1586
+ "summary": f"Codex startup probe blocked by chat-mode provider compatibility. Required Codex CLI: `{required_text}`.",
1587
+ "errors": errors,
1588
+ "guidance": guidance,
1589
+ },
1590
+ )
1591
+
1592
+ @staticmethod
1593
+ def _codex_probe_text_looks_auth_related(stdout_text: str, stderr_text: str) -> bool:
1594
+ haystack = f"{stdout_text}\n{stderr_text}".lower()
1595
+ markers = (
1596
+ "please login",
1597
+ "not logged in",
1598
+ "login required",
1599
+ "authentication required",
1600
+ "auth required",
1601
+ "oauth",
1602
+ "unauthenticated",
1603
+ "missing credentials",
1604
+ "no credentials",
1605
+ )
1606
+ return any(marker in haystack for marker in markers)
1607
+
1608
+ @staticmethod
1609
+ def _codex_probe_text_looks_network_related(stdout_text: str, stderr_text: str) -> bool:
1610
+ haystack = f"{stdout_text}\n{stderr_text}".lower()
1611
+ markers = (
1612
+ "connection refused",
1613
+ "connection reset",
1614
+ "connection timed out",
1615
+ "network is unreachable",
1616
+ "name or service not known",
1617
+ "temporary failure in name resolution",
1618
+ "dns",
1619
+ "proxy",
1620
+ "tls",
1621
+ "certificate",
1622
+ "ssl",
1623
+ "timeout",
1624
+ "timed out",
1625
+ "econnreset",
1626
+ "econnrefused",
1627
+ "enotfound",
1628
+ )
1629
+ return any(marker in haystack for marker in markers)
1630
+
1631
+ def _codex_probe_failure_guidance(
1632
+ self,
1633
+ config: dict,
1634
+ *,
1635
+ stdout_text: str = "",
1636
+ stderr_text: str = "",
1637
+ ) -> tuple[list[str], list[str]]:
1277
1638
  profile = self._codex_profile_name(config)
1639
+ config_dir = str(config.get("config_dir") or "~/.codex").strip()
1640
+ metadata = active_provider_metadata_from_home(config_dir, profile=profile or None) if config_dir else {}
1278
1641
  if profile:
1642
+ provider_hints = self._provider_profile_probe_hints(metadata)
1643
+ local_hints = self._local_provider_probe_hints(metadata)
1279
1644
  return (
1280
1645
  [
1281
1646
  f"Codex profile `{profile}` did not complete the startup hello probe successfully.",
1282
1647
  ],
1283
1648
  [
1284
- f"Run `codex --profile {profile}` in a terminal and confirm that profile can start normally.",
1649
+ f"Run `{self._codex_direct_hello_probe_command(profile=profile)}` in a terminal and confirm that a real `HELLO` request succeeds.",
1285
1650
  "If the profile uses a custom provider, make sure its API key, Base URL, and model configuration are available to Codex.",
1286
1651
  "If the provider expects the model from the Codex profile itself, set `model: inherit` in `~/DeepScientist/config/runners.yaml`.",
1652
+ *provider_hints,
1653
+ *local_hints,
1654
+ "Then run `ds doctor` and start DeepScientist again.",
1655
+ ],
1656
+ )
1657
+ if self._codex_probe_text_looks_auth_related(stdout_text, stderr_text):
1658
+ return (
1659
+ [
1660
+ "Codex reported an authentication or first-run setup problem during the startup hello probe.",
1661
+ "Run `codex login` (or just `codex`) once and complete login before starting DeepScientist.",
1662
+ ],
1663
+ [
1664
+ "Run `codex login` (or just `codex`) in a terminal and complete login or first-run setup.",
1665
+ "Then run `printf 'Reply with exactly HELLO.' | codex --search exec --json --cd /tmp --skip-git-repo-check -` and confirm the real request succeeds.",
1666
+ "If login succeeds but the direct probe still fails, check the configured model, provider profile, proxy, and Codex account access.",
1667
+ "Then run `ds doctor` and start DeepScientist again.",
1668
+ ],
1669
+ )
1670
+ if self._codex_model_unavailable(stdout_text, stderr_text):
1671
+ return (
1672
+ [
1673
+ "Codex authentication may be working, but the configured startup probe model was not accepted.",
1674
+ ],
1675
+ [
1676
+ "Run `printf 'Reply with exactly HELLO.' | codex --search exec --json --cd /tmp --skip-git-repo-check -` in the same shell and confirm the current Codex default model works.",
1677
+ "Set `runners.codex.model: inherit` in `~/DeepScientist/config/runners.yaml`, or set it to a model that your Codex account can actually use.",
1678
+ "Then run `ds doctor` and start DeepScientist again.",
1679
+ ],
1680
+ )
1681
+ if self._codex_probe_text_looks_network_related(stdout_text, stderr_text):
1682
+ return (
1683
+ [
1684
+ "Codex CLI was found, but the real startup hello request appears to have failed at the network, proxy, TLS, or provider layer.",
1685
+ ],
1686
+ [
1687
+ "Run `printf 'Reply with exactly HELLO.' | codex --search exec --json --cd /tmp --skip-git-repo-check -` from the same shell and verify the request succeeds.",
1688
+ "If this host needs a proxy, export `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` before launching `ds`; for managed or supervised runs, also put required environment variables under `runners.codex.env`.",
1287
1689
  "Then run `ds doctor` and start DeepScientist again.",
1288
1690
  ],
1289
1691
  )
1290
1692
  return (
1291
1693
  [
1292
- "Run `codex --login` (or `codex`) once and complete login before starting DeepScientist.",
1694
+ "Codex CLI was found, but its real startup hello request did not complete successfully.",
1293
1695
  ],
1294
1696
  [
1295
- "Run `codex --login` (or `codex`) in a terminal and complete login or first-run setup.",
1296
- "If `codex` is missing, install it explicitly with `npm install -g @openai/codex`.",
1297
- "If the configured model is not available to your Codex account, update `~/DeepScientist/config/runners.yaml` and try again.",
1697
+ "Run `printf 'Reply with exactly HELLO.' | codex --search exec --json --cd /tmp --skip-git-repo-check -` in the same shell and inspect the Codex stderr/stdout.",
1698
+ "If that direct command succeeds but `ds doctor` fails, compare `which codex`, `CODEX_HOME`, proxy variables, and `~/DeepScientist/config/runners.yaml`.",
1699
+ "If the configured model is not available to your Codex account, set `runners.codex.model: inherit` or another accessible model.",
1298
1700
  "Then run `ds doctor` and start DeepScientist again.",
1299
1701
  ],
1300
1702
  )
@@ -1417,8 +1819,8 @@ Use **Test** when the file exposes runtime dependencies.
1417
1819
  approval_policy = str(config.get("approval_policy") or "on-request").strip()
1418
1820
  sandbox_mode = str(config.get("sandbox_mode") or "workspace-write").strip()
1419
1821
 
1420
- env = os.environ.copy()
1421
- env.update(self._codex_runner_env(config))
1822
+ env = ensure_utf8_subprocess_env(os.environ.copy())
1823
+ env.update(self._claude_auth_runner_env(self._codex_runner_env(config)))
1422
1824
  config_dir = str(config.get("config_dir") or "~/.codex").strip()
1423
1825
  probe_home_handle: tempfile.TemporaryDirectory[str] | None = None
1424
1826
  compatibility_warnings: list[str] = []
@@ -1430,10 +1832,20 @@ Use **Test** when the file exposes runtime dependencies.
1430
1832
  env["CODEX_HOME"] = prepared_home
1431
1833
  if profile_config_warning:
1432
1834
  compatibility_warnings.append(profile_config_warning)
1433
- metadata = provider_profile_metadata_from_home(env.get("CODEX_HOME") or config_dir, profile=profile)
1835
+ metadata = active_provider_metadata_from_home(env.get("CODEX_HOME") or config_dir, profile=profile or None)
1434
1836
  if metadata.get("requires_openai_auth") is False:
1435
1837
  env.pop("OPENAI_API_KEY", None)
1436
1838
  env.pop("OPENAI_BASE_URL", None)
1839
+ configured_provider_env_key = missing_provider_env_key(metadata, env)
1840
+ details["provider_env_key"] = str(metadata.get("env_key") or "").strip() or None
1841
+ details["provider_env_missing"] = bool(configured_provider_env_key)
1842
+ details["provider_wire_api"] = str(metadata.get("wire_api") or "").strip() or None
1843
+ detected_codex_version, chat_wire_block = self._chat_wire_probe_version_block(
1844
+ metadata,
1845
+ resolved_binary=resolved_binary,
1846
+ )
1847
+ if detected_codex_version is not None:
1848
+ details["codex_cli_version"] = format_codex_cli_version(detected_codex_version) or None
1437
1849
  prompt = "Reply with exactly HELLO."
1438
1850
  if reasoning_effort_warning:
1439
1851
  compatibility_warnings.append(reasoning_effort_warning)
@@ -1442,6 +1854,15 @@ Use **Test** when the file exposes runtime dependencies.
1442
1854
  f"Codex profile `{profile}` is provider-backed. DeepScientist is probing it with `model: inherit`."
1443
1855
  )
1444
1856
  base_warnings: list[str] = list(compatibility_warnings)
1857
+ if chat_wire_block is not None:
1858
+ return {
1859
+ "ok": False,
1860
+ "summary": str(chat_wire_block["summary"]),
1861
+ "warnings": base_warnings,
1862
+ "errors": list(chat_wire_block["errors"]),
1863
+ "details": details,
1864
+ "guidance": list(chat_wire_block["guidance"]),
1865
+ }
1445
1866
 
1446
1867
  def run_probe_once(model_for_command: str) -> tuple[list[str], subprocess.CompletedProcess[str] | None, subprocess.TimeoutExpired | None]:
1447
1868
  command = self._build_codex_probe_command(
@@ -1458,10 +1879,10 @@ Use **Test** when the file exposes runtime dependencies.
1458
1879
  input=prompt,
1459
1880
  cwd=str(repo_root()),
1460
1881
  env=env,
1461
- text=True,
1462
1882
  capture_output=True,
1463
1883
  timeout=90,
1464
1884
  check=False,
1885
+ **utf8_text_subprocess_kwargs(),
1465
1886
  )
1466
1887
  except subprocess.TimeoutExpired as exc:
1467
1888
  return command, None, exc
@@ -1469,25 +1890,32 @@ Use **Test** when the file exposes runtime dependencies.
1469
1890
 
1470
1891
  command, result, timeout_error = run_probe_once(effective_model)
1471
1892
  if timeout_error is not None:
1893
+ stdout_text = str(timeout_error.stdout or "")
1894
+ stderr_text = str(timeout_error.stderr or "")
1472
1895
  details.update(
1473
1896
  {
1474
1897
  "exit_code": None,
1475
- "stdout_excerpt": self._compact_probe_text(timeout_error.stdout or ""),
1476
- "stderr_excerpt": self._compact_probe_text(timeout_error.stderr or ""),
1898
+ "stdout_excerpt": self._compact_probe_text(stdout_text),
1899
+ "stderr_excerpt": self._compact_probe_text(stderr_text),
1477
1900
  "probe_command": command,
1478
1901
  }
1479
1902
  )
1903
+ failure_errors, failure_guidance = self._codex_probe_failure_guidance(
1904
+ config,
1905
+ stdout_text=stdout_text,
1906
+ stderr_text=stderr_text,
1907
+ )
1480
1908
  return {
1481
1909
  "ok": False,
1482
1910
  "summary": "Codex startup probe timed out.",
1483
1911
  "warnings": base_warnings,
1484
1912
  "errors": [
1485
1913
  "Codex did not answer the startup hello probe within 90 seconds.",
1486
- *self._codex_probe_failure_guidance(config)[0],
1914
+ *failure_errors,
1487
1915
  ],
1488
1916
  "details": details,
1489
1917
  "guidance": [
1490
- *self._codex_probe_failure_guidance(config)[1],
1918
+ *failure_guidance,
1491
1919
  "If `codex` is missing on PATH, install it explicitly with `npm install -g @openai/codex`.",
1492
1920
  "Confirm the configured model is available to your Codex setup. DeepScientist currently probes Codex with the configured runner model first.",
1493
1921
  ],
@@ -1567,8 +1995,31 @@ Use **Test** when the file exposes runtime dependencies.
1567
1995
  warnings.append("Codex returned stderr during the startup probe.")
1568
1996
  if details.get("model_fallback_attempted") and not details.get("model_fallback_used"):
1569
1997
  warnings.append("DeepScientist also tried the current Codex default model, but that fallback probe did not succeed.")
1570
- errors.extend(self._codex_probe_failure_guidance(config)[0])
1571
- failure_guidance = self._codex_probe_failure_guidance(config)[1]
1998
+ errors.extend(
1999
+ self._codex_probe_failure_guidance(
2000
+ config,
2001
+ stdout_text=stdout_text,
2002
+ stderr_text=stderr_text,
2003
+ )[0]
2004
+ )
2005
+ missing_env_key = missing_provider_env_key_from_text(stdout_text, stderr_text) or configured_provider_env_key
2006
+ failure_guidance = self._codex_probe_failure_guidance(
2007
+ config,
2008
+ stdout_text=stdout_text,
2009
+ stderr_text=stderr_text,
2010
+ )[1]
2011
+ if not ok and missing_env_key and profile:
2012
+ errors.append(
2013
+ f"Codex profile `{profile}` requires environment variable `{missing_env_key}`, but DeepScientist did not receive it."
2014
+ )
2015
+ failure_guidance = [
2016
+ *self._missing_provider_env_guidance(
2017
+ profile=profile,
2018
+ env_key=missing_env_key,
2019
+ metadata=metadata,
2020
+ ),
2021
+ *failure_guidance,
2022
+ ]
1572
2023
  return {
1573
2024
  "ok": ok,
1574
2025
  "summary": "Codex startup probe completed." if ok else "Codex startup probe failed.",
@@ -1578,38 +2029,402 @@ Use **Test** when the file exposes runtime dependencies.
1578
2029
  "guidance": [] if ok else failure_guidance,
1579
2030
  }
1580
2031
 
1581
- def _persist_codex_bootstrap_result(self, result: dict) -> None:
2032
+ def _persist_runner_bootstrap_result(self, runner_name: str, result: dict) -> None:
2033
+ normalized_runner = str(runner_name or "codex").strip().lower() or "codex"
1582
2034
  config = self.load_named_normalized("config")
1583
2035
  bootstrap = config.get("bootstrap") if isinstance(config.get("bootstrap"), dict) else {}
1584
2036
  details = result.get("details") if isinstance(result.get("details"), dict) else {}
1585
- bootstrap["codex_ready"] = bool(result.get("ok"))
1586
- bootstrap["codex_last_checked_at"] = details.get("checked_at") or utc_now()
1587
- bootstrap["codex_last_result"] = {
1588
- "ok": bool(result.get("ok")),
1589
- "summary": result.get("summary"),
1590
- "warnings": list(result.get("warnings") or []),
1591
- "errors": list(result.get("errors") or []),
1592
- "guidance": list(result.get("guidance") or []),
1593
- "binary": details.get("binary"),
1594
- "resolved_binary": details.get("resolved_binary"),
1595
- "profile": details.get("profile"),
1596
- "model": details.get("model"),
1597
- "requested_model": details.get("requested_model"),
1598
- "effective_model": details.get("effective_model"),
1599
- "model_fallback_attempted": bool(details.get("model_fallback_attempted")),
1600
- "model_fallback_used": bool(details.get("model_fallback_used")),
1601
- "approval_policy": details.get("approval_policy"),
1602
- "sandbox_mode": details.get("sandbox_mode"),
1603
- "reasoning_effort": details.get("reasoning_effort"),
1604
- "exit_code": details.get("exit_code"),
1605
- "stdout_excerpt": details.get("stdout_excerpt"),
1606
- "stderr_excerpt": details.get("stderr_excerpt"),
2037
+ runner_readiness = bootstrap.get("runner_readiness") if isinstance(bootstrap.get("runner_readiness"), dict) else {}
2038
+ runner_readiness[normalized_runner] = {
2039
+ "ready": bool(result.get("ok")),
2040
+ "last_checked_at": details.get("checked_at") or utc_now(),
2041
+ "last_result": {
2042
+ "ok": bool(result.get("ok")),
2043
+ "summary": result.get("summary"),
2044
+ "warnings": list(result.get("warnings") or []),
2045
+ "errors": list(result.get("errors") or []),
2046
+ "guidance": list(result.get("guidance") or []),
2047
+ "binary": details.get("binary"),
2048
+ "resolved_binary": details.get("resolved_binary"),
2049
+ "model": details.get("model"),
2050
+ "requested_model": details.get("requested_model"),
2051
+ "effective_model": details.get("effective_model"),
2052
+ "exit_code": details.get("exit_code"),
2053
+ "stdout_excerpt": details.get("stdout_excerpt"),
2054
+ "stderr_excerpt": details.get("stderr_excerpt"),
2055
+ "profile": details.get("profile"),
2056
+ "permission_mode": details.get("permission_mode"),
2057
+ "variant": details.get("variant"),
2058
+ },
1607
2059
  }
2060
+ bootstrap["runner_readiness"] = runner_readiness
2061
+ if normalized_runner == "codex":
2062
+ codex_state = runner_readiness["codex"]
2063
+ bootstrap["codex_ready"] = bool(codex_state.get("ready"))
2064
+ bootstrap["codex_last_checked_at"] = codex_state.get("last_checked_at")
2065
+ bootstrap["codex_last_result"] = codex_state.get("last_result")
1608
2066
  config["bootstrap"] = bootstrap
1609
2067
  self.save_named_payload("config", config)
1610
- if bool(result.get("ok")) and bool(details.get("model_fallback_used")):
2068
+ if normalized_runner == "codex" and bool(result.get("ok")) and bool(details.get("model_fallback_used")):
1611
2069
  self._persist_codex_model_inherit(details.get("requested_model"))
1612
2070
 
2071
+ def _persist_codex_bootstrap_result(self, result: dict) -> None:
2072
+ self._persist_runner_bootstrap_result("codex", result)
2073
+
2074
+ @staticmethod
2075
+ def _copy_runner_file_if_exists(source: Path, target: Path) -> None:
2076
+ if not source.exists() or not source.is_file():
2077
+ return
2078
+ target.parent.mkdir(parents=True, exist_ok=True)
2079
+ shutil.copy2(source, target)
2080
+
2081
+ @staticmethod
2082
+ def _claude_auth_runner_env(env: dict[str, str]) -> dict[str, str]:
2083
+ resolved = dict(env)
2084
+ auth_token = str(resolved.get("ANTHROPIC_AUTH_TOKEN") or "").strip()
2085
+ api_key = str(resolved.get("ANTHROPIC_API_KEY") or "").strip()
2086
+ if auth_token and not api_key:
2087
+ resolved["ANTHROPIC_API_KEY"] = auth_token
2088
+ return resolved
2089
+
2090
+ def _runner_missing_binary_guidance(self, runner_name: str, config: dict) -> list[str]:
2091
+ normalized_runner = str(runner_name or "").strip().lower()
2092
+ binary = str(config.get("binary") or normalized_runner).strip() or normalized_runner
2093
+ if normalized_runner == "codex":
2094
+ return self._codex_missing_binary_guidance(config)
2095
+ if normalized_runner == "claude":
2096
+ return [
2097
+ f"Install Claude Code and make sure `{binary} --version` works in the current shell.",
2098
+ "If Claude Code is already installed elsewhere, set `runners.claude.binary` to the absolute path.",
2099
+ ]
2100
+ if normalized_runner == "opencode":
2101
+ return [
2102
+ f"Install OpenCode and make sure `{binary} --version` works in the current shell.",
2103
+ "If OpenCode is already installed elsewhere, set `runners.opencode.binary` to the absolute path.",
2104
+ ]
2105
+ if normalized_runner == "kimi":
2106
+ return [
2107
+ f"Install Kimi Code and make sure `{binary} --version` works in the current shell.",
2108
+ "Run `kimi login` (or just `kimi`) once to complete the first-run login flow.",
2109
+ "If Kimi Code uses a custom home, point `runners.kimi.config_dir` at the correct `~/.kimi`-style directory that contains `config.toml` and `mcp.json`.",
2110
+ ]
2111
+ return [f"Install runner `{normalized_runner}` and ensure `{binary}` is on PATH."]
2112
+
2113
+ def _probe_claude_runner(self, config: dict) -> dict:
2114
+ checked_at = utc_now()
2115
+ binary = str(config.get("binary") or "claude").strip() or "claude"
2116
+ resolved_binary = resolve_runner_binary(binary, runner_name="claude")
2117
+ requested_model = str(config.get("model") or "inherit").strip() or "inherit"
2118
+ permission_mode = str(config.get("permission_mode") or "bypassPermissions").strip() or "bypassPermissions"
2119
+ details: dict[str, object] = {
2120
+ "binary": binary,
2121
+ "resolved_binary": resolved_binary,
2122
+ "config_dir": str(config.get("config_dir") or "~/.claude"),
2123
+ "model": requested_model,
2124
+ "requested_model": requested_model,
2125
+ "effective_model": requested_model,
2126
+ "permission_mode": permission_mode,
2127
+ "checked_at": checked_at,
2128
+ }
2129
+ if not resolved_binary:
2130
+ return {
2131
+ "ok": False,
2132
+ "summary": "Claude Code startup probe failed before execution.",
2133
+ "warnings": [],
2134
+ "errors": [f"Claude Code binary `{binary}` is not available."],
2135
+ "details": details,
2136
+ "guidance": self._runner_missing_binary_guidance("claude", config),
2137
+ }
2138
+ env = ensure_utf8_subprocess_env(os.environ.copy())
2139
+ env.update(self._claude_auth_runner_env(self._codex_runner_env(config)))
2140
+ temp_home_handle = tempfile.TemporaryDirectory()
2141
+ try:
2142
+ temp_home = Path(temp_home_handle.name)
2143
+ source_home = Path(str(config.get("config_dir") or Path.home() / ".claude")).expanduser()
2144
+ for filename in (".credentials.json", "settings.json", "settings.local.json"):
2145
+ self._copy_runner_file_if_exists(source_home / filename, temp_home / filename)
2146
+ env["CLAUDE_CONFIG_DIR"] = str(temp_home)
2147
+ command = [
2148
+ resolved_binary,
2149
+ "-p",
2150
+ "--input-format",
2151
+ "text",
2152
+ "--output-format",
2153
+ "json",
2154
+ "--add-dir",
2155
+ str(repo_root()),
2156
+ "--no-session-persistence",
2157
+ "--permission-mode",
2158
+ permission_mode,
2159
+ ]
2160
+ if requested_model.lower() not in {"", "inherit", "default", "claude-default"}:
2161
+ command.extend(["--model", requested_model])
2162
+ command.extend(["--tools", ""])
2163
+ result = subprocess.run(
2164
+ command,
2165
+ input="Reply with exactly HELLO.",
2166
+ cwd=str(repo_root()),
2167
+ env=env,
2168
+ capture_output=True,
2169
+ timeout=90,
2170
+ check=False,
2171
+ **utf8_text_subprocess_kwargs(),
2172
+ )
2173
+ except subprocess.TimeoutExpired as exc:
2174
+ details.update({
2175
+ "exit_code": None,
2176
+ "stdout_excerpt": self._compact_probe_text(exc.stdout or ""),
2177
+ "stderr_excerpt": self._compact_probe_text(exc.stderr or ""),
2178
+ })
2179
+ return {
2180
+ "ok": False,
2181
+ "summary": "Claude Code startup probe timed out.",
2182
+ "warnings": [],
2183
+ "errors": ["Claude Code did not answer the startup probe within 90 seconds."],
2184
+ "details": details,
2185
+ "guidance": [
2186
+ "Run a small headless Claude Code request manually and confirm it can answer before starting DeepScientist.",
2187
+ ],
2188
+ }
2189
+ finally:
2190
+ temp_home_handle.cleanup()
2191
+ stdout_text = (result.stdout or "").strip()
2192
+ stderr_text = (result.stderr or "").strip()
2193
+ ok = result.returncode == 0 and "HELLO" in f"{stdout_text}\n{stderr_text}".upper()
2194
+ details.update({
2195
+ "exit_code": result.returncode,
2196
+ "stdout_excerpt": self._compact_probe_text(stdout_text),
2197
+ "stderr_excerpt": self._compact_probe_text(stderr_text),
2198
+ "probe_command": command,
2199
+ })
2200
+ return {
2201
+ "ok": ok,
2202
+ "summary": "Claude Code startup probe completed." if ok else "Claude Code startup probe failed.",
2203
+ "warnings": ["Claude Code returned stderr during the startup probe."] if stderr_text else [],
2204
+ "errors": [] if ok else ["Claude Code did not complete the startup hello probe successfully."],
2205
+ "details": details,
2206
+ "guidance": [] if ok else [
2207
+ "Run `claude -p \"Reply with exactly HELLO.\" --output-format json --tools \"\"` manually and confirm it returns `HELLO`.",
2208
+ "If Claude Code uses a custom account or credential path, point `runners.claude.config_dir` at the correct home.",
2209
+ ],
2210
+ }
2211
+
2212
+ def _probe_opencode_runner(self, config: dict) -> dict:
2213
+ checked_at = utc_now()
2214
+ binary = str(config.get("binary") or "opencode").strip() or "opencode"
2215
+ resolved_binary = resolve_runner_binary(binary, runner_name="opencode")
2216
+ requested_model = str(config.get("model") or "inherit").strip() or "inherit"
2217
+ variant = str(config.get("variant") or "").strip() or None
2218
+ permission_mode = str(config.get("permission_mode") or "allow").strip().lower() or "allow"
2219
+ details: dict[str, object] = {
2220
+ "binary": binary,
2221
+ "resolved_binary": resolved_binary,
2222
+ "config_dir": str(config.get("config_dir") or "~/.config/opencode"),
2223
+ "model": requested_model,
2224
+ "requested_model": requested_model,
2225
+ "effective_model": requested_model,
2226
+ "variant": variant,
2227
+ "permission_mode": permission_mode,
2228
+ "checked_at": checked_at,
2229
+ }
2230
+ if not resolved_binary:
2231
+ return {
2232
+ "ok": False,
2233
+ "summary": "OpenCode startup probe failed before execution.",
2234
+ "warnings": [],
2235
+ "errors": [f"OpenCode binary `{binary}` is not available."],
2236
+ "details": details,
2237
+ "guidance": self._runner_missing_binary_guidance("opencode", config),
2238
+ }
2239
+ env = ensure_utf8_subprocess_env(os.environ.copy())
2240
+ env.update(self._codex_runner_env(config))
2241
+ temp_home_handle = tempfile.TemporaryDirectory()
2242
+ try:
2243
+ temp_home = Path(temp_home_handle.name)
2244
+ config_root = temp_home / ".config" / "opencode"
2245
+ config_root.mkdir(parents=True, exist_ok=True)
2246
+ source_root = Path(str(config.get("config_dir") or Path.home() / ".config" / "opencode")).expanduser()
2247
+ self._copy_runner_file_if_exists(source_root / "opencode.json", config_root / "opencode.json")
2248
+ env["HOME"] = str(temp_home)
2249
+ env["XDG_CONFIG_HOME"] = str(temp_home / ".config")
2250
+ command = [
2251
+ resolved_binary,
2252
+ "run",
2253
+ "--format",
2254
+ "json",
2255
+ "--pure",
2256
+ "--dir",
2257
+ str(repo_root()),
2258
+ ]
2259
+ if requested_model.lower() not in {"", "inherit", "default", "opencode-default"}:
2260
+ command.extend(["--model", requested_model])
2261
+ if variant:
2262
+ command.extend(["--variant", variant])
2263
+ command.append("Reply with exactly HELLO")
2264
+ result = subprocess.run(
2265
+ command,
2266
+ cwd=str(repo_root()),
2267
+ env=env,
2268
+ capture_output=True,
2269
+ timeout=90,
2270
+ check=False,
2271
+ **utf8_text_subprocess_kwargs(),
2272
+ )
2273
+ except subprocess.TimeoutExpired as exc:
2274
+ details.update({
2275
+ "exit_code": None,
2276
+ "stdout_excerpt": self._compact_probe_text(exc.stdout or ""),
2277
+ "stderr_excerpt": self._compact_probe_text(exc.stderr or ""),
2278
+ })
2279
+ return {
2280
+ "ok": False,
2281
+ "summary": "OpenCode startup probe timed out.",
2282
+ "warnings": [],
2283
+ "errors": ["OpenCode did not answer the startup probe within 90 seconds."],
2284
+ "details": details,
2285
+ "guidance": [
2286
+ "Run a small `opencode run --format json` request manually and confirm it can answer before starting DeepScientist.",
2287
+ ],
2288
+ }
2289
+ finally:
2290
+ temp_home_handle.cleanup()
2291
+ stdout_text = (result.stdout or "").strip()
2292
+ stderr_text = (result.stderr or "").strip()
2293
+ ok = result.returncode == 0 and "HELLO" in f"{stdout_text}\n{stderr_text}".upper()
2294
+ details.update({
2295
+ "exit_code": result.returncode,
2296
+ "stdout_excerpt": self._compact_probe_text(stdout_text),
2297
+ "stderr_excerpt": self._compact_probe_text(stderr_text),
2298
+ "probe_command": command,
2299
+ })
2300
+ return {
2301
+ "ok": ok,
2302
+ "summary": "OpenCode startup probe completed." if ok else "OpenCode startup probe failed.",
2303
+ "warnings": ["OpenCode returned stderr during the startup probe."] if stderr_text else [],
2304
+ "errors": [] if ok else ["OpenCode did not complete the startup hello probe successfully."],
2305
+ "details": details,
2306
+ "guidance": [] if ok else [
2307
+ "Run `opencode run --format json \"Reply with exactly HELLO\"` manually and confirm it succeeds.",
2308
+ "If OpenCode uses a custom config root, point `runners.opencode.config_dir` at the correct directory.",
2309
+ ],
2310
+ }
2311
+
2312
+ def _probe_kimi_runner(self, config: dict) -> dict:
2313
+ checked_at = utc_now()
2314
+ binary = str(config.get("binary") or "kimi").strip() or "kimi"
2315
+ resolved_binary = resolve_runner_binary(binary, runner_name="kimi")
2316
+ requested_model = str(config.get("model") or "inherit").strip() or "inherit"
2317
+ agent_name = str(config.get("agent") or "").strip() or None
2318
+ yolo_enabled = bool(config.get("yolo", True))
2319
+ thinking_enabled = bool(config.get("thinking", False))
2320
+ details: dict[str, object] = {
2321
+ "binary": binary,
2322
+ "resolved_binary": resolved_binary,
2323
+ "config_dir": str(config.get("config_dir") or "~/.kimi"),
2324
+ "model": requested_model,
2325
+ "requested_model": requested_model,
2326
+ "effective_model": requested_model,
2327
+ "agent": agent_name,
2328
+ "thinking": thinking_enabled,
2329
+ "yolo": yolo_enabled,
2330
+ "checked_at": checked_at,
2331
+ }
2332
+ if not resolved_binary:
2333
+ return {
2334
+ "ok": False,
2335
+ "summary": "Kimi Code startup probe failed before execution.",
2336
+ "warnings": [],
2337
+ "errors": [f"Kimi Code binary `{binary}` is not available."],
2338
+ "details": details,
2339
+ "guidance": self._runner_missing_binary_guidance("kimi", config),
2340
+ }
2341
+ env = ensure_utf8_subprocess_env(os.environ.copy())
2342
+ env.update(self._codex_runner_env(config))
2343
+ temp_home_handle = tempfile.TemporaryDirectory()
2344
+ try:
2345
+ temp_home = Path(temp_home_handle.name)
2346
+ kimi_home = materialize_kimi_runtime_home(
2347
+ source_home=Path(str(config.get("config_dir") or Path.home() / ".kimi")).expanduser(),
2348
+ target_home=temp_home,
2349
+ )
2350
+ mcp_config_path = kimi_home / "mcp.json"
2351
+ write_json(mcp_config_path, {"mcpServers": {}})
2352
+ env["HOME"] = str(temp_home)
2353
+ env["USERPROFILE"] = str(temp_home)
2354
+ command = [
2355
+ resolved_binary,
2356
+ "--print",
2357
+ "--input-format",
2358
+ "text",
2359
+ "--output-format",
2360
+ "stream-json",
2361
+ "--work-dir",
2362
+ str(repo_root()),
2363
+ "--mcp-config-file",
2364
+ str(mcp_config_path),
2365
+ ]
2366
+ if yolo_enabled:
2367
+ command.append("--yolo")
2368
+ if requested_model.lower() not in {"", "inherit", "default", "kimi-default"}:
2369
+ command.extend(["--model", requested_model])
2370
+ if agent_name:
2371
+ command.extend(["--agent", agent_name])
2372
+ if thinking_enabled:
2373
+ command.append("--thinking")
2374
+ result = subprocess.run(
2375
+ command,
2376
+ input="Reply with exactly HELLO.",
2377
+ cwd=str(repo_root()),
2378
+ env=env,
2379
+ capture_output=True,
2380
+ timeout=90,
2381
+ check=False,
2382
+ **utf8_text_subprocess_kwargs(),
2383
+ )
2384
+ except subprocess.TimeoutExpired as exc:
2385
+ details.update(
2386
+ {
2387
+ "exit_code": None,
2388
+ "stdout_excerpt": self._compact_probe_text(exc.stdout or ""),
2389
+ "stderr_excerpt": self._compact_probe_text(exc.stderr or ""),
2390
+ }
2391
+ )
2392
+ return {
2393
+ "ok": False,
2394
+ "summary": "Kimi Code startup probe timed out.",
2395
+ "warnings": [],
2396
+ "errors": ["Kimi Code did not answer the startup probe within 90 seconds."],
2397
+ "details": details,
2398
+ "guidance": [
2399
+ "Run a small `kimi --print --output-format stream-json` request manually and confirm it can answer before starting DeepScientist.",
2400
+ ],
2401
+ }
2402
+ finally:
2403
+ temp_home_handle.cleanup()
2404
+ stdout_text = (result.stdout or "").strip()
2405
+ stderr_text = (result.stderr or "").strip()
2406
+ ok = result.returncode == 0 and "HELLO" in f"{stdout_text}\n{stderr_text}".upper()
2407
+ details.update(
2408
+ {
2409
+ "exit_code": result.returncode,
2410
+ "stdout_excerpt": self._compact_probe_text(stdout_text),
2411
+ "stderr_excerpt": self._compact_probe_text(stderr_text),
2412
+ "probe_command": command,
2413
+ }
2414
+ )
2415
+ return {
2416
+ "ok": ok,
2417
+ "summary": "Kimi Code startup probe completed." if ok else "Kimi Code startup probe failed.",
2418
+ "warnings": ["Kimi Code returned stderr during the startup probe."] if stderr_text else [],
2419
+ "errors": [] if ok else ["Kimi Code did not complete the startup hello probe successfully."],
2420
+ "details": details,
2421
+ "guidance": [] if ok else [
2422
+ "Run `kimi --print --input-format text --output-format stream-json --yolo` manually and confirm it returns `HELLO`.",
2423
+ "Run `kimi login` first if this machine has not completed the Kimi Code login flow yet.",
2424
+ "If Kimi Code uses a custom home, point `runners.kimi.config_dir` at the correct `~/.kimi`-style directory.",
2425
+ ],
2426
+ }
2427
+
1613
2428
  @staticmethod
1614
2429
  def _compact_probe_text(value: str, *, limit: int = 1200) -> str:
1615
2430
  text = str(value or "").strip()
@@ -1864,11 +2679,31 @@ Use **Test** when the file exposes runtime dependencies.
1864
2679
  defaults = default_payload(name, self.home)
1865
2680
  if name == "runners":
1866
2681
  normalized = self._deep_merge(defaults, prepared)
2682
+ runtime_enabled_runners = self._runtime_enabled_runner_names()
1867
2683
  codex = normalized.get("codex")
1868
- if isinstance(codex, dict) and self._looks_like_legacy_codex_retry_profile(codex):
1869
- codex["retry_initial_backoff_sec"] = 10.0
1870
- codex["retry_backoff_multiplier"] = 6.0
1871
- codex["retry_max_backoff_sec"] = 1800.0
2684
+ if isinstance(codex, dict):
2685
+ if self._looks_like_legacy_codex_retry_profile(codex):
2686
+ codex["retry_initial_backoff_sec"] = 10.0
2687
+ codex["retry_backoff_multiplier"] = 6.0
2688
+ codex["retry_max_backoff_sec"] = 1800.0
2689
+ if self._looks_like_preupgrade_codex_retry_attempt_profile(codex):
2690
+ codex["retry_max_attempts"] = 7
2691
+ claude = normalized.get("claude")
2692
+ if isinstance(claude, dict):
2693
+ legacy_approval_policy = str(claude.get("approval_policy") or "").strip().lower()
2694
+ if legacy_approval_policy and not str(claude.get("permission_mode") or "").strip():
2695
+ if legacy_approval_policy == "never":
2696
+ claude["permission_mode"] = "bypassPermissions"
2697
+ elif legacy_approval_policy in {"on-request", "default"}:
2698
+ claude["permission_mode"] = "default"
2699
+ if "approval_policy" in claude:
2700
+ claude.pop("approval_policy", None)
2701
+ if "sandbox_mode" in claude:
2702
+ claude.pop("sandbox_mode", None)
2703
+ for runner_name in runtime_enabled_runners:
2704
+ runner_config = normalized.get(runner_name)
2705
+ if isinstance(runner_config, dict):
2706
+ runner_config["enabled"] = True
1872
2707
  return normalized
1873
2708
  if name == "connectors":
1874
2709
  normalized = deepcopy(defaults)
@@ -1913,9 +2748,44 @@ Use **Test** when the file exposes runtime dependencies.
1913
2748
  return normalized
1914
2749
  return self._deep_merge(defaults, prepared)
1915
2750
 
2751
+ @staticmethod
2752
+ def _normalize_runtime_runner_name(value: object) -> str:
2753
+ normalized = str(value or "").strip().lower().replace("_", "-")
2754
+ if normalized in {"claude-code", "claudecode"}:
2755
+ return "claude"
2756
+ if normalized in {"kimi-code", "kimicode"}:
2757
+ return "kimi"
2758
+ if normalized in {"open-code", "open code", "opencode-ai", "opencodeai"}:
2759
+ return "opencode"
2760
+ return normalized.replace("-", "")
2761
+
2762
+ @classmethod
2763
+ def _runtime_enabled_runner_names(cls) -> set[str]:
2764
+ names: set[str] = set()
2765
+ raw_values = [
2766
+ os.environ.get("DEEPSCIENTIST_DEFAULT_RUNNER"),
2767
+ os.environ.get("DS_DEFAULT_RUNNER"),
2768
+ os.environ.get("DEEPSCIENTIST_ENABLE_RUNNER"),
2769
+ os.environ.get("DS_ENABLE_RUNNER"),
2770
+ os.environ.get("DEEPSCIENTIST_ENABLE_RUNNERS"),
2771
+ os.environ.get("DS_ENABLE_RUNNERS"),
2772
+ ]
2773
+ for raw_value in raw_values:
2774
+ for item in str(raw_value or "").replace(";", ",").split(","):
2775
+ normalized = cls._normalize_runtime_runner_name(item)
2776
+ if normalized:
2777
+ names.add(normalized)
2778
+ return names
2779
+
1916
2780
  def _normalize_config_payload(self, payload: dict) -> dict:
1917
2781
  defaults = default_payload("config", self.home)
1918
2782
  normalized = self._deep_merge(defaults, payload)
2783
+ default_runner_override = str(
2784
+ os.environ.get("DEEPSCIENTIST_DEFAULT_RUNNER") or os.environ.get("DS_DEFAULT_RUNNER") or ""
2785
+ ).strip().lower()
2786
+ default_runner_override = self._normalize_runtime_runner_name(default_runner_override)
2787
+ if default_runner_override:
2788
+ normalized["default_runner"] = default_runner_override
1919
2789
  bootstrap = normalized.get("bootstrap") if isinstance(normalized.get("bootstrap"), dict) else {}
1920
2790
  raw_bootstrap = payload.get("bootstrap") if isinstance(payload.get("bootstrap"), dict) else {}
1921
2791
  connectors = normalized.get("connectors") if isinstance(normalized.get("connectors"), dict) else {}
@@ -1942,6 +2812,25 @@ Use **Test** when the file exposes runtime dependencies.
1942
2812
  bootstrap["locale_initialized_from_browser"] = locale_initialized_from_browser
1943
2813
  bootstrap["locale_initialized_at"] = bootstrap.get("locale_initialized_at")
1944
2814
  bootstrap["locale_initialized_browser_locale"] = bootstrap.get("locale_initialized_browser_locale")
2815
+ runner_readiness = bootstrap.get("runner_readiness") if isinstance(bootstrap.get("runner_readiness"), dict) else {}
2816
+ normalized_runner_readiness: dict[str, dict[str, Any]] = {}
2817
+ for runner_name in list_builtin_runner_names():
2818
+ state = runner_readiness.get(runner_name) if isinstance(runner_readiness.get(runner_name), dict) else {}
2819
+ if runner_name == "codex" and not state:
2820
+ state = {
2821
+ "ready": bool(bootstrap.get("codex_ready", False)),
2822
+ "last_checked_at": bootstrap.get("codex_last_checked_at"),
2823
+ "last_result": bootstrap.get("codex_last_result") if isinstance(bootstrap.get("codex_last_result"), dict) else {},
2824
+ }
2825
+ normalized_runner_readiness[runner_name] = {
2826
+ "ready": bool(state.get("ready", False)),
2827
+ "last_checked_at": state.get("last_checked_at"),
2828
+ "last_result": state.get("last_result") if isinstance(state.get("last_result"), dict) else {},
2829
+ }
2830
+ bootstrap["runner_readiness"] = normalized_runner_readiness
2831
+ bootstrap["codex_ready"] = bool(normalized_runner_readiness.get("codex", {}).get("ready", False))
2832
+ bootstrap["codex_last_checked_at"] = normalized_runner_readiness.get("codex", {}).get("last_checked_at")
2833
+ bootstrap["codex_last_result"] = normalized_runner_readiness.get("codex", {}).get("last_result") if isinstance(normalized_runner_readiness.get("codex", {}).get("last_result"), dict) else {}
1945
2834
  normalized["bootstrap"] = bootstrap
1946
2835
  raw_system_enabled = raw_connectors.get("system_enabled") if isinstance(raw_connectors.get("system_enabled"), dict) else {}
1947
2836
  default_system_enabled = (
@@ -1962,6 +2851,85 @@ Use **Test** when the file exposes runtime dependencies.
1962
2851
  for name in SYSTEM_CONNECTOR_NAMES
1963
2852
  }
1964
2853
  normalized["connectors"] = connectors
2854
+ hardware = normalized.get("hardware") if isinstance(normalized.get("hardware"), dict) else {}
2855
+ raw_hardware = payload.get("hardware") if isinstance(payload.get("hardware"), dict) else {}
2856
+ gpu_selection_mode = str(raw_hardware.get("gpu_selection_mode") or hardware.get("gpu_selection_mode") or "all").strip().lower()
2857
+ if gpu_selection_mode not in {"all", "selected"}:
2858
+ gpu_selection_mode = "all"
2859
+ raw_selected_gpu_ids = raw_hardware.get("selected_gpu_ids", hardware.get("selected_gpu_ids", []))
2860
+ selected_gpu_ids: list[str] = []
2861
+ if isinstance(raw_selected_gpu_ids, list):
2862
+ for item in raw_selected_gpu_ids:
2863
+ normalized_id = str(item or "").strip()
2864
+ if normalized_id and normalized_id not in selected_gpu_ids:
2865
+ selected_gpu_ids.append(normalized_id)
2866
+ elif isinstance(raw_selected_gpu_ids, str):
2867
+ for item in raw_selected_gpu_ids.split(","):
2868
+ normalized_id = item.strip()
2869
+ if normalized_id and normalized_id not in selected_gpu_ids:
2870
+ selected_gpu_ids.append(normalized_id)
2871
+ hardware["gpu_selection_mode"] = gpu_selection_mode
2872
+ hardware["selected_gpu_ids"] = selected_gpu_ids
2873
+ hardware["include_system_hardware_in_prompt"] = self._coerce_bool(
2874
+ raw_hardware.get(
2875
+ "include_system_hardware_in_prompt",
2876
+ hardware.get("include_system_hardware_in_prompt", True),
2877
+ ),
2878
+ default=True,
2879
+ )
2880
+ normalized["hardware"] = hardware
2881
+ memory = normalized.get("memory") if isinstance(normalized.get("memory"), dict) else {}
2882
+ raw_memory = payload.get("memory") if isinstance(payload.get("memory"), dict) else {}
2883
+ read_visibility_mode = str(
2884
+ raw_memory.get("read_visibility_mode", memory.get("read_visibility_mode", "independent")) or "independent"
2885
+ ).strip().lower()
2886
+ if read_visibility_mode not in {"independent", "shared_across_quests"}:
2887
+ read_visibility_mode = "independent"
2888
+ memory["read_visibility_mode"] = read_visibility_mode
2889
+ normalized["memory"] = memory
2890
+ literature = normalized.get("literature") if isinstance(normalized.get("literature"), dict) else {}
2891
+ raw_literature = payload.get("literature") if isinstance(payload.get("literature"), dict) else {}
2892
+ default_literature = defaults.get("literature") if isinstance(defaults.get("literature"), dict) else {}
2893
+ deepxiv_defaults = default_literature.get("deepxiv") if isinstance(default_literature.get("deepxiv"), dict) else {}
2894
+ deepxiv = literature.get("deepxiv") if isinstance(literature.get("deepxiv"), dict) else {}
2895
+ raw_deepxiv = raw_literature.get("deepxiv") if isinstance(raw_literature.get("deepxiv"), dict) else {}
2896
+ deepxiv["enabled"] = self._coerce_bool(
2897
+ raw_deepxiv.get("enabled", deepxiv.get("enabled", deepxiv_defaults.get("enabled", False))),
2898
+ default=bool(deepxiv_defaults.get("enabled", False)),
2899
+ )
2900
+ deepxiv["base_url"] = str(
2901
+ raw_deepxiv.get("base_url", deepxiv.get("base_url", deepxiv_defaults.get("base_url", "https://data.rag.ac.cn"))) or ""
2902
+ ).strip() or str(deepxiv_defaults.get("base_url") or "https://data.rag.ac.cn")
2903
+ deepxiv["token"] = str(raw_deepxiv.get("token", deepxiv.get("token", "")) or "").strip() or None
2904
+ deepxiv["token_env"] = str(
2905
+ raw_deepxiv.get("token_env", deepxiv.get("token_env", deepxiv_defaults.get("token_env", "DEEPXIV_TOKEN"))) or ""
2906
+ ).strip() or None
2907
+ try:
2908
+ raw_result_size = raw_deepxiv.get(
2909
+ "default_result_size",
2910
+ deepxiv.get("default_result_size", deepxiv_defaults.get("default_result_size", 10)),
2911
+ )
2912
+ deepxiv["default_result_size"] = max(1, int(raw_result_size))
2913
+ except (TypeError, ValueError):
2914
+ deepxiv["default_result_size"] = int(deepxiv_defaults.get("default_result_size", 10) or 10)
2915
+ try:
2916
+ raw_preview_characters = raw_deepxiv.get(
2917
+ "preview_characters",
2918
+ deepxiv.get("preview_characters", deepxiv_defaults.get("preview_characters", 1200)),
2919
+ )
2920
+ deepxiv["preview_characters"] = max(200, int(raw_preview_characters))
2921
+ except (TypeError, ValueError):
2922
+ deepxiv["preview_characters"] = int(deepxiv_defaults.get("preview_characters", 1200) or 1200)
2923
+ try:
2924
+ raw_timeout = raw_deepxiv.get(
2925
+ "request_timeout_seconds",
2926
+ deepxiv.get("request_timeout_seconds", deepxiv_defaults.get("request_timeout_seconds", 20)),
2927
+ )
2928
+ deepxiv["request_timeout_seconds"] = max(3, int(raw_timeout))
2929
+ except (TypeError, ValueError):
2930
+ deepxiv["request_timeout_seconds"] = int(deepxiv_defaults.get("request_timeout_seconds", 20) or 20)
2931
+ literature["deepxiv"] = deepxiv
2932
+ normalized["literature"] = literature
1965
2933
  return normalized
1966
2934
 
1967
2935
  @staticmethod
@@ -1974,6 +2942,22 @@ Use **Test** when the file exposes runtime dependencies.
1974
2942
  return False
1975
2943
  return abs(initial - 1.0) < 1e-9 and abs(multiplier - 2.0) < 1e-9 and abs(max_backoff - 8.0) < 1e-9
1976
2944
 
2945
+ @staticmethod
2946
+ def _looks_like_preupgrade_codex_retry_attempt_profile(payload: dict) -> bool:
2947
+ try:
2948
+ max_attempts = int(payload.get("retry_max_attempts"))
2949
+ initial = float(payload.get("retry_initial_backoff_sec"))
2950
+ multiplier = float(payload.get("retry_backoff_multiplier"))
2951
+ max_backoff = float(payload.get("retry_max_backoff_sec"))
2952
+ except (TypeError, ValueError):
2953
+ return False
2954
+ return (
2955
+ max_attempts == 5
2956
+ and abs(initial - 10.0) < 1e-9
2957
+ and abs(multiplier - 6.0) < 1e-9
2958
+ and abs(max_backoff - 1800.0) < 1e-9
2959
+ )
2960
+
1977
2961
  @staticmethod
1978
2962
  def _coerce_bool(value: object, *, default: bool = False) -> bool:
1979
2963
  if value is None: