@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
@@ -22,6 +22,32 @@ _COPILOT_LEAD_MESSAGE = (
22
22
  "你可以让我读论文、改代码、看实验、整理思路,或者直接开始执行一个任务。"
23
23
  )
24
24
 
25
+ _STATIC_MIME_OVERRIDES = {
26
+ ".js": "text/javascript",
27
+ ".mjs": "text/javascript",
28
+ ".cjs": "text/javascript",
29
+ ".css": "text/css",
30
+ ".map": "application/json",
31
+ ".json": "application/json",
32
+ ".wasm": "application/wasm",
33
+ ".svg": "image/svg+xml",
34
+ ".png": "image/png",
35
+ ".jpg": "image/jpeg",
36
+ ".jpeg": "image/jpeg",
37
+ ".gif": "image/gif",
38
+ ".webp": "image/webp",
39
+ ".ico": "image/x-icon",
40
+ ".woff": "font/woff",
41
+ ".woff2": "font/woff2",
42
+ ".ttf": "font/ttf",
43
+ ".otf": "font/otf",
44
+ ".eot": "application/vnd.ms-fontobject",
45
+ ".pdf": "application/pdf",
46
+ ".zip": "application/zip",
47
+ ".txt": "text/plain; charset=utf-8",
48
+ ".md": "text/markdown; charset=utf-8",
49
+ }
50
+
25
51
 
26
52
  class ApiHandlers:
27
53
  def __init__(self, app: "DaemonApp") -> None:
@@ -51,7 +77,7 @@ class ApiHandlers:
51
77
  path = resolve_within(dist_root, ui_path)
52
78
  if not path.exists() or not path.is_file():
53
79
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
54
- mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
80
+ mime_type = self._guess_static_mime_type(path)
55
81
  return 200, self._asset_headers(mime_type), path.read_bytes()
56
82
 
57
83
  @staticmethod
@@ -68,6 +94,13 @@ class ApiHandlers:
68
94
  "Cache-Control": "no-store, max-age=0, must-revalidate",
69
95
  }
70
96
 
97
+ @staticmethod
98
+ def _guess_static_mime_type(path: Path) -> str:
99
+ extension = path.suffix.lower()
100
+ if extension in _STATIC_MIME_OVERRIDES:
101
+ return _STATIC_MIME_OVERRIDES[extension]
102
+ return mimetypes.guess_type(path.name)[0] or "application/octet-stream"
103
+
71
104
  def _ui_dist_root(self) -> Path | None:
72
105
  dist_root = self.app.repo_root / "src" / "ui" / "dist"
73
106
  if dist_root.exists() and dist_root.joinpath("index.html").exists():
@@ -283,6 +316,396 @@ npm --prefix src/ui run build</pre>
283
316
  }
284
317
  return self.app.request_shutdown(source=source)
285
318
 
