qwen-sci 0.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of qwen-sci might be problematic. Click here for more details.

Files changed (626) hide show
  1. qwen_sci-0.2.0.dist-info/METADATA +492 -0
  2. qwen_sci-0.2.0.dist-info/RECORD +626 -0
  3. qwen_sci-0.2.0.dist-info/WHEEL +4 -0
  4. qwen_sci-0.2.0.dist-info/entry_points.txt +6 -0
  5. qwen_sci-0.2.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +1 -0
  7. src/__main__.py +5 -0
  8. src/agents/__init__.py +1 -0
  9. src/agents/blog_agent/README.md +299 -0
  10. src/agents/blog_agent/__init__.py +0 -0
  11. src/agents/blog_agent/agent/__init__.py +0 -0
  12. src/agents/blog_agent/agent/llm_config.py +109 -0
  13. src/agents/blog_agent/agent/new_base_agent.py +388 -0
  14. src/agents/blog_agent/scripts/run.py +497 -0
  15. src/agents/blog_agent/skills/blog-analyze/SKILL.md +376 -0
  16. src/agents/blog_agent/skills/blog-refine/SKILL.md +246 -0
  17. src/agents/blog_agent/skills/blog-writer/SKILL.md +218 -0
  18. src/agents/blog_agent/skills/workspace-navigator/SKILL.md +147 -0
  19. src/agents/blog_agent/tools/count_words_tool.py +161 -0
  20. src/agents/blog_agent/tools/download_paper_pdf_tool.py +158 -0
  21. src/agents/blog_agent/tools/gengraph.py +389 -0
  22. src/agents/blog_agent/tools/illustrate.py +116 -0
  23. src/agents/blog_agent/tools/ocr.py +217 -0
  24. src/agents/blog_agent/tools/search_core_tool.py +159 -0
  25. src/agents/blog_agent/tools/search_paper_abstract_tool.py +164 -0
  26. src/agents/blog_agent/utils/deeperaser.pth +0 -0
  27. src/agents/blog_agent/utils/deeperaser.py +381 -0
  28. src/agents/blog_agent/utils/graph.db +0 -0
  29. src/agents/blog_agent/utils/retrieval.py +278 -0
  30. src/agents/blog_agent/utils/search_core.py +159 -0
  31. src/agents/blog_agent/utils/semantic_scholar.py +669 -0
  32. src/agents/experiment_agent/README.md +93 -0
  33. src/agents/experiment_agent/__init__.py +0 -0
  34. src/agents/experiment_agent/agents/__init__.py +4 -0
  35. src/agents/experiment_agent/agents/base/__init__.py +11 -0
  36. src/agents/experiment_agent/agents/base/agent.py +204 -0
  37. src/agents/experiment_agent/agents/code/__init__.py +21 -0
  38. src/agents/experiment_agent/agents/code/entry.py +11 -0
  39. src/agents/experiment_agent/agents/code/planner.py +599 -0
  40. src/agents/experiment_agent/agents/code/reviewer.py +128 -0
  41. src/agents/experiment_agent/agents/code/worker.py +78 -0
  42. src/agents/experiment_agent/agents/finalization/__init__.py +6 -0
  43. src/agents/experiment_agent/agents/finalization/entry.py +350 -0
  44. src/agents/experiment_agent/agents/master/__init__.py +3 -0
  45. src/agents/experiment_agent/agents/master/entry.py +424 -0
  46. src/agents/experiment_agent/agents/prepare/__init__.py +39 -0
  47. src/agents/experiment_agent/agents/prepare/entry.py +7 -0
  48. src/agents/experiment_agent/agents/prepare/planner.py +497 -0
  49. src/agents/experiment_agent/agents/prepare/reviewer.py +99 -0
  50. src/agents/experiment_agent/agents/prepare/worker.py +101 -0
  51. src/agents/experiment_agent/agents/science/__init__.py +21 -0
  52. src/agents/experiment_agent/agents/science/entry.py +11 -0
  53. src/agents/experiment_agent/agents/science/planner.py +639 -0
  54. src/agents/experiment_agent/agents/science/reviewer.py +124 -0
  55. src/agents/experiment_agent/agents/science/worker.py +34 -0
  56. src/agents/experiment_agent/config.py +937 -0
  57. src/agents/experiment_agent/main.py +378 -0
  58. src/agents/experiment_agent/runtime/__init__.py +88 -0
  59. src/agents/experiment_agent/runtime/ablation_results.py +444 -0
  60. src/agents/experiment_agent/runtime/artifacts.py +2481 -0
  61. src/agents/experiment_agent/runtime/code_review_context.py +473 -0
  62. src/agents/experiment_agent/runtime/contracts.py +1346 -0
  63. src/agents/experiment_agent/runtime/finalization_hooks.py +607 -0
  64. src/agents/experiment_agent/runtime/idea_components.py +102 -0
  65. src/agents/experiment_agent/runtime/manifests.py +192 -0
  66. src/agents/experiment_agent/runtime/openharness_runner.py +1091 -0
  67. src/agents/experiment_agent/runtime/openharness_vendor.py +58 -0
  68. src/agents/experiment_agent/runtime/phase_contracts.py +201 -0
  69. src/agents/experiment_agent/runtime/phase_runner.py +2574 -0
  70. src/agents/experiment_agent/runtime/prepare_contracts.py +614 -0
  71. src/agents/experiment_agent/runtime/project_integrity.py +409 -0
  72. src/agents/experiment_agent/runtime/report_layout.py +194 -0
  73. src/agents/experiment_agent/runtime/self_contained.py +179 -0
  74. src/agents/experiment_agent/skills/__init__.py +45 -0
  75. src/agents/experiment_agent/skills/benchmark-discovery/SKILL.md +33 -0
  76. src/agents/experiment_agent/skills/bounded-tool-use/SKILL.md +31 -0
  77. src/agents/experiment_agent/skills/code-enablement/SKILL.md +59 -0
  78. src/agents/experiment_agent/skills/code-planning/SKILL.md +55 -0
  79. src/agents/experiment_agent/skills/component-coverage/SKILL.md +26 -0
  80. src/agents/experiment_agent/skills/environment-setup/SKILL.md +23 -0
  81. src/agents/experiment_agent/skills/prepare-planning/SKILL.md +85 -0
  82. src/agents/experiment_agent/skills/resource-acquisition/SKILL.md +69 -0
  83. src/agents/experiment_agent/skills/science-execution/SKILL.md +46 -0
  84. src/agents/experiment_agent/skills/science-planning/SKILL.md +35 -0
  85. src/agents/experiment_agent/telemetry/__init__.py +161 -0
  86. src/agents/experiment_agent/tools/__init__.py +6 -0
  87. src/agents/experiment_design_agent/__init__.py +268 -0
  88. src/agents/experiment_design_agent/artifacts.py +747 -0
  89. src/agents/experiment_design_agent/cache.py +319 -0
  90. src/agents/experiment_design_agent/completeness.py +130 -0
  91. src/agents/experiment_design_agent/contracts.py +771 -0
  92. src/agents/experiment_design_agent/counterexample_analyzer.py +187 -0
  93. src/agents/experiment_design_agent/design_evidence_paper_screener.py +470 -0
  94. src/agents/experiment_design_agent/discipline_catalog.py +178 -0
  95. src/agents/experiment_design_agent/evidence_cards.py +338 -0
  96. src/agents/experiment_design_agent/evidence_planner.py +452 -0
  97. src/agents/experiment_design_agent/formal_reasoning_planner.py +884 -0
  98. src/agents/experiment_design_agent/fulltext_acquisition.py +863 -0
  99. src/agents/experiment_design_agent/idea_adapter.py +100 -0
  100. src/agents/experiment_design_agent/idea_intake.py +87 -0
  101. src/agents/experiment_design_agent/llm_json.py +328 -0
  102. src/agents/experiment_design_agent/orchestrator.py +1220 -0
  103. src/agents/experiment_design_agent/reasoning_context.py +183 -0
  104. src/agents/experiment_design_agent/reasoning_validation.py +546 -0
  105. src/agents/experiment_design_agent/run.py +481 -0
  106. src/agents/experiment_design_agent/run_logging.py +292 -0
  107. src/agents/experiment_design_agent/scope_gate.py +198 -0
  108. src/agents/experiment_design_agent/study_type_composer.py +1360 -0
  109. src/agents/experiment_design_agent/survey_evidence.py +1381 -0
  110. src/agents/experiment_design_agent/template_router.py +195 -0
  111. src/agents/experiment_design_agent/variable_claim_extractor.py +93 -0
  112. src/agents/idea_agent/__init__.py +1 -0
  113. src/agents/idea_agent/agent/__init__.py +6 -0
  114. src/agents/idea_agent/agent/artifacts.py +308 -0
  115. src/agents/idea_agent/agent/base.py +122 -0
  116. src/agents/idea_agent/agent/ligagent.py +488 -0
  117. src/agents/idea_agent/agent/mcts.py +2060 -0
  118. src/agents/idea_agent/agent/prompts/__init__.py +48 -0
  119. src/agents/idea_agent/agent/prompts/advanced_analysis.py +235 -0
  120. src/agents/idea_agent/agent/prompts/algorithm_alignment.py +55 -0
  121. src/agents/idea_agent/agent/prompts/algorithm_structuring.py +48 -0
  122. src/agents/idea_agent/agent/prompts/component_extraction.py +62 -0
  123. src/agents/idea_agent/agent/prompts/component_novelty_evaluation.py +54 -0
  124. src/agents/idea_agent/agent/prompts/experiment_findings_extraction.py +39 -0
  125. src/agents/idea_agent/agent/prompts/idea_fusion.py +295 -0
  126. src/agents/idea_agent/agent/prompts/idea_introduction.py +30 -0
  127. src/agents/idea_agent/agent/prompts/idea_result_alignment.py +39 -0
  128. src/agents/idea_agent/agent/prompts/input_interpreter.py +52 -0
  129. src/agents/idea_agent/agent/prompts/keynote_ops.py +78 -0
  130. src/agents/idea_agent/agent/prompts/mcts_evaluation.py +235 -0
  131. src/agents/idea_agent/agent/prompts/mcts_generation.py +140 -0
  132. src/agents/idea_agent/agent/prompts/mechanism_commit_query.py +99 -0
  133. src/agents/idea_agent/agent/prompts/memory_selection.py +0 -0
  134. src/agents/idea_agent/agent/prompts/prompt_modes.py +16 -0
  135. src/agents/idea_agent/agent/prompts/rag_query.py +34 -0
  136. src/agents/idea_agent/agent/prompts/re_analysis_replan.py +48 -0
  137. src/agents/idea_agent/agent/prompts/reference_grounding.py +34 -0
  138. src/agents/idea_agent/agent/prompts/root_domain_classification.py +32 -0
  139. src/agents/idea_agent/agent/prompts/scientific_debate.py +88 -0
  140. src/agents/idea_agent/agent/prompts/scientific_materialization.py +45 -0
  141. src/agents/idea_agent/agent/prompts/skill_instantiation.py +193 -0
  142. src/agents/idea_agent/agent/prompts/theory_transfer_query.py +93 -0
  143. src/agents/idea_agent/agent/prompts/topic_background.py +15 -0
  144. src/agents/idea_agent/agent/skills/DEFAULT_SKILL_TEMPLATES.json +263 -0
  145. src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/SKILL.md +40 -0
  146. src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/references/contrast_patterns.md +22 -0
  147. src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/SKILL.md +40 -0
  148. src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/references/mechanism_cards.md +24 -0
  149. src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/SKILL.md +39 -0
  150. src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/references/hierarchy_patterns.md +22 -0
  151. src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/SKILL.md +39 -0
  152. src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/references/mechanism_commit_patterns.md +24 -0
  153. src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/SKILL.md +39 -0
  154. src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/references/coordination_patterns.md +22 -0
  155. src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/SKILL.md +41 -0
  156. src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/references/speculation_patterns.md +22 -0
  157. src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/SKILL.md +39 -0
  158. src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/references/modularity_patterns.md +22 -0
  159. src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/SKILL.md +40 -0
  160. src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/references/transfer_patterns.md +24 -0
  161. src/agents/idea_agent/example/idea_result.json +900 -0
  162. src/agents/idea_agent/run.py +276 -0
  163. src/agents/idea_agent/tests/test_advanced_analysis_contract.py +90 -0
  164. src/agents/idea_agent/tests/test_gap_hypothesis_bridge.py +166 -0
  165. src/agents/idea_agent/tests/test_idea_debate.py +480 -0
  166. src/agents/idea_agent/tests/test_idea_result_v5.py +99 -0
  167. src/agents/idea_agent/tests/test_idea_synthesis.py +92 -0
  168. src/agents/idea_agent/tests/test_mature_idea_sources.py +27 -0
  169. src/agents/idea_agent/tests/test_multimodal_idea_integration.py +386 -0
  170. src/agents/idea_agent/tests/test_profile_materialization_and_scoring.py +219 -0
  171. src/agents/idea_agent/tests/test_profile_native_skill_rendering.py +122 -0
  172. src/agents/idea_agent/tests/test_result_persistence.py +183 -0
  173. src/agents/idea_agent/tests/test_scientific_intervention_profiles.py +241 -0
  174. src/agents/idea_agent/tests/test_scientific_rubric_and_prompts.py +559 -0
  175. src/agents/idea_agent/tests/test_strict_json_object_responses.py +48 -0
  176. src/agents/idea_agent/utils/core/__init__.py +1 -0
  177. src/agents/idea_agent/utils/core/ablation_inputs.py +60 -0
  178. src/agents/idea_agent/utils/core/chat_errors.py +95 -0
  179. src/agents/idea_agent/utils/core/chat_router.py +130 -0
  180. src/agents/idea_agent/utils/core/chat_transport.py +188 -0
  181. src/agents/idea_agent/utils/core/config_loader.py +166 -0
  182. src/agents/idea_agent/utils/core/json_utils.py +28 -0
  183. src/agents/idea_agent/utils/core/logger.py +287 -0
  184. src/agents/idea_agent/utils/core/progress.py +58 -0
  185. src/agents/idea_agent/utils/core/response_parsing.py +57 -0
  186. src/agents/idea_agent/utils/core/run_inputs.py +198 -0
  187. src/agents/idea_agent/utils/mcts/__init__.py +1 -0
  188. src/agents/idea_agent/utils/mcts/component_novelty.py +342 -0
  189. src/agents/idea_agent/utils/mcts/defect_registry.py +302 -0
  190. src/agents/idea_agent/utils/mcts/idea_routes.py +61 -0
  191. src/agents/idea_agent/utils/mcts/idea_taste_presets.py +312 -0
  192. src/agents/idea_agent/utils/mcts/mcts_helpers.py +738 -0
  193. src/agents/idea_agent/utils/mcts/mcts_runtime.py +3000 -0
  194. src/agents/idea_agent/utils/mcts/scientific_intervention_ontology.py +1561 -0
  195. src/agents/idea_agent/utils/mcts/scientific_rubric.py +211 -0
  196. src/agents/idea_agent/utils/mcts/skill_parsing.py +92 -0
  197. src/agents/idea_agent/utils/papers/__init__.py +1 -0
  198. src/agents/idea_agent/utils/papers/paper_graph_vector_store.py +499 -0
  199. src/agents/idea_agent/utils/papers/paper_repository.py +372 -0
  200. src/agents/idea_agent/utils/prompting/__init__.py +1 -0
  201. src/agents/idea_agent/utils/prompting/prompt_views.py +637 -0
  202. src/agents/idea_agent/utils/workflow/__init__.py +1 -0
  203. src/agents/idea_agent/utils/workflow/advanced_analysis_contract.py +244 -0
  204. src/agents/idea_agent/utils/workflow/gap_hypothesis_bridge.py +461 -0
  205. src/agents/idea_agent/utils/workflow/idea_contract.py +426 -0
  206. src/agents/idea_agent/utils/workflow/idea_debate.py +1238 -0
  207. src/agents/idea_agent/utils/workflow/idea_diversity.py +163 -0
  208. src/agents/idea_agent/utils/workflow/idea_fusion.py +889 -0
  209. src/agents/idea_agent/utils/workflow/idea_helpers.py +478 -0
  210. src/agents/idea_agent/utils/workflow/idea_portfolio.py +422 -0
  211. src/agents/idea_agent/utils/workflow/idea_synthesis.py +482 -0
  212. src/agents/idea_agent/utils/workflow/ligagent_flow.py +581 -0
  213. src/agents/idea_agent/utils/workflow/ligagent_handlers.py +1836 -0
  214. src/agents/idea_agent/utils/workflow/ligagent_helpers.py +1331 -0
  215. src/agents/idea_agent/utils/workflow/ligagent_utils.py +322 -0
  216. src/agents/idea_agent/utils/workflow/mature_idea_sources.py +648 -0
  217. src/agents/idea_agent/utils/workflow/multimodal_data_anchoring.py +578 -0
  218. src/agents/idea_agent/utils/workflow/result_persistence.py +774 -0
  219. src/agents/idea_agent/utils/workflow/stage_contract.py +76 -0
  220. src/agents/idea_agent/utils/workflow/workflow_runtime.py +288 -0
  221. src/agents/research_plan_author/__init__.py +50 -0
  222. src/agents/research_plan_author/artifacts.py +270 -0
  223. src/agents/research_plan_author/authoring_blueprint.py +1246 -0
  224. src/agents/research_plan_author/bibtex_renderer.py +252 -0
  225. src/agents/research_plan_author/contract_repair.py +131 -0
  226. src/agents/research_plan_author/contracts.py +745 -0
  227. src/agents/research_plan_author/cross_section_editor.py +232 -0
  228. src/agents/research_plan_author/document_quality.py +987 -0
  229. src/agents/research_plan_author/idea_evolution.py +223 -0
  230. src/agents/research_plan_author/input_loader.py +76 -0
  231. src/agents/research_plan_author/latex_compiler.py +260 -0
  232. src/agents/research_plan_author/latex_safety.py +159 -0
  233. src/agents/research_plan_author/llm_json.py +74 -0
  234. src/agents/research_plan_author/markdown_renderer.py +275 -0
  235. src/agents/research_plan_author/pdf_validator.py +149 -0
  236. src/agents/research_plan_author/render.py +166 -0
  237. src/agents/research_plan_author/render_artifacts.py +187 -0
  238. src/agents/research_plan_author/run.py +765 -0
  239. src/agents/research_plan_author/run_logging.py +209 -0
  240. src/agents/research_plan_author/section_cache.py +184 -0
  241. src/agents/research_plan_author/section_composer.py +1764 -0
  242. src/agents/research_plan_author/section_router.py +209 -0
  243. src/agents/research_plan_author/semantic_validator.py +254 -0
  244. src/agents/research_plan_author/source_bundle.py +46 -0
  245. src/agents/research_plan_author/source_registry.py +208 -0
  246. src/agents/research_plan_author/survey_source_loader.py +137 -0
  247. src/agents/research_plan_author/template_adapter.py +173 -0
  248. src/agents/research_plan_author/template_profile.py +204 -0
  249. src/agents/research_plan_author/tex_renderer.py +496 -0
  250. src/agents/research_plan_author/theory_presentation.py +134 -0
  251. src/agents/research_plan_author/theory_spine.py +703 -0
  252. src/agents/survey_agent/Dockerfile +54 -0
  253. src/agents/survey_agent/README.md +350 -0
  254. src/agents/survey_agent/config/deep_survey.yaml +217 -0
  255. src/agents/survey_agent/config/deep_survey_batch.yaml +176 -0
  256. src/agents/survey_agent/config/deep_survey_fast.yaml +183 -0
  257. src/agents/survey_agent/config/evaluate_baseline.yaml +197 -0
  258. src/agents/survey_agent/config/outcomeRAG.yaml +159 -0
  259. src/agents/survey_agent/env_report.md +348 -0
  260. src/agents/survey_agent/modules/__init__.py +0 -0
  261. src/agents/survey_agent/modules/code_collector.py +2194 -0
  262. src/agents/survey_agent/modules/code_report_generator.py +1282 -0
  263. src/agents/survey_agent/modules/data_manager.py +1713 -0
  264. src/agents/survey_agent/modules/database.py +167 -0
  265. src/agents/survey_agent/modules/fulltext_download_cache.py +462 -0
  266. src/agents/survey_agent/modules/fulltext_http.py +134 -0
  267. src/agents/survey_agent/modules/fulltext_resolution.py +749 -0
  268. src/agents/survey_agent/modules/judge.py +434 -0
  269. src/agents/survey_agent/modules/outcome_RAG.py +236 -0
  270. src/agents/survey_agent/modules/paper_graph_retriever.py +1075 -0
  271. src/agents/survey_agent/modules/pe.py +1729 -0
  272. src/agents/survey_agent/modules/pseudo_creator.py +782 -0
  273. src/agents/survey_agent/modules/pseudo_reviser.py +537 -0
  274. src/agents/survey_agent/modules/refine_agent.py +1952 -0
  275. src/agents/survey_agent/modules/survey_generator.py +5691 -0
  276. src/agents/survey_agent/modules/survey_visualizer.py +1817 -0
  277. src/agents/survey_agent/modules/visual_prompts.py +327 -0
  278. src/agents/survey_agent/modules/work_analyzer.py +2750 -0
  279. src/agents/survey_agent/modules/work_collector.py +5649 -0
  280. src/agents/survey_agent/scripts/baseline_evaluation.py +411 -0
  281. src/agents/survey_agent/scripts/run_deep_survey.py +224 -0
  282. src/agents/survey_agent/scripts/run_deep_survey_adapter.py +407 -0
  283. src/agents/survey_agent/scripts/run_deep_survey_adapter_with_input.py +409 -0
  284. src/agents/survey_agent/scripts/run_deep_survey_batch.py +228 -0
  285. src/agents/survey_agent/scripts/run_deep_survey_batch_arg.py +243 -0
  286. src/agents/survey_agent/scripts/run_deep_survey_batch_settings.py +218 -0
  287. src/agents/survey_agent/scripts/run_keynotes_gen.py +128 -0
  288. src/agents/survey_agent/scripts/run_work_clusters.py +140 -0
  289. src/agents/survey_agent/utils/__init__.py +0 -0
  290. src/agents/survey_agent/utils/ablation_utils.py +317 -0
  291. src/agents/survey_agent/utils/api_call.py +2518 -0
  292. src/agents/survey_agent/utils/chat_utils.py +82 -0
  293. src/agents/survey_agent/utils/config_utils.py +70 -0
  294. src/agents/survey_agent/utils/convert_to_md.py +29 -0
  295. src/agents/survey_agent/utils/err_info.py +24 -0
  296. src/agents/survey_agent/utils/file_utils.py +176 -0
  297. src/agents/survey_agent/utils/gpu_utils.py +145 -0
  298. src/agents/survey_agent/utils/graph_visualizer.py +279 -0
  299. src/agents/survey_agent/utils/html_utils.py +181 -0
  300. src/agents/survey_agent/utils/latex_generator_intra_cluster.py +529 -0
  301. src/agents/survey_agent/utils/latex_generator_relation_table.py +193 -0
  302. src/agents/survey_agent/utils/mineru_section_packer.py +334 -0
  303. src/agents/survey_agent/utils/mineru_utils.py +396 -0
  304. src/agents/survey_agent/utils/paper.py +144 -0
  305. src/agents/survey_agent/utils/read_yaml.py +15 -0
  306. src/agents/survey_agent/utils/repo_utils.py +167 -0
  307. src/agents/survey_agent/utils/rich_logger.py +77 -0
  308. src/agents/survey_agent/utils/step2v2.py +874 -0
  309. src/agents/survey_agent/utils/step2v2_extractor.py +856 -0
  310. src/agents/survey_agent/utils/topic_survey_storage.py +309 -0
  311. src/agents/survey_agent/utils/utils.py +118 -0
  312. src/agents/survey_agent/utils/work.py +2 -0
  313. src/cli.py +1611 -0
  314. src/config/__init__.py +213 -0
  315. src/config/default.yaml +1122 -0
  316. src/harness/LICENSE +21 -0
  317. src/harness/VENDORED.md +14 -0
  318. src/harness/src/__init__.py +0 -0
  319. src/harness/src/openharness/__init__.py +0 -0
  320. src/harness/src/openharness/__main__.py +6 -0
  321. src/harness/src/openharness/api/__init__.py +21 -0
  322. src/harness/src/openharness/api/client.py +281 -0
  323. src/harness/src/openharness/api/codex_client.py +407 -0
  324. src/harness/src/openharness/api/copilot_auth.py +240 -0
  325. src/harness/src/openharness/api/copilot_client.py +134 -0
  326. src/harness/src/openharness/api/errors.py +19 -0
  327. src/harness/src/openharness/api/openai_client.py +577 -0
  328. src/harness/src/openharness/api/provider.py +186 -0
  329. src/harness/src/openharness/api/registry.py +437 -0
  330. src/harness/src/openharness/api/usage.py +17 -0
  331. src/harness/src/openharness/auth/__init__.py +29 -0
  332. src/harness/src/openharness/auth/external.py +610 -0
  333. src/harness/src/openharness/auth/flows.py +189 -0
  334. src/harness/src/openharness/auth/manager.py +482 -0
  335. src/harness/src/openharness/auth/storage.py +269 -0
  336. src/harness/src/openharness/autopilot/__init__.py +23 -0
  337. src/harness/src/openharness/autopilot/service.py +2239 -0
  338. src/harness/src/openharness/autopilot/types.py +91 -0
  339. src/harness/src/openharness/bridge/__init__.py +20 -0
  340. src/harness/src/openharness/bridge/manager.py +106 -0
  341. src/harness/src/openharness/bridge/session_runner.py +46 -0
  342. src/harness/src/openharness/bridge/types.py +37 -0
  343. src/harness/src/openharness/bridge/work_secret.py +41 -0
  344. src/harness/src/openharness/channels/UPSTREAM +6 -0
  345. src/harness/src/openharness/channels/__init__.py +22 -0
  346. src/harness/src/openharness/channels/adapter.py +131 -0
  347. src/harness/src/openharness/channels/bus/__init__.py +6 -0
  348. src/harness/src/openharness/channels/bus/events.py +38 -0
  349. src/harness/src/openharness/channels/bus/queue.py +44 -0
  350. src/harness/src/openharness/channels/impl/__init__.py +6 -0
  351. src/harness/src/openharness/channels/impl/base.py +141 -0
  352. src/harness/src/openharness/channels/impl/dingtalk.py +445 -0
  353. src/harness/src/openharness/channels/impl/discord.py +311 -0
  354. src/harness/src/openharness/channels/impl/email.py +410 -0
  355. src/harness/src/openharness/channels/impl/feishu.py +1342 -0
  356. src/harness/src/openharness/channels/impl/manager.py +257 -0
  357. src/harness/src/openharness/channels/impl/matrix.py +700 -0
  358. src/harness/src/openharness/channels/impl/mochat.py +897 -0
  359. src/harness/src/openharness/channels/impl/qq.py +141 -0
  360. src/harness/src/openharness/channels/impl/slack.py +284 -0
  361. src/harness/src/openharness/channels/impl/telegram.py +525 -0
  362. src/harness/src/openharness/channels/impl/whatsapp.py +159 -0
  363. src/harness/src/openharness/cli.py +2551 -0
  364. src/harness/src/openharness/commands/__init__.py +21 -0
  365. src/harness/src/openharness/commands/registry.py +2783 -0
  366. src/harness/src/openharness/config/__init__.py +34 -0
  367. src/harness/src/openharness/config/paths.py +160 -0
  368. src/harness/src/openharness/config/schema.py +119 -0
  369. src/harness/src/openharness/config/settings.py +1104 -0
  370. src/harness/src/openharness/coordinator/__init__.py +12 -0
  371. src/harness/src/openharness/coordinator/agent_definitions.py +975 -0
  372. src/harness/src/openharness/coordinator/coordinator_mode.py +520 -0
  373. src/harness/src/openharness/engine/__init__.py +80 -0
  374. src/harness/src/openharness/engine/cost_tracker.py +24 -0
  375. src/harness/src/openharness/engine/messages.py +222 -0
  376. src/harness/src/openharness/engine/query.py +1188 -0
  377. src/harness/src/openharness/engine/query_engine.py +313 -0
  378. src/harness/src/openharness/engine/stream_events.py +90 -0
  379. src/harness/src/openharness/hooks/__init__.py +50 -0
  380. src/harness/src/openharness/hooks/events.py +20 -0
  381. src/harness/src/openharness/hooks/executor.py +242 -0
  382. src/harness/src/openharness/hooks/hot_reload.py +31 -0
  383. src/harness/src/openharness/hooks/loader.py +68 -0
  384. src/harness/src/openharness/hooks/schemas.py +66 -0
  385. src/harness/src/openharness/hooks/types.py +38 -0
  386. src/harness/src/openharness/keybindings/__init__.py +14 -0
  387. src/harness/src/openharness/keybindings/default_bindings.py +11 -0
  388. src/harness/src/openharness/keybindings/loader.py +22 -0
  389. src/harness/src/openharness/keybindings/parser.py +18 -0
  390. src/harness/src/openharness/keybindings/resolver.py +13 -0
  391. src/harness/src/openharness/mcp/__init__.py +79 -0
  392. src/harness/src/openharness/mcp/client.py +298 -0
  393. src/harness/src/openharness/mcp/config.py +16 -0
  394. src/harness/src/openharness/mcp/types.py +76 -0
  395. src/harness/src/openharness/memory/__init__.py +25 -0
  396. src/harness/src/openharness/memory/agent.py +91 -0
  397. src/harness/src/openharness/memory/manager.py +183 -0
  398. src/harness/src/openharness/memory/memdir.py +52 -0
  399. src/harness/src/openharness/memory/migrate.py +137 -0
  400. src/harness/src/openharness/memory/paths.py +22 -0
  401. src/harness/src/openharness/memory/relevance.py +145 -0
  402. src/harness/src/openharness/memory/scan.py +116 -0
  403. src/harness/src/openharness/memory/schema.py +443 -0
  404. src/harness/src/openharness/memory/search.py +71 -0
  405. src/harness/src/openharness/memory/team.py +90 -0
  406. src/harness/src/openharness/memory/types.py +33 -0
  407. src/harness/src/openharness/memory/usage.py +153 -0
  408. src/harness/src/openharness/output_styles/__init__.py +5 -0
  409. src/harness/src/openharness/output_styles/loader.py +42 -0
  410. src/harness/src/openharness/permissions/__init__.py +26 -0
  411. src/harness/src/openharness/permissions/checker.py +200 -0
  412. src/harness/src/openharness/permissions/modes.py +13 -0
  413. src/harness/src/openharness/personalization/__init__.py +6 -0
  414. src/harness/src/openharness/personalization/extractor.py +138 -0
  415. src/harness/src/openharness/personalization/rules.py +64 -0
  416. src/harness/src/openharness/personalization/session_hook.py +65 -0
  417. src/harness/src/openharness/platforms.py +86 -0
  418. src/harness/src/openharness/plugins/__init__.py +53 -0
  419. src/harness/src/openharness/plugins/bundled/__init__.py +0 -0
  420. src/harness/src/openharness/plugins/installer.py +39 -0
  421. src/harness/src/openharness/plugins/loader.py +730 -0
  422. src/harness/src/openharness/plugins/schemas.py +24 -0
  423. src/harness/src/openharness/plugins/types.py +59 -0
  424. src/harness/src/openharness/prompts/__init__.py +14 -0
  425. src/harness/src/openharness/prompts/claudemd.py +48 -0
  426. src/harness/src/openharness/prompts/context.py +187 -0
  427. src/harness/src/openharness/prompts/environment.py +135 -0
  428. src/harness/src/openharness/prompts/system_prompt.py +109 -0
  429. src/harness/src/openharness/sandbox/Dockerfile +6 -0
  430. src/harness/src/openharness/sandbox/__init__.py +33 -0
  431. src/harness/src/openharness/sandbox/adapter.py +148 -0
  432. src/harness/src/openharness/sandbox/docker_backend.py +232 -0
  433. src/harness/src/openharness/sandbox/docker_image.py +103 -0
  434. src/harness/src/openharness/sandbox/path_validator.py +37 -0
  435. src/harness/src/openharness/sandbox/session.py +63 -0
  436. src/harness/src/openharness/services/__init__.py +30 -0
  437. src/harness/src/openharness/services/autodream/__init__.py +34 -0
  438. src/harness/src/openharness/services/autodream/backup.py +104 -0
  439. src/harness/src/openharness/services/autodream/lock.py +138 -0
  440. src/harness/src/openharness/services/autodream/prompt.py +128 -0
  441. src/harness/src/openharness/services/autodream/service.py +313 -0
  442. src/harness/src/openharness/services/compact/__init__.py +1725 -0
  443. src/harness/src/openharness/services/cron.py +137 -0
  444. src/harness/src/openharness/services/cron_scheduler.py +595 -0
  445. src/harness/src/openharness/services/lsp/__init__.py +216 -0
  446. src/harness/src/openharness/services/memory_extract/__init__.py +261 -0
  447. src/harness/src/openharness/services/oauth/__init__.py +0 -0
  448. src/harness/src/openharness/services/session_backend.py +97 -0
  449. src/harness/src/openharness/services/session_memory/__init__.py +139 -0
  450. src/harness/src/openharness/services/session_storage.py +230 -0
  451. src/harness/src/openharness/services/token_estimation.py +15 -0
  452. src/harness/src/openharness/services/tool_outputs.py +55 -0
  453. src/harness/src/openharness/skills/__init__.py +44 -0
  454. src/harness/src/openharness/skills/_frontmatter.py +97 -0
  455. src/harness/src/openharness/skills/bundled/__init__.py +75 -0
  456. src/harness/src/openharness/skills/bundled/content/commit.md +25 -0
  457. src/harness/src/openharness/skills/bundled/content/debug.md +25 -0
  458. src/harness/src/openharness/skills/bundled/content/diagnose.md +35 -0
  459. src/harness/src/openharness/skills/bundled/content/plan.md +39 -0
  460. src/harness/src/openharness/skills/bundled/content/review.md +33 -0
  461. src/harness/src/openharness/skills/bundled/content/simplify.md +26 -0
  462. src/harness/src/openharness/skills/bundled/content/skill-creator.md +175 -0
  463. src/harness/src/openharness/skills/bundled/content/test.md +38 -0
  464. src/harness/src/openharness/skills/loader.py +229 -0
  465. src/harness/src/openharness/skills/registry.py +29 -0
  466. src/harness/src/openharness/skills/types.py +24 -0
  467. src/harness/src/openharness/state/__init__.py +6 -0
  468. src/harness/src/openharness/state/app_state.py +30 -0
  469. src/harness/src/openharness/state/store.py +40 -0
  470. src/harness/src/openharness/swarm/__init__.py +70 -0
  471. src/harness/src/openharness/swarm/in_process.py +693 -0
  472. src/harness/src/openharness/swarm/lockfile.py +24 -0
  473. src/harness/src/openharness/swarm/mailbox.py +522 -0
  474. src/harness/src/openharness/swarm/permission_sync.py +1168 -0
  475. src/harness/src/openharness/swarm/registry.py +410 -0
  476. src/harness/src/openharness/swarm/spawn_utils.py +229 -0
  477. src/harness/src/openharness/swarm/subprocess_backend.py +171 -0
  478. src/harness/src/openharness/swarm/team_lifecycle.py +910 -0
  479. src/harness/src/openharness/swarm/types.py +398 -0
  480. src/harness/src/openharness/swarm/worktree.py +315 -0
  481. src/harness/src/openharness/tasks/__init__.py +18 -0
  482. src/harness/src/openharness/tasks/local_agent_task.py +28 -0
  483. src/harness/src/openharness/tasks/local_shell_task.py +17 -0
  484. src/harness/src/openharness/tasks/manager.py +475 -0
  485. src/harness/src/openharness/tasks/stop_task.py +11 -0
  486. src/harness/src/openharness/tasks/types.py +32 -0
  487. src/harness/src/openharness/themes/__init__.py +21 -0
  488. src/harness/src/openharness/themes/builtin.py +89 -0
  489. src/harness/src/openharness/themes/loader.py +55 -0
  490. src/harness/src/openharness/themes/schema.py +54 -0
  491. src/harness/src/openharness/tools/__init__.py +107 -0
  492. src/harness/src/openharness/tools/agent_tool.py +141 -0
  493. src/harness/src/openharness/tools/ask_user_question_tool.py +46 -0
  494. src/harness/src/openharness/tools/base.py +80 -0
  495. src/harness/src/openharness/tools/bash_tool.py +259 -0
  496. src/harness/src/openharness/tools/brief_tool.py +33 -0
  497. src/harness/src/openharness/tools/config_tool.py +54 -0
  498. src/harness/src/openharness/tools/cron_create_tool.py +105 -0
  499. src/harness/src/openharness/tools/cron_delete_tool.py +32 -0
  500. src/harness/src/openharness/tools/cron_list_tool.py +69 -0
  501. src/harness/src/openharness/tools/cron_toggle_tool.py +37 -0
  502. src/harness/src/openharness/tools/enter_plan_mode_tool.py +28 -0
  503. src/harness/src/openharness/tools/enter_worktree_tool.py +80 -0
  504. src/harness/src/openharness/tools/exit_plan_mode_tool.py +28 -0
  505. src/harness/src/openharness/tools/exit_worktree_tool.py +42 -0
  506. src/harness/src/openharness/tools/file_edit_tool.py +95 -0
  507. src/harness/src/openharness/tools/file_read_tool.py +72 -0
  508. src/harness/src/openharness/tools/file_write_tool.py +87 -0
  509. src/harness/src/openharness/tools/glob_tool.py +175 -0
  510. src/harness/src/openharness/tools/grep_tool.py +374 -0
  511. src/harness/src/openharness/tools/image_generation_tool.py +447 -0
  512. src/harness/src/openharness/tools/image_to_text_tool.py +237 -0
  513. src/harness/src/openharness/tools/list_mcp_resources_tool.py +36 -0
  514. src/harness/src/openharness/tools/lsp_tool.py +154 -0
  515. src/harness/src/openharness/tools/mcp_auth_tool.py +71 -0
  516. src/harness/src/openharness/tools/mcp_tool.py +72 -0
  517. src/harness/src/openharness/tools/notebook_edit_tool.py +97 -0
  518. src/harness/src/openharness/tools/read_mcp_resource_tool.py +38 -0
  519. src/harness/src/openharness/tools/remote_trigger_tool.py +74 -0
  520. src/harness/src/openharness/tools/send_message_tool.py +58 -0
  521. src/harness/src/openharness/tools/skill_tool.py +43 -0
  522. src/harness/src/openharness/tools/sleep_tool.py +32 -0
  523. src/harness/src/openharness/tools/task_create_tool.py +56 -0
  524. src/harness/src/openharness/tools/task_get_tool.py +33 -0
  525. src/harness/src/openharness/tools/task_list_tool.py +35 -0
  526. src/harness/src/openharness/tools/task_output_tool.py +35 -0
  527. src/harness/src/openharness/tools/task_stop_tool.py +30 -0
  528. src/harness/src/openharness/tools/task_update_tool.py +50 -0
  529. src/harness/src/openharness/tools/team_create_tool.py +31 -0
  530. src/harness/src/openharness/tools/team_delete_tool.py +30 -0
  531. src/harness/src/openharness/tools/todo_write_tool.py +46 -0
  532. src/harness/src/openharness/tools/tool_search_tool.py +38 -0
  533. src/harness/src/openharness/tools/web_fetch_tool.py +117 -0
  534. src/harness/src/openharness/tools/web_search_tool.py +119 -0
  535. src/harness/src/openharness/ui/__init__.py +5 -0
  536. src/harness/src/openharness/ui/app.py +320 -0
  537. src/harness/src/openharness/ui/backend_host.py +941 -0
  538. src/harness/src/openharness/ui/coordinator_drain.py +198 -0
  539. src/harness/src/openharness/ui/input.py +32 -0
  540. src/harness/src/openharness/ui/output.py +265 -0
  541. src/harness/src/openharness/ui/permission_dialog.py +14 -0
  542. src/harness/src/openharness/ui/protocol.py +247 -0
  543. src/harness/src/openharness/ui/react_launcher.py +179 -0
  544. src/harness/src/openharness/ui/runtime.py +799 -0
  545. src/harness/src/openharness/ui/textual_app.py +495 -0
  546. src/harness/src/openharness/utils/__init__.py +0 -0
  547. src/harness/src/openharness/utils/file_lock.py +86 -0
  548. src/harness/src/openharness/utils/fs.py +98 -0
  549. src/harness/src/openharness/utils/helpers.py +77 -0
  550. src/harness/src/openharness/utils/network_guard.py +340 -0
  551. src/harness/src/openharness/utils/shell.py +147 -0
  552. src/harness/src/openharness/vim/__init__.py +5 -0
  553. src/harness/src/openharness/vim/transitions.py +8 -0
  554. src/harness/src/openharness/voice/__init__.py +7 -0
  555. src/harness/src/openharness/voice/keyterms.py +10 -0
  556. src/harness/src/openharness/voice/stream_stt.py +8 -0
  557. src/harness/src/openharness/voice/voice_mode.py +44 -0
  558. src/llm/__init__.py +29 -0
  559. src/llm/image_generation.py +402 -0
  560. src/llm/provider_registry.py +419 -0
  561. src/llm/runtime_env.py +64 -0
  562. src/llm/structured_output.py +131 -0
  563. src/llm/vision.py +198 -0
  564. src/memory/__init__.py +2 -0
  565. src/memory/api/__init__.py +1 -0
  566. src/memory/api/base_memory_system_api.py +98 -0
  567. src/memory/api/base_symbolic_memory_system_api.py +190 -0
  568. src/memory/api/base_vector_memory_system_api.py +116 -0
  569. src/memory/api/faiss_memory_system_api.py +228 -0
  570. src/memory/api/slot_process_api.py +827 -0
  571. src/memory/api/symbolic_memory_system_api.py +572 -0
  572. src/memory/memory_system/__init__.py +48 -0
  573. src/memory/memory_system/component_taxonomy.py +430 -0
  574. src/memory/memory_system/llm.py +437 -0
  575. src/memory/memory_system/models.py +168 -0
  576. src/memory/memory_system/user_prompt.py +418 -0
  577. src/memory/memory_system/utils.py +282 -0
  578. src/memory/memory_system/vectorstore.py +311 -0
  579. src/memory/memory_system/working_slot.py +99 -0
  580. src/pipeline/__init__.py +129 -0
  581. src/pipeline/contracts.py +531 -0
  582. src/pipeline/discipline_taxonomy.py +916 -0
  583. src/pipeline/evidence_coverage_ledger.py +1150 -0
  584. src/pipeline/evidence_refinement.py +620 -0
  585. src/pipeline/experiment_to_symbolic.py +137 -0
  586. src/pipeline/multimodal_evidence/__init__.py +28 -0
  587. src/pipeline/multimodal_evidence/capabilities.py +107 -0
  588. src/pipeline/multimodal_evidence/claims.py +135 -0
  589. src/pipeline/multimodal_evidence/contract.py +291 -0
  590. src/pipeline/multimodal_evidence/data_sh_compiler.py +505 -0
  591. src/pipeline/multimodal_evidence/handoff_binding.py +135 -0
  592. src/pipeline/multimodal_evidence/manifest.py +254 -0
  593. src/pipeline/multimodal_evidence/native_analysis.py +479 -0
  594. src/pipeline/multimodal_evidence/perception.py +277 -0
  595. src/pipeline/multimodal_evidence/query_binding.py +65 -0
  596. src/pipeline/multimodal_evidence/rendering.py +120 -0
  597. src/pipeline/multimodal_evidence/safety.py +42 -0
  598. src/pipeline/multimodal_evidence/sampling.py +69 -0
  599. src/pipeline/multimodal_evidence/service.py +148 -0
  600. src/pipeline/multimodal_evidence/survey_integration.py +460 -0
  601. src/pipeline/paper_identity.py +52 -0
  602. src/pipeline/research_design_inventory.py +338 -0
  603. src/pipeline/research_domain_resolution.py +669 -0
  604. src/pipeline/research_identity.py +548 -0
  605. src/pipeline/research_question_contract.py +646 -0
  606. src/pipeline/retrieval_lanes.py +889 -0
  607. src/pipeline/run_loop.py +862 -0
  608. src/pipeline/science_events.py +30 -0
  609. src/pipeline/science_manifests.py +602 -0
  610. src/pipeline/science_run.py +918 -0
  611. src/pipeline/science_stages.py +709 -0
  612. src/pipeline/science_workflow.py +807 -0
  613. src/pipeline/sh_cluster_projection.py +306 -0
  614. src/pipeline/sh_graph_provenance.py +648 -0
  615. src/pipeline/subhypothesis_decomposition.py +392 -0
  616. src/pipeline/subhypothesis_evidence.py +856 -0
  617. src/pipeline/survey_evidence_plan.py +858 -0
  618. src/pipeline/survey_gap_adjudication.py +647 -0
  619. src/pipeline/survey_gap_candidates.py +840 -0
  620. src/pipeline/survey_gap_ledger.py +683 -0
  621. src/pipeline/survey_gap_triage.py +401 -0
  622. src/pipeline/survey_handoff_persistence.py +481 -0
  623. src/pipeline/survey_handoff_projection.py +457 -0
  624. src/pipeline/survey_idea_handoff.py +1112 -0
  625. src/pipeline/survey_idea_loader.py +289 -0
  626. src/research_run_ids.py +11 -0
