code-muse 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_muse/__init__.py +26 -0
- code_muse/__main__.py +10 -0
- code_muse/agents/__init__.py +31 -0
- code_muse/agents/_builder.py +214 -0
- code_muse/agents/_compaction.py +506 -0
- code_muse/agents/_diagnostics.py +171 -0
- code_muse/agents/_history.py +382 -0
- code_muse/agents/_key_listeners.py +148 -0
- code_muse/agents/_non_streaming_render.py +148 -0
- code_muse/agents/_runtime.py +596 -0
- code_muse/agents/agent_creator_agent.py +603 -0
- code_muse/agents/agent_helios.py +47 -0
- code_muse/agents/agent_manager.py +740 -0
- code_muse/agents/agent_muse.py +78 -0
- code_muse/agents/agent_planning.py +44 -0
- code_muse/agents/agent_qa_melpomene.py +207 -0
- code_muse/agents/base_agent.py +194 -0
- code_muse/agents/event_stream_handler.py +361 -0
- code_muse/agents/json_agent.py +201 -0
- code_muse/agents/prompt_v3.py +521 -0
- code_muse/agents/subagent_stream_handler.py +273 -0
- code_muse/callbacks.py +941 -0
- code_muse/chatgpt_codex_client.py +333 -0
- code_muse/claude_cache_client.py +853 -0
- code_muse/cli_runner/__init__.py +319 -0
- code_muse/cli_runner/args.py +63 -0
- code_muse/cli_runner/loop.py +510 -0
- code_muse/cli_runner/resume.py +72 -0
- code_muse/cli_runner/runner.py +161 -0
- code_muse/command_line/__init__.py +1 -0
- code_muse/command_line/add_model_menu.py +1331 -0
- code_muse/command_line/agent_menu.py +674 -0
- code_muse/command_line/attachments.py +397 -0
- code_muse/command_line/autosave_menu.py +709 -0
- code_muse/command_line/clipboard.py +528 -0
- code_muse/command_line/colors_menu.py +530 -0
- code_muse/command_line/command_handler.py +262 -0
- code_muse/command_line/command_registry.py +150 -0
- code_muse/command_line/config_commands.py +711 -0
- code_muse/command_line/core_commands.py +740 -0
- code_muse/command_line/diff_menu.py +865 -0
- code_muse/command_line/file_path_completion.py +73 -0
- code_muse/command_line/load_context_completion.py +57 -0
- code_muse/command_line/model_picker_completion.py +512 -0
- code_muse/command_line/model_settings_menu.py +983 -0
- code_muse/command_line/onboarding_slides.py +162 -0
- code_muse/command_line/onboarding_wizard.py +337 -0
- code_muse/command_line/pagination.py +41 -0
- code_muse/command_line/pin_command_completion.py +329 -0
- code_muse/command_line/prompt_toolkit_completion.py +886 -0
- code_muse/command_line/session_commands.py +304 -0
- code_muse/command_line/shell_passthrough.py +145 -0
- code_muse/command_line/skills_completion.py +158 -0
- code_muse/command_line/types.py +18 -0
- code_muse/command_line/uc_menu.py +908 -0
- code_muse/command_line/utils.py +105 -0
- code_muse/command_line/wiggum_state.py +77 -0
- code_muse/config.py +1138 -0
- code_muse/config_agent.py +168 -0
- code_muse/config_appearance.py +241 -0
- code_muse/config_model.py +357 -0
- code_muse/config_security.py +73 -0
- code_muse/error_logging.py +132 -0
- code_muse/evals/__init__.py +35 -0
- code_muse/evals/eval_helpers.py +81 -0
- code_muse/evals/eval_runner.py +299 -0
- code_muse/evals/sample_evals/__init__.py +1 -0
- code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
- code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
- code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
- code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
- code_muse/fs_scan_cache/__init__.py +31 -0
- code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
- code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
- code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
- code_muse/fs_scan_cache/tool_integration.py +309 -0
- code_muse/fs_scan_cache/ttl_policy.py +44 -0
- code_muse/gemini_code_assist.py +383 -0
- code_muse/gemini_model.py +838 -0
- code_muse/hook_engine/README.md +105 -0
- code_muse/hook_engine/__init__.py +21 -0
- code_muse/hook_engine/aliases.py +153 -0
- code_muse/hook_engine/engine.py +221 -0
- code_muse/hook_engine/executor.py +347 -0
- code_muse/hook_engine/matcher.py +154 -0
- code_muse/hook_engine/models.py +245 -0
- code_muse/hook_engine/registry.py +114 -0
- code_muse/hook_engine/trust.py +268 -0
- code_muse/hook_engine/validator.py +144 -0
- code_muse/http_utils.py +360 -0
- code_muse/keymap.py +128 -0
- code_muse/list_filtering.py +26 -0
- code_muse/main.py +10 -0
- code_muse/messaging/__init__.py +259 -0
- code_muse/messaging/bus.py +621 -0
- code_muse/messaging/commands.py +166 -0
- code_muse/messaging/markdown_patches.py +57 -0
- code_muse/messaging/message_queue.py +397 -0
- code_muse/messaging/messages.py +591 -0
- code_muse/messaging/queue_console.py +269 -0
- code_muse/messaging/renderers.py +308 -0
- code_muse/messaging/rich_renderer.py +1158 -0
- code_muse/messaging/shimmer.py +154 -0
- code_muse/messaging/spinner/__init__.py +87 -0
- code_muse/messaging/spinner/console_spinner.py +250 -0
- code_muse/messaging/spinner/spinner_base.py +82 -0
- code_muse/messaging/subagent_console.py +458 -0
- code_muse/model_factory.py +1203 -0
- code_muse/model_switching.py +59 -0
- code_muse/model_utils.py +156 -0
- code_muse/models.json +66 -0
- code_muse/models_cache/__init__.py +26 -0
- code_muse/models_cache/blocking_lru_cache.py +98 -0
- code_muse/models_cache/cache_writer.py +86 -0
- code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
- code_muse/models_cache/sha256_hash.pyx +34 -0
- code_muse/models_cache/startup_integration.py +75 -0
- code_muse/models_dev_api.json +1 -0
- code_muse/models_dev_parser.py +590 -0
- code_muse/motion.py +126 -0
- code_muse/plugins/__init__.py +471 -0
- code_muse/plugins/agent_skills/__init__.py +32 -0
- code_muse/plugins/agent_skills/config.py +176 -0
- code_muse/plugins/agent_skills/discovery.py +309 -0
- code_muse/plugins/agent_skills/downloader.py +389 -0
- code_muse/plugins/agent_skills/installer.py +19 -0
- code_muse/plugins/agent_skills/metadata.py +293 -0
- code_muse/plugins/agent_skills/prompt_builder.py +66 -0
- code_muse/plugins/agent_skills/register_callbacks.py +298 -0
- code_muse/plugins/agent_skills/remote_catalog.py +320 -0
- code_muse/plugins/agent_skills/skill_catalog.py +254 -0
- code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
- code_muse/plugins/agent_skills/skills_menu.py +791 -0
- code_muse/plugins/autonomous_memory/__init__.py +39 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
- code_muse/plugins/autonomous_memory/consolidation.py +82 -0
- code_muse/plugins/autonomous_memory/extraction.py +382 -0
- code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
- code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
- code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
- code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
- code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
- code_muse/plugins/aws_bedrock/__init__.py +14 -0
- code_muse/plugins/aws_bedrock/config.py +99 -0
- code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
- code_muse/plugins/aws_bedrock/utils.py +153 -0
- code_muse/plugins/azure_foundry/README.md +238 -0
- code_muse/plugins/azure_foundry/__init__.py +15 -0
- code_muse/plugins/azure_foundry/config.py +125 -0
- code_muse/plugins/azure_foundry/discovery.py +187 -0
- code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
- code_muse/plugins/azure_foundry/token.py +180 -0
- code_muse/plugins/azure_foundry/utils.py +345 -0
- code_muse/plugins/build_filter/__init__.py +1 -0
- code_muse/plugins/build_filter/register_callbacks.py +201 -0
- code_muse/plugins/build_filter/strategies/__init__.py +1 -0
- code_muse/plugins/build_filter/strategies/build.py +397 -0
- code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
- code_muse/plugins/chatgpt_oauth/config.py +52 -0
- code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
- code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
- code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_muse/plugins/chatgpt_oauth/utils.py +538 -0
- code_muse/plugins/checkpointing/__init__.py +29 -0
- code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
- code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
- code_muse/plugins/checkpointing/register_callbacks.py +51 -0
- code_muse/plugins/checkpointing/restore_command.py +263 -0
- code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
- code_muse/plugins/checkpointing/shadow_git.py +90 -0
- code_muse/plugins/claude_code_hooks/__init__.py +1 -0
- code_muse/plugins/claude_code_hooks/config.py +188 -0
- code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
- code_muse/plugins/claude_code_oauth/README.md +167 -0
- code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
- code_muse/plugins/claude_code_oauth/__init__.py +25 -0
- code_muse/plugins/claude_code_oauth/config.py +52 -0
- code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
- code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
- code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
- code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
- code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
- code_muse/plugins/claude_code_oauth/utils.py +664 -0
- code_muse/plugins/copilot_auth/__init__.py +11 -0
- code_muse/plugins/copilot_auth/config.py +91 -0
- code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
- code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
- code_muse/plugins/copilot_auth/utils.py +584 -0
- code_muse/plugins/custom_commands/__init__.py +14 -0
- code_muse/plugins/custom_commands/args_injection.py +82 -0
- code_muse/plugins/custom_commands/command_discovery.py +89 -0
- code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
- code_muse/plugins/custom_commands/register_callbacks.py +176 -0
- code_muse/plugins/customizable_commands/__init__.py +0 -0
- code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
- code_muse/plugins/destructive_command_guard/__init__.py +14 -0
- code_muse/plugins/destructive_command_guard/detector.py +375 -0
- code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
- code_muse/plugins/example_custom_command/README.md +280 -0
- code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
- code_muse/plugins/file_permission_handler/__init__.py +4 -0
- code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
- code_muse/plugins/filter_engine/__init__.py +30 -0
- code_muse/plugins/filter_engine/classifier.py +153 -0
- code_muse/plugins/filter_engine/content_detector.py +184 -0
- code_muse/plugins/filter_engine/dispatcher.py +244 -0
- code_muse/plugins/filter_engine/register_callbacks.py +188 -0
- code_muse/plugins/filter_engine/registry.py +279 -0
- code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
- code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
- code_muse/plugins/filter_engine/verbosity.py +63 -0
- code_muse/plugins/force_push_guard/__init__.py +5 -0
- code_muse/plugins/force_push_guard/detector.py +96 -0
- code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
- code_muse/plugins/force_push_guard/test_detector.py +143 -0
- code_muse/plugins/frontend_emitter/__init__.py +25 -0
- code_muse/plugins/frontend_emitter/emitter.py +121 -0
- code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
- code_muse/plugins/gac/__init__.py +4 -0
- code_muse/plugins/gac/git_ops.py +136 -0
- code_muse/plugins/gac/prompt.py +191 -0
- code_muse/plugins/gac/register_callbacks.py +82 -0
- code_muse/plugins/hook_creator/__init__.py +1 -0
- code_muse/plugins/hook_creator/register_callbacks.py +34 -0
- code_muse/plugins/hook_manager/__init__.py +1 -0
- code_muse/plugins/hook_manager/config.py +289 -0
- code_muse/plugins/hook_manager/hooks_menu.py +563 -0
- code_muse/plugins/hook_manager/register_callbacks.py +227 -0
- code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
- code_muse/plugins/mindpack/__init__.py +0 -0
- code_muse/plugins/mindpack/factory.py +930 -0
- code_muse/plugins/mindpack/judge.py +573 -0
- code_muse/plugins/mindpack/memory.py +100 -0
- code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
- code_muse/plugins/mindpack/orchestration.py +605 -0
- code_muse/plugins/mindpack/register_callbacks.py +175 -0
- code_muse/plugins/mindpack/schemas.py +358 -0
- code_muse/plugins/mindpack/tools.py +387 -0
- code_muse/plugins/oauth_muse_html.py +226 -0
- code_muse/plugins/ollama_setup/__init__.py +5 -0
- code_muse/plugins/ollama_setup/completer.py +36 -0
- code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
- code_muse/plugins/plan_command/__init__.py +0 -0
- code_muse/plugins/plan_command/register_callbacks.py +206 -0
- code_muse/plugins/plan_mode/__init__.py +37 -0
- code_muse/plugins/plan_mode/mode_cycling.py +40 -0
- code_muse/plugins/plan_mode/plan_generation.py +68 -0
- code_muse/plugins/plan_mode/plan_hooks.py +74 -0
- code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
- code_muse/plugins/plan_mode/register_callbacks.py +121 -0
- code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
- code_muse/plugins/policy_engine/__init__.py +46 -0
- code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
- code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
- code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
- code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
- code_muse/plugins/policy_engine/register_callbacks.py +112 -0
- code_muse/plugins/pop_command/__init__.py +1 -0
- code_muse/plugins/pop_command/register_callbacks.py +189 -0
- code_muse/plugins/prompt_newline/__init__.py +13 -0
- code_muse/plugins/prompt_newline/config.py +19 -0
- code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
- code_muse/plugins/safety_status/__init__.py +0 -0
- code_muse/plugins/safety_status/register_callbacks.py +113 -0
- code_muse/plugins/semantic_compression/__init__.py +6 -0
- code_muse/plugins/semantic_compression/compressor.py +295 -0
- code_muse/plugins/semantic_compression/config.py +123 -0
- code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
- code_muse/plugins/shell_minimizer/__init__.py +50 -0
- code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
- code_muse/plugins/shell_minimizer/pipeline.py +556 -0
- code_muse/plugins/shell_minimizer/primitives.py +482 -0
- code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
- code_muse/plugins/shell_safety/__init__.py +6 -0
- code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_muse/plugins/shell_safety/command_cache.py +149 -0
- code_muse/plugins/shell_safety/register_callbacks.py +202 -0
- code_muse/plugins/synthetic_status/__init__.py +1 -0
- code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
- code_muse/plugins/synthetic_status/status_api.py +145 -0
- code_muse/plugins/token_caching/__init__.py +21 -0
- code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
- code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
- code_muse/plugins/token_caching/register_callbacks.py +54 -0
- code_muse/plugins/token_caching/stats_display.py +35 -0
- code_muse/plugins/token_tracking/__init__.py +26 -0
- code_muse/plugins/token_tracking/database.py +381 -0
- code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
- code_muse/plugins/token_tracking/record.py +55 -0
- code_muse/plugins/token_tracking/register_callbacks.py +277 -0
- code_muse/plugins/token_tracking/reports.py +329 -0
- code_muse/plugins/universal_constructor/__init__.py +13 -0
- code_muse/plugins/universal_constructor/models.py +136 -0
- code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
- code_muse/plugins/universal_constructor/registry.py +390 -0
- code_muse/plugins/universal_constructor/runner.py +474 -0
- code_muse/plugins/universal_constructor/safety.py +440 -0
- code_muse/plugins/universal_constructor/sandbox.py +584 -0
- code_muse/provider_identity.py +105 -0
- code_muse/pydantic_patches.py +410 -0
- code_muse/reopenable_async_client.py +233 -0
- code_muse/round_robin_model.py +151 -0
- code_muse/secret_storage.py +74 -0
- code_muse/security/__init__.py +1 -0
- code_muse/security/redaction.cpython-314-darwin.so +0 -0
- code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/security/redaction.pyx +135 -0
- code_muse/session_storage.py +565 -0
- code_muse/status_display.py +261 -0
- code_muse/stream_parser/__init__.py +76 -0
- code_muse/stream_parser/assistant_text_parser.py +90 -0
- code_muse/stream_parser/citation_parser.py +76 -0
- code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
- code_muse/stream_parser/proposed_plan_parser.py +158 -0
- code_muse/stream_parser/stream_text_chunk.py +23 -0
- code_muse/stream_parser/stream_text_parser.py +27 -0
- code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/tagged_line_parser.pyx +251 -0
- code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
- code_muse/summarization_agent.py +308 -0
- code_muse/terminal_utils.cpython-314-darwin.so +0 -0
- code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/terminal_utils.pyx +483 -0
- code_muse/tools/__init__.py +459 -0
- code_muse/tools/agent_tools.py +613 -0
- code_muse/tools/ask_user_question/__init__.py +26 -0
- code_muse/tools/ask_user_question/constants.py +73 -0
- code_muse/tools/ask_user_question/demo_tui.py +55 -0
- code_muse/tools/ask_user_question/handler.py +232 -0
- code_muse/tools/ask_user_question/models.py +302 -0
- code_muse/tools/ask_user_question/registration.py +37 -0
- code_muse/tools/ask_user_question/renderers.py +336 -0
- code_muse/tools/ask_user_question/terminal_ui.py +327 -0
- code_muse/tools/ask_user_question/theme.py +156 -0
- code_muse/tools/ask_user_question/tui_loop.py +422 -0
- code_muse/tools/background_jobs.py +99 -0
- code_muse/tools/browser/__init__.py +37 -0
- code_muse/tools/browser/browser_control.py +289 -0
- code_muse/tools/browser/browser_interactions.py +545 -0
- code_muse/tools/browser/browser_locators.py +640 -0
- code_muse/tools/browser/browser_manager.py +376 -0
- code_muse/tools/browser/browser_navigation.py +251 -0
- code_muse/tools/browser/browser_screenshot.py +180 -0
- code_muse/tools/browser/browser_scripts.py +462 -0
- code_muse/tools/browser/browser_workflows.py +222 -0
- code_muse/tools/chrome_cdp/__init__.py +1070 -0
- code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
- code_muse/tools/command_runner.py +1401 -0
- code_muse/tools/common.py +1407 -0
- code_muse/tools/display.py +87 -0
- code_muse/tools/file_modifications.py +1099 -0
- code_muse/tools/file_operations.py +860 -0
- code_muse/tools/image_tools.py +185 -0
- code_muse/tools/meetin_proxy/__init__.py +243 -0
- code_muse/tools/meetin_proxy/capture_addon.py +82 -0
- code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
- code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
- code_muse/tools/path_policy.py +219 -0
- code_muse/tools/skills_tools.py +586 -0
- code_muse/tools/subagent_context.py +158 -0
- code_muse/tools/tools_content.py +50 -0
- code_muse/tools/universal_constructor.py +965 -0
- code_muse/uvx_detection.py +241 -0
- code_muse/version_checker.py +86 -0
- code_muse-0.0.1.data/data/code_muse/models.json +66 -0
- code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
- code_muse-0.0.1.dist-info/METADATA +845 -0
- code_muse-0.0.1.dist-info/RECORD +394 -0
- code_muse-0.0.1.dist-info/WHEEL +4 -0
- code_muse-0.0.1.dist-info/entry_points.txt +2 -0
- code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
"""MindPack judge — LLM-backed report merging and AskMindPackOutput synthesis.
|
|
2
|
+
|
|
3
|
+
The judge reviews all expert reports, evaluates consensus and disagreement,
|
|
4
|
+
and produces the final unified advisory output.
|
|
5
|
+
|
|
6
|
+
Two components:
|
|
7
|
+
|
|
8
|
+
1. ``JudgeAgentFactory`` — creates a read-only "Judge" sub-agent that
|
|
9
|
+
produces structured ``AskMindPackOutput`` from expert reports.
|
|
10
|
+
2. ``LLMJudgeMerger`` — concrete ``JudgeMerger`` that delegates to the
|
|
11
|
+
factory, with a built-in fallback to placeholder logic on failure.
|
|
12
|
+
|
|
13
|
+
Both the judge agent and the merger degrade gracefully — the executor
|
|
14
|
+
always receives a valid ``AskMindPackOutput``, even if the LLM is down.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import logging
|
|
19
|
+
import uuid
|
|
20
|
+
from contextlib import AsyncExitStack
|
|
21
|
+
from functools import partial
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from pydantic_ai import Agent as PydanticAgent
|
|
25
|
+
from pydantic_ai import UsageLimits
|
|
26
|
+
|
|
27
|
+
from code_muse.plugins.mindpack.orchestration import JudgeMerger
|
|
28
|
+
from code_muse.plugins.mindpack.schemas import (
|
|
29
|
+
AskMindPackInput,
|
|
30
|
+
AskMindPackOutput,
|
|
31
|
+
MindPackRankedOption,
|
|
32
|
+
)
|
|
33
|
+
from code_muse.plugins.mindpack.schemas import (
|
|
34
|
+
MindPackExpertReport as ExpertReport,
|
|
35
|
+
)
|
|
36
|
+
from code_muse.tools.subagent_context import subagent_context
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Read-only tool allow-list (same as experts — judge output is advisory)
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
JUDGE_READ_ONLY_TOOLS: list[str] = [
|
|
45
|
+
"list_files",
|
|
46
|
+
"read_file",
|
|
47
|
+
"grep",
|
|
48
|
+
"load_image_for_analysis",
|
|
49
|
+
"list_or_search_skills",
|
|
50
|
+
]
|
|
51
|
+
"""Tools the judge may use. All write-capable tools are deliberately excluded
|
|
52
|
+
to guarantee the judge never mutates the codebase."""
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Judge system prompt
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
_JUDGE_SYSTEM_PROMPT = """\
|
|
59
|
+
You are Judge, the deliberation synthesizer for the MindPack expert panel.
|
|
60
|
+
|
|
61
|
+
Your role is to review reports from multiple domain experts, identify areas
|
|
62
|
+
of consensus and disagreement, and produce a unified, actionable advisory
|
|
63
|
+
output that the executor can rely on.
|
|
64
|
+
|
|
65
|
+
## CRITICAL CONSTRAINTS
|
|
66
|
+
|
|
67
|
+
You are operating in **read-only advisory mode**. You must NEVER:
|
|
68
|
+
- Create, modify, or delete any files
|
|
69
|
+
- Run shell commands
|
|
70
|
+
- Invoke other agents or sub-agents
|
|
71
|
+
- Ask the user questions
|
|
72
|
+
- Navigate websites or use browser tools
|
|
73
|
+
|
|
74
|
+
You MAY: list/read files, search with grep, load images, list skills.
|
|
75
|
+
|
|
76
|
+
## YOUR TASK
|
|
77
|
+
|
|
78
|
+
You will receive:
|
|
79
|
+
1. The original consultation request (problem, goals, context)
|
|
80
|
+
2. Structured reports from experts, each with analysis, recommendations,
|
|
81
|
+
risks, files, confidence, and disagreements.
|
|
82
|
+
|
|
83
|
+
You must:
|
|
84
|
+
1. **Identify consensus** — What do most or all experts agree on?
|
|
85
|
+
2. **Surface disagreements** — Where do experts diverge, and why?
|
|
86
|
+
3. **Synthesize recommendations** — Merge overlapping ones; resolve conflicts
|
|
87
|
+
by weighing expert confidence and domain relevance; prefer simpler paths.
|
|
88
|
+
4. **Rank options** — Up to 3 ranked alternatives. Rank 1 = most recommended.
|
|
89
|
+
5. **Assess overall confidence** — Weighted average penalised for disagreements.
|
|
90
|
+
6. **Flag risks** — Consolidate, deduplicate, prioritise by severity.
|
|
91
|
+
7. **Recommend tests** — Concrete validation steps from the combined advice.
|
|
92
|
+
|
|
93
|
+
## SYNTHESIS GUIDELINES
|
|
94
|
+
|
|
95
|
+
- When experts agree, state consensus clearly and move on.
|
|
96
|
+
- When experts disagree, present both views in ``disagreements`` and pick
|
|
97
|
+
the position supported by stronger reasoning or higher confidence.
|
|
98
|
+
- Never fabricate information not present in the expert reports.
|
|
99
|
+
- If an expert flagged a disagreement, explain both positions and justify
|
|
100
|
+
the judge's resolution.
|
|
101
|
+
- Overall confidence should decrease when key experts disagree on core points.
|
|
102
|
+
- If only one expert was consulted, still produce a full output — but note
|
|
103
|
+
the lack of cross-validation.
|
|
104
|
+
|
|
105
|
+
## OUTPUT FORMAT
|
|
106
|
+
|
|
107
|
+
You MUST produce a structured ``AskMindPackOutput`` with every field filled.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Prompt builder
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def build_judge_prompt(
|
|
116
|
+
request: AskMindPackInput,
|
|
117
|
+
reports: list[ExpertReport],
|
|
118
|
+
) -> str:
|
|
119
|
+
"""Compose the user-facing prompt for the judge agent.
|
|
120
|
+
|
|
121
|
+
Serialises the original request and expert reports into a structured
|
|
122
|
+
format the judge can evaluate.
|
|
123
|
+
"""
|
|
124
|
+
parts: list[str] = []
|
|
125
|
+
|
|
126
|
+
# --- Original request ---
|
|
127
|
+
parts.append("## ORIGINAL CONSULTATION REQUEST")
|
|
128
|
+
parts.append(f"**Problem Statement:** {request.problem_statement}")
|
|
129
|
+
parts.append(f"**Current Goal:** {request.current_goal}")
|
|
130
|
+
if request.current_plan:
|
|
131
|
+
parts.append(f"**Current Plan:** {request.current_plan}")
|
|
132
|
+
if request.what_has_been_tried:
|
|
133
|
+
parts.append(
|
|
134
|
+
"**What Has Been Tried:**\n"
|
|
135
|
+
+ "\n".join(f"- {item}" for item in request.what_has_been_tried)
|
|
136
|
+
)
|
|
137
|
+
if request.relevant_files:
|
|
138
|
+
parts.append(
|
|
139
|
+
"**Relevant Files:**\n"
|
|
140
|
+
+ "\n".join(f"- {f}" for f in request.relevant_files)
|
|
141
|
+
)
|
|
142
|
+
if request.observed_errors:
|
|
143
|
+
parts.append(
|
|
144
|
+
"**Observed Errors:**\n"
|
|
145
|
+
+ "\n".join(f"- {e}" for e in request.observed_errors)
|
|
146
|
+
)
|
|
147
|
+
if request.uncertainty:
|
|
148
|
+
parts.append(f"**Uncertainty:** {request.uncertainty}")
|
|
149
|
+
parts.append(f"**Desired Output:** {request.desired_output}")
|
|
150
|
+
|
|
151
|
+
# --- Expert reports ---
|
|
152
|
+
parts.append(f"\n## EXPERT REPORTS ({len(reports)} total)")
|
|
153
|
+
for i, report in enumerate(reports, 1):
|
|
154
|
+
parts.append(f"\n### Report {i}: {report.expert_id}")
|
|
155
|
+
parts.append(f"**Confidence:** {report.confidence:.2f}")
|
|
156
|
+
parts.append(f"**Status:** {report.status}")
|
|
157
|
+
parts.append(f"\n**Summary:**\n{report.summary}")
|
|
158
|
+
if report.findings:
|
|
159
|
+
parts.append(
|
|
160
|
+
"**Findings:**\n" + "\n".join(f"- {f}" for f in report.findings)
|
|
161
|
+
)
|
|
162
|
+
if report.proposed_plan:
|
|
163
|
+
parts.append(
|
|
164
|
+
"**Proposed Plan:**\n"
|
|
165
|
+
+ "\n".join(f"- {p}" for p in report.proposed_plan)
|
|
166
|
+
)
|
|
167
|
+
if report.risks:
|
|
168
|
+
parts.append("**Risks:**\n" + "\n".join(f"- {r}" for r in report.risks))
|
|
169
|
+
if report.files_to_inspect:
|
|
170
|
+
parts.append(
|
|
171
|
+
"**Files to Inspect:**\n"
|
|
172
|
+
+ "\n".join(f"- {f}" for f in report.files_to_inspect)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return "\n\n".join(parts)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# JudgeAgentFactory
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class JudgeAgentFactory:
|
|
184
|
+
"""Creates and runs a read-only Judge sub-agent that merges expert reports.
|
|
185
|
+
|
|
186
|
+
Follows the same construction pattern as ``ExpertAgentFactory`` but
|
|
187
|
+
outputs ``AskMindPackOutput`` instead of ``ExpertReport``.
|
|
188
|
+
|
|
189
|
+
Usage::
|
|
190
|
+
|
|
191
|
+
factory = JudgeAgentFactory()
|
|
192
|
+
output = await factory.invoke_judge(request, reports, session_id)
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
def create_judge_agent(
|
|
196
|
+
self,
|
|
197
|
+
*,
|
|
198
|
+
session_id: str,
|
|
199
|
+
message_group: str | None = None,
|
|
200
|
+
) -> PydanticAgent:
|
|
201
|
+
"""Build a pydantic-ai agent for the judge.
|
|
202
|
+
|
|
203
|
+
Configured with the judge system prompt, read-only tools, and
|
|
204
|
+
``AskMindPackOutput`` as the output type. This is a temporary agent
|
|
205
|
+
— not registered in the agent manager, does not persist.
|
|
206
|
+
"""
|
|
207
|
+
from code_muse.agents._builder import load_muse_rules
|
|
208
|
+
from code_muse.model_factory import ModelFactory, make_model_settings
|
|
209
|
+
from code_muse.model_utils import prepare_prompt_for_model
|
|
210
|
+
|
|
211
|
+
# Resolve model
|
|
212
|
+
model_name = self._resolve_model_name()
|
|
213
|
+
models_config = ModelFactory.load_config()
|
|
214
|
+
|
|
215
|
+
if model_name not in models_config:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
f"Model '{model_name}' not found in configuration — "
|
|
218
|
+
"cannot create judge agent"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
model = ModelFactory.get_model(model_name, models_config)
|
|
222
|
+
|
|
223
|
+
# Build instructions
|
|
224
|
+
instructions = _JUDGE_SYSTEM_PROMPT
|
|
225
|
+
|
|
226
|
+
# Append AGENTS.md rules if available
|
|
227
|
+
agent_rules = load_muse_rules()
|
|
228
|
+
if agent_rules:
|
|
229
|
+
instructions += f"\n\n{agent_rules}"
|
|
230
|
+
|
|
231
|
+
# Append plugin prompt additions
|
|
232
|
+
from code_muse import callbacks
|
|
233
|
+
|
|
234
|
+
prompt_additions = callbacks.on_load_prompt()
|
|
235
|
+
if prompt_additions:
|
|
236
|
+
instructions += "\n" + "\n".join(prompt_additions)
|
|
237
|
+
|
|
238
|
+
# Prepare for model (handles claude-code prepending etc.)
|
|
239
|
+
prepared = prepare_prompt_for_model(
|
|
240
|
+
model_name,
|
|
241
|
+
instructions,
|
|
242
|
+
"",
|
|
243
|
+
prepend_system_to_user=False,
|
|
244
|
+
)
|
|
245
|
+
instructions = prepared.instructions
|
|
246
|
+
|
|
247
|
+
model_settings = make_model_settings(model_name)
|
|
248
|
+
|
|
249
|
+
# Build the pydantic-ai agent with AskMindPackOutput output type
|
|
250
|
+
judge_agent = PydanticAgent(
|
|
251
|
+
model=model,
|
|
252
|
+
instructions=instructions,
|
|
253
|
+
output_type=AskMindPackOutput,
|
|
254
|
+
retries=2,
|
|
255
|
+
toolsets=[],
|
|
256
|
+
history_processors=[],
|
|
257
|
+
model_settings=model_settings,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Register read-only tools
|
|
261
|
+
from code_muse.tools import register_tools_for_agent
|
|
262
|
+
|
|
263
|
+
tools = list(JUDGE_READ_ONLY_TOOLS)
|
|
264
|
+
register_tools_for_agent(judge_agent, tools, model_name=model_name)
|
|
265
|
+
|
|
266
|
+
logger.debug(
|
|
267
|
+
"JudgeAgentFactory: created judge agent with tools=%s session=%s",
|
|
268
|
+
tools,
|
|
269
|
+
session_id,
|
|
270
|
+
)
|
|
271
|
+
return judge_agent
|
|
272
|
+
|
|
273
|
+
async def invoke_judge(
|
|
274
|
+
self,
|
|
275
|
+
request: AskMindPackInput,
|
|
276
|
+
reports: list[ExpertReport],
|
|
277
|
+
session_id: str,
|
|
278
|
+
) -> AskMindPackOutput:
|
|
279
|
+
"""Run the judge agent and return the merged advisory output.
|
|
280
|
+
|
|
281
|
+
Falls back to placeholder output if the judge fails entirely.
|
|
282
|
+
"""
|
|
283
|
+
group_id = f"mindpack-judge-{uuid.uuid4().hex[:6]}"
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
judge_agent = self.create_judge_agent(
|
|
287
|
+
session_id=session_id, message_group=group_id
|
|
288
|
+
)
|
|
289
|
+
except ValueError as exc:
|
|
290
|
+
logger.error("JudgeAgentFactory: failed to create judge agent: %s", exc)
|
|
291
|
+
return self._placeholder_output(request, reports)
|
|
292
|
+
|
|
293
|
+
user_prompt = build_judge_prompt(request, reports)
|
|
294
|
+
|
|
295
|
+
return await self._run_judge(
|
|
296
|
+
judge_agent=judge_agent,
|
|
297
|
+
user_prompt=user_prompt,
|
|
298
|
+
session_id=session_id,
|
|
299
|
+
group_id=group_id,
|
|
300
|
+
request=request,
|
|
301
|
+
reports=reports,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# -- internal -----------------------------------------------------------
|
|
305
|
+
|
|
306
|
+
@staticmethod
|
|
307
|
+
def _resolve_model_name(model_name: str | None = None) -> str:
|
|
308
|
+
"""Resolve the model name for the judge agent.
|
|
309
|
+
|
|
310
|
+
Resolution order:
|
|
311
|
+
1. Explicit ``model_name`` override (caller-supplied, e.g. from a
|
|
312
|
+
future registry or schema-driven config).
|
|
313
|
+
2. ``packmind_judge_model`` key in the Muse INI config.
|
|
314
|
+
3. Global model name from ``get_global_model_name()``.
|
|
315
|
+
|
|
316
|
+
Raises ``ValueError`` if no model can be resolved.
|
|
317
|
+
"""
|
|
318
|
+
# 1. Explicit caller override
|
|
319
|
+
if model_name:
|
|
320
|
+
return model_name
|
|
321
|
+
|
|
322
|
+
# 2. Config-file override (packmind_judge_model in muse.cfg)
|
|
323
|
+
from code_muse.config import get_value
|
|
324
|
+
|
|
325
|
+
judge_model = get_value("packmind_judge_model")
|
|
326
|
+
if judge_model:
|
|
327
|
+
return judge_model
|
|
328
|
+
|
|
329
|
+
# 3. Global fallback
|
|
330
|
+
from code_muse.config import get_global_model_name
|
|
331
|
+
|
|
332
|
+
name = get_global_model_name()
|
|
333
|
+
if not name:
|
|
334
|
+
raise ValueError("No global model configured — cannot create judge agent")
|
|
335
|
+
return name
|
|
336
|
+
|
|
337
|
+
async def _run_judge(
|
|
338
|
+
self,
|
|
339
|
+
judge_agent: PydanticAgent,
|
|
340
|
+
user_prompt: str,
|
|
341
|
+
session_id: str,
|
|
342
|
+
group_id: str,
|
|
343
|
+
request: AskMindPackInput,
|
|
344
|
+
reports: list[ExpertReport],
|
|
345
|
+
) -> AskMindPackOutput:
|
|
346
|
+
"""Execute the judge agent in a subagent context."""
|
|
347
|
+
from code_muse.agents.subagent_stream_handler import (
|
|
348
|
+
subagent_stream_handler,
|
|
349
|
+
)
|
|
350
|
+
from code_muse.callbacks import on_agent_run_cancel, on_agent_run_context
|
|
351
|
+
from code_muse.config import get_message_limit
|
|
352
|
+
from code_muse.messaging import (
|
|
353
|
+
SubAgentInvocationMessage,
|
|
354
|
+
SubAgentResponseMessage,
|
|
355
|
+
emit_success,
|
|
356
|
+
get_message_bus,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
bus = get_message_bus()
|
|
360
|
+
|
|
361
|
+
bus.emit(
|
|
362
|
+
SubAgentInvocationMessage(
|
|
363
|
+
agent_name="mindpack-judge",
|
|
364
|
+
session_id=session_id,
|
|
365
|
+
prompt=user_prompt[:200],
|
|
366
|
+
is_new_session=True,
|
|
367
|
+
message_count=0,
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
stream_handler = partial(subagent_stream_handler, session_id=session_id)
|
|
372
|
+
|
|
373
|
+
with subagent_context("mindpack-judge"):
|
|
374
|
+
run_ctxs = on_agent_run_context(
|
|
375
|
+
_JudgeAgentProxy(),
|
|
376
|
+
judge_agent,
|
|
377
|
+
group_id,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
task: asyncio.Task | None = None
|
|
381
|
+
try:
|
|
382
|
+
async with AsyncExitStack() as stack:
|
|
383
|
+
for cm in run_ctxs:
|
|
384
|
+
await stack.enter_async_context(cm)
|
|
385
|
+
|
|
386
|
+
task = asyncio.create_task(
|
|
387
|
+
judge_agent.run(
|
|
388
|
+
user_prompt,
|
|
389
|
+
message_history=[],
|
|
390
|
+
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
391
|
+
event_stream_handler=stream_handler,
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
result = await task
|
|
396
|
+
|
|
397
|
+
except asyncio.CancelledError:
|
|
398
|
+
if task and not task.done():
|
|
399
|
+
task.cancel()
|
|
400
|
+
await on_agent_run_cancel(group_id)
|
|
401
|
+
logger.warning("JudgeAgentFactory: judge cancelled")
|
|
402
|
+
return self._placeholder_output(request, reports)
|
|
403
|
+
|
|
404
|
+
except Exception as exc:
|
|
405
|
+
logger.error(
|
|
406
|
+
"JudgeAgentFactory: judge failed: %s",
|
|
407
|
+
exc,
|
|
408
|
+
exc_info=True,
|
|
409
|
+
)
|
|
410
|
+
return self._placeholder_output(request, reports)
|
|
411
|
+
|
|
412
|
+
# Extract structured output
|
|
413
|
+
output = self._extract_output(result, request, reports)
|
|
414
|
+
|
|
415
|
+
# Emit completion message
|
|
416
|
+
bus.emit(
|
|
417
|
+
SubAgentResponseMessage(
|
|
418
|
+
agent_name="mindpack-judge",
|
|
419
|
+
session_id=session_id,
|
|
420
|
+
response=output.summary[:200] if output else "",
|
|
421
|
+
message_count=0,
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
emit_success(
|
|
426
|
+
"✓ mindpack-judge completed",
|
|
427
|
+
message_group=group_id,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
return output
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def _extract_output(
|
|
434
|
+
result: Any,
|
|
435
|
+
request: AskMindPackInput,
|
|
436
|
+
reports: list[ExpertReport],
|
|
437
|
+
) -> AskMindPackOutput:
|
|
438
|
+
"""Extract ``AskMindPackOutput`` from the pydantic-ai result.
|
|
439
|
+
|
|
440
|
+
Tries structured output, then dict coercion, then placeholder.
|
|
441
|
+
"""
|
|
442
|
+
if result is not None and hasattr(result, "output"):
|
|
443
|
+
output = result.output
|
|
444
|
+
if isinstance(output, AskMindPackOutput):
|
|
445
|
+
return output
|
|
446
|
+
|
|
447
|
+
# Dict path — model returned raw dict instead of model instance
|
|
448
|
+
if isinstance(output, dict):
|
|
449
|
+
try:
|
|
450
|
+
return AskMindPackOutput(
|
|
451
|
+
**{
|
|
452
|
+
k: v
|
|
453
|
+
for k, v in output.items()
|
|
454
|
+
if k in AskMindPackOutput.model_fields
|
|
455
|
+
}
|
|
456
|
+
)
|
|
457
|
+
except Exception:
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
# Final fallback
|
|
461
|
+
return JudgeAgentFactory._placeholder_output(request, reports)
|
|
462
|
+
|
|
463
|
+
@staticmethod
|
|
464
|
+
def _placeholder_output(
|
|
465
|
+
request: AskMindPackInput,
|
|
466
|
+
reports: list[ExpertReport],
|
|
467
|
+
) -> AskMindPackOutput:
|
|
468
|
+
"""Build a placeholder output when the judge cannot produce one.
|
|
469
|
+
|
|
470
|
+
Mirrors ``PlaceholderJudgeMerger`` for graceful degradation.
|
|
471
|
+
"""
|
|
472
|
+
expert_names = [r.expert_id for r in reports]
|
|
473
|
+
all_risks = [r for report in reports for r in report.risks]
|
|
474
|
+
all_files = [f for report in reports for f in report.files_to_inspect]
|
|
475
|
+
all_recs = [r for report in reports for r in report.proposed_plan]
|
|
476
|
+
|
|
477
|
+
return AskMindPackOutput(
|
|
478
|
+
summary=(
|
|
479
|
+
f"Judge fallback: consulted {len(reports)} expert(s) "
|
|
480
|
+
f"for '{request.desired_output}' on: "
|
|
481
|
+
f"{request.problem_statement[:200]}"
|
|
482
|
+
),
|
|
483
|
+
recommended_plan=("\n".join(all_recs) or "No recommendations produced."),
|
|
484
|
+
ranked_options=[
|
|
485
|
+
MindPackRankedOption(
|
|
486
|
+
rank=1,
|
|
487
|
+
title="Fallback option",
|
|
488
|
+
source_experts=expert_names,
|
|
489
|
+
summary=("Judge LLM failed — merged option from raw expert data."),
|
|
490
|
+
)
|
|
491
|
+
],
|
|
492
|
+
risks=all_risks or ["No risks identified (judge fallback)."],
|
|
493
|
+
tests_to_run=[],
|
|
494
|
+
files_to_inspect_or_change=list(dict.fromkeys(all_files)),
|
|
495
|
+
expert_consensus=(f"Fallback: {len(reports)} expert(s) consulted."),
|
|
496
|
+
disagreements=[],
|
|
497
|
+
confidence=(sum(r.confidence for r in reports) / max(len(reports), 1)),
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
# ---------------------------------------------------------------------------
|
|
502
|
+
# Minimal agent proxy for callback hooks
|
|
503
|
+
# ---------------------------------------------------------------------------
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class _JudgeAgentProxy:
|
|
507
|
+
"""Lightweight proxy for ``on_agent_run_context`` callback hooks."""
|
|
508
|
+
|
|
509
|
+
def __init__(self) -> None:
|
|
510
|
+
self.name = "mindpack-judge"
|
|
511
|
+
self._model_name: str | None = None
|
|
512
|
+
|
|
513
|
+
def get_model_name(self) -> str:
|
|
514
|
+
if self._model_name is None:
|
|
515
|
+
self._model_name = JudgeAgentFactory._resolve_model_name() or "unknown"
|
|
516
|
+
return self._model_name
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
# ---------------------------------------------------------------------------
|
|
520
|
+
# LLMJudgeMerger
|
|
521
|
+
# ---------------------------------------------------------------------------
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
class LLMJudgeMerger(JudgeMerger):
|
|
525
|
+
"""Concrete ``JudgeMerger`` that delegates merging to an LLM judge.
|
|
526
|
+
|
|
527
|
+
Falls back to placeholder-style merging if the judge fails, so the
|
|
528
|
+
orchestrator always receives a valid ``AskMindPackOutput``.
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
def __init__(
|
|
532
|
+
self,
|
|
533
|
+
factory: JudgeAgentFactory | None = None,
|
|
534
|
+
) -> None:
|
|
535
|
+
self._factory = factory or JudgeAgentFactory()
|
|
536
|
+
|
|
537
|
+
async def merge(
|
|
538
|
+
self,
|
|
539
|
+
request: AskMindPackInput,
|
|
540
|
+
reports: list[ExpertReport],
|
|
541
|
+
session_id: str,
|
|
542
|
+
) -> AskMindPackOutput:
|
|
543
|
+
"""Produce a unified advisory from all expert reports via LLM judge.
|
|
544
|
+
|
|
545
|
+
Degrades to placeholder merge if the judge fails.
|
|
546
|
+
"""
|
|
547
|
+
if not reports:
|
|
548
|
+
return self._empty_output(request)
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
return await self._factory.invoke_judge(request, reports, session_id)
|
|
552
|
+
except Exception as exc:
|
|
553
|
+
logger.error(
|
|
554
|
+
"LLMJudgeMerger: judge invocation failed, using fallback: %s",
|
|
555
|
+
exc,
|
|
556
|
+
exc_info=True,
|
|
557
|
+
)
|
|
558
|
+
return JudgeAgentFactory._placeholder_output(request, reports)
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _empty_output(request: AskMindPackInput) -> AskMindPackOutput:
|
|
562
|
+
"""Minimal output when no expert reports are available."""
|
|
563
|
+
return AskMindPackOutput(
|
|
564
|
+
summary=(f"No expert reports available for '{request.desired_output}'."),
|
|
565
|
+
recommended_plan=("Unable to produce a plan — no experts consulted."),
|
|
566
|
+
ranked_options=[],
|
|
567
|
+
risks=["No experts were consulted — advice is unvalidated."],
|
|
568
|
+
tests_to_run=[],
|
|
569
|
+
files_to_inspect_or_change=list(request.relevant_files),
|
|
570
|
+
expert_consensus="N/A — no reports received.",
|
|
571
|
+
disagreements=[],
|
|
572
|
+
confidence=0.0,
|
|
573
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""MindPack memory — ReportStore for expert report buffering.
|
|
2
|
+
|
|
3
|
+
Provides a structured, flexible storage system for expert reports.
|
|
4
|
+
Supports in-memory buffering, persistent caching, and workspace-local storage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import pathlib
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from code_muse.plugins.mindpack.schemas import (
|
|
14
|
+
MindPackExpertReport,
|
|
15
|
+
MindPackMergedDecision,
|
|
16
|
+
MindPackReportStoreConfig,
|
|
17
|
+
ReportStoreMode,
|
|
18
|
+
)
|
|
19
|
+
from code_muse.security.redaction import redact_secrets
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ReportStore:
|
|
25
|
+
"""Manages storage for MindPack expert reports and final decisions."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, config: MindPackReportStoreConfig | None = None) -> None:
|
|
28
|
+
self.config = config or MindPackReportStoreConfig()
|
|
29
|
+
self._memory_store: dict[str, list[MindPackExpertReport]] = {}
|
|
30
|
+
self._decisions: dict[str, MindPackMergedDecision] = {}
|
|
31
|
+
|
|
32
|
+
def _get_storage_path(
|
|
33
|
+
self, run_id: str, mode: ReportStoreMode
|
|
34
|
+
) -> pathlib.Path | None:
|
|
35
|
+
if mode == "cache":
|
|
36
|
+
path = pathlib.Path(self.config.cache_dir).expanduser() / run_id
|
|
37
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
return path
|
|
39
|
+
elif mode == "workspace":
|
|
40
|
+
path = pathlib.Path.cwd() / self.config.workspace_dir / run_id
|
|
41
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
return path
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def _save_to_disk(self, run_id: str, data: BaseModel, filename: str) -> None:
|
|
46
|
+
"""Helper to save serializable data to disk."""
|
|
47
|
+
modes = (
|
|
48
|
+
[self.config.mode] if self.config.mode != "both" else ["cache", "workspace"]
|
|
49
|
+
)
|
|
50
|
+
for mode in modes:
|
|
51
|
+
path = self._get_storage_path(run_id, mode) # type: ignore
|
|
52
|
+
if path:
|
|
53
|
+
file_path = path / filename
|
|
54
|
+
data_dict = data.model_dump()
|
|
55
|
+
if not self.config.save_raw_transcripts:
|
|
56
|
+
data_dict = redact_secrets(data_dict)
|
|
57
|
+
with open(file_path, "w") as f:
|
|
58
|
+
json.dump(data_dict, f, indent=2)
|
|
59
|
+
logger.debug("ReportStore: saved %s to %s", filename, file_path)
|
|
60
|
+
|
|
61
|
+
def add_report(self, report: MindPackExpertReport) -> None:
|
|
62
|
+
"""Stores a report in memory and optionally persists it."""
|
|
63
|
+
if self.config.mode in ["memory", "both"]:
|
|
64
|
+
if report.run_id not in self._memory_store:
|
|
65
|
+
self._memory_store[report.run_id] = []
|
|
66
|
+
self._memory_store[report.run_id].append(report)
|
|
67
|
+
|
|
68
|
+
if self.config.mode in ["cache", "workspace", "both"]:
|
|
69
|
+
self._save_to_disk(report.run_id, report, f"report_{report.expert_id}.json")
|
|
70
|
+
|
|
71
|
+
def save_reports(self, reports: list[MindPackExpertReport]) -> None:
|
|
72
|
+
"""Batch save reports."""
|
|
73
|
+
for report in reports:
|
|
74
|
+
self.add_report(report)
|
|
75
|
+
|
|
76
|
+
def save_merged_decision(self, merged: MindPackMergedDecision) -> None:
|
|
77
|
+
"""Stores and persists the final judge decision."""
|
|
78
|
+
self._decisions[merged.run_id] = merged
|
|
79
|
+
if self.config.mode in ["cache", "workspace", "both"]:
|
|
80
|
+
self._save_to_disk(merged.run_id, merged, "decision.json")
|
|
81
|
+
|
|
82
|
+
def get_reports(self, run_id: str) -> list[MindPackExpertReport]:
|
|
83
|
+
"""Fetch reports from memory."""
|
|
84
|
+
return self._memory_store.get(run_id, [])
|
|
85
|
+
|
|
86
|
+
def clear_run(self, run_id: str) -> None:
|
|
87
|
+
"""Clears memory for a run."""
|
|
88
|
+
self._memory_store.pop(run_id, None)
|
|
89
|
+
self._decisions.pop(run_id, None)
|
|
90
|
+
logger.debug("ReportStore: cleared run '%s'", run_id)
|
|
91
|
+
|
|
92
|
+
def report_count(self, run_id: str) -> int:
|
|
93
|
+
"""Return the number of reports buffered for a run."""
|
|
94
|
+
return len(self._memory_store.get(run_id, []))
|
|
95
|
+
|
|
96
|
+
def clear_all(self) -> None:
|
|
97
|
+
"""Clears all buffered memory and decisions."""
|
|
98
|
+
self._memory_store.clear()
|
|
99
|
+
self._decisions.clear()
|
|
100
|
+
logger.debug("ReportStore: cleared all runs")
|