qwen-sci 0.2.2__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.
- qwen_sci-0.2.2.dist-info/METADATA +492 -0
- qwen_sci-0.2.2.dist-info/RECORD +549 -0
- qwen_sci-0.2.2.dist-info/WHEEL +4 -0
- qwen_sci-0.2.2.dist-info/entry_points.txt +6 -0
- qwen_sci-0.2.2.dist-info/licenses/LICENSE +21 -0
- src/1.txt +1 -0
- src/__init__.py +1 -0
- src/__main__.py +5 -0
- src/agents/__init__.py +1 -0
- src/agents/experiment_design_agent/__init__.py +268 -0
- src/agents/experiment_design_agent/artifacts.py +747 -0
- src/agents/experiment_design_agent/cache.py +319 -0
- src/agents/experiment_design_agent/completeness.py +130 -0
- src/agents/experiment_design_agent/contracts.py +771 -0
- src/agents/experiment_design_agent/counterexample_analyzer.py +187 -0
- src/agents/experiment_design_agent/design_evidence_paper_screener.py +470 -0
- src/agents/experiment_design_agent/discipline_catalog.py +178 -0
- src/agents/experiment_design_agent/evidence_cards.py +338 -0
- src/agents/experiment_design_agent/evidence_planner.py +452 -0
- src/agents/experiment_design_agent/formal_reasoning_planner.py +884 -0
- src/agents/experiment_design_agent/fulltext_acquisition.py +863 -0
- src/agents/experiment_design_agent/idea_adapter.py +100 -0
- src/agents/experiment_design_agent/idea_intake.py +87 -0
- src/agents/experiment_design_agent/llm_json.py +328 -0
- src/agents/experiment_design_agent/orchestrator.py +1220 -0
- src/agents/experiment_design_agent/reasoning_context.py +183 -0
- src/agents/experiment_design_agent/reasoning_validation.py +546 -0
- src/agents/experiment_design_agent/run.py +481 -0
- src/agents/experiment_design_agent/run_logging.py +292 -0
- src/agents/experiment_design_agent/scope_gate.py +198 -0
- src/agents/experiment_design_agent/study_type_composer.py +1360 -0
- src/agents/experiment_design_agent/survey_evidence.py +1381 -0
- src/agents/experiment_design_agent/template_router.py +195 -0
- src/agents/experiment_design_agent/variable_claim_extractor.py +93 -0
- src/agents/idea_agent/__init__.py +1 -0
- src/agents/idea_agent/agent/__init__.py +6 -0
- src/agents/idea_agent/agent/artifacts.py +308 -0
- src/agents/idea_agent/agent/base.py +122 -0
- src/agents/idea_agent/agent/ligagent.py +488 -0
- src/agents/idea_agent/agent/mcts.py +2060 -0
- src/agents/idea_agent/agent/prompts/__init__.py +48 -0
- src/agents/idea_agent/agent/prompts/advanced_analysis.py +235 -0
- src/agents/idea_agent/agent/prompts/algorithm_alignment.py +55 -0
- src/agents/idea_agent/agent/prompts/algorithm_structuring.py +48 -0
- src/agents/idea_agent/agent/prompts/component_extraction.py +62 -0
- src/agents/idea_agent/agent/prompts/component_novelty_evaluation.py +54 -0
- src/agents/idea_agent/agent/prompts/experiment_findings_extraction.py +39 -0
- src/agents/idea_agent/agent/prompts/idea_fusion.py +295 -0
- src/agents/idea_agent/agent/prompts/idea_introduction.py +30 -0
- src/agents/idea_agent/agent/prompts/idea_result_alignment.py +39 -0
- src/agents/idea_agent/agent/prompts/input_interpreter.py +52 -0
- src/agents/idea_agent/agent/prompts/keynote_ops.py +78 -0
- src/agents/idea_agent/agent/prompts/mcts_evaluation.py +235 -0
- src/agents/idea_agent/agent/prompts/mcts_generation.py +140 -0
- src/agents/idea_agent/agent/prompts/mechanism_commit_query.py +99 -0
- src/agents/idea_agent/agent/prompts/memory_selection.py +0 -0
- src/agents/idea_agent/agent/prompts/prompt_modes.py +16 -0
- src/agents/idea_agent/agent/prompts/rag_query.py +34 -0
- src/agents/idea_agent/agent/prompts/re_analysis_replan.py +48 -0
- src/agents/idea_agent/agent/prompts/reference_grounding.py +34 -0
- src/agents/idea_agent/agent/prompts/root_domain_classification.py +32 -0
- src/agents/idea_agent/agent/prompts/scientific_debate.py +88 -0
- src/agents/idea_agent/agent/prompts/scientific_materialization.py +45 -0
- src/agents/idea_agent/agent/prompts/skill_instantiation.py +193 -0
- src/agents/idea_agent/agent/prompts/theory_transfer_query.py +93 -0
- src/agents/idea_agent/agent/prompts/topic_background.py +15 -0
- src/agents/idea_agent/agent/skills/DEFAULT_SKILL_TEMPLATES.json +263 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/references/contrast_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/references/mechanism_cards.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/references/hierarchy_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/references/mechanism_commit_patterns.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/references/coordination_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/SKILL.md +41 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/references/speculation_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/references/modularity_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/references/transfer_patterns.md +24 -0
- src/agents/idea_agent/example/idea_result.json +900 -0
- src/agents/idea_agent/run.py +276 -0
- src/agents/idea_agent/tests/test_advanced_analysis_contract.py +90 -0
- src/agents/idea_agent/tests/test_gap_hypothesis_bridge.py +166 -0
- src/agents/idea_agent/tests/test_idea_debate.py +480 -0
- src/agents/idea_agent/tests/test_idea_result_v5.py +99 -0
- src/agents/idea_agent/tests/test_idea_synthesis.py +92 -0
- src/agents/idea_agent/tests/test_mature_idea_sources.py +27 -0
- src/agents/idea_agent/tests/test_multimodal_idea_integration.py +386 -0
- src/agents/idea_agent/tests/test_profile_materialization_and_scoring.py +219 -0
- src/agents/idea_agent/tests/test_profile_native_skill_rendering.py +122 -0
- src/agents/idea_agent/tests/test_result_persistence.py +183 -0
- src/agents/idea_agent/tests/test_scientific_intervention_profiles.py +241 -0
- src/agents/idea_agent/tests/test_scientific_rubric_and_prompts.py +559 -0
- src/agents/idea_agent/tests/test_strict_json_object_responses.py +48 -0
- src/agents/idea_agent/utils/core/__init__.py +1 -0
- src/agents/idea_agent/utils/core/ablation_inputs.py +60 -0
- src/agents/idea_agent/utils/core/chat_errors.py +95 -0
- src/agents/idea_agent/utils/core/chat_router.py +130 -0
- src/agents/idea_agent/utils/core/chat_transport.py +188 -0
- src/agents/idea_agent/utils/core/config_loader.py +166 -0
- src/agents/idea_agent/utils/core/json_utils.py +28 -0
- src/agents/idea_agent/utils/core/logger.py +287 -0
- src/agents/idea_agent/utils/core/progress.py +58 -0
- src/agents/idea_agent/utils/core/response_parsing.py +57 -0
- src/agents/idea_agent/utils/core/run_inputs.py +198 -0
- src/agents/idea_agent/utils/mcts/__init__.py +1 -0
- src/agents/idea_agent/utils/mcts/component_novelty.py +342 -0
- src/agents/idea_agent/utils/mcts/defect_registry.py +302 -0
- src/agents/idea_agent/utils/mcts/idea_routes.py +61 -0
- src/agents/idea_agent/utils/mcts/idea_taste_presets.py +312 -0
- src/agents/idea_agent/utils/mcts/mcts_helpers.py +738 -0
- src/agents/idea_agent/utils/mcts/mcts_runtime.py +3000 -0
- src/agents/idea_agent/utils/mcts/scientific_intervention_ontology.py +1561 -0
- src/agents/idea_agent/utils/mcts/scientific_rubric.py +211 -0
- src/agents/idea_agent/utils/mcts/skill_parsing.py +92 -0
- src/agents/idea_agent/utils/papers/__init__.py +1 -0
- src/agents/idea_agent/utils/papers/paper_graph_vector_store.py +499 -0
- src/agents/idea_agent/utils/papers/paper_repository.py +372 -0
- src/agents/idea_agent/utils/prompting/__init__.py +1 -0
- src/agents/idea_agent/utils/prompting/prompt_views.py +637 -0
- src/agents/idea_agent/utils/workflow/__init__.py +1 -0
- src/agents/idea_agent/utils/workflow/advanced_analysis_contract.py +244 -0
- src/agents/idea_agent/utils/workflow/gap_hypothesis_bridge.py +461 -0
- src/agents/idea_agent/utils/workflow/idea_contract.py +426 -0
- src/agents/idea_agent/utils/workflow/idea_debate.py +1238 -0
- src/agents/idea_agent/utils/workflow/idea_diversity.py +163 -0
- src/agents/idea_agent/utils/workflow/idea_fusion.py +889 -0
- src/agents/idea_agent/utils/workflow/idea_helpers.py +478 -0
- src/agents/idea_agent/utils/workflow/idea_portfolio.py +422 -0
- src/agents/idea_agent/utils/workflow/idea_synthesis.py +482 -0
- src/agents/idea_agent/utils/workflow/ligagent_flow.py +581 -0
- src/agents/idea_agent/utils/workflow/ligagent_handlers.py +1836 -0
- src/agents/idea_agent/utils/workflow/ligagent_helpers.py +1331 -0
- src/agents/idea_agent/utils/workflow/ligagent_utils.py +322 -0
- src/agents/idea_agent/utils/workflow/mature_idea_sources.py +648 -0
- src/agents/idea_agent/utils/workflow/multimodal_data_anchoring.py +578 -0
- src/agents/idea_agent/utils/workflow/result_persistence.py +774 -0
- src/agents/idea_agent/utils/workflow/stage_contract.py +76 -0
- src/agents/idea_agent/utils/workflow/workflow_runtime.py +288 -0
- src/agents/research_plan_author/__init__.py +50 -0
- src/agents/research_plan_author/artifacts.py +270 -0
- src/agents/research_plan_author/authoring_blueprint.py +1246 -0
- src/agents/research_plan_author/bibtex_renderer.py +252 -0
- src/agents/research_plan_author/contract_repair.py +131 -0
- src/agents/research_plan_author/contracts.py +745 -0
- src/agents/research_plan_author/cross_section_editor.py +232 -0
- src/agents/research_plan_author/document_quality.py +987 -0
- src/agents/research_plan_author/idea_evolution.py +223 -0
- src/agents/research_plan_author/input_loader.py +76 -0
- src/agents/research_plan_author/latex_compiler.py +260 -0
- src/agents/research_plan_author/latex_safety.py +159 -0
- src/agents/research_plan_author/llm_json.py +74 -0
- src/agents/research_plan_author/markdown_renderer.py +275 -0
- src/agents/research_plan_author/pdf_validator.py +149 -0
- src/agents/research_plan_author/render.py +166 -0
- src/agents/research_plan_author/render_artifacts.py +187 -0
- src/agents/research_plan_author/run.py +765 -0
- src/agents/research_plan_author/run_logging.py +209 -0
- src/agents/research_plan_author/section_cache.py +184 -0
- src/agents/research_plan_author/section_composer.py +1764 -0
- src/agents/research_plan_author/section_router.py +209 -0
- src/agents/research_plan_author/semantic_validator.py +254 -0
- src/agents/research_plan_author/source_bundle.py +46 -0
- src/agents/research_plan_author/source_registry.py +208 -0
- src/agents/research_plan_author/survey_source_loader.py +137 -0
- src/agents/research_plan_author/template_adapter.py +173 -0
- src/agents/research_plan_author/template_profile.py +204 -0
- src/agents/research_plan_author/tex_renderer.py +496 -0
- src/agents/research_plan_author/theory_presentation.py +134 -0
- src/agents/research_plan_author/theory_spine.py +703 -0
- src/agents/survey_agent/Dockerfile +54 -0
- src/agents/survey_agent/README.md +350 -0
- src/agents/survey_agent/config/deep_survey.yaml +217 -0
- src/agents/survey_agent/config/deep_survey_batch.yaml +176 -0
- src/agents/survey_agent/config/deep_survey_fast.yaml +183 -0
- src/agents/survey_agent/config/evaluate_baseline.yaml +197 -0
- src/agents/survey_agent/config/outcomeRAG.yaml +159 -0
- src/agents/survey_agent/env_report.md +348 -0
- src/agents/survey_agent/modules/__init__.py +0 -0
- src/agents/survey_agent/modules/code_collector.py +2194 -0
- src/agents/survey_agent/modules/code_report_generator.py +1282 -0
- src/agents/survey_agent/modules/data_manager.py +1713 -0
- src/agents/survey_agent/modules/database.py +167 -0
- src/agents/survey_agent/modules/fulltext_download_cache.py +462 -0
- src/agents/survey_agent/modules/fulltext_http.py +134 -0
- src/agents/survey_agent/modules/fulltext_resolution.py +749 -0
- src/agents/survey_agent/modules/judge.py +434 -0
- src/agents/survey_agent/modules/outcome_RAG.py +236 -0
- src/agents/survey_agent/modules/paper_graph_retriever.py +1075 -0
- src/agents/survey_agent/modules/pe.py +1729 -0
- src/agents/survey_agent/modules/pseudo_creator.py +782 -0
- src/agents/survey_agent/modules/pseudo_reviser.py +537 -0
- src/agents/survey_agent/modules/refine_agent.py +1952 -0
- src/agents/survey_agent/modules/survey_generator.py +5691 -0
- src/agents/survey_agent/modules/survey_visualizer.py +1817 -0
- src/agents/survey_agent/modules/visual_prompts.py +327 -0
- src/agents/survey_agent/modules/work_analyzer.py +2750 -0
- src/agents/survey_agent/modules/work_collector.py +5649 -0
- src/agents/survey_agent/scripts/baseline_evaluation.py +411 -0
- src/agents/survey_agent/scripts/run_deep_survey.py +224 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter.py +407 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter_with_input.py +409 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch.py +228 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_arg.py +243 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_settings.py +218 -0
- src/agents/survey_agent/scripts/run_keynotes_gen.py +128 -0
- src/agents/survey_agent/scripts/run_work_clusters.py +140 -0
- src/agents/survey_agent/utils/__init__.py +0 -0
- src/agents/survey_agent/utils/ablation_utils.py +317 -0
- src/agents/survey_agent/utils/api_call.py +2518 -0
- src/agents/survey_agent/utils/chat_utils.py +82 -0
- src/agents/survey_agent/utils/config_utils.py +70 -0
- src/agents/survey_agent/utils/convert_to_md.py +29 -0
- src/agents/survey_agent/utils/err_info.py +24 -0
- src/agents/survey_agent/utils/file_utils.py +176 -0
- src/agents/survey_agent/utils/gpu_utils.py +145 -0
- src/agents/survey_agent/utils/graph_visualizer.py +279 -0
- src/agents/survey_agent/utils/html_utils.py +181 -0
- src/agents/survey_agent/utils/latex_generator_intra_cluster.py +529 -0
- src/agents/survey_agent/utils/latex_generator_relation_table.py +193 -0
- src/agents/survey_agent/utils/mineru_section_packer.py +334 -0
- src/agents/survey_agent/utils/mineru_utils.py +396 -0
- src/agents/survey_agent/utils/paper.py +144 -0
- src/agents/survey_agent/utils/read_yaml.py +15 -0
- src/agents/survey_agent/utils/repo_utils.py +167 -0
- src/agents/survey_agent/utils/rich_logger.py +77 -0
- src/agents/survey_agent/utils/step2v2.py +874 -0
- src/agents/survey_agent/utils/step2v2_extractor.py +856 -0
- src/agents/survey_agent/utils/topic_survey_storage.py +309 -0
- src/agents/survey_agent/utils/utils.py +118 -0
- src/agents/survey_agent/utils/work.py +2 -0
- src/cli.py +1611 -0
- src/config/__init__.py +213 -0
- src/config/default.yaml +1122 -0
- src/harness/LICENSE +21 -0
- src/harness/VENDORED.md +14 -0
- src/harness/src/__init__.py +0 -0
- src/harness/src/openharness/__init__.py +0 -0
- src/harness/src/openharness/__main__.py +6 -0
- src/harness/src/openharness/api/__init__.py +21 -0
- src/harness/src/openharness/api/client.py +281 -0
- src/harness/src/openharness/api/codex_client.py +407 -0
- src/harness/src/openharness/api/copilot_auth.py +240 -0
- src/harness/src/openharness/api/copilot_client.py +134 -0
- src/harness/src/openharness/api/errors.py +19 -0
- src/harness/src/openharness/api/openai_client.py +577 -0
- src/harness/src/openharness/api/provider.py +186 -0
- src/harness/src/openharness/api/registry.py +437 -0
- src/harness/src/openharness/api/usage.py +17 -0
- src/harness/src/openharness/auth/__init__.py +29 -0
- src/harness/src/openharness/auth/external.py +610 -0
- src/harness/src/openharness/auth/flows.py +189 -0
- src/harness/src/openharness/auth/manager.py +482 -0
- src/harness/src/openharness/auth/storage.py +269 -0
- src/harness/src/openharness/autopilot/__init__.py +23 -0
- src/harness/src/openharness/autopilot/service.py +2239 -0
- src/harness/src/openharness/autopilot/types.py +91 -0
- src/harness/src/openharness/bridge/__init__.py +20 -0
- src/harness/src/openharness/bridge/manager.py +106 -0
- src/harness/src/openharness/bridge/session_runner.py +46 -0
- src/harness/src/openharness/bridge/types.py +37 -0
- src/harness/src/openharness/bridge/work_secret.py +41 -0
- src/harness/src/openharness/channels/UPSTREAM +6 -0
- src/harness/src/openharness/channels/__init__.py +22 -0
- src/harness/src/openharness/channels/adapter.py +131 -0
- src/harness/src/openharness/channels/bus/__init__.py +6 -0
- src/harness/src/openharness/channels/bus/events.py +38 -0
- src/harness/src/openharness/channels/bus/queue.py +44 -0
- src/harness/src/openharness/channels/impl/__init__.py +6 -0
- src/harness/src/openharness/channels/impl/base.py +141 -0
- src/harness/src/openharness/channels/impl/dingtalk.py +445 -0
- src/harness/src/openharness/channels/impl/discord.py +311 -0
- src/harness/src/openharness/channels/impl/email.py +410 -0
- src/harness/src/openharness/channels/impl/feishu.py +1342 -0
- src/harness/src/openharness/channels/impl/manager.py +257 -0
- src/harness/src/openharness/channels/impl/matrix.py +700 -0
- src/harness/src/openharness/channels/impl/mochat.py +897 -0
- src/harness/src/openharness/channels/impl/qq.py +141 -0
- src/harness/src/openharness/channels/impl/slack.py +284 -0
- src/harness/src/openharness/channels/impl/telegram.py +525 -0
- src/harness/src/openharness/channels/impl/whatsapp.py +159 -0
- src/harness/src/openharness/cli.py +2551 -0
- src/harness/src/openharness/commands/__init__.py +21 -0
- src/harness/src/openharness/commands/registry.py +2783 -0
- src/harness/src/openharness/config/__init__.py +34 -0
- src/harness/src/openharness/config/paths.py +160 -0
- src/harness/src/openharness/config/schema.py +119 -0
- src/harness/src/openharness/config/settings.py +1104 -0
- src/harness/src/openharness/coordinator/__init__.py +12 -0
- src/harness/src/openharness/coordinator/agent_definitions.py +975 -0
- src/harness/src/openharness/coordinator/coordinator_mode.py +520 -0
- src/harness/src/openharness/engine/__init__.py +80 -0
- src/harness/src/openharness/engine/cost_tracker.py +24 -0
- src/harness/src/openharness/engine/messages.py +222 -0
- src/harness/src/openharness/engine/query.py +1188 -0
- src/harness/src/openharness/engine/query_engine.py +313 -0
- src/harness/src/openharness/engine/stream_events.py +90 -0
- src/harness/src/openharness/hooks/__init__.py +50 -0
- src/harness/src/openharness/hooks/events.py +20 -0
- src/harness/src/openharness/hooks/executor.py +242 -0
- src/harness/src/openharness/hooks/hot_reload.py +31 -0
- src/harness/src/openharness/hooks/loader.py +68 -0
- src/harness/src/openharness/hooks/schemas.py +66 -0
- src/harness/src/openharness/hooks/types.py +38 -0
- src/harness/src/openharness/keybindings/__init__.py +14 -0
- src/harness/src/openharness/keybindings/default_bindings.py +11 -0
- src/harness/src/openharness/keybindings/loader.py +22 -0
- src/harness/src/openharness/keybindings/parser.py +18 -0
- src/harness/src/openharness/keybindings/resolver.py +13 -0
- src/harness/src/openharness/mcp/__init__.py +79 -0
- src/harness/src/openharness/mcp/client.py +298 -0
- src/harness/src/openharness/mcp/config.py +16 -0
- src/harness/src/openharness/mcp/types.py +76 -0
- src/harness/src/openharness/memory/__init__.py +25 -0
- src/harness/src/openharness/memory/agent.py +91 -0
- src/harness/src/openharness/memory/manager.py +183 -0
- src/harness/src/openharness/memory/memdir.py +52 -0
- src/harness/src/openharness/memory/migrate.py +137 -0
- src/harness/src/openharness/memory/paths.py +22 -0
- src/harness/src/openharness/memory/relevance.py +145 -0
- src/harness/src/openharness/memory/scan.py +116 -0
- src/harness/src/openharness/memory/schema.py +443 -0
- src/harness/src/openharness/memory/search.py +71 -0
- src/harness/src/openharness/memory/team.py +90 -0
- src/harness/src/openharness/memory/types.py +33 -0
- src/harness/src/openharness/memory/usage.py +153 -0
- src/harness/src/openharness/output_styles/__init__.py +5 -0
- src/harness/src/openharness/output_styles/loader.py +42 -0
- src/harness/src/openharness/permissions/__init__.py +26 -0
- src/harness/src/openharness/permissions/checker.py +200 -0
- src/harness/src/openharness/permissions/modes.py +13 -0
- src/harness/src/openharness/personalization/__init__.py +6 -0
- src/harness/src/openharness/personalization/extractor.py +138 -0
- src/harness/src/openharness/personalization/rules.py +64 -0
- src/harness/src/openharness/personalization/session_hook.py +65 -0
- src/harness/src/openharness/platforms.py +86 -0
- src/harness/src/openharness/plugins/__init__.py +53 -0
- src/harness/src/openharness/plugins/bundled/__init__.py +0 -0
- src/harness/src/openharness/plugins/installer.py +39 -0
- src/harness/src/openharness/plugins/loader.py +730 -0
- src/harness/src/openharness/plugins/schemas.py +24 -0
- src/harness/src/openharness/plugins/types.py +59 -0
- src/harness/src/openharness/prompts/__init__.py +14 -0
- src/harness/src/openharness/prompts/claudemd.py +48 -0
- src/harness/src/openharness/prompts/context.py +187 -0
- src/harness/src/openharness/prompts/environment.py +135 -0
- src/harness/src/openharness/prompts/system_prompt.py +109 -0
- src/harness/src/openharness/sandbox/Dockerfile +6 -0
- src/harness/src/openharness/sandbox/__init__.py +33 -0
- src/harness/src/openharness/sandbox/adapter.py +148 -0
- src/harness/src/openharness/sandbox/docker_backend.py +232 -0
- src/harness/src/openharness/sandbox/docker_image.py +103 -0
- src/harness/src/openharness/sandbox/path_validator.py +37 -0
- src/harness/src/openharness/sandbox/session.py +63 -0
- src/harness/src/openharness/services/__init__.py +30 -0
- src/harness/src/openharness/services/autodream/__init__.py +34 -0
- src/harness/src/openharness/services/autodream/backup.py +104 -0
- src/harness/src/openharness/services/autodream/lock.py +138 -0
- src/harness/src/openharness/services/autodream/prompt.py +128 -0
- src/harness/src/openharness/services/autodream/service.py +313 -0
- src/harness/src/openharness/services/compact/__init__.py +1725 -0
- src/harness/src/openharness/services/cron.py +137 -0
- src/harness/src/openharness/services/cron_scheduler.py +595 -0
- src/harness/src/openharness/services/lsp/__init__.py +216 -0
- src/harness/src/openharness/services/memory_extract/__init__.py +261 -0
- src/harness/src/openharness/services/oauth/__init__.py +0 -0
- src/harness/src/openharness/services/session_backend.py +97 -0
- src/harness/src/openharness/services/session_memory/__init__.py +139 -0
- src/harness/src/openharness/services/session_storage.py +230 -0
- src/harness/src/openharness/services/token_estimation.py +15 -0
- src/harness/src/openharness/services/tool_outputs.py +55 -0
- src/harness/src/openharness/skills/__init__.py +44 -0
- src/harness/src/openharness/skills/_frontmatter.py +97 -0
- src/harness/src/openharness/skills/bundled/__init__.py +75 -0
- src/harness/src/openharness/skills/bundled/content/commit.md +25 -0
- src/harness/src/openharness/skills/bundled/content/debug.md +25 -0
- src/harness/src/openharness/skills/bundled/content/diagnose.md +35 -0
- src/harness/src/openharness/skills/bundled/content/plan.md +39 -0
- src/harness/src/openharness/skills/bundled/content/review.md +33 -0
- src/harness/src/openharness/skills/bundled/content/simplify.md +26 -0
- src/harness/src/openharness/skills/bundled/content/skill-creator.md +175 -0
- src/harness/src/openharness/skills/bundled/content/test.md +38 -0
- src/harness/src/openharness/skills/loader.py +229 -0
- src/harness/src/openharness/skills/registry.py +29 -0
- src/harness/src/openharness/skills/types.py +24 -0
- src/harness/src/openharness/state/__init__.py +6 -0
- src/harness/src/openharness/state/app_state.py +30 -0
- src/harness/src/openharness/state/store.py +40 -0
- src/harness/src/openharness/swarm/__init__.py +70 -0
- src/harness/src/openharness/swarm/in_process.py +693 -0
- src/harness/src/openharness/swarm/lockfile.py +24 -0
- src/harness/src/openharness/swarm/mailbox.py +522 -0
- src/harness/src/openharness/swarm/permission_sync.py +1168 -0
- src/harness/src/openharness/swarm/registry.py +410 -0
- src/harness/src/openharness/swarm/spawn_utils.py +229 -0
- src/harness/src/openharness/swarm/subprocess_backend.py +171 -0
- src/harness/src/openharness/swarm/team_lifecycle.py +910 -0
- src/harness/src/openharness/swarm/types.py +398 -0
- src/harness/src/openharness/swarm/worktree.py +315 -0
- src/harness/src/openharness/tasks/__init__.py +18 -0
- src/harness/src/openharness/tasks/local_agent_task.py +28 -0
- src/harness/src/openharness/tasks/local_shell_task.py +17 -0
- src/harness/src/openharness/tasks/manager.py +475 -0
- src/harness/src/openharness/tasks/stop_task.py +11 -0
- src/harness/src/openharness/tasks/types.py +32 -0
- src/harness/src/openharness/themes/__init__.py +21 -0
- src/harness/src/openharness/themes/builtin.py +89 -0
- src/harness/src/openharness/themes/loader.py +55 -0
- src/harness/src/openharness/themes/schema.py +54 -0
- src/harness/src/openharness/tools/__init__.py +107 -0
- src/harness/src/openharness/tools/agent_tool.py +141 -0
- src/harness/src/openharness/tools/ask_user_question_tool.py +46 -0
- src/harness/src/openharness/tools/base.py +80 -0
- src/harness/src/openharness/tools/bash_tool.py +259 -0
- src/harness/src/openharness/tools/brief_tool.py +33 -0
- src/harness/src/openharness/tools/config_tool.py +54 -0
- src/harness/src/openharness/tools/cron_create_tool.py +105 -0
- src/harness/src/openharness/tools/cron_delete_tool.py +32 -0
- src/harness/src/openharness/tools/cron_list_tool.py +69 -0
- src/harness/src/openharness/tools/cron_toggle_tool.py +37 -0
- src/harness/src/openharness/tools/enter_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/enter_worktree_tool.py +80 -0
- src/harness/src/openharness/tools/exit_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/exit_worktree_tool.py +42 -0
- src/harness/src/openharness/tools/file_edit_tool.py +95 -0
- src/harness/src/openharness/tools/file_read_tool.py +72 -0
- src/harness/src/openharness/tools/file_write_tool.py +87 -0
- src/harness/src/openharness/tools/glob_tool.py +175 -0
- src/harness/src/openharness/tools/grep_tool.py +374 -0
- src/harness/src/openharness/tools/image_generation_tool.py +447 -0
- src/harness/src/openharness/tools/image_to_text_tool.py +237 -0
- src/harness/src/openharness/tools/list_mcp_resources_tool.py +36 -0
- src/harness/src/openharness/tools/lsp_tool.py +154 -0
- src/harness/src/openharness/tools/mcp_auth_tool.py +71 -0
- src/harness/src/openharness/tools/mcp_tool.py +72 -0
- src/harness/src/openharness/tools/notebook_edit_tool.py +97 -0
- src/harness/src/openharness/tools/read_mcp_resource_tool.py +38 -0
- src/harness/src/openharness/tools/remote_trigger_tool.py +74 -0
- src/harness/src/openharness/tools/send_message_tool.py +58 -0
- src/harness/src/openharness/tools/skill_tool.py +43 -0
- src/harness/src/openharness/tools/sleep_tool.py +32 -0
- src/harness/src/openharness/tools/task_create_tool.py +56 -0
- src/harness/src/openharness/tools/task_get_tool.py +33 -0
- src/harness/src/openharness/tools/task_list_tool.py +35 -0
- src/harness/src/openharness/tools/task_output_tool.py +35 -0
- src/harness/src/openharness/tools/task_stop_tool.py +30 -0
- src/harness/src/openharness/tools/task_update_tool.py +50 -0
- src/harness/src/openharness/tools/team_create_tool.py +31 -0
- src/harness/src/openharness/tools/team_delete_tool.py +30 -0
- src/harness/src/openharness/tools/todo_write_tool.py +46 -0
- src/harness/src/openharness/tools/tool_search_tool.py +38 -0
- src/harness/src/openharness/tools/web_fetch_tool.py +117 -0
- src/harness/src/openharness/tools/web_search_tool.py +119 -0
- src/harness/src/openharness/ui/__init__.py +5 -0
- src/harness/src/openharness/ui/app.py +320 -0
- src/harness/src/openharness/ui/backend_host.py +941 -0
- src/harness/src/openharness/ui/coordinator_drain.py +198 -0
- src/harness/src/openharness/ui/input.py +32 -0
- src/harness/src/openharness/ui/output.py +265 -0
- src/harness/src/openharness/ui/permission_dialog.py +14 -0
- src/harness/src/openharness/ui/protocol.py +247 -0
- src/harness/src/openharness/ui/react_launcher.py +179 -0
- src/harness/src/openharness/ui/runtime.py +799 -0
- src/harness/src/openharness/ui/textual_app.py +495 -0
- src/harness/src/openharness/utils/__init__.py +0 -0
- src/harness/src/openharness/utils/file_lock.py +86 -0
- src/harness/src/openharness/utils/fs.py +98 -0
- src/harness/src/openharness/utils/helpers.py +77 -0
- src/harness/src/openharness/utils/network_guard.py +340 -0
- src/harness/src/openharness/utils/shell.py +147 -0
- src/harness/src/openharness/vim/__init__.py +5 -0
- src/harness/src/openharness/vim/transitions.py +8 -0
- src/harness/src/openharness/voice/__init__.py +7 -0
- src/harness/src/openharness/voice/keyterms.py +10 -0
- src/harness/src/openharness/voice/stream_stt.py +8 -0
- src/harness/src/openharness/voice/voice_mode.py +44 -0
- src/llm/__init__.py +29 -0
- src/llm/image_generation.py +402 -0
- src/llm/provider_registry.py +419 -0
- src/llm/runtime_env.py +64 -0
- src/llm/structured_output.py +131 -0
- src/llm/vision.py +198 -0
- src/memory/__init__.py +2 -0
- src/memory/api/__init__.py +1 -0
- src/memory/api/base_memory_system_api.py +98 -0
- src/memory/api/base_symbolic_memory_system_api.py +190 -0
- src/memory/api/base_vector_memory_system_api.py +116 -0
- src/memory/api/faiss_memory_system_api.py +228 -0
- src/memory/api/slot_process_api.py +827 -0
- src/memory/api/symbolic_memory_system_api.py +572 -0
- src/memory/memory_system/__init__.py +48 -0
- src/memory/memory_system/component_taxonomy.py +430 -0
- src/memory/memory_system/llm.py +437 -0
- src/memory/memory_system/models.py +168 -0
- src/memory/memory_system/user_prompt.py +418 -0
- src/memory/memory_system/utils.py +282 -0
- src/memory/memory_system/vectorstore.py +311 -0
- src/memory/memory_system/working_slot.py +99 -0
- src/pipeline/__init__.py +129 -0
- src/pipeline/contracts.py +531 -0
- src/pipeline/discipline_taxonomy.py +916 -0
- src/pipeline/evidence_coverage_ledger.py +1150 -0
- src/pipeline/evidence_refinement.py +620 -0
- src/pipeline/experiment_to_symbolic.py +137 -0
- src/pipeline/multimodal_evidence/__init__.py +28 -0
- src/pipeline/multimodal_evidence/capabilities.py +107 -0
- src/pipeline/multimodal_evidence/claims.py +135 -0
- src/pipeline/multimodal_evidence/contract.py +291 -0
- src/pipeline/multimodal_evidence/data_sh_compiler.py +505 -0
- src/pipeline/multimodal_evidence/handoff_binding.py +135 -0
- src/pipeline/multimodal_evidence/manifest.py +254 -0
- src/pipeline/multimodal_evidence/native_analysis.py +479 -0
- src/pipeline/multimodal_evidence/perception.py +277 -0
- src/pipeline/multimodal_evidence/query_binding.py +65 -0
- src/pipeline/multimodal_evidence/rendering.py +120 -0
- src/pipeline/multimodal_evidence/safety.py +42 -0
- src/pipeline/multimodal_evidence/sampling.py +69 -0
- src/pipeline/multimodal_evidence/service.py +148 -0
- src/pipeline/multimodal_evidence/survey_integration.py +460 -0
- src/pipeline/paper_identity.py +52 -0
- src/pipeline/research_design_inventory.py +338 -0
- src/pipeline/research_domain_resolution.py +669 -0
- src/pipeline/research_identity.py +548 -0
- src/pipeline/research_question_contract.py +646 -0
- src/pipeline/retrieval_lanes.py +889 -0
- src/pipeline/run_loop.py +862 -0
- src/pipeline/science_events.py +30 -0
- src/pipeline/science_manifests.py +602 -0
- src/pipeline/science_run.py +918 -0
- src/pipeline/science_stages.py +709 -0
- src/pipeline/science_workflow.py +807 -0
- src/pipeline/sh_cluster_projection.py +306 -0
- src/pipeline/sh_graph_provenance.py +648 -0
- src/pipeline/subhypothesis_decomposition.py +392 -0
- src/pipeline/subhypothesis_evidence.py +856 -0
- src/pipeline/survey_evidence_plan.py +858 -0
- src/pipeline/survey_gap_adjudication.py +647 -0
- src/pipeline/survey_gap_candidates.py +840 -0
- src/pipeline/survey_gap_ledger.py +683 -0
- src/pipeline/survey_gap_triage.py +401 -0
- src/pipeline/survey_handoff_persistence.py +481 -0
- src/pipeline/survey_handoff_projection.py +457 -0
- src/pipeline/survey_idea_handoff.py +1112 -0
- src/pipeline/survey_idea_loader.py +289 -0
- src/research_run_ids.py +11 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Project a bounded, source-anchored Idea evolution appendix without invention."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
from src.agents.experiment_design_agent.idea_intake import load_idea_artifact_bundle
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
IDEA_EVOLUTION_APPENDIX_SCHEMA_VERSION = "idea_evolution_appendix_v1"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IdeaEvolutionError(ValueError):
|
|
16
|
+
"""Raised when the requested Idea history conflicts with the selected direction."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _mapping(value: object) -> dict[str, Any]:
|
|
20
|
+
return dict(value) if isinstance(value, Mapping) else {}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _text(value: object) -> str:
|
|
24
|
+
return str(value or "").strip()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _identifier_candidates(direction: Mapping[str, Any]) -> set[str]:
|
|
28
|
+
values = (
|
|
29
|
+
direction.get("id"),
|
|
30
|
+
direction.get("direction_id"),
|
|
31
|
+
direction.get("direction_mode"),
|
|
32
|
+
direction.get("title"),
|
|
33
|
+
)
|
|
34
|
+
return {_text(value) for value in values if _text(value)}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _selected_direction(idea_result: Mapping[str, Any], selected_direction_id: str) -> tuple[dict[str, Any], str]:
|
|
38
|
+
directions = idea_result.get("directions")
|
|
39
|
+
candidates = directions if isinstance(directions, list) else []
|
|
40
|
+
for index, raw_direction in enumerate(candidates):
|
|
41
|
+
direction = _mapping(raw_direction)
|
|
42
|
+
if selected_direction_id in _identifier_candidates(direction):
|
|
43
|
+
return direction, f"/directions/{index}"
|
|
44
|
+
primary_direction = _text(idea_result.get("primary_direction"))
|
|
45
|
+
if selected_direction_id == primary_direction:
|
|
46
|
+
for index, raw_direction in enumerate(candidates):
|
|
47
|
+
direction = _mapping(raw_direction)
|
|
48
|
+
if primary_direction in _identifier_candidates(direction):
|
|
49
|
+
return direction, f"/directions/{index}"
|
|
50
|
+
raise IdeaEvolutionError(
|
|
51
|
+
f"selected direction mismatch: '{selected_direction_id}' is not present in idea_result.json directions"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _snapshot(record: Mapping[str, Any]) -> dict[str, Any]:
|
|
56
|
+
allowed_fields = (
|
|
57
|
+
"id",
|
|
58
|
+
"direction_id",
|
|
59
|
+
"direction_mode",
|
|
60
|
+
"title",
|
|
61
|
+
"research_question",
|
|
62
|
+
"central_hypothesis",
|
|
63
|
+
"hypothesis",
|
|
64
|
+
"mechanism_or_relation",
|
|
65
|
+
"contribution",
|
|
66
|
+
"contributions",
|
|
67
|
+
"claim_scope",
|
|
68
|
+
"boundary_conditions",
|
|
69
|
+
"alternative_explanations",
|
|
70
|
+
"falsifiers",
|
|
71
|
+
"known_unknowns",
|
|
72
|
+
"target_gap_ids",
|
|
73
|
+
)
|
|
74
|
+
return {
|
|
75
|
+
field: deepcopy(record[field])
|
|
76
|
+
for field in allowed_fields
|
|
77
|
+
if field in record and record[field] not in (None, "", [], {})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _append_checkpoint(
|
|
82
|
+
checkpoints: list[dict[str, Any]],
|
|
83
|
+
*,
|
|
84
|
+
source_file: str,
|
|
85
|
+
source_path: str,
|
|
86
|
+
source_json_pointer: str,
|
|
87
|
+
record: Mapping[str, Any],
|
|
88
|
+
) -> None:
|
|
89
|
+
snapshot = _snapshot(record)
|
|
90
|
+
if not snapshot:
|
|
91
|
+
return
|
|
92
|
+
if any(item["snapshot"] == snapshot for item in checkpoints):
|
|
93
|
+
return
|
|
94
|
+
checkpoints.append(
|
|
95
|
+
{
|
|
96
|
+
"checkpoint_id": f"checkpoint-{len(checkpoints) + 1}",
|
|
97
|
+
"checkpoint_type": "available_audit_snapshot",
|
|
98
|
+
"temporal_order": "unknown",
|
|
99
|
+
"source_file": source_file,
|
|
100
|
+
"source_path": source_path,
|
|
101
|
+
"source_json_pointer": source_json_pointer,
|
|
102
|
+
"snapshot": snapshot,
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def project_idea_evolution(
|
|
108
|
+
idea_result_path: str | Path,
|
|
109
|
+
*,
|
|
110
|
+
selected_direction_id: str,
|
|
111
|
+
max_iterations: int = 3,
|
|
112
|
+
) -> dict[str, Any]:
|
|
113
|
+
"""Project available Idea audit snapshots without inventing an iteration history.
|
|
114
|
+
|
|
115
|
+
The common Idea artifacts are parallel audit outputs from one run. Their
|
|
116
|
+
filenames do not establish chronology or parentage, so they remain neutral
|
|
117
|
+
source checkpoints unless a future producer supplies auditable evolution
|
|
118
|
+
metadata.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
if max_iterations not in {2, 3}:
|
|
122
|
+
raise ValueError("max_iterations must be either 2 or 3")
|
|
123
|
+
expected_direction_id = _text(selected_direction_id)
|
|
124
|
+
if not expected_direction_id:
|
|
125
|
+
raise IdeaEvolutionError("selected_direction_id is required for Idea evolution projection")
|
|
126
|
+
try:
|
|
127
|
+
bundle = load_idea_artifact_bundle(idea_result_path)
|
|
128
|
+
except (FileNotFoundError, ValueError) as error:
|
|
129
|
+
raise IdeaEvolutionError(str(error)) from error
|
|
130
|
+
idea_result = _mapping(bundle.get("idea_result"))
|
|
131
|
+
selected, selected_pointer = _selected_direction(idea_result, expected_direction_id)
|
|
132
|
+
source_paths = _mapping(bundle.get("source_paths"))
|
|
133
|
+
audit_sources = _mapping(bundle.get("audit_sources"))
|
|
134
|
+
checkpoints: list[dict[str, Any]] = []
|
|
135
|
+
candidate = _mapping(audit_sources.get("idea_candidate"))
|
|
136
|
+
_append_checkpoint(
|
|
137
|
+
checkpoints,
|
|
138
|
+
source_file="idea_candidate.json",
|
|
139
|
+
source_path=_text(source_paths.get("idea_candidate")),
|
|
140
|
+
source_json_pointer="/",
|
|
141
|
+
record=candidate,
|
|
142
|
+
)
|
|
143
|
+
portfolio = _mapping(audit_sources.get("idea_portfolio"))
|
|
144
|
+
selected_primary_idea = _mapping(
|
|
145
|
+
audit_sources.get("selected_primary_idea") or portfolio.get("selected_primary_idea")
|
|
146
|
+
)
|
|
147
|
+
_append_checkpoint(
|
|
148
|
+
checkpoints,
|
|
149
|
+
source_file="idea_portfolio.json",
|
|
150
|
+
source_path=_text(source_paths.get("idea_portfolio")),
|
|
151
|
+
source_json_pointer="/selected_primary_idea",
|
|
152
|
+
record=selected_primary_idea,
|
|
153
|
+
)
|
|
154
|
+
_append_checkpoint(
|
|
155
|
+
checkpoints,
|
|
156
|
+
source_file="idea_result.json",
|
|
157
|
+
source_path=_text(source_paths.get("idea_result")),
|
|
158
|
+
source_json_pointer=selected_pointer,
|
|
159
|
+
record=selected,
|
|
160
|
+
)
|
|
161
|
+
if len(checkpoints) > max_iterations:
|
|
162
|
+
checkpoints = [checkpoints[0], checkpoints[-1]] if max_iterations == 2 else checkpoints[-max_iterations:]
|
|
163
|
+
for index, checkpoint in enumerate(checkpoints, start=1):
|
|
164
|
+
checkpoint["checkpoint_id"] = f"checkpoint-{index}"
|
|
165
|
+
return {
|
|
166
|
+
"schema_version": IDEA_EVOLUTION_APPENDIX_SCHEMA_VERSION,
|
|
167
|
+
"status": "CHECKPOINTS_AVAILABLE" if len(checkpoints) >= 2 else "INSUFFICIENT_HISTORY",
|
|
168
|
+
"selected_direction_id": expected_direction_id,
|
|
169
|
+
"max_iterations": max_iterations,
|
|
170
|
+
"temporal_order": "unknown",
|
|
171
|
+
"checkpoints": checkpoints,
|
|
172
|
+
"iterations": [],
|
|
173
|
+
"missing_audit_sources": list(bundle.get("missing_sources") or []),
|
|
174
|
+
"authoring_constraints": {
|
|
175
|
+
"language": "en",
|
|
176
|
+
"do_not_invent_missing_iterations": True,
|
|
177
|
+
"do_not_describe_checkpoints_as_temporal_iterations": True,
|
|
178
|
+
"temporal_order_is_unknown_without_iteration_id_timestamp_parent_or_auditable_diff": True,
|
|
179
|
+
"do_not_present_idea_history_as_empirical_evidence": True,
|
|
180
|
+
"all_displayed_checkpoints_require_source_anchors": True,
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def disabled_idea_evolution() -> dict[str, Any]:
|
|
186
|
+
"""Represent an explicit CLI opt-out without pretending history was unavailable."""
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"schema_version": IDEA_EVOLUTION_APPENDIX_SCHEMA_VERSION,
|
|
190
|
+
"status": "DISABLED",
|
|
191
|
+
"selected_direction_id": "",
|
|
192
|
+
"max_iterations": 0,
|
|
193
|
+
"temporal_order": "not_applicable",
|
|
194
|
+
"checkpoints": [],
|
|
195
|
+
"iterations": [],
|
|
196
|
+
"missing_audit_sources": [],
|
|
197
|
+
"authoring_constraints": {
|
|
198
|
+
"language": "en",
|
|
199
|
+
"do_not_invent_missing_iterations": True,
|
|
200
|
+
"do_not_describe_checkpoints_as_temporal_iterations": True,
|
|
201
|
+
"temporal_order_is_unknown_without_iteration_id_timestamp_parent_or_auditable_diff": True,
|
|
202
|
+
"do_not_present_idea_history_as_empirical_evidence": True,
|
|
203
|
+
"all_displayed_checkpoints_require_source_anchors": True,
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def unavailable_idea_evolution(reason: str) -> dict[str, Any]:
|
|
209
|
+
"""Expose missing optional history explicitly rather than fabricating an appendix."""
|
|
210
|
+
|
|
211
|
+
payload = disabled_idea_evolution()
|
|
212
|
+
payload["status"] = "UNAVAILABLE"
|
|
213
|
+
payload["unavailable_reason"] = _text(reason)
|
|
214
|
+
return payload
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
__all__ = [
|
|
218
|
+
"IDEA_EVOLUTION_APPENDIX_SCHEMA_VERSION",
|
|
219
|
+
"IdeaEvolutionError",
|
|
220
|
+
"disabled_idea_evolution",
|
|
221
|
+
"project_idea_evolution",
|
|
222
|
+
"unavailable_idea_evolution",
|
|
223
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Load and validate a canonical ExperimentDesign Author handoff."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import hashlib
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
from .contracts import validate_author_input
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthorInputLoadError(ValueError):
|
|
14
|
+
"""Raised when an Author handoff cannot safely enter the writing workflow."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_author_input_with_identity(path: str | Path) -> tuple[Path, dict[str, Any], dict[str, Any]]:
|
|
18
|
+
"""Read, validate, snapshot, and identify one handoff from the same bytes."""
|
|
19
|
+
|
|
20
|
+
resolved_path = Path(path).expanduser().resolve()
|
|
21
|
+
if not resolved_path.is_file():
|
|
22
|
+
raise AuthorInputLoadError(f"Research Plan Author input is not a file: {resolved_path}")
|
|
23
|
+
try:
|
|
24
|
+
raw_bytes = resolved_path.read_bytes()
|
|
25
|
+
except OSError as error:
|
|
26
|
+
raise AuthorInputLoadError(f"Cannot read Research Plan Author input '{resolved_path}': {error}") from error
|
|
27
|
+
try:
|
|
28
|
+
payload = json.loads(raw_bytes.decode("utf-8"))
|
|
29
|
+
except json.JSONDecodeError as error:
|
|
30
|
+
raise AuthorInputLoadError(f"Research Plan Author input is not valid JSON: {resolved_path}: {error}") from error
|
|
31
|
+
except UnicodeDecodeError as error:
|
|
32
|
+
raise AuthorInputLoadError(f"Research Plan Author input is not UTF-8 JSON: {resolved_path}: {error}") from error
|
|
33
|
+
if not isinstance(payload, Mapping):
|
|
34
|
+
raise AuthorInputLoadError("Research Plan Author input must contain one JSON object")
|
|
35
|
+
errors = validate_author_input(payload)
|
|
36
|
+
if errors:
|
|
37
|
+
raise AuthorInputLoadError("Research Plan Author input validation failed: " + "; ".join(errors))
|
|
38
|
+
return (
|
|
39
|
+
resolved_path,
|
|
40
|
+
dict(payload),
|
|
41
|
+
{
|
|
42
|
+
"path": str(resolved_path),
|
|
43
|
+
"sha256": hashlib.sha256(raw_bytes).hexdigest(),
|
|
44
|
+
"byte_size": len(raw_bytes),
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_author_input(path: str | Path) -> tuple[Path, dict[str, Any]]:
|
|
50
|
+
"""Read one explicit Author JSON artifact without selecting an ambiguous run file."""
|
|
51
|
+
|
|
52
|
+
resolved_path, payload, _identity = load_author_input_with_identity(path)
|
|
53
|
+
return resolved_path, payload
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def identify_author_input(path: str | Path) -> dict[str, Any]:
|
|
57
|
+
"""Return a stable identity for the exact handoff bytes that were loaded."""
|
|
58
|
+
|
|
59
|
+
resolved_path = Path(path).expanduser().resolve()
|
|
60
|
+
try:
|
|
61
|
+
raw_bytes = resolved_path.read_bytes()
|
|
62
|
+
except OSError as error:
|
|
63
|
+
raise AuthorInputLoadError(f"Cannot identify Research Plan Author input '{resolved_path}': {error}") from error
|
|
64
|
+
return {
|
|
65
|
+
"path": str(resolved_path),
|
|
66
|
+
"sha256": hashlib.sha256(raw_bytes).hexdigest(),
|
|
67
|
+
"byte_size": len(raw_bytes),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
"AuthorInputLoadError",
|
|
73
|
+
"identify_author_input",
|
|
74
|
+
"load_author_input",
|
|
75
|
+
"load_author_input_with_identity",
|
|
76
|
+
]
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Isolated, shell-free LaTeX/BibTeX compilation for Author render projects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import tempfile
|
|
12
|
+
from time import perf_counter
|
|
13
|
+
from collections.abc import Mapping
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LATEX_COMPILE_SCHEMA_VERSION = "research_plan_author_latex_compile_v1"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LatexCompilerError(RuntimeError):
|
|
21
|
+
"""Raised when a required LaTeX executable cannot be resolved safely."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class LatexCompileResult:
|
|
26
|
+
success: bool
|
|
27
|
+
report: dict[str, Any]
|
|
28
|
+
log_text: str
|
|
29
|
+
staged_pdf: Path | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _text(value: object) -> str:
|
|
33
|
+
return str(value or "").strip()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _absolute_executable_path(value: str | Path) -> Path:
|
|
37
|
+
"""Make an executable path absolute without dereferencing its command name.
|
|
38
|
+
|
|
39
|
+
LaTeX distributions frequently expose ``pdflatex`` and ``xelatex`` as
|
|
40
|
+
symlinks to lower-level binaries. The invoked filename selects the TeX
|
|
41
|
+
format, so resolving that symlink changes the command's behavior.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
return Path(value).expanduser().absolute()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def resolve_executable(
|
|
48
|
+
*,
|
|
49
|
+
explicit: str | Path | None,
|
|
50
|
+
environment_variable: str,
|
|
51
|
+
configured: str | Path | None,
|
|
52
|
+
fallback: str,
|
|
53
|
+
label: str,
|
|
54
|
+
) -> Path:
|
|
55
|
+
"""Resolve one executable in declared precedence order; a supplied bad path fails."""
|
|
56
|
+
|
|
57
|
+
candidates = (
|
|
58
|
+
("explicit", _text(explicit)),
|
|
59
|
+
(f"environment:{environment_variable}", _text(os.environ.get(environment_variable))),
|
|
60
|
+
("configuration", _text(configured)),
|
|
61
|
+
("PATH", fallback),
|
|
62
|
+
)
|
|
63
|
+
for source, candidate in candidates:
|
|
64
|
+
if not candidate:
|
|
65
|
+
continue
|
|
66
|
+
path = Path(candidate).expanduser()
|
|
67
|
+
has_path_syntax = path.is_absolute() or any(separator in candidate for separator in ("/", "\\"))
|
|
68
|
+
if has_path_syntax:
|
|
69
|
+
executable_path = _absolute_executable_path(path)
|
|
70
|
+
if executable_path.is_file():
|
|
71
|
+
return executable_path
|
|
72
|
+
if source != "PATH":
|
|
73
|
+
raise LatexCompilerError(f"{label} from {source} is not an executable file: {executable_path}")
|
|
74
|
+
continue
|
|
75
|
+
located = shutil.which(candidate)
|
|
76
|
+
if located:
|
|
77
|
+
return _absolute_executable_path(located)
|
|
78
|
+
if source != "PATH":
|
|
79
|
+
raise LatexCompilerError(f"{label} from {source} is not available on PATH: {candidate}")
|
|
80
|
+
raise LatexCompilerError(
|
|
81
|
+
f"{label} is unavailable; set {environment_variable}, pass the matching CLI option, or configure it explicitly"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _safe_command(command: list[str]) -> list[str]:
|
|
86
|
+
return [str(argument).replace("\n", " ").replace("\r", " ")[:2000] for argument in command]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _run_command(command: list[str], *, cwd: Path, timeout_seconds: int) -> tuple[dict[str, Any], str]:
|
|
90
|
+
started_at = perf_counter()
|
|
91
|
+
safe = _safe_command(command)
|
|
92
|
+
try:
|
|
93
|
+
completed = subprocess.run(
|
|
94
|
+
command,
|
|
95
|
+
cwd=str(cwd),
|
|
96
|
+
shell=False,
|
|
97
|
+
capture_output=True,
|
|
98
|
+
text=True,
|
|
99
|
+
encoding="utf-8",
|
|
100
|
+
errors="replace",
|
|
101
|
+
timeout=max(1, int(timeout_seconds)),
|
|
102
|
+
check=False,
|
|
103
|
+
)
|
|
104
|
+
output = (completed.stdout or "") + ("\n" if completed.stdout and completed.stderr else "") + (completed.stderr or "")
|
|
105
|
+
return (
|
|
106
|
+
{
|
|
107
|
+
"command": safe,
|
|
108
|
+
"return_code": completed.returncode,
|
|
109
|
+
"timed_out": False,
|
|
110
|
+
"elapsed_ms": round((perf_counter() - started_at) * 1000, 3),
|
|
111
|
+
},
|
|
112
|
+
output,
|
|
113
|
+
)
|
|
114
|
+
except subprocess.TimeoutExpired as error:
|
|
115
|
+
stdout = error.stdout.decode("utf-8", "replace") if isinstance(error.stdout, bytes) else str(error.stdout or "")
|
|
116
|
+
stderr = error.stderr.decode("utf-8", "replace") if isinstance(error.stderr, bytes) else str(error.stderr or "")
|
|
117
|
+
return (
|
|
118
|
+
{
|
|
119
|
+
"command": safe,
|
|
120
|
+
"return_code": None,
|
|
121
|
+
"timed_out": True,
|
|
122
|
+
"elapsed_ms": round((perf_counter() - started_at) * 1000, 3),
|
|
123
|
+
},
|
|
124
|
+
stdout + ("\n" if stdout and stderr else "") + stderr,
|
|
125
|
+
)
|
|
126
|
+
except OSError as error:
|
|
127
|
+
return (
|
|
128
|
+
{
|
|
129
|
+
"command": safe,
|
|
130
|
+
"return_code": None,
|
|
131
|
+
"timed_out": False,
|
|
132
|
+
"elapsed_ms": round((perf_counter() - started_at) * 1000, 3),
|
|
133
|
+
"execution_error": type(error).__name__,
|
|
134
|
+
},
|
|
135
|
+
str(error),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def compile_latex_project(
|
|
140
|
+
project_dir: str | Path,
|
|
141
|
+
*,
|
|
142
|
+
main_tex: str | Path,
|
|
143
|
+
latex_engine: str | Path,
|
|
144
|
+
bibtex: str | Path | None,
|
|
145
|
+
run_bibtex: bool,
|
|
146
|
+
timeout_seconds: int,
|
|
147
|
+
staged_pdf_path: str | Path,
|
|
148
|
+
logger: Any | None = None,
|
|
149
|
+
) -> LatexCompileResult:
|
|
150
|
+
"""Compile an isolated copy and stage a PDF only after all passes succeed."""
|
|
151
|
+
|
|
152
|
+
source_project = Path(project_dir).expanduser().resolve()
|
|
153
|
+
source_main = Path(main_tex).expanduser().resolve()
|
|
154
|
+
target_pdf = Path(staged_pdf_path).expanduser().resolve()
|
|
155
|
+
try:
|
|
156
|
+
main_relative = source_main.relative_to(source_project)
|
|
157
|
+
except ValueError as error:
|
|
158
|
+
raise LatexCompilerError("main TeX file must remain inside the render project") from error
|
|
159
|
+
if not source_project.is_dir() or not source_main.is_file():
|
|
160
|
+
raise LatexCompilerError("render project or main TeX file does not exist")
|
|
161
|
+
engine_path = _absolute_executable_path(latex_engine)
|
|
162
|
+
bibtex_path = _absolute_executable_path(bibtex) if bibtex else None
|
|
163
|
+
if not engine_path.is_file():
|
|
164
|
+
raise LatexCompilerError(f"LaTeX engine is not an executable file: {engine_path}")
|
|
165
|
+
if run_bibtex and (bibtex_path is None or not bibtex_path.is_file()):
|
|
166
|
+
raise LatexCompilerError("BibTeX is required by emitted references but is not an executable file")
|
|
167
|
+
base_name = main_relative.stem
|
|
168
|
+
commands: list[tuple[str, list[str]]] = [
|
|
169
|
+
(
|
|
170
|
+
"latex_initial",
|
|
171
|
+
[str(engine_path), "-interaction=nonstopmode", "-halt-on-error", "-no-shell-escape", main_relative.name],
|
|
172
|
+
)
|
|
173
|
+
]
|
|
174
|
+
if run_bibtex:
|
|
175
|
+
commands.append(("bibtex", [str(bibtex_path), base_name]))
|
|
176
|
+
commands.extend(
|
|
177
|
+
[
|
|
178
|
+
(
|
|
179
|
+
"latex_resolve_references_1",
|
|
180
|
+
[str(engine_path), "-interaction=nonstopmode", "-halt-on-error", "-no-shell-escape", main_relative.name],
|
|
181
|
+
),
|
|
182
|
+
(
|
|
183
|
+
"latex_resolve_references_2",
|
|
184
|
+
[str(engine_path), "-interaction=nonstopmode", "-halt-on-error", "-no-shell-escape", main_relative.name],
|
|
185
|
+
),
|
|
186
|
+
]
|
|
187
|
+
)
|
|
188
|
+
command_reports: list[dict[str, Any]] = []
|
|
189
|
+
log_parts: list[str] = []
|
|
190
|
+
failure_stage = ""
|
|
191
|
+
with tempfile.TemporaryDirectory(prefix="research-plan-author-compile-") as temporary_root:
|
|
192
|
+
workspace = Path(temporary_root) / "project"
|
|
193
|
+
shutil.copytree(source_project, workspace)
|
|
194
|
+
compile_cwd = workspace / main_relative.parent
|
|
195
|
+
for stage, command in commands:
|
|
196
|
+
if logger is not None:
|
|
197
|
+
logger.emit("latex_compile", "command_started", status="RUNNING", compile_stage=stage, command=_safe_command(command))
|
|
198
|
+
report, output = _run_command(command, cwd=compile_cwd, timeout_seconds=timeout_seconds)
|
|
199
|
+
report["stage"] = stage
|
|
200
|
+
command_reports.append(report)
|
|
201
|
+
log_parts.extend(
|
|
202
|
+
[
|
|
203
|
+
f"===== {stage} =====",
|
|
204
|
+
"COMMAND: " + " ".join(report["command"]),
|
|
205
|
+
output.rstrip(),
|
|
206
|
+
"",
|
|
207
|
+
]
|
|
208
|
+
)
|
|
209
|
+
successful = report.get("return_code") == 0 and not report.get("timed_out")
|
|
210
|
+
if logger is not None:
|
|
211
|
+
logger.emit(
|
|
212
|
+
"latex_compile",
|
|
213
|
+
"command_completed" if successful else "command_failed",
|
|
214
|
+
level="INFO" if successful else "ERROR",
|
|
215
|
+
status="COMPLETED" if successful else "FAILED",
|
|
216
|
+
compile_stage=stage,
|
|
217
|
+
return_code=report.get("return_code"),
|
|
218
|
+
timed_out=report.get("timed_out"),
|
|
219
|
+
elapsed_ms=report.get("elapsed_ms"),
|
|
220
|
+
)
|
|
221
|
+
if not successful:
|
|
222
|
+
failure_stage = stage
|
|
223
|
+
break
|
|
224
|
+
compiled_pdf = compile_cwd / f"{base_name}.pdf"
|
|
225
|
+
success = not failure_stage and compiled_pdf.is_file() and compiled_pdf.stat().st_size > 0
|
|
226
|
+
if not success and not failure_stage:
|
|
227
|
+
failure_stage = "pdf_missing"
|
|
228
|
+
log_parts.append("Compilation commands completed but the expected PDF was not produced.\n")
|
|
229
|
+
staged: Path | None = None
|
|
230
|
+
if success:
|
|
231
|
+
target_pdf.parent.mkdir(parents=True, exist_ok=True)
|
|
232
|
+
if target_pdf.exists():
|
|
233
|
+
raise LatexCompilerError(f"staged PDF target already exists: {target_pdf}")
|
|
234
|
+
temporary_pdf = target_pdf.with_name(f".{target_pdf.name}.{os.getpid()}.tmp")
|
|
235
|
+
shutil.copy2(compiled_pdf, temporary_pdf)
|
|
236
|
+
os.replace(temporary_pdf, target_pdf)
|
|
237
|
+
staged = target_pdf
|
|
238
|
+
report = {
|
|
239
|
+
"schema_version": LATEX_COMPILE_SCHEMA_VERSION,
|
|
240
|
+
"generated_at": datetime.now(timezone.utc).isoformat(timespec="milliseconds"),
|
|
241
|
+
"success": bool(success),
|
|
242
|
+
"failure_stage": failure_stage,
|
|
243
|
+
"project_dir": str(source_project),
|
|
244
|
+
"main_tex": str(source_main),
|
|
245
|
+
"run_bibtex": bool(run_bibtex),
|
|
246
|
+
"timeout_seconds": int(timeout_seconds),
|
|
247
|
+
"allow_shell_escape": False,
|
|
248
|
+
"commands": command_reports,
|
|
249
|
+
"staged_pdf": str(staged) if staged is not None else "",
|
|
250
|
+
}
|
|
251
|
+
return LatexCompileResult(success=bool(success), report=report, log_text="\n".join(log_parts), staged_pdf=staged)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
__all__ = [
|
|
255
|
+
"LATEX_COMPILE_SCHEMA_VERSION",
|
|
256
|
+
"LatexCompileResult",
|
|
257
|
+
"LatexCompilerError",
|
|
258
|
+
"compile_latex_project",
|
|
259
|
+
"resolve_executable",
|
|
260
|
+
]
|