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.
Files changed (330) hide show
  1. {misterdev-0.3.1/misterdev.egg-info → misterdev-0.4.0}/PKG-INFO +3 -3
  2. {misterdev-0.3.1 → misterdev-0.4.0}/README.md +2 -2
  3. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/__init__.py +1 -1
  4. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/config.py +10 -0
  5. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/__init__.py +11 -0
  6. misterdev-0.4.0/misterdev/core/evolution/tool_corpus.py +199 -0
  7. misterdev-0.4.0/misterdev/core/evolution/tool_invention.py +140 -0
  8. misterdev-0.4.0/misterdev/core/evolution/tool_promotion.py +101 -0
  9. misterdev-0.4.0/misterdev/core/evolution/tool_runner.py +167 -0
  10. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/reproduction.py +21 -0
  11. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/context_mixin.py +56 -0
  12. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/execute_mixin.py +32 -4
  13. misterdev-0.4.0/misterdev/task_executors/markdown_plan_executor/results_mixin.py +49 -0
  14. {misterdev-0.3.1 → misterdev-0.4.0/misterdev.egg-info}/PKG-INFO +3 -3
  15. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/SOURCES.txt +11 -0
  16. misterdev-0.4.0/tests/test_no_output_forgiveness.py +87 -0
  17. misterdev-0.4.0/tests/test_runtime_tooling_wiring.py +74 -0
  18. misterdev-0.4.0/tests/test_tool_capture_wiring.py +92 -0
  19. misterdev-0.4.0/tests/test_tool_corpus.py +79 -0
  20. misterdev-0.4.0/tests/test_tool_invention.py +119 -0
  21. misterdev-0.4.0/tests/test_tool_promotion.py +77 -0
  22. misterdev-0.4.0/tests/test_tool_runner.py +133 -0
  23. misterdev-0.3.1/misterdev/task_executors/markdown_plan_executor/results_mixin.py +0 -23
  24. {misterdev-0.3.1 → misterdev-0.4.0}/COMMERCIAL_LICENSE.md +0 -0
  25. {misterdev-0.3.1 → misterdev-0.4.0}/LICENSE +0 -0
  26. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/agent.py +0 -0
  27. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/agent_helpers.py +0 -0
  28. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/__init__.py +0 -0
  29. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/__init__.py +0 -0
  30. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/detection.py +0 -0
  31. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/merge.py +0 -0
  32. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/overview.py +0 -0
  33. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/analyzers/project_analyzer/prompts.py +0 -0
  34. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/cli.py +0 -0
  35. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/__init__.py +0 -0
  36. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/audit.py +0 -0
  37. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/config.py +0 -0
  38. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/__init__.py +0 -0
  39. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/change_tracker.py +0 -0
  40. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/__init__.py +0 -0
  41. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/_log.py +0 -0
  42. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/_text.py +0 -0
  43. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/c_tree_sitter.py +0 -0
  44. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/cpp_tree_sitter.py +0 -0
  45. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/csharp_tree_sitter.py +0 -0
  46. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/extraction.py +0 -0
  47. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/javascript_tree_sitter.py +0 -0
  48. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/kotlin_tree_sitter.py +0 -0
  49. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/python_generic.py +0 -0
  50. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/registry.py +0 -0
  51. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/rust_line.py +0 -0
  52. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/rust_tree_sitter.py +0 -0
  53. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/swift_tree_sitter.py +0 -0
  54. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/contracts/typescript_tree_sitter.py +0 -0
  55. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/__init__.py +0 -0
  56. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/_rules.py +0 -0
  57. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/cpp.py +0 -0
  58. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/csharp.py +0 -0
  59. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/css.py +0 -0
  60. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/elixir.py +0 -0
  61. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/html.py +0 -0
  62. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/kotlin.py +0 -0
  63. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/python.py +0 -0
  64. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/react.py +0 -0
  65. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/rust.py +0 -0
  66. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/swift.py +0 -0
  67. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/guidance/typescript.py +0 -0
  68. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/lsp.py +0 -0
  69. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/lsp_swift.py +0 -0
  70. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/scratchpad.py +0 -0
  71. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/__init__.py +0 -0
  72. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/_log.py +0 -0
  73. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/cache.py +0 -0
  74. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/engine.py +0 -0
  75. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/graph.py +0 -0
  76. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/nodes.py +0 -0
  77. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/parsers.py +0 -0
  78. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/context/topography/syntax.py +0 -0
  79. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/__init__.py +0 -0
  80. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/context_budget.py +0 -0
  81. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/embeddings.py +0 -0
  82. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/free_models.py +0 -0
  83. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/llm_cache.py +0 -0
  84. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_catalog.py +0 -0
  85. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_ledger.py +0 -0
  86. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/economics/model_selector.py +0 -0
  87. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/__main__.py +0 -0
  88. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/adapters.py +0 -0
  89. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/archive.py +0 -0
  90. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/attribution.py +0 -0
  91. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/driver.py +0 -0
  92. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/fitness.py +0 -0
  93. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/guardrail.py +0 -0
  94. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/holdout.py +0 -0
  95. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/loop.py +0 -0
  96. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/prior.py +0 -0
  97. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/proposer.py +0 -0
  98. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/sandbox.py +0 -0
  99. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/screen.py +0 -0
  100. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/evolution/tool_library.py +0 -0
  101. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/__init__.py +0 -0
  102. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/bounded.py +0 -0
  103. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/compile_view.py +0 -0
  104. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/container.py +0 -0
  105. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_classifier.py +0 -0
  106. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_log_compressor.py +0 -0
  107. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/error_resolver.py +0 -0
  108. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/failure_view.py +0 -0
  109. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/governance.py +0 -0
  110. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/outcomes.py +0 -0
  111. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/probe.py +0 -0
  112. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/progress.py +0 -0
  113. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/project.py +0 -0
  114. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/registry.py +0 -0
  115. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/execution/runtime.py +0 -0
  116. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/gitcmd.py +0 -0
  117. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/__init__.py +0 -0
  118. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/mcp.py +0 -0
  119. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/integration/mcp_gather.py +0 -0
  120. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/__init__.py +0 -0
  121. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/failure_log.py +0 -0
  122. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/failure_taxonomy.py +0 -0
  123. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/targeting.py +0 -0
  124. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/learning/warm_start.py +0 -0
  125. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/models.py +0 -0
  126. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/modes.py +0 -0
  127. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/__init__.py +0 -0
  128. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/advisor.py +0 -0
  129. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/assessment.py +0 -0
  130. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/decomposer.py +0 -0
  131. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/lesson_store.py +0 -0
  132. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/metacognition.py +0 -0
  133. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/sovereign.py +0 -0
  134. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/targets.py +0 -0
  135. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/planning/verifier_decomposition.py +0 -0
  136. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/__init__.py +0 -0
  137. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/report.py +0 -0
  138. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/reporting/report_view.py +0 -0
  139. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/task.py +0 -0
  140. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/__init__.py +0 -0
  141. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/claim_verifier.py +0 -0
  142. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/critic.py +0 -0
  143. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/__init__.py +0 -0
  144. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/constants.py +0 -0
  145. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/gatekeeper/helpers.py +0 -0
  146. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/goal_check.py +0 -0
  147. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/independent.py +0 -0
  148. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/mutation_gate.py +0 -0
  149. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/preflight.py +0 -0
  150. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/reflection.py +0 -0
  151. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/spec_tests.py +0 -0
  152. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/validator.py +0 -0
  153. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/vision_verify.py +0 -0
  154. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/core/verification/web_verify.py +0 -0
  155. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/__init__.py +0 -0
  156. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/base_env.py +0 -0
  157. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/container_env.py +0 -0
  158. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/environments/venv_env.py +0 -0
  159. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/__init__.py +0 -0
  160. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/__init__.py +0 -0
  161. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/base.py +0 -0
  162. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/edits.py +0 -0
  163. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/embeddings.py +0 -0
  164. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/errors.py +0 -0
  165. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/providers.py +0 -0
  166. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/client/response.py +0 -0
  167. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/prompt_manager.py +0 -0
  168. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/__init__.py +0 -0
  169. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/apply.py +0 -0
  170. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/json_extract.py +0 -0
  171. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/models.py +0 -0
  172. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/llm/responses/parsing.py +0 -0
  173. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/logging_setup.py +0 -0
  174. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/mcp_server.py +0 -0
  175. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/nl_cli.py +0 -0
  176. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/plugins.py +0 -0
  177. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/py.typed +0 -0
  178. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/__init__.py +0 -0
  179. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/base_executor.py +0 -0
  180. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/__init__.py +0 -0
  181. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/commands_mixin.py +0 -0
  182. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/critic_spec_mixin.py +0 -0
  183. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/edits_mixin.py +0 -0
  184. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/gates_mixin.py +0 -0
  185. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/git_mixin.py +0 -0
  186. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/helpers.py +0 -0
  187. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/task_executors/markdown_plan_executor/llm_mixin.py +0 -0
  188. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/__init__.py +0 -0
  189. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/base_tool.py +0 -0
  190. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/command.py +0 -0
  191. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/dependency.py +0 -0
  192. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/file_io.py +0 -0
  193. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/formatter.py +0 -0
  194. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/tools/git_tool.py +0 -0
  195. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/__init__.py +0 -0
  196. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/file_utils.py +0 -0
  197. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev/utils/process.py +0 -0
  198. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/dependency_links.txt +0 -0
  199. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/entry_points.txt +0 -0
  200. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/requires.txt +0 -0
  201. {misterdev-0.3.1 → misterdev-0.4.0}/misterdev.egg-info/top_level.txt +0 -0
  202. {misterdev-0.3.1 → misterdev-0.4.0}/pyproject.toml +0 -0
  203. {misterdev-0.3.1 → misterdev-0.4.0}/setup.cfg +0 -0
  204. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_advisor.py +0 -0
  205. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_agent_helpers.py +0 -0
  206. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_assessment.py +0 -0
  207. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_audit.py +0 -0
  208. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_audit_gate.py +0 -0
  209. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_bounded.py +0 -0
  210. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_budget_halt.py +0 -0
  211. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_change_tracker.py +0 -0
  212. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_claim_verifier.py +0 -0
  213. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_command_tool.py +0 -0
  214. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_compile_view.py +0 -0
  215. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_config.py +0 -0
  216. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_container.py +0 -0
  217. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_context_budget.py +0 -0
  218. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts.py +0 -0
  219. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_c.py +0 -0
  220. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_cpp.py +0 -0
  221. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_csharp_ts.py +0 -0
  222. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_javascript.py +0 -0
  223. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_kotlin.py +0 -0
  224. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_swift.py +0 -0
  225. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_contracts_typescript.py +0 -0
  226. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_critic.py +0 -0
  227. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_decomposer.py +0 -0
  228. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_dependency_tool.py +0 -0
  229. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_detect_lint_deps.py +0 -0
  230. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_dynamic_model_integration.py +0 -0
  231. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_embeddings.py +0 -0
  232. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_classifier.py +0 -0
  233. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_log_compressor.py +0 -0
  234. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_error_resolver.py +0 -0
  235. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_adapters.py +0 -0
  236. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_archive.py +0 -0
  237. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_attribution.py +0 -0
  238. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_driver.py +0 -0
  239. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_fitness.py +0 -0
  240. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_guardrail.py +0 -0
  241. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_holdout.py +0 -0
  242. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_holdout_wiring.py +0 -0
  243. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_loop.py +0 -0
  244. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_prior.py +0 -0
  245. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_proposer.py +0 -0
  246. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_sandbox.py +0 -0
  247. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_evolution_screen.py +0 -0
  248. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_executor.py +0 -0
  249. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_executor_git.py +0 -0
  250. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_extensibility.py +0 -0
  251. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_log.py +0 -0
  252. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_targeting.py +0 -0
  253. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_taxonomy.py +0 -0
  254. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_failure_view.py +0 -0
  255. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_file_utils.py +0 -0
  256. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_free_models.py +0 -0
  257. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gatekeeper.py +0 -0
  258. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gates_error_context.py +0 -0
  259. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_gitcmd.py +0 -0
  260. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_goal_check.py +0 -0
  261. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_goal_check_wiring.py +0 -0
  262. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_governance.py +0 -0
  263. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_grounded_spec.py +0 -0
  264. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance.py +0 -0
  265. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_cpp.py +0 -0
  266. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_csharp.py +0 -0
  267. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_css.py +0 -0
  268. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_elixir.py +0 -0
  269. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_html.py +0 -0
  270. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_kotlin.py +0 -0
  271. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_python.py +0 -0
  272. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_react.py +0 -0
  273. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_rust.py +0 -0
  274. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_swift.py +0 -0
  275. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_guidance_typescript.py +0 -0
  276. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_hms_integration.py +0 -0
  277. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_independent.py +0 -0
  278. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_integration_gate_count.py +0 -0
  279. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_learning_semantic.py +0 -0
  280. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lesson_efficacy.py +0 -0
  281. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lesson_store.py +0 -0
  282. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_llm_cache.py +0 -0
  283. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_llm_client.py +0 -0
  284. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp.py +0 -0
  285. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_context.py +0 -0
  286. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_integration.py +0 -0
  287. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_lsp_swift.py +0 -0
  288. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp.py +0 -0
  289. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp_remote.py +0 -0
  290. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mcp_server.py +0 -0
  291. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_metacognition.py +0 -0
  292. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_catalog.py +0 -0
  293. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_ledger.py +0 -0
  294. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_model_selector.py +0 -0
  295. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_modes.py +0 -0
  296. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_mutation_gate.py +0 -0
  297. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_native_harness.py +0 -0
  298. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_nl_cli.py +0 -0
  299. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_orchestrator_config.py +0 -0
  300. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_orchestrator_fixes.py +0 -0
  301. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_outcomes.py +0 -0
  302. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_plugins.py +0 -0
  303. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_polyglot_harness.py +0 -0
  304. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_preflight.py +0 -0
  305. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_probe.py +0 -0
  306. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_process.py +0 -0
  307. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_progress.py +0 -0
  308. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_prompt_manager.py +0 -0
  309. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_reflection.py +0 -0
  310. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_registry.py +0 -0
  311. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_report.py +0 -0
  312. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_report_view.py +0 -0
  313. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_reproduction_corpus.py +0 -0
  314. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_responses.py +0 -0
  315. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_runtime.py +0 -0
  316. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_scratchpad.py +0 -0
  317. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_security.py +0 -0
  318. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_sovereign.py +0 -0
  319. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_spec_tests.py +0 -0
  320. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_swebench_harness.py +0 -0
  321. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_targets.py +0 -0
  322. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_task_manager.py +0 -0
  323. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_tool_library.py +0 -0
  324. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_topography.py +0 -0
  325. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_topography_cache.py +0 -0
  326. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_validator.py +0 -0
  327. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_verifier_decomposition.py +0 -0
  328. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_vision_verify.py +0 -0
  329. {misterdev-0.3.1 → misterdev-0.4.0}/tests/test_web_verify.py +0 -0
  330. {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.1
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** *(design; see [docs/two-timescale-evolution.md](docs/two-timescale-evolution.md))* takes the self-improvement further than a memoryless runtime agent can: the agent invents task-specific tools at runtime, and the ones that **prove they generalize** are consolidatedthrough the same held-out promotion gate into a **persistent, best-per-capability tool library that future runs start from**. Fast loop explores; slow loop keeps the winners; the held-out gate keeps the library general rather than benchmark-overfit. Capability compounds across runs instead of being reinvented each task.
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,897 passing** — **[docs/TESTING.md](docs/TESTING.md)**.
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** *(design; see [docs/two-timescale-evolution.md](docs/two-timescale-evolution.md))* takes the self-improvement further than a memoryless runtime agent can: the agent invents task-specific tools at runtime, and the ones that **prove they generalize** are consolidatedthrough the same held-out promotion gate into a **persistent, best-per-capability tool library that future runs start from**. Fast loop explores; slow loop keeps the winners; the held-out gate keeps the library general rather than benchmark-overfit. Capability compounds across runs instead of being reinvented each task.
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,897 passing** — **[docs/TESTING.md](docs/TESTING.md)**.
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
 
@@ -1,3 +1,3 @@
1
1
  """misterdev — an autonomous, extensible LLM build orchestrator."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.4.0"
@@ -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())