@researai/deepscientist 1.5.17 → 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 (894) 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 +132 -11
  344. package/bin/ds.js +376 -49
  345. package/docs/en/00_QUICK_START.md +135 -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 +64 -4
  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 +622 -187
  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 +105 -2
  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 +29 -0
  375. package/docs/en/99_ACKNOWLEDGEMENTS.md +24 -19
  376. package/docs/en/README.md +44 -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 +92 -17
  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 +39 -4
  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 +550 -188
  416. package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +105 -2
  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 +29 -7
  429. package/install.sh +122 -16
  430. package/package.json +4 -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/channels/weixin_ilink.py +8 -1
  451. package/src/deepscientist/cli.py +92 -17
  452. package/src/deepscientist/codex_cli_compat.py +2 -2
  453. package/src/deepscientist/config/models.py +82 -11
  454. package/src/deepscientist/config/service.py +927 -91
  455. package/src/deepscientist/connector/weixin_support.py +48 -17
  456. package/src/deepscientist/daemon/api/handlers.py +697 -210
  457. package/src/deepscientist/daemon/api/router.py +76 -1
  458. package/src/deepscientist/daemon/app.py +1054 -51
  459. package/src/deepscientist/diagnostics/runner_failures.py +147 -0
  460. package/src/deepscientist/doctor.py +212 -65
  461. package/src/deepscientist/evidence_packets.py +590 -0
  462. package/src/deepscientist/home.py +52 -4
  463. package/src/deepscientist/kimi_cli_compat.py +50 -0
  464. package/src/deepscientist/latex_runtime.py +2 -2
  465. package/src/deepscientist/mcp/context.py +2 -0
  466. package/src/deepscientist/mcp/schemas.py +114 -0
  467. package/src/deepscientist/mcp/server.py +1566 -126
  468. package/src/deepscientist/memory/service.py +203 -16
  469. package/src/deepscientist/process_control.py +8 -1
  470. package/src/deepscientist/prompts/builder.py +836 -92
  471. package/src/deepscientist/quest/__init__.py +2 -2
  472. package/src/deepscientist/quest/layout.py +12 -1
  473. package/src/deepscientist/quest/node_traces.py +10 -0
  474. package/src/deepscientist/quest/service.py +1430 -139
  475. package/src/deepscientist/quest/stage_views.py +1 -1
  476. package/src/deepscientist/runners/__init__.py +18 -0
  477. package/src/deepscientist/runners/base.py +89 -1
  478. package/src/deepscientist/runners/builtins.py +13 -1
  479. package/src/deepscientist/runners/claude.py +391 -0
  480. package/src/deepscientist/runners/codex.py +421 -21
  481. package/src/deepscientist/runners/codex_telemetry.py +127 -0
  482. package/src/deepscientist/runners/kimi.py +334 -0
  483. package/src/deepscientist/runners/metadata.py +68 -0
  484. package/src/deepscientist/runners/opencode.py +414 -0
  485. package/src/deepscientist/runners/runtime_overrides.py +100 -0
  486. package/src/deepscientist/runners/simple_cli.py +538 -0
  487. package/src/deepscientist/runtime_storage.py +303 -0
  488. package/src/deepscientist/shared.py +61 -16
  489. package/src/deepscientist/skills/installer.py +37 -0
  490. package/src/deepscientist/skills/registry.py +2 -0
  491. package/src/deepscientist/tinytex.py +2 -2
  492. package/src/deepscientist/tui.py +10 -3
  493. package/src/prompts/benchstore/system.md +77 -0
  494. package/src/prompts/connectors/qq.md +33 -2
  495. package/src/prompts/connectors/weixin.md +208 -23
  496. package/src/prompts/contracts/admin_ops.md +74 -0
  497. package/src/prompts/contracts/admin_ops_knowledge.md +138 -0
  498. package/src/prompts/contracts/shared_interaction.md +5 -11
  499. package/src/prompts/start_setup/system.md +422 -0
  500. package/src/prompts/system.md +409 -315
  501. package/src/prompts/system_copilot.md +88 -12
  502. package/src/skills/analysis-campaign/SKILL.md +239 -578
  503. package/src/skills/analysis-campaign/references/artifact-flow-examples.md +102 -0
  504. package/src/skills/analysis-campaign/references/boundary-cases.md +98 -0
  505. package/src/skills/analysis-campaign/references/campaign-checklist-template.md +39 -24
  506. package/src/skills/analysis-campaign/references/campaign-design.md +26 -10
  507. package/src/skills/analysis-campaign/references/campaign-plan-template.md +53 -54
  508. package/src/skills/analysis-campaign/references/operational-guidance.md +97 -0
  509. package/src/skills/analysis-campaign/references/writing-facing-slice-examples.md +10 -20
  510. package/src/skills/baseline/SKILL.md +183 -461
  511. package/src/skills/baseline/references/artifact-flow-examples.md +106 -0
  512. package/src/skills/baseline/references/artifact-payload-examples.md +1 -1
  513. package/src/skills/baseline/references/baseline-checklist-template.md +27 -35
  514. package/src/skills/baseline/references/baseline-plan-template.md +37 -76
  515. package/src/skills/baseline/references/boundary-cases.md +86 -0
  516. package/src/skills/baseline/references/codebase-audit-checklist.md +2 -6
  517. package/src/skills/baseline/references/comparability-contract.md +7 -12
  518. package/src/skills/baseline/references/operational-guidance.md +56 -0
  519. package/src/skills/baseline/references/route-selection.md +5 -25
  520. package/src/skills/decision/SKILL.md +113 -306
  521. package/src/skills/decision/references/checkpoint-memory-template.md +47 -0
  522. package/src/skills/decision/references/operational-guidance.md +94 -0
  523. package/src/skills/decision/references/research-route-criteria.md +7 -8
  524. package/src/skills/decision/references/strategic-decision-template.md +13 -26
  525. package/src/skills/experiment/SKILL.md +132 -670
  526. package/src/skills/experiment/references/execution-playbook.md +374 -0
  527. package/src/skills/experiment/references/main-experiment-checklist-template.md +26 -2
  528. package/src/skills/experiment/references/main-experiment-plan-template.md +28 -17
  529. package/src/skills/experiment/references/operational-guidance.md +108 -0
  530. package/src/skills/finalize/SKILL.md +62 -0
  531. package/src/skills/finalize/references/checkpoint-memory-template.md +49 -0
  532. package/src/skills/finalize/references/resume-packet-template.md +7 -0
  533. package/src/skills/idea/SKILL.md +228 -15
  534. package/src/skills/idea/references/controlled-brainstorming-playbook.md +78 -0
  535. package/src/skills/idea/references/current-board-packet-template.md +61 -0
  536. package/src/skills/idea/references/high-value-idea-sourcing.md +119 -0
  537. package/src/skills/idea/references/idea-generation-playbook.md +21 -0
  538. package/src/skills/idea/references/idea-thinking-flow.md +6 -0
  539. package/src/skills/idea/references/literature-survey-template.md +3 -0
  540. package/src/skills/idea/references/objective-contract-template.md +54 -0
  541. package/src/skills/idea/references/outline-seeding-example.md +56 -0
  542. package/src/skills/idea/references/pre-idea-draft-template.md +105 -0
  543. package/src/skills/idea/references/related-work-playbook.md +75 -2
  544. package/src/skills/idea/references/research-history-playbook.md +114 -0
  545. package/src/skills/idea/references/selection-gate.md +58 -6
  546. package/src/skills/intake-audit/SKILL.md +43 -2
  547. package/src/skills/intake-audit/references/state-audit-template.md +10 -0
  548. package/src/skills/nature-data/SKILL.md +128 -0
  549. package/src/skills/nature-data/UPSTREAM_LICENSE.txt +21 -0
  550. package/src/skills/nature-data/agents/openai.yaml +4 -0
  551. package/src/skills/nature-data/references/chinese-author-alignment.md +84 -0
  552. package/src/skills/nature-data/references/fair-metadata-checklist.md +105 -0
  553. package/src/skills/nature-data/references/policy-principles.md +103 -0
  554. package/src/skills/nature-data/references/repository-and-identifiers.md +96 -0
  555. package/src/skills/nature-data/references/source-basis.md +54 -0
  556. package/src/skills/nature-data/references/statement-patterns.md +153 -0
  557. package/src/skills/nature-figure/SKILL.md +197 -0
  558. package/src/skills/nature-figure/UPSTREAM_LICENSE.txt +21 -0
  559. package/src/skills/nature-figure/agents/openai.yaml +4 -0
  560. package/src/skills/nature-figure/evals/evals.json +37 -0
  561. package/src/skills/nature-figure/references/api.md +428 -0
  562. package/src/skills/nature-figure/references/backend-selection.md +100 -0
  563. package/src/skills/nature-figure/references/chart-types.md +281 -0
  564. package/src/skills/nature-figure/references/common-patterns.md +349 -0
  565. package/src/skills/nature-figure/references/design-theory.md +436 -0
  566. package/src/skills/nature-figure/references/figure-contract.md +93 -0
  567. package/src/skills/nature-figure/references/nature-2026-observations.md +112 -0
  568. package/src/skills/nature-figure/references/qa-contract.md +119 -0
  569. package/src/skills/nature-figure/references/r-template-index.md +66 -0
  570. package/src/skills/nature-figure/references/r-workflow.md +161 -0
  571. package/src/skills/nature-figure/references/tutorials.md +250 -0
  572. package/src/skills/nature-paper2ppt/SKILL.md +507 -0
  573. package/src/skills/nature-paper2ppt/UPSTREAM_LICENSE.txt +21 -0
  574. package/src/skills/nature-paper2ppt/agents/openai.yaml +4 -0
  575. package/src/skills/nature-polishing/SKILL.md +385 -0
  576. package/src/skills/nature-polishing/UPSTREAM_LICENSE.txt +21 -0
  577. package/src/skills/nature-polishing/agents/openai.yaml +4 -0
  578. package/src/skills/nature-polishing/references/phrasebank-playbook.md +162 -0
  579. package/src/skills/nature-polishing/references/section-moves.md +240 -0
  580. package/src/skills/nature-polishing/references/style-guardrails.md +94 -0
  581. package/src/skills/nature-polishing/references/writing-strategy.md +148 -0
  582. package/src/skills/optimize/SKILL.md +177 -1568
  583. package/src/skills/optimize/references/brief-shaping-playbook.md +95 -0
  584. package/src/skills/optimize/references/candidate-board-template.md +13 -0
  585. package/src/skills/optimize/references/candidate-ranking-template.md +51 -0
  586. package/src/skills/optimize/references/codegen-route-playbook.md +50 -0
  587. package/src/skills/optimize/references/debug-response-template.md +29 -0
  588. package/src/skills/optimize/references/frontier-review-template.md +32 -0
  589. package/src/skills/optimize/references/fusion-playbook.md +36 -0
  590. package/src/skills/optimize/references/method-brief-template.md +73 -0
  591. package/src/skills/optimize/references/operational-guidance.md +621 -0
  592. package/src/skills/optimize/references/optimization-memory-template.md +30 -0
  593. package/src/skills/optimize/references/optimize-checklist-template.md +18 -0
  594. package/src/skills/optimize/references/plateau-response-playbook.md +28 -0
  595. package/src/skills/optimize/references/prompt-patterns.md +49 -0
  596. package/src/skills/paper-outline/SKILL.md +227 -0
  597. package/src/skills/paper-outline/references/outline-patterns.md +87 -0
  598. package/src/skills/paper-plot/SKILL.md +79 -0
  599. package/src/skills/paper-plot/agents/openai.yaml +4 -0
  600. package/src/skills/paper-plot/references/bar_grouped_hatch.md +96 -0
  601. package/src/skills/paper-plot/references/bar_paired_delta.md +72 -0
  602. package/src/skills/paper-plot/references/line_confidence_band.md +75 -0
  603. package/src/skills/paper-plot/references/line_loss_with_inset.md +65 -0
  604. package/src/skills/paper-plot/references/line_training_curve.md +44 -0
  605. package/src/skills/paper-plot/references/radar_dual_series.md +59 -0
  606. package/src/skills/paper-plot/references/scatter_broken_axis.md +59 -0
  607. package/src/skills/paper-plot/references/scatter_tsne_cluster.md +72 -0
  608. package/src/skills/paper-plot/scripts/bar_memevolve.py +109 -0
  609. package/src/skills/paper-plot/scripts/bar_spice.py +166 -0
  610. package/src/skills/paper-plot/scripts/line_aime.py +94 -0
  611. package/src/skills/paper-plot/scripts/line_loss_inset.py +157 -0
  612. package/src/skills/paper-plot/scripts/line_selfdistill.py +168 -0
  613. package/src/skills/paper-plot/scripts/radar_dora.py +151 -0
  614. package/src/skills/paper-plot/scripts/scatter_break.py +169 -0
  615. package/src/skills/paper-plot/scripts/scatter_tsne.py +133 -0
  616. package/src/skills/rebuttal/SKILL.md +9 -0
  617. package/src/skills/references/tool-usage-by-stage.md +438 -0
  618. package/src/skills/review/SKILL.md +105 -7
  619. package/src/skills/science/PROVENANCE.md +44 -0
  620. package/src/skills/science/SKILL.md +137 -0
  621. package/src/skills/science/references/artifact-science-tool.md +110 -0
  622. package/src/skills/science/references/claim-type-discipline.md +56 -0
  623. package/src/skills/science/references/domain-index.md +422 -0
  624. package/src/skills/science/references/hpc-via-bash-exec.md +42 -0
  625. package/src/skills/science/references/package-check-playbook.md +64 -0
  626. package/src/skills/science/references/package-index.min.json +3616 -0
  627. package/src/skills/science/references/packages/abinit.md +80 -0
  628. package/src/skills/science/references/packages/acts.md +73 -0
  629. package/src/skills/science/references/packages/aiida-core.md +80 -0
  630. package/src/skills/science/references/packages/alamode.md +80 -0
  631. package/src/skills/science/references/packages/amuse.md +88 -0
  632. package/src/skills/science/references/packages/anndata.md +88 -0
  633. package/src/skills/science/references/packages/arbor.md +80 -0
  634. package/src/skills/science/references/packages/arc.md +73 -0
  635. package/src/skills/science/references/packages/astropy.md +88 -0
  636. package/src/skills/science/references/packages/astroquery.md +88 -0
  637. package/src/skills/science/references/packages/atomate2.md +80 -0
  638. package/src/skills/science/references/packages/atomsmltr.md +73 -0
  639. package/src/skills/science/references/packages/awkward.md +73 -0
  640. package/src/skills/science/references/packages/batman.md +88 -0
  641. package/src/skills/science/references/packages/biopython.md +88 -0
  642. package/src/skills/science/references/packages/bloqade.md +73 -0
  643. package/src/skills/science/references/packages/brian2.md +73 -0
  644. package/src/skills/science/references/packages/bullet3.md +73 -0
  645. package/src/skills/science/references/packages/calculix.md +80 -0
  646. package/src/skills/science/references/packages/cantera.md +73 -0
  647. package/src/skills/science/references/packages/cavity-md-ipi.md +80 -0
  648. package/src/skills/science/references/packages/ccdproc.md +88 -0
  649. package/src/skills/science/references/packages/celerite2.md +88 -0
  650. package/src/skills/science/references/packages/cellrank.md +73 -0
  651. package/src/skills/science/references/packages/cesm.md +80 -0
  652. package/src/skills/science/references/packages/chemicals.md +73 -0
  653. package/src/skills/science/references/packages/chempy.md +73 -0
  654. package/src/skills/science/references/packages/cirq.md +73 -0
  655. package/src/skills/science/references/packages/coffea.md +73 -0
  656. package/src/skills/science/references/packages/cp2k.md +88 -0
  657. package/src/skills/science/references/packages/custodian.md +80 -0
  658. package/src/skills/science/references/packages/dart.md +73 -0
  659. package/src/skills/science/references/packages/datamol.md +88 -0
  660. package/src/skills/science/references/packages/dd4hep.md +73 -0
  661. package/src/skills/science/references/packages/dealii.md +80 -0
  662. package/src/skills/science/references/packages/deepchem.md +88 -0
  663. package/src/skills/science/references/packages/delphes.md +73 -0
  664. package/src/skills/science/references/packages/devito.md +80 -0
  665. package/src/skills/science/references/packages/dftb.md +88 -0
  666. package/src/skills/science/references/packages/dftd4.md +88 -0
  667. package/src/skills/science/references/packages/dftk-jl.md +80 -0
  668. package/src/skills/science/references/packages/dolfinx.md +80 -0
  669. package/src/skills/science/references/packages/drake.md +73 -0
  670. package/src/skills/science/references/packages/dumux.md +73 -0
  671. package/src/skills/science/references/packages/elk.md +80 -0
  672. package/src/skills/science/references/packages/elmerfem.md +80 -0
  673. package/src/skills/science/references/packages/enzo-e.md +88 -0
  674. package/src/skills/science/references/packages/espresso.md +80 -0
  675. package/src/skills/science/references/packages/exoplanet.md +88 -0
  676. package/src/skills/science/references/packages/fairroot.md +73 -0
  677. package/src/skills/science/references/packages/fbpic.md +80 -0
  678. package/src/skills/science/references/packages/fdtdbath-meep.md +80 -0
  679. package/src/skills/science/references/packages/geant4.md +73 -0
  680. package/src/skills/science/references/packages/geosx.md +80 -0
  681. package/src/skills/science/references/packages/gprmax.md +80 -0
  682. package/src/skills/science/references/packages/gromacs.md +80 -0
  683. package/src/skills/science/references/packages/gwaslab.md +73 -0
  684. package/src/skills/science/references/packages/gz-sim.md +73 -0
  685. package/src/skills/science/references/packages/hail.md +88 -0
  686. package/src/skills/science/references/packages/hiphive.md +80 -0
  687. package/src/skills/science/references/packages/hoomd-blue.md +80 -0
  688. package/src/skills/science/references/packages/itensor.md +73 -0
  689. package/src/skills/science/references/packages/itensors-jl.md +73 -0
  690. package/src/skills/science/references/packages/jdftx.md +73 -0
  691. package/src/skills/science/references/packages/jobflow.md +80 -0
  692. package/src/skills/science/references/packages/kadanoffbaym-jl.md +73 -0
  693. package/src/skills/science/references/packages/kite.md +80 -0
  694. package/src/skills/science/references/packages/kratos.md +80 -0
  695. package/src/skills/science/references/packages/kwant.md +73 -0
  696. package/src/skills/science/references/packages/lammps.md +80 -0
  697. package/src/skills/science/references/packages/lightkurve.md +88 -0
  698. package/src/skills/science/references/packages/limix.md +73 -0
  699. package/src/skills/science/references/packages/maxwelllink.md +80 -0
  700. package/src/skills/science/references/packages/mcdc.md +73 -0
  701. package/src/skills/science/references/packages/meep.md +80 -0
  702. package/src/skills/science/references/packages/mfem.md +80 -0
  703. package/src/skills/science/references/packages/mitgcm.md +73 -0
  704. package/src/skills/science/references/packages/modflow6.md +73 -0
  705. package/src/skills/science/references/packages/molecool.md +73 -0
  706. package/src/skills/science/references/packages/mom6.md +73 -0
  707. package/src/skills/science/references/packages/moose.md +80 -0
  708. package/src/skills/science/references/packages/mpas-model.md +73 -0
  709. package/src/skills/science/references/packages/mujoco.md +73 -0
  710. package/src/skills/science/references/packages/mumax3.md +73 -0
  711. package/src/skills/science/references/packages/nekrs.md +80 -0
  712. package/src/skills/science/references/packages/nessi.md +73 -0
  713. package/src/skills/science/references/packages/nest-simulator.md +73 -0
  714. package/src/skills/science/references/packages/netket.md +73 -0
  715. package/src/skills/science/references/packages/neuron.md +73 -0
  716. package/src/skills/science/references/packages/nextflow.md +88 -0
  717. package/src/skills/science/references/packages/nwchem.md +88 -0
  718. package/src/skills/science/references/packages/openbabel.md +88 -0
  719. package/src/skills/science/references/packages/openems.md +80 -0
  720. package/src/skills/science/references/packages/openff-toolkit.md +88 -0
  721. package/src/skills/science/references/packages/openfoam-dev.md +80 -0
  722. package/src/skills/science/references/packages/openmc.md +73 -0
  723. package/src/skills/science/references/packages/openmm.md +80 -0
  724. package/src/skills/science/references/packages/openmoc.md +73 -0
  725. package/src/skills/science/references/packages/openmx.md +80 -0
  726. package/src/skills/science/references/packages/opensees.md +80 -0
  727. package/src/skills/science/references/packages/opensn.md +80 -0
  728. package/src/skills/science/references/packages/opm-simulators.md +73 -0
  729. package/src/skills/science/references/packages/oqupy.md +73 -0
  730. package/src/skills/science/references/packages/packmol.md +80 -0
  731. package/src/skills/science/references/packages/palabos.md +80 -0
  732. package/src/skills/science/references/packages/parflow.md +80 -0
  733. package/src/skills/science/references/packages/pennylane.md +88 -0
  734. package/src/skills/science/references/packages/perceval.md +73 -0
  735. package/src/skills/science/references/packages/phono3py.md +73 -0
  736. package/src/skills/science/references/packages/phonopy.md +73 -0
  737. package/src/skills/science/references/packages/photutils.md +88 -0
  738. package/src/skills/science/references/packages/picongpu.md +80 -0
  739. package/src/skills/science/references/packages/plink-ng.md +88 -0
  740. package/src/skills/science/references/packages/precice.md +73 -0
  741. package/src/skills/science/references/packages/psc.md +80 -0
  742. package/src/skills/science/references/packages/psi4.md +88 -0
  743. package/src/skills/science/references/packages/pybinding.md +73 -0
  744. package/src/skills/science/references/packages/pyfr.md +80 -0
  745. package/src/skills/science/references/packages/pyhf.md +73 -0
  746. package/src/skills/science/references/packages/pyiron_base.md +80 -0
  747. package/src/skills/science/references/packages/pylcp.md +73 -0
  748. package/src/skills/science/references/packages/pylith.md +80 -0
  749. package/src/skills/science/references/packages/pynbody.md +88 -0
  750. package/src/skills/science/references/packages/pysam.md +88 -0
  751. package/src/skills/science/references/packages/pyscf.md +88 -0
  752. package/src/skills/science/references/packages/q-e.md +73 -0
  753. package/src/skills/science/references/packages/qibo.md +73 -0
  754. package/src/skills/science/references/packages/qiskit.md +73 -0
  755. package/src/skills/science/references/packages/quantica-jl.md +73 -0
  756. package/src/skills/science/references/packages/quantumoptics-jl.md +73 -0
  757. package/src/skills/science/references/packages/quimb.md +73 -0
  758. package/src/skills/science/references/packages/qulacs.md +73 -0
  759. package/src/skills/science/references/packages/qutip.md +73 -0
  760. package/src/skills/science/references/packages/rdkit.md +88 -0
  761. package/src/skills/science/references/packages/rmg-py.md +73 -0
  762. package/src/skills/science/references/packages/root.md +73 -0
  763. package/src/skills/science/references/packages/scanpy.md +88 -0
  764. package/src/skills/science/references/packages/scikit-allel.md +88 -0
  765. package/src/skills/science/references/packages/scikit-bio.md +88 -0
  766. package/src/skills/science/references/packages/scqubits.md +73 -0
  767. package/src/skills/science/references/packages/scuff-em.md +80 -0
  768. package/src/skills/science/references/packages/scvi-tools.md +73 -0
  769. package/src/skills/science/references/packages/seissol.md +73 -0
  770. package/src/skills/science/references/packages/sfepy.md +80 -0
  771. package/src/skills/science/references/packages/sisl.md +73 -0
  772. package/src/skills/science/references/packages/smilei.md +80 -0
  773. package/src/skills/science/references/packages/snakemake.md +88 -0
  774. package/src/skills/science/references/packages/specfem3d-globe.md +80 -0
  775. package/src/skills/science/references/packages/specutils.md +88 -0
  776. package/src/skills/science/references/packages/spglib.md +80 -0
  777. package/src/skills/science/references/packages/squidpy.md +88 -0
  778. package/src/skills/science/references/packages/starry.md +88 -0
  779. package/src/skills/science/references/packages/strawberryfields.md +73 -0
  780. package/src/skills/science/references/packages/su2.md +80 -0
  781. package/src/skills/science/references/packages/sunny-jl.md +73 -0
  782. package/src/skills/science/references/packages/sw4.md +73 -0
  783. package/src/skills/science/references/packages/swift.md +88 -0
  784. package/src/skills/science/references/packages/tdnegf.md +73 -0
  785. package/src/skills/science/references/packages/tenpy.md +73 -0
  786. package/src/skills/science/references/packages/thermo.md +73 -0
  787. package/src/skills/science/references/packages/tkwant.md +73 -0
  788. package/src/skills/science/references/packages/tvb-root.md +73 -0
  789. package/src/skills/science/references/packages/uproot5.md +73 -0
  790. package/src/skills/science/references/packages/vampire.md +80 -0
  791. package/src/skills/science/references/packages/wannier_tools.md +73 -0
  792. package/src/skills/science/references/packages/warpx.md +80 -0
  793. package/src/skills/science/references/packages/wrf.md +73 -0
  794. package/src/skills/science/references/packages/xtb.md +88 -0
  795. package/src/skills/science/references/packages/yt.md +73 -0
  796. package/src/skills/science/references/science-task-brief-template.md +71 -0
  797. package/src/skills/scout/SKILL.md +83 -425
  798. package/src/skills/scout/references/literature-scout-template.md +5 -24
  799. package/src/skills/scout/references/operational-guidance.md +191 -0
  800. package/src/skills/scout/references/paper-triage-playbook.md +11 -35
  801. package/src/skills/write/SKILL.md +744 -1246
  802. package/src/skills/write/references/experiments_analysis_patterns.md +129 -0
  803. package/src/skills/write/references/oral_package_patterns.md +252 -0
  804. package/src/skills/write/references/oral_writing_principles.md +291 -0
  805. package/src/skills/write/references/section_rewrite_checklist.md +234 -0
  806. package/src/tui/dist/app/AppContainer.js +1314 -27
  807. package/src/tui/dist/components/Composer.js +26 -1
  808. package/src/tui/dist/components/ConfigScreen.js +2 -1
  809. package/src/tui/dist/components/InputPrompt.js +25 -9
  810. package/src/tui/dist/components/MainContent.js +18 -3
  811. package/src/tui/dist/components/QuestScreen.js +3 -2
  812. package/src/tui/dist/components/UtilityScreen.js +37 -0
  813. package/src/tui/dist/hooks/useSafeInput.js +10 -0
  814. package/src/tui/dist/index.js +13 -1
  815. package/src/tui/dist/layouts/DefaultAppLayout.js +11 -8
  816. package/src/tui/dist/lib/api.js +89 -1
  817. package/src/tui/package.json +1 -1
  818. package/src/ui/dist/assets/{AnalysisPlugin-BCKAfjba.js → AnalysisPlugin-CA94NGmI.js} +1 -1
  819. package/src/ui/dist/assets/CliPlugin-DHBzphZU.js +79 -0
  820. package/src/ui/dist/assets/CodeEditorPlugin-BOFwD2rn.js +2 -0
  821. package/src/ui/dist/assets/{CodeViewerPlugin-CbaFRrUU.js → CodeViewerPlugin-CqDpgjik.js} +4 -4
  822. package/src/ui/dist/assets/{DocViewerPlugin-DAjLVeQD.js → DocViewerPlugin-UDBgt8-4.js} +3 -3
  823. package/src/ui/dist/assets/GitCommitViewerPlugin-BmHtZ0bZ.js +6 -0
  824. package/src/ui/dist/assets/{GitDiffViewerPlugin-CQACjoAA.js → GitDiffViewerPlugin-CAxjNorQ.js} +2 -2
  825. package/src/ui/dist/assets/{GitSnapshotViewer-0r4nLPke.js → GitSnapshotViewer-CweA6VON.js} +2 -2
  826. package/src/ui/dist/assets/{ImageViewerPlugin-nBOmI2v_.js → ImageViewerPlugin-C8wHGvGN.js} +5 -5
  827. package/src/ui/dist/assets/LabPlugin-COyyLUol.js +32 -0
  828. package/src/ui/dist/assets/{LatexPlugin-ZwtV8pIp.js → LatexPlugin-BQjAaA5J.js} +4 -4
  829. package/src/ui/dist/assets/{MarkdownViewerPlugin-DKqVfKyW.js → MarkdownViewerPlugin-Dy1NE2dI.js} +3 -3
  830. package/src/ui/dist/assets/{MarketplacePlugin-BwxStZ9D.js → MarketplacePlugin-DMIZtEJ2.js} +2 -2
  831. package/src/ui/dist/assets/NotebookEditor-CFHMq_Qt.js +91 -0
  832. package/src/ui/dist/assets/{NotebookEditor-DB9N_T9q.js → NotebookEditor-WFyd8Ybt.js} +3 -3
  833. package/src/ui/dist/assets/{PdfLoader-eWBONbQP.js → PdfLoader-CLE5u5TS.js} +3 -3
  834. package/src/ui/dist/assets/{PdfMarkdownPlugin-D22YOZL3.js → PdfMarkdownPlugin-_iNK_H83.js} +1 -1
  835. package/src/ui/dist/assets/PdfViewerPlugin-DgWsbInT.js +22 -0
  836. package/src/ui/dist/assets/SearchPlugin-DrZmn5iw.js +11 -0
  837. package/src/ui/dist/assets/{TextViewerPlugin-C5xqeeUH.js → TextViewerPlugin-D1-T3aC7.js} +4 -4
  838. package/src/ui/dist/assets/branding/runner-claude.svg +107 -0
  839. package/src/ui/dist/assets/branding/runner-codex.svg +10 -0
  840. package/src/ui/dist/assets/branding/runner-kimi.svg +14 -0
  841. package/src/ui/dist/assets/branding/runner-opencode.svg +7 -0
  842. package/src/ui/dist/assets/cli-store-CoZ-x5Ip.js +1 -0
  843. package/src/ui/dist/assets/{code-WlFHE7z_.js → code-DbsmSd3Y.js} +1 -1
  844. package/src/ui/dist/assets/file-diff-panel-DsvyRz47.js +1 -0
  845. package/src/ui/dist/assets/{wrap-text-BC-Hltpd.js → file-jump-queue-DeQBikaw.js} +3 -3
  846. package/src/ui/dist/assets/{file-socket-CfQPKQKj.js → file-socket-DA5XIx88.js} +1 -1
  847. package/src/ui/dist/assets/fonts/ds-fonts.css +50 -4
  848. package/src/ui/dist/assets/images/deepxiv/register-guide.png +0 -0
  849. package/src/ui/dist/assets/index-39vY9LmZ.js +1 -0
  850. package/src/ui/dist/assets/{index-CwNu1aH4.js → index-BsO46tJA.js} +1 -1
  851. package/src/ui/dist/assets/index-CHzJ2xtB.js +3530 -0
  852. package/src/ui/dist/assets/index-DH-zxoZ3.css +33 -0
  853. package/src/ui/dist/assets/{plugin-notebook-HbW2K-1c.js → plugin-notebook-JRhysCqj.js} +2 -2
  854. package/src/ui/dist/assets/{project-sync-C9IdzdZW.js → project-sync-DPmWKmKD.js} +1 -1
  855. package/src/ui/dist/assets/{zoom-out-E_gaeAxL.js → zoom-out-DAukFWen.js} +3 -3
  856. package/src/ui/dist/index.html +3 -3
  857. package/src/skills/analysis-campaign/references/artifact-orchestration.md +0 -58
  858. package/src/skills/baseline/references/memory-playbook.md +0 -40
  859. package/src/skills/baseline/references/publishable-baseline-package.md +0 -30
  860. package/src/skills/write/references/outline-evidence-contract-example.md +0 -107
  861. package/src/skills/write/references/paper-experiment-matrix-template.md +0 -131
  862. package/src/skills/write/references/paper-section-playbook.md +0 -64
  863. package/src/skills/write/references/reviewer-first-writing.md +0 -64
  864. package/src/skills/write/references/revision-checklist.md +0 -70
  865. package/src/skills/write/references/section-contracts.md +0 -82
  866. package/src/skills/write/references/sentence-level-proofing.md +0 -49
  867. package/src/ui/dist/assets/AiManusChatView-Bv-Z8YpU.js +0 -204
  868. package/src/ui/dist/assets/CliPlugin-BCKcpc35.js +0 -109
  869. package/src/ui/dist/assets/CodeEditorPlugin-DbOfSJ8K.js +0 -2
  870. package/src/ui/dist/assets/GitCommitViewerPlugin-CIUqbUDO.js +0 -1
  871. package/src/ui/dist/assets/LabCopilotPanel-BHxOxF4z.js +0 -14
  872. package/src/ui/dist/assets/LabPlugin-BKoZGs95.js +0 -22
  873. package/src/ui/dist/assets/NotebookEditor-BEQhaQbt.js +0 -81
  874. package/src/ui/dist/assets/PdfViewerPlugin-c-RK9DLM.js +0 -17
  875. package/src/ui/dist/assets/SearchPlugin-CxF9ytAx.js +0 -16
  876. package/src/ui/dist/assets/VNCViewer-BoLGLnHz.js +0 -11
  877. package/src/ui/dist/assets/bot-DREQOxzP.js +0 -6
  878. package/src/ui/dist/assets/chevron-up-C9Qpx4DE.js +0 -6
  879. package/src/ui/dist/assets/file-content-BZMz3RYp.js +0 -1
  880. package/src/ui/dist/assets/file-diff-panel-CQhw0jS2.js +0 -1
  881. package/src/ui/dist/assets/file-jump-queue-DA-SdG__.js +0 -1
  882. package/src/ui/dist/assets/git-commit-horizontal-DxZ8DCZh.js +0 -6
  883. package/src/ui/dist/assets/image-Bgl4VIyx.js +0 -6
  884. package/src/ui/dist/assets/index-BpV6lusQ.css +0 -33
  885. package/src/ui/dist/assets/index-CBNVuWcP.js +0 -2496
  886. package/src/ui/dist/assets/index-DrUnlf6K.js +0 -1
  887. package/src/ui/dist/assets/index-NW-h8VzN.js +0 -1
  888. package/src/ui/dist/assets/pdf-effect-queue-J8OnM0jE.js +0 -6
  889. package/src/ui/dist/assets/popover-CLc0pPP8.js +0 -1
  890. package/src/ui/dist/assets/select-Cs2PmzwL.js +0 -11
  891. package/src/ui/dist/assets/sigma-ClKcHAXm.js +0 -6
  892. package/src/ui/dist/assets/trash-DwpbFr3w.js +0 -11
  893. package/src/ui/dist/assets/useCliAccess-NQ8m0Let.js +0 -1
  894. package/src/ui/dist/assets/useFileDiffOverlay-FuhcnKiw.js +0 -1
