misterdev 0.3.1__tar.gz → 0.4.0__tar.gz
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.
- {misterdev-0.3.1/misterdev.egg-info → misterdev-0.4.0}/PKG-INFO +3 -3
- {misterdev-0.3.1 → misterdev-0.4.0}/README.md +2 -2
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/__init__.py +1 -1
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/config.py +10 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/__init__.py +11 -0
- misterdev-0.4.0/misterdev/core/evolution/tool_corpus.py +199 -0
- misterdev-0.4.0/misterdev/core/evolution/tool_invention.py +140 -0
- misterdev-0.4.0/misterdev/core/evolution/tool_promotion.py +101 -0
- misterdev-0.4.0/misterdev/core/evolution/tool_runner.py +167 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/reproduction.py +21 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/context_mixin.py +56 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/execute_mixin.py +32 -4
- misterdev-0.4.0/misterdev/task_executors/markdown_plan_executor/results_mixin.py +49 -0
- {misterdev-0.3.1 → misterdev-0.4.0/misterdev.egg-info}/PKG-INFO +3 -3
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/SOURCES.txt +11 -0
- misterdev-0.4.0/tests/test_no_output_forgiveness.py +87 -0
- misterdev-0.4.0/tests/test_runtime_tooling_wiring.py +74 -0
- misterdev-0.4.0/tests/test_tool_capture_wiring.py +92 -0
- misterdev-0.4.0/tests/test_tool_corpus.py +79 -0
- misterdev-0.4.0/tests/test_tool_invention.py +119 -0
- misterdev-0.4.0/tests/test_tool_promotion.py +77 -0
- misterdev-0.4.0/tests/test_tool_runner.py +133 -0
- misterdev-0.3.1/misterdev/task_executors/markdown_plan_executor/results_mixin.py +0 -23
- {misterdev-0.3.1 → misterdev-0.4.0}/COMMERCIAL_LICENSE.md +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/LICENSE +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/agent.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/agent_helpers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/detection.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/merge.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/overview.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/prompts.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/cli.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/audit.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/config.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/change_tracker.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/_log.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/_text.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/c_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/cpp_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/csharp_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/extraction.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/javascript_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/kotlin_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/python_generic.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/registry.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/rust_line.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/rust_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/swift_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/typescript_tree_sitter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/_rules.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/cpp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/csharp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/css.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/elixir.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/html.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/kotlin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/python.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/react.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/rust.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/swift.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/typescript.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/lsp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/lsp_swift.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/scratchpad.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/_log.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/cache.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/engine.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/graph.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/nodes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/parsers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/syntax.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/context_budget.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/embeddings.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/free_models.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/llm_cache.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_catalog.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_ledger.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_selector.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/__main__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/adapters.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/archive.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/attribution.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/driver.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/fitness.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/guardrail.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/holdout.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/loop.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/prior.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/proposer.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/sandbox.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/screen.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/tool_library.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/bounded.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/compile_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/container.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_classifier.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_log_compressor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_resolver.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/failure_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/governance.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/outcomes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/probe.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/progress.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/project.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/registry.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/runtime.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/gitcmd.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/mcp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/mcp_gather.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/failure_log.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/failure_taxonomy.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/targeting.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/warm_start.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/models.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/modes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/advisor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/assessment.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/decomposer.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/lesson_store.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/metacognition.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/sovereign.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/targets.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/verifier_decomposition.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/report.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/report_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/task.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/claim_verifier.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/critic.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/constants.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/helpers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/goal_check.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/independent.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/mutation_gate.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/preflight.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/reflection.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/spec_tests.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/validator.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/vision_verify.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/web_verify.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/base_env.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/container_env.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/venv_env.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/base.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/edits.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/embeddings.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/errors.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/providers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/response.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/prompt_manager.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/apply.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/json_extract.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/models.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/parsing.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/logging_setup.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/mcp_server.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/nl_cli.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/plugins.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/py.typed +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/base_executor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/commands_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/critic_spec_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/edits_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/gates_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/git_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/helpers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/llm_mixin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/base_tool.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/command.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/dependency.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/file_io.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/formatter.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/git_tool.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/__init__.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/file_utils.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/process.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/dependency_links.txt +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/entry_points.txt +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/requires.txt +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/top_level.txt +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/pyproject.toml +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/setup.cfg +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_advisor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_agent_helpers.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_assessment.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_audit.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_audit_gate.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_bounded.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_budget_halt.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_change_tracker.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_claim_verifier.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_command_tool.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_compile_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_config.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_container.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_context_budget.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_c.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_cpp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_csharp_ts.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_javascript.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_kotlin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_swift.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_typescript.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_critic.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_decomposer.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_dependency_tool.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_detect_lint_deps.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_dynamic_model_integration.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_embeddings.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_classifier.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_log_compressor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_resolver.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_adapters.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_archive.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_attribution.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_driver.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_fitness.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_guardrail.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_holdout.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_holdout_wiring.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_loop.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_prior.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_proposer.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_sandbox.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_screen.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_executor.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_executor_git.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_extensibility.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_log.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_targeting.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_taxonomy.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_file_utils.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_free_models.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gatekeeper.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gates_error_context.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gitcmd.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_goal_check.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_goal_check_wiring.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_governance.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_grounded_spec.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_cpp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_csharp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_css.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_elixir.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_html.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_kotlin.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_python.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_react.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_rust.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_swift.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_typescript.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_hms_integration.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_independent.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_integration_gate_count.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_learning_semantic.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lesson_efficacy.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lesson_store.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_llm_cache.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_llm_client.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_context.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_integration.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_swift.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp_remote.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp_server.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_metacognition.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_catalog.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_ledger.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_selector.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_modes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mutation_gate.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_native_harness.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_nl_cli.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_orchestrator_config.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_orchestrator_fixes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_outcomes.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_plugins.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_polyglot_harness.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_preflight.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_probe.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_process.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_progress.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_prompt_manager.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_reflection.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_registry.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_report.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_report_view.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_reproduction_corpus.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_responses.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_runtime.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_scratchpad.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_security.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_sovereign.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_spec_tests.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_swebench_harness.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_targets.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_task_manager.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_tool_library.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_topography.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_topography_cache.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_validator.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_verifier_decomposition.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_vision_verify.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_web_verify.py +0 -0
- {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_wiring_probe_staging.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: misterdev
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: An autonomous, extensible LLM build orchestrator that plans, edits, and verifies code across languages.
|
|
5
5
|
Author-email: David Condrey <david@writerslogic.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -150,7 +150,7 @@ misterdev keeps a durable, fingerprinted stream of its own real failures and run
|
|
|
150
150
|
|
|
151
151
|
On the correctness side, misterdev works **reproduction-first**: for an issue-driven task it synthesizes a failing test from the acceptance criteria, **validates that the test actually fails on the clean tree** (a test that reproduces nothing is discarded rather than trusted), then drives the fix to turn it green — so the model edits toward a concrete, verified target instead of a description.
|
|
152
152
|
|
|
153
|
-
**Two-timescale evolution** *(
|
|
153
|
+
**Two-timescale evolution** *(built; opt-in via `orchestrator.runtime_tooling`; see [docs/two-timescale-evolution.md](docs/two-timescale-evolution.md))* takes the self-improvement further than a memoryless runtime agent can. At **runtime**, the model may author a small task-specific helper tool that runs **sandboxed** (a hardened, network-less container — untrusted code never touches the host or the repo, and with no container engine the capability degrades off); its output feeds the edit. Every invented tool is then captured with the task's outcome into a **tool corpus** — a free byproduct of normal runs — and a deliberate promotion pass admits the tools whose success **generalizes** on a held-out task split (baseline drawn from the reproduction corpus) into a **persistent, best-per-capability tool library that future runs start from**. Fast loop invents; slow loop keeps only the winners; the same held-out gate that guards scaffold self-edits keeps the library general rather than benchmark-overfit. Capability **compounds across runs** instead of being reinvented each task — the memory the current top open-source scaffolds lack. Run the promotion pass with `python -m misterdev.core.evolution.tool_promotion <project>`.
|
|
154
154
|
|
|
155
155
|
### Extensibility
|
|
156
156
|
|
|
@@ -170,7 +170,7 @@ Gate-verified pass@1 on [Aider's polyglot benchmark](https://github.com/Aider-AI
|
|
|
170
170
|
| Python | 8 / 10 | **80%** |
|
|
171
171
|
| Rust | 7 / 10 | **70%** |
|
|
172
172
|
|
|
173
|
-
A continuous stress run has solved **20/20** across the three languages with zero failures — including the exercises usually cited as hard (bowling, forth, arbitrary-precision decimal). Every solve is judged by the exercise's own hidden tests, not the model's say-so. Full numbers, methodology, and how to reproduce: **[docs/benchmark-results.md](docs/benchmark-results.md)**. Test suite: **1,
|
|
173
|
+
A continuous stress run has solved **20/20** across the three languages with zero failures — including the exercises usually cited as hard (bowling, forth, arbitrary-precision decimal). Every solve is judged by the exercise's own hidden tests, not the model's say-so. Full numbers, methodology, and how to reproduce: **[docs/benchmark-results.md](docs/benchmark-results.md)**. Test suite: **1,941 passing** — **[docs/TESTING.md](docs/TESTING.md)**.
|
|
174
174
|
|
|
175
175
|
## CLI reference
|
|
176
176
|
|
|
@@ -97,7 +97,7 @@ misterdev keeps a durable, fingerprinted stream of its own real failures and run
|
|
|
97
97
|
|
|
98
98
|
On the correctness side, misterdev works **reproduction-first**: for an issue-driven task it synthesizes a failing test from the acceptance criteria, **validates that the test actually fails on the clean tree** (a test that reproduces nothing is discarded rather than trusted), then drives the fix to turn it green — so the model edits toward a concrete, verified target instead of a description.
|
|
99
99
|
|
|
100
|
-
**Two-timescale evolution** *(
|
|
100
|
+
**Two-timescale evolution** *(built; opt-in via `orchestrator.runtime_tooling`; see [docs/two-timescale-evolution.md](docs/two-timescale-evolution.md))* takes the self-improvement further than a memoryless runtime agent can. At **runtime**, the model may author a small task-specific helper tool that runs **sandboxed** (a hardened, network-less container — untrusted code never touches the host or the repo, and with no container engine the capability degrades off); its output feeds the edit. Every invented tool is then captured with the task's outcome into a **tool corpus** — a free byproduct of normal runs — and a deliberate promotion pass admits the tools whose success **generalizes** on a held-out task split (baseline drawn from the reproduction corpus) into a **persistent, best-per-capability tool library that future runs start from**. Fast loop invents; slow loop keeps only the winners; the same held-out gate that guards scaffold self-edits keeps the library general rather than benchmark-overfit. Capability **compounds across runs** instead of being reinvented each task — the memory the current top open-source scaffolds lack. Run the promotion pass with `python -m misterdev.core.evolution.tool_promotion <project>`.
|
|
101
101
|
|
|
102
102
|
### Extensibility
|
|
103
103
|
|
|
@@ -117,7 +117,7 @@ Gate-verified pass@1 on [Aider's polyglot benchmark](https://github.com/Aider-AI
|
|
|
117
117
|
| Python | 8 / 10 | **80%** |
|
|
118
118
|
| Rust | 7 / 10 | **70%** |
|
|
119
119
|
|
|
120
|
-
A continuous stress run has solved **20/20** across the three languages with zero failures — including the exercises usually cited as hard (bowling, forth, arbitrary-precision decimal). Every solve is judged by the exercise's own hidden tests, not the model's say-so. Full numbers, methodology, and how to reproduce: **[docs/benchmark-results.md](docs/benchmark-results.md)**. Test suite: **1,
|
|
120
|
+
A continuous stress run has solved **20/20** across the three languages with zero failures — including the exercises usually cited as hard (bowling, forth, arbitrary-precision decimal). Every solve is judged by the exercise's own hidden tests, not the model's say-so. Full numbers, methodology, and how to reproduce: **[docs/benchmark-results.md](docs/benchmark-results.md)**. Test suite: **1,941 passing** — **[docs/TESTING.md](docs/TESTING.md)**.
|
|
121
121
|
|
|
122
122
|
## CLI reference
|
|
123
123
|
|
|
@@ -277,6 +277,16 @@ class OrchestratorSettings:
|
|
|
277
277
|
# Each round is at most one model turn plus one tool call; the loop always
|
|
278
278
|
# stops at this count even if the model keeps requesting tools.
|
|
279
279
|
mcp_max_tool_rounds: int = 3
|
|
280
|
+
# Runtime tool-invention (two-timescale evolution P2): when on, the model may
|
|
281
|
+
# author a small task-specific helper tool that runs SANDBOXED (hardened,
|
|
282
|
+
# network-less container via ToolRunner; untrusted code never touches the host
|
|
283
|
+
# or the repo), and its output is fed into the edit context. Off by default —
|
|
284
|
+
# it is an untrusted-code-execution surface and requires a container engine;
|
|
285
|
+
# with no engine the invention degrades to a no-op (skip). Additive: off ->
|
|
286
|
+
# the edit path is byte-identical to today. Hard-capped by
|
|
287
|
+
# ``runtime_tooling_rounds``.
|
|
288
|
+
runtime_tooling: bool = False
|
|
289
|
+
runtime_tooling_rounds: int = 2
|
|
280
290
|
# Optional governance layer: when on, a risk classifier gates risky commands
|
|
281
291
|
# (destructive/irreversible/paid) at the command seam and an append-only
|
|
282
292
|
# audit trail is written. Off by default and additive — when off the command
|
|
@@ -25,6 +25,10 @@ from .proposer import LLMProposer, build_instruction, parse_paths, parse_tag
|
|
|
25
25
|
from .sandbox import SandboxEvaluator
|
|
26
26
|
from .screen import MicroEvaluator, ScreenVerdict
|
|
27
27
|
from .tool_library import ToolCandidate, ToolLibrary
|
|
28
|
+
from .tool_corpus import ToolCorpus, ToolRecord, promote_from_corpus
|
|
29
|
+
from .tool_promotion import run_tool_promotion
|
|
30
|
+
from .tool_invention import invent_tool
|
|
31
|
+
from .tool_runner import ToolRunner, ToolRunResult
|
|
28
32
|
|
|
29
33
|
__all__ = [
|
|
30
34
|
"MicroEvaluator",
|
|
@@ -47,6 +51,13 @@ __all__ = [
|
|
|
47
51
|
"SandboxEvaluator",
|
|
48
52
|
"ToolLibrary",
|
|
49
53
|
"ToolCandidate",
|
|
54
|
+
"ToolRunner",
|
|
55
|
+
"ToolRunResult",
|
|
56
|
+
"invent_tool",
|
|
57
|
+
"ToolCorpus",
|
|
58
|
+
"ToolRecord",
|
|
59
|
+
"promote_from_corpus",
|
|
60
|
+
"run_tool_promotion",
|
|
50
61
|
"LLMProposer",
|
|
51
62
|
"build_instruction",
|
|
52
63
|
"parse_paths",
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""The tool corpus — passive accumulation of self-authored tools and the task
|
|
2
|
+
outcomes they rode along with, closing the two-timescale loop
|
|
3
|
+
(``docs/two-timescale-evolution.md``).
|
|
4
|
+
|
|
5
|
+
The runtime layer (:mod:`.tool_invention`) lets the model author a tool mid-task;
|
|
6
|
+
live-SWE-agent throws that tool away when the task ends. This corpus keeps it:
|
|
7
|
+
every invented tool is recorded with the task it ran on and whether that task
|
|
8
|
+
resolved. The signal is a FREE byproduct of normal operation — no dedicated,
|
|
9
|
+
paid A/B runs — which is the only economics a self-improvement loop can afford.
|
|
10
|
+
As runs accumulate, each tool's per-task outcome history becomes dense enough for
|
|
11
|
+
:func:`promote_from_corpus` to decide, through the SAME held-out generalization
|
|
12
|
+
gate as scaffold evolution, which tools become permanent
|
|
13
|
+
:class:`~.tool_library.ToolLibrary` entries that seed future runs.
|
|
14
|
+
|
|
15
|
+
Honesty about the signal: a corpus outcome is CORRELATIONAL — the tool was present
|
|
16
|
+
when the task resolved, not proven to have caused it — and the without-tool
|
|
17
|
+
baseline is supplied by the caller (from the reproduction corpus or a prior), not
|
|
18
|
+
fabricated here. So promotion is a defensible *ranking* of tools by held-out
|
|
19
|
+
association, sharpening as data grows; it is not a controlled causal A/B. Per-task
|
|
20
|
+
regressions are not attributed (no per-task counterfactual), so the promotion is
|
|
21
|
+
association-positive, regression-agnostic — documented, not hidden.
|
|
22
|
+
|
|
23
|
+
Persistence mirrors the reproduction corpus: one JSON file, atomic write,
|
|
24
|
+
best-effort load that degrades to empty.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import hashlib
|
|
28
|
+
import json
|
|
29
|
+
from dataclasses import asdict, dataclass, field
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Dict, List, Tuple
|
|
32
|
+
|
|
33
|
+
from misterdev.logging_setup import setup_logger
|
|
34
|
+
from misterdev.utils.file_utils import atomic_write
|
|
35
|
+
|
|
36
|
+
logger = setup_logger(__name__)
|
|
37
|
+
|
|
38
|
+
_MAX_TOOLS = 1000 # runaway guard; a real corpus is far smaller
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _tool_id(source: str) -> str:
|
|
42
|
+
"""A stable short id for a tool, keyed on its normalized source (so the same
|
|
43
|
+
tool re-authored with trivial whitespace differences collapses to one)."""
|
|
44
|
+
normalized = "\n".join(line.rstrip() for line in source.strip().splitlines())
|
|
45
|
+
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()[:16]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class ToolRecord:
|
|
50
|
+
"""One self-authored tool and its per-task outcome history."""
|
|
51
|
+
|
|
52
|
+
tool_id: str
|
|
53
|
+
niche: str
|
|
54
|
+
source: str
|
|
55
|
+
outcomes: Dict[str, bool] = field(default_factory=dict) # task_id -> resolved
|
|
56
|
+
first_run: int = 0
|
|
57
|
+
last_run: int = 0
|
|
58
|
+
|
|
59
|
+
def score_on(self, task_ids) -> Tuple[int, int]:
|
|
60
|
+
"""(resolved_count, total) over the given task ids present in this record."""
|
|
61
|
+
rel = [self.outcomes[t] for t in task_ids if t in self.outcomes]
|
|
62
|
+
return sum(1 for x in rel if x), len(rel)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ToolCorpus:
|
|
66
|
+
"""Persistent per-tool outcome history backing held-out tool promotion."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, path):
|
|
69
|
+
self.path = Path(path)
|
|
70
|
+
|
|
71
|
+
def _load(self) -> Tuple[Dict[str, ToolRecord], int]:
|
|
72
|
+
if not self.path.exists():
|
|
73
|
+
return {}, 0
|
|
74
|
+
try:
|
|
75
|
+
raw = json.loads(self.path.read_text(encoding="utf-8"))
|
|
76
|
+
except (json.JSONDecodeError, OSError):
|
|
77
|
+
return {}, 0
|
|
78
|
+
if not isinstance(raw, dict):
|
|
79
|
+
return {}, 0
|
|
80
|
+
run = int(raw.get("run", 0))
|
|
81
|
+
recs: Dict[str, ToolRecord] = {}
|
|
82
|
+
for item in raw.get("tools", []):
|
|
83
|
+
if not isinstance(item, dict) or not item.get("tool_id"):
|
|
84
|
+
continue
|
|
85
|
+
try:
|
|
86
|
+
recs[str(item["tool_id"])] = ToolRecord(
|
|
87
|
+
tool_id=str(item["tool_id"]),
|
|
88
|
+
niche=str(item.get("niche", "")),
|
|
89
|
+
source=str(item.get("source", "")),
|
|
90
|
+
outcomes={
|
|
91
|
+
str(k): bool(v) for k, v in (item.get("outcomes") or {}).items()
|
|
92
|
+
},
|
|
93
|
+
first_run=int(item.get("first_run", 0)),
|
|
94
|
+
last_run=int(item.get("last_run", 0)),
|
|
95
|
+
)
|
|
96
|
+
except (TypeError, ValueError):
|
|
97
|
+
continue
|
|
98
|
+
return recs, run
|
|
99
|
+
|
|
100
|
+
def _save(self, recs: Dict[str, ToolRecord], run: int) -> None:
|
|
101
|
+
ordered = sorted(recs.values(), key=lambda r: r.last_run, reverse=True)
|
|
102
|
+
kept = ordered[:_MAX_TOOLS]
|
|
103
|
+
payload = {"run": run, "tools": [asdict(r) for r in kept]}
|
|
104
|
+
try:
|
|
105
|
+
atomic_write(self.path, json.dumps(payload, indent=2))
|
|
106
|
+
except OSError as e: # a learning stream never fails a build
|
|
107
|
+
logger.warning(f"Tool corpus save failed (non-fatal): {e}")
|
|
108
|
+
|
|
109
|
+
def record(self, source: str, niche: str, task_id: str, resolved: bool) -> str:
|
|
110
|
+
"""Fold one (tool, task, outcome) observation in. Returns the tool id.
|
|
111
|
+
|
|
112
|
+
Best-effort: a persistence failure is logged, never raised. Last outcome
|
|
113
|
+
wins for a repeated (tool, task) pair, so a re-run updates rather than
|
|
114
|
+
double-counts.
|
|
115
|
+
"""
|
|
116
|
+
if not source or not source.strip() or not task_id:
|
|
117
|
+
return ""
|
|
118
|
+
tid = _tool_id(source)
|
|
119
|
+
recs, run = self._load()
|
|
120
|
+
run += 1
|
|
121
|
+
rec = recs.get(tid)
|
|
122
|
+
if rec is None:
|
|
123
|
+
rec = ToolRecord(tool_id=tid, niche=niche, source=source, first_run=run)
|
|
124
|
+
recs[tid] = rec
|
|
125
|
+
rec.niche = niche or rec.niche
|
|
126
|
+
rec.outcomes[task_id] = resolved
|
|
127
|
+
rec.last_run = run
|
|
128
|
+
self._save(recs, run)
|
|
129
|
+
return tid
|
|
130
|
+
|
|
131
|
+
def records(self) -> List[ToolRecord]:
|
|
132
|
+
return list(self._load()[0].values())
|
|
133
|
+
|
|
134
|
+
def stats(self) -> Dict[str, int]:
|
|
135
|
+
recs = self.records()
|
|
136
|
+
obs = sum(len(r.outcomes) for r in recs)
|
|
137
|
+
return {"tools": len(recs), "observations": obs}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def promote_from_corpus(
|
|
141
|
+
corpus: ToolCorpus,
|
|
142
|
+
library,
|
|
143
|
+
*,
|
|
144
|
+
baseline_rate,
|
|
145
|
+
min_observations: int = 5,
|
|
146
|
+
holdout_fraction: float = 0.3,
|
|
147
|
+
noise_band: float = 0.0,
|
|
148
|
+
) -> List[str]:
|
|
149
|
+
"""Promote corpus tools that GENERALIZE into the library; return promoted ids.
|
|
150
|
+
|
|
151
|
+
For each tool with at least ``min_observations`` task outcomes, split its tasks
|
|
152
|
+
into disjoint DERIVE/HOLDOUT pools (stable per-task hash), score the tool's
|
|
153
|
+
with-tool resolve-rate on each, and admit it through the library's held-out
|
|
154
|
+
gate against the WITHOUT-tool baseline. A tool is promoted only if its
|
|
155
|
+
association with success holds on the tasks it was NOT selected on — the same
|
|
156
|
+
anti-overfit ratchet scaffold evolution uses.
|
|
157
|
+
|
|
158
|
+
``baseline_rate`` is the without-tool resolve-rate: a float applied to every
|
|
159
|
+
tool, or a callable ``baseline(niche) -> float`` so the baseline can come
|
|
160
|
+
per-niche from the reproduction corpus (see ``tool_promotion``). Pure: no I/O
|
|
161
|
+
beyond the corpus/library the caller passes.
|
|
162
|
+
"""
|
|
163
|
+
from .fitness import FitnessScore
|
|
164
|
+
from .holdout import split_tasks
|
|
165
|
+
from .tool_library import ToolCandidate
|
|
166
|
+
|
|
167
|
+
baseline_of = (
|
|
168
|
+
baseline_rate if callable(baseline_rate) else (lambda _n: baseline_rate)
|
|
169
|
+
)
|
|
170
|
+
promoted: List[str] = []
|
|
171
|
+
for rec in corpus.records():
|
|
172
|
+
base = max(0.0, min(1.0, float(baseline_of(rec.niche))))
|
|
173
|
+
task_ids = sorted(rec.outcomes)
|
|
174
|
+
if len(task_ids) < min_observations:
|
|
175
|
+
continue
|
|
176
|
+
derive_ids, holdout_ids = split_tasks(task_ids, holdout_fraction)
|
|
177
|
+
dr, dt = rec.score_on(derive_ids)
|
|
178
|
+
hr, ht = rec.score_on(holdout_ids)
|
|
179
|
+
if dt == 0 or ht == 0:
|
|
180
|
+
continue
|
|
181
|
+
candidate = ToolCandidate(
|
|
182
|
+
id=rec.tool_id,
|
|
183
|
+
niche=rec.niche,
|
|
184
|
+
source=rec.source,
|
|
185
|
+
resolved=dr,
|
|
186
|
+
total=dt,
|
|
187
|
+
cost=0.0,
|
|
188
|
+
provenance=f"corpus:{len(task_ids)} tasks",
|
|
189
|
+
)
|
|
190
|
+
decision = library.consider(
|
|
191
|
+
candidate,
|
|
192
|
+
derive=FitnessScore(dr, dt, 0.0),
|
|
193
|
+
derive_base=FitnessScore(round(base * dt), dt, 0.0),
|
|
194
|
+
holdout=FitnessScore(hr, ht, 0.0),
|
|
195
|
+
holdout_base=FitnessScore(round(base * ht), ht, 0.0),
|
|
196
|
+
)
|
|
197
|
+
if decision.promote:
|
|
198
|
+
promoted.append(rec.tool_id)
|
|
199
|
+
return promoted
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Runtime tool-invention: let the model author a task-specific helper tool and
|
|
2
|
+
run it sandboxed, feeding the output back — the runtime half of two-timescale
|
|
3
|
+
evolution (``docs/two-timescale-evolution.md``) and the capability that sits #1
|
|
4
|
+
open on SWE-bench (live-SWE-agent).
|
|
5
|
+
|
|
6
|
+
Additive and best-effort, mirroring
|
|
7
|
+
:mod:`misterdev.core.integration.mcp_gather`: the model MAY emit a ``tool`` block
|
|
8
|
+
(Python source) and an optional ``stdin`` block; the source runs via the hardened,
|
|
9
|
+
network-less :class:`~misterdev.core.evolution.tool_runner.ToolRunner` (untrusted
|
|
10
|
+
code never touches the host or the repo), and its output is returned as a context
|
|
11
|
+
block the executor prepends to the edit context. A model that wants no tool
|
|
12
|
+
replies ``NO_TOOL`` and the result is ``""`` — byte-identical to the no-tooling
|
|
13
|
+
path. The loop is hard-capped so a model can author, see the output, and refine
|
|
14
|
+
once or twice, then stop.
|
|
15
|
+
|
|
16
|
+
Never raises into the build: no runner, no sandbox, a model error, or an
|
|
17
|
+
unparseable reply all degrade to whatever was produced so far (usually nothing).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
from typing import Callable, List, Optional, Protocol
|
|
22
|
+
|
|
23
|
+
from misterdev.logging_setup import setup_logger
|
|
24
|
+
|
|
25
|
+
logger = setup_logger(__name__)
|
|
26
|
+
|
|
27
|
+
# The model authors source in a ```tool (or ```python-tool) fence, with optional
|
|
28
|
+
# stdin in a ```stdin fence. Searched (not anchored) and DOTALL so the blocks may
|
|
29
|
+
# span lines and sit amid prose.
|
|
30
|
+
_TOOL_RE = re.compile(r"```(?:tool|python-tool)\s*\n(.*?)```", re.DOTALL)
|
|
31
|
+
_STDIN_RE = re.compile(r"```stdin\s*\n(.*?)```", re.DOTALL)
|
|
32
|
+
|
|
33
|
+
_HEADER = "## Tools you authored and ran (sandboxed) to inform the edit\n"
|
|
34
|
+
|
|
35
|
+
_INSTRUCTION = (
|
|
36
|
+
"You MAY author ONE small Python helper tool to compute or verify something "
|
|
37
|
+
"for this task before you edit — for example a reference calculation, a "
|
|
38
|
+
"parser, or a checker. It runs SANDBOXED: no network, no filesystem or "
|
|
39
|
+
"repository access, bounded time and memory. It is a pure function — supply "
|
|
40
|
+
"any input it needs yourself.\n\n"
|
|
41
|
+
"To author a tool, emit its source in a fenced ```tool block, and (optionally) "
|
|
42
|
+
"its stdin in a fenced ```stdin block. Its stdout is returned to you so you "
|
|
43
|
+
"can use the result in your edit. To author no tool and proceed straight to "
|
|
44
|
+
"editing, reply with NO_TOOL.\n"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _ToolRunnerLike(Protocol):
|
|
49
|
+
def run(self, source: str, stdin: str = ""): ...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _render(result) -> str:
|
|
53
|
+
"""The tool's output for the model: stdout on success, else stderr/detail."""
|
|
54
|
+
body = (result.stdout or "").strip() or (result.stderr or "").strip()
|
|
55
|
+
return body or "(no output)"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def invent_tool(
|
|
59
|
+
runner: Optional[_ToolRunnerLike],
|
|
60
|
+
ask: Callable[[str], Optional[str]],
|
|
61
|
+
*,
|
|
62
|
+
task_description: str = "",
|
|
63
|
+
error_context: str = "",
|
|
64
|
+
max_rounds: int = 2,
|
|
65
|
+
sink: Optional[List[str]] = None,
|
|
66
|
+
seeds: Optional[List[str]] = None,
|
|
67
|
+
) -> str:
|
|
68
|
+
"""Run the bounded tool-invention loop; return the invented-tools context block.
|
|
69
|
+
|
|
70
|
+
``runner`` is a :class:`ToolRunner` (may be ``None``). ``ask`` takes a prompt
|
|
71
|
+
and returns the model's reply. Each round the model may author one tool; it is
|
|
72
|
+
run sandboxed and its output fed into the next round's prompt. ``max_rounds``
|
|
73
|
+
hard-caps iterations (< 1 disables). Returns a context string suitable for
|
|
74
|
+
prepending to the edit context, or ``""`` when no tool was authored or run.
|
|
75
|
+
"""
|
|
76
|
+
if runner is None or max_rounds < 1:
|
|
77
|
+
return ""
|
|
78
|
+
invented: List[str] = []
|
|
79
|
+
for round_idx in range(max_rounds):
|
|
80
|
+
prompt = _INSTRUCTION
|
|
81
|
+
if seeds:
|
|
82
|
+
prompt += (
|
|
83
|
+
"\n## Proven tools from past runs — reuse or adapt one if it fits\n"
|
|
84
|
+
+ "\n\n".join(f"```python\n{s}\n```" for s in seeds)
|
|
85
|
+
+ "\n"
|
|
86
|
+
)
|
|
87
|
+
if task_description:
|
|
88
|
+
prompt += f"\n## Task\n{task_description}\n"
|
|
89
|
+
if error_context:
|
|
90
|
+
prompt += (
|
|
91
|
+
"\n## The current failure you may write a tool to diagnose\n"
|
|
92
|
+
f"{error_context}\n"
|
|
93
|
+
)
|
|
94
|
+
if invented:
|
|
95
|
+
prompt += "\n" + _HEADER + "\n\n".join(invented) + "\n"
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
reply = ask(prompt)
|
|
99
|
+
except Exception as e: # a model/client failure must not sink the build
|
|
100
|
+
logger.debug(
|
|
101
|
+
f"tool-invention: model call failed round {round_idx + 1}: {e}"
|
|
102
|
+
)
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
m = _TOOL_RE.search(reply or "")
|
|
106
|
+
if m is None:
|
|
107
|
+
logger.info(
|
|
108
|
+
f"tool-invention: model authored no tool in round {round_idx + 1}; "
|
|
109
|
+
"stopping."
|
|
110
|
+
)
|
|
111
|
+
break
|
|
112
|
+
source = m.group(1).strip()
|
|
113
|
+
if not source:
|
|
114
|
+
break
|
|
115
|
+
sm = _STDIN_RE.search(reply or "")
|
|
116
|
+
stdin = sm.group(1) if sm else ""
|
|
117
|
+
|
|
118
|
+
result = runner.run(source, stdin)
|
|
119
|
+
if result.status == "skip":
|
|
120
|
+
logger.info("tool-invention: no sandbox available; tool not run.")
|
|
121
|
+
break
|
|
122
|
+
if sink is not None:
|
|
123
|
+
sink.append(source) # captured for the tool corpus (P2c)
|
|
124
|
+
logger.info(
|
|
125
|
+
f"tool-invention round {round_idx + 1}/{max_rounds}: ran a tool "
|
|
126
|
+
f"({len(source)}B source) -> {result.status}"
|
|
127
|
+
)
|
|
128
|
+
invented.append(
|
|
129
|
+
f"### tool (round {round_idx + 1})\n```python\n{source}\n```\n"
|
|
130
|
+
f"output [{result.status}]:\n{_render(result)}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if not invented:
|
|
134
|
+
return ""
|
|
135
|
+
return (
|
|
136
|
+
"\n\n"
|
|
137
|
+
+ _HEADER
|
|
138
|
+
+ "You wrote and ran these tools; use their results in your edit.\n\n"
|
|
139
|
+
+ "\n\n".join(invented)
|
|
140
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""The tool-promotion pass — the deliberate step that CLOSES the two-timescale
|
|
2
|
+
loop (``docs/two-timescale-evolution.md``).
|
|
3
|
+
|
|
4
|
+
Capture is automatic (every build folds invented tools + outcomes into the tool
|
|
5
|
+
corpus). Promotion is deliberate, like the scaffold-evolution run: this pass reads
|
|
6
|
+
the tool corpus, derives the WITHOUT-tool baseline PER NICHE from the reproduction
|
|
7
|
+
corpus (the resolve-rate on comparable tasks with no self-authored tool), and
|
|
8
|
+
admits the tools whose with-tool success-association holds on a held-out task
|
|
9
|
+
split into the persistent :class:`~.tool_library.ToolLibrary`. Promoted tools then
|
|
10
|
+
seed future runs, so capability compounds.
|
|
11
|
+
|
|
12
|
+
The two corpora together are the fitness signal that a single run cannot give:
|
|
13
|
+
reproduction corpus = without-tool outcomes, tool corpus = with-tool outcomes,
|
|
14
|
+
their difference = a tool's (as-causal-as-observational-data-allows) effect. It is
|
|
15
|
+
correlational, not a controlled A/B — so this ranks tools by held-out association
|
|
16
|
+
and sharpens as data accumulates; it does not certify causation. Pure of LLM/cost:
|
|
17
|
+
it only reads the two JSON corpora and writes the library.
|
|
18
|
+
|
|
19
|
+
CLI: ``python -m misterdev.core.evolution.tool_promotion <project_path>``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Dict
|
|
26
|
+
|
|
27
|
+
from misterdev.core.evolution.tool_corpus import ToolCorpus, promote_from_corpus
|
|
28
|
+
from misterdev.core.evolution.tool_library import ToolLibrary
|
|
29
|
+
from misterdev.core.learning.reproduction import ReproductionCorpus
|
|
30
|
+
from misterdev.logging_setup import setup_logger
|
|
31
|
+
|
|
32
|
+
logger = setup_logger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def run_tool_promotion(
|
|
36
|
+
project_path,
|
|
37
|
+
*,
|
|
38
|
+
default_baseline: float = 0.5,
|
|
39
|
+
min_observations: int = 5,
|
|
40
|
+
holdout_fraction: float = 0.3,
|
|
41
|
+
noise_band: float = 0.0,
|
|
42
|
+
) -> Dict:
|
|
43
|
+
"""Run the promotion pass for a project; return a summary dict.
|
|
44
|
+
|
|
45
|
+
``default_baseline`` is the prior used for a niche the reproduction corpus has
|
|
46
|
+
no data on (so an empty baseline never spuriously promotes). Never raises: a
|
|
47
|
+
missing/corrupt corpus degrades to "nothing promoted".
|
|
48
|
+
"""
|
|
49
|
+
ev = Path(project_path) / ".orchestrator" / "evolution"
|
|
50
|
+
corpus = ToolCorpus(ev / "tool_corpus.json")
|
|
51
|
+
repro = ReproductionCorpus(ev / "reproduction.json")
|
|
52
|
+
library = ToolLibrary(ev / "tool_library.json")
|
|
53
|
+
|
|
54
|
+
def baseline_for(niche: str) -> float:
|
|
55
|
+
rate = repro.resolve_rate(niche)
|
|
56
|
+
return rate if rate is not None else default_baseline
|
|
57
|
+
|
|
58
|
+
promoted = promote_from_corpus(
|
|
59
|
+
corpus,
|
|
60
|
+
library,
|
|
61
|
+
baseline_rate=baseline_for,
|
|
62
|
+
min_observations=min_observations,
|
|
63
|
+
holdout_fraction=holdout_fraction,
|
|
64
|
+
noise_band=noise_band,
|
|
65
|
+
)
|
|
66
|
+
stats = corpus.stats()
|
|
67
|
+
logger.info(
|
|
68
|
+
f"Tool promotion: {len(promoted)} promoted from {stats['tools']} tool(s) / "
|
|
69
|
+
f"{stats['observations']} observation(s); library now {len(library.elites())}."
|
|
70
|
+
)
|
|
71
|
+
return {
|
|
72
|
+
"promoted": promoted,
|
|
73
|
+
"corpus": stats,
|
|
74
|
+
"library_size": len(library.elites()),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main(argv=None) -> int:
|
|
79
|
+
parser = argparse.ArgumentParser(prog="misterdev.core.evolution.tool_promotion")
|
|
80
|
+
parser.add_argument("project_path", help="project whose .orchestrator/ to promote")
|
|
81
|
+
parser.add_argument("--default-baseline", type=float, default=0.5)
|
|
82
|
+
parser.add_argument("--min-observations", type=int, default=5)
|
|
83
|
+
parser.add_argument("--holdout-fraction", type=float, default=0.3)
|
|
84
|
+
parser.add_argument("--noise-band", type=float, default=0.0)
|
|
85
|
+
args = parser.parse_args(argv)
|
|
86
|
+
result = run_tool_promotion(
|
|
87
|
+
args.project_path,
|
|
88
|
+
default_baseline=args.default_baseline,
|
|
89
|
+
min_observations=args.min_observations,
|
|
90
|
+
holdout_fraction=args.holdout_fraction,
|
|
91
|
+
noise_band=args.noise_band,
|
|
92
|
+
)
|
|
93
|
+
sys.stdout.write(
|
|
94
|
+
f"promoted {len(result['promoted'])} tool(s); "
|
|
95
|
+
f"corpus={result['corpus']}; library={result['library_size']}\n"
|
|
96
|
+
)
|
|
97
|
+
return 0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
sys.exit(main())
|