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,547 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude Code OAuth Plugin for Muse.
|
|
3
|
+
|
|
4
|
+
Provides OAuth authentication for Claude Code models and registers
|
|
5
|
+
the 'claude_code' model type handler.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
12
|
+
from typing import Any
|
|
13
|
+
from urllib.parse import parse_qs, urlparse
|
|
14
|
+
|
|
15
|
+
from code_muse.callbacks import register_callback
|
|
16
|
+
from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
17
|
+
from code_muse.model_switching import set_model_and_reload_agent
|
|
18
|
+
from code_muse.provider_identity import (
|
|
19
|
+
make_anthropic_provider,
|
|
20
|
+
resolve_provider_identity,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from ..oauth_muse_html import oauth_failure_html, oauth_success_html
|
|
24
|
+
from .config import CLAUDE_CODE_OAUTH_CONFIG, get_token_storage_path
|
|
25
|
+
from .fast_mode import (
|
|
26
|
+
FAST_SETTING_KEY,
|
|
27
|
+
ensure_fast_beta_header,
|
|
28
|
+
is_fast_mode_enabled,
|
|
29
|
+
patch_anthropic_client_fast_mode,
|
|
30
|
+
)
|
|
31
|
+
from .prompt_handler import prepare_claude_code_prompt
|
|
32
|
+
from .utils import (
|
|
33
|
+
OAuthContext,
|
|
34
|
+
add_models_to_extra_config,
|
|
35
|
+
assign_redirect_uri,
|
|
36
|
+
build_authorization_url,
|
|
37
|
+
exchange_code_for_tokens,
|
|
38
|
+
fetch_claude_code_models,
|
|
39
|
+
get_valid_access_token,
|
|
40
|
+
load_claude_models_filtered,
|
|
41
|
+
load_stored_tokens,
|
|
42
|
+
prepare_oauth_context,
|
|
43
|
+
remove_claude_code_models,
|
|
44
|
+
save_tokens,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class _OAuthResult:
|
|
51
|
+
def __init__(self) -> None:
|
|
52
|
+
self.code: str | None = None
|
|
53
|
+
self.state: str | None = None
|
|
54
|
+
self.error: str | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class _CallbackHandler(BaseHTTPRequestHandler):
|
|
58
|
+
result: _OAuthResult
|
|
59
|
+
received_event: threading.Event
|
|
60
|
+
|
|
61
|
+
def do_GET(self) -> None: # noqa: N802
|
|
62
|
+
parsed = urlparse(self.path)
|
|
63
|
+
logger.info("Callback received: path=%s", parsed.path)
|
|
64
|
+
params: dict[str, list[str]] = parse_qs(parsed.query)
|
|
65
|
+
|
|
66
|
+
code = params.get("code", [None])[0]
|
|
67
|
+
state = params.get("state", [None])[0]
|
|
68
|
+
|
|
69
|
+
if code and state:
|
|
70
|
+
self.result.code = code
|
|
71
|
+
self.result.state = state
|
|
72
|
+
success_html = oauth_success_html(
|
|
73
|
+
"Claude Code",
|
|
74
|
+
"You're totally synced with Claude Code now!",
|
|
75
|
+
)
|
|
76
|
+
self._write_response(200, success_html)
|
|
77
|
+
else:
|
|
78
|
+
self.result.error = "Missing code or state"
|
|
79
|
+
failure_html = oauth_failure_html(
|
|
80
|
+
"Claude Code",
|
|
81
|
+
"Missing code or state parameter [Warn]",
|
|
82
|
+
)
|
|
83
|
+
self._write_response(400, failure_html)
|
|
84
|
+
|
|
85
|
+
self.received_event.set()
|
|
86
|
+
|
|
87
|
+
def log_message(self, log_format: str, *args: Any) -> None:
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
def _write_response(self, status: int, body: str) -> None:
|
|
91
|
+
self.send_response(status)
|
|
92
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
93
|
+
self.end_headers()
|
|
94
|
+
self.wfile.write(body.encode("utf-8"))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _start_callback_server(
|
|
98
|
+
context: OAuthContext,
|
|
99
|
+
) -> tuple[HTTPServer, _OAuthResult, threading.Event | None]:
|
|
100
|
+
port_range = CLAUDE_CODE_OAUTH_CONFIG["callback_port_range"]
|
|
101
|
+
|
|
102
|
+
for port in range(port_range[0], port_range[1] + 1):
|
|
103
|
+
try:
|
|
104
|
+
server = HTTPServer(("localhost", port), _CallbackHandler)
|
|
105
|
+
assign_redirect_uri(context, port)
|
|
106
|
+
result = _OAuthResult()
|
|
107
|
+
event = threading.Event()
|
|
108
|
+
_CallbackHandler.result = result
|
|
109
|
+
_CallbackHandler.received_event = event
|
|
110
|
+
|
|
111
|
+
def run_server(server=server) -> None:
|
|
112
|
+
with server:
|
|
113
|
+
server.serve_forever()
|
|
114
|
+
|
|
115
|
+
threading.Thread(target=run_server, daemon=True).start()
|
|
116
|
+
return server, result, event
|
|
117
|
+
except OSError:
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
emit_error("Could not start OAuth callback server; all candidate ports are in use")
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _await_callback(context: OAuthContext) -> str | None:
|
|
125
|
+
timeout = CLAUDE_CODE_OAUTH_CONFIG["callback_timeout"]
|
|
126
|
+
|
|
127
|
+
started = _start_callback_server(context)
|
|
128
|
+
if not started:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
server, result, event = started
|
|
132
|
+
redirect_uri = context.redirect_uri
|
|
133
|
+
if not redirect_uri:
|
|
134
|
+
emit_error("Failed to assign redirect URI for OAuth flow")
|
|
135
|
+
server.shutdown()
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
auth_url = build_authorization_url(context)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
import webbrowser
|
|
142
|
+
|
|
143
|
+
from code_muse.tools.common import should_suppress_browser
|
|
144
|
+
|
|
145
|
+
if should_suppress_browser():
|
|
146
|
+
emit_info(
|
|
147
|
+
"[HEADLESS MODE] Would normally open browser for Claude Code OAuth…"
|
|
148
|
+
)
|
|
149
|
+
emit_info(f"In normal mode, would visit: {auth_url}")
|
|
150
|
+
else:
|
|
151
|
+
emit_info("Opening browser for Claude Code OAuth…")
|
|
152
|
+
webbrowser.open(auth_url)
|
|
153
|
+
emit_info(f"If it doesn't open automatically, visit: {auth_url}")
|
|
154
|
+
except Exception as exc: # pragma: no cover
|
|
155
|
+
if not should_suppress_browser():
|
|
156
|
+
emit_warning(f"Failed to open browser automatically: {exc}")
|
|
157
|
+
emit_info(f"Please open the URL manually: {auth_url}")
|
|
158
|
+
|
|
159
|
+
emit_info(f"Listening for callback on {redirect_uri}")
|
|
160
|
+
emit_info(
|
|
161
|
+
"If Claude redirects you to the console callback page, copy the full URL "
|
|
162
|
+
"and paste it back into Muse."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if not event.wait(timeout=timeout):
|
|
166
|
+
emit_error("OAuth callback timed out. Please try again.")
|
|
167
|
+
server.shutdown()
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
server.shutdown()
|
|
171
|
+
|
|
172
|
+
if result.error:
|
|
173
|
+
emit_error(f"OAuth callback error: {result.error}")
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
if result.state != context.state:
|
|
177
|
+
emit_error("State mismatch detected; aborting authentication.")
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
return result.code
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _custom_help() -> list[tuple[str, str]]:
|
|
184
|
+
return [
|
|
185
|
+
(
|
|
186
|
+
"claude-code-auth",
|
|
187
|
+
"Authenticate with Claude Code via OAuth and import available models",
|
|
188
|
+
),
|
|
189
|
+
(
|
|
190
|
+
"claude-code-status",
|
|
191
|
+
"Check Claude Code OAuth authentication status and configured models",
|
|
192
|
+
),
|
|
193
|
+
("claude-code-logout", "Remove Claude Code OAuth tokens and imported models"),
|
|
194
|
+
(
|
|
195
|
+
"claude-code-fast",
|
|
196
|
+
"Toggle fast mode (speed=fast + fast-mode beta) for the active Claude Code model",
|
|
197
|
+
),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _perform_authentication() -> None:
|
|
202
|
+
context = prepare_oauth_context()
|
|
203
|
+
code = _await_callback(context)
|
|
204
|
+
if not code:
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
emit_info("Exchanging authorization code for tokens…")
|
|
208
|
+
tokens = exchange_code_for_tokens(code, context)
|
|
209
|
+
if not tokens:
|
|
210
|
+
emit_error("Token exchange failed. Please retry the authentication flow.")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
if not save_tokens(tokens):
|
|
214
|
+
emit_error(
|
|
215
|
+
"Tokens retrieved but failed to save locally. Check file permissions."
|
|
216
|
+
)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
emit_success("Claude Code OAuth authentication successful!")
|
|
220
|
+
|
|
221
|
+
access_token = tokens.get("access_token")
|
|
222
|
+
if not access_token:
|
|
223
|
+
emit_warning("No access token returned; skipping model discovery.")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
emit_info("Fetching available Claude Code models…")
|
|
227
|
+
models = fetch_claude_code_models(access_token)
|
|
228
|
+
if not models:
|
|
229
|
+
emit_warning(
|
|
230
|
+
"Claude Code authentication succeeded but no models were returned."
|
|
231
|
+
)
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
emit_info(f"Discovered {len(models)} models: {', '.join(models)}")
|
|
235
|
+
if add_models_to_extra_config(models):
|
|
236
|
+
emit_success(
|
|
237
|
+
"Claude Code models added to your configuration. Use the `claude-code-` prefix!"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _reauthenticate_after_expired_oauth(model_name: str) -> str | None:
|
|
242
|
+
"""Run full Claude Code OAuth only for configured claude-code-* models."""
|
|
243
|
+
prefix = CLAUDE_CODE_OAUTH_CONFIG["prefix"]
|
|
244
|
+
if not model_name.startswith(prefix):
|
|
245
|
+
logger.debug(
|
|
246
|
+
"Skipping Claude Code OAuth flow for non-prefixed model: %s", model_name
|
|
247
|
+
)
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
emit_warning(
|
|
251
|
+
"Claude Code OAuth refresh failed; launching the browser sign-in flow again."
|
|
252
|
+
)
|
|
253
|
+
_perform_authentication()
|
|
254
|
+
|
|
255
|
+
access_token = get_valid_access_token()
|
|
256
|
+
if access_token:
|
|
257
|
+
emit_success("Claude Code OAuth restored. Retrying the failed request…")
|
|
258
|
+
return access_token
|
|
259
|
+
|
|
260
|
+
emit_error("Claude Code OAuth reauthentication did not produce a usable token.")
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _handle_custom_command(command: str, name: str) -> bool | None:
|
|
265
|
+
if not name:
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
if name == "claude-code-auth":
|
|
269
|
+
emit_info("Starting Claude Code OAuth authentication…")
|
|
270
|
+
tokens = load_stored_tokens()
|
|
271
|
+
if tokens and tokens.get("access_token"):
|
|
272
|
+
emit_warning(
|
|
273
|
+
"Existing Claude Code tokens found. Continuing will overwrite them."
|
|
274
|
+
)
|
|
275
|
+
_perform_authentication()
|
|
276
|
+
set_model_and_reload_agent("claude-code-claude-opus-4-7")
|
|
277
|
+
return True
|
|
278
|
+
|
|
279
|
+
if name == "claude-code-status":
|
|
280
|
+
tokens = load_stored_tokens()
|
|
281
|
+
if tokens and tokens.get("access_token"):
|
|
282
|
+
emit_success("Claude Code OAuth: Authenticated")
|
|
283
|
+
expires_at = tokens.get("expires_at")
|
|
284
|
+
if expires_at:
|
|
285
|
+
remaining = max(0, int(expires_at - time.time()))
|
|
286
|
+
hours, minutes = divmod(remaining // 60, 60)
|
|
287
|
+
emit_info(f"Token expires in ~{hours}h {minutes}m")
|
|
288
|
+
|
|
289
|
+
claude_models = [
|
|
290
|
+
name
|
|
291
|
+
for name, cfg in load_claude_models_filtered().items()
|
|
292
|
+
if cfg.get("oauth_source") == "claude-code-plugin"
|
|
293
|
+
]
|
|
294
|
+
if claude_models:
|
|
295
|
+
emit_info(f"Configured Claude Code models: {', '.join(claude_models)}")
|
|
296
|
+
else:
|
|
297
|
+
emit_warning("No Claude Code models configured yet.")
|
|
298
|
+
else:
|
|
299
|
+
emit_warning("Claude Code OAuth: Not authenticated")
|
|
300
|
+
emit_info("Run /claude-code-auth to begin the browser sign-in flow.")
|
|
301
|
+
return True
|
|
302
|
+
|
|
303
|
+
if name == "claude-code-fast":
|
|
304
|
+
from code_muse.config import (
|
|
305
|
+
get_global_model_name,
|
|
306
|
+
set_model_setting,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
active_model = get_global_model_name() or ""
|
|
310
|
+
if not active_model.startswith(CLAUDE_CODE_OAUTH_CONFIG["prefix"]):
|
|
311
|
+
emit_warning(
|
|
312
|
+
"Fast mode only applies to Claude Code models. "
|
|
313
|
+
"Switch to a claude-code-* model first."
|
|
314
|
+
)
|
|
315
|
+
return True
|
|
316
|
+
|
|
317
|
+
currently_on = is_fast_mode_enabled(active_model)
|
|
318
|
+
new_value = not currently_on
|
|
319
|
+
# Config stores bools as string values; "true"/"false" round-trips cleanly
|
|
320
|
+
set_model_setting(active_model, FAST_SETTING_KEY, str(new_value).lower())
|
|
321
|
+
|
|
322
|
+
if new_value:
|
|
323
|
+
emit_success(f"Fast mode ENABLED for {active_model}")
|
|
324
|
+
emit_info(
|
|
325
|
+
"Injecting speed=fast into payloads and fast-mode-2026-02-01 beta header."
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
emit_info(f"Fast mode DISABLED for {active_model}")
|
|
329
|
+
|
|
330
|
+
# Reload agent so the anthropic-beta header update (set at client
|
|
331
|
+
# construction time) takes effect. Payload side is live either way.
|
|
332
|
+
set_model_and_reload_agent(active_model, warn_on_pinned_mismatch=False)
|
|
333
|
+
return True
|
|
334
|
+
|
|
335
|
+
if name == "claude-code-logout":
|
|
336
|
+
token_path = get_token_storage_path()
|
|
337
|
+
if token_path.exists():
|
|
338
|
+
token_path.unlink()
|
|
339
|
+
emit_info("Removed Claude Code OAuth tokens")
|
|
340
|
+
|
|
341
|
+
removed = remove_claude_code_models()
|
|
342
|
+
if removed:
|
|
343
|
+
emit_info(f"Removed {removed} Claude Code models from configuration")
|
|
344
|
+
|
|
345
|
+
emit_success("Claude Code logout complete")
|
|
346
|
+
return True
|
|
347
|
+
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _create_claude_code_model(model_name: str, model_config: dict, config: dict) -> Any:
|
|
352
|
+
"""Create a Claude Code model instance.
|
|
353
|
+
|
|
354
|
+
This handler is registered via the 'register_model_type' callback to handle
|
|
355
|
+
models with type='claude_code'.
|
|
356
|
+
"""
|
|
357
|
+
from anthropic import AsyncAnthropic
|
|
358
|
+
from pydantic_ai.models.anthropic import AnthropicModel
|
|
359
|
+
|
|
360
|
+
from code_muse.claude_cache_client import (
|
|
361
|
+
ClaudeCacheAsyncClient,
|
|
362
|
+
patch_anthropic_client_messages,
|
|
363
|
+
)
|
|
364
|
+
from code_muse.http_utils import get_cert_bundle_path
|
|
365
|
+
from code_muse.model_factory import get_custom_config
|
|
366
|
+
|
|
367
|
+
url, headers, verify, api_key, timeout = get_custom_config(model_config)
|
|
368
|
+
|
|
369
|
+
# Refresh token if this is from the plugin
|
|
370
|
+
if model_config.get("oauth_source") == "claude-code-plugin":
|
|
371
|
+
refreshed_token = get_valid_access_token()
|
|
372
|
+
if refreshed_token:
|
|
373
|
+
api_key = refreshed_token
|
|
374
|
+
custom_endpoint = model_config.get("custom_endpoint")
|
|
375
|
+
if isinstance(custom_endpoint, dict):
|
|
376
|
+
custom_endpoint["api_key"] = refreshed_token
|
|
377
|
+
|
|
378
|
+
if not api_key:
|
|
379
|
+
emit_warning(
|
|
380
|
+
f"API key is not set for Claude Code endpoint; skipping model '{model_config.get('name')}'."
|
|
381
|
+
)
|
|
382
|
+
return None
|
|
383
|
+
|
|
384
|
+
# Check if interleaved thinking is enabled (defaults to True for OAuth models).
|
|
385
|
+
# NOTE: we read via get_all_model_settings (not get_effective_model_settings)
|
|
386
|
+
# because these are plugin-owned settings that aren't in the core
|
|
387
|
+
# supported_settings allowlist and would otherwise be filtered out.
|
|
388
|
+
# See fast_mode.FAST_SETTING_KEY for the full rationale.
|
|
389
|
+
from code_muse.config import get_all_model_settings
|
|
390
|
+
|
|
391
|
+
per_model_settings = get_all_model_settings(model_name)
|
|
392
|
+
interleaved_thinking = per_model_settings.get("interleaved_thinking", True)
|
|
393
|
+
fast_enabled = bool(per_model_settings.get(FAST_SETTING_KEY, False))
|
|
394
|
+
|
|
395
|
+
# Handle anthropic-beta header based on interleaved_thinking setting
|
|
396
|
+
if "anthropic-beta" in headers:
|
|
397
|
+
beta_parts = [p.strip() for p in headers["anthropic-beta"].split(",")]
|
|
398
|
+
if interleaved_thinking:
|
|
399
|
+
if "interleaved-thinking-2025-05-14" not in beta_parts:
|
|
400
|
+
beta_parts.append("interleaved-thinking-2025-05-14")
|
|
401
|
+
else:
|
|
402
|
+
beta_parts = [p for p in beta_parts if "interleaved-thinking" not in p]
|
|
403
|
+
headers["anthropic-beta"] = ",".join(beta_parts) if beta_parts else None
|
|
404
|
+
if headers.get("anthropic-beta") is None:
|
|
405
|
+
del headers["anthropic-beta"]
|
|
406
|
+
elif interleaved_thinking:
|
|
407
|
+
headers["anthropic-beta"] = "interleaved-thinking-2025-05-14"
|
|
408
|
+
|
|
409
|
+
# Add 1M context beta header for long-context models
|
|
410
|
+
from code_muse.model_factory import CONTEXT_1M_BETA
|
|
411
|
+
|
|
412
|
+
if model_config.get("context_length", 0) >= 1_000_000:
|
|
413
|
+
if "anthropic-beta" in headers:
|
|
414
|
+
beta_parts = [p.strip() for p in headers["anthropic-beta"].split(",")]
|
|
415
|
+
if CONTEXT_1M_BETA not in beta_parts:
|
|
416
|
+
beta_parts.append(CONTEXT_1M_BETA)
|
|
417
|
+
headers["anthropic-beta"] = ",".join(beta_parts)
|
|
418
|
+
else:
|
|
419
|
+
headers["anthropic-beta"] = CONTEXT_1M_BETA
|
|
420
|
+
|
|
421
|
+
# Fast mode: append fast-mode-2026-02-01 beta marker when enabled
|
|
422
|
+
ensure_fast_beta_header(headers, fast_enabled)
|
|
423
|
+
|
|
424
|
+
# Use a dedicated client wrapper that injects cache_control on /v1/messages
|
|
425
|
+
if verify is None:
|
|
426
|
+
verify = get_cert_bundle_path()
|
|
427
|
+
|
|
428
|
+
# Disable HTTP/2 for Claude Code OAuth - the UnprefixingStream wrapper
|
|
429
|
+
# that transforms tool names in streaming responses doesn't play well
|
|
430
|
+
# with HTTP/2's compression handling, causing zlib decompression errors.
|
|
431
|
+
client = ClaudeCacheAsyncClient(
|
|
432
|
+
headers=headers,
|
|
433
|
+
verify=verify,
|
|
434
|
+
timeout=180,
|
|
435
|
+
http2=False,
|
|
436
|
+
oauth_reauthentication_callback=lambda: _reauthenticate_after_expired_oauth(
|
|
437
|
+
model_name
|
|
438
|
+
),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
anthropic_client = AsyncAnthropic(
|
|
442
|
+
base_url=url,
|
|
443
|
+
http_client=client,
|
|
444
|
+
auth_token=api_key,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def _update_runtime_token(access_token: str) -> None:
|
|
448
|
+
anthropic_client.auth_token = access_token
|
|
449
|
+
custom_endpoint = model_config.get("custom_endpoint")
|
|
450
|
+
if isinstance(custom_endpoint, dict):
|
|
451
|
+
custom_endpoint["api_key"] = access_token
|
|
452
|
+
|
|
453
|
+
client.set_token_update_callback(_update_runtime_token)
|
|
454
|
+
patch_anthropic_client_messages(anthropic_client)
|
|
455
|
+
# Fast mode wrapper sits outside cache-control injector and re-reads
|
|
456
|
+
# the setting on every call so /claude-code-fast takes effect live.
|
|
457
|
+
patch_anthropic_client_fast_mode(anthropic_client, model_name)
|
|
458
|
+
anthropic_client.api_key = None
|
|
459
|
+
anthropic_client.auth_token = api_key
|
|
460
|
+
provider = make_anthropic_provider(
|
|
461
|
+
resolve_provider_identity(model_name, model_config),
|
|
462
|
+
anthropic_client=anthropic_client,
|
|
463
|
+
)
|
|
464
|
+
return AnthropicModel(model_name=model_config["name"], provider=provider)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _register_model_types() -> list[dict[str, Any]]:
|
|
468
|
+
"""Register the claude_code model type handler."""
|
|
469
|
+
return [{"type": "claude_code", "handler": _create_claude_code_model}]
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
# Global storage for the token refresh heartbeat
|
|
473
|
+
# Using a dict to allow multiple concurrent agent runs (keyed by session_id)
|
|
474
|
+
_active_heartbeats: dict[str, Any] = {}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
async def _on_agent_run_start(
|
|
478
|
+
agent_name: str,
|
|
479
|
+
model_name: str,
|
|
480
|
+
session_id: str | None = None,
|
|
481
|
+
) -> None:
|
|
482
|
+
"""Start token refresh heartbeat for Claude Code OAuth models.
|
|
483
|
+
|
|
484
|
+
This callback is triggered when an agent run starts. If the model is a
|
|
485
|
+
Claude Code OAuth model, we start a background heartbeat to keep the
|
|
486
|
+
token fresh during long-running operations.
|
|
487
|
+
"""
|
|
488
|
+
# Only start heartbeat for Claude Code models
|
|
489
|
+
if not model_name.startswith("claude-code"):
|
|
490
|
+
return
|
|
491
|
+
|
|
492
|
+
try:
|
|
493
|
+
from .token_refresh_heartbeat import TokenRefreshHeartbeat
|
|
494
|
+
|
|
495
|
+
heartbeat = TokenRefreshHeartbeat()
|
|
496
|
+
await heartbeat.start()
|
|
497
|
+
|
|
498
|
+
# Store heartbeat for cleanup, keyed by session_id
|
|
499
|
+
key = session_id or "default"
|
|
500
|
+
_active_heartbeats[key] = heartbeat
|
|
501
|
+
logger.debug(
|
|
502
|
+
"Started token refresh heartbeat for session %s (model: %s)",
|
|
503
|
+
key,
|
|
504
|
+
model_name,
|
|
505
|
+
)
|
|
506
|
+
except ImportError:
|
|
507
|
+
logger.debug("Token refresh heartbeat module not available")
|
|
508
|
+
except Exception as exc:
|
|
509
|
+
logger.debug("Failed to start token refresh heartbeat: %s", exc)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
async def _on_agent_run_end(
|
|
513
|
+
agent_name: str,
|
|
514
|
+
model_name: str,
|
|
515
|
+
session_id: str | None = None,
|
|
516
|
+
success: bool = True,
|
|
517
|
+
error: Exception | None = None,
|
|
518
|
+
response_text: str | None = None,
|
|
519
|
+
metadata: dict[str, Any | None] = None,
|
|
520
|
+
) -> None:
|
|
521
|
+
"""Stop token refresh heartbeat when agent run ends.
|
|
522
|
+
|
|
523
|
+
This callback is triggered when an agent run completes (success or failure).
|
|
524
|
+
We stop any heartbeat that was started for this session.
|
|
525
|
+
"""
|
|
526
|
+
# We don't use response_text or metadata, just cleanup the heartbeat
|
|
527
|
+
key = session_id or "default"
|
|
528
|
+
heartbeat = _active_heartbeats.pop(key, None)
|
|
529
|
+
|
|
530
|
+
if heartbeat is not None:
|
|
531
|
+
try:
|
|
532
|
+
await heartbeat.stop()
|
|
533
|
+
logger.debug(
|
|
534
|
+
"Stopped token refresh heartbeat for session %s (refreshed %d times)",
|
|
535
|
+
key,
|
|
536
|
+
heartbeat.refresh_count,
|
|
537
|
+
)
|
|
538
|
+
except Exception as exc:
|
|
539
|
+
logger.debug("Error stopping token refresh heartbeat: %s", exc)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
register_callback("custom_command_help", _custom_help)
|
|
543
|
+
register_callback("custom_command", _handle_custom_command)
|
|
544
|
+
register_callback("register_model_type", _register_model_types)
|
|
545
|
+
register_callback("prepare_model_prompt", prepare_claude_code_prompt)
|
|
546
|
+
register_callback("agent_run_start", _on_agent_run_start)
|
|
547
|
+
register_callback("agent_run_end", _on_agent_run_end)
|