@@ -3,10 +3,12 @@ from __future__ import annotations
3
3
  import base64
4
4
  from collections import deque
5
5
  from dataclasses import dataclass
6
+ import errno
6
7
  import faulthandler
7
8
  import hashlib
8
9
  import hmac
9
10
  import json
11
+ import logging
10
12
  import mimetypes
11
13
  import os
12
14
  import re
@@ -28,8 +30,12 @@ from urllib.request import Request
28
30
 
29
31
  from .. import __version__
30
32
  from ..annotations import AnnotationService
33
+ from ..admin import AdminTaskService
34
+ from ..admin.repairs import AdminRepairService
35
+ from ..admin.service import AdminService
31
36
  from ..acp import build_session_update
32
37
  from ..artifact import ArtifactService
38
+ from ..benchstore import BenchStoreService
33
39
  from ..bash_exec import BashExecService
34
40
  from ..bash_exec.models import TerminalClient
35
41
  from ..bash_exec.service import DEFAULT_TERMINAL_SESSION_ID
@@ -78,10 +84,11 @@ from ..connector.lingzhu_support import (
78
84
  from ..prompts import PromptBuilder
79
85
  from ..prompts.builder import classify_turn_intent, current_standard_skills
80
86
  from ..connector.qq_profiles import list_qq_profiles, merge_qq_profile_config, normalize_qq_connector_config
81
- from ..quest import QuestService
82
- from ..runners import CodexRunner, RunRequest, get_runner_factory, register_builtin_runners
87
+ from ..quest import AUTONOMOUS_BLOCKING_WAIT_REASONS, QuestService
88
+ from ..runners import ClaudeRunner, CodexRunner, KimiRunner, OpenCodeRunner, RunRequest, get_runner_factory, register_builtin_runners
89
+ from ..runners.metadata import get_runner_metadata
83
90
  from ..runtime_logs import JsonlLogger
84
- from ..shared import append_jsonl, ensure_dir, generate_id, iter_jsonl, read_json, read_jsonl, read_jsonl_tail, read_text, resolve_within, run_command, slugify, utc_now, which, write_json
91
+ from ..shared import append_jsonl, ensure_dir, generate_id, iter_jsonl, read_json, read_jsonl, read_jsonl_tail, read_text, resolve_within, run_command, slugify, utc_now, utf8_text_subprocess_kwargs, which, write_json
85
92
  from ..skills import SkillInstaller
86
93
  from ..team import SingleTeamService
87
94
  from ..connector.weixin_support import (
@@ -108,7 +115,10 @@ _AUTO_CONTINUE_ACTIVE_WORK_DELAY_SECONDS = 0.2
108
115
  _TERMINAL_PREWARM_DEBOUNCE_SECONDS = 20.0
109
116
  _STALLED_RUNNING_TURN_INACTIVITY_SECONDS = 30 * 60
110
117
  _STALLED_RUNNING_TURN_INTERRUPT_TIMEOUT_SECONDS = 5.0
111
- CODEX_RETRY_DEFAULT_MAX_ATTEMPTS = 5
118
+ _IMMEDIATE_READ_INTERRUPT_TIMEOUT_SECONDS = 5.0
119
+ _RESUME_CONTINUE_TEXT = "Continue"
120
+ CODEX_RETRY_DEFAULT_MAX_ATTEMPTS = 7
121
+ PREVIOUS_CODEX_RETRY_DEFAULT_MAX_ATTEMPTS = 5
112
122
  CODEX_RETRY_DEFAULT_INITIAL_BACKOFF_SEC = 10.0
113
123
  CODEX_RETRY_DEFAULT_BACKOFF_MULTIPLIER = 6.0
114
124
  CODEX_RETRY_DEFAULT_MAX_BACKOFF_SEC = 1800.0
@@ -202,6 +212,7 @@ class DaemonApp:
202
212
  self.memory_service = MemoryService(home)
203
213
  self.annotation_service = AnnotationService(home)
204
214
  self.artifact_service = ArtifactService(home)
215
+ self.benchstore_service = BenchStoreService(home, repo_root=self.repo_root)
205
216
  self.bash_exec_service = BashExecService(home)
206
217
  self.team_service = SingleTeamService(home)
207
218
  self.cloud_service = CloudLinkService(home)
@@ -213,6 +224,16 @@ class DaemonApp:
213
224
  sync_existing_quests_enabled=bool(skill_config.get("sync_quest_on_open", True)),
214
225
  )
215
226
  self.logger = JsonlLogger(home / "logs", level=config.get("logging", {}).get("level", "info"))
227
+ self.admin_task_service = AdminTaskService(
228
+ home,
229
+ repo_root=self.repo_root,
230
+ logger=self.logger,
231
+ system_update_status_fn=self.system_update_status,
232
+ system_update_action_fn=self.request_system_update,
233
+ )
234
+ self.admin_task_service.logger = self.logger
235
+ self.admin_repair_service = AdminRepairService(self)
236
+ self.admin_service = AdminService(self)
216
237
  self.reconciled_quests = self.quest_service.reconcile_runtime_state()
217
238
  for item in self.reconciled_quests:
218
239
  self.logger.log(
@@ -236,11 +257,41 @@ class DaemonApp:
236
257
  prompt_builder=self.prompt_builder,
237
258
  artifact_service=self.artifact_service,
238
259
  )
239
- register_builtin_runners(codex_runner=self.codex_runner)
260
+ self.claude_runner = ClaudeRunner(
261
+ home=home,
262
+ repo_root=self.repo_root,
263
+ binary=self.runners_config.get("claude", {}).get("binary", "claude"),
264
+ logger=self.logger,
265
+ prompt_builder=self.prompt_builder,
266
+ artifact_service=self.artifact_service,
267
+ )
268
+ self.kimi_runner = KimiRunner(
269
+ home=home,
270
+ repo_root=self.repo_root,
271
+ binary=self.runners_config.get("kimi", {}).get("binary", "kimi"),
272
+ logger=self.logger,
273
+ prompt_builder=self.prompt_builder,
274
+ artifact_service=self.artifact_service,
275
+ )
276
+ self.opencode_runner = OpenCodeRunner(
277
+ home=home,
278
+ repo_root=self.repo_root,
279
+ binary=self.runners_config.get("opencode", {}).get("binary", "opencode"),
280
+ logger=self.logger,
281
+ prompt_builder=self.prompt_builder,
282
+ artifact_service=self.artifact_service,
283
+ )
284
+ register_builtin_runners(
285
+ codex_runner=self.codex_runner,
286
+ claude_runner=self.claude_runner,
287
+ kimi_runner=self.kimi_runner,
288
+ opencode_runner=self.opencode_runner,
289
+ )
240
290
  register_builtin_connector_bridges()
241
291
  register_builtin_channels(home=home, connectors_config=self.connectors_config)
242
292
  self.runners = {
243
- "codex": self._create_runner("codex"),
293
+ name: self._create_runner(name)
294
+ for name in ("codex", "claude", "kimi", "opencode")
244
295
  }
245
296
  self.channels = {name: self._create_channel(name) for name in list_channel_names()}
246
297
  self.sessions = SessionStore()
@@ -1028,10 +1079,10 @@ class DaemonApp:
1028
1079
  command,
1029
1080
  cwd=str(self.repo_root),
1030
1081
  capture_output=True,
1031
- text=True,
1032
1082
  timeout=8,
1033
1083
  check=False,
1034
1084
  env=os.environ.copy(),
1085
+ **utf8_text_subprocess_kwargs(),
1035
1086
  **_windows_hidden_subprocess_kwargs(),
1036
1087
  )
