@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
@@ -2,8 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import argparse
4
4
  from collections import deque
5
+ import json
5
6
  from pathlib import Path
7
+ import shlex
8
+ import sys
6
9
  from typing import Any
10
+ from urllib import error as urllib_error
11
+ from urllib import request as urllib_request
7
12
 
8
13
  from mcp.server.fastmcp import FastMCP
9
14
  from mcp.types import ToolAnnotations
@@ -11,21 +16,183 @@ from mcp.types import ToolAnnotations
11
16
  from ..artifact import ArtifactService
12
17
  from ..artifact.metrics import MetricContractValidationError
13
18
  from ..bash_exec import BashExecService
19
+ from ..evidence_packets import cached_compact_mcp_tool_result, compact_mcp_tool_result
14
20
  from ..memory import MemoryService
15
21
  from ..quest import QuestService
22
+ from ..shared import read_json
16
23
  from .context import McpContext
24
+ from .schemas import MetricContractPayload, PrimaryMetricPayload, SupplementaryBaselinePayload
17
25
 
18
26
  DEFAULT_INLINE_BASH_LOG_LINE_LIMIT = 2000
19
27
  DEFAULT_INLINE_BASH_LOG_HEAD_LINES = 500
20
28
  DEFAULT_INLINE_BASH_LOG_TAIL_LINES = 1500
21
29
  DEFAULT_INLINE_BASH_LOG_WINDOW_LINES = 200
22
30
  MAX_INLINE_BASH_LOG_WINDOW_LINES = 2000
31
+ DEFAULT_BASH_EXEC_AWAIT_WAIT_TIMEOUT_SECONDS = 1800
32
+ BASH_EXEC_TERMINAL_STATUSES = {"completed", "failed", "terminated"}
23
33
  INTERACTION_WATCHDOG_TOOL_CALL_THRESHOLD = 25
24
34
  INTERACTION_WATCHDOG_SILENCE_THRESHOLD_SECONDS = 30 * 60
25
35
  LONG_BASH_LOG_HINT = (
26
36
  "Use `bash_exec(mode='read', id=..., start=..., tail=...)` to inspect a specific log window, "
27
37
  "or `bash_exec(mode='read', id=..., tail=...)` to inspect the latest rendered lines."
28
38
  )
