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
code_muse/callbacks.py
ADDED
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import atexit
|
|
3
|
+
import concurrent.futures
|
|
4
|
+
import logging
|
|
5
|
+
import traceback
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
PhaseType = Literal[
|
|
10
|
+
"startup",
|
|
11
|
+
"shutdown",
|
|
12
|
+
"invoke_agent",
|
|
13
|
+
"agent_exception",
|
|
14
|
+
"version_check",
|
|
15
|
+
"edit_file",
|
|
16
|
+
"create_file",
|
|
17
|
+
"replace_in_file",
|
|
18
|
+
"delete_snippet",
|
|
19
|
+
"delete_file",
|
|
20
|
+
"run_shell_command",
|
|
21
|
+
"load_model_config",
|
|
22
|
+
"load_models_config",
|
|
23
|
+
"load_prompt",
|
|
24
|
+
"agent_reload",
|
|
25
|
+
"custom_command",
|
|
26
|
+
"custom_command_help",
|
|
27
|
+
"file_permission",
|
|
28
|
+
"pre_tool_call",
|
|
29
|
+
"post_tool_call",
|
|
30
|
+
"stream_event",
|
|
31
|
+
"register_tools",
|
|
32
|
+
"register_agents",
|
|
33
|
+
"register_model_type",
|
|
34
|
+
"get_model_system_prompt",
|
|
35
|
+
"prepare_model_prompt",
|
|
36
|
+
"agent_run_start",
|
|
37
|
+
"agent_run_end",
|
|
38
|
+
"agent_run_result",
|
|
39
|
+
"register_browser_types",
|
|
40
|
+
"register_model_providers",
|
|
41
|
+
"message_history_processor_start",
|
|
42
|
+
"message_history_processor_end",
|
|
43
|
+
"on_message",
|
|
44
|
+
"agent_run_context",
|
|
45
|
+
"agent_run_cancel",
|
|
46
|
+
"should_skip_fallback_render",
|
|
47
|
+
]
|
|
48
|
+
CallbackFunc = Callable[..., Any]
|
|
49
|
+
|
|
50
|
+
# (priority, func) tuples — higher priority runs first.
|
|
51
|
+
_callbacks: dict[PhaseType, list[tuple[int, CallbackFunc]]] = {
|
|
52
|
+
"startup": [],
|
|
53
|
+
"shutdown": [],
|
|
54
|
+
"invoke_agent": [],
|
|
55
|
+
"agent_exception": [],
|
|
56
|
+
"version_check": [],
|
|
57
|
+
"edit_file": [],
|
|
58
|
+
"create_file": [],
|
|
59
|
+
"replace_in_file": [],
|
|
60
|
+
"delete_snippet": [],
|
|
61
|
+
"delete_file": [],
|
|
62
|
+
"run_shell_command": [],
|
|
63
|
+
"load_model_config": [],
|
|
64
|
+
"load_models_config": [],
|
|
65
|
+
"load_prompt": [],
|
|
66
|
+
"agent_reload": [],
|
|
67
|
+
"custom_command": [],
|
|
68
|
+
"custom_command_help": [],
|
|
69
|
+
"file_permission": [],
|
|
70
|
+
"pre_tool_call": [],
|
|
71
|
+
"post_tool_call": [],
|
|
72
|
+
"stream_event": [],
|
|
73
|
+
"register_tools": [],
|
|
74
|
+
"register_agents": [],
|
|
75
|
+
"register_model_type": [],
|
|
76
|
+
"get_model_system_prompt": [],
|
|
77
|
+
"prepare_model_prompt": [],
|
|
78
|
+
"agent_run_start": [],
|
|
79
|
+
"agent_run_end": [],
|
|
80
|
+
"agent_run_result": [],
|
|
81
|
+
"register_browser_types": [],
|
|
82
|
+
"register_model_providers": [],
|
|
83
|
+
"message_history_processor_start": [],
|
|
84
|
+
"message_history_processor_end": [],
|
|
85
|
+
"on_message": [],
|
|
86
|
+
"agent_run_context": [],
|
|
87
|
+
"agent_run_cancel": [],
|
|
88
|
+
"should_skip_fallback_render": [],
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
logger = logging.getLogger(__name__)
|
|
92
|
+
# FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14 —
|
|
93
|
+
# no GIL contention for I/O-bound callback work.
|
|
94
|
+
_executor = concurrent.futures.ThreadPoolExecutor(max_workers=8)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _shutdown_executor() -> None:
|
|
98
|
+
"""Gracefully shut down the callback ThreadPoolExecutor.
|
|
99
|
+
|
|
100
|
+
Called automatically at interpreter exit via ``atexit``. Plugins that
|
|
101
|
+
spawn long-running threads inside callbacks should clean themselves up
|
|
102
|
+
in response to the ``shutdown`` hook; this function only terminates the
|
|
103
|
+
executor that runs those callbacks.
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
_executor.shutdown(wait=True, cancel_futures=True)
|
|
107
|
+
logger.debug("Callback executor shut down cleanly")
|
|
108
|
+
except Exception as exc:
|
|
109
|
+
logger.warning(f"Callback executor shutdown error: {exc}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
atexit.register(_shutdown_executor)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def register_callback(phase: PhaseType, func: CallbackFunc, priority: int = 0) -> None:
|
|
116
|
+
if phase not in _callbacks:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Unsupported phase: {phase}. Supported phases: {list(_callbacks.keys())}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if not callable(func):
|
|
122
|
+
raise TypeError(f"Callback must be callable, got {type(func)}")
|
|
123
|
+
|
|
124
|
+
# Prevent duplicate registration of the same callback function
|
|
125
|
+
# This can happen if plugins are accidentally loaded multiple times
|
|
126
|
+
for existing_priority, existing_func in _callbacks[phase]:
|
|
127
|
+
if existing_func is func:
|
|
128
|
+
logger.debug(
|
|
129
|
+
f"Callback {func.__name__} already registered for phase '{phase}', skipping"
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
_callbacks[phase].append((priority, func))
|
|
134
|
+
logger.debug(
|
|
135
|
+
f"Registered async callback {func.__name__} for phase '{phase}' (priority={priority})"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def unregister_callback(phase: PhaseType, func: CallbackFunc) -> bool:
|
|
140
|
+
if phase not in _callbacks:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
for idx, (existing_priority, existing_func) in enumerate(_callbacks[phase]):
|
|
144
|
+
if existing_func is func:
|
|
145
|
+
del _callbacks[phase][idx]
|
|
146
|
+
logger.debug(
|
|
147
|
+
f"Unregistered async callback {func.__name__} from phase '{phase}'"
|
|
148
|
+
)
|
|
149
|
+
return True
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def clear_callbacks(phase: PhaseType | None = None) -> None:
|
|
154
|
+
if phase is None:
|
|
155
|
+
for p in _callbacks:
|
|
156
|
+
_callbacks[p].clear()
|
|
157
|
+
logger.debug("Cleared all async callbacks")
|
|
158
|
+
else:
|
|
159
|
+
if phase in _callbacks:
|
|
160
|
+
_callbacks[phase].clear()
|
|
161
|
+
logger.debug(f"Cleared async callbacks for phase '{phase}'")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_callbacks(phase: PhaseType) -> list[CallbackFunc]:
|
|
165
|
+
"""Return callbacks for *phase* sorted by priority (highest first)."""
|
|
166
|
+
callbacks = _callbacks.get(phase, [])
|
|
167
|
+
# Sort by priority descending, then by registration order (stable sort)
|
|
168
|
+
sorted_callbacks = sorted(callbacks, key=lambda item: item[0], reverse=True)
|
|
169
|
+
return [func for _priority, func in sorted_callbacks]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def count_callbacks(phase: PhaseType | None = None) -> int:
|
|
173
|
+
if phase is None:
|
|
174
|
+
return sum(len(callbacks) for callbacks in _callbacks.values())
|
|
175
|
+
return len(_callbacks.get(phase, []))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def fire_callbacks(phase: PhaseType, *args, **kwargs) -> None:
|
|
179
|
+
"""Fire callbacks without blocking; async callbacks run in the background.
|
|
180
|
+
|
|
181
|
+
Useful for observation hooks (like ``on_message``) that must not delay
|
|
182
|
+
the caller. Exceptions are logged and swallowed.
|
|
183
|
+
"""
|
|
184
|
+
callbacks = get_callbacks(phase)
|
|
185
|
+
if not callbacks:
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
for callback in callbacks:
|
|
189
|
+
try:
|
|
190
|
+
result = callback(*args, **kwargs)
|
|
191
|
+
if asyncio.iscoroutine(result):
|
|
192
|
+
try:
|
|
193
|
+
loop = asyncio.get_running_loop()
|
|
194
|
+
task = loop.create_task(result)
|
|
195
|
+
task.add_done_callback(
|
|
196
|
+
lambda t: logger.debug("Fire-and-forget callback completed")
|
|
197
|
+
)
|
|
198
|
+
except RuntimeError:
|
|
199
|
+
_executor.submit(asyncio.run, result)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.debug(f"Fire-and-forget callback {callback.__name__} failed: {e}")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _trigger_callbacks_sync(phase: PhaseType, *args, **kwargs) -> list[Any]:
|
|
205
|
+
callbacks = get_callbacks(phase)
|
|
206
|
+
if not callbacks:
|
|
207
|
+
logger.debug(f"No callbacks registered for phase '{phase}'")
|
|
208
|
+
return []
|
|
209
|
+
|
|
210
|
+
results = []
|
|
211
|
+
for callback in callbacks:
|
|
212
|
+
try:
|
|
213
|
+
result = callback(*args, **kwargs)
|
|
214
|
+
# Handle async callbacks - if we get a coroutine, run it
|
|
215
|
+
if asyncio.iscoroutine(result):
|
|
216
|
+
try:
|
|
217
|
+
asyncio.get_running_loop()
|
|
218
|
+
# We're in an async context (e.g., the TUI).
|
|
219
|
+
# Run the coroutine in a separate thread so it gets
|
|
220
|
+
# its own event loop. No timeout — long-running
|
|
221
|
+
# commands (MindPack, menus) need unlimited time.
|
|
222
|
+
future = _executor.submit(asyncio.run, result)
|
|
223
|
+
result = future.result(timeout=None)
|
|
224
|
+
except RuntimeError:
|
|
225
|
+
# No running loop — we're in a sync/worker thread.
|
|
226
|
+
result = asyncio.run(result)
|
|
227
|
+
results.append(result)
|
|
228
|
+
logger.debug(f"Successfully executed callback {callback.__name__}")
|
|
229
|
+
except Exception as e:
|
|
230
|
+
logger.error(
|
|
231
|
+
f"Callback {callback.__name__} failed in phase '{phase}': {e}\n"
|
|
232
|
+
f"{traceback.format_exc()}"
|
|
233
|
+
)
|
|
234
|
+
results.append(None)
|
|
235
|
+
|
|
236
|
+
return results
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def _trigger_callbacks(phase: PhaseType, *args, **kwargs) -> list[Any]:
|
|
240
|
+
"""Trigger all registered callbacks for *phase* in priority order.
|
|
241
|
+
|
|
242
|
+
Hook execution order: callbacks run from highest priority to lowest.
|
|
243
|
+
For ``run_shell_command``, the caller (``command_runner.py``) iterates
|
|
244
|
+
results and the first non-None dict with ``pre_executed: True`` wins.
|
|
245
|
+
By default priority values, ``filter_engine`` runs before
|
|
246
|
+
``policy_engine`` before ``shell_minimizer``.
|
|
247
|
+
"""
|
|
248
|
+
callbacks = get_callbacks(phase)
|
|
249
|
+
|
|
250
|
+
if not callbacks:
|
|
251
|
+
logger.debug(f"No callbacks registered for phase '{phase}'")
|
|
252
|
+
return []
|
|
253
|
+
|
|
254
|
+
logger.debug(f"Triggering {len(callbacks)} async callbacks for phase '{phase}'")
|
|
255
|
+
|
|
256
|
+
results = []
|
|
257
|
+
for callback in callbacks:
|
|
258
|
+
try:
|
|
259
|
+
result = callback(*args, **kwargs)
|
|
260
|
+
if asyncio.iscoroutine(result):
|
|
261
|
+
result = await result
|
|
262
|
+
results.append(result)
|
|
263
|
+
logger.debug(f"Successfully executed async callback {callback.__name__}")
|
|
264
|
+
except Exception as e:
|
|
265
|
+
logger.error(
|
|
266
|
+
f"Async callback {callback.__name__} failed in phase '{phase}': {e}\n"
|
|
267
|
+
f"{traceback.format_exc()}"
|
|
268
|
+
)
|
|
269
|
+
results.append(None)
|
|
270
|
+
|
|
271
|
+
return results
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
async def on_startup() -> list[Any]:
|
|
275
|
+
callbacks = get_callbacks("startup")
|
|
276
|
+
results: list[Any] = []
|
|
277
|
+
failed_names: list[str] = []
|
|
278
|
+
for callback in callbacks:
|
|
279
|
+
try:
|
|
280
|
+
result = callback()
|
|
281
|
+
if asyncio.iscoroutine(result):
|
|
282
|
+
result = await result
|
|
283
|
+
results.append(result)
|
|
284
|
+
logger.debug(f"Successfully executed async callback {callback.__name__}")
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.error(
|
|
287
|
+
f"Async callback {callback.__name__} failed in phase 'startup': {e}\n"
|
|
288
|
+
f"{traceback.format_exc()}"
|
|
289
|
+
)
|
|
290
|
+
failed_names.append(callback.__name__)
|
|
291
|
+
results.append(None)
|
|
292
|
+
if failed_names:
|
|
293
|
+
count = len(failed_names)
|
|
294
|
+
names_str = ", ".join(failed_names)
|
|
295
|
+
logger.warning(f"{count} startup callback(s) failed: {names_str}")
|
|
296
|
+
from code_muse.messaging import emit_warning
|
|
297
|
+
|
|
298
|
+
emit_warning(f"⚠️ {count} plugin(s) failed to load: {names_str}")
|
|
299
|
+
|
|
300
|
+
# Report Cython status from the core package
|
|
301
|
+
import code_muse
|
|
302
|
+
|
|
303
|
+
if code_muse.CYTHON_ENABLED:
|
|
304
|
+
from code_muse.messaging import emit_success
|
|
305
|
+
|
|
306
|
+
emit_success(
|
|
307
|
+
f"✅ Cython enabled — {code_muse.PYX_MODULE_COUNT} modules compiled"
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
from code_muse.messaging import emit_warning
|
|
311
|
+
|
|
312
|
+
emit_warning("⚠️ Cython not available — running in pure Python mode")
|
|
313
|
+
return results
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
async def on_shutdown() -> list[Any]:
|
|
317
|
+
return await _trigger_callbacks("shutdown")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
async def on_invoke_agent(*args, **kwargs) -> list[Any]:
|
|
321
|
+
return await _trigger_callbacks("invoke_agent", *args, **kwargs)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
async def on_agent_exception(exception: Exception, *args, **kwargs) -> list[Any]:
|
|
325
|
+
return await _trigger_callbacks("agent_exception", exception, *args, **kwargs)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
async def on_version_check(*args, **kwargs) -> list[Any]:
|
|
329
|
+
return await _trigger_callbacks("version_check", *args, **kwargs)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def on_load_model_config(*args, **kwargs) -> list[Any]:
|
|
333
|
+
return _trigger_callbacks_sync("load_model_config", *args, **kwargs)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def on_load_models_config() -> list[Any]:
|
|
337
|
+
"""Trigger callbacks to load additional model configurations.
|
|
338
|
+
|
|
339
|
+
Plugins can register callbacks that return a dict of model configurations
|
|
340
|
+
to be merged with the built-in models.json. Plugin models override built-in
|
|
341
|
+
models with the same name.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
List of model config dicts from all registered callbacks.
|
|
345
|
+
"""
|
|
346
|
+
return _trigger_callbacks_sync("load_models_config")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def on_edit_file(*args, **kwargs) -> Any:
|
|
350
|
+
return _trigger_callbacks_sync("edit_file", *args, **kwargs)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def on_create_file(*args, **kwargs) -> Any:
|
|
354
|
+
return _trigger_callbacks_sync("create_file", *args, **kwargs)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def on_replace_in_file(*args, **kwargs) -> Any:
|
|
358
|
+
return _trigger_callbacks_sync("replace_in_file", *args, **kwargs)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def on_delete_snippet(*args, **kwargs) -> Any:
|
|
362
|
+
return _trigger_callbacks_sync("delete_snippet", *args, **kwargs)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def on_delete_file(*args, **kwargs) -> Any:
|
|
366
|
+
return _trigger_callbacks_sync("delete_file", *args, **kwargs)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
async def on_run_shell_command(*args, **kwargs) -> Any:
|
|
370
|
+
"""Trigger callbacks for shell command execution.
|
|
371
|
+
|
|
372
|
+
Multiple plugins can register for this hook. The caller iterates through
|
|
373
|
+
registered callbacks in priority order (highest first). The first callback
|
|
374
|
+
returning ``{"pre_executed": True, "output": ...}`` wins — the rest are
|
|
375
|
+
skipped.
|
|
376
|
+
|
|
377
|
+
Recommended priority chain for ``run_shell_command``:
|
|
378
|
+
|
|
379
|
+
1. ``filter_engine`` (priority 100) — intercepts and transforms shell output
|
|
380
|
+
2. ``policy_engine`` / ``shell_safety`` (priority 50) — enforces allow/deny/ask-user rules
|
|
381
|
+
3. ``shell_minimizer`` (priority 0) — compresses known command output
|
|
382
|
+
|
|
383
|
+
Future handlers should use an explicit ``priority`` argument when calling
|
|
384
|
+
``register_callback()`` if they need to run earlier or later in the pipeline.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
*args: Positional arguments passed to callbacks (context, command, ...).
|
|
388
|
+
**kwargs: Keyword arguments passed to callbacks (cwd, timeout, ...).
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
List of results from all registered callbacks.
|
|
392
|
+
"""
|
|
393
|
+
return await _trigger_callbacks("run_shell_command", *args, **kwargs)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def on_agent_reload(*args, **kwargs) -> Any:
|
|
397
|
+
return _trigger_callbacks_sync("agent_reload", *args, **kwargs)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def on_load_prompt():
|
|
401
|
+
return _trigger_callbacks_sync("load_prompt")
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def on_custom_command_help() -> list[Any]:
|
|
405
|
+
"""Collect custom command help entries from plugins.
|
|
406
|
+
|
|
407
|
+
Each callback should return a list of tuples [(name, description), ...]
|
|
408
|
+
or a single tuple, or None. We'll flatten and sanitize results.
|
|
409
|
+
"""
|
|
410
|
+
return _trigger_callbacks_sync("custom_command_help")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def on_custom_command(command: str, name: str) -> list[Any]:
|
|
414
|
+
"""Trigger custom command callbacks.
|
|
415
|
+
|
|
416
|
+
This allows plugins to register handlers for slash commands
|
|
417
|
+
that are not built into the core command handler.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
command: The full command string (e.g., "/foo bar baz").
|
|
421
|
+
name: The primary command name without the leading slash (e.g., "foo").
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Implementations may return:
|
|
425
|
+
- True if the command was handled (and no further action is needed)
|
|
426
|
+
- A string to be processed as user input by the caller
|
|
427
|
+
- None to indicate not handled
|
|
428
|
+
"""
|
|
429
|
+
return _trigger_callbacks_sync("custom_command", command, name)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def on_file_permission(
|
|
433
|
+
context: Any,
|
|
434
|
+
file_path: str,
|
|
435
|
+
operation: str,
|
|
436
|
+
preview: str | None = None,
|
|
437
|
+
message_group: str | None = None,
|
|
438
|
+
operation_data: Any = None,
|
|
439
|
+
) -> list[Any]:
|
|
440
|
+
"""Trigger file permission callbacks.
|
|
441
|
+
|
|
442
|
+
This allows plugins to register handlers for file permission checks
|
|
443
|
+
before file operations are performed.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
context: The operation context
|
|
447
|
+
file_path: Path to the file being operated on
|
|
448
|
+
operation: Description of the operation
|
|
449
|
+
preview: Optional preview of changes (deprecated - use operation_data instead)
|
|
450
|
+
message_group: Optional message group
|
|
451
|
+
operation_data: Operation-specific data for preview generation (recommended)
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
List of boolean results from permission handlers.
|
|
455
|
+
Returns True if permission should be granted, False if denied.
|
|
456
|
+
"""
|
|
457
|
+
# PERF-08: Skip callback overhead entirely in yolo mode
|
|
458
|
+
from code_muse.config import get_yolo_mode
|
|
459
|
+
|
|
460
|
+
if get_yolo_mode():
|
|
461
|
+
return [True]
|
|
462
|
+
|
|
463
|
+
# For backward compatibility, if operation_data is provided, prefer it over preview
|
|
464
|
+
if operation_data is not None:
|
|
465
|
+
preview = None
|
|
466
|
+
return _trigger_callbacks_sync(
|
|
467
|
+
"file_permission",
|
|
468
|
+
context,
|
|
469
|
+
file_path,
|
|
470
|
+
operation,
|
|
471
|
+
preview,
|
|
472
|
+
message_group,
|
|
473
|
+
operation_data,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
async def on_pre_tool_call(
|
|
478
|
+
tool_name: str, tool_args: dict, context: Any = None
|
|
479
|
+
) -> list[Any]:
|
|
480
|
+
"""Trigger callbacks before a tool is called.
|
|
481
|
+
|
|
482
|
+
This allows plugins to inspect, modify, or log tool calls before
|
|
483
|
+
they are executed.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
tool_name: Name of the tool being called
|
|
487
|
+
tool_args: Arguments being passed to the tool
|
|
488
|
+
context: Optional context data for the tool call
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
List of results from registered callbacks.
|
|
492
|
+
"""
|
|
493
|
+
return await _trigger_callbacks("pre_tool_call", tool_name, tool_args, context)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
async def on_post_tool_call(
|
|
497
|
+
tool_name: str,
|
|
498
|
+
tool_args: dict,
|
|
499
|
+
result: Any,
|
|
500
|
+
duration_ms: float,
|
|
501
|
+
context: Any = None,
|
|
502
|
+
) -> list[Any]:
|
|
503
|
+
"""Trigger callbacks after a tool completes.
|
|
504
|
+
|
|
505
|
+
This allows plugins to inspect tool results, log execution times,
|
|
506
|
+
or perform post-processing.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
tool_name: Name of the tool that was called
|
|
510
|
+
tool_args: Arguments that were passed to the tool
|
|
511
|
+
result: The result returned by the tool
|
|
512
|
+
duration_ms: Execution time in milliseconds
|
|
513
|
+
context: Optional context data for the tool call
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
List of results from registered callbacks.
|
|
517
|
+
"""
|
|
518
|
+
return await _trigger_callbacks(
|
|
519
|
+
"post_tool_call", tool_name, tool_args, result, duration_ms, context
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
async def on_stream_event(
|
|
524
|
+
event_type: str, event_data: Any, agent_session_id: str | None = None
|
|
525
|
+
) -> list[Any]:
|
|
526
|
+
"""Trigger callbacks for streaming events.
|
|
527
|
+
|
|
528
|
+
This allows plugins to react to streaming events in real-time,
|
|
529
|
+
such as tokens being generated, tool calls starting, etc.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
event_type: Type of the streaming event
|
|
533
|
+
event_data: Data associated with the event
|
|
534
|
+
agent_session_id: Optional session ID of the agent emitting the event
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
List of results from registered callbacks.
|
|
538
|
+
"""
|
|
539
|
+
return await _trigger_callbacks(
|
|
540
|
+
"stream_event", event_type, event_data, agent_session_id
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def on_register_tools() -> list[dict[str, Any]]:
|
|
545
|
+
"""Collect custom tool registrations from plugins.
|
|
546
|
+
|
|
547
|
+
Each callback should return a list of dicts with:
|
|
548
|
+
- "name": str - the tool name
|
|
549
|
+
- "register_func": callable - function that takes an agent and registers the tool
|
|
550
|
+
|
|
551
|
+
Example return: [{"name": "my_tool", "register_func": register_my_tool}]
|
|
552
|
+
"""
|
|
553
|
+
return _trigger_callbacks_sync("register_tools")
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def on_register_agents() -> list[dict[str, Any]]:
|
|
557
|
+
"""Collect custom agent registrations from plugins.
|
|
558
|
+
|
|
559
|
+
Each callback should return a list of dicts with either:
|
|
560
|
+
- "name": str, "class": Type[BaseAgent] - for Python agent classes
|
|
561
|
+
- "name": str, "json_path": str - for JSON agent files
|
|
562
|
+
|
|
563
|
+
Example return: [{"name": "my-agent", "class": MyAgentClass}]
|
|
564
|
+
"""
|
|
565
|
+
return _trigger_callbacks_sync("register_agents")
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def on_register_model_types() -> list[dict[str, Any]]:
|
|
569
|
+
"""Collect custom model type registrations from plugins.
|
|
570
|
+
|
|
571
|
+
This hook allows plugins to register custom model types that can be used
|
|
572
|
+
in model configurations. Each callback should return a list of dicts with:
|
|
573
|
+
- "type": str - the model type name (e.g., "claude_code")
|
|
574
|
+
- "handler": callable - function(model_name, model_config, config) -> model instance
|
|
575
|
+
|
|
576
|
+
The handler function receives:
|
|
577
|
+
- model_name: str - the name of the model being created
|
|
578
|
+
- model_config: dict - the model's configuration from models.json
|
|
579
|
+
- config: dict - the full models configuration
|
|
580
|
+
|
|
581
|
+
The handler should return a model instance or None if creation fails.
|
|
582
|
+
|
|
583
|
+
Example callback:
|
|
584
|
+
def register_my_model_types():
|
|
585
|
+
return [{
|
|
586
|
+
"type": "my_custom_type",
|
|
587
|
+
"handler": create_my_custom_model,
|
|
588
|
+
}]
|
|
589
|
+
|
|
590
|
+
Example return: [{"type": "my_custom_type", "handler": create_my_custom_model}]
|
|
591
|
+
"""
|
|
592
|
+
return _trigger_callbacks_sync("register_model_type")
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def on_get_model_system_prompt(
|
|
596
|
+
model_name: str, default_system_prompt: str, user_prompt: str
|
|
597
|
+
) -> list[dict[str, Any]]:
|
|
598
|
+
"""Allow plugins to provide custom system prompts for specific model types.
|
|
599
|
+
|
|
600
|
+
This hook allows plugins to override the system prompt handling for custom
|
|
601
|
+
model types (like claude_code models). Each callback receives
|
|
602
|
+
the model name and should return a dict if it handles that model type, or None.
|
|
603
|
+
|
|
604
|
+
Args:
|
|
605
|
+
model_name: The name of the model being used (e.g., "claude-code-sonnet")
|
|
606
|
+
default_system_prompt: The default system prompt from the agent
|
|
607
|
+
user_prompt: The user's prompt/message
|
|
608
|
+
|
|
609
|
+
Each callback should return a dict with:
|
|
610
|
+
- "instructions": str - the system prompt/instructions to use
|
|
611
|
+
- "user_prompt": str - the (possibly modified) user prompt
|
|
612
|
+
- "handled": bool - True if this callback handled the model
|
|
613
|
+
|
|
614
|
+
Or return None if the callback doesn't handle this model type.
|
|
615
|
+
|
|
616
|
+
Example callback:
|
|
617
|
+
def get_my_model_system_prompt(model_name, default_system_prompt, user_prompt):
|
|
618
|
+
if model_name.startswith("my-custom-"):
|
|
619
|
+
return {
|
|
620
|
+
"instructions": "You are MyCustomBot.",
|
|
621
|
+
"user_prompt": f"{default_system_prompt}\n\n{user_prompt}",
|
|
622
|
+
"handled": True,
|
|
623
|
+
}
|
|
624
|
+
return None # Not handled by this callback
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
List of results from registered callbacks (dicts or None values).
|
|
628
|
+
"""
|
|
629
|
+
return _trigger_callbacks_sync(
|
|
630
|
+
"get_model_system_prompt", model_name, default_system_prompt, user_prompt
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def on_prepare_model_prompt(
|
|
635
|
+
model_name: str,
|
|
636
|
+
system_prompt: str,
|
|
637
|
+
user_prompt: str,
|
|
638
|
+
prepend_system_to_user: bool = True,
|
|
639
|
+
) -> list[dict[str, Any | None]]:
|
|
640
|
+
"""Allow plugins to fully prepare the prompt (instructions + user prompt) for a model.
|
|
641
|
+
|
|
642
|
+
This is the hook fired from ``model_utils.prepare_prompt_for_model`` to let
|
|
643
|
+
plugins take over prompt preparation for specific model families (e.g.
|
|
644
|
+
claude-code OAuth models which need a hard-coded instruction string and
|
|
645
|
+
have the system prompt prepended to the user message).
|
|
646
|
+
|
|
647
|
+
Unlike ``get_model_system_prompt`` (which is used by augmenting plugins like
|
|
648
|
+
agent_skills), this hook is for plugins that want to *fully handle* the
|
|
649
|
+
prompt prep for a given model. The first callback returning ``handled=True``
|
|
650
|
+
wins; the rest are ignored.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
model_name: The name of the model being used.
|
|
654
|
+
system_prompt: The default system prompt from the agent.
|
|
655
|
+
user_prompt: The user's prompt/message.
|
|
656
|
+
prepend_system_to_user: Whether the caller wants system prompt prepended
|
|
657
|
+
to the user prompt (only meaningful for plugins that manipulate the
|
|
658
|
+
user prompt, like claude-code).
|
|
659
|
+
|
|
660
|
+
Each callback should return a dict with:
|
|
661
|
+
- ``"handled"``: bool — True if this callback fully prepared the prompt.
|
|
662
|
+
- ``"instructions"``: str — the system prompt/instructions to use.
|
|
663
|
+
- ``"user_prompt"``: str — the (possibly modified) user prompt.
|
|
664
|
+
- ``"is_claude_code"``: bool — (optional) flag preserved on PreparedPrompt.
|
|
665
|
+
|
|
666
|
+
Or return ``None`` to indicate "I don't handle this model".
|
|
667
|
+
|
|
668
|
+
Returns:
|
|
669
|
+
List of results (dicts or ``None``) from registered callbacks.
|
|
670
|
+
"""
|
|
671
|
+
return _trigger_callbacks_sync(
|
|
672
|
+
"prepare_model_prompt",
|
|
673
|
+
model_name,
|
|
674
|
+
system_prompt,
|
|
675
|
+
user_prompt,
|
|
676
|
+
prepend_system_to_user,
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
async def on_agent_run_start(
|
|
681
|
+
agent_name: str,
|
|
682
|
+
model_name: str,
|
|
683
|
+
session_id: str | None = None,
|
|
684
|
+
) -> list[Any]:
|
|
685
|
+
"""Trigger callbacks when an agent run starts.
|
|
686
|
+
|
|
687
|
+
This fires at the beginning of run, before the agent task is created.
|
|
688
|
+
Useful for:
|
|
689
|
+
- Starting background tasks (like token refresh heartbeats)
|
|
690
|
+
- Logging/analytics
|
|
691
|
+
- Resource allocation
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
agent_name: Name of the agent starting
|
|
695
|
+
model_name: Name of the model being used
|
|
696
|
+
session_id: Optional session identifier
|
|
697
|
+
|
|
698
|
+
Returns:
|
|
699
|
+
List of results from registered callbacks.
|
|
700
|
+
"""
|
|
701
|
+
return await _trigger_callbacks(
|
|
702
|
+
"agent_run_start", agent_name, model_name, session_id
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
async def on_agent_run_end(
|
|
707
|
+
agent_name: str,
|
|
708
|
+
model_name: str,
|
|
709
|
+
session_id: str | None = None,
|
|
710
|
+
success: bool = True,
|
|
711
|
+
error: Exception | None = None,
|
|
712
|
+
response_text: str | None = None,
|
|
713
|
+
metadata: dict | None = None,
|
|
714
|
+
) -> list[Any]:
|
|
715
|
+
"""Trigger callbacks when an agent run ends.
|
|
716
|
+
|
|
717
|
+
This fires at the end of run, in the finally block.
|
|
718
|
+
Always fires regardless of success/failure/cancellation.
|
|
719
|
+
|
|
720
|
+
Useful for:
|
|
721
|
+
- Stopping background tasks (like token refresh heartbeats)
|
|
722
|
+
- Workflow orchestration (like Ralph's autonomous loop)
|
|
723
|
+
- Logging/analytics
|
|
724
|
+
- Resource cleanup
|
|
725
|
+
- Detecting completion signals in responses
|
|
726
|
+
|
|
727
|
+
Args:
|
|
728
|
+
agent_name: Name of the agent that finished
|
|
729
|
+
model_name: Name of the model that was used
|
|
730
|
+
session_id: Optional session identifier
|
|
731
|
+
success: Whether the run completed successfully
|
|
732
|
+
error: Exception if the run failed, None otherwise
|
|
733
|
+
response_text: The final text response from the agent (if successful)
|
|
734
|
+
metadata: Optional dict with additional context (tokens used, etc.)
|
|
735
|
+
|
|
736
|
+
Returns:
|
|
737
|
+
List of results from registered callbacks.
|
|
738
|
+
"""
|
|
739
|
+
return await _trigger_callbacks(
|
|
740
|
+
"agent_run_end",
|
|
741
|
+
agent_name,
|
|
742
|
+
model_name,
|
|
743
|
+
session_id,
|
|
744
|
+
success,
|
|
745
|
+
error,
|
|
746
|
+
response_text,
|
|
747
|
+
metadata,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
async def on_agent_run_result(
|
|
752
|
+
result: Any,
|
|
753
|
+
agent_name: str,
|
|
754
|
+
model_name: str,
|
|
755
|
+
) -> list[Any]:
|
|
756
|
+
"""Trigger callbacks after an agent run returns a result.
|
|
757
|
+
|
|
758
|
+
Fires after ``pydantic_agent.run()`` completes successfully, **before**
|
|
759
|
+
the result is handed back to the caller. Plugins can inspect the result
|
|
760
|
+
and request an automatic retry (e.g. when an upstream content-filter
|
|
761
|
+
produced a false-positive refusal).
|
|
762
|
+
|
|
763
|
+
Callback signature::
|
|
764
|
+
|
|
765
|
+
async def my_callback(result, agent_name: str, model_name: str)
|
|
766
|
+
-> dict | None
|
|
767
|
+
|
|
768
|
+
To request a retry, return a dict with::
|
|
769
|
+
|
|
770
|
+
{
|
|
771
|
+
"retry": True,
|
|
772
|
+
"prompt": "<message to send on retry>",
|
|
773
|
+
"delay": 1.0, # optional, seconds before retry
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
Return ``None`` (or omit a return) to let the result pass through.
|
|
777
|
+
The first callback that returns a retry request wins; the agent
|
|
778
|
+
replays at most a small fixed number of times to prevent runaway loops.
|
|
779
|
+
|
|
780
|
+
Args:
|
|
781
|
+
result: The ``RunResult`` returned by ``pydantic_agent.run()``.
|
|
782
|
+
agent_name: Name of the agent that produced the result.
|
|
783
|
+
model_name: Name of the model that was used.
|
|
784
|
+
|
|
785
|
+
Returns:
|
|
786
|
+
List of results from registered callbacks.
|
|
787
|
+
"""
|
|
788
|
+
return await _trigger_callbacks("agent_run_result", result, agent_name, model_name)
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def on_register_browser_types() -> list[Any]:
|
|
792
|
+
"""Trigger callbacks to register custom browser types/providers.
|
|
793
|
+
|
|
794
|
+
Plugins can register callbacks that return a dict mapping browser type names
|
|
795
|
+
to initialization functions. This allows plugins to provide custom browser
|
|
796
|
+
implementations (like Camoufox for stealth browsing).
|
|
797
|
+
|
|
798
|
+
Each callback should return a dict with:
|
|
799
|
+
- key: str - the browser type name (e.g., "camoufox", "firefox-stealth")
|
|
800
|
+
- value: callable - async initialization function that takes (manager, **kwargs)
|
|
801
|
+
and sets up the browser on the manager instance
|
|
802
|
+
|
|
803
|
+
Example callback:
|
|
804
|
+
def register_my_browser_types():
|
|
805
|
+
return {
|
|
806
|
+
"camoufox": initialize_camoufox,
|
|
807
|
+
"my-stealth-browser": initialize_my_stealth,
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
List of dicts from all registered callbacks.
|
|
812
|
+
"""
|
|
813
|
+
return _trigger_callbacks_sync("register_browser_types")
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def on_register_model_providers() -> list[Any]:
|
|
817
|
+
"""Trigger callbacks to register custom model provider classes.
|
|
818
|
+
|
|
819
|
+
Plugins can register callbacks that return a dict mapping provider names
|
|
820
|
+
to model classes.
|
|
821
|
+
|
|
822
|
+
Returns:
|
|
823
|
+
List of dicts from all registered callbacks.
|
|
824
|
+
"""
|
|
825
|
+
return _trigger_callbacks_sync("register_model_providers")
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def on_message_history_processor_start(
|
|
829
|
+
agent_name: str,
|
|
830
|
+
session_id: str | None,
|
|
831
|
+
message_history: list[Any],
|
|
832
|
+
incoming_messages: list[Any],
|
|
833
|
+
) -> list[Any]:
|
|
834
|
+
"""Trigger callbacks at the start of message history processing.
|
|
835
|
+
|
|
836
|
+
This hook fires at the beginning of the message_history_accumulator,
|
|
837
|
+
before any deduplication or processing occurs. Useful for:
|
|
838
|
+
- Logging/debugging message flow
|
|
839
|
+
- Observing raw incoming messages
|
|
840
|
+
- Analytics on message history growth
|
|
841
|
+
|
|
842
|
+
Args:
|
|
843
|
+
agent_name: Name of the agent processing messages
|
|
844
|
+
session_id: Optional session identifier
|
|
845
|
+
message_history: Current message history (before processing)
|
|
846
|
+
incoming_messages: New messages being added
|
|
847
|
+
|
|
848
|
+
Returns:
|
|
849
|
+
List of results from registered callbacks.
|
|
850
|
+
"""
|
|
851
|
+
return _trigger_callbacks_sync(
|
|
852
|
+
"message_history_processor_start",
|
|
853
|
+
agent_name,
|
|
854
|
+
session_id,
|
|
855
|
+
message_history,
|
|
856
|
+
incoming_messages,
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def on_message_history_processor_end(
|
|
861
|
+
agent_name: str,
|
|
862
|
+
session_id: str | None,
|
|
863
|
+
message_history: list[Any],
|
|
864
|
+
messages_added: int,
|
|
865
|
+
messages_filtered: int,
|
|
866
|
+
) -> list[Any]:
|
|
867
|
+
"""Trigger callbacks at the end of message history processing.
|
|
868
|
+
|
|
869
|
+
This hook fires at the end of the message_history_accumulator,
|
|
870
|
+
after deduplication and filtering has been applied. Useful for:
|
|
871
|
+
- Logging/debugging final message state
|
|
872
|
+
- Analytics on deduplication effectiveness
|
|
873
|
+
- Observing what was actually added to history
|
|
874
|
+
|
|
875
|
+
Args:
|
|
876
|
+
agent_name: Name of the agent processing messages
|
|
877
|
+
session_id: Optional session identifier
|
|
878
|
+
message_history: Final message history (after processing)
|
|
879
|
+
messages_added: Count of new messages that were added
|
|
880
|
+
messages_filtered: Count of messages that were filtered out (dupes/empty)
|
|
881
|
+
|
|
882
|
+
Returns:
|
|
883
|
+
List of results from registered callbacks.
|
|
884
|
+
"""
|
|
885
|
+
return _trigger_callbacks_sync(
|
|
886
|
+
"message_history_processor_end",
|
|
887
|
+
agent_name,
|
|
888
|
+
session_id,
|
|
889
|
+
message_history,
|
|
890
|
+
messages_added,
|
|
891
|
+
messages_filtered,
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
async def on_message(message_id: str, message: Any) -> list[Any]:
|
|
896
|
+
"""Trigger callbacks when a message is emitted.
|
|
897
|
+
|
|
898
|
+
This is the global observation hook for the messaging system.
|
|
899
|
+
For per-message interception with pattern matching, use
|
|
900
|
+
messaging.interceptors.register_interceptor() instead.
|
|
901
|
+
|
|
902
|
+
This hook is for observation (logging, analytics, WebSocket forwarding),
|
|
903
|
+
while interceptors are for control (silencing, replacing, redirecting).
|
|
904
|
+
|
|
905
|
+
Args:
|
|
906
|
+
message_id: The well-known message identifier (e.g., "tool:edit_file:complete")
|
|
907
|
+
message: The full Pydantic BaseMessage model (or UIMessage for legacy)
|
|
908
|
+
|
|
909
|
+
Returns:
|
|
910
|
+
List of results from registered callbacks.
|
|
911
|
+
"""
|
|
912
|
+
return await _trigger_callbacks("on_message", message_id, message)
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def on_agent_run_context(agent, pydantic_agent, group_id) -> list[Any]:
|
|
916
|
+
"""Collect async context managers that should wrap the ``pydantic_agent.run()`` call.
|
|
917
|
+
|
|
918
|
+
Each callback returns an async CM (with ``__aenter__``/``__aexit__``) or
|
|
919
|
+
``None``. The caller composes all non-``None`` results via
|
|
920
|
+
``contextlib.AsyncExitStack``.
|
|
921
|
+
|
|
922
|
+
Returns a list of async context managers (may be empty).
|
|
923
|
+
"""
|
|
924
|
+
results = _trigger_callbacks_sync(
|
|
925
|
+
"agent_run_context", agent, pydantic_agent, group_id
|
|
926
|
+
)
|
|
927
|
+
return [r for r in results if r is not None]
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
async def on_agent_run_cancel(group_id: str) -> list[Any]:
|
|
931
|
+
"""Fired when an agent run is cancelled or interrupted.
|
|
932
|
+
|
|
933
|
+
Plugins use this to cancel any external workflow tracking the run.
|
|
934
|
+
"""
|
|
935
|
+
return await _trigger_callbacks("agent_run_cancel", group_id)
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
def on_should_skip_fallback_render(agent) -> bool:
|
|
939
|
+
"""Return True if any plugin requests skipping the non-streaming fallback render."""
|
|
940
|
+
results = _trigger_callbacks_sync("should_skip_fallback_render", agent)
|
|
941
|
+
return any(r is True for r in results)
|