1037
1088
  except subprocess.TimeoutExpired as exc:
@@ -1076,10 +1127,10 @@ class DaemonApp:
1076
1127
  command,
1077
1128
  cwd=str(self.repo_root),
1078
1129
  capture_output=True,
1079
- text=True,
1080
1130
  timeout=8,
1081
1131
  check=False,
1082
1132
  env=os.environ.copy(),
1133
+ **utf8_text_subprocess_kwargs(),
1083
1134
  **_windows_hidden_subprocess_kwargs(),
1084
1135
  )
1085
1136
  except subprocess.TimeoutExpired as exc:
@@ -1094,6 +1145,24 @@ class DaemonApp:
1094
1145
  raise RuntimeError("DeepScientist update request returned an invalid payload.")
1095
1146
  return payload
1096
1147
 
1148
+ def start_benchstore_install(self, entry_id: str) -> dict[str, Any]:
1149
+ entry_payload = self.benchstore_service.get_entry(entry_id)
1150
+ entry = entry_payload.get("entry") if isinstance(entry_payload.get("entry"), dict) else {}
1151
+ normalized_id = str(entry.get("id") or entry_id).strip() or str(entry_id).strip()
1152
+ entry_name = str(entry.get("name") or normalized_id).strip() or normalized_id
1153
+ return self.admin_task_service.start_task(
1154
+ "benchstore_install",
1155
+ metadata={
1156
+ "entry_id": normalized_id,
1157
+ "entry_name": entry_name,
1158
+ },
1159
+ target=lambda reporter, current_task: self.benchstore_service.run_install_task(
1160
+ entry_id=normalized_id,
1161
+ reporter=reporter,
1162
+ task_id=str(current_task.get("task_id") or ""),
1163
+ ),
1164
+ )
1165
+
1097
1166
  def _process_terminal_attach_request(
1098
1167
  self,
1099
1168
  connection: ServerConnection,
@@ -1438,6 +1507,9 @@ class DaemonApp:
1438
1507
  def _start_terminal_attach_server(self, host: str, port: int) -> None:
1439
1508
  if self._terminal_attach_server is not None:
1440
1509
  return
1510
+ terminal_ws_logger = logging.getLogger("deepscientist.terminal_attach.websocket")
1511
+ terminal_ws_logger.setLevel(logging.CRITICAL + 1)
1512
+ terminal_ws_logger.propagate = False
1441
1513
  candidates: list[int] = []
1442
1514
  if port > 0 and port < 65535:
1443
1515
  candidates.append(port + 1)
@@ -1453,6 +1525,7 @@ class DaemonApp:
1453
1525
  compression=None,
1454
1526
  max_size=None,
1455
1527
  max_queue=None,
1528
+ logger=terminal_ws_logger,
1456
1529
  )