319
+ def admin_doctor(self) -> dict:
320
+ cached = self.app.admin_task_service.cached_result("doctor.json")
321
+ latest_task = next(
322
+ (
323
+ item
324
+ for item in self.app.admin_task_service.list_tasks(kind="doctor", limit=10)
325
+ if str(item.get("status") or "").strip().lower() in {"queued", "running"}
326
+ ),
327
+ None,
328
+ )
329
+ return {
330
+ "ok": True,
331
+ "cached": cached,
332
+ "latest_task": latest_task,
333
+ }
334
+
335
+ def admin_overview(self) -> dict:
336
+ return self.app.admin_service.overview()
337
+
338
+ def admin_quests(self, path: str) -> dict:
339
+ query = self.parse_query(path)
340
+ limit_raw = ((query.get("limit") or ["100"])[0] or "100").strip()
341
+ try:
342
+ limit = max(1, min(int(limit_raw), 500))
343
+ except ValueError:
344
+ limit = 100
345
+ return self.app.admin_service.quests(limit=limit)
346
+
347
+ def admin_quest_summary(self, quest_id: str) -> dict | tuple[int, dict]:
348
+ try:
349
+ return self.app.admin_service.quest_summary(quest_id)
350
+ except FileNotFoundError:
351
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
352
+
353
+ def admin_runtime_sessions(self, path: str) -> dict:
354
+ query = self.parse_query(path)
355
+ limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
356
+ try:
357
+ limit = max(1, min(int(limit_raw), 1000))
358
+ except ValueError:
359
+ limit = 200
360
+ return self.app.admin_service.runtime_sessions(limit=limit)
361
+
362
+ def admin_log_sources(self) -> dict:
363
+ return self.app.admin_service.log_sources()
364
+
365
+ def admin_log_tail(self, path: str) -> dict | tuple[int, dict]:
366
+ query = self.parse_query(path)
367
+ source = ((query.get("source") or [""])[0] or "").strip()
368
+ if not source:
369
+ return 400, {"ok": False, "message": "`source` is required."}
370
+ line_count_raw = ((query.get("line_count") or ["200"])[0] or "200").strip()
371
+ try:
372
+ line_count = max(1, min(int(line_count_raw), 2000))
373
+ except ValueError:
374
+ line_count = 200
375
+ try:
376
+ return self.app.admin_service.log_tail(source, line_count=line_count)
377
+ except FileNotFoundError as exc:
378
+ return 404, {"ok": False, "message": str(exc)}
379
+
380
+ def admin_failures(self, path: str) -> dict:
381
+ query = self.parse_query(path)
382
+ limit_raw = ((query.get("limit") or ["100"])[0] or "100").strip()
383
+ try:
384
+ limit = max(1, min(int(limit_raw), 500))
385
+ except ValueError:
386
+ limit = 100
387
+ return self.app.admin_service.failures(limit=limit)
388
+
389
+ def admin_errors(self, path: str) -> dict:
390
+ query = self.parse_query(path)
391
+ limit_raw = ((query.get("limit") or ["100"])[0] or "100").strip()
392
+ try:
393
+ limit = max(1, min(int(limit_raw), 500))
394
+ except ValueError:
395
+ limit = 100
396
+ return self.app.admin_service.error_console(limit=limit)
397
+
398
+ def admin_runtime_tools(self) -> dict:
399
+ return self.app.admin_service.runtime_tools()
400
+
401
+ def admin_system_hardware(self) -> dict:
402
+ return self.app.admin_service.system_hardware(refresh=False)
403
+
404
+ def admin_system_hardware_update(self, body: dict | None = None) -> dict | tuple[int, dict]:
405
+ payload = body if isinstance(body, dict) else {}
406
+ selection_mode = payload.get("gpu_selection_mode")
407
+ selected_gpu_ids_raw = payload.get("selected_gpu_ids")
408
+ selected_gpu_ids = (
409
+ [str(item) for item in selected_gpu_ids_raw]
410
+ if isinstance(selected_gpu_ids_raw, list)
411
+ else None
412
+ )
413
+ include_in_prompt_raw = payload.get("include_system_hardware_in_prompt")
414
+ memory_read_visibility_mode = payload.get("memory_read_visibility_mode")
415
+ result = self.app.admin_service.update_system_hardware_preferences(
416
+ gpu_selection_mode=str(selection_mode) if selection_mode is not None else None,
417
+ selected_gpu_ids=selected_gpu_ids,
418
+ include_system_hardware_in_prompt=(
419
+ bool(include_in_prompt_raw) if include_in_prompt_raw is not None else None
420
+ ),
421
+ memory_read_visibility_mode=(
422
+ str(memory_read_visibility_mode) if memory_read_visibility_mode is not None else None
423
+ ),
424
+ )
425
+ self.app.admin_service.write_audit(
426
+ action="system.hardware.update",
427
+ gpu_selection_mode=((result.get("preferences") or {}) if isinstance(result.get("preferences"), dict) else {}).get("gpu_selection_mode"),
428
+ selected_gpu_ids=((result.get("preferences") or {}) if isinstance(result.get("preferences"), dict) else {}).get("selected_gpu_ids"),
429
+ effective_gpu_ids=((result.get("preferences") or {}) if isinstance(result.get("preferences"), dict) else {}).get("effective_gpu_ids"),
430
+ memory_read_visibility_mode=((result.get("memory_preferences") or {}) if isinstance(result.get("memory_preferences"), dict) else {}).get("read_visibility_mode"),
431
+ )
432
+ return result
433
+
434
+ def admin_chart_catalog(self) -> dict:
435
+ return self.app.admin_service.chart_catalog()
436
+
437
+ def admin_chart_query(self, body: dict | None = None) -> dict | tuple[int, dict]:
438
+ payload = body if isinstance(body, dict) else {}
439
+ items = payload.get("items")
440
+ if not isinstance(items, list) or not items:
441
+ return 400, {"ok": False, "message": "`items` must be a non-empty list."}
442
+ normalized_items = [dict(item) for item in items if isinstance(item, dict)]
443
+ if not normalized_items:
444
+ return 400, {"ok": False, "message": "`items` must contain query objects."}
445
+ try:
446
+ return self.app.admin_service.chart_query(normalized_items)
447
+ except FileNotFoundError as exc:
448
+ return 404, {"ok": False, "message": str(exc)}
449
+
450
+ def admin_audit(self, path: str) -> dict:
451
+ query = self.parse_query(path)
452
+ limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
453
+ try:
454
+ limit = max(1, min(int(limit_raw), 1000))
455
+ except ValueError:
456
+ limit = 200
457
+ return self.app.admin_service.audit(limit=limit)
458
+
459
+ def admin_stats_summary(self) -> dict:
460
+ return self.app.admin_service.stats_summary()
461
+
462
+ def admin_search(self, path: str) -> dict | tuple[int, dict]:
463
+ query = self.parse_query(path)
464
+ term = ((query.get("q") or [""])[0] or "").strip()
465
+ if not term:
466
+ return 400, {"ok": False, "message": "`q` is required."}
467
+ limit_raw = ((query.get("limit") or ["100"])[0] or "100").strip()
468
+ try:
469
+ limit = max(1, min(int(limit_raw), 500))
470
+ except ValueError:
471
+ limit = 100
472
+ return self.app.admin_service.search(term, limit=limit)
473
+
474
+ def admin_issue_draft(self, body: dict | None = None) -> dict:
475
+ payload = body if isinstance(body, dict) else {}
476
+ return self.app.admin_service.issue_draft(
477
+ summary=str(payload.get("summary") or "").strip() or None,
478
+ user_notes=str(payload.get("user_notes") or "").strip() or None,
479
+ include_doctor=payload.get("include_doctor") is not False,
480
+ include_logs=payload.get("include_logs") is not False,
481
+ include_system_settings=payload.get("include_system_settings") is not False,
482
+ include_system_quirks=payload.get("include_system_quirks") is True,
483
+ )
484
+
485
+ def admin_controllers(self) -> dict:
486
+ return self.app.admin_service.controllers()
487
+
488
+ def admin_controller_run(self, controller_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
489
+ _unused = body or {}
490
+ try:
491
+ result = self.app.admin_service.controller_run(controller_id)
492
+ except FileNotFoundError:
493
+ return 404, {"ok": False, "message": f"Unknown controller `{controller_id}`."}
494
+ self.app.admin_service.write_audit(
495
+ action="controller.run",
496
+ controller_id=controller_id,
497
+ result=result,
498
+ )
499
+ return {"ok": True, "controller_id": controller_id, "result": result}
500
+
501
+ def admin_controller_toggle(self, controller_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
502
+ payload = body if isinstance(body, dict) else {}
503
+ if "enabled" not in payload:
504
+ return 400, {"ok": False, "message": "`enabled` is required."}
505
+ try:
506
+ controller = self.app.admin_service.controller_toggle(controller_id, enabled=bool(payload.get("enabled")))
507
+ except StopIteration:
508
+ return 404, {"ok": False, "message": f"Unknown controller `{controller_id}`."}
509
+ self.app.admin_service.write_audit(
510
+ action="controller.toggle",
511
+ controller_id=controller_id,
512
+ enabled=bool(payload.get("enabled")),
513
+ )
514
+ return {"ok": True, "controller": controller}
515
+
516
+ def admin_repairs(self, path: str) -> dict:
517
+ query = self.parse_query(path)
518
+ limit_raw = ((query.get("limit") or ["50"])[0] or "50").strip()
519
+ try:
520
+ limit = max(1, min(int(limit_raw), 200))
521
+ except ValueError:
522
+ limit = 50
523
+ return {
524
+ "ok": True,
525
+ "items": self.app.admin_repair_service.list_repairs(limit=limit),
526
+ }
527
+
528
+ def admin_repair_create(self, body: dict | None = None) -> dict | tuple[int, dict]:
529
+ payload = body if isinstance(body, dict) else {}
530
+ request_text = str(payload.get("request_text") or payload.get("message") or "").strip()
531
+ if not request_text:
532
+ return 400, {"ok": False, "message": "`request_text` is required."}
533
+ try:
534
+ repair = self.app.admin_repair_service.create_repair(
535
+ request_text=request_text,
536
+ source_page=str(payload.get("source_page") or "").strip() or None,
537
+ scope=str(payload.get("scope") or "system").strip() or "system",
538
+ targets=dict(payload.get("targets") or {}) if isinstance(payload.get("targets"), dict) else None,
539
+ repair_policy=str(payload.get("repair_policy") or "diagnose_only").strip() or "diagnose_only",
540
+ selected_paths=[str(item) for item in payload.get("selected_paths") or []] if isinstance(payload.get("selected_paths"), list) else None,
541
+ )
542
+ except ValueError as exc:
543
+ return 400, {"ok": False, "message": str(exc)}
544
+ self.app.admin_service.write_audit(
545
+ action="repair.create",
546
+ repair_id=repair.get("repair_id"),
547
+ ops_quest_id=repair.get("ops_quest_id"),
548
+ scope=repair.get("scope"),
549
+ )
550
+ return {"ok": True, "repair": repair}
551
+
552
+ def admin_repair_detail(self, repair_id: str) -> dict | tuple[int, dict]:
553
+ try:
554
+ repair = self.app.admin_repair_service.get_repair(repair_id)
555
+ except FileNotFoundError:
556
+ return 404, {"ok": False, "message": f"Unknown repair `{repair_id}`."}
557
+ return {"ok": True, "repair": repair}
558
+
559
+ def admin_repair_close(self, repair_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
560
+ _unused = body or {}
561
+ try:
562
+ repair = self.app.admin_repair_service.close_repair(repair_id)
563
+ except FileNotFoundError:
564
+ return 404, {"ok": False, "message": f"Unknown repair `{repair_id}`."}
565
+ self.app.admin_service.write_audit(
566
+ action="repair.close",
567
+ repair_id=repair.get("repair_id"),
568
+ ops_quest_id=repair.get("ops_quest_id"),
569
+ )
570
+ return {"ok": True, "repair": repair}
571
+
572
+ def admin_tasks(self, path: str) -> dict:
573
+ query = self.parse_query(path)
574
+ kind = ((query.get("kind") or [""])[0] or "").strip() or None
575
+ limit_raw = ((query.get("limit") or ["50"])[0] or "50").strip()
576
+ try:
577
+ limit = max(1, min(int(limit_raw), 200))
578
+ except ValueError:
579
+ limit = 50
580
+ return {
581
+ "ok": True,
582
+ "items": self.app.admin_task_service.list_tasks(kind=kind, limit=limit),
583
+ }
584
+
585
+ def admin_task_doctor_start(self, body: dict | None = None) -> dict:
586
+ _unused = body or {}
587
+ task = self.app.admin_task_service.start_doctor_task()
588
+ return {"ok": True, "task": task}
589
+
590
+ def admin_task_system_update_check_start(self, body: dict | None = None) -> dict:
591
+ _unused = body or {}
592
+ task = self.app.admin_task_service.start_system_update_check_task()
593
+ return {"ok": True, "task": task}
594
+
595
+ def admin_task_system_update_action_start(self, body: dict | None = None) -> dict | tuple[int, dict]:
596
+ payload = body if isinstance(body, dict) else {}
597
+ action = str(payload.get("action") or "").strip().lower()
598
+ if not action:
599
+ return 400, {"ok": False, "message": "`action` is required."}
600
+ try:
601
+ task = self.app.admin_task_service.start_system_update_action_task(action=action)
602
+ except ValueError as exc:
603
+ return 400, {"ok": False, "message": str(exc)}
604
+ return {"ok": True, "task": task}
605
+
606
+ def admin_task_detail(self, task_id: str) -> dict | tuple[int, dict]:
607
+ try:
608
+ task = self.app.admin_task_service.get_task(task_id)
609
+ except FileNotFoundError:
610
+ return 404, {"ok": False, "message": f"Unknown admin task `{task_id}`."}
611
+ return {
612
+ "ok": True,
613
+ "task": task,
614
+ }
615
+
616
+ def system_shutdown(self, body: dict | None = None) -> dict:
617
+ return self.admin_shutdown(body if isinstance(body, dict) else {})
618
+
619
+ def system_doctor(self) -> dict:
620
+ return self.admin_doctor()
621
+
622
+ def system_overview(self) -> dict:
623
+ return self.admin_overview()
624
+
625
+ def system_quests(self, path: str) -> dict:
626
+ return self.admin_quests(path)
627
+
628
+ def system_quest_summary(self, quest_id: str) -> dict | tuple[int, dict]:
629
+ return self.admin_quest_summary(quest_id)
630
+
631
+ def system_runtime_sessions(self, path: str) -> dict:
632
+ return self.admin_runtime_sessions(path)
633
+
634
+ def system_log_sources(self) -> dict:
635
+ return self.admin_log_sources()
636
+
637
+ def system_log_tail(self, path: str) -> dict | tuple[int, dict]:
638
+ return self.admin_log_tail(path)
639
+
640
+ def system_failures(self, path: str) -> dict:
641
+ return self.admin_failures(path)
642
+
643
+ def system_errors(self, path: str) -> dict:
644
+ return self.admin_errors(path)
645
+
646
+ def system_runtime_tools(self) -> dict:
647
+ return self.admin_runtime_tools()
648
+
649
+ def system_hardware(self) -> dict:
650
+ return self.admin_system_hardware()
651
+
652
+ def system_hardware_update(self, body: dict | None = None) -> dict | tuple[int, dict]:
653
+ return self.admin_system_hardware_update(body)
654
+
655
+ def system_chart_catalog(self) -> dict:
656
+ return self.admin_chart_catalog()
657
+
658
+ def system_chart_query(self, body: dict | None = None) -> dict | tuple[int, dict]:
659
+ return self.admin_chart_query(body)
660
+
661
+ def system_audit(self, path: str) -> dict:
662
+ return self.admin_audit(path)
663
+
664
+ def system_stats_summary(self) -> dict:
665
+ return self.admin_stats_summary()
666
+
667
+ def system_search(self, path: str) -> dict | tuple[int, dict]:
668
+ return self.admin_search(path)
669
+
670
+ def system_issue_draft(self, body: dict | None = None) -> dict:
671
+ return self.admin_issue_draft(body)
672
+
673
+ def system_controllers(self) -> dict:
674
+ return self.admin_controllers()
675
+
676
+ def system_controller_run(self, controller_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
677
+ return self.admin_controller_run(controller_id, body)
678
+
679
+ def system_controller_toggle(self, controller_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
680
+ return self.admin_controller_toggle(controller_id, body)
681
+
682
+ def system_repairs(self, path: str) -> dict:
683
+ return self.admin_repairs(path)
684
+
685
+ def system_repair_create(self, body: dict | None = None) -> dict | tuple[int, dict]:
686
+ return self.admin_repair_create(body)
687
+
688
+ def system_repair_detail(self, repair_id: str) -> dict | tuple[int, dict]:
689
+ return self.admin_repair_detail(repair_id)
690
+
691
+ def system_repair_close(self, repair_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
692
+ return self.admin_repair_close(repair_id, body)
693
+
694
+ def system_tasks(self, path: str) -> dict:
695
+ return self.admin_tasks(path)
696
+
697
+ def system_task_doctor_start(self, body: dict | None = None) -> dict:
698
+ return self.admin_task_doctor_start(body)
699
+
700
+ def system_task_system_update_check_start(self, body: dict | None = None) -> dict:
701
+ return self.admin_task_system_update_check_start(body)
702
+
703
+ def system_task_system_update_action_start(self, body: dict | None = None) -> dict | tuple[int, dict]:
704
+ return self.admin_task_system_update_action_start(body)
705
+
706
+ def system_task_detail(self, task_id: str) -> dict | tuple[int, dict]:
707
+ return self.admin_task_detail(task_id)
708
+
286
709
  def acp_status(self) -> dict:
287
710
  return get_acp_bridge_status().as_dict()
288
711
 
@@ -317,6 +740,108 @@ npm --prefix src/ui run build</pre>
317
740
  except ValueError as exc:
318
741
  return 400, {"ok": False, "message": str(exc), "baseline_id": baseline_id}
319
742
 
743
+ def benchstore_entries(self, path: str = "") -> dict:
744
+ try:
745
+ hardware = self.app.admin_service.system_hardware(refresh=False)
746
+ except Exception:
747
+ hardware = {}
748
+ return self.app.benchstore_service.list_entries(hardware_payload=hardware, locale=self._locale_from_path(path))
749
+
750
+ def benchstore_entry(self, entry_id: str, path: str = "") -> dict | tuple[int, dict]:
751
+ try:
752
+ hardware = self.app.admin_service.system_hardware(refresh=False)
753
+ except Exception:
754
+ hardware = {}
755
+ try:
756
+ return self.app.benchstore_service.get_entry(entry_id, hardware_payload=hardware, locale=self._locale_from_path(path))
757
+ except FileNotFoundError as exc:
758
+ return 404, {"ok": False, "message": str(exc), "entry_id": entry_id}
759
+
760
+ def benchstore_entry_image(self, entry_id: str, path: str = "") -> tuple[int, dict, bytes]:
761
+ try:
762
+ path = self.app.benchstore_service.entry_image_asset_path(entry_id, locale=self._locale_from_path(path))
763
+ except FileNotFoundError as exc:
764
+ return 404, {"Content-Type": "text/plain; charset=utf-8"}, str(exc).encode("utf-8")
765
+ mime_type = self._guess_static_mime_type(path)
766
+ return 200, self._asset_headers(mime_type), path.read_bytes()
767
+
768
+ def benchstore_entry_install(self, entry_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
769
+ _unused = body or {}
770
+ try:
771
+ task = self.app.start_benchstore_install(entry_id)
772
+ except FileNotFoundError as exc:
773
+ return 404, {"ok": False, "message": str(exc), "entry_id": entry_id}
774
+ except ValueError as exc:
775
+ return 400, {"ok": False, "message": str(exc), "entry_id": entry_id}
776
+ return {"ok": True, "entry_id": entry_id, "task": task}
777
+
778
+ def benchstore_entry_setup_packet(self, entry_id: str, path: str = "") -> dict | tuple[int, dict]:
779
+ try:
780
+ hardware = self.app.admin_service.system_hardware(refresh=False)
781
+ except Exception:
782
+ hardware = {}
783
+ try:
784
+ setup_packet = self.app.benchstore_service.build_setup_packet(
785
+ entry_id=entry_id,
786
+ hardware_payload=hardware,
787
+ locale=self._locale_from_path(path, default="zh"),
788
+ )
789
+ except FileNotFoundError as exc:
790
+ return 404, {"ok": False, "message": str(exc), "entry_id": entry_id}
791
+ except ValueError as exc:
792
+ return 409, {"ok": False, "message": str(exc), "entry_id": entry_id}
793
+ return {"ok": True, "entry_id": entry_id, "setup_packet": setup_packet}
794
+
795
+ def benchstore_entry_launch(self, entry_id: str, path: str = "", body: dict | None = None) -> dict | tuple[int, dict]:
796
+ _unused = body or {}
797
+ try:
798
+ hardware = self.app.admin_service.system_hardware(refresh=False)
799
+ except Exception:
800
+ hardware = {}
801
+ try:
802
+ setup_packet = self.app.benchstore_service.build_setup_packet(
803
+ entry_id=entry_id,
804
+ hardware_payload=hardware,
805
+ locale=self._locale_from_path(path, default="zh"),
806
+ )
807
+ except FileNotFoundError as exc:
808
+ return 404, {"ok": False, "message": str(exc), "entry_id": entry_id}
809
+ except ValueError as exc:
810
+ return 409, {"ok": False, "message": str(exc), "entry_id": entry_id}
811
+
812
+ launch_payload = setup_packet.get("launch_payload") if isinstance(setup_packet.get("launch_payload"), dict) else {}
813
+ goal = str(launch_payload.get("goal") or "").strip()
814
+ title = str(launch_payload.get("title") or "").strip() or None
815
+ initial_message = str(launch_payload.get("initial_message") or goal).strip() or goal
816
+ startup_contract = launch_payload.get("startup_contract") if isinstance(launch_payload.get("startup_contract"), dict) else None
817
+ if not goal:
818
+ return 400, {"ok": False, "message": "BenchStore launch payload is missing `goal`.", "entry_id": entry_id}
819
+
820
+ snapshot = self.app.create_quest(
821
+ goal=goal,
822
+ title=title,
823
+ quest_id=f"B-{self.app.quest_service.preview_next_numeric_quest_id()}",
824
+ source="benchstore",
825
+ startup_contract=startup_contract,
826
+ auto_bind_latest_connectors=False,
827
+ )
828
+ workspace_mode = str((startup_contract or {}).get("workspace_mode") or "").strip().lower()
829
+ if workspace_mode in {"copilot", "autonomous"}:
830
+ quest_root = self.app.quest_service._quest_root(snapshot["quest_id"])
831
+ self.app.quest_service.update_research_state(quest_root, workspace_mode=workspace_mode)
832
+ startup = self.app.submit_user_message(
833
+ snapshot["quest_id"],
834
+ text=initial_message,
835
+ source="benchstore",
836
+ )
837
+ return {
838
+ "ok": True,
839
+ "entry_id": entry_id,
840
+ "setup_packet": setup_packet,
841
+ "startup": startup,
842
+ "snapshot": self.app.quest_service.snapshot(snapshot["quest_id"]),
843
+ }
844
+
320
845
  def qq_bindings(self) -> list[dict]:
321
846
  return self.app.list_qq_bindings()
322
847
 
@@ -332,8 +857,13 @@ npm --prefix src/ui run build</pre>
332
857
  def connector_inbound(self, connector: str, body: dict) -> dict:
333
858
  return self.app.handle_connector_inbound(connector, body)
334
859
 
335
- def quests(self) -> list[dict]:
336
- return self.app.quest_service.list_quests()
860
+ def quests(self, path: str | None = None) -> list[dict]:
861
+ hidden_prefixes = ('b-', 's-')
862
+ return [
863
+ item
864
+ for item in self.app.quest_service.list_quests()
865
+ if not str(item.get('quest_id') or '').strip().lower().startswith(hidden_prefixes)
866
+ ]
337
867
 
338
868
  def quest_next_id(self) -> dict:
339
869
  return {
@@ -523,7 +1053,10 @@ npm --prefix src/ui run build</pre>
523
1053
  }
524
1054
 
525
1055
  def quest(self, quest_id: str) -> dict:
526
- return self.app.quest_service.snapshot(quest_id)
1056
+ try:
1057
+ return self.app.quest_service.snapshot(quest_id)
1058
+ except FileNotFoundError:
1059
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
527
1060
 
528
1061
  def quest_delete(self, quest_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
529
1062
  source = "web"
@@ -536,18 +1069,42 @@ npm --prefix src/ui run build</pre>
536
1069
  "title": body.get("title") if "title" in body else None,
537
1070
  "active_anchor": body.get("active_anchor") if "active_anchor" in body else None,
538
1071
  "default_runner": body.get("default_runner") if "default_runner" in body else None,
1072
+ "workspace_mode": body.get("workspace_mode") if "workspace_mode" in body else None,
1073
+ "decision_policy": body.get("decision_policy") if "decision_policy" in body else None,
539
1074
  }
540
1075
  if all(value is None for value in updates.values()):
541
1076
  return {
542
1077
  "ok": True,
543
1078
  "snapshot": self.app.quest_service.snapshot(quest_id),
544
1079
  }
1080
+ previous_snapshot = self.app.quest_service.snapshot(quest_id) if updates.get("workspace_mode") or updates.get("decision_policy") else None
545
1081
  try:
546
1082
  snapshot = self.app.quest_service.update_settings(quest_id, **updates)
547
1083
  except FileNotFoundError:
548
1084
  return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
549
1085
  except ValueError as exc:
550
1086
  return 400, {"ok": False, "message": str(exc)}
1087
+ # When switching from copilot to autonomous, continue safely without
1088
+ # replaying stale history-only user messages.
1089
+ if previous_snapshot is not None and (updates.get("workspace_mode") or updates.get("decision_policy")):
1090
+ prev_policy = str(previous_snapshot.get("continuation_policy") or "").strip().lower()
1091
+ new_policy = str(snapshot.get("continuation_policy") or "").strip().lower()
1092
+ prev_status = str(previous_snapshot.get("status") or previous_snapshot.get("runtime_status") or "").strip().lower()
1093
+ if (
1094
+ prev_policy == "wait_for_user_or_resume"
1095
+ and new_policy == "auto"
1096
+ and prev_status not in {"completed", "paused", "stopped", "error"}
1097
+ and not str(previous_snapshot.get("active_run_id") or "").strip()
1098
+ ):
1099
+ if int(snapshot.get("pending_user_message_count") or 0) > 0:
1100
+ self.app.schedule_turn(quest_id, reason="queued_user_messages")
1101
+ else:
1102
+ self.app.submit_user_message(
1103
+ quest_id,
1104
+ text="Continue",
1105
+ source="local",
1106
+ )
1107
+ snapshot = self.app.quest_service.snapshot(quest_id)
551
1108
  return {
552
1109
  "ok": True,
553
1110
  "snapshot": snapshot,
@@ -585,7 +1142,10 @@ npm --prefix src/ui run build</pre>
585
1142
  }
586
1143
 
587
1144
  def quest_session(self, quest_id: str) -> dict:
588
- snapshot = self.app.quest_service.snapshot_fast(quest_id)
1145
+ try:
1146
+ snapshot = self.app.quest_service.snapshot_fast(quest_id)
1147
+ except FileNotFoundError:
1148
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
589
1149
  for kind in ("details", "canvas", "git_canvas"):
590
1150
  try:
591
1151
  self.app.quest_service.prime_projection(quest_id, kind)
@@ -616,6 +1176,10 @@ npm --prefix src/ui run build</pre>
616
1176
  limit=limit,
617
1177
  tail=tail,
618
1178
  )
1179
+ payload["events"] = [
1180
+ self.app.quest_service.enrich_conversation_message_event(quest_id, event)
1181
+ for event in payload["events"]
1182
+ ]
619
1183
  if format_name in {"acp", "both"}:
620
1184
  payload["acp_updates"] = [
621
1185
  build_session_update(
@@ -885,6 +1449,26 @@ npm --prefix src/ui run build</pre>
885
1449
  return {"ok": False, "message": "Quest control action must be `pause`, `stop` or `resume`."}
886
1450
  return self.app.control_quest(quest_id, action=action, source=source)
887
1451
 
1452
+ def quest_message_read_now(self, quest_id: str, body: dict) -> dict:
1453
+ source = str(body.get("source") or "local-ui").strip() or "local-ui"
1454
+ message_id = str(body.get("message_id") or "").strip() or None
1455
+ client_message_id = str(body.get("client_message_id") or "").strip() or None
1456
+ return self.app.read_queued_user_messages_now(
1457
+ quest_id,
1458
+ message_id=message_id,
1459
+ client_message_id=client_message_id,
1460
+ source=source,
1461
+ )
1462
+
1463
+ def quest_message_withdraw(self, quest_id: str, body: dict) -> dict:
1464
+ source = str(body.get("source") or "local-ui").strip() or "local-ui"
1465
+ message_id = str(body.get("message_id") or "").strip() or None
1466
+ return self.app.withdraw_queued_user_message(
1467
+ quest_id,
1468
+ message_id=message_id or "",
1469
+ source=source,
1470
+ )
1471
+
888
1472
  def workflow(self, quest_id: str) -> dict:
889
1473
  payload = self.app.quest_service.workflow(quest_id)
890
1474
  projection_state = str(((payload or {}).get("projection_status") or {}).get("state") or "").strip().lower()
@@ -952,194 +1536,7 @@ npm --prefix src/ui run build</pre>
952
1536
 
953
1537
  def git_branches(self, quest_id: str) -> dict:
954
1538
  quest_root = self._fresh_quest_service()._quest_root(quest_id)
955
- payload = self.app.quest_service.git_branch_canvas(quest_id)
956
- research_state = self.app.quest_service.read_research_state(quest_root)
957
- active_workspace_branch = str(research_state.get("current_workspace_branch") or "").strip() or None
958
- research_head_branch = str(research_state.get("research_head_branch") or "").strip() or None
959
- payload["active_workspace_ref"] = active_workspace_branch
960
- payload["research_head_ref"] = research_head_branch
961
- payload["workspace_mode"] = str(research_state.get("workspace_mode") or "quest").strip() or "quest"
962
- projection_state = str(((payload or {}).get("projection_status") or {}).get("state") or "").strip().lower()
963
- if projection_state and projection_state != "ready" and not (payload.get("nodes") or []):
964
- return payload
965
- quest_data = self.app.quest_service.read_quest_yaml(quest_root)
966
- active_anchor = str(quest_data.get("active_anchor") or "").strip().lower()
967
- active_analysis_campaign_id = str(research_state.get("active_analysis_campaign_id") or "").strip() or None
968
- current_workspace_branch = str(research_state.get("current_workspace_branch") or "").strip() or None
969
- workspace_mode = str(research_state.get("workspace_mode") or "").strip().lower() or "quest"
970
- paper_parent_branch = str(research_state.get("paper_parent_branch") or "").strip() or None
971
- paper_parent_run_id = str(research_state.get("paper_parent_run_id") or "").strip() or None
972
- next_pending_slice_id = str(research_state.get("next_pending_slice_id") or "").strip() or None
973
- try:
974
- branch_summary = self.app.artifact_service.list_research_branches(quest_root)
975
- except Exception:
976
- branch_summary = {"branches": []}
977
- try:
978
- optimization_frontier = self.app.artifact_service.get_optimization_frontier(quest_root)
979
- except Exception:
980
- optimization_frontier = {"ok": False}
981
- branch_summary_by_name = {
982
- str(item.get("branch_name") or "").strip(): item
983
- for item in (branch_summary.get("branches") or [])
984
- if str(item.get("branch_name") or "").strip()
985
- }
986
- frontier_payload = (
987
- dict(optimization_frontier.get("optimization_frontier") or {})
988
- if isinstance(optimization_frontier, dict)
989
- and isinstance(optimization_frontier.get("optimization_frontier"), dict)
990
- else {}
991
- )
992
- best_branch_name = str(((frontier_payload.get("best_branch") or {}) if isinstance(frontier_payload.get("best_branch"), dict) else {}).get("branch_name") or "").strip() or None
993
- stagnant_branch_names = {
994
- str(item.get("branch_name") or "").strip()
995
- for item in (frontier_payload.get("stagnant_branches") or [])
996
- if isinstance(item, dict) and str(item.get("branch_name") or "").strip()
997
- }
998
- fusion_candidate_names = {
999
- str(item.get("branch_name") or "").strip()
1000
- for item in (frontier_payload.get("fusion_candidates") or [])
1001
- if isinstance(item, dict) and str(item.get("branch_name") or "").strip()
1002
- }
1003
- candidate_count_by_branch: dict[str, int] = {}
1004
- for item in frontier_payload.get("implementation_candidates") or []:
1005
- if not isinstance(item, dict):
1006
- continue
1007
- branch_name = str(item.get("branch") or "").strip()
1008
- if not branch_name:
1009
- continue
1010
- candidate_count_by_branch[branch_name] = candidate_count_by_branch.get(branch_name, 0) + 1
1011
- active_campaign = {}
1012
- if active_analysis_campaign_id:
1013
- try:
1014
- active_campaign = self.app.artifact_service.get_analysis_campaign(
1015
- quest_root,
1016
- campaign_id=active_analysis_campaign_id,
1017
- )
1018
- except Exception:
1019
- active_campaign = {}
1020
- campaign_parent_branch = (
1021
- str(active_campaign.get("parent_branch") or "").strip() or None
1022
- if isinstance(active_campaign, dict)
1023
- else None
1024
- )
1025
- campaign_paper_line_branch = (
1026
- str(active_campaign.get("paper_line_branch") or "").strip() or None
1027
- if isinstance(active_campaign, dict)
1028
- else None
1029
- )
1030
- campaign_slices = [
1031
- dict(item)
1032
- for item in ((active_campaign or {}).get("slices") or [])
1033
- if isinstance(item, dict)
1034
- ]
1035
- campaign_total_slices = len(campaign_slices)
1036
- campaign_completed_slices = sum(
1037
- 1 for item in campaign_slices if str(item.get("status") or "").strip().lower() == "completed"
1038
- )
1039
- slice_by_branch = {
1040
- str(item.get("branch") or "").strip(): item
1041
- for item in campaign_slices
1042
- if str(item.get("branch") or "").strip()
1043
- }
1044
-
1045
- def resolve_workflow_state(ref: str, summary: dict[str, object] | None) -> dict[str, object]:
1046
- branch_kind = self.app.artifact_service._branch_kind_from_name(ref)
1047
- has_main_result = bool((summary or {}).get("has_main_result"))
1048
- workflow_state: dict[str, object] = {
1049
- "analysis_state": "none",
1050
- "writing_state": "not_ready",
1051
- "analysis_campaign_id": active_analysis_campaign_id,
1052
- "total_slices": campaign_total_slices or None,
1053
- "completed_slices": campaign_completed_slices or None,
1054
- "next_pending_slice_id": next_pending_slice_id,
1055
- "paper_parent_branch": paper_parent_branch,
1056
- "paper_parent_run_id": paper_parent_run_id,
1057
- "status_reason": None,
1058
- }
1059
- if branch_kind == "analysis":
1060
- slice_entry = slice_by_branch.get(ref)
1061
- slice_status = str((slice_entry or {}).get("status") or "pending").strip().lower() or "pending"
1062
- if slice_status == "completed":
1063
- workflow_state["analysis_state"] = "completed"
1064
- workflow_state["status_reason"] = "Analysis slice completed."
1065
- elif ref == current_workspace_branch or str((slice_entry or {}).get("slice_id") or "").strip() == next_pending_slice_id:
1066
- workflow_state["analysis_state"] = "active"
1067
- workflow_state["status_reason"] = (
1068
- f"Analysis {campaign_completed_slices}/{campaign_total_slices} done"
1069
- if campaign_total_slices
1070
- else "Analysis slice active."
1071
- )
1072
- else:
1073
- workflow_state["analysis_state"] = "pending"
1074
- workflow_state["status_reason"] = "Analysis slice pending."
1075
- return workflow_state
1076
- if branch_kind == "paper":
1077
- if campaign_paper_line_branch and ref == campaign_paper_line_branch and next_pending_slice_id is not None:
1078
- workflow_state["analysis_state"] = "active"
1079
- workflow_state["writing_state"] = "blocked_by_analysis"
1080
- workflow_state["status_reason"] = (
1081
- f"Analysis {campaign_completed_slices}/{campaign_total_slices} done"
1082
- + (f" · next: {next_pending_slice_id}" if next_pending_slice_id else "")
1083
- )
1084
- return workflow_state
1085
- if ref == current_workspace_branch and workspace_mode == "paper":
1086
- workflow_state["writing_state"] = "completed" if active_anchor == "finalize" else "active"
1087
- workflow_state["status_reason"] = (
1088
- "Writing finalized." if active_anchor == "finalize" else "Writing workspace active."
1089
- )
1090
- else:
1091
- workflow_state["writing_state"] = "ready"
1092
- workflow_state["status_reason"] = "Writing workspace prepared."
1093
- return workflow_state
1094
- if campaign_parent_branch and not campaign_paper_line_branch and ref == campaign_parent_branch:
1095
- workflow_state["analysis_state"] = "completed" if next_pending_slice_id is None else "active"
1096
- if has_main_result:
1097
- workflow_state["writing_state"] = "ready" if next_pending_slice_id is None else "blocked_by_analysis"
1098
- workflow_state["status_reason"] = (
1099
- "Analysis complete. Ready for writing."
1100
- if next_pending_slice_id is None
1101
- else (
1102
- f"Analysis {campaign_completed_slices}/{campaign_total_slices} done"
1103
- + (f" · next: {next_pending_slice_id}" if next_pending_slice_id else "")
1104
- )
1105
- )
1106
- return workflow_state
1107
- if has_main_result:
1108
- workflow_state["writing_state"] = "ready"
1109
- workflow_state["status_reason"] = "Main experiment recorded. Ready for writing."
1110
- return workflow_state
1111
- workflow_state["status_reason"] = "Awaiting main experiment result."
1112
- return workflow_state
1113
-
1114
- for node in payload.get("nodes", []):
1115
- ref = str(node.get("ref") or "").strip()
1116
- if not ref:
1117
- continue
1118
- summary = branch_summary_by_name.get(ref)
1119
- node["active_workspace"] = ref == active_workspace_branch
1120
- node["research_head"] = ref == research_head_branch
1121
- node["workflow_state"] = resolve_workflow_state(ref, summary if isinstance(summary, dict) else None)
1122
- if not isinstance(summary, dict):
1123
- continue
1124
- node["branch_no"] = summary.get("branch_no")
1125
- node["idea_title"] = summary.get("idea_title")
1126
- node["idea_problem"] = summary.get("idea_problem")
1127
- node["next_target"] = summary.get("next_target")
1128
- node["lineage_intent"] = summary.get("lineage_intent")
1129
- node["parent_branch"] = summary.get("parent_branch")
1130
- node["foundation_ref"] = summary.get("foundation_ref")
1131
- node["foundation_reason"] = summary.get("foundation_reason")
1132
- node["idea_md_path"] = summary.get("idea_md_path")
1133
- node["idea_draft_path"] = summary.get("idea_draft_path")
1134
- node["latest_main_experiment"] = summary.get("latest_main_experiment")
1135
- node["experiment_count"] = summary.get("experiment_count")
1136
- node["has_main_result"] = summary.get("has_main_result")
1137
- node["optimization_mode"] = frontier_payload.get("mode")
1138
- node["optimization_best"] = ref == best_branch_name
1139
- node["optimization_stagnant"] = ref in stagnant_branch_names
1140
- node["optimization_fusion_candidate"] = ref in fusion_candidate_names
1141
- node["optimization_candidate_count"] = candidate_count_by_branch.get(ref, 0)
1142
- return payload
1539
+ return self.app.artifact_service.get_research_canvas(quest_root)
1143
1540
 
1144
1541
  def git_canvas(self, quest_id: str) -> dict:
1145
1542
  quest_root = self._fresh_quest_service()._quest_root(quest_id)
@@ -1341,7 +1738,7 @@ npm --prefix src/ui run build</pre>
1341
1738
  if not path.exists() or not path.is_file():
1342
1739
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
1343
1740
 
1344
- mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
1741
+ mime_type = self._guess_static_mime_type(path)
1345
1742
  return 200, {"Content-Type": mime_type}, path.read_bytes()
1346
1743
 
1347
1744
  def runs(self, quest_id: str) -> list[dict]:
@@ -1468,25 +1865,35 @@ npm --prefix src/ui run build</pre>
1468
1865
 
1469
1866
  def quest_memory(self, quest_id: str) -> list[dict]:
1470
1867
  quest_service = self._fresh_quest_service()
1471
- return self._fresh_memory_service().list_cards(
1472
- scope="quest",
1473
- quest_root=quest_service._quest_root(quest_id),
1868
+ quest_root = quest_service._require_initialized_quest_root(quest_id)
1869
+ memory_service = self._fresh_memory_service()
1870
+ return memory_service.list_visible_quest_cards(
1871
+ active_quest_root=quest_root,
1872
+ active_quest_id=quest_id,
1873
+ limit=30,
1874
+ include_shared=memory_service.shared_read_enabled(),
1474
1875
  )
1475
1876
 
1476
1877
  def documents(self, quest_id: str) -> list[dict]:
1477
- return self.app.quest_service.list_documents(quest_id)
1878
+ try:
1879
+ return self.app.quest_service.list_documents(quest_id)
1880
+ except FileNotFoundError:
1881
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1478
1882
 
1479
1883
  def explorer(self, quest_id: str, path: str) -> dict:
1480
1884
  query = self.parse_query(path)
1481
1885
  revision = ((query.get("revision") or [""])[0] or "").strip() or None
1482
1886
  mode = ((query.get("mode") or [""])[0] or "").strip() or None
1483
1887
  profile = ((query.get("profile") or [""])[0] or "").strip() or None
1484
- return self.app.quest_service.explorer(
1485
- quest_id,
1486
- revision=revision,
1487
- mode=mode,
1488
- profile=profile,
1489
- )
1888
+ try:
1889
+ return self.app.quest_service.explorer(
1890
+ quest_id,
1891
+ revision=revision,
1892
+ mode=mode,
1893
+ profile=profile,
1894
+ )
1895
+ except FileNotFoundError:
1896
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1490
1897
 
1491
1898
  def quest_search(self, quest_id: str, path: str) -> dict:
1492
1899
  query = self.parse_query(path)
@@ -1495,7 +1902,90 @@ npm --prefix src/ui run build</pre>
1495
1902
  limit = int(((query.get("limit") or ["50"])[0] or "50").strip())
1496
1903
  except ValueError:
1497
1904
  limit = 50
1498
- return self.app.quest_service.search_files(quest_id, term=term, limit=limit)
1905
+ try:
1906
+ return self.app.quest_service.search_files(quest_id, term=term, limit=limit)
1907
+ except FileNotFoundError:
1908
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1909
+
1910
+ def quest_file_create_folder(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1911
+ try:
1912
+ return self._fresh_quest_service().create_workspace_folder(
1913
+ quest_id,
1914
+ name=body.get("name"),
1915
+ parent_path=body.get("parent_path"),
1916
+ )
1917
+ except FileNotFoundError as exc:
1918
+ return 404, {"ok": False, "message": str(exc)}
1919
+ except FileExistsError as exc:
1920
+ return 409, {"ok": False, "message": str(exc)}
1921
+ except ValueError as exc:
1922
+ return 400, {"ok": False, "message": str(exc)}
1923
+
1924
+ def quest_file_upload(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1925
+ file_name = str(body.get("file_name") or "").strip()
1926
+ content_base64 = str(body.get("content_base64") or "").strip()
1927
+ mime_type = str(body.get("mime_type") or "").strip() or None
1928
+ if not file_name:
1929
+ return 400, {"ok": False, "message": "`file_name` is required."}
1930
+ if not content_base64:
1931
+ return 400, {"ok": False, "message": "`content_base64` is required."}
1932
+ try:
1933
+ content = base64.b64decode(content_base64, validate=True)
1934
+ except (ValueError, TypeError):
1935
+ return 400, {"ok": False, "message": "Invalid `content_base64` payload."}
1936
+ try:
1937
+ return self._fresh_quest_service().upload_workspace_file(
1938
+ quest_id,
1939
+ file_name=file_name,
1940
+ content=content,
1941
+ mime_type=mime_type,
1942
+ parent_path=body.get("parent_path"),
1943
+ )
1944
+ except FileNotFoundError as exc:
1945
+ return 404, {"ok": False, "message": str(exc)}
1946
+ except FileExistsError as exc:
1947
+ return 409, {"ok": False, "message": str(exc)}
1948
+ except ValueError as exc:
1949
+ return 400, {"ok": False, "message": str(exc)}
1950
+
1951
+ def quest_file_rename(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1952
+ try:
1953
+ return self._fresh_quest_service().rename_workspace_entry(
1954
+ quest_id,
1955
+ path=body.get("path"),
1956
+ new_name=body.get("new_name"),
1957
+ )
1958
+ except FileNotFoundError as exc:
1959
+ return 404, {"ok": False, "message": str(exc)}
1960
+ except FileExistsError as exc:
1961
+ return 409, {"ok": False, "message": str(exc)}
1962
+ except ValueError as exc:
1963
+ return 400, {"ok": False, "message": str(exc)}
1964
+
1965
+ def quest_file_move(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1966
+ try:
1967
+ return self._fresh_quest_service().move_workspace_entries(
1968
+ quest_id,
1969
+ paths=body.get("paths"),
1970
+ target_parent_path=body.get("target_parent_path"),
1971
+ )
1972
+ except FileNotFoundError as exc:
1973
+ return 404, {"ok": False, "message": str(exc)}
1974
+ except FileExistsError as exc:
1975
+ return 409, {"ok": False, "message": str(exc)}
1976
+ except ValueError as exc:
1977
+ return 400, {"ok": False, "message": str(exc)}
1978
+
1979
+ def quest_file_delete(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1980
+ try:
1981
+ return self._fresh_quest_service().delete_workspace_entries(
1982
+ quest_id,
1983
+ paths=body.get("paths"),
1984
+ )
1985
+ except FileNotFoundError as exc:
1986
+ return 404, {"ok": False, "message": str(exc)}
1987
+ except ValueError as exc:
1988
+ return 400, {"ok": False, "message": str(exc)}
1499
1989
 
1500
1990
  def document_asset(self, quest_id: str, path: str) -> tuple[int, dict, bytes]:
1501
1991
  quest_service = self._fresh_quest_service()
@@ -1509,17 +1999,20 @@ npm --prefix src/ui run build</pre>
1509
1999
  if not quest_service._git_revision_exists(quest_root, revision):
1510
2000
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
1511
2001
  file_path = Path(relative)
1512
- mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream"
2002
+ mime_type = self._guess_static_mime_type(file_path)
1513
2003
  content = quest_service._read_git_bytes(quest_root, revision, relative)
1514
2004
  return 200, self._asset_headers(mime_type), content
1515
2005
  path, _writable, _scope, _source_kind = quest_service.resolve_document(quest_id, document_id)
1516
2006
  if not path.exists() or not path.is_file():
1517
2007
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
1518
- mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
2008
+ mime_type = self._guess_static_mime_type(path)
1519
2009
  return 200, self._asset_headers(mime_type), path.read_bytes()
1520
2010
 
1521
2011
  def document_open(self, quest_id: str, body: dict) -> dict:
1522
- return self._fresh_quest_service().open_document(quest_id, body["document_id"])
2012
+ try:
2013
+ return self._fresh_quest_service().open_document(quest_id, body["document_id"])
2014
+ except FileNotFoundError:
2015
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1523
2016
 
1524
2017
  def document_asset_upload(self, quest_id: str, body: dict) -> dict:
1525
2018
  document_id = str(body.get("document_id") or "").strip()
@@ -1537,22 +2030,77 @@ npm --prefix src/ui run build</pre>
1537
2030
  content = base64.b64decode(content_base64, validate=True)
1538
2031
  except (ValueError, TypeError):
1539
2032
  return {"ok": False, "message": "Invalid `content_base64` payload."}
1540
- return self.app.quest_service.save_document_asset(
1541
- quest_id,
1542
- document_id,
1543
- file_name=file_name,
1544
- mime_type=mime_type or None,
1545
- content=content,
1546
- kind=kind,
1547
- )
2033
+ try:
2034
+ return self.app.quest_service.save_document_asset(
2035
+ quest_id,
2036
+ document_id,
2037
+ file_name=file_name,
2038
+ mime_type=mime_type or None,
2039
+ content=content,
2040
+ kind=kind,
2041
+ )
2042
+ except FileNotFoundError:
2043
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1548
2044
 
1549
2045
  def document_save(self, quest_id: str, document_id: str, body: dict) -> dict:
1550
- return self.app.quest_service.save_document(
1551
- quest_id,
1552
- document_id,
1553
- body["content"],
1554
- previous_revision=body.get("revision"),
1555
- )
2046
+ try:
2047
+ return self.app.quest_service.save_document(
2048
+ quest_id,
2049
+ document_id,
2050
+ body["content"],
2051
+ previous_revision=body.get("revision"),
2052
+ )
2053
+ except FileNotFoundError:
2054
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
2055
+
2056
+ def chat_upload_create(self, quest_id: str, body: dict) -> dict:
2057
+ file_name = str(body.get("file_name") or "").strip()
2058
+ mime_type = str(body.get("mime_type") or "").strip()
2059
+ content_base64 = str(body.get("content_base64") or "").strip()
2060
+ draft_id = str(body.get("draft_id") or "").strip() or None
2061
+ if not file_name:
2062
+ return {"ok": False, "message": "`file_name` is required."}
2063
+ if not content_base64:
2064
+ return {"ok": False, "message": "`content_base64` is required."}
2065
+ try:
2066
+ content = base64.b64decode(content_base64, validate=True)
2067
+ except (ValueError, TypeError):
2068
+ return {"ok": False, "message": "Invalid `content_base64` payload."}
2069
+ try:
2070
+ return self.app.quest_service.save_chat_attachment_draft(
2071
+ quest_id,
2072
+ file_name=file_name,
2073
+ mime_type=mime_type or None,
2074
+ content=content,
2075
+ draft_id=draft_id,
2076
+ )
2077
+ except FileNotFoundError:
2078
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
2079
+
2080
+ def chat_upload_delete(self, quest_id: str, draft_id: str, body: dict | None = None) -> dict:
2081
+ try:
2082
+ return self.app.quest_service.delete_chat_attachment_draft(
2083
+ quest_id,
2084
+ draft_id=draft_id,
2085
+ )
2086
+ except FileNotFoundError:
2087
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
2088
+
2089
+ def chat_upload_import(self, quest_id: str, body: dict) -> dict:
2090
+ source_quest_id = str(body.get("source_quest_id") or "").strip()
2091
+ attachments = [dict(item) for item in (body.get("attachments") or []) if isinstance(item, dict)]
2092
+ if not source_quest_id:
2093
+ return {"ok": False, "message": "`source_quest_id` is required."}
2094
+ if not attachments:
2095
+ return {"ok": False, "message": "`attachments` is required."}
2096
+ try:
2097
+ return self.app.quest_service.import_chat_attachment_drafts(
2098
+ quest_id,
2099
+ source_quest_id=source_quest_id,
2100
+ attachments=attachments,
2101
+ )
2102
+ except FileNotFoundError as exc:
2103
+ return 404, {"ok": False, "message": str(exc)}
1556
2104
 
1557
2105
  def latex_init(self, project_id: str, body: dict) -> dict:
1558
2106
  return self.app.latex_service.init_project(
@@ -1607,17 +2155,35 @@ npm --prefix src/ui run build</pre>
1607
2155
 
1608
2156
  def chat(self, quest_id: str, body: dict) -> dict:
1609
2157
  text = body.get("text", "").strip()
1610
- if not text:
2158
+ attachment_draft_ids = [
2159
+ str(item or "").strip()
2160
+ for item in (body.get("attachment_draft_ids") or [])
2161
+ if str(item or "").strip()
2162
+ ]
2163
+ if not text and not attachment_draft_ids:
1611
2164
  return {"ok": False, "message": "Empty message."}
1612
2165
  source = body.get("source", "api")
1613
2166
  self.app.sessions.bind(quest_id, source)
1614
- payload = self.app.submit_user_message(
1615
- quest_id,
1616
- text=text,
1617
- source=source,
1618
- reply_to_interaction_id=body.get("reply_to_interaction_id"),
1619
- client_message_id=body.get("client_message_id"),
1620
- )
2167
+ try:
2168
+ if attachment_draft_ids:
2169
+ payload = self.app.submit_web_user_message(
2170
+ quest_id,
2171
+ text=text,
2172
+ source=source,
2173
+ attachment_draft_ids=attachment_draft_ids,
2174
+ reply_to_interaction_id=body.get("reply_to_interaction_id"),
2175
+ client_message_id=body.get("client_message_id"),
2176
+ )
2177
+ else:
2178
+ payload = self.app.submit_user_message(
2179
+ quest_id,
2180
+ text=text,
2181
+ source=source,
2182
+ reply_to_interaction_id=body.get("reply_to_interaction_id"),
2183
+ client_message_id=body.get("client_message_id"),
2184
+ )
2185
+ except FileNotFoundError as exc:
2186
+ return 404, {"ok": False, "message": str(exc)}
1621
2187
  return {
1622
2188
  "ok": True,
1623
2189
  "ack": f"Received for {quest_id}. Stored and queued for execution.",
@@ -1802,8 +2368,9 @@ npm --prefix src/ui run build</pre>
1802
2368
  config = self.app.config_manager.load_named("config")
1803
2369
  runners = self.app.config_manager.load_runners_config()
1804
2370
  snapshot = self.app.quest_service.snapshot(quest_id)
1805
- runner_name = str(body.get("runner") or snapshot.get("runner") or config.get("default_runner", "codex")).strip().lower()
1806
- runner_cfg = runners.get(runner_name, {})
2371
+ requested_runner = str(body.get("runner") or "").strip().lower()
2372
+ runner_name = self.app._resolve_enabled_runner_name(snapshot, requested_runner=requested_runner)
2373
+ runner_cfg = runners.get(runner_name, {}) if isinstance(runners.get(runner_name), dict) else {}
1807
2374
  if runner_cfg.get("enabled") is False:
1808
2375
  return {
1809
2376
  "ok": False,
@@ -1928,7 +2495,7 @@ npm --prefix src/ui run build</pre>
1928
2495
  path = resolve_within(docs_root, relative)
1929
2496
  if not path.exists() or not path.is_file():
1930
2497
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
1931
- mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
2498
+ mime_type = self._guess_static_mime_type(path)
1932
2499
  return 200, self._asset_headers(mime_type), path.read_bytes()
1933
2500
 
1934
2501
  def config_files(self) -> list[dict]:
@@ -1969,6 +2536,17 @@ npm --prefix src/ui run build</pre>
1969
2536
  }
1970
2537
 
1971
2538
  def config_save(self, name: str, body: dict) -> dict:
2539
+ if name == "config" and isinstance(body.get("structured"), dict):
2540
+ default_runner = self.app.config_manager._normalize_runtime_runner_name(
2541
+ body["structured"].get("default_runner")
2542
+ )
2543
+ if default_runner in {"codex", "claude", "kimi", "opencode"}:
2544
+ os.environ["DEEPSCIENTIST_DEFAULT_RUNNER"] = default_runner
2545
+ os.environ["DEEPSCIENTIST_ENABLE_RUNNER"] = default_runner
2546
+ os.environ.pop("DS_DEFAULT_RUNNER", None)
2547
+ os.environ.pop("DS_ENABLE_RUNNER", None)
2548
+ os.environ.pop("DEEPSCIENTIST_ENABLE_RUNNERS", None)
2549
+ os.environ.pop("DS_ENABLE_RUNNERS", None)
1972
2550
  if isinstance(body.get("structured"), dict):
1973
2551
  result = self.app.config_manager.save_named_payload(name, body["structured"])
1974
2552
  else:
@@ -2002,21 +2580,29 @@ npm --prefix src/ui run build</pre>
2002
2580
  return {"ok": False, "summary": "Config test requires `name` and `content`."}
2003
2581
  return self.app.config_manager.test_named_text(body["name"], body["content"], live=bool(body.get("live", True)))
2004
2582
 
2583
+ def config_deepxiv_test(self, body: dict | None = None) -> dict:
2584
+ payload = body.get("structured") if isinstance((body or {}).get("structured"), dict) else None
2585
+ return self.app.config_manager.test_deepxiv_payload(payload)
2586
+
2005
2587
  def asset(self, asset_path: str) -> tuple[int, dict, bytes]:
2588
+ dist_root = self._ui_dist_root()
2006
2589
  candidate_roots = [
2590
+ dist_root / "assets" if dist_root is not None else None,
2007
2591
  self.app.repo_root / "src" / "ui" / "public" / "assets",
2008
2592
  self.app.repo_root / "assets",
2009
2593
  ]
2010
2594
  path = None
2011
2595
  for root in candidate_roots:
2596
+ if root is None:
2597
+ continue
2012
2598
  candidate = resolve_within(root, asset_path)
2013
2599
  if candidate.exists() and candidate.is_file():
2014
2600
  path = candidate
2015
2601
  break
2016
2602
  if path is None:
2017
2603
  return 404, {"Content-Type": "text/plain; charset=utf-8"}, b"Not Found"
2018
- mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
2019
- return 200, {"Content-Type": mime_type}, path.read_bytes()
2604
+ mime_type = self._guess_static_mime_type(path)
2605
+ return 200, self._asset_headers(mime_type), path.read_bytes()
2020
2606
 
2021
2607
  @staticmethod
2022
2608
  def parse_query(path: str) -> dict[str, list[str]]:
@@ -2025,6 +2611,12 @@ npm --prefix src/ui run build</pre>
2025
2611
  return parse_qs(path.split("?", 1)[1], keep_blank_values=True)
2026
2612
 
2027
2613
 
2614
+ @staticmethod
2615
+ def _locale_from_path(path: str, *, default: str = "en") -> str:
2616
+ query = ApiHandlers.parse_query(path)
2617
+ locale = ((query.get("locale") or [default])[0] or default).strip().lower()
2618
+ return "zh" if locale.startswith("zh") else "en"
2619
+
2028
2620
  @staticmethod
2029
2621
  def parse_body(raw: bytes) -> dict:
2030
2622
  if not raw: