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,208 @@
|
|
|
1
|
+
"""Build a global, source-bounded knowledge base for Research Plan Authoring."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _mapping(value: object) -> dict[str, Any]:
|
|
12
|
+
return dict(value) if isinstance(value, Mapping) else {}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _text(value: object) -> str:
|
|
16
|
+
return str(value or "").strip()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _append_unique(records: list[dict[str, Any]], record: dict[str, Any], *, key: str) -> None:
|
|
20
|
+
value = _text(record.get(key))
|
|
21
|
+
if value and not any(_text(existing.get(key)) == value for existing in records):
|
|
22
|
+
records.append(record)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _citation_key(source_id: str) -> str:
|
|
26
|
+
"""Keep BibTeX keys stable and syntax-safe without exposing a raw URL/DOI."""
|
|
27
|
+
|
|
28
|
+
digest = hashlib.sha256(source_id.encode("utf-8")).hexdigest()[:16]
|
|
29
|
+
return f"cite_{digest}"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _source_items(items: object, *, prefix: str) -> list[dict[str, Any]]:
|
|
33
|
+
values = items if isinstance(items, list) else []
|
|
34
|
+
records: list[dict[str, Any]] = []
|
|
35
|
+
for index, item in enumerate(values, start=1):
|
|
36
|
+
canonical = json.dumps(item, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False)
|
|
37
|
+
records.append(
|
|
38
|
+
{
|
|
39
|
+
"source_item_id": f"{prefix}-{index}",
|
|
40
|
+
"source_item_sha256": hashlib.sha256(canonical.encode("utf-8")).hexdigest(),
|
|
41
|
+
"original_item": deepcopy(item),
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
return records
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def build_frozen_source_registry(preparation: Mapping[str, Any]) -> dict[str, Any]:
|
|
48
|
+
"""Expose every already-known source; an LLM cannot mint bibliography data."""
|
|
49
|
+
|
|
50
|
+
bundle = _mapping(preparation.get("source_bundle"))
|
|
51
|
+
handoff = _mapping(bundle.get("author_context"))
|
|
52
|
+
compact = _mapping(handoff.get("source_registry"))
|
|
53
|
+
evidence_cards_by_id = {
|
|
54
|
+
str(card_id): deepcopy(dict(card))
|
|
55
|
+
for card_id, card in (_mapping(compact.get("evidence_cards_by_id"))).items()
|
|
56
|
+
if isinstance(card, Mapping)
|
|
57
|
+
}
|
|
58
|
+
allowed_source_ids = [
|
|
59
|
+
_text(source_id)
|
|
60
|
+
for source_id in compact.get("allowed_source_ids") or []
|
|
61
|
+
if _text(source_id)
|
|
62
|
+
]
|
|
63
|
+
citations = [
|
|
64
|
+
deepcopy(dict(citation))
|
|
65
|
+
for citation in compact.get("citation_registry") or []
|
|
66
|
+
if isinstance(citation, Mapping)
|
|
67
|
+
]
|
|
68
|
+
survey_sources = _mapping(bundle.get("survey_sources"))
|
|
69
|
+
survey_artifacts = _mapping(survey_sources.get("artifacts"))
|
|
70
|
+
survey_anchor_ids = [f"survey:{name}" for name in sorted(survey_artifacts)]
|
|
71
|
+
survey_markdown = _mapping(survey_artifacts.get("survey_markdown"))
|
|
72
|
+
for excerpt in survey_markdown.get("excerpts") or []:
|
|
73
|
+
if not isinstance(excerpt, Mapping):
|
|
74
|
+
continue
|
|
75
|
+
anchor_id = _text(excerpt.get("anchor_id"))
|
|
76
|
+
if anchor_id.startswith("survey:survey_markdown#") and anchor_id not in survey_anchor_ids:
|
|
77
|
+
survey_anchor_ids.append(anchor_id)
|
|
78
|
+
unknown_items = _source_items(handoff.get("unknown_items"), prefix="unknown")
|
|
79
|
+
review_items = _source_items(handoff.get("review_items"), prefix="review")
|
|
80
|
+
return {
|
|
81
|
+
"schema_version": "research_plan_author_source_registry_v3",
|
|
82
|
+
"allowed_source_ids": allowed_source_ids,
|
|
83
|
+
"allowed_survey_anchor_ids": survey_anchor_ids,
|
|
84
|
+
"evidence_cards_by_id": evidence_cards_by_id,
|
|
85
|
+
"citation_registry": deepcopy(citations),
|
|
86
|
+
"unknown_items": unknown_items,
|
|
87
|
+
"review_items": review_items,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
_RECOMMENDED_SLOTS_BY_SECTION = {
|
|
92
|
+
"introduction": {"research_object_measurability", "mechanism", "boundary_conditions"},
|
|
93
|
+
"survey_and_research_gap": {"research_object_measurability", "mechanism", "boundary_conditions"},
|
|
94
|
+
"study_design_and_methods": {
|
|
95
|
+
"study_design",
|
|
96
|
+
"measurement_calibration",
|
|
97
|
+
"comparison_controls",
|
|
98
|
+
"statistics_bias",
|
|
99
|
+
},
|
|
100
|
+
"computational_evaluation_protocol": {
|
|
101
|
+
"study_design",
|
|
102
|
+
"measurement_calibration",
|
|
103
|
+
"comparison_controls",
|
|
104
|
+
"statistics_bias",
|
|
105
|
+
},
|
|
106
|
+
"expected_outcomes": {"mechanism", "comparison_controls", "statistics_bias"},
|
|
107
|
+
"risk_limitations_and_review": {"risk_ethics_reproducibility", "boundary_conditions"},
|
|
108
|
+
"appendix_evidence_and_review": None,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def build_authoring_knowledge_base(
|
|
113
|
+
preparation: Mapping[str, Any],
|
|
114
|
+
source_registry: Mapping[str, Any],
|
|
115
|
+
) -> dict[str, Any]:
|
|
116
|
+
"""Assemble every frozen upstream artifact into one writer-visible knowledge base.
|
|
117
|
+
|
|
118
|
+
The knowledge base is deliberately broader than a route recommendation. It
|
|
119
|
+
preserves provenance by exposing canonical IDs and metadata, but it never
|
|
120
|
+
turns an evidence slot into a permission boundary for scholarly synthesis.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
bundle = _mapping(preparation.get("source_bundle"))
|
|
124
|
+
author_context = _mapping(bundle.get("author_context"))
|
|
125
|
+
return {
|
|
126
|
+
"schema_version": "research_plan_authoring_knowledge_base_v1",
|
|
127
|
+
"theory_spine": deepcopy(_mapping(preparation.get("theory_spine"))),
|
|
128
|
+
"source_catalog": {
|
|
129
|
+
"allowed_source_ids": list(source_registry.get("allowed_source_ids") or []),
|
|
130
|
+
"citation_registry": deepcopy(list(source_registry.get("citation_registry") or [])),
|
|
131
|
+
"evidence_cards_by_id": deepcopy(_mapping(source_registry.get("evidence_cards_by_id"))),
|
|
132
|
+
"survey_anchor_ids": list(source_registry.get("allowed_survey_anchor_ids") or []),
|
|
133
|
+
},
|
|
134
|
+
"upstream_artifacts": {
|
|
135
|
+
"selected_direction": deepcopy(author_context.get("selected_direction")),
|
|
136
|
+
"research_design": deepcopy(author_context.get("research_design")),
|
|
137
|
+
"hypothesis_mapping": deepcopy(author_context.get("hypothesis_mapping")),
|
|
138
|
+
"formal_reasoning": deepcopy(author_context.get("formal_reasoning")),
|
|
139
|
+
"counterexample_analysis": deepcopy(author_context.get("counterexample_analysis")),
|
|
140
|
+
"outcome_branches": deepcopy(author_context.get("outcome_branches")),
|
|
141
|
+
"reasoning_context": deepcopy(author_context.get("reasoning_context")),
|
|
142
|
+
"variables_and_operationalization": deepcopy(author_context.get("variables_and_operationalization")),
|
|
143
|
+
"idea_evolution": deepcopy(_mapping(bundle.get("idea_evolution"))),
|
|
144
|
+
"survey_binding": deepcopy(_mapping(bundle.get("survey_binding"))),
|
|
145
|
+
},
|
|
146
|
+
"unknown_items": deepcopy(list(source_registry.get("unknown_items") or [])),
|
|
147
|
+
"review_items": deepcopy(list(source_registry.get("review_items") or [])),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def source_registry_for_route(source_registry: Mapping[str, Any], route: Mapping[str, Any]) -> dict[str, Any]:
|
|
152
|
+
"""Return the global catalog plus non-binding relevance recommendations.
|
|
153
|
+
|
|
154
|
+
Earlier versions used ``claim_slot`` to hide sources from most sections.
|
|
155
|
+
That made source traceability a writing prohibition and caused shallow,
|
|
156
|
+
repetitive prose. The writer may now use any canonical source; slots only
|
|
157
|
+
help it start from a sensible evidence cluster.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
section_id = _text(route.get("section_id")).casefold()
|
|
161
|
+
recommended_slots = _RECOMMENDED_SLOTS_BY_SECTION.get(section_id, set())
|
|
162
|
+
cards = _mapping(source_registry.get("evidence_cards_by_id"))
|
|
163
|
+
if recommended_slots is None:
|
|
164
|
+
recommended_source_ids = list(source_registry.get("allowed_source_ids") or [])
|
|
165
|
+
else:
|
|
166
|
+
recommended_source_ids = sorted(
|
|
167
|
+
{
|
|
168
|
+
_text(_mapping(card).get("source_id"))
|
|
169
|
+
for card in cards.values()
|
|
170
|
+
if _text(_mapping(card).get("source_id"))
|
|
171
|
+
and _text(_mapping(card).get("claim_slot")).casefold() in recommended_slots
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
return {
|
|
175
|
+
"schema_version": _text(source_registry.get("schema_version")),
|
|
176
|
+
"allowed_source_ids": list(source_registry.get("allowed_source_ids") or []),
|
|
177
|
+
"allowed_survey_anchor_ids": list(source_registry.get("allowed_survey_anchor_ids") or []),
|
|
178
|
+
"evidence_cards_by_id": deepcopy(cards),
|
|
179
|
+
"citation_registry": deepcopy(list(source_registry.get("citation_registry") or [])),
|
|
180
|
+
"unknown_items": deepcopy(list(source_registry.get("unknown_items") or [])),
|
|
181
|
+
"review_items": deepcopy(list(source_registry.get("review_items") or [])),
|
|
182
|
+
"recommended_source_ids": recommended_source_ids,
|
|
183
|
+
"recommended_claim_slots": sorted(recommended_slots or set()),
|
|
184
|
+
"authoring_knowledge_base": deepcopy(_mapping(source_registry.get("authoring_knowledge_base"))),
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def source_registry_for_blueprint_section(
|
|
189
|
+
source_registry: Mapping[str, Any],
|
|
190
|
+
route: Mapping[str, Any],
|
|
191
|
+
blueprint_section: Mapping[str, Any],
|
|
192
|
+
) -> dict[str, Any]:
|
|
193
|
+
"""Preserve global source access after Blueprint planning.
|
|
194
|
+
|
|
195
|
+
``allowed_source_ids`` in historic Blueprint artifacts is retained only for
|
|
196
|
+
cache compatibility and observability. It must never narrow the sources
|
|
197
|
+
available to the section writer.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
return source_registry_for_route(source_registry, route)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
__all__ = [
|
|
204
|
+
"build_frozen_source_registry",
|
|
205
|
+
"build_authoring_knowledge_base",
|
|
206
|
+
"source_registry_for_blueprint_section",
|
|
207
|
+
"source_registry_for_route",
|
|
208
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Load the Survey artifacts required by a complete Research Plan Author run."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
from src.pipeline.survey_idea_loader import SurveyIdeaLoadError, load_survey_idea_context
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SurveyAuthorSourceError(ValueError):
|
|
14
|
+
"""Raised when verified Survey sources cannot be used for Author writing."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_REQUIRED_ARTIFACTS = (
|
|
18
|
+
"idea_handoff",
|
|
19
|
+
"gap_ledger",
|
|
20
|
+
"project_context",
|
|
21
|
+
"survey_json",
|
|
22
|
+
"survey_markdown",
|
|
23
|
+
"claim_traceability",
|
|
24
|
+
"evidence_plan",
|
|
25
|
+
)
|
|
26
|
+
_MARKDOWN_HEADING = re.compile(r"^#{1,6}\s+.+?\s*$", re.MULTILINE)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _mapping(value: object) -> dict[str, Any]:
|
|
30
|
+
return dict(value) if isinstance(value, Mapping) else {}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _text(value: object) -> str:
|
|
34
|
+
return str(value or "").strip()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _safe_artifact_path(base_dir: Path, entry: Mapping[str, Any], name: str) -> Path:
|
|
38
|
+
relative_path = _text(entry.get("path"))
|
|
39
|
+
if not relative_path:
|
|
40
|
+
raise SurveyAuthorSourceError(f"Survey manifest is missing artifact entry: {name}")
|
|
41
|
+
candidate = (base_dir / relative_path).resolve()
|
|
42
|
+
try:
|
|
43
|
+
candidate.relative_to(base_dir)
|
|
44
|
+
except ValueError as error:
|
|
45
|
+
raise SurveyAuthorSourceError(f"Survey artifact path escapes manifest directory: {name}") from error
|
|
46
|
+
if not candidate.is_file():
|
|
47
|
+
raise SurveyAuthorSourceError(f"Survey artifact is missing: {candidate}")
|
|
48
|
+
return candidate
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _read_json(path: Path, name: str) -> dict[str, Any]:
|
|
52
|
+
try:
|
|
53
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
54
|
+
except OSError as error:
|
|
55
|
+
raise SurveyAuthorSourceError(f"Cannot read Survey artifact '{name}': {error}") from error
|
|
56
|
+
except json.JSONDecodeError as error:
|
|
57
|
+
raise SurveyAuthorSourceError(f"Survey artifact '{name}' is not valid JSON: {error}") from error
|
|
58
|
+
if not isinstance(payload, Mapping):
|
|
59
|
+
raise SurveyAuthorSourceError(f"Survey artifact '{name}' must contain one JSON object")
|
|
60
|
+
return dict(payload)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _survey_markdown_excerpts(markdown: str) -> list[dict[str, Any]]:
|
|
64
|
+
"""Split verified Survey Markdown into complete, source-addressable excerpts."""
|
|
65
|
+
|
|
66
|
+
starts = [match.start() for match in _MARKDOWN_HEADING.finditer(markdown)]
|
|
67
|
+
boundaries = [0, *starts] if starts and starts[0] else starts
|
|
68
|
+
if not boundaries:
|
|
69
|
+
boundaries = [0] if markdown.strip() else []
|
|
70
|
+
excerpts: list[dict[str, Any]] = []
|
|
71
|
+
for ordinal, start in enumerate(boundaries, start=1):
|
|
72
|
+
end = boundaries[ordinal] if ordinal < len(boundaries) else len(markdown)
|
|
73
|
+
text = markdown[start:end].strip()
|
|
74
|
+
if not text:
|
|
75
|
+
continue
|
|
76
|
+
first_line = text.splitlines()[0].strip()
|
|
77
|
+
heading = first_line.lstrip("#").strip() if first_line.startswith("#") else "Survey preamble"
|
|
78
|
+
excerpts.append(
|
|
79
|
+
{
|
|
80
|
+
"anchor_id": f"survey:survey_markdown#section-{ordinal:03d}",
|
|
81
|
+
"heading": heading,
|
|
82
|
+
"ordinal": ordinal,
|
|
83
|
+
"text": text,
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
return excerpts
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_verified_survey_sources(source: str | Path) -> dict[str, Any]:
|
|
90
|
+
"""Return a verified, source-addressable Survey bundle for Author composition."""
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
context = load_survey_idea_context(source)
|
|
94
|
+
except SurveyIdeaLoadError as error:
|
|
95
|
+
raise SurveyAuthorSourceError(str(error)) from error
|
|
96
|
+
if context.legacy:
|
|
97
|
+
raise SurveyAuthorSourceError(
|
|
98
|
+
"Research Plan Author requires a completed, verified survey_manifest.json; legacy Survey directories are unsupported"
|
|
99
|
+
)
|
|
100
|
+
artifact_entries = _mapping(context.manifest.get("artifacts"))
|
|
101
|
+
artifact_paths: dict[str, str] = {}
|
|
102
|
+
artifacts: dict[str, Any] = {}
|
|
103
|
+
for name in _REQUIRED_ARTIFACTS:
|
|
104
|
+
path = _safe_artifact_path(context.base_dir, _mapping(artifact_entries.get(name)), name)
|
|
105
|
+
artifact_paths[name] = str(path)
|
|
106
|
+
if name == "survey_markdown":
|
|
107
|
+
artifacts[name] = {
|
|
108
|
+
"path": str(path),
|
|
109
|
+
"content_available": True,
|
|
110
|
+
"excerpts": _survey_markdown_excerpts(context.survey_markdown),
|
|
111
|
+
}
|
|
112
|
+
else:
|
|
113
|
+
artifacts[name] = _read_json(path, name)
|
|
114
|
+
for name, raw_entry in artifact_entries.items():
|
|
115
|
+
if name in artifacts:
|
|
116
|
+
continue
|
|
117
|
+
entry = _mapping(raw_entry)
|
|
118
|
+
relative_path = _text(entry.get("path"))
|
|
119
|
+
if not relative_path:
|
|
120
|
+
continue
|
|
121
|
+
optional_path = _safe_artifact_path(context.base_dir, entry, str(name))
|
|
122
|
+
artifact_paths[str(name)] = str(optional_path)
|
|
123
|
+
return {
|
|
124
|
+
"schema_version": "research_plan_author_survey_sources_v1",
|
|
125
|
+
"manifest_path": str(context.manifest_path),
|
|
126
|
+
"base_dir": str(context.base_dir),
|
|
127
|
+
"survey_run_id": context.survey_run_id,
|
|
128
|
+
"project_id": context.project_id,
|
|
129
|
+
"project_context_fingerprint": context.project_context_fingerprint,
|
|
130
|
+
"topic": context.topic,
|
|
131
|
+
"manifest": dict(context.manifest),
|
|
132
|
+
"artifacts": artifacts,
|
|
133
|
+
"artifact_paths": artifact_paths,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = ["SurveyAuthorSourceError", "load_verified_survey_sources"]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Copy and adapt declared LaTeX templates without changing their source tree."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import shutil
|
|
9
|
+
import tempfile
|
|
10
|
+
from typing import Mapping
|
|
11
|
+
|
|
12
|
+
from .template_profile import TemplateInsertion, TemplateProfile
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TemplateAdapterError(RuntimeError):
|
|
16
|
+
"""Raised when an explicit template cannot be copied or adapted safely."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class MaterializedTemplate:
|
|
21
|
+
profile_id: str
|
|
22
|
+
source_dir: Path
|
|
23
|
+
project_dir: Path
|
|
24
|
+
main_tex: Path
|
|
25
|
+
generated_bib: Path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_relative(root: Path, value: str, *, label: str) -> Path:
|
|
29
|
+
relative = Path(value)
|
|
30
|
+
if relative.is_absolute() or ".." in relative.parts:
|
|
31
|
+
raise TemplateAdapterError(f"{label} must be a relative path inside the template")
|
|
32
|
+
candidate = (root / relative).resolve()
|
|
33
|
+
try:
|
|
34
|
+
candidate.relative_to(root)
|
|
35
|
+
except ValueError as error:
|
|
36
|
+
raise TemplateAdapterError(f"{label} escapes the template root") from error
|
|
37
|
+
return candidate
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _reject_symlinks(root: Path) -> None:
|
|
41
|
+
for candidate in root.rglob("*"):
|
|
42
|
+
if candidate.is_symlink():
|
|
43
|
+
raise TemplateAdapterError(
|
|
44
|
+
f"template contains unsupported symlink '{candidate.relative_to(root)}'; copy real supporting files instead"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _find_exact(content: str, needle: str, *, label: str) -> int:
|
|
49
|
+
count = content.count(needle)
|
|
50
|
+
if count != 1:
|
|
51
|
+
raise TemplateAdapterError(
|
|
52
|
+
f"template insertion '{label}' must occur exactly once; found {count} occurrences"
|
|
53
|
+
)
|
|
54
|
+
return content.index(needle)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _replacement_span(content: str, insertion: TemplateInsertion, *, label: str) -> tuple[int, int]:
|
|
58
|
+
if insertion.kind == "marker":
|
|
59
|
+
start = _find_exact(content, insertion.marker, label=label)
|
|
60
|
+
return start, start + len(insertion.marker)
|
|
61
|
+
if insertion.kind != "region":
|
|
62
|
+
raise TemplateAdapterError(f"template insertion '{label}' has unsupported kind '{insertion.kind}'")
|
|
63
|
+
start_anchor = _find_exact(content, insertion.start, label=f"{label}.start")
|
|
64
|
+
end_anchor = _find_exact(content, insertion.end, label=f"{label}.end")
|
|
65
|
+
if end_anchor <= start_anchor:
|
|
66
|
+
raise TemplateAdapterError(f"template insertion '{label}' end anchor must follow its start anchor")
|
|
67
|
+
start = start_anchor if insertion.include_start else start_anchor + len(insertion.start)
|
|
68
|
+
end = end_anchor + len(insertion.end) if insertion.include_end else end_anchor
|
|
69
|
+
if end <= start:
|
|
70
|
+
raise TemplateAdapterError(f"template insertion '{label}' resolves to an empty or reversed span")
|
|
71
|
+
return start, end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TemplateAdapter:
|
|
75
|
+
"""Materialize a user-owned template and apply one prevalidated replacement set."""
|
|
76
|
+
|
|
77
|
+
def materialize(
|
|
78
|
+
self,
|
|
79
|
+
template_dir: str | Path,
|
|
80
|
+
project_dir: str | Path,
|
|
81
|
+
profile: TemplateProfile,
|
|
82
|
+
) -> MaterializedTemplate:
|
|
83
|
+
source_dir = Path(template_dir).expanduser().resolve()
|
|
84
|
+
destination = Path(project_dir).expanduser().resolve()
|
|
85
|
+
if not source_dir.is_dir():
|
|
86
|
+
raise TemplateAdapterError(f"template directory does not exist: {source_dir}")
|
|
87
|
+
_reject_symlinks(source_dir)
|
|
88
|
+
source_main = _resolve_relative(source_dir, profile.main_tex, label="template main TeX path")
|
|
89
|
+
if not source_main.is_file():
|
|
90
|
+
raise TemplateAdapterError(
|
|
91
|
+
f"template profile '{profile.profile_id}' main TeX file does not exist: {profile.main_tex}"
|
|
92
|
+
)
|
|
93
|
+
reserved_destination = False
|
|
94
|
+
try:
|
|
95
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
96
|
+
destination.mkdir(exist_ok=False)
|
|
97
|
+
reserved_destination = True
|
|
98
|
+
shutil.copytree(source_dir, destination, dirs_exist_ok=True)
|
|
99
|
+
except OSError as error:
|
|
100
|
+
try:
|
|
101
|
+
if reserved_destination and destination.exists():
|
|
102
|
+
shutil.rmtree(destination, ignore_errors=True)
|
|
103
|
+
except OSError:
|
|
104
|
+
pass
|
|
105
|
+
raise TemplateAdapterError(f"cannot copy template to output-owned project: {error}") from error
|
|
106
|
+
return MaterializedTemplate(
|
|
107
|
+
profile_id=profile.profile_id,
|
|
108
|
+
source_dir=source_dir,
|
|
109
|
+
project_dir=destination,
|
|
110
|
+
main_tex=_resolve_relative(destination, profile.main_tex, label="template main TeX path"),
|
|
111
|
+
generated_bib=_resolve_relative(destination, profile.generated_bib, label="generated BibTeX path"),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def apply(
|
|
115
|
+
self,
|
|
116
|
+
materialized: MaterializedTemplate,
|
|
117
|
+
profile: TemplateProfile,
|
|
118
|
+
replacements: Mapping[str, str],
|
|
119
|
+
) -> Path:
|
|
120
|
+
required = set(profile.insertions)
|
|
121
|
+
missing = sorted(required - set(replacements))
|
|
122
|
+
unexpected = sorted(set(replacements) - required)
|
|
123
|
+
if missing or unexpected:
|
|
124
|
+
details: list[str] = []
|
|
125
|
+
if missing:
|
|
126
|
+
details.append("missing replacements: " + ", ".join(missing))
|
|
127
|
+
if unexpected:
|
|
128
|
+
details.append("unexpected replacements: " + ", ".join(unexpected))
|
|
129
|
+
raise TemplateAdapterError("; ".join(details))
|
|
130
|
+
try:
|
|
131
|
+
content = materialized.main_tex.read_text(encoding="utf-8")
|
|
132
|
+
except OSError as error:
|
|
133
|
+
raise TemplateAdapterError(f"cannot read copied template main TeX file: {error}") from error
|
|
134
|
+
spans: list[tuple[int, int, str, str]] = []
|
|
135
|
+
for label, insertion in profile.insertions.items():
|
|
136
|
+
start, end = _replacement_span(content, insertion, label=label)
|
|
137
|
+
spans.append((start, end, label, str(replacements[label])))
|
|
138
|
+
for index, insertion in enumerate(profile.discard_regions):
|
|
139
|
+
start, end = _replacement_span(content, insertion, label=f"discard_regions[{index}]")
|
|
140
|
+
spans.append((start, end, f"discard_regions[{index}]", ""))
|
|
141
|
+
ordered = sorted(spans)
|
|
142
|
+
for previous, current in zip(ordered, ordered[1:]):
|
|
143
|
+
if previous[1] > current[0]:
|
|
144
|
+
raise TemplateAdapterError(
|
|
145
|
+
f"template profile '{profile.profile_id}' contains overlapping insertion regions "
|
|
146
|
+
f"('{previous[2]}' and '{current[2]}')"
|
|
147
|
+
)
|
|
148
|
+
rendered = content
|
|
149
|
+
for start, end, _label, replacement in sorted(spans, reverse=True):
|
|
150
|
+
rendered = rendered[:start] + replacement + rendered[end:]
|
|
151
|
+
temporary: Path | None = None
|
|
152
|
+
try:
|
|
153
|
+
descriptor, raw_path = tempfile.mkstemp(
|
|
154
|
+
prefix=f".{materialized.main_tex.name}.",
|
|
155
|
+
suffix=".tmp",
|
|
156
|
+
dir=materialized.main_tex.parent,
|
|
157
|
+
text=True,
|
|
158
|
+
)
|
|
159
|
+
temporary = Path(raw_path)
|
|
160
|
+
with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as handle:
|
|
161
|
+
handle.write(rendered)
|
|
162
|
+
handle.flush()
|
|
163
|
+
os.fsync(handle.fileno())
|
|
164
|
+
os.replace(temporary, materialized.main_tex)
|
|
165
|
+
except OSError as error:
|
|
166
|
+
raise TemplateAdapterError(f"cannot write adapted TeX file: {error}") from error
|
|
167
|
+
finally:
|
|
168
|
+
if temporary is not None:
|
|
169
|
+
temporary.unlink(missing_ok=True)
|
|
170
|
+
return materialized.main_tex
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
__all__ = ["MaterializedTemplate", "TemplateAdapter", "TemplateAdapterError"]
|