1457
1530
  self._terminal_attach_server = server
1458
1531
  self._terminal_attach_host = host
@@ -1566,19 +1639,31 @@ class DaemonApp:
1566
1639
 
1567
1640
  def reload_runners_config(self) -> dict[str, object]:
1568
1641
  self.runners_config = self.config_manager.load_runners_config()
1569
- codex_config = self.runners_config.get("codex", {})
1570
- if isinstance(codex_config, dict):
1571
- self.codex_runner.binary = str(codex_config.get("binary") or "codex")
1572
- return {
1642
+ runner_instances = {
1643
+ "codex": self.codex_runner,
1644
+ "claude": self.claude_runner,
1645
+ "kimi": self.kimi_runner,
1646
+ "opencode": self.opencode_runner,
1647
+ }
1648
+ payload: dict[str, object] = {
1573
1649
  "ok": True,
1574
1650
  "runners": sorted(name for name, config in self.runners_config.items() if isinstance(config, dict)),
1575
- "codex": {
1576
- "binary": self.codex_runner.binary,
1577
- "approval_policy": codex_config.get("approval_policy") if isinstance(codex_config, dict) else None,
1578
- "sandbox_mode": codex_config.get("sandbox_mode") if isinstance(codex_config, dict) else None,
1579
- "mcp_tool_timeout_sec": codex_config.get("mcp_tool_timeout_sec") if isinstance(codex_config, dict) else None,
1580
- },
1581
1651
  }
1652
+ for runner_name, instance in runner_instances.items():
1653
+ runner_config = self.runners_config.get(runner_name, {})
1654
+ if isinstance(runner_config, dict):
1655
+ instance.binary = str(runner_config.get("binary") or getattr(instance, "binary", runner_name))
1656
+ payload[runner_name] = {
1657
+ "binary": getattr(instance, "binary", None),
1658
+ "enabled": runner_config.get("enabled") if isinstance(runner_config, dict) else None,
1659
+ "model": runner_config.get("model") if isinstance(runner_config, dict) else None,
1660
+ "config_dir": runner_config.get("config_dir") if isinstance(runner_config, dict) else None,
1661
+ }
1662
+ if runner_name == "codex" and isinstance(runner_config, dict):
1663
+ payload[runner_name]["approval_policy"] = runner_config.get("approval_policy")
1664
+ payload[runner_name]["sandbox_mode"] = runner_config.get("sandbox_mode")
1665
+ payload[runner_name]["mcp_tool_timeout_sec"] = runner_config.get("mcp_tool_timeout_sec")
1666
+ return payload
1582
1667
 
1583
1668
  def _preferred_locale(self) -> str:
1584
1669
  return str(self.runtime_config.get("default_locale") or "en-US").lower()
@@ -1586,6 +1671,14 @@ class DaemonApp:
1586
1671
  def _polite_copy(self, *, zh: str, en: str) -> str:
1587
1672
  return zh if self._preferred_locale().startswith("zh") else en
1588
1673
 
1674
+ @staticmethod
1675
+ def _resume_continue_source(source: str) -> str:
1676
+ normalized = normalize_conversation_id(source)
1677
+ parsed = parse_conversation_id(normalized)
1678
+ if parsed is not None and str(parsed.get("connector") or "").strip().lower() != "local":
1679
+ return normalized
1680
+ return "local"
1681
+
1589
1682
  def submit_user_message(
1590
1683
  self,
1591
1684
  quest_id: str,
@@ -1623,13 +1716,36 @@ class DaemonApp:
1623
1716
  )
1624
1717
  turn_state = self._refresh_turn_worker_state(quest_id)
1625
1718
  has_live_turn = bool(turn_state.get("running"))
1719
+ retry_wait_details = self._retry_waiting_turn_details(
1720
+ quest_id,
1721
+ snapshot=snapshot,
1722
+ turn_state=turn_state,
1723
+ turn_reason="user_message",
1724
+ )
1626
1725
  stalled_details = self._stalled_running_turn_details(
1627
1726
  quest_id,
1628
1727
  snapshot=snapshot,
1629
1728
  turn_state=turn_state,
1630
1729
  turn_reason="user_message",
1631
1730
  )
1632
- if runtime_status == "running" and has_live_turn and stalled_details is None:
1731
+ if retry_wait_details is not None:
1732
+ restart = self._interrupt_retry_wait_for_new_user_message(
1733
+ quest_id,
1734
+ snapshot=snapshot,
1735
+ turn_state=turn_state,
1736
+ details=retry_wait_details,
1737
+ source=source,
1738
+ )
1739
+ if restart is not None:
1740
+ scheduled = restart
1741
+ else:
1742
+ scheduled = {
1743
+ "scheduled": True,
1744
+ "started": False,
1745
+ "queued": True,
1746
+ "reason": "queued_for_artifact_interact",
1747
+ }
1748
+ elif runtime_status == "running" and has_live_turn and stalled_details is None:
1633
1749
  scheduled = {
1634
1750
  "scheduled": True,
1635
1751
  "started": False,
@@ -1645,6 +1761,151 @@ class DaemonApp:
1645
1761
  **scheduled,
1646
1762
  }
1647
1763
 
1764
+ def _retry_waiting_turn_details(
1765
+ self,
1766
+ quest_id: str,
1767
+ *,
1768
+ snapshot: dict | None,
1769
+ turn_state: dict[str, object] | None,
1770
+ turn_reason: str,
1771
+ ) -> dict[str, Any] | None:
1772
+ if str(turn_reason or "").strip() != "user_message":
1773
+ return None
1774
+ snapshot = dict(snapshot or self.quest_service.snapshot(quest_id))
1775
+ runtime_status = str(snapshot.get("runtime_status") or snapshot.get("status") or "").strip().lower()
1776
+ if runtime_status != "running":
1777
+ return None
1778
+ state = dict(turn_state or self._refresh_turn_worker_state(quest_id))
1779
+ if not state.get("running"):
1780
+ return None
1781
+ retry_state = snapshot.get("retry_state") if isinstance(snapshot.get("retry_state"), dict) else None
1782
+ if not retry_state:
1783
+ return None
1784
+ next_retry_at = str(retry_state.get("next_retry_at") or "").strip()
1785
+ if not next_retry_at:
1786
+ return None
1787
+ if str(snapshot.get("active_run_id") or "").strip():
1788
+ return None
1789
+ pending_user_count = int(snapshot.get("pending_user_message_count") or 0)
1790
+ if pending_user_count <= 0:
1791
+ return None
1792
+ try:
1793
+ attempt_index = int(retry_state.get("attempt_index") or 0)
1794
+ except (TypeError, ValueError):
1795
+ attempt_index = 0
1796
+ try:
1797
+ max_attempts = int(retry_state.get("max_attempts") or 0)
1798
+ except (TypeError, ValueError):
1799
+ max_attempts = 0
1800
+ delay_seconds = self._recovery_retry_delay_seconds(snapshot) or 0.0
1801
+ return {
1802
+ "retry_state": dict(retry_state),
1803
+ "attempt_index": attempt_index,
1804
+ "max_attempts": max_attempts,
1805
+ "pending_user_count": pending_user_count,
1806
+ "delay_seconds": max(0.0, float(delay_seconds)),
1807
+ }
1808
+
1809
+ def _interrupt_retry_wait_for_new_user_message(
1810
+ self,
1811
+ quest_id: str,
1812
+ *,
1813
+ snapshot: dict[str, Any],
1814
+ turn_state: dict[str, object],
1815
+ details: dict[str, Any],
1816
+ source: str,
1817
+ ) -> dict[str, Any] | None:
1818
+ try:
1819
+ restart = self._quiet_interrupt_for_turn_restart(quest_id, source=source)
1820
+ except RuntimeError as exc:
1821
+ self.logger.log(
1822
+ "warning",
1823
+ "runner.turn_retry_interrupt_failed",
1824
+ quest_id=quest_id,
1825
+ source=source,
1826
+ error=str(exc),
1827
+ pending_user_message_count=int(details.get("pending_user_count") or 0),
1828
+ )
1829
+ return None
1830
+
1831
+ retry_state = details.get("retry_state") if isinstance(details.get("retry_state"), dict) else {}
1832
+ turn_id = str(retry_state.get("turn_id") or "").strip() or None
1833
+ previous_run_id = str(retry_state.get("last_run_id") or "").strip() or None
1834
+ attempt_index = max(0, int(details.get("attempt_index") or 0))
1835
+ max_attempts = max(0, int(details.get("max_attempts") or 0))
1836
+ pending_user_count = max(0, int(details.get("pending_user_count") or 0))
1837
+ delay_seconds = max(0.0, float(details.get("delay_seconds") or 0.0))
1838
+ runner_name = self._runner_name_for(snapshot)
1839
+ skill_id = self._turn_skill_for(snapshot, None, turn_reason="user_message", turn_mode=self._turn_mode_for(snapshot, None, turn_reason="user_message"))
1840
+ model = str((self.runners_config.get(runner_name) or {}).get("model", "gpt-5.4"))
1841
+ summary = (
1842
+ f"Retry wait aborted after attempt {attempt_index}/{max_attempts or attempt_index or 1} "
1843
+ f"because a newer user message arrived. "
1844
+ f"Pending queued user messages: {pending_user_count}. "
1845
+ f"Skipped remaining backoff wait: {delay_seconds:.1f}s."
1846
+ )
1847
+ self.quest_service.update_runtime_state(
1848
+ quest_root=self.quest_service._quest_root(quest_id),
1849
+ retry_state=None,
1850
+ )
1851
+ self._append_retry_event(
1852
+ quest_id,
1853
+ event_type="runner.turn_retry_aborted",
1854
+ runner_name=runner_name,
1855
+ run_id=previous_run_id,
1856
+ turn_id=turn_id,
1857
+ skill_id=skill_id,
1858
+ model=model,
1859
+ attempt_index=attempt_index,
1860
+ max_attempts=max_attempts,
1861
+ summary=summary,
1862
+ failure_summary=str(retry_state.get("last_error") or "").strip() or None,
1863
+ previous_run_id=previous_run_id,
1864
+ )
1865
+ self._relay_quest_message_to_bound_connectors(
1866
+ quest_id,
1867
+ message=self._polite_copy(
1868
+ zh="我收到新的用户消息,已中断上一轮等待中的自动重试,先优先处理最新要求。",
1869
+ en="A new user message arrived, so I interrupted the pending retry wait and will handle the latest request first.",
1870
+ ),
1871
+ kind="system",
1872
+ response_phase="update",
1873
+ importance="normal",
1874
+ attachments=[
1875
+ {
1876
+ "kind": "retry_interrupted",
1877
+ "run_id": previous_run_id,
1878
+ "runner": runner_name,
1879
+ "turn_id": turn_id,
1880
+ }
1881
+ ],
1882
+ )
1883
+ return self.schedule_turn(quest_id, reason="user_message")
1884
+
1885
+ def submit_web_user_message(
1886
+ self,
1887
+ quest_id: str,
1888
+ *,
1889
+ text: str,
1890
+ source: str,
1891
+ attachment_draft_ids: list[str],
1892
+ reply_to_interaction_id: str | None = None,
1893
+ client_message_id: str | None = None,
1894
+ ) -> dict:
1895
+ finalized_attachments = self.quest_service.finalize_chat_attachment_drafts(
1896
+ quest_id,
1897
+ draft_ids=attachment_draft_ids,
1898
+ client_message_id=client_message_id,
1899
+ )
1900
+ return self.submit_user_message(
1901
+ quest_id,
1902
+ text=text,
1903
+ source=source,
1904
+ attachments=finalized_attachments,
1905
+ reply_to_interaction_id=reply_to_interaction_id,
1906
+ client_message_id=client_message_id,
1907
+ )
1908
+
1648
1909
  def create_quest(
1649
1910
  self,
1650
1911
  *,
@@ -1664,10 +1925,13 @@ class DaemonApp:
1664
1925
  normalized_requested_bindings = self._normalize_requested_connector_bindings(requested_connector_bindings)
1665
1926
  if len(normalized_requested_bindings) > 1:
1666
1927
  raise ValueError("A quest may bind at most one external connector target.")
1928
+ configured = self.config_manager.load_named("config")
1929
+ default_runner = str(configured.get("default_runner") or "codex").strip().lower() or "codex"
1667
1930
  snapshot = self.quest_service.create(
1668
1931
  goal=goal,
1669
1932
  title=title,
1670
1933
  quest_id=quest_id,
1934
+ runner=default_runner,
1671
1935
  requested_baseline_ref=dict(requested_baseline_ref) if isinstance(requested_baseline_ref, dict) else None,
1672
1936
  startup_contract=dict(startup_contract) if isinstance(startup_contract, dict) else None,
1673
1937
  )
@@ -2282,6 +2546,300 @@ class DaemonApp:
2282
2546
  "notice": notice,
2283
2547
  }
2284
2548
 
2549
+ def _quiet_interrupt_for_turn_restart(self, quest_id: str, *, source: str) -> dict[str, Any]:
2550
+ snapshot = self.quest_service.snapshot(quest_id)
2551
+ snapshot = self._reconcile_stale_active_turn(quest_id, snapshot=snapshot)
2552
+ runner_name = self._runner_name_for(snapshot)
2553
+ active_run_id = str(snapshot.get("active_run_id") or "").strip() or None
2554
+ turn_state = self._refresh_turn_worker_state(quest_id)
2555
+ runtime_status = str(snapshot.get("runtime_status") or snapshot.get("status") or "").strip().lower()
2556
+ if not active_run_id and not turn_state.get("running"):
2557
+ if runtime_status in {"stopped", "paused", "completed", "error"}:
2558
+ snapshot = self.quest_service.set_status(quest_id, "active")
2559
+ return {
2560
+ "snapshot": snapshot,
2561
+ "interrupted": False,
2562
+ "stopped_bash_session_ids": [],
2563
+ }
2564
+
2565
+ interrupted = False
2566
+ try:
2567
+ runner = self.get_runner(runner_name)
2568
+ except KeyError:
2569
+ runner = None
2570
+ with self._turn_lock:
2571
+ state = self._turn_state.setdefault(quest_id, {"running": False, "pending": False})
2572
+ state["pending"] = False
2573
+ state["stop_requested"] = True
2574
+ runner_supports_interrupt = runner is not None and hasattr(runner, "interrupt")
2575
+ if runner_supports_interrupt:
2576
+ interrupted = bool(getattr(runner, "interrupt")(quest_id))
2577
+ stopped_bash_session_ids = self._stop_active_bash_exec_sessions(
2578
+ quest_id,
2579
+ run_id=active_run_id,
2580
+ reason="immediate_read",
2581
+ user_id=source,
2582
+ )
2583
+ turn_state = self._wait_for_turn_worker_exit(
2584
+ quest_id,
2585
+ timeout_seconds=_IMMEDIATE_READ_INTERRUPT_TIMEOUT_SECONDS,
2586
+ )
2587
+ if turn_state.get("running"):
2588
+ detail_parts = [
2589
+ f"runner={runner_name or 'unknown'}",
2590
+ f"interrupt_attempted={runner_supports_interrupt}",
2591
+ f"interrupt_returned={interrupted}",
2592
+ f"active_run_id={active_run_id or 'none'}",
2593
+ f"runtime_status={runtime_status or 'unknown'}",
2594
+ f"timeout_seconds={_IMMEDIATE_READ_INTERRUPT_TIMEOUT_SECONDS:g}",
2595
+ ]
2596
+ worker = turn_state.get("worker")
2597
+ if isinstance(worker, threading.Thread):
2598
+ detail_parts.append(f"worker_alive={worker.is_alive()}")
2599
+ detail_parts.append(f"worker_name={worker.name}")
2600
+ stopped_count = len(stopped_bash_session_ids)
2601
+ if stopped_count > 0:
2602
+ detail_parts.append(f"stopped_bash_sessions={stopped_count}")
2603
+ if runner is None:
2604
+ detail_parts.append("reason=runner_not_available")
2605
+ elif not runner_supports_interrupt:
2606
+ detail_parts.append("reason=runner_has_no_interrupt")
2607
+ elif not interrupted:
2608
+ detail_parts.append("reason=runner_interrupt_returned_false")
2609
+ else:
2610
+ detail_parts.append("reason=worker_did_not_exit_before_timeout")
2611
+ raise RuntimeError(
2612
+ "The active agent turn could not be interrupted for immediate read. "
2613
+ + "; ".join(detail_parts)
2614
+ )
2615
+ normalized_status = "active" if runtime_status == "running" else (runtime_status or "active")
2616
+ if normalized_status in {"stopped", "paused", "completed", "error"}:
2617
+ snapshot = self.quest_service.set_status(quest_id, "active")
2618
+ else:
2619
+ snapshot = self.quest_service.mark_turn_finished(quest_id, status=normalized_status)
2620
+ with self._turn_lock:
2621
+ state = self._turn_state.setdefault(quest_id, {"running": False, "pending": False})
2622
+ state["stop_requested"] = False
2623
+ state["running"] = False
2624
+ state.pop("worker", None)
2625
+ return {
2626
+ "snapshot": snapshot,
2627
+ "interrupted": interrupted,
2628
+ "stopped_bash_session_ids": stopped_bash_session_ids,
2629
+ }
2630
+
2631
+ def read_queued_user_messages_now(
2632
+ self,
2633
+ quest_id: str,
2634
+ *,
2635
+ message_id: str | None = None,
2636
+ client_message_id: str | None = None,
2637
+ source: str = "local",
2638
+ ) -> dict[str, Any]:
2639
+ snapshot = self.quest_service.snapshot(quest_id)
2640
+ snapshot = self._reconcile_stale_active_turn(quest_id, snapshot=snapshot)
2641
+ quest_root = self.quest_service._quest_root(quest_id)
2642
+ target_message_id = str(message_id or "").strip() or None
2643
+ target_client_message_id = str(client_message_id or "").strip() or None
2644
+ if target_message_id or target_client_message_id:
2645
+ status_payload = self.quest_service.pending_user_message_status(
2646
+ quest_root,
2647
+ message_id=target_message_id,
2648
+ client_message_id=target_client_message_id,
2649
+ )
2650
+ queue_state = str(status_payload.get("queue_state") or "missing")
2651
+ current_message_state = (
2652
+ dict(status_payload.get("message_state") or {})
2653
+ if isinstance(status_payload.get("message_state"), dict)
2654
+ else None
2655
+ )
2656
+ if queue_state == "read":
2657
+ return {
2658
+ "ok": True,
2659
+ "status": "already_read",
2660
+ "quest_id": quest_id,
2661
+ "message": "This message was already sent to the agent.",
2662
+ "message_ids": [target_message_id] if target_message_id else [],
2663
+ "client_message_ids": [target_client_message_id] if target_client_message_id else [],
2664
+ "current_message_state": current_message_state,
2665
+ "snapshot": snapshot,
2666
+ }
2667
+ if queue_state == "withdrawn":
2668
+ return {
2669
+ "ok": False,
2670
+ "status": "withdrawn",
2671
+ "quest_id": quest_id,
2672
+ "message": "This message was already withdrawn from the waiting queue.",
2673
+ "message_ids": [target_message_id] if target_message_id else [],
2674
+ "client_message_ids": [target_client_message_id] if target_client_message_id else [],
2675
+ "current_message_state": current_message_state,
2676
+ "snapshot": snapshot,
2677
+ }
2678
+ if queue_state == "missing":
2679
+ target_label = target_message_id or f"client:{target_client_message_id}"
2680
+ return {
2681
+ "ok": False,
2682
+ "status": "missing",
2683
+ "quest_id": quest_id,
2684
+ "message": f"Message `{target_label}` is not waiting in the queue.",
2685
+ "message_ids": [target_message_id] if target_message_id else [],
2686
+ "client_message_ids": [target_client_message_id] if target_client_message_id else [],
2687
+ "current_message_state": current_message_state,
2688
+ "snapshot": snapshot,
2689
+ }
2690
+ queue_payload = self.quest_service._read_message_queue(quest_root)
2691
+ pending = [dict(item) for item in (queue_payload.get("pending") or [])]
2692
+ runtime_status = str(snapshot.get("runtime_status") or snapshot.get("status") or "").strip().lower()
2693
+ self.logger.log(
2694
+ "info",
2695
+ "quest.user_message.read_now.requested",
2696
+ quest_id=quest_id,
2697
+ source=source,
2698
+ message_id=target_message_id,
2699
+ client_message_id=target_client_message_id,
2700
+ pending_user_message_count=len(pending),
2701
+ runtime_status=runtime_status or None,
2702
+ )
2703
+ if not pending:
2704
+ return {
2705
+ "ok": False,
2706
+ "status": "empty",
2707
+ "quest_id": quest_id,
2708
+ "message": "No unread user message is waiting in the queue.",
2709
+ "snapshot": snapshot,
2710
+ }
2711
+
2712
+ try:
2713
+ restart = self._quiet_interrupt_for_turn_restart(quest_id, source=source)
2714
+ except RuntimeError as exc:
2715
+ failed_message_ids = (
2716
+ [target_message_id]
2717
+ if target_message_id
2718
+ else [
2719
+ str(item.get("message_id") or "").strip()
2720
+ for item in pending
2721
+ if str(item.get("message_id") or "").strip()
2722
+ ]
2723
+ )
2724
+ self.logger.log(
2725
+ "warning",
2726
+ "quest.user_message.read_now.interrupt_failed",
2727
+ quest_id=quest_id,
2728
+ source=source,
2729
+ message_id=target_message_id,
2730
+ client_message_id=target_client_message_id,
2731
+ pending_user_message_count=len(pending),
2732
+ error=str(exc),
2733
+ )
2734
+ return {
2735
+ "ok": False,
2736
+ "status": "interrupt_failed",
2737
+ "quest_id": quest_id,
2738
+ "message": str(exc),
2739
+ "message_ids": failed_message_ids,
2740
+ "snapshot": snapshot,
2741
+ }
2742
+ mailbox_payload = self.quest_service.consume_pending_user_messages(
2743
+ quest_root,
2744
+ interaction_id=None,
2745
+ delivery_reason="immediate_read",
2746
+ )
2747
+ recent_inbound_messages = [
2748
+ dict(item)
2749
+ for item in (mailbox_payload.get("recent_inbound_messages") or [])
2750
+ if isinstance(item, dict)
2751
+ ]
2752
+ if not recent_inbound_messages:
2753
+ return {
2754
+ "ok": False,
2755
+ "quest_id": quest_id,
2756
+ "message": "No unread user message remained after the restart.",
2757
+ }
2758
+
2759
+ self.quest_service.set_turn_message_override(
2760
+ quest_root,
2761
+ turn_reason="immediate_read",
2762
+ message=str(mailbox_payload.get("agent_instruction") or "").strip(),
2763
+ message_ids=[
2764
+ str(item.get("message_id") or "").strip()
2765
+ for item in recent_inbound_messages
2766
+ if str(item.get("message_id") or "").strip()
2767
+ ],
2768
+ delivery_batch_id=str(((mailbox_payload.get("delivery_batch") or {}).get("batch_id") or "")).strip() or None,
2769
+ source=source,
2770
+ )
2771
+ post_snapshot = self.quest_service.snapshot(quest_id)
2772
+ runtime_status = str(post_snapshot.get("runtime_status") or post_snapshot.get("status") or "").strip().lower()
2773
+ if runtime_status in {"stopped", "paused", "completed", "error"}:
2774
+ post_snapshot = self.quest_service.set_status(quest_id, "active")
2775
+ scheduling = self.schedule_turn(quest_id, reason="immediate_read")
2776
+ message_states = [
2777
+ dict(item)
2778
+ for item in (mailbox_payload.get("message_states") or [])
2779
+ if isinstance(item, dict)
2780
+ ]
2781
+ self.logger.log(
2782
+ "info",
2783
+ "quest.user_message.read_now.scheduled",
2784
+ quest_id=quest_id,
2785
+ source=source,
2786
+ message_ids=[
2787
+ str(item.get("message_id") or "").strip()
2788
+ for item in recent_inbound_messages
2789
+ if str(item.get("message_id") or "").strip()
2790
+ ],
2791
+ client_message_ids=[
2792
+ str(item.get("client_message_id") or "").strip()
2793
+ for item in recent_inbound_messages
2794
+ if str(item.get("client_message_id") or "").strip()
2795
+ ],
2796
+ scheduled=bool(scheduling.get("scheduled")),
2797
+ started=bool(scheduling.get("started")),
2798
+ queued=bool(scheduling.get("queued")),
2799
+ interrupted=bool(restart.get("interrupted")),
2800
+ )
2801
+ return {
2802
+ "ok": True,
2803
+ "status": "scheduled",
2804
+ "quest_id": quest_id,
2805
+ "snapshot": self.quest_service.snapshot(quest_id),
2806
+ "message_ids": [
2807
+ str(item.get("message_id") or "").strip()
2808
+ for item in recent_inbound_messages
2809
+ if str(item.get("message_id") or "").strip()
2810
+ ],
2811
+ "client_message_ids": [
2812
+ str(item.get("client_message_id") or "").strip()
2813
+ for item in recent_inbound_messages
2814
+ if str(item.get("client_message_id") or "").strip()
2815
+ ],
2816
+ "message_states": message_states,
2817
+ "current_message_state": message_states[0] if message_states else None,
2818
+ "delivery_batch": mailbox_payload.get("delivery_batch"),
2819
+ "recent_inbound_messages": recent_inbound_messages,
2820
+ "interrupted": bool(restart.get("interrupted")),
2821
+ "stopped_bash_session_ids": list(restart.get("stopped_bash_session_ids") or []),
2822
+ "message": "Queued user messages were forced into an immediate-read turn.",
2823
+ **scheduling,
2824
+ }
2825
+
2826
+ def withdraw_queued_user_message(
2827
+ self,
2828
+ quest_id: str,
2829
+ *,
2830
+ message_id: str,
2831
+ source: str = "local",
2832
+ ) -> dict[str, Any]:
2833
+ quest_root = self.quest_service._quest_root(quest_id)
2834
+ result = self.quest_service.withdraw_pending_user_message(
2835
+ quest_id,
2836
+ message_id=message_id,
2837
+ source=source,
2838
+ )
2839
+ result.setdefault("quest_id", quest_id)
2840
+ result["snapshot"] = self.quest_service.snapshot(quest_id)
2841
+ return result
2842
+
2285
2843
  def _stop_active_bash_exec_sessions(
2286
2844
  self,
2287
2845
  quest_id: str,
@@ -2345,6 +2903,23 @@ class DaemonApp:
2345
2903
  last_recovery_abandoned_run_id=recovery_abandoned_run_id,
2346
2904
  last_recovery_summary=recovery_summary,
2347
2905
  )
2906
+ scheduling: dict[str, Any] | None = None
2907
+ if not source.startswith("auto:"):
2908
+ if int(snapshot.get("pending_user_message_count") or 0) > 0:
2909
+ scheduling = self.schedule_turn(quest_id, reason="queued_user_messages")
2910
+ else:
2911
+ continue_payload = self.submit_user_message(
2912
+ quest_id,
2913
+ text=_RESUME_CONTINUE_TEXT,
2914
+ source=self._resume_continue_source(source),
2915
+ )
2916
+ scheduling = {
2917
+ "scheduled": bool(continue_payload.get("scheduled")),
2918
+ "started": bool(continue_payload.get("started")),
2919
+ "queued": bool(continue_payload.get("queued")),
2920
+ "reason": str(continue_payload.get("reason") or "user_message"),
2921
+ "resume_trigger_message": continue_payload.get("message"),
2922
+ }
2348
2923
  summary = f"Quest {quest_id} resumed."
2349
2924
  event = self._append_control_event(
2350
2925
  quest_id,
@@ -2373,6 +2948,7 @@ class DaemonApp:
2373
2948
  "message": summary,
2374
2949
  "event": event,
2375
2950
  "notice": notice,
2951
+ **(dict(scheduling or {})),
2376
2952
  }
2377
2953
 
2378
2954
  def _append_control_event(
@@ -2584,8 +3160,17 @@ class DaemonApp:
2584
3160
  runtime_status = str(snapshot.get("runtime_status") or snapshot.get("status") or "").strip()
2585
3161
  if runtime_status in {"stopped", "paused", "completed", "error"} and not snapshot.get("active_run_id"):
2586
3162
  return
2587
- latest_user_message = self._latest_user_message(quest_id)
2588
- if turn_reason != "auto_continue" and latest_user_message is None:
3163
+ quest_root = Path(snapshot["quest_root"])
3164
+ turn_override = (
3165
+ self.quest_service.consume_turn_message_override(
3166
+ quest_root,
3167
+ expected_turn_reason=turn_reason,
3168
+ )
3169
+ if turn_reason == "immediate_read"
3170
+ else None
3171
+ )
3172
+ latest_user_message = self._latest_actionable_user_message(quest_id, turn_reason=turn_reason)
3173
+ if turn_reason not in {"auto_continue", "immediate_read"} and latest_user_message is None:
2589
3174
  return
2590
3175
 
2591
3176
  runner_name = self._runner_name_for(snapshot)
@@ -2597,7 +3182,11 @@ class DaemonApp:
2597
3182
  model = str(runner_cfg.get("model", "gpt-5.4"))
2598
3183
  run_message = ""
2599
3184
  claimed_message_id: str | None = None
2600
- if turn_reason != "auto_continue":
3185
+ if turn_reason == "immediate_read":
3186
+ run_message = str((turn_override or {}).get("message") or "").strip()
3187
+ if not run_message:
3188
+ return
3189
+ elif turn_reason != "auto_continue":
2601
3190
  run_message = str((latest_user_message or {}).get("content") or "").strip()
2602
3191
  claimed_message_id = str((latest_user_message or {}).get("id") or "").strip() or None
2603
3192
  if not run_message:
@@ -2661,7 +3250,6 @@ class DaemonApp:
2661
3250
  max_attempts=max_attempts,
2662
3251
  )
2663
3252
  turn_id = resumed_turn_id or generate_id("turn")
2664
- quest_root = Path(snapshot["quest_root"])
2665
3253
  worktree_root = Path(str(snapshot["current_workspace_root"])) if snapshot.get("current_workspace_root") else None
2666
3254
 
2667
3255
  for attempt_index in range(resumed_start_attempt, max_attempts + 1):
@@ -2807,6 +3395,19 @@ class DaemonApp:
2807
3395
  )
