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