39
+
40
+
41
+ def _attach_bash_log_truncation_metadata(payload: dict[str, Any]) -> dict[str, Any]:
42
+ result = dict(payload)
43
+
44
+ log_line_count = result.get("log_line_count")
45
+ latest_seq = result.get("latest_seq")
46
+ tail_start_seq = result.get("tail_start_seq")
47
+ line_start = result.get("line_start")
48
+ line_end = result.get("line_end")
49
+
50
+ def _as_int(value: object) -> int | None:
51
+ if isinstance(value, bool):
52
+ return None
53
+ if isinstance(value, int):
54
+ return value
55
+ try:
56
+ return int(str(value))
57
+ except (TypeError, ValueError):
58
+ return None
59
+
60
+ total_lines = _as_int(log_line_count) or 0
61
+ latest_seq_value = _as_int(latest_seq)
62
+ tail_start_seq_value = _as_int(tail_start_seq)
63
+ line_start_value = _as_int(line_start)
64
+ line_end_value = _as_int(line_end)
65
+
66
+ if result.get("log_truncated") is True:
67
+ head_lines = _as_int(result.get("log_preview_head_lines")) or 0
68
+ tail_lines = _as_int(result.get("log_preview_tail_lines")) or 0
69
+ omitted_lines = _as_int(result.get("log_preview_omitted_lines")) or max(total_lines - head_lines - tail_lines, 0)
70
+ after_preview_lines = tail_lines
71
+ before_preview_lines = head_lines
72
+ result["log_is_partial"] = True
73
+ result["log_visible_line_start"] = 1 if total_lines else 0
74
+ result["log_visible_line_end"] = total_lines if total_lines and total_lines <= head_lines + tail_lines else head_lines + tail_lines
75
+ result["log_lines_before_window"] = 0
76
+ result["log_lines_after_window"] = max(total_lines - tail_lines, 0) if total_lines else 0
77
+ result["seq_has_more_before"] = False
78
+ result["seq_has_more_after"] = omitted_lines > 0
79
+ result["seqs_before_window"] = 0
80
+ result["seqs_after_window"] = omitted_lines
81
+ result["log_truncation_notice"] = (
82
+ "This log payload is truncated to a preview window rather than the full output. "
83
+ f"It shows the first {head_lines} line(s) and the last {tail_lines} line(s), omitting {omitted_lines} middle line(s). "
84
+ "Do not treat this preview as exhaustive evidence. "
85
+ "Use bash_exec(mode='read', id=..., start=..., tail=...) for a specific line window, "
86
+ "or bash_exec(mode='read', id=..., tail_limit=..., before_seq=..., after_seq=...) for seq-based continuation."
87
+ )
88
+ return result
89
+
90
+ if result.get("log_windowed") is True:
91
+ if line_start_value is None:
92
+ line_start_value = 1 if total_lines else 0
93
+ if line_end_value is None:
94
+ line_end_value = line_start_value - 1 if line_start_value else 0
95
+ lines_before = max((line_start_value - 1) if line_start_value else 0, 0)
96
+ lines_after = max(total_lines - max(line_end_value or 0, 0), 0)
97
+ result["log_is_partial"] = bool(result.get("has_more_before")) or bool(result.get("has_more_after"))
98
+ result["log_lines_before_window"] = lines_before
99
+ result["log_lines_after_window"] = lines_after
100
+ result["log_visible_line_start"] = line_start_value
101
+ result["log_visible_line_end"] = line_end_value
102
+
103
+ if tail_start_seq_value is not None:
104
+ seq_window_start = tail_start_seq_value
105
+ seq_window_end = (
106
+ tail_start_seq_value + (_as_int(result.get("returned_line_count")) or 0) - 1
107
+ if (_as_int(result.get("returned_line_count")) or 0) > 0
108
+ else tail_start_seq_value - 1
109
+ )
110
+ else:
111
+ seq_window_start = line_start_value
112
+ seq_window_end = line_end_value
113
+ seqs_before = max((seq_window_start or 0) - 1, 0)
114
+ seqs_after = (
115
+ max(latest_seq_value - max(seq_window_end or 0, 0), 0)
116
+ if latest_seq_value is not None
117
+ else lines_after
118
+ )
119
+ result["seq_window_start"] = seq_window_start
120
+ result["seq_window_end"] = seq_window_end
121
+ result["seq_has_more_before"] = seqs_before > 0
122
+ result["seq_has_more_after"] = seqs_after > 0
123
+ result["seqs_before_window"] = seqs_before
124
+ result["seqs_after_window"] = seqs_after
125
+
126
+ if result["log_is_partial"]:
127
+ result["log_truncation_notice"] = (
128
+ "This log payload is only a partial window, not the full output. "
129
+ f"It currently covers lines {line_start_value} to {line_end_value} out of {total_lines}, "
130
+ f"with {lines_before} line(s) before and {lines_after} line(s) after this window. "
131
+ "If you need adjacent output, continue with bash_exec(mode='read', id=..., start=..., tail=...) "
132
+ "or use the seq-based before_seq / after_seq parameters."
133
+ )
134
+ return result
135
+
136
+ if total_lines > 0:
137
+ result["log_is_partial"] = False
138
+ result["log_lines_before_window"] = 0
139
+ result["log_lines_after_window"] = 0
140
+ if latest_seq_value is not None:
141
+ result["seq_has_more_before"] = False
142
+ result["seq_has_more_after"] = False
143
+ result["seqs_before_window"] = 0
144
+ result["seqs_after_window"] = 0
145
+ result["seq_window_start"] = 1
146
+ result["seq_window_end"] = latest_seq_value
147
+ return result
148
+
149
+
150
+ def _normalize_positive_timeout_seconds(value: Any, *, field_name: str) -> int | None:
151
+ if value is None or value == "":
152
+ return None
153
+ if isinstance(value, bool):
154
+ raise ValueError(f"{field_name} must be a positive integer.")
155
+ try:
156
+ normalized = int(value)
157
+ except (TypeError, ValueError) as exc:
158
+ raise ValueError(f"{field_name} must be a positive integer.") from exc
159
+ if normalized <= 0:
160
+ raise ValueError(f"{field_name} must be a positive integer.")
161
+ return normalized
162
+
163
+
164
+ def _build_bash_exec_wait_notice(
165
+ *,
166
+ bash_id: str,
167
+ wait_timeout_seconds: int,
168
+ status: str,
169
+ ) -> dict[str, Any]:
170
+ suggested_poll_interval_seconds = DEFAULT_BASH_EXEC_AWAIT_WAIT_TIMEOUT_SECONDS
171
+ suggested_sleep_timeout = suggested_poll_interval_seconds + 60
172
+ return {
173
+ "wait_timed_out": True,
174
+ "still_running": status in {"running", "terminating"},
175
+ "wait_timeout_seconds": wait_timeout_seconds,
176
+ "suggested_poll_interval_seconds": suggested_poll_interval_seconds,
177
+ "suggested_read_command": f"bash_exec(mode='read', id='{bash_id}')",
178
+ "suggested_await_command": (
179
+ f"bash_exec(mode='await', id='{bash_id}', "
180
+ f"wait_timeout_seconds={suggested_poll_interval_seconds})"
181
+ ),
182
+ "suggested_sleep_command": (
183
+ f"bash_exec(command='sleep {suggested_poll_interval_seconds}', mode='await', "
184
+ f"timeout_seconds={suggested_sleep_timeout})"
185
+ ),
186
+ "long_wait_notice": (
187
+ "This managed bash_exec session is still running, so the bounded await window ended without final completion. "
188
+ f"Read the saved log with bash_exec(mode='read', id='{bash_id}') and check again in about "
189
+ f"{suggested_poll_interval_seconds} seconds. Prefer "
190
+ f"bash_exec(mode='await', id='{bash_id}', wait_timeout_seconds={suggested_poll_interval_seconds}) "
191
+ "for the next bounded wait on this managed session; if you only need wall-clock waiting between checks, "
192
+ f"you may use bash_exec(command='sleep {suggested_poll_interval_seconds}', mode='await', "
193
+ f"timeout_seconds={suggested_sleep_timeout}) before reading the log again."
194
+ ),
195
+ }
29
196
  ARTIFACT_STATE_CHANGE_WATCHDOG_NOTES = {
30
197
  "confirm_baseline": (
31
198
  "Baseline confirmation changed durable quest state and this tool does not send a user-visible "
@@ -39,6 +206,10 @@ ARTIFACT_STATE_CHANGE_WATCHDOG_NOTES = {
39
206
  "Paper outline state changed durably and this tool does not send a user-visible summary on its own. "
40
207
  "Send one concise artifact.interact(...) update now."
41
208
  ),
209
+ "compile_outline_to_writing_plan": (
210
+ "Paper writing plan state changed durably and this tool does not send a user-visible summary on its own. "
211
+ "Send one concise artifact.interact(...) update now."
212
+ ),
42
213
  "publish_baseline": (
43
214
  "Baseline publication changed durable state and this tool does not send a user-visible summary "
44
215
  "on its own. Send one concise artifact.interact(...) update now."
@@ -53,6 +224,85 @@ ARTIFACT_STATE_CHANGE_WATCHDOG_NOTES = {
53
224
  "received an equivalent completion summary."
54
225
  ),
55
226
  }
227
+ START_SETUP_FORM_FIELDS: tuple[str, ...] = (
228
+ "title",
229
+ "goal",
230
+ "baseline_id",
231
+ "baseline_variant_id",
232
+ "baseline_source_mode",
233
+ "execution_start_mode",
234
+ "baseline_acceptance_target",
235
+ "baseline_urls",
236
+ "paper_urls",
237
+ "runtime_constraints",
238
+ "objectives",
239
+ "need_research_paper",
240
+ "research_intensity",
241
+ "decision_policy",
242
+ "launch_mode",
243
+ "standard_profile",
244
+ "custom_profile",
245
+ "review_followup_policy",
246
+ "baseline_execution_policy",
247
+ "manuscript_edit_mode",
248
+ "entry_state_summary",
249
+ "review_summary",
250
+ "review_materials",
251
+ "custom_brief",
252
+ "user_language",
253
+ )
254
+ START_SETUP_SESSION_FIELDS: tuple[str, ...] = (
255
+ "fit_assessment",
256
+ "recommended_workspace_mode",
257
+ "launch_readiness",
258
+ "missing_confirmations",
259
+ "preview_plan",
260
+ "materials_summary",
261
+ "copilot_handoff",
262
+ "science_task",
263
+ "science_task_brief",
264
+ "science_package_cards",
265
+ )
266
+
267
+
268
+ def _split_nested_start_setup_payload(payload: dict[str, Any] | None) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
269
+ if not isinstance(payload, dict):
270
+ return None, None
271
+ form_patch = payload.get("form_patch") if isinstance(payload.get("form_patch"), dict) else None
272
+ session_patch = payload.get("session_patch") if isinstance(payload.get("session_patch"), dict) else None
273
+ suggested_form = payload.get("suggested_form") if isinstance(payload.get("suggested_form"), dict) else None
274
+ if isinstance(suggested_form, dict):
275
+ if form_patch is None and isinstance(suggested_form.get("form_patch"), dict):
276
+ form_patch = suggested_form.get("form_patch")
277
+ if session_patch is None and isinstance(suggested_form.get("session_patch"), dict):
278
+ session_patch = suggested_form.get("session_patch")
279
+ return form_patch, session_patch
280
+
281
+
282
+ def _normalize_bash_exec_command_input(raw_command: Any) -> str:
283
+ if isinstance(raw_command, str):
284
+ return raw_command
285
+ if isinstance(raw_command, (list, tuple)):
286
+ items = [str(item).strip() for item in raw_command if str(item).strip()]
287
+ if not items:
288
+ return ""
289
+ if len(items) == 1:
290
+ return items[0]
291
+ return shlex.join(items)
292
+ return str(raw_command or "")
293
+
294
+
295
+ def _normalize_bash_exec_command_input(raw_command: Any) -> str:
296
+ if isinstance(raw_command, str):
297
+ return raw_command
298
+ if isinstance(raw_command, (list, tuple)):
299
+ items = [str(item).strip() for item in raw_command if str(item).strip()]
300
+ if not items:
301
+ return ""
302
+ if len(items) == 1:
303
+ return items[0]
304
+ return shlex.join(items)
305
+ return str(raw_command or "")
56
306
 
57
307
 
58
308
  def _read_only_tool_annotations(*, title: str | None = None) -> ToolAnnotations:
@@ -69,6 +319,158 @@ def _metric_validation_error_payload(exc: MetricContractValidationError) -> dict
69
319
  return exc.as_payload()
70
320
 
71
321
 
322
+ def _artifact_call(name: str, why: str) -> dict[str, str]:
323
+ return {"name": name, "why": why}
324
+
325
+
326
+ def _artifact_guided_error_payload(
327
+ service: ArtifactService,
328
+ quest_root: Path,
329
+ *,
330
+ tool_name: str,
331
+ exc: Exception,
332
+ ) -> dict[str, Any]:
333
+ message = str(exc).strip() or f"`artifact.{tool_name}` failed."
334
+ guidance: list[str] = []
335
+ suggested_artifact_calls: list[dict[str, str]] = []
336
+ extra: dict[str, Any] = {}
337
+
338
+ if "selected_outline_ref" in message or "No selected outline is available" in message:
339
+ outlines = service.list_paper_outlines(quest_root)
340
+ outline_items = [dict(item) for item in (outlines.get("outlines") or []) if isinstance(item, dict)]
341
+ candidate_ids = [
342
+ str(item.get("outline_id") or "").strip()
343
+ for item in outline_items
344
+ if str(item.get("status") or "").strip() in {"candidate", "revised"}
345
+ and str(item.get("outline_id") or "").strip()
346
+ ]
347
+ extra["outline_candidates"] = outline_items
348
+ if candidate_ids:
349
+ guidance.append(f"Select a candidate outline first. Available candidates: {', '.join(candidate_ids[:5])}.")
350
+ guidance.append(
351
+ "Use `artifact.submit_paper_outline(mode='select', outline_id='...', selected_reason='...')` "
352
+ "to promote one of those candidates before retrying the writing-facing campaign."
353
+ )
354
+ suggested_artifact_calls.append(
355
+ _artifact_call(
356
+ "artifact.submit_paper_outline(mode='select', outline_id='...', selected_reason='...')",
357
+ "Promote one candidate outline into the active paper line.",
358
+ )
359
+ )
360
+ else:
361
+ guidance.append("Create a candidate outline first, then select it before launching a writing-facing analysis campaign.")
362
+ suggested_artifact_calls.append(
363
+ _artifact_call(
364
+ "artifact.submit_paper_outline(mode='candidate', ...)",
365
+ "Create the first paper outline candidate for this quest.",
366
+ )
367
+ )
368
+ guidance.append(
369
+ "If the work is only exploratory evidence and not yet bound to the paper contract, downgrade it to a pre-outline analysis-lite / non-writing-facing campaign instead."
370
+ )
371
+ suggested_artifact_calls.append(
372
+ _artifact_call(
373
+ "artifact.create_analysis_campaign(...)",
374
+ "Retry only after the selected outline and paper-facing fields are explicit, or simplify the campaign so it is no longer writing-facing.",
375
+ )
376
+ )
377
+ elif "research_questions" in message:
378
+ guidance.append("Provide non-empty research_questions before launching a writing-facing analysis campaign.")
379
+ guidance.append("The cleanest fix is to revise the selected outline so the paper contract and campaign input agree.")
380
+ suggested_artifact_calls.extend(
381
+ [
382
+ _artifact_call(
383
+ "artifact.submit_paper_outline(mode='revise', ...)",
384
+ "Add research_questions to the selected outline.",
385
+ ),
386
+ _artifact_call(
387
+ "artifact.create_analysis_campaign(..., research_questions=[...], experimental_designs=[...], todo_items=[...])",
388
+ "Retry the campaign with explicit writing-facing fields.",
389
+ ),
390
+ ]
391
+ )
392
+ elif "experimental_designs" in message:
393
+ guidance.append("Provide non-empty experimental_designs before launching a writing-facing analysis campaign.")
394
+ guidance.append("If the paper contract is still thin, revise the selected outline before retrying.")
395
+ suggested_artifact_calls.extend(
396
+ [
397
+ _artifact_call(
398
+ "artifact.submit_paper_outline(mode='revise', ...)",
399
+ "Add experimental_designs to the selected outline.",
400
+ ),
401
+ _artifact_call(
402
+ "artifact.create_analysis_campaign(..., experimental_designs=[...])",
403
+ "Retry the campaign with explicit experimental designs.",
404
+ ),
405
+ ]
406
+ )
407
+ elif "todo_items" in message or "outline-bound paper contract fields" in message:
408
+ guidance.append("Every writing-facing analysis slice needs a todo item with section_id, item_id, paper_role, and claim_links.")
409
+ guidance.append("Do not launch a paper-facing campaign until every slice is mapped back into the outline contract.")
410
+ suggested_artifact_calls.append(
411
+ _artifact_call(
412
+ "artifact.create_analysis_campaign(..., todo_items=[...])",
413
+ "Retry with one outline-bound todo item per slice.",
414
+ )
415
+ )
416
+ elif "confirmed or waived baseline gate" in message:
417
+ guidance.append("Open the baseline gate first, or explicitly waive it if skipping is justified.")
418
+ suggested_artifact_calls.extend(
419
+ [
420
+ _artifact_call("artifact.confirm_baseline(...)", "Confirm the active baseline and metric contract."),
421
+ _artifact_call("artifact.waive_baseline(...)", "Record an explicit waiver if baseline work should be skipped."),
422
+ ]
423
+ )
424
+ elif "An active idea is required before starting an analysis campaign." in message:
425
+ guidance.append("Create or activate an idea before launching an analysis campaign.")
426
+ suggested_artifact_calls.extend(
427
+ [
428
+ _artifact_call("artifact.submit_idea(...)", "Create the next durable idea route first."),
429
+ _artifact_call("artifact.activate_branch(...)", "Switch back to the branch that owns the accepted idea line."),
430
+ ]
431
+ )
432
+ elif "baseline_path" in message:
433
+ guidance.append("Use a quest-local baseline path under `baselines/local/...` or `baselines/imported/...`.")
434
+ guidance.append("If the baseline lives outside the quest, materialize or attach it first instead of passing an external path directly.")
435
+ suggested_artifact_calls.extend(
436
+ [
437
+ _artifact_call("artifact.attach_baseline(...)", "Attach a reusable baseline package when one already exists."),
438
+ _artifact_call("artifact.confirm_baseline(...)", "Confirm the quest-local baseline after it is materialized."),
439
+ ]
440
+ )
441
+ elif "imported baseline" in message or "protocol-breaking" in message:
442
+ guidance.append("Do not overwrite the accepted baseline in place when comparability changed materially.")
443
+ guidance.append("Use a new baseline id or a new variant when the path, protocol, or comparator meaning changed.")
444
+ suggested_artifact_calls.extend(
445
+ [
446
+ _artifact_call("artifact.confirm_baseline(...)", "Confirm the new baseline or variant as a separate comparator."),
447
+ _artifact_call("artifact.overwrite_baseline(...)", "Use overwrite only when the baseline is still the same accepted comparator."),
448
+ ]
449
+ )
450
+ elif "submit_paper_bundle requires a selected outline" in message:
451
+ guidance.append("Select an outline before bundling the paper package.")
452
+ suggested_artifact_calls.append(
453
+ _artifact_call(
454
+ "artifact.submit_paper_outline(mode='select', outline_id='...', selected_reason='...')",
455
+ "Select the active outline before generating the final paper bundle.",
456
+ )
457
+ )
458
+ else:
459
+ guidance.append("Inspect the current quest state and the tool-specific required fields before retrying.")
460
+ suggested_artifact_calls.append(
461
+ _artifact_call("artifact.get_quest_state(detail='full')", "Read the current durable quest state before retrying the tool.")
462
+ )
463
+
464
+ return {
465
+ "ok": False,
466
+ "tool_name": f"artifact.{tool_name}",
467
+ "message": message,
468
+ "guidance": guidance,
469
+ "suggested_artifact_calls": suggested_artifact_calls,
470
+ **extra,
471
+ }
472
+
473
+
72
474
  def _progress_watchdog_note(tool_call_count: int) -> str:
73
475
  return (
74
476
  "By the way, you have gone "
@@ -152,6 +554,180 @@ def _attach_interaction_watchdog(
152
554
  return enriched
153
555
 
154
556
 
557
+ def _local_daemon_api_base_url(home: Path) -> tuple[str | None, dict[str, Any]]:
558
+ state = read_json(home / "runtime" / "daemon.json", {})
559
+ if not isinstance(state, dict):
560
+ return None, {}
561
+ for key in ("url", "bind_url"):
562
+ value = str(state.get(key) or "").strip()
563
+ if value:
564
+ return value.rstrip("/"), state
565
+ return None, state
566
+
567
+
568
+ def _issue_draft_route_effect(payload: dict[str, Any]) -> dict[str, Any]:
569
+ return {
570
+ "name": "route:navigate",
571
+ "data": {
572
+ "to": "/settings/issues",
573
+ "issueDraft": {
574
+ "ok": True,
575
+ "title": str(payload.get("title") or "").strip(),
576
+ "body_markdown": str(payload.get("body_markdown") or "").strip(),
577
+ "issue_url_base": str(payload.get("issue_url_base") or "").strip(),
578
+ "repo_url": str(payload.get("repo_url") or "").strip(),
579
+ "generated_at": payload.get("generated_at"),
580
+ },
581
+ },
582
+ }
583
+
584
+
585
+ def _coerce_prepare_bool(value: Any, *, field_name: str) -> bool:
586
+ if isinstance(value, bool):
587
+ return value
588
+ if isinstance(value, str):
589
+ normalized = value.strip().lower()
590
+ if normalized in {"true", "1", "yes", "on"}:
591
+ return True
592
+ if normalized in {"false", "0", "no", "off"}:
593
+ return False
594
+ raise ValueError(f"`{field_name}` must be a boolean.")
595
+
596
+
597
+ def _sanitize_start_setup_form_patch(form_patch: dict[str, Any] | None) -> dict[str, Any]:
598
+ if form_patch is None:
599
+ return {}
600
+ if not isinstance(form_patch, dict):
601
+ raise ValueError("`form_patch` must be an object.")
602
+ patch: dict[str, Any] = {}
603
+ for key in START_SETUP_FORM_FIELDS:
604
+ if key not in form_patch:
605
+ continue
606
+ value = form_patch.get(key)
607
+ if value is None:
608
+ continue
609
+ if key == "need_research_paper":
610
+ patch[key] = _coerce_prepare_bool(value, field_name=key)
611
+ continue
612
+ if isinstance(value, (str, int, float, bool)):
613
+ patch[key] = str(value).strip() if not isinstance(value, bool) else value
614
+ continue
615
+ raise ValueError(f"`form_patch.{key}` must be a string or boolean.")
616
+ return patch
617
+
618
+
619
+ def _sanitize_start_setup_session_patch(session_patch: dict[str, Any] | None) -> dict[str, Any]:
620
+ if session_patch is None:
621
+ return {}
622
+ if not isinstance(session_patch, dict):
623
+ raise ValueError("`session_patch` must be an object.")
624
+ patch: dict[str, Any] = {}
625
+ for key in START_SETUP_SESSION_FIELDS:
626
+ if key not in session_patch:
627
+ continue
628
+ value = session_patch.get(key)
629
+ if value is None:
630
+ continue
631
+ if key in {"recommended_workspace_mode", "launch_readiness"}:
632
+ patch[key] = str(value).strip()
633
+ continue
634
+ if key == "missing_confirmations":
635
+ if not isinstance(value, list):
636
+ raise ValueError("`session_patch.missing_confirmations` must be an array of strings.")
637
+ patch[key] = [str(item).strip() for item in value if str(item).strip()]
638
+ continue
639
+ if key == "science_package_cards":
640
+ if not isinstance(value, list):
641
+ raise ValueError("`session_patch.science_package_cards` must be an array of strings.")
642
+ patch[key] = [str(item).strip() for item in value if str(item).strip()]
643
+ continue
644
+ if key in {"fit_assessment", "preview_plan", "copilot_handoff", "science_task", "science_task_brief"}:
645
+ if not isinstance(value, dict):
646
+ raise ValueError(f"`session_patch.{key}` must be an object.")
647
+ patch[key] = json.loads(json.dumps(value, ensure_ascii=False))
648
+ continue
649
+ if key == "materials_summary":
650
+ if not isinstance(value, list):
651
+ raise ValueError("`session_patch.materials_summary` must be an array.")
652
+ patch[key] = [dict(item) for item in value if isinstance(item, dict)]
653
+ continue
654
+ return patch
655
+
656
+
657
+ def _start_setup_patch_effect(
658
+ form_patch: dict[str, Any],
659
+ *,
660
+ session_patch: dict[str, Any] | None = None,
661
+ message: str | None = None,
662
+ ) -> dict[str, Any]:
663
+ return {
664
+ "name": "start_setup:patch",
665
+ "data": {
666
+ "patch": dict(form_patch),
667
+ "session_patch": dict(session_patch or {}),
668
+ "message": str(message or "").strip() or None,
669
+ },
670
+ }
671
+
672
+
673
+ def _prepare_github_issue_payload_via_daemon(
674
+ home: Path,
675
+ *,
676
+ summary: str | None = None,
677
+ user_notes: str | None = None,
678
+ include_doctor: bool = True,
679
+ include_logs: bool = True,
680
+ include_system_quirks: bool = False,
681
+ ) -> dict[str, Any]:
682
+ base_url, daemon_state = _local_daemon_api_base_url(home)
683
+ if not base_url:
684
+ raise ValueError("The local daemon URL is unavailable. Start DeepScientist before preparing a GitHub issue draft.")
685
+
686
+ token = str((daemon_state or {}).get("auth_token") or "").strip()
687
+ auth_enabled = bool((daemon_state or {}).get("auth_enabled"))
688
+ if auth_enabled and not token:
689
+ raise ValueError("Browser auth is enabled for the local daemon, but no auth token was found in runtime/daemon.json.")
690
+
691
+ body = {
692
+ "summary": str(summary or "").strip() or None,
693
+ "user_notes": str(user_notes or "").strip() or None,
694
+ "include_doctor": bool(include_doctor),
695
+ "include_logs": bool(include_logs),
696
+ "include_system_quirks": bool(include_system_quirks),
697
+ }
698
+ headers = {
699
+ "Content-Type": "application/json",
700
+ }
701
+ if auth_enabled and token:
702
+ headers["Authorization"] = f"Bearer {token}"
703
+
704
+ request = urllib_request.Request(
705
+ f"{base_url}/api/system/issues/draft",
706
+ data=json.dumps(body).encode("utf-8"),
707
+ headers=headers,
708
+ method="POST",
709
+ )
710
+ try:
711
+ with urllib_request.urlopen(request, timeout=15) as response:
712
+ raw = response.read().decode("utf-8", errors="replace")
713
+ except urllib_error.HTTPError as exc: # pragma: no cover - exercised via unit monkeypatching
714
+ detail = exc.read().decode("utf-8", errors="replace").strip()
715
+ message = detail or getattr(exc, "reason", "") or "request failed"
716
+ raise ValueError(f"Local daemon issue draft request failed with HTTP {exc.code}: {message}") from exc
717
+ except OSError as exc:
718
+ raise ValueError(f"Unable to reach the local daemon at `{base_url}`: {exc}") from exc
719
+
720
+ try:
721
+ payload = json.loads(raw or "{}")
722
+ except json.JSONDecodeError as exc:
723
+ raise ValueError("The local daemon returned an invalid issue draft payload.") from exc
724
+ if not isinstance(payload, dict):
725
+ raise ValueError("The local daemon returned a non-object issue draft payload.")
726
+ if payload.get("ok") is False:
727
+ raise ValueError(str(payload.get("message") or "The local daemon rejected the issue draft request."))
728
+ return payload
729
+
730
+
155
731
  def _split_bash_log_lines(log_text: str) -> list[str]:
156
732
  return log_text.splitlines()
157
733
 
@@ -421,8 +997,31 @@ def build_memory_server(context: McpContext) -> FastMCP:
421
997
  comment: str | dict[str, Any] | None = None,
422
998
  ) -> dict[str, Any]:
423
999
  resolved_scope = _resolve_search_scope(context, scope)
424
- quest_root = context.quest_root if resolved_scope in {"quest", "both"} else None
425
- items = service.search(query, scope=resolved_scope, quest_root=quest_root, limit=limit, kind=kind)
1000
+ if resolved_scope == "quest" and context.quest_root is not None and service.shared_read_enabled():
1001
+ items = service.search_visible_quest_cards(
1002
+ query,
1003
+ active_quest_root=context.quest_root,
1004
+ active_quest_id=context.quest_id,
1005
+ limit=limit,
1006
+ kind=kind,
1007
+ include_shared=True,
1008
+ )
1009
+ elif resolved_scope == "both" and context.quest_root is not None:
1010
+ quest_items = service.search_visible_quest_cards(
1011
+ query,
1012
+ active_quest_root=context.quest_root,
1013
+ active_quest_id=context.quest_id,
1014
+ limit=limit,
1015
+ kind=kind,
1016
+ include_shared=service.shared_read_enabled(),
1017
+ )
1018
+ global_items = service.search(query, scope="global", limit=limit, kind=kind)
1019
+ items = quest_items + global_items
1020
+ items.sort(key=lambda item: service._visible_card_sort_key(item, active_quest_id=context.quest_id))
1021
+ items = items[:limit]
1022
+ else:
1023
+ quest_root = context.quest_root if resolved_scope in {"quest", "both"} else None
1024
+ items = service.search(query, scope=resolved_scope, quest_root=quest_root, limit=limit, kind=kind)
426
1025
  return {"ok": True, "count": len(items), "items": items}
427
1026
 
428
1027
  @server.tool(
@@ -440,10 +1039,30 @@ def build_memory_server(context: McpContext) -> FastMCP:
440
1039
  comment: str | dict[str, Any] | None = None,
441
1040
  ) -> dict[str, Any]:
442
1041
  resolved_scope = _resolve_search_scope(context, scope)
443
- if resolved_scope == "both":
444
- quest_items = service.list_recent(scope="quest", quest_root=context.require_quest_root(), limit=limit, kind=kind)
1042
+ if resolved_scope == "quest" and context.quest_root is not None and service.shared_read_enabled():
1043
+ items = service.list_visible_quest_cards(
1044
+ active_quest_root=context.quest_root,
1045
+ active_quest_id=context.quest_id,
1046
+ limit=limit,
1047
+ kind=kind,
1048
+ include_shared=True,
1049
+ )
1050
+ elif resolved_scope == "both":
1051
+ quest_items = (
1052
+ service.list_visible_quest_cards(
1053
+ active_quest_root=context.require_quest_root(),
1054
+ active_quest_id=context.quest_id,
1055
+ limit=limit,
1056
+ kind=kind,
1057
+ include_shared=service.shared_read_enabled(),
1058
+ )
1059
+ if context.quest_root is not None
1060
+ else []
1061
+ )
445
1062
  global_items = service.list_recent(scope="global", limit=limit, kind=kind)
446
- items = (quest_items + global_items)[-limit:]
1063
+ items = quest_items + global_items
1064
+ items.sort(key=lambda item: service._visible_card_sort_key(item, active_quest_id=context.quest_id))
1065
+ items = items[:limit]
447
1066
  else:
448
1067
  quest_root = context.quest_root if resolved_scope == "quest" else None
449
1068
  items = service.list_recent(scope=resolved_scope, quest_root=quest_root, limit=limit, kind=kind)
@@ -469,6 +1088,9 @@ def build_memory_server(context: McpContext) -> FastMCP:
469
1088
  def build_artifact_server(context: McpContext) -> FastMCP:
470
1089
  service = ArtifactService(context.home)
471
1090
  quest_service = service.quest_service
1091
+ custom_profile = str(context.custom_profile or "").strip().lower()
1092
+ issue_only_profile = custom_profile == "settings_issue"
1093
+ start_setup_prepare_profile = custom_profile == "start_setup_prepare"
472
1094
  server = FastMCP(
473
1095
  "artifact",
474
1096
  instructions=(
@@ -479,7 +1101,12 @@ def build_artifact_server(context: McpContext) -> FastMCP:
479
1101
  log_level="ERROR",
480
1102
  )
481
1103
 
482
- def finalize_state_changing_artifact_tool(payload: dict[str, Any], *, tool_name: str) -> dict[str, Any]:
1104
+ def finalize_artifact_tool(
1105
+ payload: dict[str, Any],
1106
+ *,
1107
+ tool_name: str,
1108
+ state_change_note: str | None = None,
1109
+ ) -> dict[str, Any]:
483
1110
  quest_root = context.require_quest_root().resolve()
484
1111
  quest_service.record_tool_activity(
485
1112
  quest_root,
@@ -489,9 +1116,188 @@ def build_artifact_server(context: McpContext) -> FastMCP:
489
1116
  return _attach_interaction_watchdog(
490
1117
  payload,
491
1118
  watchdog,
1119
+ state_change_note=state_change_note,
1120
+ )
1121
+
1122
+ def finalize_state_changing_artifact_tool(payload: dict[str, Any], *, tool_name: str) -> dict[str, Any]:
1123
+ return finalize_artifact_tool(
1124
+ payload,
1125
+ tool_name=tool_name,
492
1126
  state_change_note=ARTIFACT_STATE_CHANGE_WATCHDOG_NOTES.get(tool_name),
493
1127
  )
494
1128
 
1129
+ def _quest_relative_path(value: Any) -> str | None:
1130
+ text = str(value or "").strip()
1131
+ if not text:
1132
+ return None
1133
+ path = Path(text)
1134
+ if not path.is_absolute():
1135
+ return text
1136
+ quest_root = context.require_quest_root().resolve()
1137
+ try:
1138
+ active_workspace_root = quest_service.active_workspace_root(quest_root)
1139
+ except Exception:
1140
+ active_workspace_root = None
1141
+ roots = [context.worktree_root, active_workspace_root, context.quest_root]
1142
+ seen: set[str] = set()
1143
+ for raw_root in roots:
1144
+ if raw_root is None:
1145
+ continue
1146
+ root = raw_root.resolve()
1147
+ key = str(root)
1148
+ if key in seen:
1149
+ continue
1150
+ seen.add(key)
1151
+ try:
1152
+ return path.resolve().relative_to(root).as_posix()
1153
+ except ValueError:
1154
+ continue
1155
+ try:
1156
+ return path.resolve().relative_to(quest_root).as_posix()
1157
+ except ValueError:
1158
+ return text
1159
+
1160
+ def _compact_artifact_delta(value: Any) -> dict[str, Any] | None:
1161
+ if not isinstance(value, dict):
1162
+ return None
1163
+ return {
1164
+ key: value.get(key)
1165
+ for key in (
1166
+ "schema_version",
1167
+ "delta_id",
1168
+ "delta_kind",
1169
+ "sidecar_rel_path",
1170
+ "path_count",
1171
+ "changed_paths",
1172
+ )
1173
+ if value.get(key) is not None
1174
+ }
1175
+
1176
+ def _compact_paths(payload: dict[str, Any], keys: tuple[str, ...]) -> dict[str, str]:
1177
+ paths: dict[str, str] = {}
1178
+ for key in keys:
1179
+ relative = _quest_relative_path(payload.get(key))
1180
+ if relative:
1181
+ paths[key] = relative
1182
+ return paths
1183
+
1184
+ def compact_paper_write_result(payload: dict[str, Any], *, tool_name: str) -> dict[str, Any]:
1185
+ compact: dict[str, Any] = {
1186
+ "ok": bool(payload.get("ok")),
1187
+ "tool_name": tool_name,
1188
+ "artifact_delta": _compact_artifact_delta(payload.get("artifact_delta")),
1189
+ "hint": "Full paper artifact content was written to files; read the listed paths or artifact_delta sidecar only when needed.",
1190
+ }
1191
+ if tool_name == "submit_paper_outline":
1192
+ compact.update(
1193
+ {
1194
+ "mode": payload.get("mode"),
1195
+ "outline_id": payload.get("outline_id"),
1196
+ "paths": _compact_paths(
1197
+ payload,
1198
+ (
1199
+ "outline_path",
1200
+ "selected_outline_path",
1201
+ "outline_manifest_path",
1202
+ "paper_line_state_path",
1203
+ "outline_selection_path",
1204
+ "revised_outline_path",
1205
+ ),
1206
+ ),
1207
+ }
1208
+ )
1209
+ return compact
1210
+
1211
+ manifest = payload.get("manifest") if isinstance(payload.get("manifest"), dict) else {}
1212
+ paper_line_state = payload.get("paper_line_state") if isinstance(payload.get("paper_line_state"), dict) else {}
1213
+ continuation = payload.get("continuation") if isinstance(payload.get("continuation"), dict) else {}
1214
+ compact.update(
1215
+ {
1216
+ "package_type": manifest.get("package_type"),
1217
+ "title": manifest.get("title"),
1218
+ "selected_outline_ref": manifest.get("selected_outline_ref"),
1219
+ "paper_branch": manifest.get("paper_branch") or paper_line_state.get("paper_branch"),
1220
+ "next_anchor": continuation.get("anchor") or "decision",
1221
+ "paths": _compact_paths(
1222
+ payload,
1223
+ (
1224
+ "manifest_path",
1225
+ "baseline_inventory_path",
1226
+ "evidence_ledger_path",
1227
+ "paper_line_state_path",
1228
+ "manuscript_coverage_path",
1229
+ "open_source_manifest_path",
1230
+ ),
1231
+ ),
1232
+ }
1233
+ )
1234
+ return compact
1235
+
1236
+ if issue_only_profile:
1237
+ @server.tool(
1238
+ name="prepare_github_issue",
1239
+ description=(
1240
+ "Generate a prefilled GitHub issue draft from the live local daemon's settings diagnostics. "
1241
+ "By default this also asks the browser UI to navigate to `/settings/issues` and preload the generated draft."
1242
+ ),
1243
+ )
1244
+ def prepare_github_issue(
1245
+ summary: str = "",
1246
+ user_notes: str = "",
1247
+ include_doctor: bool = True,
1248
+ include_logs: bool = True,
1249
+ include_system_quirks: bool = False,
1250
+ open_settings_page: bool = True,
1251
+ comment: str | dict[str, Any] | None = None,
1252
+ ) -> dict[str, Any]:
1253
+ result = _prepare_github_issue_payload_via_daemon(
1254
+ context.home,
1255
+ summary=summary,
1256
+ user_notes=user_notes,
1257
+ include_doctor=include_doctor,
1258
+ include_logs=include_logs,
1259
+ include_system_quirks=include_system_quirks,
1260
+ )
1261
+ if open_settings_page:
1262
+ result["ui_effects"] = [_issue_draft_route_effect(result)]
1263
+ return finalize_artifact_tool(result, tool_name="prepare_github_issue")
1264
+
1265
+ return server
1266
+
1267
+ if start_setup_prepare_profile:
1268
+ @server.tool(
1269
+ name="prepare_start_setup_form",
1270
+ description=(
1271
+ "Prepare and apply a structured patch for the autonomous start form. "
1272
+ "Use this when the setup agent has enough information to fill or refine the form automatically."
1273
+ ),
1274
+ )
1275
+ def prepare_start_setup_form(
1276
+ form_patch: dict[str, Any] | None = None,
1277
+ session_patch: dict[str, Any] | None = None,
1278
+ message: str = "",
1279
+ comment: str | dict[str, Any] | None = None,
1280
+ ) -> dict[str, Any]:
1281
+ nested_form_patch, nested_session_patch = _split_nested_start_setup_payload(form_patch)
1282
+ if nested_form_patch is not None:
1283
+ form_patch = nested_form_patch
1284
+ if session_patch is None and nested_session_patch is not None:
1285
+ session_patch = nested_session_patch
1286
+ sanitized_patch = _sanitize_start_setup_form_patch(form_patch)
1287
+ sanitized_session_patch = _sanitize_start_setup_session_patch(session_patch)
1288
+ if not sanitized_patch and not sanitized_session_patch:
1289
+ raise ValueError("At least one of `form_patch` or `session_patch` must include supported fields.")
1290
+ result = service.apply_start_setup_form_patch(
1291
+ context.require_quest_root(),
1292
+ form_patch=sanitized_patch,
1293
+ session_patch=sanitized_session_patch,
1294
+ message=message,
1295
+ )
1296
+ result["ui_effects"] = [_start_setup_patch_effect(sanitized_patch, session_patch=sanitized_session_patch, message=message)]
1297
+ return finalize_artifact_tool(result, tool_name="prepare_start_setup_form")
1298
+
1299
+ return server
1300
+
495
1301
  @server.tool(name="record", description="Write a structured artifact record under the current quest.")
496
1302
  def record(payload: dict[str, Any], comment: str | dict[str, Any] | None = None) -> dict[str, Any]:
497
1303
  enriched = dict(payload)
@@ -514,6 +1320,94 @@ def build_artifact_server(context: McpContext) -> FastMCP:
514
1320
  workspace_root=context.worktree_root,
515
1321
  )
516
1322
 
1323
+ @server.tool(
1324
+ name="science",
1325
+ description=(
1326
+ "Record and update Science Evidence Graph nodes under the artifact namespace. "
1327
+ "Use this for natural-science and engineering package checks, computational runs, dataset analyses, "
1328
+ "parameter sweeps, validation results, and scientific claims. Do not use it for execution; "
1329
+ "all commands, solver runs, SSH, and HPC work must go through bash_exec."
1330
+ ),
1331
+ )
1332
+ def science(
1333
+ action: str,
1334
+ node_type: str | None = None,
1335
+ node_id: str | None = None,
1336
+ title: str | None = None,
1337
+ summary: str | None = None,
1338
+ status: str | None = None,
1339
+ domain: str | None = None,
1340
+ package_id: str | None = None,
1341
+ task_type: str | None = None,
1342
+ key_results: list[dict[str, Any]] | None = None,
1343
+ evidence_paths: list[str] | None = None,
1344
+ input_paths: list[str] | None = None,
1345
+ output_paths: list[str] | None = None,
1346
+ log_paths: list[str] | None = None,
1347
+ validation_paths: list[str] | None = None,
1348
+ parent_node_ids: list[str] | None = None,
1349
+ related_node_ids: list[str] | None = None,
1350
+ claim_type: str | None = None,
1351
+ trust: str | None = None,
1352
+ canvas: dict[str, Any] | None = None,
1353
+ notify: bool = False,
1354
+ relation_type: str | None = None,
1355
+ relation_summary: str | None = None,
1356
+ metadata: dict[str, Any] | None = None,
1357
+ comment: str | dict[str, Any] | None = None,
1358
+ ) -> dict[str, Any]:
1359
+ def _normalize_paths(values: list[str] | None) -> list[str]:
1360
+ if not isinstance(values, list):
1361
+ return []
1362
+ normalized: list[str] = []
1363
+ seen: set[str] = set()
1364
+ for value in values:
1365
+ path = _quest_relative_path(value)
1366
+ if not path or path in seen:
1367
+ continue
1368
+ seen.add(path)
1369
+ normalized.append(path)
1370
+ return normalized
1371
+
1372
+ source: dict[str, Any] = {"kind": "agent"}
1373
+ if context.agent_role:
1374
+ source["role"] = context.agent_role
1375
+ if context.run_id:
1376
+ source["run_id"] = context.run_id
1377
+ if context.worker_id:
1378
+ source["worker_id"] = context.worker_id
1379
+ result = service.science(
1380
+ context.require_quest_root(),
1381
+ action=action,
1382
+ node_type=node_type,
1383
+ node_id=node_id,
1384
+ title=title,
1385
+ summary=summary,
1386
+ status=status,
1387
+ domain=domain,
1388
+ package_id=package_id,
1389
+ task_type=task_type,
1390
+ key_results=key_results,
1391
+ evidence_paths=_normalize_paths(evidence_paths),
1392
+ input_paths=_normalize_paths(input_paths),
1393
+ output_paths=_normalize_paths(output_paths),
1394
+ log_paths=_normalize_paths(log_paths),
1395
+ validation_paths=_normalize_paths(validation_paths),
1396
+ parent_node_ids=parent_node_ids,
1397
+ related_node_ids=related_node_ids,
1398
+ claim_type=claim_type,
1399
+ trust=trust,
1400
+ canvas=canvas,
1401
+ notify=notify,
1402
+ relation_type=relation_type,
1403
+ relation_summary=relation_summary,
1404
+ metadata=metadata,
1405
+ source=source,
1406
+ run_id=context.run_id,
1407
+ workspace_root=context.worktree_root,
1408
+ )
1409
+ return finalize_artifact_tool(result, tool_name="science")
1410
+
517
1411
  @server.tool(name="checkpoint", description="Create a Git checkpoint in the current quest repository.")
518
1412
  def checkpoint(
519
1413
  message: str,
@@ -601,15 +1495,26 @@ def build_artifact_server(context: McpContext) -> FastMCP:
601
1495
  create_worktree_if_missing: bool = True,
602
1496
  comment: str | dict[str, Any] | None = None,
603
1497
  ) -> dict[str, Any]:
604
- return service.activate_branch(
605
- context.require_quest_root(),
606
- branch=branch,
607
- idea_id=idea_id,
608
- run_id=run_id,
609
- anchor=anchor,
610
- promote_to_head=promote_to_head,
611
- create_worktree_if_missing=create_worktree_if_missing,
612
- )
1498
+ try:
1499
+ return service.activate_branch(
1500
+ context.require_quest_root(),
1501
+ branch=branch,
1502
+ idea_id=idea_id,
1503
+ run_id=run_id,
1504
+ anchor=anchor,
1505
+ promote_to_head=promote_to_head,
1506
+ create_worktree_if_missing=create_worktree_if_missing,
1507
+ )
1508
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
1509
+ return finalize_artifact_tool(
1510
+ _artifact_guided_error_payload(
1511
+ service,
1512
+ context.require_quest_root(),
1513
+ tool_name="activate_branch",
1514
+ exc=exc,
1515
+ ),
1516
+ tool_name="activate_branch",
1517
+ )
613
1518
 
614
1519
  @server.tool(
615
1520
  name="submit_idea",
@@ -646,31 +1551,42 @@ def build_artifact_server(context: McpContext) -> FastMCP:
646
1551
  source_candidate_id: str | None = None,
647
1552
  comment: str | dict[str, Any] | None = None,
648
1553
  ) -> dict[str, Any]:
649
- return service.submit_idea(
650
- context.require_quest_root(),
651
- mode=mode,
652
- submission_mode=submission_mode,
653
- idea_id=idea_id,
654
- lineage_intent=lineage_intent,
655
- title=title,
656
- problem=problem,
657
- hypothesis=hypothesis,
658
- mechanism=mechanism,
659
- method_brief=method_brief,
660
- selection_scores=selection_scores,
661
- mechanism_family=mechanism_family,
662
- change_layer=change_layer,
663
- source_lens=source_lens,
664
- expected_gain=expected_gain,
665
- evidence_paths=evidence_paths,
666
- risks=risks,
667
- decision_reason=decision_reason,
668
- foundation_ref=foundation_ref,
669
- foundation_reason=foundation_reason,
670
- next_target=next_target,
671
- draft_markdown=draft_markdown,
672
- source_candidate_id=source_candidate_id,
673
- )
1554
+ try:
1555
+ return service.submit_idea(
1556
+ context.require_quest_root(),
1557
+ mode=mode,
1558
+ submission_mode=submission_mode,
1559
+ idea_id=idea_id,
1560
+ lineage_intent=lineage_intent,
1561
+ title=title,
1562
+ problem=problem,
1563
+ hypothesis=hypothesis,
1564
+ mechanism=mechanism,
1565
+ method_brief=method_brief,
1566
+ selection_scores=selection_scores,
1567
+ mechanism_family=mechanism_family,
1568
+ change_layer=change_layer,
1569
+ source_lens=source_lens,
1570
+ expected_gain=expected_gain,
1571
+ evidence_paths=evidence_paths,
1572
+ risks=risks,
1573
+ decision_reason=decision_reason,
1574
+ foundation_ref=foundation_ref,
1575
+ foundation_reason=foundation_reason,
1576
+ next_target=next_target,
1577
+ draft_markdown=draft_markdown,
1578
+ source_candidate_id=source_candidate_id,
1579
+ )
1580
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
1581
+ return finalize_artifact_tool(
1582
+ _artifact_guided_error_payload(
1583
+ service,
1584
+ context.require_quest_root(),
1585
+ tool_name="submit_idea",
1586
+ exc=exc,
1587
+ ),
1588
+ tool_name="submit_idea",
1589
+ )
674
1590
 
675
1591
  @server.tool(
676
1592
  name="list_research_branches",
@@ -694,6 +1610,23 @@ def build_artifact_server(context: McpContext) -> FastMCP:
694
1610
  def resolve_runtime_refs(comment: str | dict[str, Any] | None = None) -> dict[str, Any]:
695
1611
  return service.resolve_runtime_refs(context.require_quest_root())
696
1612
 
1613
+ @server.tool(
1614
+ name="get_paper_contract",
1615
+ description=(
1616
+ "Read the active paper contract, including selected outline, section result tables, evidence ledger items, "
1617
+ "analysis inventory, and experiment matrix rows. Use detail='full' by default when writing or finalizing from paper evidence."
1618
+ ),
1619
+ annotations=_read_only_tool_annotations(title="Get paper contract"),
1620
+ )
1621
+ def get_paper_contract(
1622
+ detail: str = "full",
1623
+ comment: str | dict[str, Any] | None = None,
1624
+ ) -> dict[str, Any]:
1625
+ return service.get_paper_contract(
1626
+ context.require_quest_root(),
1627
+ detail=detail,
1628
+ )
1629
+
697
1630
  @server.tool(
698
1631
  name="get_paper_contract_health",
699
1632
  description=(
@@ -706,10 +1639,107 @@ def build_artifact_server(context: McpContext) -> FastMCP:
706
1639
  detail: str = "summary",
707
1640
  comment: str | dict[str, Any] | None = None,
708
1641
  ) -> dict[str, Any]:
709
- return service.get_paper_contract_health(
1642
+ result = service.get_paper_contract_health(
710
1643
  context.require_quest_root(),
711
1644
  detail=detail,
712
1645
  )
1646
+ normalized_detail = str(detail or "summary").strip().lower() or "summary"
1647
+ return compact_mcp_tool_result(
1648
+ result,
1649
+ quest_root=context.require_quest_root(),
1650
+ run_id=context.run_id,
1651
+ tool_name="artifact.get_paper_contract_health",
1652
+ detail=normalized_detail,
1653
+ force=normalized_detail == "full",
1654
+ reason="artifact_full_detail_context_budget",
1655
+ full_detail_requested=normalized_detail == "full",
1656
+ )
1657
+
1658
+ @server.tool(
1659
+ name="validate_manuscript_coverage",
1660
+ description=(
1661
+ "Validate whether the current paper is only a draft checkpoint or a full manuscript/submission package. "
1662
+ "Checks section coverage, figures/tables, ready analysis groups, PDF, and submission checklist state."
1663
+ ),
1664
+ annotations=_read_only_tool_annotations(title="Validate manuscript coverage"),
1665
+ )
1666
+ def validate_manuscript_coverage(
1667
+ detail: str = "summary",
1668
+ minimum_sections: int = 5,
1669
+ minimum_analysis_groups: int = 5,
1670
+ comment: str | dict[str, Any] | None = None,
1671
+ ) -> dict[str, Any]:
1672
+ return service.validate_manuscript_coverage(
1673
+ context.require_quest_root(),
1674
+ detail=detail,
1675
+ minimum_sections=minimum_sections,
1676
+ minimum_analysis_groups=minimum_analysis_groups,
1677
+ )
1678
+
1679
+ @server.tool(
1680
+ name="validate_academic_outline",
1681
+ description=(
1682
+ "Validate that the selected outline is a real paper plan before drafting. "
1683
+ "Checks the one-sentence paper idea, story spine, evidence boundaries, core claims, method, evaluation plan, "
1684
+ "4-8 planned analyses or an explicit waiver, and implementation wording leakage."
1685
+ ),
1686
+ annotations=_read_only_tool_annotations(title="Validate academic outline"),
1687
+ )
1688
+ def validate_academic_outline(
1689
+ detail: str = "summary",
1690
+ comment: str | dict[str, Any] | None = None,
1691
+ ) -> dict[str, Any]:
1692
+ return service.validate_academic_outline(
1693
+ context.require_quest_root(),
1694
+ detail=detail,
1695
+ )
1696
+
1697
+ @server.tool(
1698
+ name="validate_manuscript_language",
1699
+ description=(
1700
+ "Scan paper draft/LaTeX for main-text implementation or route terms that should stay in artifact records "
1701
+ "or appendix reproducibility details."
1702
+ ),
1703
+ annotations=_read_only_tool_annotations(title="Validate manuscript language"),
1704
+ )
1705
+ def validate_manuscript_language(
1706
+ detail: str = "summary",
1707
+ scope: str = "main_text",
1708
+ comment: str | dict[str, Any] | None = None,
1709
+ ) -> dict[str, Any]:
1710
+ return service.validate_manuscript_language(
1711
+ context.require_quest_root(),
1712
+ detail=detail,
1713
+ scope=scope,
1714
+ )
1715
+
1716
+ @server.tool(
1717
+ name="compile_outline_to_writing_plan",
1718
+ description=(
1719
+ "Turn the selected outline into concrete writing jobs for introduction, method, setup, results, analyses, "
1720
+ "limitations, and abstract. Use after validate_academic_outline and before drafting."
1721
+ ),
1722
+ )
1723
+ def compile_outline_to_writing_plan(
1724
+ detail: str = "summary",
1725
+ comment: str | dict[str, Any] | None = None,
1726
+ ) -> dict[str, Any]:
1727
+ try:
1728
+ result = service.compile_outline_to_writing_plan(
1729
+ context.require_quest_root(),
1730
+ detail=detail,
1731
+ )
1732
+ return finalize_state_changing_artifact_tool(result, tool_name="compile_outline_to_writing_plan")
1733
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
1734
+ return finalize_artifact_tool(
1735
+ _artifact_guided_error_payload(
1736
+ service,
1737
+ context.require_quest_root(),
1738
+ tool_name="compile_outline_to_writing_plan",
1739
+ exc=exc,
1740
+ ),
1741
+ tool_name="compile_outline_to_writing_plan",
1742
+ )
713
1743
 
714
1744
  @server.tool(
715
1745
  name="get_quest_state",
@@ -723,10 +1753,22 @@ def build_artifact_server(context: McpContext) -> FastMCP:
723
1753
  detail: str = "summary",
724
1754
  comment: str | dict[str, Any] | None = None,
725
1755
  ) -> dict[str, Any]:
726
- return service.get_quest_state(
1756
+ result = service.get_quest_state(
727
1757
  context.require_quest_root(),
728
1758
  detail=detail,
729
1759
  )
1760
+ normalized_detail = str(detail or "summary").strip().lower() or "summary"
1761
+ return cached_compact_mcp_tool_result(
1762
+ result,
1763
+ quest_root=context.require_quest_root(),
1764
+ run_id=context.run_id,
1765
+ tool_name="artifact.get_quest_state",
1766
+ detail=normalized_detail,
1767
+ cache_key={"detail": normalized_detail},
1768
+ force=normalized_detail == "full",
1769
+ reason="artifact_full_detail_context_budget",
1770
+ full_detail_requested=normalized_detail == "full",
1771
+ )
730
1772
 
731
1773
  @server.tool(
732
1774
  name="get_global_status",
@@ -741,12 +1783,71 @@ def build_artifact_server(context: McpContext) -> FastMCP:
741
1783
  locale: str = "zh",
742
1784
  comment: str | dict[str, Any] | None = None,
743
1785
  ) -> dict[str, Any]:
744
- return service.get_global_status(
1786
+ result = service.get_global_status(
1787
+ context.require_quest_root(),
1788
+ detail=detail,
1789
+ locale=locale,
1790
+ )
1791
+ normalized_detail = str(detail or "brief").strip().lower() or "brief"
1792
+ normalized_locale = str(locale or "zh").strip().lower() or "zh"
1793
+ return cached_compact_mcp_tool_result(
1794
+ result,
1795
+ quest_root=context.require_quest_root(),
1796
+ run_id=context.run_id,
1797
+ tool_name="artifact.get_global_status",
1798
+ detail=normalized_detail,
1799
+ cache_key={"detail": normalized_detail, "locale": normalized_locale},
1800
+ force=normalized_detail == "full",
1801
+ reason="artifact_full_detail_context_budget",
1802
+ full_detail_requested=normalized_detail == "full",
1803
+ )
1804
+
1805
+ @server.tool(
1806
+ name="get_research_map_status",
1807
+ description=(
1808
+ "Read the current research-node progress state that corresponds to the quest canvas. "
1809
+ "Returns the active workspace node, research head node, node history, runtime refs, canvas freshness, recommended activation ref, and Git identifiers so agents can recover progress or switch branches without guessing."
1810
+ ),
1811
+ annotations=_read_only_tool_annotations(title="Get research map status"),
1812
+ )
1813
+ def get_research_map_status(
1814
+ detail: str = "summary",
1815
+ locale: str = "zh",
1816
+ comment: str | dict[str, Any] | None = None,
1817
+ ) -> dict[str, Any]:
1818
+ return service.get_research_map_status(
745
1819
  context.require_quest_root(),
746
1820
  detail=detail,
747
1821
  locale=locale,
748
1822
  )
749
1823
 
1824
+ @server.tool(
1825
+ name="get_benchstore_catalog",
1826
+ description=(
1827
+ "Read the BenchStore catalog with current-device recommendation hints. "
1828
+ "Use detail='summary' for compact recommendations or detail='full' for the full structured catalog."
1829
+ ),
1830
+ annotations=_read_only_tool_annotations(title="Get BenchStore catalog"),
1831
+ )
1832
+ def get_benchstore_catalog(
1833
+ detail: str = "summary",
1834
+ comment: str | dict[str, Any] | None = None,
1835
+ ) -> dict[str, Any]:
1836
+ return service.get_benchstore_catalog(
1837
+ context.require_quest_root(),
1838
+ detail=detail,
1839
+ )
1840
+
1841
+ @server.tool(
1842
+ name="get_start_setup_context",
1843
+ description=(
1844
+ "Read the current autonomous start-setup context, including the suggested form and selected benchmark context when present."
1845
+ ),
1846
+ annotations=_read_only_tool_annotations(title="Get start setup context"),
1847
+ )
1848
+ def get_start_setup_context(comment: str | dict[str, Any] | None = None) -> dict[str, Any]:
1849
+ return service.get_start_setup_context(context.require_quest_root())
1850
+
750
1851
  @server.tool(
751
1852
  name="get_method_scoreboard",
752
1853
  description=(
@@ -883,6 +1984,16 @@ def build_artifact_server(context: McpContext) -> FastMCP:
883
1984
  )
884
1985
  except MetricContractValidationError as exc:
885
1986
  return _metric_validation_error_payload(exc)
1987
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
1988
+ return finalize_artifact_tool(
1989
+ _artifact_guided_error_payload(
1990
+ service,
1991
+ context.require_quest_root(),
1992
+ tool_name="record_main_experiment",
1993
+ exc=exc,
1994
+ ),
1995
+ tool_name="record_main_experiment",
1996
+ )
886
1997
 
887
1998
  @server.tool(
888
1999
  name="create_analysis_campaign",
@@ -903,24 +2014,39 @@ def build_artifact_server(context: McpContext) -> FastMCP:
903
2014
  todo_items: list[dict[str, Any]] | None = None,
904
2015
  comment: str | dict[str, Any] | None = None,
905
2016
  ) -> dict[str, Any]:
906
- return service.create_analysis_campaign(
907
- context.require_quest_root(),
908
- campaign_title=campaign_title,
909
- campaign_goal=campaign_goal,
910
- parent_run_id=parent_run_id,
911
- slices=slices,
912
- campaign_origin=campaign_origin,
913
- selected_outline_ref=selected_outline_ref,
914
- research_questions=research_questions,
915
- experimental_designs=experimental_designs,
916
- todo_items=todo_items,
917
- )
2017
+ try:
2018
+ return service.create_analysis_campaign(
2019
+ context.require_quest_root(),
2020
+ campaign_title=campaign_title,
2021
+ campaign_goal=campaign_goal,
2022
+ parent_run_id=parent_run_id,
2023
+ slices=slices,
2024
+ campaign_origin=campaign_origin,
2025
+ selected_outline_ref=selected_outline_ref,
2026
+ research_questions=research_questions,
2027
+ experimental_designs=experimental_designs,
2028
+ todo_items=todo_items,
2029
+ )
2030
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2031
+ return finalize_artifact_tool(
2032
+ _artifact_guided_error_payload(
2033
+ service,
2034
+ context.require_quest_root(),
2035
+ tool_name="create_analysis_campaign",
2036
+ exc=exc,
2037
+ ),
2038
+ tool_name="create_analysis_campaign",
2039
+ )
918
2040
 
919
2041
  @server.tool(
920
2042
  name="submit_paper_outline",
921
2043
  description=(
922
2044
  "Persist a paper outline candidate, select an approved outline, or revise the selected outline. "
923
- "Use this before analysis campaigns that should support final writing claims."
2045
+ "Use this before analysis campaigns that should support final writing claims. "
2046
+ "For paper-facing work, put the paper idea and section plan in detailed_outline.paper_view, "
2047
+ "and keep result rows, paths, run ids, and reproducibility details in detailed_outline.evidence_view. "
2048
+ "Good paper_view records a one-sentence thesis, what the reader should learn, evidence boundaries, "
2049
+ "and a 4-8 item analysis plan when the paper is mature."
924
2050
  ),
925
2051
  )
926
2052
  def submit_paper_outline(
@@ -935,19 +2061,33 @@ def build_artifact_server(context: McpContext) -> FastMCP:
935
2061
  selected_reason: str | None = None,
936
2062
  comment: str | dict[str, Any] | None = None,
937
2063
  ) -> dict[str, Any]:
938
- result = service.submit_paper_outline(
939
- context.require_quest_root(),
940
- mode=mode,
941
- outline_id=outline_id,
942
- title=title,
943
- note=note,
944
- story=story,
945
- ten_questions=ten_questions,
946
- detailed_outline=detailed_outline,
947
- review_result=review_result,
948
- selected_reason=selected_reason,
949
- )
950
- return finalize_state_changing_artifact_tool(result, tool_name="submit_paper_outline")
2064
+ try:
2065
+ result = service.submit_paper_outline(
2066
+ context.require_quest_root(),
2067
+ mode=mode,
2068
+ outline_id=outline_id,
2069
+ title=title,
2070
+ note=note,
2071
+ story=story,
2072
+ ten_questions=ten_questions,
2073
+ detailed_outline=detailed_outline,
2074
+ review_result=review_result,
2075
+ selected_reason=selected_reason,
2076
+ )
2077
+ return finalize_state_changing_artifact_tool(
2078
+ compact_paper_write_result(result, tool_name="submit_paper_outline"),
2079
+ tool_name="submit_paper_outline",
2080
+ )
2081
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2082
+ return finalize_artifact_tool(
2083
+ _artifact_guided_error_payload(
2084
+ service,
2085
+ context.require_quest_root(),
2086
+ tool_name="submit_paper_outline",
2087
+ exc=exc,
2088
+ ),
2089
+ tool_name="submit_paper_outline",
2090
+ )
951
2091
 
952
2092
  @server.tool(
953
2093
  name="list_paper_outlines",
@@ -958,12 +2098,20 @@ def build_artifact_server(context: McpContext) -> FastMCP:
958
2098
  annotations=_read_only_tool_annotations(title="List paper outlines"),
959
2099
  )
960
2100
  def list_paper_outlines(comment: str | dict[str, Any] | None = None) -> dict[str, Any]:
961
- return service.list_paper_outlines(context.require_quest_root())
2101
+ return cached_compact_mcp_tool_result(
2102
+ service.list_paper_outlines(context.require_quest_root()),
2103
+ quest_root=context.require_quest_root(),
2104
+ run_id=context.run_id,
2105
+ tool_name="artifact.list_paper_outlines",
2106
+ detail="inventory",
2107
+ cache_key={"detail": "inventory"},
2108
+ reason="artifact_inventory_context_budget",
2109
+ )
962
2110
 
963
2111
  @server.tool(
964
2112
  name="submit_paper_bundle",
965
2113
  description=(
966
- "Persist the final paper bundle manifest, including outline, draft, LaTeX/PDF outputs, and build reports."
2114
+ "Persist a paper bundle manifest. Defaults to a draft checkpoint; use package_type='submission_package' only for submission-ready manuscripts."
967
2115
  ),
968
2116
  )
969
2117
  def submit_paper_bundle(
@@ -977,21 +2125,38 @@ def build_artifact_server(context: McpContext) -> FastMCP:
977
2125
  compile_report_path: str | None = None,
978
2126
  pdf_path: str | None = None,
979
2127
  latex_root_path: str | None = None,
2128
+ package_type: str = "draft_checkpoint",
980
2129
  comment: str | dict[str, Any] | None = None,
981
2130
  ) -> dict[str, Any]:
982
- return service.submit_paper_bundle(
983
- context.require_quest_root(),
984
- title=title,
985
- summary=summary,
986
- outline_path=outline_path,
987
- draft_path=draft_path,
988
- writing_plan_path=writing_plan_path,
989
- references_path=references_path,
990
- claim_evidence_map_path=claim_evidence_map_path,
991
- compile_report_path=compile_report_path,
992
- pdf_path=pdf_path,
993
- latex_root_path=latex_root_path,
994
- )
2131
+ try:
2132
+ result = service.submit_paper_bundle(
2133
+ context.require_quest_root(),
2134
+ title=title,
2135
+ summary=summary,
2136
+ outline_path=outline_path,
2137
+ draft_path=draft_path,
2138
+ writing_plan_path=writing_plan_path,
2139
+ references_path=references_path,
2140
+ claim_evidence_map_path=claim_evidence_map_path,
2141
+ compile_report_path=compile_report_path,
2142
+ pdf_path=pdf_path,
2143
+ latex_root_path=latex_root_path,
2144
+ package_type=package_type,
2145
+ )
2146
+ return finalize_state_changing_artifact_tool(
2147
+ compact_paper_write_result(result, tool_name="submit_paper_bundle"),
2148
+ tool_name="submit_paper_bundle",
2149
+ )
2150
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2151
+ return finalize_artifact_tool(
2152
+ _artifact_guided_error_payload(
2153
+ service,
2154
+ context.require_quest_root(),
2155
+ tool_name="submit_paper_bundle",
2156
+ exc=exc,
2157
+ ),
2158
+ tool_name="submit_paper_bundle",
2159
+ )
995
2160
 
996
2161
  @server.tool(
997
2162
  name="record_analysis_slice",
@@ -1050,8 +2215,19 @@ def build_artifact_server(context: McpContext) -> FastMCP:
1050
2215
  if comment is not None and "comment" not in enriched:
1051
2216
  enriched["comment"] = comment
1052
2217
  enriched.setdefault("source", {"kind": "artifact_publish", "quest_id": context.quest_id, "quest_root": str(context.require_quest_root())})
1053
- result = service.publish_baseline(context.require_quest_root(), enriched)
1054
- return finalize_state_changing_artifact_tool(result, tool_name="publish_baseline")
2218
+ try:
2219
+ result = service.publish_baseline(context.require_quest_root(), enriched)
2220
+ return finalize_state_changing_artifact_tool(result, tool_name="publish_baseline")
2221
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2222
+ return finalize_artifact_tool(
2223
+ _artifact_guided_error_payload(
2224
+ service,
2225
+ context.require_quest_root(),
2226
+ tool_name="publish_baseline",
2227
+ exc=exc,
2228
+ ),
2229
+ tool_name="publish_baseline",
2230
+ )
1055
2231
 
1056
2232
  @server.tool(name="attach_baseline", description="Attach a published baseline to the current quest.")
1057
2233
  def attach_baseline(
@@ -1059,14 +2235,27 @@ def build_artifact_server(context: McpContext) -> FastMCP:
1059
2235
  variant_id: str | None = None,
1060
2236
  comment: str | dict[str, Any] | None = None,
1061
2237
  ) -> dict[str, Any]:
1062
- result = service.attach_baseline(context.require_quest_root(), baseline_id, variant_id)
1063
- return finalize_state_changing_artifact_tool(result, tool_name="attach_baseline")
2238
+ try:
2239
+ result = service.attach_baseline(context.require_quest_root(), baseline_id, variant_id)
2240
+ return finalize_state_changing_artifact_tool(result, tool_name="attach_baseline")
2241
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2242
+ return finalize_artifact_tool(
2243
+ _artifact_guided_error_payload(
2244
+ service,
2245
+ context.require_quest_root(),
2246
+ tool_name="attach_baseline",
2247
+ exc=exc,
2248
+ ),
2249
+ tool_name="attach_baseline",
2250
+ )
1064
2251
 
1065
2252
  @server.tool(
1066
2253
  name="confirm_baseline",
1067
2254
  description=(
1068
2255
  "Confirm the active quest baseline and open the stage gate into idea work. "
1069
- "The baseline path must point at a quest-local baseline under baselines/local or baselines/imported."
2256
+ "The baseline path must point at a quest-local baseline under baselines/local or baselines/imported. "
2257
+ "Descriptions, derivations, and source refs must live on entries inside "
2258
+ "`metric_contract.metrics`."
1070
2259
  ),
1071
2260
  )
1072
2261
  def confirm_baseline(
@@ -1075,13 +2264,13 @@ def build_artifact_server(context: McpContext) -> FastMCP:
1075
2264
  variant_id: str | None = None,
1076
2265
  summary: str | None = None,
1077
2266
  baseline_kind: str | None = None,
1078
- metric_contract: dict[str, Any] | None = None,
2267
+ metric_contract: MetricContractPayload | None = None,
1079
2268
  metric_directions: dict[str, str] | None = None,
1080
2269
  metrics_summary: dict[str, Any] | None = None,
1081
- primary_metric: dict[str, Any] | None = None,
2270
+ primary_metric: PrimaryMetricPayload | None = None,
1082
2271
  auto_advance: bool = True,
1083
2272
  comment: str | dict[str, Any] | None = None,
1084
- ) -> dict[str, Any]:
2273
+ ) -> dict[str, Any]:
1085
2274
  try:
1086
2275
  result = service.confirm_baseline(
1087
2276
  context.require_quest_root(),
@@ -1091,16 +2280,156 @@ def build_artifact_server(context: McpContext) -> FastMCP:
1091
2280
  variant_id=variant_id,
1092
2281
  summary=summary,
1093
2282
  baseline_kind=baseline_kind,
1094
- metric_contract=metric_contract,
2283
+ metric_contract=metric_contract.model_dump(exclude_none=True) if metric_contract is not None else None,
1095
2284
  metric_directions=metric_directions,
1096
2285
  metrics_summary=metrics_summary,
1097
- primary_metric=primary_metric,
2286
+ primary_metric=primary_metric.model_dump(exclude_none=True) if primary_metric is not None else None,
1098
2287
  auto_advance=auto_advance,
1099
2288
  strict_metric_contract=True,
1100
2289
  )
1101
2290
  return finalize_state_changing_artifact_tool(result, tool_name="confirm_baseline")
1102
2291
  except MetricContractValidationError as exc:
1103
2292
  return _metric_validation_error_payload(exc)
2293
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2294
+ return finalize_artifact_tool(
2295
+ _artifact_guided_error_payload(
2296
+ service,
2297
+ context.require_quest_root(),
2298
+ tool_name="confirm_baseline",
2299
+ exc=exc,
2300
+ ),
2301
+ tool_name="confirm_baseline",
2302
+ )
2303
+
2304
+ @server.tool(
2305
+ name="overwrite_baseline",
2306
+ description=(
2307
+ "Refresh an already accepted baseline after verified code, variant, or canonical metric changes. "
2308
+ "This rewrites the active baseline reference and downstream inventories, so comparator-breaking changes "
2309
+ "should usually become a new baseline id or variant instead of an in-place overwrite."
2310
+ ),
2311
+ )
2312
+ def overwrite_baseline(
2313
+ change_summary: str,
2314
+ baseline_id: str | None = None,
2315
+ baseline_path: str | None = None,
2316
+ variant_id: str | None = None,
2317
+ summary: str | None = None,
2318
+ baseline_kind: str | None = None,
2319
+ metric_contract: MetricContractPayload | None = None,
2320
+ metric_directions: dict[str, str] | None = None,
2321
+ metrics_summary: dict[str, Any] | None = None,
2322
+ primary_metric: PrimaryMetricPayload | None = None,
2323
+ supplementary_baselines: list[SupplementaryBaselinePayload] | None = None,
2324
+ overwrite_scope: str = "full_refresh",
2325
+ allow_path_change: bool = False,
2326
+ allow_protocol_breaking_change: bool = False,
2327
+ sync_requested_baseline_ref: bool = True,
2328
+ refresh_analysis_inventory: bool = True,
2329
+ refresh_paper_inventory: bool = True,
2330
+ auto_advance: bool = True,
2331
+ comment: str | dict[str, Any] | None = None,
2332
+ ) -> dict[str, Any]:
2333
+ try:
2334
+ result = service.overwrite_baseline(
2335
+ context.require_quest_root(),
2336
+ baseline_id=baseline_id,
2337
+ baseline_path=baseline_path,
2338
+ variant_id=variant_id,
2339
+ summary=summary,
2340
+ change_summary=change_summary,
2341
+ baseline_kind=baseline_kind,
2342
+ metric_contract=metric_contract.model_dump(exclude_none=True) if metric_contract is not None else None,
2343
+ metric_directions=metric_directions,
2344
+ metrics_summary=metrics_summary,
2345
+ primary_metric=primary_metric.model_dump(exclude_none=True) if primary_metric is not None else None,
2346
+ supplementary_baselines=[
2347
+ item.model_dump(exclude_none=True) for item in (supplementary_baselines or [])
2348
+ ]
2349
+ or None,
2350
+ overwrite_scope=overwrite_scope,
2351
+ allow_path_change=allow_path_change,
2352
+ allow_protocol_breaking_change=allow_protocol_breaking_change,
2353
+ sync_requested_baseline_ref=sync_requested_baseline_ref,
2354
+ refresh_analysis_inventory=refresh_analysis_inventory,
2355
+ refresh_paper_inventory=refresh_paper_inventory,
2356
+ auto_advance=auto_advance,
2357
+ strict_metric_contract=True,
2358
+ comment=comment,
2359
+ )
2360
+ return finalize_state_changing_artifact_tool(result, tool_name="overwrite_baseline")
2361
+ except MetricContractValidationError as exc:
2362
+ return _metric_validation_error_payload(exc)
2363
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2364
+ return finalize_artifact_tool(
2365
+ _artifact_guided_error_payload(
2366
+ service,
2367
+ context.require_quest_root(),
2368
+ tool_name="overwrite_baseline",
2369
+ exc=exc,
2370
+ ),
2371
+ tool_name="overwrite_baseline",
2372
+ )
2373
+
2374
+ @server.tool(
2375
+ name="overwrite_baseline",
2376
+ description=(
2377
+ "Refresh an already accepted baseline after verified code, variant, or canonical metric changes. "
2378
+ "This rewrites the active baseline reference and downstream inventories, so comparator-breaking changes "
2379
+ "should usually become a new baseline id or variant instead of an in-place overwrite."
2380
+ ),
2381
+ )
2382
+ def overwrite_baseline(
2383
+ change_summary: str,
2384
+ baseline_id: str | None = None,
2385
+ baseline_path: str | None = None,
2386
+ variant_id: str | None = None,
2387
+ summary: str | None = None,
2388
+ baseline_kind: str | None = None,
2389
+ metric_contract: MetricContractPayload | None = None,
2390
+ metric_directions: dict[str, str] | None = None,
2391
+ metrics_summary: dict[str, Any] | None = None,
2392
+ primary_metric: PrimaryMetricPayload | None = None,
2393
+ supplementary_baselines: list[SupplementaryBaselinePayload] | None = None,
2394
+ overwrite_scope: str = "full_refresh",
2395
+ allow_path_change: bool = False,
2396
+ allow_protocol_breaking_change: bool = False,
2397
+ sync_requested_baseline_ref: bool = True,
2398
+ refresh_analysis_inventory: bool = True,
2399
+ refresh_paper_inventory: bool = True,
2400
+ auto_advance: bool = True,
2401
+ comment: str | dict[str, Any] | None = None,
2402
+ ) -> dict[str, Any]:
2403
+ try:
2404
+ result = service.overwrite_baseline(
2405
+ context.require_quest_root(),
2406
+ baseline_id=baseline_id,
2407
+ baseline_path=baseline_path,
2408
+ variant_id=variant_id,
2409
+ summary=summary,
2410
+ change_summary=change_summary,
2411
+ baseline_kind=baseline_kind,
2412
+ metric_contract=metric_contract.model_dump(exclude_none=True) if metric_contract is not None else None,
2413
+ metric_directions=metric_directions,
2414
+ metrics_summary=metrics_summary,
2415
+ primary_metric=primary_metric.model_dump(exclude_none=True) if primary_metric is not None else None,
2416
+ supplementary_baselines=[
2417
+ item.model_dump(exclude_none=True) for item in (supplementary_baselines or [])
2418
+ ]
2419
+ or None,
2420
+ overwrite_scope=overwrite_scope,
2421
+ allow_path_change=allow_path_change,
2422
+ allow_protocol_breaking_change=allow_protocol_breaking_change,
2423
+ sync_requested_baseline_ref=sync_requested_baseline_ref,
2424
+ refresh_analysis_inventory=refresh_analysis_inventory,
2425
+ refresh_paper_inventory=refresh_paper_inventory,
2426
+ auto_advance=auto_advance,
2427
+ strict_metric_contract=True,
2428
+ comment=comment,
2429
+ )
2430
+ return finalize_state_changing_artifact_tool(result, tool_name="overwrite_baseline")
2431
+ except MetricContractValidationError as exc:
2432
+ return _metric_validation_error_payload(exc)
1104
2433
 
1105
2434
  @server.tool(
1106
2435
  name="waive_baseline",
@@ -1111,13 +2440,24 @@ def build_artifact_server(context: McpContext) -> FastMCP:
1111
2440
  auto_advance: bool = True,
1112
2441
  comment: str | dict[str, Any] | None = None,
1113
2442
  ) -> dict[str, Any]:
1114
- result = service.waive_baseline(
1115
- context.require_quest_root(),
1116
- reason=reason,
1117
- comment=comment,
1118
- auto_advance=auto_advance,
1119
- )
1120
- return finalize_state_changing_artifact_tool(result, tool_name="waive_baseline")
2443
+ try:
2444
+ result = service.waive_baseline(
2445
+ context.require_quest_root(),
2446
+ reason=reason,
2447
+ comment=comment,
2448
+ auto_advance=auto_advance,
2449
+ )
2450
+ return finalize_state_changing_artifact_tool(result, tool_name="waive_baseline")
2451
+ except (ValueError, FileNotFoundError, RuntimeError) as exc:
2452
+ return finalize_artifact_tool(
2453
+ _artifact_guided_error_payload(
2454
+ service,
2455
+ context.require_quest_root(),
2456
+ tool_name="waive_baseline",
2457
+ exc=exc,
2458
+ ),
2459
+ tool_name="waive_baseline",
2460
+ )
1121
2461
 
1122
2462
  @server.tool(
1123
2463
  name="arxiv",
@@ -1248,7 +2588,8 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1248
2588
  name="bash_exec",
1249
2589
  description=(
1250
2590
  "Execute a bash command inside the current quest. "
1251
- "mode=detach returns immediately. mode=await/create waits for completion. "
2591
+ "mode=detach returns immediately. mode=await/create waits for completion up to a bounded wait window, "
2592
+ "then returns a running-session notice if the command is still active. "
1252
2593
  "mode=read returns the saved log. It returns the full saved log up to 2000 lines, "
1253
2594
  "or a 500-line head plus 1500-line tail preview for longer logs. "
1254
2595
  "Use start/tail for rendered line windows and tail_limit/after_seq for seq-based monitoring. "
@@ -1257,7 +2598,7 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1257
2598
  ),
1258
2599
  )
1259
2600
  def bash_exec(
1260
- command: str = "",
2601
+ command: Any = "",
1261
2602
  mode: str = "detach",
1262
2603
  id: str | None = None,
1263
2604
  reason: str | None = None,
@@ -1266,6 +2607,7 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1266
2607
  export_log: bool = False,
1267
2608
  export_log_to: str | None = None,
1268
2609
  timeout_seconds: int | None = None,
2610
+ wait_timeout_seconds: int | None = None,
1269
2611
  status: str | None = None,
1270
2612
  kind: str | None = None,
1271
2613
  agent_ids: list[str] | None = None,
@@ -1286,6 +2628,28 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1286
2628
  quest_root = context.require_quest_root().resolve()
1287
2629
 
1288
2630
  def finalize(payload: dict[str, Any]) -> dict[str, Any]:
2631
+ if normalized_mode == "read":
2632
+ bash_id = str(payload.get("bash_id") or payload.get("id") or id or "")
2633
+ payload = cached_compact_mcp_tool_result(
2634
+ payload,
2635
+ quest_root=quest_root,
2636
+ run_id=context.run_id,
2637
+ tool_name="bash_exec.bash_exec",
2638
+ detail="read",
2639
+ cache_key={
2640
+ "mode": "read",
2641
+ "bash_id": bash_id,
2642
+ "start": start,
2643
+ "tail": tail,
2644
+ "tail_limit": tail_limit,
2645
+ "before_seq": before_seq,
2646
+ "after_seq": after_seq,
2647
+ "order": order,
2648
+ "include_log": include_log,
2649
+ },
2650
+ source_path=service.terminal_log_path(quest_root, bash_id) if bash_id else None,
2651
+ reason="bash_exec_read_context_budget",
2652
+ )
1289
2653
  quest_service.record_tool_activity(
1290
2654
  quest_root,
1291
2655
  tool_name=f"bash_exec.{normalized_mode}",
@@ -1298,6 +2662,35 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1298
2662
  normalized_mode = "await"
1299
2663
  if normalized_mode not in {"detach", "await", "read", "kill", "list", "history"}:
1300
2664
  raise ValueError("Mode must be one of `detach`, `await`, `create`, `read`, `kill`, `list`, or `history`.")
2665
+ normalized_command = _normalize_bash_exec_command_input(command)
2666
+ normalized_timeout_seconds = _normalize_positive_timeout_seconds(
2667
+ timeout_seconds,
2668
+ field_name="timeout_seconds",
2669
+ )
2670
+ normalized_wait_timeout_seconds = _normalize_positive_timeout_seconds(
2671
+ wait_timeout_seconds,
2672
+ field_name="wait_timeout_seconds",
2673
+ )
2674
+
2675
+ def build_await_payload(session: dict[str, Any], *, wait_timeout: int | None) -> dict[str, Any]:
2676
+ payload = service.build_tool_result(
2677
+ context,
2678
+ session=session,
2679
+ include_log=False,
2680
+ export_log=export_log,
2681
+ export_log_to=export_log_to,
2682
+ )
2683
+ session_status = str(session.get("status") or "").strip().lower()
2684
+ if wait_timeout is not None and session_status not in BASH_EXEC_TERMINAL_STATUSES:
2685
+ payload.update(
2686
+ _build_bash_exec_wait_notice(
2687
+ bash_id=str(session["bash_id"]),
2688
+ wait_timeout_seconds=wait_timeout,
2689
+ status=session_status,
2690
+ )
2691
+ )
2692
+ return payload
2693
+
1301
2694
  if normalized_mode in {"list", "history"}:
1302
2695
  resolved_limit = 500 if normalized_mode == "history" and limit == 20 else max(1, min(limit, 500))
1303
2696
  items = service.list_sessions(
@@ -1356,7 +2749,7 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1356
2749
  tail=tail if tail is not None else tail_limit,
1357
2750
  )
1358
2751
  )
1359
- return finalize(payload)
2752
+ return finalize(_attach_bash_log_truncation_metadata(payload))
1360
2753
  use_tail = tail_limit is not None or before_seq is not None or after_seq is not None or normalized_order != "asc"
1361
2754
  if use_tail:
1362
2755
  resolved_tail_limit = max(1, min(int(tail_limit or 200), 1000))
@@ -1383,6 +2776,39 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1383
2776
  payload["after_seq"] = tail_meta.get("after_seq")
1384
2777
  payload["before_seq"] = tail_meta.get("before_seq")
1385
2778
  payload["order"] = normalized_order
2779
+ visible_seqs = [int(entry.get("seq") or 0) for entry in entries if int(entry.get("seq") or 0) > 0]
2780
+ seq_window_start = min(visible_seqs) if visible_seqs else None
2781
+ seq_window_end = max(visible_seqs) if visible_seqs else None
2782
+ latest_seq_value = tail_meta.get("latest_seq")
2783
+ seqs_before_window = max(int(seq_window_start or 0) - 1, 0) if seq_window_start is not None else None
2784
+ seqs_after_window = (
2785
+ max(int(latest_seq_value or 0) - int(seq_window_end or 0), 0)
2786
+ if seq_window_end is not None and latest_seq_value is not None
2787
+ else None
2788
+ )
2789
+ payload["seq_window_start"] = seq_window_start
2790
+ payload["seq_window_end"] = seq_window_end
2791
+ payload["seq_has_more_before"] = bool(seqs_before_window and seqs_before_window > 0)
2792
+ payload["seq_has_more_after"] = bool(seqs_after_window and seqs_after_window > 0)
2793
+ payload["seqs_before_window"] = seqs_before_window
2794
+ payload["seqs_after_window"] = seqs_after_window
2795
+ payload["tail_is_partial"] = payload["seq_has_more_before"] or payload["seq_has_more_after"]
2796
+ if payload["tail_is_partial"]:
2797
+ before_text = (
2798
+ f"{seqs_before_window} seq(s) before"
2799
+ if seqs_before_window is not None
2800
+ else "some earlier seqs outside this filtered window"
2801
+ )
2802
+ after_text = (
2803
+ f"{seqs_after_window} seq(s) after"
2804
+ if seqs_after_window is not None
2805
+ else "some later seqs outside this filtered window"
2806
+ )
2807
+ payload["log_truncation_notice"] = (
2808
+ "This seq-based bash_exec read is only a partial window, not the full log. "
2809
+ f"It currently covers seq {seq_window_start} to {seq_window_end}; there are {before_text} and {after_text}. "
2810
+ "Continue with before_seq / after_seq or adjust tail_limit if you need more surrounding output."
2811
+ )
1386
2812
  return finalize(payload)
1387
2813
  payload = service.build_tool_result(
1388
2814
  context,
@@ -1392,7 +2818,7 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1392
2818
  export_log_to=export_log_to,
1393
2819
  )
1394
2820
  payload.update(_build_default_bash_log_payload_from_path(service.terminal_log_path(quest_root, bash_id)))
1395
- return finalize(payload)
2821
+ return finalize(_attach_bash_log_truncation_metadata(payload))
1396
2822
  if normalized_mode == "kill":
1397
2823
  bash_id = service.resolve_session_id(quest_root, id)
1398
2824
  session = service.request_stop(
@@ -1403,39 +2829,41 @@ def build_bash_exec_server(context: McpContext) -> FastMCP:
1403
2829
  force=force,
1404
2830
  )
1405
2831
  if wait:
1406
- session = service.wait_for_session(quest_root, bash_id, timeout_seconds=timeout_seconds)
2832
+ resolved_wait_timeout = normalized_wait_timeout_seconds or normalized_timeout_seconds
2833
+ session = service.wait_for_session(quest_root, bash_id, timeout_seconds=resolved_wait_timeout)
1407
2834
  return finalize(service.build_tool_result(context, session=session, include_log=False))
1408
- if normalized_mode == "await" and not command:
2835
+ if normalized_mode == "await" and not normalized_command:
1409
2836
  bash_id = service.resolve_session_id(quest_root, id)
1410
- session = service.wait_for_session(quest_root, bash_id, timeout_seconds=timeout_seconds)
1411
- return finalize(service.build_tool_result(
1412
- context,
1413
- session=session,
1414
- include_log=False,
1415
- export_log=export_log,
1416
- export_log_to=export_log_to,
1417
- ))
1418
- if not (command or "").strip():
2837
+ resolved_wait_timeout = (
2838
+ normalized_wait_timeout_seconds
2839
+ or normalized_timeout_seconds
2840
+ or DEFAULT_BASH_EXEC_AWAIT_WAIT_TIMEOUT_SECONDS
2841
+ )
2842
+ session = service.wait_for_session(quest_root, bash_id, timeout_seconds=resolved_wait_timeout)
2843
+ return finalize(build_await_payload(session, wait_timeout=resolved_wait_timeout))
2844
+ if not normalized_command.strip():
1419
2845
  raise ValueError("command is required for `detach` and `await`.")
1420
2846
  session = service.start_session(
1421
2847
  context,
1422
- command=command,
2848
+ command=normalized_command,
1423
2849
  mode=normalized_mode,
1424
2850
  workdir=workdir,
1425
2851
  env=env,
1426
- timeout_seconds=timeout_seconds,
2852
+ timeout_seconds=normalized_timeout_seconds,
1427
2853
  comment=comment,
1428
2854
  )
1429
2855
  if normalized_mode == "detach":
1430
2856
  return finalize(service.build_tool_result(context, session=session, include_log=False))
1431
- session = service.wait_for_session(quest_root, str(session["bash_id"]), timeout_seconds=timeout_seconds)
1432
- return finalize(service.build_tool_result(
1433
- context,
1434
- session=session,
1435
- include_log=False,
1436
- export_log=export_log,
1437
- export_log_to=export_log_to,
1438
- ))
2857
+ resolved_wait_timeout = (
2858
+ normalized_wait_timeout_seconds
2859
+ or DEFAULT_BASH_EXEC_AWAIT_WAIT_TIMEOUT_SECONDS
2860
+ )
2861
+ session = service.wait_for_session(
2862
+ quest_root,
2863
+ str(session["bash_id"]),
2864
+ timeout_seconds=resolved_wait_timeout,
2865
+ )
2866
+ return finalize(build_await_payload(session, wait_timeout=resolved_wait_timeout))
1439
2867
 
1440
2868
  return server
1441
2869
 
@@ -1458,7 +2886,19 @@ def _resolve_search_scope(context: McpContext, scope: str) -> str:
1458
2886
  return normalized
1459
2887
 
1460
2888
 
2889
+ def _ensure_utf8_stdio() -> None:
2890
+ for stream in (sys.stdin, sys.stdout, sys.stderr):
2891
+ reconfigure = getattr(stream, "reconfigure", None)
2892
+ if not callable(reconfigure):
2893
+ continue
2894
+ try:
2895
+ reconfigure(encoding="utf-8", errors="replace")
2896
+ except Exception:
2897
+ continue
2898
+
2899
+
1461
2900
  def main() -> int:
2901
+ _ensure_utf8_stdio()
1462
2902
  parser = argparse.ArgumentParser(description="DeepScientist built-in MCP server")
1463
2903
  parser.add_argument("--namespace", choices=("memory", "artifact", "bash_exec"), required=True)
1464
2904
  args = parser.parse_args()