2808
3396
  return
2809
3397
  exhausted_summary = f"{failure_summary} Retry budget exhausted after {attempt_index} attempt(s)."
3398
+ diagnosis = self._runner_failure_diagnosis(
3399
+ runner_name=runner_name,
3400
+ summary=exhausted_summary,
3401
+ stderr_text=str(exc),
3402
+ output_text="",
3403
+ )
3404
+ if diagnosis is not None and diagnosis.retriable:
3405
+ self.quest_service.update_runtime_state(
3406
+ quest_root=quest_root,
3407
+ continuation_policy="wait_for_user_or_resume",
3408
+ continuation_reason=self._retry_exhausted_continuation_reason(diagnosis),
3409
+ continuation_updated_at=utc_now(),
3410
+ )
2810
3411
  self._append_retry_event(
2811
3412
  quest_id,
2812
3413
  event_type="runner.turn_retry_exhausted",
@@ -2819,6 +3420,7 @@ class DaemonApp:
2819
3420
  max_attempts=max_attempts,
2820
3421
  summary=exhausted_summary,
2821
3422
  failure_summary=failure_summary,
3423
+ diagnosis=diagnosis,
2822
3424
  )
2823
3425
  self._record_turn_error(
2824
3426
  quest_id=quest_id,
@@ -2828,6 +3430,8 @@ class DaemonApp:
2828
3430
  model=model,
2829
3431
  summary=exhausted_summary,
2830
3432
  retry_state=None,
3433
+ diagnosis_code=diagnosis.code if diagnosis is not None else None,
3434
+ guidance=list(diagnosis.guidance) if diagnosis is not None else None,
2831
3435
  )