@@ -0,0 +1,520 @@
1
+ """Coordinator mode detection and orchestration support."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional
9
+ from xml.sax.saxutils import escape, unescape
10
+
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # TeamRegistry (kept for backward compatibility)
14
+ # ---------------------------------------------------------------------------
15
+
16
+
17
+ @dataclass
18
+ class TeamRecord:
19
+ """A lightweight in-memory team."""
20
+
21
+ name: str
22
+ description: str = ""
23
+ agents: list[str] = field(default_factory=list)
24
+ messages: list[str] = field(default_factory=list)
25
+
26
+
27
+ class TeamRegistry:
28
+ """Store teams and agent memberships."""
29
+
30
+ def __init__(self) -> None:
31
+ self._teams: dict[str, TeamRecord] = {}
32
+
33
+ def create_team(self, name: str, description: str = "") -> TeamRecord:
34
+ if name in self._teams:
35
+ raise ValueError(f"Team '{name}' already exists")
36
+ team = TeamRecord(name=name, description=description)
37
+ self._teams[name] = team
38
+ return team
39
+
40
+ def delete_team(self, name: str) -> None:
41
+ if name not in self._teams:
42
+ raise ValueError(f"Team '{name}' does not exist")
43
+ del self._teams[name]
44
+
45
+ def add_agent(self, team_name: str, task_id: str) -> None:
46
+ team = self._require_team(team_name)
47
+ if task_id not in team.agents:
48
+ team.agents.append(task_id)
49
+
50
+ def send_message(self, team_name: str, message: str) -> None:
51
+ self._require_team(team_name).messages.append(message)
52
+
53
+ def list_teams(self) -> list[TeamRecord]:
54
+ return sorted(self._teams.values(), key=lambda item: item.name)
55
+
56
+ def _require_team(self, name: str) -> TeamRecord:
57
+ team = self._teams.get(name)
58
+ if team is None:
59
+ raise ValueError(f"Team '{name}' does not exist")
60
+ return team
61
+
62
+
63
+ _DEFAULT_TEAM_REGISTRY: TeamRegistry | None = None
64
+
65
+
66
+ def get_team_registry() -> TeamRegistry:
67
+ """Return the singleton team registry."""
68
+ global _DEFAULT_TEAM_REGISTRY
69
+ if _DEFAULT_TEAM_REGISTRY is None:
70
+ _DEFAULT_TEAM_REGISTRY = TeamRegistry()
71
+ return _DEFAULT_TEAM_REGISTRY
72
+
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Data classes
76
+ # ---------------------------------------------------------------------------
77
+
78
+
79
+ @dataclass
80
+ class TaskNotification:
81
+ """Structured result from a completed agent task."""
82
+
83
+ task_id: str
84
+ status: str
85
+ summary: str
86
+ result: Optional[str] = None
87
+ usage: Optional[dict[str, int]] = None
88
+
89
+
90
+ @dataclass
91
+ class WorkerConfig:
92
+ """Configuration for a spawned worker agent."""
93
+
94
+ agent_id: str
95
+ name: str
96
+ prompt: str
97
+ model: Optional[str] = None
98
+ color: Optional[str] = None
99
+ team: Optional[str] = None
100
+
101
+
102
+ # ---------------------------------------------------------------------------
103
+ # XML helpers
104
+ # ---------------------------------------------------------------------------
105
+
106
+ _USAGE_FIELDS = ("total_tokens", "tool_uses", "duration_ms")
107
+
108
+
109
+ def format_task_notification(n: TaskNotification) -> str:
110
+ """Serialize a TaskNotification to the canonical XML envelope."""
111
+ parts = [
112
+ "<task-notification>",
113
+ f"<task-id>{escape(n.task_id)}</task-id>",
114
+ f"<status>{escape(n.status)}</status>",
115
+ f"<summary>{escape(n.summary)}</summary>",
116
+ ]
117
+ if n.result is not None:
118
+ parts.append(f"<result>{escape(n.result)}</result>")
119
+ if n.usage:
120
+ parts.append("<usage>")
121
+ for key in _USAGE_FIELDS:
122
+ if key in n.usage:
123
+ parts.append(f" <{key}>{n.usage[key]}</{key}>")
124
+ parts.append("</usage>")
125
+ parts.append("</task-notification>")
126
+ return "\n".join(parts)
127
+
128
+
129
+ def parse_task_notification(xml: str) -> TaskNotification:
130
+ """Parse a <task-notification> XML string into a TaskNotification."""
131
+
132
+ def _extract(tag: str) -> Optional[str]:
133
+ m = re.search(rf"<{tag}>(.*?)</{tag}>", xml, re.DOTALL)
134
+ return unescape(m.group(1).strip()) if m else None
135
+
136
+ task_id = _extract("task-id") or ""
137
+ status = _extract("status") or ""
138
+ summary = _extract("summary") or ""
139
+ result = _extract("result")
140
+
141
+ usage: Optional[dict[str, int]] = None
142
+ usage_block = re.search(r"<usage>(.*?)</usage>", xml, re.DOTALL)
143
+ if usage_block:
144
+ usage = {}
145
+ for key in _USAGE_FIELDS:
146
+ m = re.search(rf"<{key}>(\d+)</{key}>", usage_block.group(1))
147
+ if m:
148
+ usage[key] = int(m.group(1))
149
+
150
+ return TaskNotification(
151
+ task_id=task_id,
152
+ status=status,
153
+ summary=summary,
154
+ result=result,
155
+ usage=usage,
156
+ )
157
+
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # CoordinatorMode
161
+ # ---------------------------------------------------------------------------
162
+
163
+ _AGENT_TOOL_NAME = "agent"
164
+ _SEND_MESSAGE_TOOL_NAME = "send_message"
165
+ _TASK_STOP_TOOL_NAME = "task_stop"
166
+
167
+ _WORKER_TOOLS = [
168
+ "bash",
169
+ "file_read",
170
+ "file_edit",
171
+ "file_write",
172
+ "glob",
173
+ "grep",
174
+ "web_fetch",
175
+ "web_search",
176
+ "task_create",
177
+ "task_get",
178
+ "task_list",
179
+ "task_output",
180
+ "skill",
181
+ ]
182
+
183
+ _SIMPLE_WORKER_TOOLS = ["bash", "file_read", "file_edit"]
184
+
185
+
186
+ def is_coordinator_mode() -> bool:
187
+ """Return True when the process is running in coordinator mode."""
188
+ val = os.environ.get("CLAUDE_CODE_COORDINATOR_MODE", "")
189
+ return val.lower() in {"1", "true", "yes"}
190
+
191
+
192
+ def match_session_mode(session_mode: Optional[str]) -> Optional[str]:
193
+ """Align the env-var coordinator flag with a resumed session's stored mode.
194
+
195
+ Returns a warning string if the mode was switched, or None if no change.
196
+ """
197
+ if not session_mode:
198
+ return None
199
+
200
+ current_is_coordinator = is_coordinator_mode()
201
+ session_is_coordinator = session_mode == "coordinator"
202
+
203
+ if current_is_coordinator == session_is_coordinator:
204
+ return None
205
+
206
+ if session_is_coordinator:
207
+ os.environ["CLAUDE_CODE_COORDINATOR_MODE"] = "1"
208
+ else:
209
+ os.environ.pop("CLAUDE_CODE_COORDINATOR_MODE", None)
210
+
211
+ if session_is_coordinator:
212
+ return "Entered coordinator mode to match resumed session."
213
+ return "Exited coordinator mode to match resumed session."
214
+
215
+
216
+ def get_coordinator_tools() -> list[str]:
217
+ """Return the tool names reserved for the coordinator."""
218
+ return [_AGENT_TOOL_NAME, _SEND_MESSAGE_TOOL_NAME, _TASK_STOP_TOOL_NAME]
219
+
220
+
221
+ def get_coordinator_user_context(
222
+ mcp_clients: list[dict[str, str]] | None = None,
223
+ scratchpad_dir: Optional[str] = None,
224
+ ) -> dict[str, str]:
225
+ """Build the workerToolsContext injected into the coordinator's user turn."""
226
+ if not is_coordinator_mode():
227
+ return {}
228
+
229
+ is_simple = os.environ.get("CLAUDE_CODE_SIMPLE", "").lower() in {"1", "true", "yes"}
230
+ tools = sorted(_SIMPLE_WORKER_TOOLS if is_simple else _WORKER_TOOLS)
231
+ worker_tools_str = ", ".join(tools)
232
+
233
+ content = (
234
+ f"Workers spawned via the {_AGENT_TOOL_NAME} tool have access to these tools: "
235
+ f"{worker_tools_str}"
236
+ )
237
+
238
+ if mcp_clients:
239
+ server_names = ", ".join(c["name"] for c in mcp_clients)
240
+ content += f"\n\nWorkers also have access to MCP tools from connected MCP servers: {server_names}"
241
+
242
+ if scratchpad_dir:
243
+ content += (
244
+ f"\n\nScratchpad directory: {scratchpad_dir}\n"
245
+ "Workers can read and write here without permission prompts. "
246
+ "Use this for durable cross-worker knowledge — structure files however fits the work."
247
+ )
248
+
249
+ return {"workerToolsContext": content}
250
+
251
+
252
+ def get_coordinator_system_prompt() -> str:
253
+ """Return the system prompt injected when running in coordinator mode."""
254
+ is_simple = os.environ.get("CLAUDE_CODE_SIMPLE", "").lower() in {"1", "true", "yes"}
255
+
256
+ if is_simple:
257
+ worker_capabilities = (
258
+ "Workers have access to Bash, Read, and Edit tools, "
259
+ "plus MCP tools from configured MCP servers."
260
+ )
261
+ else:
262
+ worker_capabilities = (
263
+ "Workers have access to standard tools, MCP tools from configured MCP servers, "
264
+ "and project skills via the Skill tool. "
265
+ "Delegate skill invocations (e.g. /commit, /verify) to workers."
266
+ )
267
+
268
+ return f"""You are Claude Code, an AI assistant that orchestrates software engineering tasks across multiple workers.
269
+
270
+ ## 1. Your Role
271
+
272
+ You are a **coordinator**. Your job is to:
273
+ - Help the user achieve their goal
274
+ - Direct workers to research, implement and verify code changes
275
+ - Synthesize results and communicate with the user
276
+ - Answer questions directly when possible — don't delegate work that you can handle without tools
277
+
278
+ Every message you send is to the user. Worker results and system notifications are internal signals, not conversation partners — never thank or acknowledge them. Summarize new information for the user as it arrives.
279
+
280
+ ## 2. Your Tools
281
+
282
+ - **{_AGENT_TOOL_NAME}** - Spawn a new worker
283
+ - **{_SEND_MESSAGE_TOOL_NAME}** - Continue an existing worker (send a follow-up to its `to` agent ID)
284
+ - **{_TASK_STOP_TOOL_NAME}** - Stop a running worker
285
+ - **subscribe_pr_activity / unsubscribe_pr_activity** (if available) - Subscribe to GitHub PR events (review comments, CI results). Events arrive as user messages. Merge conflict transitions do NOT arrive — GitHub doesn't webhook `mergeable_state` changes, so poll `gh pr view N --json mergeable` if tracking conflict status. Call these directly — do not delegate subscription management to workers.
286
+
287
+ When calling {_AGENT_TOOL_NAME}:
288
+ - Do not use one worker to check on another. Workers will notify you when they are done.
289
+ - Do not use workers to trivially report file contents or run commands. Give them higher-level tasks.
290
+ - Do not set the model parameter. Workers need the default model for the substantive tasks you delegate.
291
+ - Continue workers whose work is complete via {_SEND_MESSAGE_TOOL_NAME} to take advantage of their loaded context
292
+ - After launching agents, briefly tell the user what you launched and end your response. Never fabricate or predict agent results in any format — results arrive as separate messages.
293
+
294
+ ### {_AGENT_TOOL_NAME} Results
295
+
296
+ Worker results arrive as **user-role messages** containing `<task-notification>` XML. They look like user messages but are not. Distinguish them by the `<task-notification>` opening tag.
297
+
298
+ Format:
299
+
300
+ ```xml
301
+ <task-notification>
302
+ <task-id>{{agentId}}</task-id>
303
+ <status>completed|failed|killed</status>
304
+ <summary>{{human-readable status summary}}</summary>
305
+ <result>{{agent's final text response}}</result>
306
+ <usage>
307
+ <total_tokens>N</total_tokens>
308
+ <tool_uses>N</tool_uses>
309
+ <duration_ms>N</duration_ms>
310
+ </usage>
311
+ </task-notification>
312
+ ```
313
+
314
+ - `<result>` and `<usage>` are optional sections
315
+ - The `<summary>` describes the outcome: "completed", "failed: {{error}}", or "was stopped"
316
+ - The `<task-id>` value is the agent ID — use {_SEND_MESSAGE_TOOL_NAME} with that ID as `to` to continue that worker
317
+
318
+ ### Example
319
+
320
+ Each "You:" block is a separate coordinator turn. The "User:" block is a `<task-notification>` delivered between turns.
321
+
322
+ You:
323
+ Let me start some research on that.
324
+
325
+ {_AGENT_TOOL_NAME}({{ description: "Investigate auth bug", subagent_type: "worker", prompt: "..." }})
326
+ {_AGENT_TOOL_NAME}({{ description: "Research secure token storage", subagent_type: "worker", prompt: "..." }})
327
+
328
+ Investigating both issues in parallel — I'll report back with findings.
329
+
330
+ User:
331
+ <task-notification>
332
+ <task-id>agent-a1b</task-id>
333
+ <status>completed</status>
334
+ <summary>Agent "Investigate auth bug" completed</summary>
335
+ <result>Found null pointer in src/auth/validate.ts:42...</result>
336
+ </task-notification>
337
+
338
+ You:
339
+ Found the bug — null pointer in confirmTokenExists in validate.ts. I'll fix it.
340
+ Still waiting on the token storage research.
341
+
342
+ {_SEND_MESSAGE_TOOL_NAME}({{ to: "agent-a1b", message: "Fix the null pointer in src/auth/validate.ts:42..." }})
343
+
344
+ ## 3. Workers
345
+
346
+ When calling {_AGENT_TOOL_NAME}, use subagent_type `worker`. Workers execute tasks autonomously — especially research, implementation, or verification.
347
+
348
+ {worker_capabilities}
349
+
350
+ ## 4. Task Workflow
351
+
352
+ Most tasks can be broken down into the following phases:
353
+
354
+ ### Phases
355
+
356
+ | Phase | Who | Purpose |
357
+ |-------|-----|---------|
358
+ | Research | Workers (parallel) | Investigate codebase, find files, understand problem |
359
+ | Synthesis | **You** (coordinator) | Read findings, understand the problem, craft implementation specs (see Section 5) |
360
+ | Implementation | Workers | Make targeted changes per spec, commit |
361
+ | Verification | Workers | Test changes work |
362
+
363
+ ### Concurrency
364
+
365
+ **Parallelism is your superpower. Workers are async. Launch independent workers concurrently whenever possible — don't serialize work that can run simultaneously and look for opportunities to fan out. When doing research, cover multiple angles. To launch workers in parallel, make multiple tool calls in a single message.**
366
+
367
+ Manage concurrency:
368
+ - **Read-only tasks** (research) — run in parallel freely
369
+ - **Write-heavy tasks** (implementation) — one at a time per set of files
370
+ - **Verification** can sometimes run alongside implementation on different file areas
371
+
372
+ ### What Real Verification Looks Like
373
+
374
+ Verification means **proving the code works**, not confirming it exists. A verifier that rubber-stamps weak work undermines everything.
375
+
376
+ - Run tests **with the feature enabled** — not just "tests pass"
377
+ - Run typechecks and **investigate errors** — don't dismiss as "unrelated"
378
+ - Be skeptical — if something looks off, dig in
379
+ - **Test independently** — prove the change works, don't rubber-stamp
380
+
381
+ ### Handling Worker Failures
382
+
383
+ When a worker reports failure (tests failed, build errors, file not found):
384
+ - Continue the same worker with {_SEND_MESSAGE_TOOL_NAME} — it has the full error context
385
+ - If a correction attempt fails, try a different approach or report to the user
386
+
387
+ ### Stopping Workers
388
+
389
+ Use {_TASK_STOP_TOOL_NAME} to stop a worker you sent in the wrong direction — for example, when you realize mid-flight that the approach is wrong, or the user changes requirements after you launched the worker. Pass the `task_id` from the {_AGENT_TOOL_NAME} tool's launch result. Stopped workers can be continued with {_SEND_MESSAGE_TOOL_NAME}.
390
+
391
+ ```
392
+ // Launched a worker to refactor auth to use JWT
393
+ {_AGENT_TOOL_NAME}({{ description: "Refactor auth to JWT", subagent_type: "worker", prompt: "Replace session-based auth with JWT..." }})
394
+ // ... returns task_id: "agent-x7q" ...
395
+
396
+ // User clarifies: "Actually, keep sessions — just fix the null pointer"
397
+ {_TASK_STOP_TOOL_NAME}({{ task_id: "agent-x7q" }})
398
+
399
+ // Continue with corrected instructions
400
+ {_SEND_MESSAGE_TOOL_NAME}({{ to: "agent-x7q", message: "Stop the JWT refactor. Instead, fix the null pointer in src/auth/validate.ts:42..." }})
401
+ ```
402
+
403
+ ## 5. Writing Worker Prompts
404
+
405
+ **Workers can't see your conversation.** Every prompt must be self-contained with everything the worker needs. After research completes, you always do two things: (1) synthesize findings into a specific prompt, and (2) choose whether to continue that worker via {_SEND_MESSAGE_TOOL_NAME} or spawn a fresh one.
406
+
407
+ ### Always synthesize — your most important job
408
+
409
+ When workers report research findings, **you must understand them before directing follow-up work**. Read the findings. Identify the approach. Then write a prompt that proves you understood by including specific file paths, line numbers, and exactly what to change.
410
+
411
+ Never write "based on your findings" or "based on the research." These phrases delegate understanding to the worker instead of doing it yourself. You never hand off understanding to another worker.
412
+
413
+ ```
414
+ // Anti-pattern — lazy delegation (bad whether continuing or spawning)
415
+ {_AGENT_TOOL_NAME}({{ prompt: "Based on your findings, fix the auth bug", ... }})
416
+ {_AGENT_TOOL_NAME}({{ prompt: "The worker found an issue in the auth module. Please fix it.", ... }})
417
+
418
+ // Good — synthesized spec (works with either continue or spawn)
419
+ {_AGENT_TOOL_NAME}({{ prompt: "Fix the null pointer in src/auth/validate.ts:42. The user field on Session (src/auth/types.ts:15) is undefined when sessions expire but the token remains cached. Add a null check before user.id access — if null, return 401 with 'Session expired'. Commit and report the hash.", ... }})
420
+ ```
421
+
422
+ A well-synthesized spec gives the worker everything it needs in a few sentences. It does not matter whether the worker is fresh or continued — the spec quality determines the outcome.
423
+
424
+ ### Add a purpose statement
425
+
426
+ Include a brief purpose so workers can calibrate depth and emphasis:
427
+
428
+ - "This research will inform a PR description — focus on user-facing changes."
429
+ - "I need this to plan an implementation — report file paths, line numbers, and type signatures."
430
+ - "This is a quick check before we merge — just verify the happy path."
431
+
432
+ ### Choose continue vs. spawn by context overlap
433
+
434
+ After synthesizing, decide whether the worker's existing context helps or hurts:
435
+
436
+ | Situation | Mechanism | Why |
437
+ |-----------|-----------|-----|
438
+ | Research explored exactly the files that need editing | **Continue** ({_SEND_MESSAGE_TOOL_NAME}) with synthesized spec | Worker already has the files in context AND now gets a clear plan |
439
+ | Research was broad but implementation is narrow | **Spawn fresh** ({_AGENT_TOOL_NAME}) with synthesized spec | Avoid dragging along exploration noise; focused context is cleaner |
440
+ | Correcting a failure or extending recent work | **Continue** | Worker has the error context and knows what it just tried |
441
+ | Verifying code a different worker just wrote | **Spawn fresh** | Verifier should see the code with fresh eyes, not carry implementation assumptions |
442
+ | First implementation attempt used the wrong approach entirely | **Spawn fresh** | Wrong-approach context pollutes the retry; clean slate avoids anchoring on the failed path |
443
+ | Completely unrelated task | **Spawn fresh** | No useful context to reuse |
444
+
445
+ There is no universal default. Think about how much of the worker's context overlaps with the next task. High overlap -> continue. Low overlap -> spawn fresh.
446
+
447
+ ### Continue mechanics
448
+
449
+ When continuing a worker with {_SEND_MESSAGE_TOOL_NAME}, it has full context from its previous run:
450
+ ```
451
+ // Continuation — worker finished research, now give it a synthesized implementation spec
452
+ {_SEND_MESSAGE_TOOL_NAME}({{ to: "xyz-456", message: "Fix the null pointer in src/auth/validate.ts:42. The user field is undefined when Session.expired is true but the token is still cached. Add a null check before accessing user.id — if null, return 401 with 'Session expired'. Commit and report the hash." }})
453
+ ```
454
+
455
+ ```
456
+ // Correction — worker just reported test failures from its own change, keep it brief
457
+ {_SEND_MESSAGE_TOOL_NAME}({{ to: "xyz-456", message: "Two tests still failing at lines 58 and 72 — update the assertions to match the new error message." }})
458
+ ```
459
+
460
+ ### Prompt tips
461
+
462
+ **Good examples:**
463
+
464
+ 1. Implementation: "Fix the null pointer in src/auth/validate.ts:42. The user field can be undefined when the session expires. Add a null check and return early with an appropriate error. Commit and report the hash."
465
+
466
+ 2. Precise git operation: "Create a new branch from main called 'fix/session-expiry'. Cherry-pick only commit abc123 onto it. Push and create a draft PR targeting main. Add anthropics/claude-code as reviewer. Report the PR URL."
467
+
468
+ 3. Correction (continued worker, short): "The tests failed on the null check you added — validate.test.ts:58 expects 'Invalid session' but you changed it to 'Session expired'. Fix the assertion. Commit and report the hash."
469
+
470
+ **Bad examples:**
471
+
472
+ 1. "Fix the bug we discussed" — no context, workers can't see your conversation
473
+ 2. "Based on your findings, implement the fix" — lazy delegation; synthesize the findings yourself
474
+ 3. "Create a PR for the recent changes" — ambiguous scope: which changes? which branch? draft?
475
+ 4. "Something went wrong with the tests, can you look?" — no error message, no file path, no direction
476
+
477
+ Additional tips:
478
+ - Include file paths, line numbers, error messages — workers start fresh and need complete context
479
+ - State what "done" looks like
480
+ - For implementation: "Run relevant tests and typecheck, then commit your changes and report the hash" — workers self-verify before reporting done. This is the first layer of QA; a separate verification worker is the second layer.
481
+ - For research: "Report findings — do not modify files"
482
+ - Be precise about git operations — specify branch names, commit hashes, draft vs ready, reviewers
483
+ - When continuing for corrections: reference what the worker did ("the null check you added") not what you discussed with the user
484
+ - For implementation: "Fix the root cause, not the symptom" — guide workers toward durable fixes
485
+ - For verification: "Prove the code works, don't just confirm it exists"
486
+ - For verification: "Try edge cases and error paths — don't just re-run what the implementation worker ran"
487
+ - For verification: "Investigate failures — don't dismiss as unrelated without evidence"
488
+
489
+ ## 6. Example Session
490
+
491
+ User: "There's a null pointer in the auth module. Can you fix it?"
492
+
493
+ You:
494
+ Let me investigate first.
495
+
496
+ {_AGENT_TOOL_NAME}({{ description: "Investigate auth bug", subagent_type: "worker", prompt: "Investigate the auth module in src/auth/. Find where null pointer exceptions could occur around session handling and token validation... Report specific file paths, line numbers, and types involved. Do not modify files." }})
497
+ {_AGENT_TOOL_NAME}({{ description: "Research auth tests", subagent_type: "worker", prompt: "Find all test files related to src/auth/. Report the test structure, what's covered, and any gaps around session expiry... Do not modify files." }})
498
+
499
+ Investigating from two angles — I'll report back with findings.
500
+
501
+ User:
502
+ <task-notification>
503
+ <task-id>agent-a1b</task-id>
504
+ <status>completed</status>
505
+ <summary>Agent "Investigate auth bug" completed</summary>
506
+ <result>Found null pointer in src/auth/validate.ts:42. The user field on Session is undefined when the session expires but ...</result>
507
+ </task-notification>
508
+
509
+ You:
510
+ Found the bug — null pointer in validate.ts:42.
511
+
512
+ {_SEND_MESSAGE_TOOL_NAME}({{ to: "agent-a1b", message: "Fix the null pointer in src/auth/validate.ts:42. Add a null check before accessing user.id — if null, return 401 with 'Session expired'. Commit and report the hash." }})
513
+
514
+ Fix is in progress.
515
+
516
+ User:
517
+ How's it going?
518
+
519
+ You:
520
+ Fix for the new test is in progress. Still waiting to hear back about the test suite."""
@@ -0,0 +1,80 @@
1
+ """Core engine exports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING: # pragma: no cover
8
+ from openharness.engine.messages import (
9
+ ConversationMessage,
10
+ ImageBlock,
11
+ TextBlock,
12
+ ToolResultBlock,
13
+ ToolUseBlock,
14
+ )
15
+ from openharness.engine.query_engine import QueryEngine
16
+ from openharness.engine.stream_events import (
17
+ AssistantTextDelta,
18
+ AssistantTurnComplete,
19
+ ToolExecutionCompleted,
20
+ ToolExecutionStarted,
21
+ )
22
+
23
+ __all__ = [
24
+ "AssistantTextDelta",
25
+ "AssistantTurnComplete",
26
+ "ConversationMessage",
27
+ "ImageBlock",
28
+ "QueryEngine",
29
+ "TextBlock",
30
+ "ToolExecutionCompleted",
31
+ "ToolExecutionStarted",
32
+ "ToolResultBlock",
33
+ "ToolUseBlock",
34
+ ]
35
+
36
+
37
+ def __getattr__(name: str):
38
+ if name in {"ConversationMessage", "ImageBlock", "TextBlock", "ToolResultBlock", "ToolUseBlock"}:
39
+ from openharness.engine.messages import (
40
+ ConversationMessage,
41
+ ImageBlock,
42
+ TextBlock,
43
+ ToolResultBlock,
44
+ ToolUseBlock,
45
+ )
46
+
47
+ return {
48
+ "ConversationMessage": ConversationMessage,
49
+ "ImageBlock": ImageBlock,
50
+ "TextBlock": TextBlock,
51
+ "ToolResultBlock": ToolResultBlock,
52
+ "ToolUseBlock": ToolUseBlock,
53
+ }[name]
54
+
55
+ if name == "QueryEngine":
56
+ from openharness.engine.query_engine import QueryEngine
57
+
58
+ return QueryEngine
59
+
60
+ if name in {
61
+ "AssistantTextDelta",
62
+ "AssistantTurnComplete",
63
+ "ToolExecutionCompleted",
64
+ "ToolExecutionStarted",
65
+ }:
66
+ from openharness.engine.stream_events import (
67
+ AssistantTextDelta,
68
+ AssistantTurnComplete,
69
+ ToolExecutionCompleted,
70
+ ToolExecutionStarted,
71
+ )
72
+
73
+ return {
74
+ "AssistantTextDelta": AssistantTextDelta,
75
+ "AssistantTurnComplete": AssistantTurnComplete,
76
+ "ToolExecutionCompleted": ToolExecutionCompleted,
77
+ "ToolExecutionStarted": ToolExecutionStarted,
78
+ }[name]
79
+
80
+ raise AttributeError(name)
@@ -0,0 +1,24 @@
1
+ """Simple usage aggregation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openharness.api.usage import UsageSnapshot
6
+
7
+
8
+ class CostTracker:
9
+ """Accumulate usage over the lifetime of a session."""
10
+
11
+ def __init__(self) -> None:
12
+ self._usage = UsageSnapshot()
13
+
14
+ def add(self, usage: UsageSnapshot) -> None:
15
+ """Add a usage snapshot to the running total."""
16
+ self._usage = UsageSnapshot(
17
+ input_tokens=self._usage.input_tokens + usage.input_tokens,
18
+ output_tokens=self._usage.output_tokens + usage.output_tokens,
19
+ )
20
+
21
+ @property
22
+ def total(self) -> UsageSnapshot:
23
+ """Return the aggregated usage."""
24
+ return self._usage