2832
3436
  return
2833
3437
 
@@ -2981,6 +3585,19 @@ class DaemonApp:
2981
3585
  return
2982
3586
 
2983
3587
  exhausted_summary = f"{failure_summary} Retry budget exhausted after {attempt_index} attempt(s)."
3588
+ diagnosis = self._runner_failure_diagnosis(
3589
+ runner_name=runner_name,
3590
+ summary=exhausted_summary,
3591
+ stderr_text=result.stderr_text,
3592
+ output_text=result.output_text,
3593
+ )
3594
+ if diagnosis is not None and diagnosis.retriable:
3595
+ self.quest_service.update_runtime_state(
3596
+ quest_root=quest_root,
3597
+ continuation_policy="wait_for_user_or_resume",
3598
+ continuation_reason=self._retry_exhausted_continuation_reason(diagnosis),
3599
+ continuation_updated_at=utc_now(),
3600
+ )
2984
3601
  self._append_retry_event(
2985
3602
  quest_id,
2986
3603
  event_type="runner.turn_retry_exhausted",
@@ -2993,6 +3610,7 @@ class DaemonApp:
2993
3610
  max_attempts=max_attempts,
2994
3611
  summary=exhausted_summary,
2995
3612
  failure_summary=failure_summary,
3613
+ diagnosis=diagnosis,
2996
3614
  )
2997
3615
  self._record_turn_error(
2998
3616
  quest_id=quest_id,
@@ -3002,6 +3620,8 @@ class DaemonApp:
3002
3620
  model=model,
3003
3621
  summary=exhausted_summary,
3004
3622
  retry_state=None,
3623
+ diagnosis_code=diagnosis.code if diagnosis is not None else None,
3624
+ guidance=list(diagnosis.guidance) if diagnosis is not None else None,
3005
3625
  )
3006
3626
  return
3007
3627
  finally:
@@ -3012,8 +3632,37 @@ class DaemonApp:
3012
3632
  )
3013
3633
 
3014
3634
  def _runner_name_for(self, snapshot: dict) -> str:
3635
+ return self._resolve_enabled_runner_name(snapshot)
3636
+
3637
+ def _resolve_enabled_runner_name(self, snapshot: dict, requested_runner: str | None = None) -> str:
3015
3638
  configured = self.config_manager.load_named("config")
3016
- return str(snapshot.get("runner") or configured.get("default_runner", "codex")).strip().lower()
3639
+ candidates = [
3640
+ requested_runner,
3641
+ snapshot.get("runner"),
3642
+ snapshot.get("default_runner"),
3643
+ configured.get("default_runner", "codex"),
3644
+ "codex",
3645
+ ]
3646
+ enabled: list[str] = []
3647
+ seen_enabled: set[str] = set()
3648
+ for name, cfg in self.runners_config.items():
3649
+ normalized = str(name or "").strip().lower()
3650
+ if not normalized or normalized in seen_enabled:
3651
+ continue
3652
+ seen_enabled.add(normalized)
3653
+ if isinstance(cfg, dict) and cfg.get("enabled") is not False:
3654
+ enabled.append(normalized)
3655
+
3656
+ checked: set[str] = set()
3657
+ for raw in candidates:
3658
+ normalized = str(raw or "").strip().lower()
3659
+ if not normalized or normalized in checked:
3660
+ continue
3661
+ checked.add(normalized)
3662
+ cfg = self.runners_config.get(normalized, {})
3663
+ if isinstance(cfg, dict) and cfg.get("enabled") is not False:
3664
+ return normalized
3665
+ return enabled[0] if enabled else "codex"
3017
3666
 
3018
3667
  @staticmethod
3019
3668
  def _stage_state_fingerprint(snapshot: dict) -> str:
@@ -3087,14 +3736,54 @@ class DaemonApp:
3087
3736
  return value
3088
3737
  return "autonomous"
3089
3738
 
3739
+ @staticmethod
3740
+ def _decision_policy_for(snapshot: dict) -> str:
3741
+ startup_contract = snapshot.get("startup_contract")
3742
+ if isinstance(startup_contract, dict):
3743
+ value = str(startup_contract.get("decision_policy") or "").strip().lower()
3744
+ if value in {"autonomous", "user_gated"}:
3745
+ return value
3746
+ return "user_gated"
3747
+
3748
+ def _auto_resume_wait_if_allowed(self, quest_id: str, snapshot: dict) -> tuple[dict, bool]:
3749
+ if str(snapshot.get("continuation_policy") or "").strip().lower() != "wait_for_user_or_resume":
3750
+ return snapshot, False
3751
+ if self._decision_policy_for(snapshot) != "autonomous":
3752
+ return snapshot, False
3753
+ reason = str(snapshot.get("continuation_reason") or "").strip() or "wait_for_user_or_resume"
3754
+ if reason in AUTONOMOUS_BLOCKING_WAIT_REASONS:
3755
+ return snapshot, False
3756
+ quest_root = self.quest_service._quest_root(quest_id)
3757
+ message = self.quest_service.localized_copy(
3758
+ quest_root=quest_root,
3759
+ zh=f"【自动继续】系统检测到当前为无需询问模式,本次“等待反馈”已自动转换为继续执行。原因:{reason}。",
3760
+ en=f"[Auto-resumed] This quest is in autonomous decision mode, so the waiting state was converted back to automatic continuation. Reason: {reason}.",
3761
+ )
3762
+ notice = {
3763
+ "status": "auto_resumed",
3764
+ "reason": reason,
3765
+ "message": message,
3766
+ "decision_policy": "autonomous",
3767
+ "label": self.quest_service.localized_copy(quest_root=quest_root, zh="自动继续", en="Auto-resumed"),
3768
+ "created_at": utc_now(),
3769
+ }
3770
+ self.quest_service.update_runtime_state(
3771
+ quest_root=quest_root,
3772
+ continuation_policy="auto",
3773
+ continuation_reason=f"{reason}_auto_resumed",
3774
+ waiting_notice=notice,
3775
+ )
3776
+ snapshot = self.quest_service.snapshot(quest_id)
3777
+ return snapshot, True
3778
+
3090
3779
  def _resolve_continuation_policy(self, snapshot: dict, *, current_policy: str) -> tuple[str, str]:
3091
3780
  normalized = str(current_policy or "auto").strip().lower() or "auto"
3092
3781
  if normalized != "auto":
3093
3782
  return normalized, str(snapshot.get("continuation_reason") or "").strip() or "explicit_continuation_policy"
3094
- if self._workspace_mode_for(snapshot) == "copilot":
3095
- return "wait_for_user_or_resume", "copilot_mode"
3096
3783
  if self._has_external_progress(snapshot):
3097
3784
  return "when_external_progress", "background_external_progress_active"
3785
+ if self._workspace_mode_for(snapshot) == "copilot":
3786
+ return "wait_for_user_or_resume", "copilot_mode"
3098
3787
  return "auto", "autonomous_prepare_or_launch_long_run"
3099
3788
 
3100
3789
  @staticmethod
@@ -3127,13 +3816,24 @@ class DaemonApp:
3127
3816
  @staticmethod
3128
3817
  def _direct_user_turn_skill(snapshot: dict) -> str:
3129
3818
  available_stage_skills = current_standard_skills(repo_root())
3819
+ workspace_mode = DaemonApp._workspace_mode_for(snapshot)
3820
+ has_explicit_baseline_context = bool(
3821
+ isinstance(snapshot.get("confirmed_baseline_ref"), dict)
3822
+ or isinstance(snapshot.get("requested_baseline_ref"), dict)
3823
+ or str(snapshot.get("active_baseline_id") or "").strip()
3824
+ )
3130
3825
  for candidate in (
3131
3826
  str(snapshot.get("active_anchor") or "").strip(),
3132
3827
  str(snapshot.get("continuation_anchor") or "").strip(),
3133
3828
  ):
3134
3829
  if candidate in available_stage_skills and candidate != "decision":
3830
+ if workspace_mode == "copilot" and candidate == "baseline" and not has_explicit_baseline_context:
3831
+ continue
3135
3832
  return DaemonApp._turn_skill_stage_gate(snapshot, candidate)
3136
- fallback = "baseline" if "baseline" in available_stage_skills else "scout"
3833
+ if workspace_mode == "copilot" and "scout" in available_stage_skills:
3834
+ fallback = "scout"
3835
+ else:
3836
+ fallback = "baseline" if "baseline" in available_stage_skills else "scout"
3137
3837
  return DaemonApp._turn_skill_stage_gate(snapshot, fallback)
3138
3838
 
3139
3839
  @staticmethod
@@ -3208,6 +3908,14 @@ class DaemonApp:
3208
3908
  return item
3209
3909
  return None
3210
3910
 
3911
+ def _latest_actionable_user_message(self, quest_id: str, *, turn_reason: str) -> dict | None:
3912
+ normalized_reason = str(turn_reason or "").strip() or "user_message"
3913
+ if normalized_reason in {"user_message", "queued_user_messages"}:
3914
+ pending_message = self.quest_service.latest_pending_user_message(quest_id)
3915
+ if pending_message is not None:
3916
+ return pending_message
3917
+ return self._latest_user_message(quest_id)
3918
+
3211
3919
  @staticmethod
3212
3920
  def _runner_binary_issue(runner_name: str, runner: object) -> str | None:
3213
3921
  binary = getattr(runner, "binary", None)
@@ -3286,6 +3994,14 @@ class DaemonApp:
3286
3994
  initial_backoff = CODEX_RETRY_DEFAULT_INITIAL_BACKOFF_SEC
3287
3995
  multiplier = CODEX_RETRY_DEFAULT_BACKOFF_MULTIPLIER
3288
3996
  max_backoff = CODEX_RETRY_DEFAULT_MAX_BACKOFF_SEC
3997
+ if (
3998
+ runner_name == "codex"
3999
+ and max_attempts == PREVIOUS_CODEX_RETRY_DEFAULT_MAX_ATTEMPTS
4000
+ and self._float_matches(initial_backoff, CODEX_RETRY_DEFAULT_INITIAL_BACKOFF_SEC)
4001
+ and self._float_matches(multiplier, CODEX_RETRY_DEFAULT_BACKOFF_MULTIPLIER)
4002
+ and self._float_matches(max_backoff, CODEX_RETRY_DEFAULT_MAX_BACKOFF_SEC)
4003
+ ):
4004
+ max_attempts = CODEX_RETRY_DEFAULT_MAX_ATTEMPTS
3289
4005
  return {
3290
4006
  "enabled": enabled,
3291
4007
  "max_attempts": max_attempts,
@@ -3344,6 +4060,7 @@ class DaemonApp:
3344
4060
  backoff_seconds: float | None = None,
3345
4061
  next_attempt_index: int | None = None,
3346
4062
  previous_run_id: str | None = None,
4063
+ diagnosis: FailureDiagnosis | None = None,
3347
4064
  ) -> dict[str, Any]:
3348
4065
  payload = {
3349
4066
  "event_id": generate_id("evt"),
@@ -3367,6 +4084,9 @@ class DaemonApp:
3367
4084
  payload["next_attempt_index"] = next_attempt_index
3368
4085
  if previous_run_id:
3369
4086
  payload["previous_run_id"] = previous_run_id
4087
+ if diagnosis is not None:
4088
+ payload["diagnosis_code"] = diagnosis.code
4089
+ payload["diagnosis"] = self._failure_diagnosis_payload(diagnosis)
3370
4090
  append_jsonl(self.home / "quests" / quest_id / ".ds" / "events.jsonl", payload)
3371
4091
  self.logger.log(
3372
4092
  "warning" if "scheduled" in event_type or "exhausted" in event_type else "info",
@@ -3380,6 +4100,7 @@ class DaemonApp:
3380
4100
  backoff_seconds=backoff_seconds,
3381
4101
  next_attempt_index=next_attempt_index,
3382
4102
  previous_run_id=previous_run_id,
4103
+ diagnosis_code=diagnosis.code if diagnosis is not None else None,
3383
4104
  )
3384
4105
  return payload
3385
4106
 
@@ -3526,10 +4247,17 @@ class DaemonApp:
3526
4247
  active_run_id=None,
3527
4248
  retry_state=retry_state,
3528
4249
  )
3529
- notice_message = summary
4250
+ runner_label = self._connector_runner_label(runner_name)
4251
+ user_notice = self._polite_copy(
4252
+ zh=f"DeepScientist 意外停止运行,请检查你所绑定的{runner_label}是否能够顺利连接。",
4253
+ en=f"DeepScientist stopped unexpectedly. Please check whether the bound {runner_label} can connect normally.",
4254
+ )
4255
+ notice_message = "\n".join([user_notice, "", summary]).strip()
3530
4256
  if normalized_guidance:
3531
4257
  notice_message = "\n".join(
3532
4258
  [
4259
+ user_notice,
4260
+ "",
3533
4261
  summary,
3534
4262
  "",
3535
4263
  "Suggested fix:",
@@ -3564,6 +4292,34 @@ class DaemonApp:
3564
4292
  ],
3565
4293
  )
3566
4294
 
4295
+ @staticmethod
4296
+ def _failure_diagnosis_payload(diagnosis: FailureDiagnosis) -> dict[str, Any]:
4297
+ fix = [str(line) for line in diagnosis.guidance if str(line).strip()]
4298
+ return {
4299
+ "code": diagnosis.code,
4300
+ "problem": diagnosis.problem,
4301
+ "why": diagnosis.why,
4302
+ "fix": fix,
4303
+ "guidance": fix,
4304
+ "retriable": bool(diagnosis.retriable),
4305
+ "matched_text": diagnosis.matched_text,
4306
+ }
4307
+
4308
+ @staticmethod
4309
+ def _runner_failure_diagnosis(
4310
+ *,
4311
+ runner_name: str,
4312
+ summary: str,
4313
+ stderr_text: str,
4314
+ output_text: str,
4315
+ ) -> FailureDiagnosis | None:
4316
+ return diagnose_runner_failure(
4317
+ runner_name=runner_name,
4318
+ summary=summary,
4319
+ stderr_text=stderr_text,
4320
+ output_text=output_text,
4321
+ )
4322
+
3567
4323
  @staticmethod
3568
4324
  def _non_retryable_failure_diagnosis(
3569
4325
  *,
@@ -3572,7 +4328,7 @@ class DaemonApp:
3572
4328
  stderr_text: str,
3573
4329
  output_text: str,
3574
4330
  ) -> FailureDiagnosis | None:
3575
- diagnosis = diagnose_runner_failure(
4331
+ diagnosis = DaemonApp._runner_failure_diagnosis(
3576
4332
  runner_name=runner_name,
3577
4333
  summary=summary,
3578
4334
  stderr_text=stderr_text,
@@ -3582,6 +4338,12 @@ class DaemonApp:
3582
4338
  return None
3583
4339
  return diagnosis
3584
4340
 
4341
+ @staticmethod
4342
+ def _retry_exhausted_continuation_reason(diagnosis: FailureDiagnosis) -> str:
4343
+ if diagnosis.code == "codex_upstream_provider_error":
4344
+ return "external_codex_upstream_provider_error"
4345
+ return "runner_retry_budget_exhausted"
4346
+
3585
4347
  def _record_turn_postprocess_warning(
3586
4348
  self,
3587
4349
  *,
@@ -3713,11 +4475,13 @@ class DaemonApp:
3713
4475
  self.schedule_turn(quest_id, reason="queued_user_messages")
3714
4476
  else:
3715
4477
  continuation_policy = str(snapshot.get("continuation_policy") or "auto").strip().lower() or "auto"
4478
+ resolved_external_progress = False
3716
4479
  if continuation_policy == "auto":
3717
4480
  continuation_policy, continuation_reason = self._resolve_continuation_policy(
3718
4481
  snapshot,
3719
4482
  current_policy=continuation_policy,
3720
4483
  )
4484
+ resolved_external_progress = continuation_policy == "when_external_progress"
3721
4485
  self.quest_service.update_runtime_state(
3722
4486
  quest_root=self.quest_service._quest_root(quest_id),
3723
4487
  continuation_policy=continuation_policy,
@@ -3735,12 +4499,22 @@ class DaemonApp:
3735
4499
  if int(snapshot.get("pending_user_message_count") or 0) > 0:
3736
4500
  self.schedule_turn(quest_id, reason="queued_user_messages")
3737
4501
  return
4502
+ snapshot, auto_resumed_wait = self._auto_resume_wait_if_allowed(quest_id, snapshot)
4503
+ if auto_resumed_wait:
4504
+ self._schedule_turn_later(
4505
+ quest_id,
4506
+ reason="auto_continue",
4507
+ delay_seconds=self._auto_continue_delay_for_policy("auto"),
4508
+ )
4509
+ return
3738
4510
  continuation_policy = str(snapshot.get("continuation_policy") or "auto").strip().lower() or "auto"
4511
+ resolved_external_progress = False
3739
4512
  if continuation_policy == "auto":
3740
4513
  continuation_policy, continuation_reason = self._resolve_continuation_policy(
3741
4514
  snapshot,
3742
4515
  current_policy=continuation_policy,
3743
4516
  )
4517
+ resolved_external_progress = continuation_policy == "when_external_progress"
3744
4518
  self.quest_service.update_runtime_state(
3745
4519
  quest_root=self.quest_service._quest_root(quest_id),
3746
4520
  continuation_policy=continuation_policy,
@@ -3753,7 +4527,7 @@ class DaemonApp:
3753
4527
  if continuation_policy == "wait_for_user_or_resume":
3754
4528
  return
3755
4529
  if continuation_policy == "when_external_progress":
3756
- if not self._has_external_progress(snapshot):
4530
+ if not resolved_external_progress and not self._has_external_progress(snapshot):
3757
4531
  next_policy = "wait_for_user_or_resume" if self._workspace_mode_for(snapshot) == "copilot" else "auto"
3758
4532
  next_reason = "external_progress_finished" if next_policy == "wait_for_user_or_resume" else "external_progress_finished_continue_autonomous"
3759
4533
  self.quest_service.update_runtime_state(
@@ -3785,11 +4559,13 @@ class DaemonApp:
3785
4559
  if status in {"completed", "paused", "stopped", "error", "waiting_for_user"}:
3786
4560
  return
3787
4561
  continuation_policy = str(snapshot.get("continuation_policy") or "auto").strip().lower() or "auto"
4562
+ resolved_external_progress = False
3788
4563
  if continuation_policy == "auto":
3789
4564
  continuation_policy, continuation_reason = self._resolve_continuation_policy(
3790
4565
  snapshot,
3791
4566
  current_policy=continuation_policy,
3792
4567
  )
4568
+ resolved_external_progress = continuation_policy == "when_external_progress"
3793
4569
  self.quest_service.update_runtime_state(
3794
4570
  quest_root=self.quest_service._quest_root(quest_id),
3795
4571
  continuation_policy=continuation_policy,
@@ -3797,10 +4573,15 @@ class DaemonApp:
3797
4573
  continuation_updated_at=utc_now(),
3798
4574
  )
3799
4575
  snapshot = self.quest_service.snapshot(quest_id)
4576
+ snapshot, auto_resumed_wait = self._auto_resume_wait_if_allowed(quest_id, snapshot)
4577
+ if auto_resumed_wait:
4578
+ self.schedule_turn(quest_id, reason=reason)
4579
+ return
4580
+ continuation_policy = str(snapshot.get("continuation_policy") or "auto").strip().lower() or "auto"
3800
4581
  if continuation_policy in {"none", "wait_for_user_or_resume"}:
3801
4582
  return
3802
4583
  if continuation_policy == "when_external_progress":
3803
- if not self._has_external_progress(snapshot):
4584
+ if not resolved_external_progress and not self._has_external_progress(snapshot):
3804
4585
  next_policy = "wait_for_user_or_resume" if self._workspace_mode_for(snapshot) == "copilot" else "auto"
3805
4586
  next_reason = "external_progress_finished" if next_policy == "wait_for_user_or_resume" else "external_progress_finished_continue_autonomous"
3806
4587
  self.quest_service.update_runtime_state(
@@ -3824,16 +4605,18 @@ class DaemonApp:
3824
4605
  quest_id = str(snapshot.get("quest_id") or "").strip()
3825
4606
  if not quest_id:
3826
4607
  return False
4608
+ counts = snapshot.get("counts") if isinstance(snapshot.get("counts"), dict) else {}
3827
4609
  try:
3828
4610
  quest_root = self.quest_service._quest_root(quest_id)
3829
4611
  except FileNotFoundError:
3830
- return False
4612
+ return int(counts.get("bash_running_count") or 0) > 0
3831
4613
  try:
3832
4614
  sessions = self.bash_exec_service.list_sessions(quest_root, limit=200)
3833
- return any(str(item.get("status") or "").strip().lower() == "running" for item in sessions if isinstance(item, dict))
4615
+ if any(str(item.get("status") or "").strip().lower() == "running" for item in sessions if isinstance(item, dict)):
4616
+ return True
3834
4617
  except Exception:
3835
- counts = snapshot.get("counts") if isinstance(snapshot.get("counts"), dict) else {}
3836
- return int(counts.get("bash_running_count") or 0) > 0
4618
+ pass
4619
+ return int(counts.get("bash_running_count") or 0) > 0
3837
4620
 
3838
4621
  def _relay_quest_message_to_bound_connectors(
3839
4622
  self,
@@ -3891,6 +4674,7 @@ class DaemonApp:
3891
4674
  self._shutdown_requested.set()
3892
4675
  self._stop_background_connectors()
3893
4676
  self._stop_terminal_attach_server()
4677
+ self.admin_service.system_monitor.stop()
3894
4678
  self.bash_exec_service.shutdown()
3895
4679
  self.logger.log(
3896
4680
  "info",
@@ -4639,10 +5423,21 @@ class DaemonApp:
4639
5423
  removed_conversations = self._unbind_external_bindings(quest_id)
4640
5424
  self.sessions.forget(quest_id)
4641
5425
 
4642
- try:
4643
- shutil.rmtree(quest_root)
4644
- except FileNotFoundError:
4645
- return {"ok": True, "quest_id": quest_id, "deleted": False}
5426
+ last_delete_error: OSError | None = None
5427
+ for attempt in range(5):
5428
+ try:
5429
+ shutil.rmtree(quest_root)
5430
+ last_delete_error = None
5431
+ break
5432
+ except FileNotFoundError:
5433
+ return {"ok": True, "quest_id": quest_id, "deleted": False}
5434
+ except OSError as exc:
5435
+ last_delete_error = exc
5436
+ if exc.errno not in {errno.ENOTEMPTY, errno.EBUSY} or attempt == 4:
5437
+ raise
5438
+ time.sleep(0.05 * (attempt + 1))
5439
+ if last_delete_error is not None and quest_root.exists():
5440
+ raise last_delete_error
4646
5441
 
4647
5442
  self.logger.log(
4648
5443
  "info",
@@ -4757,11 +5552,17 @@ class DaemonApp:
4757
5552
  exclude_conversation_id=conversation_id,
4758
5553
  )
4759
5554
  self.update_quest_binding(created["quest_id"], binding_conversation_id, force=True)
4760
- self.submit_user_message(
5555
+ startup = self.submit_user_message(
4761
5556
  created["quest_id"],
4762
5557
  text=goal_text,
4763
5558
  source=conversation_id,
4764
5559
  )
5560
+ created_snapshot = self.quest_service.snapshot(created["quest_id"])
5561
+ startup_message = self._connector_turn_start_message(
5562
+ quest_id=created["quest_id"],
5563
+ startup=startup,
5564
+ snapshot=created_snapshot,
5565
+ )
4765
5566
  return channel.send(
4766
5567
  {
4767
5568
  "conversation_id": conversation_id,
@@ -4774,7 +5575,9 @@ class DaemonApp:
4774
5575
  quest_id=created["quest_id"],
4775
5576
  goal=goal_text,
4776
5577
  previous_quest_id=previous_quest_id,
4777
- ),
5578
+ )
5579
+ + "\n"
5580
+ + startup_message,
4778
5581
  ),
4779
5582
  }
4780
5583
  )
@@ -5183,7 +5986,7 @@ class DaemonApp:
5183
5986
  message_id=str(message.get("message_id") or "").strip() or None,
5184
5987
  attachments=[dict(item) for item in (message.get("attachments") or []) if isinstance(item, dict)],
5185
5988
  )
5186
- self.submit_user_message(
5989
+ startup = self.submit_user_message(
5187
5990
  quest_id,
5188
5991
  text=self._connector_message_text_with_attachment_notice(
5189
5992
  original_text=text,
@@ -5192,18 +5995,18 @@ class DaemonApp:
5192
5995
  source=conversation_id,
5193
5996
  attachments=materialized_attachments,
5194
5997
  )
5998
+ snapshot = self.quest_service.snapshot(quest_id)
5999
+ startup_message = self._connector_turn_start_message(
6000
+ quest_id=quest_id,
6001
+ startup=startup,
6002
+ snapshot=snapshot,
6003
+ )
5195
6004
  return channel.send(
5196
6005
  {
5197
6006
  "conversation_id": conversation_id,
5198
6007
  "quest_id": quest_id,
5199
6008
  "kind": "ack",
5200
- "message": self._with_qq_main_chat_notice(
5201
- message,
5202
- self._polite_copy(
5203
- zh=f"老师,已收到您的消息,当前会话已绑定到 {quest_id}。我会继续推进,并通过后续消息同步进展。",
5204
- en=f"Received. This conversation is bound to {quest_id}; I’ll continue the work and keep you updated with the next progress checkpoint.",
5205
- ),
5206
- ),
6009
+ "message": self._with_qq_main_chat_notice(message, startup_message),
5207
6010
  }
5208
6011
  )
5209
6012
 
@@ -6196,6 +6999,78 @@ class DaemonApp:
6196
6999
  en="I will clarify the current task first, then keep moving. ✨",
6197
7000
  )
6198
7001
 
7002
+ def _connector_runner_label(self, runner_name: str) -> str:
7003
+ normalized = str(runner_name or "codex").strip().lower() or "codex"
7004
+ if normalized == "claude":
7005
+ return "Claude Code"
7006
+ if normalized == "opencode":
7007
+ return "Open Code"
7008
+ try:
7009
+ metadata = get_runner_metadata(normalized)
7010
+ except Exception:
7011
+ return runner_name or "Codex"
7012
+ return str(metadata.label or runner_name or "Codex")
7013
+
7014
+ def _connector_runner_readiness(self, runner_name: str) -> dict[str, Any]:
7015
+ try:
7016
+ state = self.config_manager.runner_bootstrap_state(runner_name)
7017
+ except Exception:
7018
+ return {"runner": runner_name, "ready": True, "last_result": {}}
7019
+ if not isinstance(state, dict):
7020
+ return {"runner": runner_name, "ready": True, "last_result": {}}
7021
+ return state
7022
+
7023
+ def _connector_turn_start_message(
7024
+ self,
7025
+ *,
7026
+ quest_id: str,
7027
+ startup: dict[str, Any],
7028
+ snapshot: dict[str, Any],
7029
+ ) -> str:
7030
+ runner_name = self._runner_name_for(snapshot)
7031
+ runner_label = self._connector_runner_label(runner_name)
7032
+ readiness = self._connector_runner_readiness(runner_name)
7033
+ readiness_result = readiness.get("last_result") if isinstance(readiness.get("last_result"), dict) else {}
7034
+ readiness_summary = str(readiness_result.get("summary") or "").strip()
7035
+ readiness_errors = [str(item).strip() for item in (readiness_result.get("errors") or []) if str(item).strip()]
7036
+ readiness_guidance = [str(item).strip() for item in (readiness_result.get("guidance") or []) if str(item).strip()]
7037
+ ready = bool(readiness.get("ready", True))
7038
+ reason = str(startup.get("reason") or "").strip().lower()
7039
+ started = bool(startup.get("started"))
7040
+ queued = bool(startup.get("queued"))
7041
+ auto_resumed = bool(startup.get("auto_resumed"))
7042
+ has_explicit_runner_failure = bool(readiness_summary or readiness_errors or readiness_guidance)
7043
+ if reason == "stalled_turn_recovery_pending":
7044
+ return self._polite_copy(
7045
+ zh=f"正在启动 DeepScientist,当前会先恢复 `{quest_id}` 里停滞的运行,再继续处理您的新消息。",
7046
+ en=f"DeepScientist is starting up. I’m recovering the stalled run in `{quest_id}` first, then I’ll continue with your new message.",
7047
+ )
7048
+ if not ready and has_explicit_runner_failure:
7049
+ status_line = f" 当前状态:{readiness_summary}" if readiness_summary else ""
7050
+ return self._polite_copy(
7051
+ zh=(
7052
+ f"DeepScientist 仍然不在线哟,请检查你所绑定的{runner_label}是否能够顺利连接。{status_line}"
7053
+ ),
7054
+ en=(
7055
+ f"DeepScientist is still offline. Please check whether the bound {runner_label} can connect normally."
7056
+ f"{(' Current status: ' + readiness_summary) if readiness_summary else ''}"
7057
+ ),
7058
+ )
7059
+ if started or auto_resumed:
7060
+ return self._polite_copy(
7061
+ zh=f"已经成功收到消息,DeepScientist 已经启动并开始处理 `{quest_id}` 啦。",
7062
+ en=f"Message received successfully. DeepScientist has started and is now processing `{quest_id}`.",
7063
+ )
7064
+ if queued:
7065
+ return self._polite_copy(
7066
+ zh=f"已经成功收到消息,`{quest_id}` 当前正在运行中,这条消息也已经排进队列了。",
7067
+ en=f"Message received successfully. `{quest_id}` is already running, and this message has been queued.",
7068
+ )
7069
+ return self._polite_copy(
7070
+ zh=f"已经成功收到消息,我会继续推进 `{quest_id}` 并同步后续进展。",
7071
+ en=f"Message received successfully. I’ll continue `{quest_id}` and keep you updated.",
7072
+ )
7073
+
6199
7074
  def _quest_created_connector_message(
6200
7075
  self,
6201
7076
  connector_name: str,
@@ -7252,6 +8127,10 @@ class DaemonApp:
7252
8127
  "event_id": event.get("event_id") or f"evt-{quest_id}-{current_cursor}",
7253
8128
  **event,
7254
8129
  }
8130
+ enriched_event = self.quest_service.enrich_conversation_message_event(
8131
+ quest_id,
8132
+ enriched_event,
8133
+ )
7255
8134
  update = build_session_update(
7256
8135
  enriched_event,
7257
8136
  quest_id=quest_id,
@@ -7332,6 +8211,75 @@ class DaemonApp:
7332
8211
  except (BrokenPipeError, ConnectionResetError, TimeoutError):
7333
8212
  return
7334
8213
 
8214
+ def stream_admin_task(
8215
+ self,
8216
+ handler: BaseHTTPRequestHandler,
8217
+ *,
8218
+ task_id: str,
8219
+ headers: dict[str, str] | None = None,
8220
+ extra_headers: dict[str, str] | None = None,
8221
+ ) -> None:
8222
+ try:
8223
+ task = self.admin_task_service.get_task(task_id)
8224
+ except FileNotFoundError:
8225
+ self._write_handler_response(
8226
+ handler,
8227
+ code=404,
8228
+ content=json.dumps(
8229
+ {"ok": False, "message": f"Unknown admin task `{task_id}`."},
8230
+ ensure_ascii=False,
8231
+ ).encode("utf-8"),
8232
+ content_type="application/json; charset=utf-8",
8233
+ extra_headers=extra_headers,
8234
+ )
8235
+ return
8236
+
8237
+ last_event_raw = str((headers or {}).get("Last-Event-ID") or (headers or {}).get("last-event-id") or "").strip()
8238
+ cursor = int(last_event_raw) if last_event_raw.isdigit() else 0
8239
+
8240
+ handler.send_response(200)
8241
+ handler.send_header("Content-Type", "text/event-stream; charset=utf-8")
8242
+ handler.send_header("Cache-Control", "no-cache, no-transform")
8243
+ handler.send_header("Connection", "keep-alive")
8244
+ handler.send_header("X-Accel-Buffering", "no")
8245
+ for key, value in (extra_headers or {}).items():
8246
+ handler.send_header(key, value)
8247
+ handler.end_headers()
8248
+ handler.wfile.write(b"retry: 1000\n\n")
8249
+ handler.wfile.flush()
8250
+
8251
+ self._write_sse_event(
8252
+ handler,
8253
+ event="task.snapshot",
8254
+ data={"task": task},
8255
+ )
8256
+ heartbeat_at = time.monotonic()
8257
+ terminal_statuses = {"completed", "failed", "cancelled"}
8258
+ try:
8259
+ while True:
8260
+ current_task = self.admin_task_service.get_task(task_id)
8261
+ events = self.admin_task_service.read_events(task_id, after=cursor, limit=200)
8262
+ if events:
8263
+ for item in events:
8264
+ cursor = max(cursor, int(item.get("seq") or 0))
8265
+ self._write_sse_event(
8266
+ handler,
8267
+ event=str(item.get("event") or "task.log"),
8268
+ data=item,
8269
+ event_id=str(cursor),
8270
+ )
8271
+ heartbeat_at = time.monotonic()
8272
+ elif time.monotonic() - heartbeat_at >= 10:
8273
+ handler.wfile.write(b": keep-alive\n\n")
8274
+ handler.wfile.flush()
8275
+ heartbeat_at = time.monotonic()
8276
+ current_status = str(current_task.get("status") or "").strip().lower()
8277
+ if current_status in terminal_statuses and cursor >= int(current_task.get("last_event_seq") or 0):
8278
+ return
8279
+ time.sleep(0.2 if current_status == "running" else 0.6)
8280
+ except (BrokenPipeError, ConnectionResetError, TimeoutError):
8281
+ return
8282
+
7335
8283
  def stream_bash_sessions(
7336
8284
  self,
7337
8285
  handler: BaseHTTPRequestHandler,
@@ -7651,6 +8599,13 @@ class DaemonApp:
7651
8599
  app = self
7652
8600
 
7653
8601
  class RequestHandler(BaseHTTPRequestHandler):
8602
+ def handle(self) -> None:
8603
+ try:
8604
+ super().handle()
8605
+ except (BrokenPipeError, ConnectionResetError, TimeoutError):
8606
+ self.close_connection = True
8607
+ return
8608
+
7654
8609
  def log_message(self, format: str, *args) -> None:
7655
8610
  return
7656
8611
 
@@ -7694,6 +8649,18 @@ class DaemonApp:
7694
8649
  },
7695
8650
  )
7696
8651
  return
8652
+ if route_name in {"admin_task_stream", "system_task_stream"}:
8653
+ try:
8654
+ app.stream_admin_task(self, **params, headers=request_headers, extra_headers=auth_headers)
8655
+ except Exception as exc:
8656
+ app.logger.log(
8657
+ "error",
8658
+ "http.stream_admin_task_failed",
8659
+ path=self.path,
8660
+ error=str(exc),
8661
+ )
8662
+ self.close_connection = True
8663
+ return
7697
8664
  if route_name == "quest_events" and app._wants_event_stream(self.path, request_headers):
7698
8665
  try:
7699
8666
  app.stream_quest_events(self, **params, path=self.path, headers=request_headers, extra_headers=auth_headers)
@@ -7765,6 +8732,7 @@ class DaemonApp:
7765
8732
 
7766
8733
  try:
7767
8734
  result = getattr(app.handlers, route_name)
8735
+ route_alias = route_name.removeprefix("admin_").removeprefix("system_")
7768
8736
  if route_name == "asset":
7769
8737
  status, headers, content = result(**params)
7770
8738
  app._write_handler_response(
@@ -7774,7 +8742,17 @@ class DaemonApp:
7774
8742
  extra_headers=app._merge_response_headers(headers, auth_headers),
7775
8743
  )
7776
8744
  return
7777
- if route_name in {
8745
+ if route_alias in {
8746
+ "quests",
8747
+ "runtime_sessions",
8748
+ "log_tail",
8749
+ "failures",
8750
+ "errors",
8751
+ "audit",
8752
+ "search",
8753
+ "repairs",
8754
+ "tasks",
8755
+ } or route_name in {
7778
8756
  "quest_events",
7779
8757
  "bash_sessions",
7780
8758
  "bash_logs",
@@ -7797,9 +8775,28 @@ class DaemonApp:
7797
8775
  "annotations_project",
7798
8776
  }:
7799
8777
  payload = result(**params, path=self.path)
8778
+ elif route_name in {
8779
+ "benchstore_entries",
8780
+ "benchstore_entry",
8781
+ "benchstore_entry_image",
8782
+ "benchstore_entry_setup_packet",
8783
+ }:
8784
+ payload = result(**params, path=self.path) if params else result(self.path)
7800
8785
  elif method == "GET":
7801
8786
  payload = result(**params) if params else result()
7802
- elif route_name in {"document_open", "document_asset_upload", "quest_file_create_folder", "quest_file_upload", "quest_file_rename", "quest_file_move", "quest_file_delete", "chat", "command", "quest_control", "config_save", "quest_create", "quest_baseline_binding", "run_create", "qq_inbound", "connector_inbound", "docs_open", "admin_shutdown", "bash_stop", "quest_settings", "quest_bindings", "quest_delete", "quest_layout_update", "terminal_session_ensure", "terminal_attach", "terminal_input", "stage_view", "latex_init", "latex_compile", "system_update_action", "weixin_login_qr_start", "weixin_login_qr_wait", "arxiv_import", "annotation_create", "auth_login", "auth_rotate"}:
8787
+ elif route_alias in {
8788
+ "shutdown",
8789
+ "chart_query",
8790
+ "task_doctor_start",
8791
+ "task_system_update_check_start",
8792
+ "task_system_update_action_start",
8793
+ "issue_draft",
8794
+ "controller_run",
8795
+ "controller_toggle",
8796
+ "repair_create",
8797
+ "repair_close",
8798
+ "hardware_update",
8799
+ } or route_name in {"document_open", "document_asset_upload", "quest_file_create_folder", "quest_file_upload", "quest_file_rename", "quest_file_move", "quest_file_delete", "chat_upload_create", "chat_upload_delete", "chat", "command", "quest_control", "quest_message_read_now", "quest_message_withdraw", "config_save", "quest_create", "quest_baseline_binding", "run_create", "qq_inbound", "connector_inbound", "docs_open", "bash_stop", "quest_settings", "quest_bindings", "quest_delete", "quest_layout_update", "terminal_session_ensure", "terminal_attach", "terminal_input", "stage_view", "latex_init", "latex_compile", "system_update_action", "weixin_login_qr_start", "weixin_login_qr_wait", "arxiv_import", "annotation_create", "auth_login", "auth_rotate"}:
7803
8800
  payload = result(**params, body=body)
7804
8801
  elif route_name == "config_validate":
7805
8802
  payload = result(body)
@@ -7809,6 +8806,8 @@ class DaemonApp:
7809
8806
  payload = result(**params, body=body)
7810
8807
  elif route_name == "memory":
7811
8808
  payload = result(app.handlers.parse_query(self.path))
8809
+ elif route_name == "benchstore_entry_launch":
8810
+ payload = result(**params, path=self.path, body=body)
7812
8811
  else:
7813
8812
  payload = result(**params) if params else result()
7814
8813
  except Exception as exc:
@@ -7856,6 +8855,8 @@ class DaemonApp:
7856
8855
  self._serve_host = host
7857
8856
  self._serve_port = port
7858
8857
  self._shutdown_requested.clear()
8858
+ self.admin_service.system_monitor.start()
8859
+ self.admin_service.metrics_collector.start()
7859
8860
  self._start_terminal_attach_server(host, port)
7860
8861
  self._start_background_connectors()
7861
8862
  self._resume_reconciled_quests()
@@ -7877,6 +8878,8 @@ class DaemonApp:
7877
8878
  finally:
7878
8879
  self._stop_background_connectors()
7879
8880
  self._stop_terminal_attach_server()
8881
+ self.admin_service.metrics_collector.stop()
8882
+ self.admin_service.system_monitor.stop()
7880
8883
  self.bash_exec_service.shutdown()
7881
8884
  self._server = None
7882
8885
  self._serve_host = None