calvyn-code 0.14.0
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.
- package/LICENSE +21 -0
- package/README.md +217 -0
- package/README.zh-CN.md +180 -0
- package/acp_adapter/__init__.py +1 -0
- package/acp_adapter/__main__.py +5 -0
- package/acp_adapter/auth.py +68 -0
- package/acp_adapter/bootstrap/__init__.py +0 -0
- package/acp_adapter/bootstrap/bootstrap_browser_tools.ps1 +288 -0
- package/acp_adapter/bootstrap/bootstrap_browser_tools.sh +399 -0
- package/acp_adapter/entry.py +292 -0
- package/acp_adapter/events.py +265 -0
- package/acp_adapter/permissions.py +148 -0
- package/acp_adapter/server.py +1713 -0
- package/acp_adapter/session.py +629 -0
- package/acp_adapter/tools.py +1180 -0
- package/agent/__init__.py +6 -0
- package/agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/agent/__pycache__/account_usage.cpython-312.pyc +0 -0
- package/agent/__pycache__/anthropic_adapter.cpython-312.pyc +0 -0
- package/agent/__pycache__/async_utils.cpython-312.pyc +0 -0
- package/agent/__pycache__/auxiliary_client.cpython-312.pyc +0 -0
- package/agent/__pycache__/codex_responses_adapter.cpython-312.pyc +0 -0
- package/agent/__pycache__/context_compressor.cpython-312.pyc +0 -0
- package/agent/__pycache__/context_engine.cpython-312.pyc +0 -0
- package/agent/__pycache__/context_references.cpython-312.pyc +0 -0
- package/agent/__pycache__/credential_pool.cpython-312.pyc +0 -0
- package/agent/__pycache__/curator.cpython-312.pyc +0 -0
- package/agent/__pycache__/display.cpython-312.pyc +0 -0
- package/agent/__pycache__/error_classifier.cpython-312.pyc +0 -0
- package/agent/__pycache__/file_safety.cpython-312.pyc +0 -0
- package/agent/__pycache__/google_code_assist.cpython-312.pyc +0 -0
- package/agent/__pycache__/google_oauth.cpython-312.pyc +0 -0
- package/agent/__pycache__/i18n.cpython-312.pyc +0 -0
- package/agent/__pycache__/image_gen_provider.cpython-312.pyc +0 -0
- package/agent/__pycache__/image_gen_registry.cpython-312.pyc +0 -0
- package/agent/__pycache__/insights.cpython-312.pyc +0 -0
- package/agent/__pycache__/lmstudio_reasoning.cpython-312.pyc +0 -0
- package/agent/__pycache__/manual_compression_feedback.cpython-312.pyc +0 -0
- package/agent/__pycache__/markdown_tables.cpython-312.pyc +0 -0
- package/agent/__pycache__/memory_manager.cpython-312.pyc +0 -0
- package/agent/__pycache__/memory_provider.cpython-312.pyc +0 -0
- package/agent/__pycache__/model_metadata.cpython-312.pyc +0 -0
- package/agent/__pycache__/models_dev.cpython-312.pyc +0 -0
- package/agent/__pycache__/moonshot_schema.cpython-312.pyc +0 -0
- package/agent/__pycache__/onboarding.cpython-312.pyc +0 -0
- package/agent/__pycache__/portal_tags.cpython-312.pyc +0 -0
- package/agent/__pycache__/prompt_builder.cpython-312.pyc +0 -0
- package/agent/__pycache__/prompt_caching.cpython-312.pyc +0 -0
- package/agent/__pycache__/redact.cpython-312.pyc +0 -0
- package/agent/__pycache__/retry_utils.cpython-312.pyc +0 -0
- package/agent/__pycache__/shell_hooks.cpython-312.pyc +0 -0
- package/agent/__pycache__/skill_commands.cpython-312.pyc +0 -0
- package/agent/__pycache__/skill_preprocessing.cpython-312.pyc +0 -0
- package/agent/__pycache__/skill_utils.cpython-312.pyc +0 -0
- package/agent/__pycache__/subdirectory_hints.cpython-312.pyc +0 -0
- package/agent/__pycache__/think_scrubber.cpython-312.pyc +0 -0
- package/agent/__pycache__/title_generator.cpython-312.pyc +0 -0
- package/agent/__pycache__/tool_guardrails.cpython-312.pyc +0 -0
- package/agent/__pycache__/tool_result_classification.cpython-312.pyc +0 -0
- package/agent/__pycache__/trajectory.cpython-312.pyc +0 -0
- package/agent/__pycache__/usage_pricing.cpython-312.pyc +0 -0
- package/agent/__pycache__/video_gen_provider.cpython-312.pyc +0 -0
- package/agent/__pycache__/video_gen_registry.cpython-312.pyc +0 -0
- package/agent/__pycache__/web_search_provider.cpython-312.pyc +0 -0
- package/agent/__pycache__/web_search_registry.cpython-312.pyc +0 -0
- package/agent/account_usage.py +326 -0
- package/agent/anthropic_adapter.py +2087 -0
- package/agent/async_utils.py +68 -0
- package/agent/auxiliary_client.py +4893 -0
- package/agent/bedrock_adapter.py +1276 -0
- package/agent/codex_responses_adapter.py +1084 -0
- package/agent/context_compressor.py +1583 -0
- package/agent/context_engine.py +211 -0
- package/agent/context_references.py +519 -0
- package/agent/copilot_acp_client.py +684 -0
- package/agent/credential_pool.py +1780 -0
- package/agent/credential_sources.py +449 -0
- package/agent/curator.py +1782 -0
- package/agent/curator_backup.py +694 -0
- package/agent/display.py +987 -0
- package/agent/error_classifier.py +1058 -0
- package/agent/file_safety.py +112 -0
- package/agent/gemini_cloudcode_adapter.py +909 -0
- package/agent/gemini_native_adapter.py +971 -0
- package/agent/gemini_schema.py +99 -0
- package/agent/google_code_assist.py +452 -0
- package/agent/google_oauth.py +1062 -0
- package/agent/i18n.py +258 -0
- package/agent/image_gen_provider.py +243 -0
- package/agent/image_gen_registry.py +145 -0
- package/agent/image_routing.py +301 -0
- package/agent/insights.py +931 -0
- package/agent/lmstudio_reasoning.py +48 -0
- package/agent/lsp/__init__.py +106 -0
- package/agent/lsp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/cli.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/client.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/eventlog.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/manager.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/protocol.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/servers.cpython-312.pyc +0 -0
- package/agent/lsp/__pycache__/workspace.cpython-312.pyc +0 -0
- package/agent/lsp/cli.py +308 -0
- package/agent/lsp/client.py +930 -0
- package/agent/lsp/eventlog.py +213 -0
- package/agent/lsp/install.py +376 -0
- package/agent/lsp/manager.py +644 -0
- package/agent/lsp/protocol.py +196 -0
- package/agent/lsp/range_shift.py +149 -0
- package/agent/lsp/reporter.py +78 -0
- package/agent/lsp/servers.py +1040 -0
- package/agent/lsp/workspace.py +223 -0
- package/agent/manual_compression_feedback.py +49 -0
- package/agent/markdown_tables.py +309 -0
- package/agent/memory_manager.py +556 -0
- package/agent/memory_provider.py +279 -0
- package/agent/model_metadata.py +1827 -0
- package/agent/models_dev.py +724 -0
- package/agent/moonshot_schema.py +231 -0
- package/agent/nous_rate_guard.py +326 -0
- package/agent/onboarding.py +193 -0
- package/agent/plugin_llm.py +1046 -0
- package/agent/portal_tags.py +64 -0
- package/agent/prompt_builder.py +1457 -0
- package/agent/prompt_caching.py +79 -0
- package/agent/rate_limit_tracker.py +246 -0
- package/agent/redact.py +403 -0
- package/agent/retry_utils.py +57 -0
- package/agent/shell_hooks.py +837 -0
- package/agent/skill_commands.py +502 -0
- package/agent/skill_preprocessing.py +131 -0
- package/agent/skill_utils.py +512 -0
- package/agent/subdirectory_hints.py +224 -0
- package/agent/think_scrubber.py +386 -0
- package/agent/title_generator.py +171 -0
- package/agent/tool_guardrails.py +458 -0
- package/agent/tool_result_classification.py +26 -0
- package/agent/trajectory.py +56 -0
- package/agent/transports/__init__.py +68 -0
- package/agent/transports/__pycache__/__init__.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/anthropic.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/base.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/bedrock.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/chat_completions.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/codex.cpython-312.pyc +0 -0
- package/agent/transports/__pycache__/types.cpython-312.pyc +0 -0
- package/agent/transports/anthropic.py +179 -0
- package/agent/transports/base.py +89 -0
- package/agent/transports/bedrock.py +154 -0
- package/agent/transports/chat_completions.py +614 -0
- package/agent/transports/codex.py +283 -0
- package/agent/transports/codex_app_server.py +368 -0
- package/agent/transports/codex_app_server_session.py +810 -0
- package/agent/transports/codex_event_projector.py +312 -0
- package/agent/transports/hermes_tools_mcp_server.py +233 -0
- package/agent/transports/types.py +162 -0
- package/agent/usage_pricing.py +877 -0
- package/agent/video_gen_provider.py +300 -0
- package/agent/video_gen_registry.py +117 -0
- package/agent/web_search_provider.py +221 -0
- package/agent/web_search_registry.py +262 -0
- package/assets/banner.png +0 -0
- package/batch_runner.py +1303 -0
- package/bin/calvyn.js +67 -0
- package/calvyn_bootstrap.py +130 -0
- package/calvyn_constants.py +346 -0
- package/calvyn_logging.py +390 -0
- package/calvyn_state.py +2967 -0
- package/calvyn_time.py +105 -0
- package/cli.py +14160 -0
- package/cron/__init__.py +42 -0
- package/cron/__pycache__/__init__.cpython-312.pyc +0 -0
- package/cron/__pycache__/jobs.cpython-312.pyc +0 -0
- package/cron/__pycache__/scheduler.cpython-312.pyc +0 -0
- package/cron/jobs.py +1160 -0
- package/cron/scheduler.py +1832 -0
- package/gateway/__init__.py +35 -0
- package/gateway/__pycache__/__init__.cpython-312.pyc +0 -0
- package/gateway/__pycache__/channel_directory.cpython-312.pyc +0 -0
- package/gateway/__pycache__/config.cpython-312.pyc +0 -0
- package/gateway/__pycache__/delivery.cpython-312.pyc +0 -0
- package/gateway/__pycache__/display_config.cpython-312.pyc +0 -0
- package/gateway/__pycache__/hooks.cpython-312.pyc +0 -0
- package/gateway/__pycache__/pairing.cpython-312.pyc +0 -0
- package/gateway/__pycache__/platform_registry.cpython-312.pyc +0 -0
- package/gateway/__pycache__/restart.cpython-312.pyc +0 -0
- package/gateway/__pycache__/run.cpython-312.pyc +0 -0
- package/gateway/__pycache__/runtime_footer.cpython-312.pyc +0 -0
- package/gateway/__pycache__/session.cpython-312.pyc +0 -0
- package/gateway/__pycache__/session_context.cpython-312.pyc +0 -0
- package/gateway/__pycache__/shutdown_forensics.cpython-312.pyc +0 -0
- package/gateway/__pycache__/slash_access.cpython-312.pyc +0 -0
- package/gateway/__pycache__/status.cpython-312.pyc +0 -0
- package/gateway/__pycache__/stream_consumer.cpython-312.pyc +0 -0
- package/gateway/__pycache__/whatsapp_identity.cpython-312.pyc +0 -0
- package/gateway/assets/telegram-botfather-threads-settings.jpg +0 -0
- package/gateway/builtin_hooks/__init__.py +1 -0
- package/gateway/channel_directory.py +357 -0
- package/gateway/config.py +1873 -0
- package/gateway/delivery.py +258 -0
- package/gateway/display_config.py +206 -0
- package/gateway/hooks.py +210 -0
- package/gateway/mirror.py +179 -0
- package/gateway/pairing.py +322 -0
- package/gateway/platform_registry.py +260 -0
- package/gateway/platforms/ADDING_A_PLATFORM.md +374 -0
- package/gateway/platforms/__init__.py +45 -0
- package/gateway/platforms/__pycache__/__init__.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/base.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/helpers.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/telegram.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/telegram_network.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/yuanbao.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/yuanbao_media.cpython-312.pyc +0 -0
- package/gateway/platforms/__pycache__/yuanbao_proto.cpython-312.pyc +0 -0
- package/gateway/platforms/_http_client_limits.py +84 -0
- package/gateway/platforms/api_server.py +3488 -0
- package/gateway/platforms/base.py +3747 -0
- package/gateway/platforms/bluebubbles.py +937 -0
- package/gateway/platforms/dingtalk.py +1473 -0
- package/gateway/platforms/discord.py +5584 -0
- package/gateway/platforms/email.py +773 -0
- package/gateway/platforms/feishu.py +5059 -0
- package/gateway/platforms/feishu_comment.py +1382 -0
- package/gateway/platforms/feishu_comment_rules.py +430 -0
- package/gateway/platforms/helpers.py +279 -0
- package/gateway/platforms/homeassistant.py +449 -0
- package/gateway/platforms/matrix.py +2777 -0
- package/gateway/platforms/mattermost.py +852 -0
- package/gateway/platforms/msgraph_webhook.py +397 -0
- package/gateway/platforms/qqbot/__init__.py +91 -0
- package/gateway/platforms/qqbot/adapter.py +3072 -0
- package/gateway/platforms/qqbot/chunked_upload.py +602 -0
- package/gateway/platforms/qqbot/constants.py +74 -0
- package/gateway/platforms/qqbot/crypto.py +45 -0
- package/gateway/platforms/qqbot/keyboards.py +473 -0
- package/gateway/platforms/qqbot/onboard.py +220 -0
- package/gateway/platforms/qqbot/utils.py +71 -0
- package/gateway/platforms/signal.py +1518 -0
- package/gateway/platforms/signal_rate_limit.py +369 -0
- package/gateway/platforms/slack.py +3028 -0
- package/gateway/platforms/sms.py +377 -0
- package/gateway/platforms/telegram.py +4836 -0
- package/gateway/platforms/telegram_network.py +249 -0
- package/gateway/platforms/webhook.py +806 -0
- package/gateway/platforms/wecom.py +1610 -0
- package/gateway/platforms/wecom_callback.py +403 -0
- package/gateway/platforms/wecom_crypto.py +142 -0
- package/gateway/platforms/weixin.py +2170 -0
- package/gateway/platforms/whatsapp.py +1283 -0
- package/gateway/platforms/yuanbao.py +4873 -0
- package/gateway/platforms/yuanbao_media.py +645 -0
- package/gateway/platforms/yuanbao_proto.py +1209 -0
- package/gateway/platforms/yuanbao_sticker.py +558 -0
- package/gateway/restart.py +20 -0
- package/gateway/run.py +17074 -0
- package/gateway/runtime_footer.py +150 -0
- package/gateway/session.py +1399 -0
- package/gateway/session_context.py +156 -0
- package/gateway/shutdown_forensics.py +462 -0
- package/gateway/slash_access.py +229 -0
- package/gateway/status.py +972 -0
- package/gateway/sticker_cache.py +111 -0
- package/gateway/stream_consumer.py +1286 -0
- package/gateway/whatsapp_identity.py +156 -0
- package/hermes_cli/__init__.py +47 -0
- package/hermes_cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/_parser.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/auth.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/banner.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/browser_connect.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/callbacks.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/checkpoints.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/cli_output.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/codex_models.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/codex_runtime_switch.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/colors.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/commands.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/config.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/copilot_auth.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/curator.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/curses_ui.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/debug.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/default_soul.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/env_loader.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/fallback_cmd.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/gateway.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/gateway_windows.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/goals.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/inventory.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/kanban.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/kanban_db.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/main.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/model_catalog.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/model_normalize.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/model_switch.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/models.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/nous_subscription.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/pairing.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/platforms.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/plugins.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/profiles.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/providers.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/pt_input_extras.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/runtime_provider.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/security_advisories.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/setup.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/skills_hub.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/skin_engine.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/stdio.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/timeouts.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/tips.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/tools_config.cpython-312.pyc +0 -0
- package/hermes_cli/__pycache__/voice.cpython-312.pyc +0 -0
- package/hermes_cli/_parser.py +365 -0
- package/hermes_cli/_subprocess_compat.py +175 -0
- package/hermes_cli/auth.py +6299 -0
- package/hermes_cli/auth_commands.py +749 -0
- package/hermes_cli/azure_detect.py +300 -0
- package/hermes_cli/backup.py +938 -0
- package/hermes_cli/banner.py +703 -0
- package/hermes_cli/browser_connect.py +139 -0
- package/hermes_cli/callbacks.py +243 -0
- package/hermes_cli/checkpoints.py +244 -0
- package/hermes_cli/claw.py +810 -0
- package/hermes_cli/cli_output.py +78 -0
- package/hermes_cli/clipboard.py +495 -0
- package/hermes_cli/codex_models.py +198 -0
- package/hermes_cli/codex_runtime_plugin_migration.py +757 -0
- package/hermes_cli/codex_runtime_switch.py +266 -0
- package/hermes_cli/colors.py +38 -0
- package/hermes_cli/commands.py +1728 -0
- package/hermes_cli/completion.py +315 -0
- package/hermes_cli/config.py +5382 -0
- package/hermes_cli/copilot_auth.py +392 -0
- package/hermes_cli/cron.py +313 -0
- package/hermes_cli/curator.py +598 -0
- package/hermes_cli/curses_ui.py +472 -0
- package/hermes_cli/debug.py +747 -0
- package/hermes_cli/default_soul.py +11 -0
- package/hermes_cli/dep_ensure.py +107 -0
- package/hermes_cli/dingtalk_auth.py +293 -0
- package/hermes_cli/doctor.py +1863 -0
- package/hermes_cli/dump.py +326 -0
- package/hermes_cli/env_loader.py +175 -0
- package/hermes_cli/fallback_cmd.py +361 -0
- package/hermes_cli/gateway.py +5422 -0
- package/hermes_cli/gateway_windows.py +692 -0
- package/hermes_cli/goals.py +757 -0
- package/hermes_cli/hooks.py +385 -0
- package/hermes_cli/inventory.py +240 -0
- package/hermes_cli/kanban.py +2252 -0
- package/hermes_cli/kanban_db.py +4840 -0
- package/hermes_cli/kanban_diagnostics.py +776 -0
- package/hermes_cli/kanban_specify.py +266 -0
- package/hermes_cli/logs.py +391 -0
- package/hermes_cli/main.py +12396 -0
- package/hermes_cli/mcp_config.py +781 -0
- package/hermes_cli/memory_setup.py +465 -0
- package/hermes_cli/model_catalog.py +330 -0
- package/hermes_cli/model_normalize.py +473 -0
- package/hermes_cli/model_switch.py +1777 -0
- package/hermes_cli/models.py +3789 -0
- package/hermes_cli/nous_subscription.py +799 -0
- package/hermes_cli/oneshot.py +351 -0
- package/hermes_cli/pairing.py +115 -0
- package/hermes_cli/platforms.py +83 -0
- package/hermes_cli/plugins.py +1562 -0
- package/hermes_cli/plugins_cmd.py +1587 -0
- package/hermes_cli/profile_distribution.py +703 -0
- package/hermes_cli/profiles.py +1319 -0
- package/hermes_cli/providers.py +720 -0
- package/hermes_cli/proxy/__init__.py +20 -0
- package/hermes_cli/proxy/adapters/__init__.py +35 -0
- package/hermes_cli/proxy/adapters/base.py +94 -0
- package/hermes_cli/proxy/adapters/nous_portal.py +137 -0
- package/hermes_cli/proxy/cli.py +141 -0
- package/hermes_cli/proxy/server.py +265 -0
- package/hermes_cli/pt_input_extras.py +83 -0
- package/hermes_cli/pty_bridge.py +237 -0
- package/hermes_cli/relaunch.py +205 -0
- package/hermes_cli/runtime_provider.py +1428 -0
- package/hermes_cli/security_advisories.py +452 -0
- package/hermes_cli/setup.py +3559 -0
- package/hermes_cli/skills_config.py +177 -0
- package/hermes_cli/skills_hub.py +1595 -0
- package/hermes_cli/skin_engine.py +929 -0
- package/hermes_cli/slack_cli.py +160 -0
- package/hermes_cli/status.py +550 -0
- package/hermes_cli/stdio.py +252 -0
- package/hermes_cli/timeouts.py +82 -0
- package/hermes_cli/tips.py +487 -0
- package/hermes_cli/tools_config.py +3151 -0
- package/hermes_cli/uninstall.py +681 -0
- package/hermes_cli/vercel_auth.py +70 -0
- package/hermes_cli/voice.py +846 -0
- package/hermes_cli/web_server.py +4438 -0
- package/hermes_cli/webhook.py +275 -0
- package/locales/af.yaml +350 -0
- package/locales/de.yaml +350 -0
- package/locales/en.yaml +365 -0
- package/locales/es.yaml +350 -0
- package/locales/fr.yaml +350 -0
- package/locales/ga.yaml +354 -0
- package/locales/hu.yaml +350 -0
- package/locales/it.yaml +350 -0
- package/locales/ja.yaml +350 -0
- package/locales/ko.yaml +350 -0
- package/locales/pt.yaml +350 -0
- package/locales/ru.yaml +350 -0
- package/locales/tr.yaml +350 -0
- package/locales/uk.yaml +350 -0
- package/locales/zh-hant.yaml +350 -0
- package/locales/zh.yaml +350 -0
- package/mcp_serve.py +898 -0
- package/model_tools.py +899 -0
- package/optional-skills/DESCRIPTION.md +24 -0
- package/optional-skills/autonomous-ai-agents/DESCRIPTION.md +2 -0
- package/optional-skills/autonomous-ai-agents/blackbox/SKILL.md +144 -0
- package/optional-skills/autonomous-ai-agents/honcho/SKILL.md +431 -0
- package/optional-skills/blockchain/evm/SKILL.md +211 -0
- package/optional-skills/blockchain/evm/scripts/evm_client.py +1508 -0
- package/optional-skills/blockchain/hyperliquid/SKILL.md +211 -0
- package/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1660 -0
- package/optional-skills/blockchain/solana/SKILL.md +208 -0
- package/optional-skills/blockchain/solana/scripts/solana_client.py +698 -0
- package/optional-skills/communication/DESCRIPTION.md +1 -0
- package/optional-skills/communication/one-three-one-rule/SKILL.md +104 -0
- package/optional-skills/creative/blender-mcp/SKILL.md +117 -0
- package/optional-skills/creative/concept-diagrams/SKILL.md +362 -0
- package/optional-skills/creative/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
- package/optional-skills/creative/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
- package/optional-skills/creative/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
- package/optional-skills/creative/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
- package/optional-skills/creative/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
- package/optional-skills/creative/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
- package/optional-skills/creative/concept-diagrams/examples/electricity-grid-flow.md +182 -0
- package/optional-skills/creative/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
- package/optional-skills/creative/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
- package/optional-skills/creative/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
- package/optional-skills/creative/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
- package/optional-skills/creative/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
- package/optional-skills/creative/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
- package/optional-skills/creative/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
- package/optional-skills/creative/concept-diagrams/examples/wind-turbine-structure.md +338 -0
- package/optional-skills/creative/concept-diagrams/references/dashboard-patterns.md +43 -0
- package/optional-skills/creative/concept-diagrams/references/infrastructure-patterns.md +144 -0
- package/optional-skills/creative/concept-diagrams/references/physical-shape-cookbook.md +42 -0
- package/optional-skills/creative/concept-diagrams/templates/template.html +174 -0
- package/optional-skills/creative/hyperframes/SKILL.md +191 -0
- package/optional-skills/creative/hyperframes/references/cli.md +185 -0
- package/optional-skills/creative/hyperframes/references/composition.md +129 -0
- package/optional-skills/creative/hyperframes/references/features.md +289 -0
- package/optional-skills/creative/hyperframes/references/gsap.md +136 -0
- package/optional-skills/creative/hyperframes/references/troubleshooting.md +137 -0
- package/optional-skills/creative/hyperframes/references/website-to-video.md +145 -0
- package/optional-skills/creative/hyperframes/scripts/setup.sh +135 -0
- package/optional-skills/creative/kanban-video-orchestrator/SKILL.md +207 -0
- package/optional-skills/creative/kanban-video-orchestrator/assets/brief.md.tmpl +79 -0
- package/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +185 -0
- package/optional-skills/creative/kanban-video-orchestrator/assets/soul.md.tmpl +38 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/examples.md +227 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/intake.md +166 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +276 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/monitoring.md +180 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/role-archetypes.md +298 -0
- package/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +317 -0
- package/optional-skills/creative/kanban-video-orchestrator/scripts/bootstrap_pipeline.py +501 -0
- package/optional-skills/creative/kanban-video-orchestrator/scripts/monitor.py +195 -0
- package/optional-skills/creative/meme-generation/EXAMPLES.md +46 -0
- package/optional-skills/creative/meme-generation/SKILL.md +130 -0
- package/optional-skills/creative/meme-generation/scripts/generate_meme.py +471 -0
- package/optional-skills/creative/meme-generation/scripts/templates.json +97 -0
- package/optional-skills/devops/cli/SKILL.md +156 -0
- package/optional-skills/devops/cli/references/app-discovery.md +112 -0
- package/optional-skills/devops/cli/references/authentication.md +59 -0
- package/optional-skills/devops/cli/references/cli-reference.md +104 -0
- package/optional-skills/devops/cli/references/running-apps.md +171 -0
- package/optional-skills/devops/docker-management/SKILL.md +281 -0
- package/optional-skills/devops/pinggy-tunnel/SKILL.md +309 -0
- package/optional-skills/devops/watchers/SKILL.md +112 -0
- package/optional-skills/devops/watchers/scripts/_watermark.py +148 -0
- package/optional-skills/devops/watchers/scripts/watch_github.py +168 -0
- package/optional-skills/devops/watchers/scripts/watch_http_json.py +131 -0
- package/optional-skills/devops/watchers/scripts/watch_rss.py +121 -0
- package/optional-skills/dogfood/DESCRIPTION.md +3 -0
- package/optional-skills/dogfood/adversarial-ux-test/SKILL.md +191 -0
- package/optional-skills/email/agentmail/SKILL.md +126 -0
- package/optional-skills/finance/3-statement-model/SKILL.md +433 -0
- package/optional-skills/finance/3-statement-model/references/formatting.md +118 -0
- package/optional-skills/finance/3-statement-model/references/formulas.md +292 -0
- package/optional-skills/finance/3-statement-model/references/sec-filings.md +125 -0
- package/optional-skills/finance/comps-analysis/SKILL.md +662 -0
- package/optional-skills/finance/dcf-model/SKILL.md +1270 -0
- package/optional-skills/finance/dcf-model/TROUBLESHOOTING.md +40 -0
- package/optional-skills/finance/dcf-model/requirements.txt +7 -0
- package/optional-skills/finance/dcf-model/scripts/validate_dcf.py +292 -0
- package/optional-skills/finance/excel-author/SKILL.md +244 -0
- package/optional-skills/finance/excel-author/scripts/recalc.py +88 -0
- package/optional-skills/finance/lbo-model/SKILL.md +291 -0
- package/optional-skills/finance/merger-model/SKILL.md +144 -0
- package/optional-skills/finance/pptx-author/SKILL.md +173 -0
- package/optional-skills/finance/stocks/SKILL.md +95 -0
- package/optional-skills/finance/stocks/scripts/stocks_client.py +755 -0
- package/optional-skills/health/DESCRIPTION.md +1 -0
- package/optional-skills/health/fitness-nutrition/SKILL.md +256 -0
- package/optional-skills/health/fitness-nutrition/references/FORMULAS.md +100 -0
- package/optional-skills/health/fitness-nutrition/scripts/body_calc.py +210 -0
- package/optional-skills/health/fitness-nutrition/scripts/nutrition_search.py +86 -0
- package/optional-skills/health/neuroskill-bci/SKILL.md +459 -0
- package/optional-skills/health/neuroskill-bci/references/api.md +286 -0
- package/optional-skills/health/neuroskill-bci/references/metrics.md +220 -0
- package/optional-skills/health/neuroskill-bci/references/protocols.md +452 -0
- package/optional-skills/mcp/DESCRIPTION.md +3 -0
- package/optional-skills/mcp/fastmcp/SKILL.md +300 -0
- package/optional-skills/mcp/fastmcp/references/fastmcp-cli.md +110 -0
- package/optional-skills/mcp/fastmcp/scripts/scaffold_fastmcp.py +56 -0
- package/optional-skills/mcp/fastmcp/templates/api_wrapper.py +54 -0
- package/optional-skills/mcp/fastmcp/templates/database_server.py +77 -0
- package/optional-skills/mcp/fastmcp/templates/file_processor.py +55 -0
- package/optional-skills/mcp/mcporter/SKILL.md +123 -0
- package/optional-skills/migration/DESCRIPTION.md +2 -0
- package/optional-skills/migration/openclaw-migration/SKILL.md +298 -0
- package/optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py +3136 -0
- package/optional-skills/mlops/accelerate/SKILL.md +336 -0
- package/optional-skills/mlops/accelerate/references/custom-plugins.md +453 -0
- package/optional-skills/mlops/accelerate/references/megatron-integration.md +489 -0
- package/optional-skills/mlops/accelerate/references/performance.md +525 -0
- package/optional-skills/mlops/chroma/SKILL.md +410 -0
- package/optional-skills/mlops/chroma/references/integration.md +38 -0
- package/optional-skills/mlops/clip/SKILL.md +257 -0
- package/optional-skills/mlops/clip/references/applications.md +207 -0
- package/optional-skills/mlops/faiss/SKILL.md +225 -0
- package/optional-skills/mlops/faiss/references/index_types.md +280 -0
- package/optional-skills/mlops/flash-attention/SKILL.md +367 -0
- package/optional-skills/mlops/flash-attention/references/benchmarks.md +215 -0
- package/optional-skills/mlops/flash-attention/references/transformers-integration.md +293 -0
- package/optional-skills/mlops/guidance/SKILL.md +576 -0
- package/optional-skills/mlops/guidance/references/backends.md +554 -0
- package/optional-skills/mlops/guidance/references/constraints.md +674 -0
- package/optional-skills/mlops/guidance/references/examples.md +767 -0
- package/optional-skills/mlops/huggingface-tokenizers/SKILL.md +520 -0
- package/optional-skills/mlops/huggingface-tokenizers/references/algorithms.md +653 -0
- package/optional-skills/mlops/huggingface-tokenizers/references/integration.md +637 -0
- package/optional-skills/mlops/huggingface-tokenizers/references/pipeline.md +723 -0
- package/optional-skills/mlops/huggingface-tokenizers/references/training.md +565 -0
- package/optional-skills/mlops/inference/outlines/SKILL.md +656 -0
- package/optional-skills/mlops/inference/outlines/references/backends.md +615 -0
- package/optional-skills/mlops/inference/outlines/references/examples.md +773 -0
- package/optional-skills/mlops/inference/outlines/references/json_generation.md +652 -0
- package/optional-skills/mlops/instructor/SKILL.md +744 -0
- package/optional-skills/mlops/instructor/references/examples.md +107 -0
- package/optional-skills/mlops/instructor/references/providers.md +70 -0
- package/optional-skills/mlops/instructor/references/validation.md +606 -0
- package/optional-skills/mlops/lambda-labs/SKILL.md +549 -0
- package/optional-skills/mlops/lambda-labs/references/advanced-usage.md +611 -0
- package/optional-skills/mlops/lambda-labs/references/troubleshooting.md +530 -0
- package/optional-skills/mlops/llava/SKILL.md +308 -0
- package/optional-skills/mlops/llava/references/training.md +197 -0
- package/optional-skills/mlops/modal/SKILL.md +345 -0
- package/optional-skills/mlops/modal/references/advanced-usage.md +503 -0
- package/optional-skills/mlops/modal/references/troubleshooting.md +494 -0
- package/optional-skills/mlops/nemo-curator/SKILL.md +387 -0
- package/optional-skills/mlops/nemo-curator/references/deduplication.md +87 -0
- package/optional-skills/mlops/nemo-curator/references/filtering.md +102 -0
- package/optional-skills/mlops/peft/SKILL.md +435 -0
- package/optional-skills/mlops/peft/references/advanced-usage.md +514 -0
- package/optional-skills/mlops/peft/references/troubleshooting.md +480 -0
- package/optional-skills/mlops/pinecone/SKILL.md +362 -0
- package/optional-skills/mlops/pinecone/references/deployment.md +181 -0
- package/optional-skills/mlops/pytorch-fsdp/SKILL.md +130 -0
- package/optional-skills/mlops/pytorch-fsdp/references/index.md +7 -0
- package/optional-skills/mlops/pytorch-fsdp/references/other.md +4261 -0
- package/optional-skills/mlops/pytorch-lightning/SKILL.md +350 -0
- package/optional-skills/mlops/pytorch-lightning/references/callbacks.md +436 -0
- package/optional-skills/mlops/pytorch-lightning/references/distributed.md +490 -0
- package/optional-skills/mlops/pytorch-lightning/references/hyperparameter-tuning.md +556 -0
- package/optional-skills/mlops/qdrant/SKILL.md +497 -0
- package/optional-skills/mlops/qdrant/references/advanced-usage.md +648 -0
- package/optional-skills/mlops/qdrant/references/troubleshooting.md +631 -0
- package/optional-skills/mlops/saelens/SKILL.md +390 -0
- package/optional-skills/mlops/saelens/references/README.md +69 -0
- package/optional-skills/mlops/saelens/references/api.md +333 -0
- package/optional-skills/mlops/saelens/references/tutorials.md +318 -0
- package/optional-skills/mlops/simpo/SKILL.md +223 -0
- package/optional-skills/mlops/simpo/references/datasets.md +478 -0
- package/optional-skills/mlops/simpo/references/hyperparameters.md +452 -0
- package/optional-skills/mlops/simpo/references/loss-functions.md +350 -0
- package/optional-skills/mlops/slime/SKILL.md +468 -0
- package/optional-skills/mlops/slime/references/api-reference.md +392 -0
- package/optional-skills/mlops/slime/references/troubleshooting.md +386 -0
- package/optional-skills/mlops/stable-diffusion/SKILL.md +523 -0
- package/optional-skills/mlops/stable-diffusion/references/advanced-usage.md +716 -0
- package/optional-skills/mlops/stable-diffusion/references/troubleshooting.md +555 -0
- package/optional-skills/mlops/tensorrt-llm/SKILL.md +191 -0
- package/optional-skills/mlops/tensorrt-llm/references/multi-gpu.md +298 -0
- package/optional-skills/mlops/tensorrt-llm/references/optimization.md +242 -0
- package/optional-skills/mlops/tensorrt-llm/references/serving.md +470 -0
- package/optional-skills/mlops/torchtitan/SKILL.md +362 -0
- package/optional-skills/mlops/torchtitan/references/checkpoint.md +181 -0
- package/optional-skills/mlops/torchtitan/references/custom-models.md +258 -0
- package/optional-skills/mlops/torchtitan/references/float8.md +133 -0
- package/optional-skills/mlops/torchtitan/references/fsdp.md +126 -0
- package/optional-skills/mlops/training/axolotl/SKILL.md +166 -0
- package/optional-skills/mlops/training/axolotl/references/api.md +5548 -0
- package/optional-skills/mlops/training/axolotl/references/dataset-formats.md +1029 -0
- package/optional-skills/mlops/training/axolotl/references/index.md +15 -0
- package/optional-skills/mlops/training/axolotl/references/other.md +3563 -0
- package/optional-skills/mlops/training/trl-fine-tuning/SKILL.md +463 -0
- package/optional-skills/mlops/training/trl-fine-tuning/references/dpo-variants.md +227 -0
- package/optional-skills/mlops/training/trl-fine-tuning/references/grpo-training.md +504 -0
- package/optional-skills/mlops/training/trl-fine-tuning/references/online-rl.md +82 -0
- package/optional-skills/mlops/training/trl-fine-tuning/references/reward-modeling.md +122 -0
- package/optional-skills/mlops/training/trl-fine-tuning/references/sft-training.md +168 -0
- package/optional-skills/mlops/training/trl-fine-tuning/templates/basic_grpo_training.py +228 -0
- package/optional-skills/mlops/training/unsloth/SKILL.md +84 -0
- package/optional-skills/mlops/training/unsloth/references/index.md +7 -0
- package/optional-skills/mlops/training/unsloth/references/llms-full.md +16799 -0
- package/optional-skills/mlops/training/unsloth/references/llms-txt.md +12044 -0
- package/optional-skills/mlops/training/unsloth/references/llms.md +82 -0
- package/optional-skills/mlops/whisper/SKILL.md +321 -0
- package/optional-skills/mlops/whisper/references/languages.md +189 -0
- package/optional-skills/productivity/canvas/SKILL.md +98 -0
- package/optional-skills/productivity/canvas/scripts/canvas_api.py +157 -0
- package/optional-skills/productivity/here-now/SKILL.md +217 -0
- package/optional-skills/productivity/here-now/scripts/drive.sh +406 -0
- package/optional-skills/productivity/here-now/scripts/publish.sh +445 -0
- package/optional-skills/productivity/memento-flashcards/SKILL.md +324 -0
- package/optional-skills/productivity/memento-flashcards/scripts/memento_cards.py +353 -0
- package/optional-skills/productivity/memento-flashcards/scripts/youtube_quiz.py +88 -0
- package/optional-skills/productivity/shop-app/SKILL.md +340 -0
- package/optional-skills/productivity/shopify/SKILL.md +373 -0
- package/optional-skills/productivity/siyuan/SKILL.md +298 -0
- package/optional-skills/productivity/telephony/SKILL.md +418 -0
- package/optional-skills/productivity/telephony/scripts/telephony.py +1343 -0
- package/optional-skills/research/bioinformatics/SKILL.md +235 -0
- package/optional-skills/research/darwinian-evolver/SKILL.md +199 -0
- package/optional-skills/research/darwinian-evolver/scripts/parrot_openrouter.py +218 -0
- package/optional-skills/research/darwinian-evolver/scripts/show_snapshot.py +69 -0
- package/optional-skills/research/darwinian-evolver/templates/custom_problem_template.py +240 -0
- package/optional-skills/research/domain-intel/SKILL.md +97 -0
- package/optional-skills/research/domain-intel/scripts/domain_intel.py +397 -0
- package/optional-skills/research/drug-discovery/SKILL.md +227 -0
- package/optional-skills/research/drug-discovery/references/ADMET_REFERENCE.md +66 -0
- package/optional-skills/research/drug-discovery/scripts/chembl_target.py +53 -0
- package/optional-skills/research/drug-discovery/scripts/ro5_screen.py +44 -0
- package/optional-skills/research/duckduckgo-search/SKILL.md +238 -0
- package/optional-skills/research/duckduckgo-search/scripts/duckduckgo.sh +28 -0
- package/optional-skills/research/gitnexus-explorer/SKILL.md +214 -0
- package/optional-skills/research/gitnexus-explorer/scripts/proxy.mjs +92 -0
- package/optional-skills/research/osint-investigation/SKILL.md +277 -0
- package/optional-skills/research/osint-investigation/references/sources/courtlistener.md +98 -0
- package/optional-skills/research/osint-investigation/references/sources/gdelt.md +104 -0
- package/optional-skills/research/osint-investigation/references/sources/icij-offshore.md +104 -0
- package/optional-skills/research/osint-investigation/references/sources/nyc-acris.md +90 -0
- package/optional-skills/research/osint-investigation/references/sources/ofac-sdn.md +92 -0
- package/optional-skills/research/osint-investigation/references/sources/opencorporates.md +103 -0
- package/optional-skills/research/osint-investigation/references/sources/sec-edgar.md +83 -0
- package/optional-skills/research/osint-investigation/references/sources/senate-ld.md +89 -0
- package/optional-skills/research/osint-investigation/references/sources/usaspending.md +97 -0
- package/optional-skills/research/osint-investigation/references/sources/wayback.md +93 -0
- package/optional-skills/research/osint-investigation/references/sources/wikipedia.md +107 -0
- package/optional-skills/research/osint-investigation/scripts/_http.py +82 -0
- package/optional-skills/research/osint-investigation/scripts/_normalize.py +67 -0
- package/optional-skills/research/osint-investigation/scripts/build_findings.py +221 -0
- package/optional-skills/research/osint-investigation/scripts/entity_resolution.py +228 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_courtlistener.py +149 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_gdelt.py +162 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_opencorporates.py +192 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_senate_ld.py +146 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_usaspending.py +170 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_wayback.py +142 -0
- package/optional-skills/research/osint-investigation/scripts/fetch_wikipedia.py +267 -0
- package/optional-skills/research/osint-investigation/scripts/timing_analysis.py +253 -0
- package/optional-skills/research/osint-investigation/templates/source-template.md +59 -0
- package/optional-skills/research/parallel-cli/SKILL.md +391 -0
- package/optional-skills/research/qmd/SKILL.md +441 -0
- package/optional-skills/research/scrapling/SKILL.md +336 -0
- package/optional-skills/research/searxng-search/SKILL.md +212 -0
- package/optional-skills/research/searxng-search/scripts/searxng.sh +22 -0
- package/optional-skills/security/1password/SKILL.md +163 -0
- package/optional-skills/security/1password/references/cli-examples.md +31 -0
- package/optional-skills/security/1password/references/get-started.md +21 -0
- package/optional-skills/security/DESCRIPTION.md +3 -0
- package/optional-skills/security/oss-forensics/SKILL.md +423 -0
- package/optional-skills/security/oss-forensics/references/evidence-types.md +89 -0
- package/optional-skills/security/oss-forensics/references/github-archive-guide.md +184 -0
- package/optional-skills/security/oss-forensics/references/investigation-templates.md +131 -0
- package/optional-skills/security/oss-forensics/references/recovery-techniques.md +164 -0
- package/optional-skills/security/oss-forensics/scripts/evidence-store.py +313 -0
- package/optional-skills/security/oss-forensics/templates/forensic-report.md +151 -0
- package/optional-skills/security/oss-forensics/templates/malicious-package-report.md +43 -0
- package/optional-skills/security/sherlock/SKILL.md +193 -0
- package/optional-skills/software-development/rest-graphql-debug/SKILL.md +514 -0
- package/optional-skills/web-development/DESCRIPTION.md +5 -0
- package/optional-skills/web-development/page-agent/SKILL.md +190 -0
- package/package.json +78 -0
- package/plugins/__init__.py +1 -0
- package/plugins/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/context_engine/__init__.py +219 -0
- package/plugins/disk-cleanup/README.md +51 -0
- package/plugins/disk-cleanup/__init__.py +316 -0
- package/plugins/disk-cleanup/disk_cleanup.py +497 -0
- package/plugins/disk-cleanup/plugin.yaml +7 -0
- package/plugins/example-dashboard/dashboard/manifest.json +14 -0
- package/plugins/example-dashboard/dashboard/plugin_api.py +17 -0
- package/plugins/google_meet/README.md +131 -0
- package/plugins/google_meet/SKILL.md +148 -0
- package/plugins/google_meet/__init__.py +103 -0
- package/plugins/google_meet/audio_bridge.py +244 -0
- package/plugins/google_meet/cli.py +479 -0
- package/plugins/google_meet/meet_bot.py +852 -0
- package/plugins/google_meet/node/__init__.py +54 -0
- package/plugins/google_meet/node/cli.py +125 -0
- package/plugins/google_meet/node/client.py +107 -0
- package/plugins/google_meet/node/protocol.py +124 -0
- package/plugins/google_meet/node/registry.py +113 -0
- package/plugins/google_meet/node/server.py +201 -0
- package/plugins/google_meet/plugin.yaml +16 -0
- package/plugins/google_meet/process_manager.py +324 -0
- package/plugins/google_meet/realtime/__init__.py +10 -0
- package/plugins/google_meet/realtime/openai_client.py +332 -0
- package/plugins/google_meet/tools.py +348 -0
- package/plugins/hermes-achievements/LICENSE +21 -0
- package/plugins/hermes-achievements/README.md +150 -0
- package/plugins/hermes-achievements/dashboard/dist/index.js +732 -0
- package/plugins/hermes-achievements/dashboard/dist/style.css +146 -0
- package/plugins/hermes-achievements/dashboard/manifest.json +11 -0
- package/plugins/hermes-achievements/dashboard/plugin_api.py +1062 -0
- package/plugins/hermes-achievements/docs/achievements-performance-implementation-plan.md +157 -0
- package/plugins/hermes-achievements/docs/achievements-performance-implementation-spec.md +219 -0
- package/plugins/hermes-achievements/docs/achievements-performance-spec.md +174 -0
- package/plugins/hermes-achievements/docs/assets/achievements-dashboard-hd.png +0 -0
- package/plugins/hermes-achievements/docs/assets/achievements-tier-showcase-hd.png +0 -0
- package/plugins/hermes-achievements/tests/test_achievement_engine.py +156 -0
- package/plugins/image_gen/openai/__init__.py +303 -0
- package/plugins/image_gen/openai/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/image_gen/openai/plugin.yaml +7 -0
- package/plugins/image_gen/openai-codex/__init__.py +378 -0
- package/plugins/image_gen/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/image_gen/openai-codex/plugin.yaml +5 -0
- package/plugins/image_gen/xai/__init__.py +316 -0
- package/plugins/image_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/image_gen/xai/plugin.yaml +7 -0
- package/plugins/kanban/dashboard/dist/index.js +3143 -0
- package/plugins/kanban/dashboard/dist/style.css +1500 -0
- package/plugins/kanban/dashboard/manifest.json +14 -0
- package/plugins/kanban/dashboard/plugin_api.py +1612 -0
- package/plugins/kanban/systemd/hermes-kanban-dispatcher.service +32 -0
- package/plugins/memory/__init__.py +408 -0
- package/plugins/memory/byterover/README.md +41 -0
- package/plugins/memory/byterover/__init__.py +384 -0
- package/plugins/memory/byterover/plugin.yaml +9 -0
- package/plugins/memory/hindsight/README.md +138 -0
- package/plugins/memory/hindsight/__init__.py +1758 -0
- package/plugins/memory/hindsight/plugin.yaml +8 -0
- package/plugins/memory/holographic/README.md +36 -0
- package/plugins/memory/holographic/__init__.py +409 -0
- package/plugins/memory/holographic/holographic.py +203 -0
- package/plugins/memory/holographic/plugin.yaml +5 -0
- package/plugins/memory/holographic/retrieval.py +593 -0
- package/plugins/memory/holographic/store.py +579 -0
- package/plugins/memory/honcho/README.md +328 -0
- package/plugins/memory/honcho/__init__.py +1329 -0
- package/plugins/memory/honcho/cli.py +1452 -0
- package/plugins/memory/honcho/client.py +784 -0
- package/plugins/memory/honcho/plugin.yaml +7 -0
- package/plugins/memory/honcho/session.py +1255 -0
- package/plugins/memory/mem0/README.md +38 -0
- package/plugins/memory/mem0/__init__.py +374 -0
- package/plugins/memory/mem0/plugin.yaml +5 -0
- package/plugins/memory/openviking/README.md +40 -0
- package/plugins/memory/openviking/__init__.py +945 -0
- package/plugins/memory/openviking/plugin.yaml +9 -0
- package/plugins/memory/retaindb/README.md +40 -0
- package/plugins/memory/retaindb/__init__.py +767 -0
- package/plugins/memory/retaindb/plugin.yaml +7 -0
- package/plugins/memory/supermemory/README.md +99 -0
- package/plugins/memory/supermemory/__init__.py +792 -0
- package/plugins/memory/supermemory/plugin.yaml +5 -0
- package/plugins/model-providers/README.md +70 -0
- package/plugins/model-providers/ai-gateway/__init__.py +43 -0
- package/plugins/model-providers/ai-gateway/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/ai-gateway/plugin.yaml +5 -0
- package/plugins/model-providers/alibaba/__init__.py +13 -0
- package/plugins/model-providers/alibaba/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/alibaba/plugin.yaml +5 -0
- package/plugins/model-providers/alibaba-coding-plan/__init__.py +21 -0
- package/plugins/model-providers/alibaba-coding-plan/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/alibaba-coding-plan/plugin.yaml +5 -0
- package/plugins/model-providers/anthropic/__init__.py +52 -0
- package/plugins/model-providers/anthropic/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/anthropic/plugin.yaml +5 -0
- package/plugins/model-providers/arcee/__init__.py +13 -0
- package/plugins/model-providers/arcee/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/arcee/plugin.yaml +5 -0
- package/plugins/model-providers/azure-foundry/__init__.py +21 -0
- package/plugins/model-providers/azure-foundry/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/azure-foundry/plugin.yaml +5 -0
- package/plugins/model-providers/bedrock/__init__.py +29 -0
- package/plugins/model-providers/bedrock/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/bedrock/plugin.yaml +5 -0
- package/plugins/model-providers/copilot/__init__.py +58 -0
- package/plugins/model-providers/copilot/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/copilot/plugin.yaml +5 -0
- package/plugins/model-providers/copilot-acp/__init__.py +34 -0
- package/plugins/model-providers/copilot-acp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/copilot-acp/plugin.yaml +5 -0
- package/plugins/model-providers/custom/__init__.py +68 -0
- package/plugins/model-providers/custom/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/custom/plugin.yaml +5 -0
- package/plugins/model-providers/deepseek/__init__.py +99 -0
- package/plugins/model-providers/deepseek/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/deepseek/plugin.yaml +5 -0
- package/plugins/model-providers/gemini/__init__.py +72 -0
- package/plugins/model-providers/gemini/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/gemini/plugin.yaml +5 -0
- package/plugins/model-providers/gmi/__init__.py +31 -0
- package/plugins/model-providers/gmi/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/gmi/plugin.yaml +5 -0
- package/plugins/model-providers/huggingface/__init__.py +20 -0
- package/plugins/model-providers/huggingface/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/huggingface/plugin.yaml +5 -0
- package/plugins/model-providers/kilocode/__init__.py +14 -0
- package/plugins/model-providers/kilocode/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/kilocode/plugin.yaml +5 -0
- package/plugins/model-providers/kimi-coding/__init__.py +71 -0
- package/plugins/model-providers/kimi-coding/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/kimi-coding/plugin.yaml +5 -0
- package/plugins/model-providers/minimax/__init__.py +45 -0
- package/plugins/model-providers/minimax/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/minimax/plugin.yaml +5 -0
- package/plugins/model-providers/nous/__init__.py +54 -0
- package/plugins/model-providers/nous/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/nous/plugin.yaml +5 -0
- package/plugins/model-providers/novita/__init__.py +27 -0
- package/plugins/model-providers/novita/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/novita/plugin.yaml +5 -0
- package/plugins/model-providers/nvidia/__init__.py +21 -0
- package/plugins/model-providers/nvidia/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/nvidia/plugin.yaml +5 -0
- package/plugins/model-providers/ollama-cloud/__init__.py +14 -0
- package/plugins/model-providers/ollama-cloud/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/ollama-cloud/plugin.yaml +5 -0
- package/plugins/model-providers/openai-codex/__init__.py +15 -0
- package/plugins/model-providers/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/openai-codex/plugin.yaml +5 -0
- package/plugins/model-providers/opencode-zen/__init__.py +30 -0
- package/plugins/model-providers/opencode-zen/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/opencode-zen/plugin.yaml +5 -0
- package/plugins/model-providers/openrouter/__init__.py +115 -0
- package/plugins/model-providers/openrouter/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/openrouter/plugin.yaml +5 -0
- package/plugins/model-providers/qwen-oauth/__init__.py +82 -0
- package/plugins/model-providers/qwen-oauth/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/qwen-oauth/plugin.yaml +5 -0
- package/plugins/model-providers/stepfun/__init__.py +14 -0
- package/plugins/model-providers/stepfun/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/stepfun/plugin.yaml +5 -0
- package/plugins/model-providers/xai/__init__.py +15 -0
- package/plugins/model-providers/xai/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/xai/plugin.yaml +5 -0
- package/plugins/model-providers/xiaomi/__init__.py +14 -0
- package/plugins/model-providers/xiaomi/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/xiaomi/plugin.yaml +5 -0
- package/plugins/model-providers/zai/__init__.py +21 -0
- package/plugins/model-providers/zai/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/model-providers/zai/plugin.yaml +5 -0
- package/plugins/observability/langfuse/README.md +53 -0
- package/plugins/observability/langfuse/__init__.py +1004 -0
- package/plugins/observability/langfuse/plugin.yaml +14 -0
- package/plugins/platforms/google_chat/__init__.py +3 -0
- package/plugins/platforms/google_chat/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/platforms/google_chat/__pycache__/adapter.cpython-312.pyc +0 -0
- package/plugins/platforms/google_chat/adapter.py +3343 -0
- package/plugins/platforms/google_chat/oauth.py +639 -0
- package/plugins/platforms/google_chat/plugin.yaml +39 -0
- package/plugins/platforms/irc/__init__.py +3 -0
- package/plugins/platforms/irc/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/platforms/irc/__pycache__/adapter.cpython-312.pyc +0 -0
- package/plugins/platforms/irc/adapter.py +969 -0
- package/plugins/platforms/irc/plugin.yaml +54 -0
- package/plugins/platforms/line/__init__.py +3 -0
- package/plugins/platforms/line/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/platforms/line/__pycache__/adapter.cpython-312.pyc +0 -0
- package/plugins/platforms/line/adapter.py +1639 -0
- package/plugins/platforms/line/plugin.yaml +65 -0
- package/plugins/platforms/simplex/__init__.py +3 -0
- package/plugins/platforms/simplex/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/platforms/simplex/__pycache__/adapter.cpython-312.pyc +0 -0
- package/plugins/platforms/simplex/adapter.py +746 -0
- package/plugins/platforms/simplex/plugin.yaml +37 -0
- package/plugins/platforms/teams/__init__.py +3 -0
- package/plugins/platforms/teams/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/platforms/teams/__pycache__/adapter.cpython-312.pyc +0 -0
- package/plugins/platforms/teams/adapter.py +1188 -0
- package/plugins/platforms/teams/plugin.yaml +48 -0
- package/plugins/spotify/__init__.py +66 -0
- package/plugins/spotify/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/spotify/__pycache__/client.cpython-312.pyc +0 -0
- package/plugins/spotify/__pycache__/tools.cpython-312.pyc +0 -0
- package/plugins/spotify/client.py +435 -0
- package/plugins/spotify/plugin.yaml +13 -0
- package/plugins/spotify/tools.py +454 -0
- package/plugins/teams_pipeline/__init__.py +23 -0
- package/plugins/teams_pipeline/cli.py +463 -0
- package/plugins/teams_pipeline/meetings.py +333 -0
- package/plugins/teams_pipeline/models.py +350 -0
- package/plugins/teams_pipeline/pipeline.py +692 -0
- package/plugins/teams_pipeline/plugin.yaml +9 -0
- package/plugins/teams_pipeline/runtime.py +135 -0
- package/plugins/teams_pipeline/store.py +194 -0
- package/plugins/teams_pipeline/subscriptions.py +249 -0
- package/plugins/video_gen/fal/__init__.py +523 -0
- package/plugins/video_gen/fal/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/video_gen/fal/plugin.yaml +7 -0
- package/plugins/video_gen/xai/__init__.py +441 -0
- package/plugins/video_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/video_gen/xai/plugin.yaml +7 -0
- package/plugins/web/__init__.py +7 -0
- package/plugins/web/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/brave_free/__init__.py +14 -0
- package/plugins/web/brave_free/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/brave_free/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/brave_free/plugin.yaml +7 -0
- package/plugins/web/brave_free/provider.py +137 -0
- package/plugins/web/ddgs/__init__.py +15 -0
- package/plugins/web/ddgs/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/ddgs/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/ddgs/plugin.yaml +7 -0
- package/plugins/web/ddgs/provider.py +104 -0
- package/plugins/web/exa/__init__.py +15 -0
- package/plugins/web/exa/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/exa/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/exa/plugin.yaml +7 -0
- package/plugins/web/exa/provider.py +212 -0
- package/plugins/web/firecrawl/__init__.py +28 -0
- package/plugins/web/firecrawl/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/firecrawl/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/firecrawl/plugin.yaml +7 -0
- package/plugins/web/firecrawl/provider.py +773 -0
- package/plugins/web/parallel/__init__.py +16 -0
- package/plugins/web/parallel/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/parallel/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/parallel/plugin.yaml +7 -0
- package/plugins/web/parallel/provider.py +291 -0
- package/plugins/web/searxng/__init__.py +15 -0
- package/plugins/web/searxng/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/searxng/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/searxng/plugin.yaml +7 -0
- package/plugins/web/searxng/provider.py +140 -0
- package/plugins/web/tavily/__init__.py +15 -0
- package/plugins/web/tavily/__pycache__/__init__.cpython-312.pyc +0 -0
- package/plugins/web/tavily/__pycache__/provider.cpython-312.pyc +0 -0
- package/plugins/web/tavily/plugin.yaml +7 -0
- package/plugins/web/tavily/provider.py +285 -0
- package/providers/README.md +78 -0
- package/providers/__init__.py +192 -0
- package/providers/__pycache__/__init__.cpython-312.pyc +0 -0
- package/providers/__pycache__/base.cpython-312.pyc +0 -0
- package/providers/base.py +184 -0
- package/pyproject.toml +255 -0
- package/run_agent.py +16409 -0
- package/scripts/benchmark_browser_eval.py +138 -0
- package/scripts/build_model_catalog.py +95 -0
- package/scripts/build_skills_index.py +325 -0
- package/scripts/check-windows-footguns.py +624 -0
- package/scripts/contributor_audit.py +473 -0
- package/scripts/discord-voice-doctor.py +396 -0
- package/scripts/hermes-gateway +416 -0
- package/scripts/install.cmd +28 -0
- package/scripts/install.ps1 +1611 -0
- package/scripts/install.sh +2007 -0
- package/scripts/install_psutil_android.py +117 -0
- package/scripts/keystroke_diagnostic.py +81 -0
- package/scripts/kill_modal.sh +34 -0
- package/scripts/lib/node-bootstrap.sh +238 -0
- package/scripts/lint_diff.py +207 -0
- package/scripts/postinstall.js +150 -0
- package/scripts/profile-tui.py +626 -0
- package/scripts/release.py +1680 -0
- package/scripts/run_tests.sh +129 -0
- package/scripts/sample_and_compress.py +409 -0
- package/scripts/setup_open_webui.sh +349 -0
- package/scripts/whatsapp-bridge/allowlist.js +88 -0
- package/scripts/whatsapp-bridge/allowlist.test.mjs +80 -0
- package/scripts/whatsapp-bridge/bridge.js +729 -0
- package/scripts/whatsapp-bridge/package-lock.json +2141 -0
- package/scripts/whatsapp-bridge/package.json +19 -0
- package/skills/apple/DESCRIPTION.md +2 -0
- package/skills/apple/apple-notes/SKILL.md +90 -0
- package/skills/apple/apple-reminders/SKILL.md +98 -0
- package/skills/apple/findmy/SKILL.md +131 -0
- package/skills/apple/imessage/SKILL.md +102 -0
- package/skills/apple/macos-computer-use/SKILL.md +201 -0
- package/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
- package/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
- package/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
- package/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1014 -0
- package/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
- package/skills/creative/DESCRIPTION.md +3 -0
- package/skills/creative/architecture-diagram/SKILL.md +148 -0
- package/skills/creative/architecture-diagram/templates/template.html +319 -0
- package/skills/creative/ascii-art/SKILL.md +322 -0
- package/skills/creative/ascii-video/README.md +290 -0
- package/skills/creative/ascii-video/SKILL.md +241 -0
- package/skills/creative/ascii-video/references/architecture.md +802 -0
- package/skills/creative/ascii-video/references/composition.md +892 -0
- package/skills/creative/ascii-video/references/effects.md +1865 -0
- package/skills/creative/ascii-video/references/inputs.md +685 -0
- package/skills/creative/ascii-video/references/optimization.md +688 -0
- package/skills/creative/ascii-video/references/scenes.md +1011 -0
- package/skills/creative/ascii-video/references/shaders.md +1385 -0
- package/skills/creative/ascii-video/references/troubleshooting.md +367 -0
- package/skills/creative/baoyu-comic/PORT_NOTES.md +77 -0
- package/skills/creative/baoyu-comic/SKILL.md +247 -0
- package/skills/creative/baoyu-comic/references/analysis-framework.md +176 -0
- package/skills/creative/baoyu-comic/references/art-styles/chalk.md +101 -0
- package/skills/creative/baoyu-comic/references/art-styles/ink-brush.md +97 -0
- package/skills/creative/baoyu-comic/references/art-styles/ligne-claire.md +75 -0
- package/skills/creative/baoyu-comic/references/art-styles/manga.md +93 -0
- package/skills/creative/baoyu-comic/references/art-styles/minimalist.md +84 -0
- package/skills/creative/baoyu-comic/references/art-styles/realistic.md +89 -0
- package/skills/creative/baoyu-comic/references/auto-selection.md +71 -0
- package/skills/creative/baoyu-comic/references/base-prompt.md +98 -0
- package/skills/creative/baoyu-comic/references/character-template.md +180 -0
- package/skills/creative/baoyu-comic/references/layouts/cinematic.md +23 -0
- package/skills/creative/baoyu-comic/references/layouts/dense.md +23 -0
- package/skills/creative/baoyu-comic/references/layouts/four-panel.md +40 -0
- package/skills/creative/baoyu-comic/references/layouts/mixed.md +23 -0
- package/skills/creative/baoyu-comic/references/layouts/splash.md +23 -0
- package/skills/creative/baoyu-comic/references/layouts/standard.md +23 -0
- package/skills/creative/baoyu-comic/references/layouts/webtoon.md +30 -0
- package/skills/creative/baoyu-comic/references/ohmsha-guide.md +85 -0
- package/skills/creative/baoyu-comic/references/partial-workflows.md +106 -0
- package/skills/creative/baoyu-comic/references/presets/concept-story.md +121 -0
- package/skills/creative/baoyu-comic/references/presets/four-panel.md +107 -0
- package/skills/creative/baoyu-comic/references/presets/ohmsha.md +114 -0
- package/skills/creative/baoyu-comic/references/presets/shoujo.md +116 -0
- package/skills/creative/baoyu-comic/references/presets/wuxia.md +110 -0
- package/skills/creative/baoyu-comic/references/storyboard-template.md +143 -0
- package/skills/creative/baoyu-comic/references/tones/action.md +110 -0
- package/skills/creative/baoyu-comic/references/tones/dramatic.md +95 -0
- package/skills/creative/baoyu-comic/references/tones/energetic.md +105 -0
- package/skills/creative/baoyu-comic/references/tones/neutral.md +63 -0
- package/skills/creative/baoyu-comic/references/tones/romantic.md +100 -0
- package/skills/creative/baoyu-comic/references/tones/vintage.md +104 -0
- package/skills/creative/baoyu-comic/references/tones/warm.md +94 -0
- package/skills/creative/baoyu-comic/references/workflow.md +401 -0
- package/skills/creative/baoyu-infographic/PORT_NOTES.md +43 -0
- package/skills/creative/baoyu-infographic/SKILL.md +237 -0
- package/skills/creative/baoyu-infographic/references/analysis-framework.md +182 -0
- package/skills/creative/baoyu-infographic/references/base-prompt.md +43 -0
- package/skills/creative/baoyu-infographic/references/layouts/bento-grid.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/binary-comparison.md +48 -0
- package/skills/creative/baoyu-infographic/references/layouts/bridge.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/circular-flow.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/comic-strip.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/comparison-matrix.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/dashboard.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/dense-modules.md +72 -0
- package/skills/creative/baoyu-infographic/references/layouts/funnel.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/hierarchical-layers.md +48 -0
- package/skills/creative/baoyu-infographic/references/layouts/hub-spoke.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/iceberg.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/isometric-map.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/jigsaw.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/linear-progression.md +48 -0
- package/skills/creative/baoyu-infographic/references/layouts/periodic-table.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/story-mountain.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/structural-breakdown.md +48 -0
- package/skills/creative/baoyu-infographic/references/layouts/tree-branching.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/venn-diagram.md +41 -0
- package/skills/creative/baoyu-infographic/references/layouts/winding-roadmap.md +41 -0
- package/skills/creative/baoyu-infographic/references/structured-content-template.md +244 -0
- package/skills/creative/baoyu-infographic/references/styles/aged-academia.md +36 -0
- package/skills/creative/baoyu-infographic/references/styles/bold-graphic.md +36 -0
- package/skills/creative/baoyu-infographic/references/styles/chalkboard.md +61 -0
- package/skills/creative/baoyu-infographic/references/styles/claymation.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/corporate-memphis.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/craft-handmade.md +44 -0
- package/skills/creative/baoyu-infographic/references/styles/cyberpunk-neon.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/hand-drawn-edu.md +63 -0
- package/skills/creative/baoyu-infographic/references/styles/ikea-manual.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/kawaii.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/knolling.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/lego-brick.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/morandi-journal.md +60 -0
- package/skills/creative/baoyu-infographic/references/styles/origami.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/pixel-art.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/pop-laboratory.md +48 -0
- package/skills/creative/baoyu-infographic/references/styles/retro-pop-grid.md +47 -0
- package/skills/creative/baoyu-infographic/references/styles/storybook-watercolor.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/subway-map.md +29 -0
- package/skills/creative/baoyu-infographic/references/styles/technical-schematic.md +36 -0
- package/skills/creative/baoyu-infographic/references/styles/ui-wireframe.md +29 -0
- package/skills/creative/claude-design/SKILL.md +591 -0
- package/skills/creative/comfyui/SKILL.md +612 -0
- package/skills/creative/comfyui/references/official-cli.md +255 -0
- package/skills/creative/comfyui/references/rest-api.md +312 -0
- package/skills/creative/comfyui/references/template-integrity.md +243 -0
- package/skills/creative/comfyui/references/workflow-format.md +226 -0
- package/skills/creative/comfyui/scripts/_common.py +835 -0
- package/skills/creative/comfyui/scripts/auto_fix_deps.py +225 -0
- package/skills/creative/comfyui/scripts/check_deps.py +437 -0
- package/skills/creative/comfyui/scripts/comfyui_setup.sh +286 -0
- package/skills/creative/comfyui/scripts/extract_schema.py +315 -0
- package/skills/creative/comfyui/scripts/fetch_logs.py +158 -0
- package/skills/creative/comfyui/scripts/hardware_check.py +497 -0
- package/skills/creative/comfyui/scripts/health_check.py +223 -0
- package/skills/creative/comfyui/scripts/run_batch.py +243 -0
- package/skills/creative/comfyui/scripts/run_workflow.py +796 -0
- package/skills/creative/comfyui/scripts/ws_monitor.py +267 -0
- package/skills/creative/comfyui/tests/README.md +50 -0
- package/skills/creative/comfyui/tests/conftest.py +64 -0
- package/skills/creative/comfyui/tests/pytest.ini +5 -0
- package/skills/creative/comfyui/tests/test_check_deps.py +68 -0
- package/skills/creative/comfyui/tests/test_cloud_integration.py +95 -0
- package/skills/creative/comfyui/tests/test_common.py +447 -0
- package/skills/creative/comfyui/tests/test_extract_schema.py +185 -0
- package/skills/creative/comfyui/tests/test_run_workflow.py +213 -0
- package/skills/creative/comfyui/workflows/README.md +86 -0
- package/skills/creative/comfyui/workflows/animatediff_video.json +64 -0
- package/skills/creative/comfyui/workflows/flux_dev_txt2img.json +78 -0
- package/skills/creative/comfyui/workflows/sd15_txt2img.json +49 -0
- package/skills/creative/comfyui/workflows/sdxl_img2img.json +54 -0
- package/skills/creative/comfyui/workflows/sdxl_inpaint.json +59 -0
- package/skills/creative/comfyui/workflows/sdxl_txt2img.json +49 -0
- package/skills/creative/comfyui/workflows/upscale_4x.json +27 -0
- package/skills/creative/comfyui/workflows/wan_video_t2v.json +69 -0
- package/skills/creative/creative-ideation/SKILL.md +152 -0
- package/skills/creative/creative-ideation/references/full-prompt-library.md +110 -0
- package/skills/creative/design-md/SKILL.md +199 -0
- package/skills/creative/design-md/templates/starter.md +99 -0
- package/skills/creative/excalidraw/SKILL.md +199 -0
- package/skills/creative/excalidraw/references/colors.md +44 -0
- package/skills/creative/excalidraw/references/dark-mode.md +68 -0
- package/skills/creative/excalidraw/references/examples.md +141 -0
- package/skills/creative/excalidraw/scripts/upload.py +133 -0
- package/skills/creative/humanizer/LICENSE +21 -0
- package/skills/creative/humanizer/SKILL.md +578 -0
- package/skills/creative/manim-video/README.md +23 -0
- package/skills/creative/manim-video/SKILL.md +269 -0
- package/skills/creative/manim-video/references/animation-design-thinking.md +161 -0
- package/skills/creative/manim-video/references/animations.md +282 -0
- package/skills/creative/manim-video/references/camera-and-3d.md +135 -0
- package/skills/creative/manim-video/references/decorations.md +202 -0
- package/skills/creative/manim-video/references/equations.md +216 -0
- package/skills/creative/manim-video/references/graphs-and-data.md +163 -0
- package/skills/creative/manim-video/references/mobjects.md +333 -0
- package/skills/creative/manim-video/references/paper-explainer.md +255 -0
- package/skills/creative/manim-video/references/production-quality.md +190 -0
- package/skills/creative/manim-video/references/rendering.md +185 -0
- package/skills/creative/manim-video/references/scene-planning.md +118 -0
- package/skills/creative/manim-video/references/troubleshooting.md +135 -0
- package/skills/creative/manim-video/references/updaters-and-trackers.md +260 -0
- package/skills/creative/manim-video/references/visual-design.md +124 -0
- package/skills/creative/manim-video/scripts/setup.sh +14 -0
- package/skills/creative/p5js/README.md +64 -0
- package/skills/creative/p5js/SKILL.md +556 -0
- package/skills/creative/p5js/references/animation.md +439 -0
- package/skills/creative/p5js/references/color-systems.md +352 -0
- package/skills/creative/p5js/references/core-api.md +410 -0
- package/skills/creative/p5js/references/export-pipeline.md +566 -0
- package/skills/creative/p5js/references/interaction.md +398 -0
- package/skills/creative/p5js/references/shapes-and-geometry.md +300 -0
- package/skills/creative/p5js/references/troubleshooting.md +532 -0
- package/skills/creative/p5js/references/typography.md +302 -0
- package/skills/creative/p5js/references/visual-effects.md +895 -0
- package/skills/creative/p5js/references/webgl-and-3d.md +423 -0
- package/skills/creative/p5js/scripts/export-frames.js +179 -0
- package/skills/creative/p5js/scripts/render.sh +108 -0
- package/skills/creative/p5js/scripts/serve.sh +28 -0
- package/skills/creative/p5js/scripts/setup.sh +87 -0
- package/skills/creative/p5js/templates/viewer.html +395 -0
- package/skills/creative/pixel-art/ATTRIBUTION.md +54 -0
- package/skills/creative/pixel-art/SKILL.md +218 -0
- package/skills/creative/pixel-art/references/palettes.md +49 -0
- package/skills/creative/pixel-art/scripts/__init__.py +0 -0
- package/skills/creative/pixel-art/scripts/palettes.py +167 -0
- package/skills/creative/pixel-art/scripts/pixel_art.py +162 -0
- package/skills/creative/pixel-art/scripts/pixel_art_video.py +345 -0
- package/skills/creative/popular-web-designs/SKILL.md +214 -0
- package/skills/creative/popular-web-designs/templates/airbnb.md +259 -0
- package/skills/creative/popular-web-designs/templates/airtable.md +102 -0
- package/skills/creative/popular-web-designs/templates/apple.md +326 -0
- package/skills/creative/popular-web-designs/templates/bmw.md +193 -0
- package/skills/creative/popular-web-designs/templates/cal.md +272 -0
- package/skills/creative/popular-web-designs/templates/claude.md +325 -0
- package/skills/creative/popular-web-designs/templates/clay.md +317 -0
- package/skills/creative/popular-web-designs/templates/clickhouse.md +294 -0
- package/skills/creative/popular-web-designs/templates/cohere.md +279 -0
- package/skills/creative/popular-web-designs/templates/coinbase.md +142 -0
- package/skills/creative/popular-web-designs/templates/composio.md +320 -0
- package/skills/creative/popular-web-designs/templates/cursor.md +322 -0
- package/skills/creative/popular-web-designs/templates/elevenlabs.md +278 -0
- package/skills/creative/popular-web-designs/templates/expo.md +294 -0
- package/skills/creative/popular-web-designs/templates/figma.md +233 -0
- package/skills/creative/popular-web-designs/templates/framer.md +259 -0
- package/skills/creative/popular-web-designs/templates/hashicorp.md +291 -0
- package/skills/creative/popular-web-designs/templates/ibm.md +345 -0
- package/skills/creative/popular-web-designs/templates/intercom.md +159 -0
- package/skills/creative/popular-web-designs/templates/kraken.md +138 -0
- package/skills/creative/popular-web-designs/templates/linear.app.md +380 -0
- package/skills/creative/popular-web-designs/templates/lovable.md +311 -0
- package/skills/creative/popular-web-designs/templates/minimax.md +270 -0
- package/skills/creative/popular-web-designs/templates/mintlify.md +339 -0
- package/skills/creative/popular-web-designs/templates/miro.md +121 -0
- package/skills/creative/popular-web-designs/templates/mistral.ai.md +274 -0
- package/skills/creative/popular-web-designs/templates/mongodb.md +279 -0
- package/skills/creative/popular-web-designs/templates/notion.md +322 -0
- package/skills/creative/popular-web-designs/templates/nvidia.md +306 -0
- package/skills/creative/popular-web-designs/templates/ollama.md +280 -0
- package/skills/creative/popular-web-designs/templates/opencode.ai.md +294 -0
- package/skills/creative/popular-web-designs/templates/pinterest.md +243 -0
- package/skills/creative/popular-web-designs/templates/posthog.md +269 -0
- package/skills/creative/popular-web-designs/templates/raycast.md +281 -0
- package/skills/creative/popular-web-designs/templates/replicate.md +274 -0
- package/skills/creative/popular-web-designs/templates/resend.md +316 -0
- package/skills/creative/popular-web-designs/templates/revolut.md +198 -0
- package/skills/creative/popular-web-designs/templates/runwayml.md +257 -0
- package/skills/creative/popular-web-designs/templates/sanity.md +370 -0
- package/skills/creative/popular-web-designs/templates/sentry.md +275 -0
- package/skills/creative/popular-web-designs/templates/spacex.md +207 -0
- package/skills/creative/popular-web-designs/templates/spotify.md +259 -0
- package/skills/creative/popular-web-designs/templates/stripe.md +335 -0
- package/skills/creative/popular-web-designs/templates/supabase.md +268 -0
- package/skills/creative/popular-web-designs/templates/superhuman.md +265 -0
- package/skills/creative/popular-web-designs/templates/together.ai.md +276 -0
- package/skills/creative/popular-web-designs/templates/uber.md +308 -0
- package/skills/creative/popular-web-designs/templates/vercel.md +323 -0
- package/skills/creative/popular-web-designs/templates/voltagent.md +336 -0
- package/skills/creative/popular-web-designs/templates/warp.md +266 -0
- package/skills/creative/popular-web-designs/templates/webflow.md +105 -0
- package/skills/creative/popular-web-designs/templates/wise.md +186 -0
- package/skills/creative/popular-web-designs/templates/x.ai.md +270 -0
- package/skills/creative/popular-web-designs/templates/zapier.md +341 -0
- package/skills/creative/pretext/SKILL.md +220 -0
- package/skills/creative/pretext/references/patterns.md +258 -0
- package/skills/creative/pretext/templates/donut-orbit.html +1468 -0
- package/skills/creative/pretext/templates/hello-orb-flow.html +95 -0
- package/skills/creative/sketch/SKILL.md +218 -0
- package/skills/creative/songwriting-and-ai-music/SKILL.md +287 -0
- package/skills/creative/touchdesigner-mcp/SKILL.md +356 -0
- package/skills/creative/touchdesigner-mcp/references/3d-scene.md +275 -0
- package/skills/creative/touchdesigner-mcp/references/animation.md +221 -0
- package/skills/creative/touchdesigner-mcp/references/audio-reactive.md +175 -0
- package/skills/creative/touchdesigner-mcp/references/dat-scripting.md +352 -0
- package/skills/creative/touchdesigner-mcp/references/external-data.md +322 -0
- package/skills/creative/touchdesigner-mcp/references/geometry-comp.md +121 -0
- package/skills/creative/touchdesigner-mcp/references/glsl.md +151 -0
- package/skills/creative/touchdesigner-mcp/references/layout-compositor.md +131 -0
- package/skills/creative/touchdesigner-mcp/references/mcp-tools.md +382 -0
- package/skills/creative/touchdesigner-mcp/references/midi-osc.md +211 -0
- package/skills/creative/touchdesigner-mcp/references/network-patterns.md +966 -0
- package/skills/creative/touchdesigner-mcp/references/operator-tips.md +106 -0
- package/skills/creative/touchdesigner-mcp/references/operators.md +239 -0
- package/skills/creative/touchdesigner-mcp/references/panel-ui.md +281 -0
- package/skills/creative/touchdesigner-mcp/references/particles.md +245 -0
- package/skills/creative/touchdesigner-mcp/references/pitfalls.md +704 -0
- package/skills/creative/touchdesigner-mcp/references/postfx.md +183 -0
- package/skills/creative/touchdesigner-mcp/references/projection-mapping.md +211 -0
- package/skills/creative/touchdesigner-mcp/references/python-api.md +463 -0
- package/skills/creative/touchdesigner-mcp/references/replicator.md +198 -0
- package/skills/creative/touchdesigner-mcp/references/troubleshooting.md +244 -0
- package/skills/creative/touchdesigner-mcp/scripts/setup.sh +115 -0
- package/skills/data-science/DESCRIPTION.md +3 -0
- package/skills/data-science/jupyter-live-kernel/SKILL.md +167 -0
- package/skills/devops/kanban-orchestrator/SKILL.md +189 -0
- package/skills/devops/kanban-worker/SKILL.md +184 -0
- package/skills/devops/webhook-subscriptions/SKILL.md +204 -0
- package/skills/diagramming/DESCRIPTION.md +3 -0
- package/skills/dogfood/SKILL.md +162 -0
- package/skills/dogfood/references/issue-taxonomy.md +109 -0
- package/skills/dogfood/templates/dogfood-report-template.md +86 -0
- package/skills/domain/DESCRIPTION.md +24 -0
- package/skills/email/DESCRIPTION.md +3 -0
- package/skills/email/himalaya/SKILL.md +299 -0
- package/skills/email/himalaya/references/configuration.md +227 -0
- package/skills/email/himalaya/references/message-composition.md +199 -0
- package/skills/gaming/DESCRIPTION.md +3 -0
- package/skills/gaming/minecraft-modpack-server/SKILL.md +187 -0
- package/skills/gaming/pokemon-player/SKILL.md +216 -0
- package/skills/gifs/DESCRIPTION.md +3 -0
- package/skills/github/DESCRIPTION.md +3 -0
- package/skills/github/codebase-inspection/SKILL.md +116 -0
- package/skills/github/github-auth/SKILL.md +247 -0
- package/skills/github/github-auth/scripts/gh-env.sh +66 -0
- package/skills/github/github-code-review/SKILL.md +481 -0
- package/skills/github/github-code-review/references/review-output-template.md +74 -0
- package/skills/github/github-issues/SKILL.md +370 -0
- package/skills/github/github-issues/templates/bug-report.md +35 -0
- package/skills/github/github-issues/templates/feature-request.md +31 -0
- package/skills/github/github-pr-workflow/SKILL.md +367 -0
- package/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
- package/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
- package/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
- package/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
- package/skills/github/github-repo-management/SKILL.md +516 -0
- package/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
- package/skills/index-cache/anthropics_skills_skills_.json +1 -0
- package/skills/index-cache/claude_marketplace_anthropics_skills.json +1 -0
- package/skills/index-cache/lobehub_index.json +1 -0
- package/skills/index-cache/openai_skills_skills_.json +1 -0
- package/skills/inference-sh/DESCRIPTION.md +19 -0
- package/skills/mcp/DESCRIPTION.md +3 -0
- package/skills/mcp/native-mcp/SKILL.md +357 -0
- package/skills/media/DESCRIPTION.md +3 -0
- package/skills/media/gif-search/SKILL.md +91 -0
- package/skills/media/heartmula/SKILL.md +171 -0
- package/skills/media/songsee/SKILL.md +83 -0
- package/skills/media/spotify/SKILL.md +135 -0
- package/skills/media/youtube-content/SKILL.md +73 -0
- package/skills/media/youtube-content/references/output-formats.md +56 -0
- package/skills/media/youtube-content/scripts/fetch_transcript.py +124 -0
- package/skills/mlops/DESCRIPTION.md +3 -0
- package/skills/mlops/evaluation/DESCRIPTION.md +3 -0
- package/skills/mlops/evaluation/lm-evaluation-harness/SKILL.md +498 -0
- package/skills/mlops/evaluation/lm-evaluation-harness/references/api-evaluation.md +490 -0
- package/skills/mlops/evaluation/lm-evaluation-harness/references/benchmark-guide.md +488 -0
- package/skills/mlops/evaluation/lm-evaluation-harness/references/custom-tasks.md +602 -0
- package/skills/mlops/evaluation/lm-evaluation-harness/references/distributed-eval.md +519 -0
- package/skills/mlops/evaluation/weights-and-biases/SKILL.md +594 -0
- package/skills/mlops/evaluation/weights-and-biases/references/artifacts.md +584 -0
- package/skills/mlops/evaluation/weights-and-biases/references/integrations.md +700 -0
- package/skills/mlops/evaluation/weights-and-biases/references/sweeps.md +847 -0
- package/skills/mlops/huggingface-hub/SKILL.md +81 -0
- package/skills/mlops/inference/DESCRIPTION.md +3 -0
- package/skills/mlops/inference/llama-cpp/SKILL.md +249 -0
- package/skills/mlops/inference/llama-cpp/references/advanced-usage.md +504 -0
- package/skills/mlops/inference/llama-cpp/references/hub-discovery.md +168 -0
- package/skills/mlops/inference/llama-cpp/references/optimization.md +89 -0
- package/skills/mlops/inference/llama-cpp/references/quantization.md +243 -0
- package/skills/mlops/inference/llama-cpp/references/server.md +150 -0
- package/skills/mlops/inference/llama-cpp/references/troubleshooting.md +442 -0
- package/skills/mlops/inference/obliteratus/SKILL.md +342 -0
- package/skills/mlops/inference/obliteratus/references/analysis-modules.md +166 -0
- package/skills/mlops/inference/obliteratus/references/methods-guide.md +141 -0
- package/skills/mlops/inference/obliteratus/templates/abliteration-config.yaml +33 -0
- package/skills/mlops/inference/obliteratus/templates/analysis-study.yaml +40 -0
- package/skills/mlops/inference/obliteratus/templates/batch-abliteration.yaml +41 -0
- package/skills/mlops/inference/vllm/SKILL.md +372 -0
- package/skills/mlops/inference/vllm/references/optimization.md +226 -0
- package/skills/mlops/inference/vllm/references/quantization.md +284 -0
- package/skills/mlops/inference/vllm/references/server-deployment.md +255 -0
- package/skills/mlops/inference/vllm/references/troubleshooting.md +447 -0
- package/skills/mlops/models/DESCRIPTION.md +3 -0
- package/skills/mlops/models/audiocraft/SKILL.md +568 -0
- package/skills/mlops/models/audiocraft/references/advanced-usage.md +666 -0
- package/skills/mlops/models/audiocraft/references/troubleshooting.md +504 -0
- package/skills/mlops/models/segment-anything/SKILL.md +506 -0
- package/skills/mlops/models/segment-anything/references/advanced-usage.md +589 -0
- package/skills/mlops/models/segment-anything/references/troubleshooting.md +484 -0
- package/skills/mlops/research/DESCRIPTION.md +3 -0
- package/skills/mlops/research/dspy/SKILL.md +594 -0
- package/skills/mlops/research/dspy/references/examples.md +663 -0
- package/skills/mlops/research/dspy/references/modules.md +475 -0
- package/skills/mlops/research/dspy/references/optimizers.md +566 -0
- package/skills/mlops/training/DESCRIPTION.md +3 -0
- package/skills/mlops/vector-databases/DESCRIPTION.md +3 -0
- package/skills/note-taking/DESCRIPTION.md +3 -0
- package/skills/note-taking/obsidian/SKILL.md +61 -0
- package/skills/productivity/DESCRIPTION.md +3 -0
- package/skills/productivity/airtable/SKILL.md +229 -0
- package/skills/productivity/google-workspace/SKILL.md +335 -0
- package/skills/productivity/google-workspace/references/gmail-search-syntax.md +63 -0
- package/skills/productivity/google-workspace/scripts/_hermes_home.py +43 -0
- package/skills/productivity/google-workspace/scripts/google_api.py +1221 -0
- package/skills/productivity/google-workspace/scripts/gws_bridge.py +108 -0
- package/skills/productivity/google-workspace/scripts/setup.py +454 -0
- package/skills/productivity/linear/SKILL.md +380 -0
- package/skills/productivity/linear/scripts/linear_api.py +445 -0
- package/skills/productivity/maps/SKILL.md +195 -0
- package/skills/productivity/maps/scripts/maps_client.py +1298 -0
- package/skills/productivity/nano-pdf/SKILL.md +52 -0
- package/skills/productivity/notion/SKILL.md +448 -0
- package/skills/productivity/notion/references/block-types.md +112 -0
- package/skills/productivity/ocr-and-documents/DESCRIPTION.md +3 -0
- package/skills/productivity/ocr-and-documents/SKILL.md +172 -0
- package/skills/productivity/ocr-and-documents/scripts/extract_marker.py +87 -0
- package/skills/productivity/ocr-and-documents/scripts/extract_pymupdf.py +98 -0
- package/skills/productivity/powerpoint/LICENSE.txt +30 -0
- package/skills/productivity/powerpoint/SKILL.md +237 -0
- package/skills/productivity/powerpoint/editing.md +205 -0
- package/skills/productivity/powerpoint/pptxgenjs.md +420 -0
- package/skills/productivity/powerpoint/scripts/__init__.py +0 -0
- package/skills/productivity/powerpoint/scripts/add_slide.py +195 -0
- package/skills/productivity/powerpoint/scripts/clean.py +286 -0
- package/skills/productivity/powerpoint/scripts/office/helpers/__init__.py +0 -0
- package/skills/productivity/powerpoint/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/productivity/powerpoint/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/productivity/powerpoint/scripts/office/pack.py +159 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-contentTypes.xsd +42 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-coreProperties.xsd +50 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-digSig.xsd +49 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-relationships.xsd +33 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/productivity/teams-meeting-pipeline/SKILL.md +116 -0
- package/skills/red-teaming/godmode/SKILL.md +404 -0
- package/skills/red-teaming/godmode/references/jailbreak-templates.md +128 -0
- package/skills/red-teaming/godmode/references/refusal-detection.md +142 -0
- package/skills/red-teaming/godmode/scripts/auto_jailbreak.py +769 -0
- package/skills/red-teaming/godmode/scripts/godmode_race.py +530 -0
- package/skills/red-teaming/godmode/scripts/load_godmode.py +45 -0
- package/skills/red-teaming/godmode/scripts/parseltongue.py +550 -0
- package/skills/red-teaming/godmode/templates/prefill-subtle.json +10 -0
- package/skills/red-teaming/godmode/templates/prefill.json +18 -0
- package/skills/research/DESCRIPTION.md +3 -0
- package/skills/research/arxiv/SKILL.md +282 -0
- package/skills/research/arxiv/scripts/search_arxiv.py +114 -0
- package/skills/research/blogwatcher/SKILL.md +137 -0
- package/skills/research/llm-wiki/SKILL.md +507 -0
- package/skills/research/polymarket/SKILL.md +77 -0
- package/skills/research/polymarket/references/api-endpoints.md +220 -0
- package/skills/research/polymarket/scripts/polymarket.py +284 -0
- package/skills/research/research-paper-writing/SKILL.md +2377 -0
- package/skills/research/research-paper-writing/references/autoreason-methodology.md +394 -0
- package/skills/research/research-paper-writing/references/checklists.md +434 -0
- package/skills/research/research-paper-writing/references/citation-workflow.md +564 -0
- package/skills/research/research-paper-writing/references/experiment-patterns.md +728 -0
- package/skills/research/research-paper-writing/references/human-evaluation.md +476 -0
- package/skills/research/research-paper-writing/references/paper-types.md +481 -0
- package/skills/research/research-paper-writing/references/reviewer-guidelines.md +433 -0
- package/skills/research/research-paper-writing/references/sources.md +191 -0
- package/skills/research/research-paper-writing/references/writing-guide.md +474 -0
- package/skills/research/research-paper-writing/templates/README.md +251 -0
- package/skills/research/research-paper-writing/templates/aaai2026/README.md +534 -0
- package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +144 -0
- package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +952 -0
- package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bib +111 -0
- package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bst +1493 -0
- package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.sty +315 -0
- package/skills/research/research-paper-writing/templates/acl/README.md +50 -0
- package/skills/research/research-paper-writing/templates/acl/acl.sty +312 -0
- package/skills/research/research-paper-writing/templates/acl/acl_latex.tex +377 -0
- package/skills/research/research-paper-writing/templates/acl/acl_lualatex.tex +101 -0
- package/skills/research/research-paper-writing/templates/acl/acl_natbib.bst +1940 -0
- package/skills/research/research-paper-writing/templates/acl/anthology.bib.txt +26 -0
- package/skills/research/research-paper-writing/templates/acl/custom.bib +70 -0
- package/skills/research/research-paper-writing/templates/acl/formatting.md +326 -0
- package/skills/research/research-paper-writing/templates/colm2025/README.md +3 -0
- package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bib +11 -0
- package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bst +1440 -0
- package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.sty +218 -0
- package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.tex +305 -0
- package/skills/research/research-paper-writing/templates/colm2025/fancyhdr.sty +485 -0
- package/skills/research/research-paper-writing/templates/colm2025/math_commands.tex +508 -0
- package/skills/research/research-paper-writing/templates/colm2025/natbib.sty +1246 -0
- package/skills/research/research-paper-writing/templates/iclr2026/fancyhdr.sty +485 -0
- package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bib +24 -0
- package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bst +1440 -0
- package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.sty +246 -0
- package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.tex +414 -0
- package/skills/research/research-paper-writing/templates/iclr2026/math_commands.tex +508 -0
- package/skills/research/research-paper-writing/templates/iclr2026/natbib.sty +1246 -0
- package/skills/research/research-paper-writing/templates/icml2026/algorithm.sty +79 -0
- package/skills/research/research-paper-writing/templates/icml2026/algorithmic.sty +201 -0
- package/skills/research/research-paper-writing/templates/icml2026/example_paper.bib +75 -0
- package/skills/research/research-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- package/skills/research/research-paper-writing/templates/icml2026/example_paper.tex +662 -0
- package/skills/research/research-paper-writing/templates/icml2026/fancyhdr.sty +864 -0
- package/skills/research/research-paper-writing/templates/icml2026/icml2026.bst +1443 -0
- package/skills/research/research-paper-writing/templates/icml2026/icml2026.sty +767 -0
- package/skills/research/research-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- package/skills/research/research-paper-writing/templates/neurips2025/Makefile +36 -0
- package/skills/research/research-paper-writing/templates/neurips2025/extra_pkgs.tex +53 -0
- package/skills/research/research-paper-writing/templates/neurips2025/main.tex +38 -0
- package/skills/research/research-paper-writing/templates/neurips2025/neurips.sty +382 -0
- package/skills/smart-home/DESCRIPTION.md +3 -0
- package/skills/smart-home/openhue/SKILL.md +109 -0
- package/skills/social-media/DESCRIPTION.md +3 -0
- package/skills/social-media/xurl/SKILL.md +414 -0
- package/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
- package/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
- package/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
- package/skills/software-development/plan/SKILL.md +58 -0
- package/skills/software-development/python-debugpy/SKILL.md +375 -0
- package/skills/software-development/requesting-code-review/SKILL.md +280 -0
- package/skills/software-development/spike/SKILL.md +197 -0
- package/skills/software-development/subagent-driven-development/SKILL.md +352 -0
- package/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
- package/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
- package/skills/software-development/systematic-debugging/SKILL.md +367 -0
- package/skills/software-development/test-driven-development/SKILL.md +343 -0
- package/skills/software-development/writing-plans/SKILL.md +297 -0
- package/skills/yuanbao/SKILL.md +108 -0
- package/tools/__init__.py +25 -0
- package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/__pycache__/approval.cpython-312.pyc +0 -0
- package/tools/__pycache__/binary_extensions.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_camofox.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_camofox_state.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_cdp_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_dialog_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_supervisor.cpython-312.pyc +0 -0
- package/tools/__pycache__/browser_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/budget_config.cpython-312.pyc +0 -0
- package/tools/__pycache__/checkpoint_manager.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_gateway.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/code_execution_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/computer_use_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/cronjob_tools.cpython-312.pyc +0 -0
- package/tools/__pycache__/debug_helpers.cpython-312.pyc +0 -0
- package/tools/__pycache__/delegate_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/discord_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/feishu_doc_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/feishu_drive_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/file_operations.cpython-312.pyc +0 -0
- package/tools/__pycache__/file_state.cpython-312.pyc +0 -0
- package/tools/__pycache__/file_tools.cpython-312.pyc +0 -0
- package/tools/__pycache__/homeassistant_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/image_generation_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/interrupt.cpython-312.pyc +0 -0
- package/tools/__pycache__/kanban_tools.cpython-312.pyc +0 -0
- package/tools/__pycache__/lazy_deps.cpython-312.pyc +0 -0
- package/tools/__pycache__/managed_tool_gateway.cpython-312.pyc +0 -0
- package/tools/__pycache__/mcp_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/memory_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/mixture_of_agents_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/openrouter_client.cpython-312.pyc +0 -0
- package/tools/__pycache__/process_registry.cpython-312.pyc +0 -0
- package/tools/__pycache__/registry.cpython-312.pyc +0 -0
- package/tools/__pycache__/schema_sanitizer.cpython-312.pyc +0 -0
- package/tools/__pycache__/send_message_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/session_search_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/skill_manager_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/skill_provenance.cpython-312.pyc +0 -0
- package/tools/__pycache__/skill_usage.cpython-312.pyc +0 -0
- package/tools/__pycache__/skills_guard.cpython-312.pyc +0 -0
- package/tools/__pycache__/skills_sync.cpython-312.pyc +0 -0
- package/tools/__pycache__/skills_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/slash_confirm.cpython-312.pyc +0 -0
- package/tools/__pycache__/terminal_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/tirith_security.cpython-312.pyc +0 -0
- package/tools/__pycache__/todo_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/tool_backend_helpers.cpython-312.pyc +0 -0
- package/tools/__pycache__/tool_result_storage.cpython-312.pyc +0 -0
- package/tools/__pycache__/tts_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/url_safety.cpython-312.pyc +0 -0
- package/tools/__pycache__/video_generation_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/vision_tools.cpython-312.pyc +0 -0
- package/tools/__pycache__/voice_mode.cpython-312.pyc +0 -0
- package/tools/__pycache__/web_tools.cpython-312.pyc +0 -0
- package/tools/__pycache__/website_policy.cpython-312.pyc +0 -0
- package/tools/__pycache__/x_search_tool.cpython-312.pyc +0 -0
- package/tools/__pycache__/xai_http.cpython-312.pyc +0 -0
- package/tools/__pycache__/yuanbao_tools.cpython-312.pyc +0 -0
- package/tools/ansi_strip.py +44 -0
- package/tools/approval.py +1392 -0
- package/tools/binary_extensions.py +42 -0
- package/tools/browser_camofox.py +700 -0
- package/tools/browser_camofox_state.py +48 -0
- package/tools/browser_cdp_tool.py +569 -0
- package/tools/browser_dialog_tool.py +148 -0
- package/tools/browser_providers/__init__.py +10 -0
- package/tools/browser_providers/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/browser_providers/__pycache__/base.cpython-312.pyc +0 -0
- package/tools/browser_providers/__pycache__/browser_use.cpython-312.pyc +0 -0
- package/tools/browser_providers/__pycache__/browserbase.cpython-312.pyc +0 -0
- package/tools/browser_providers/__pycache__/firecrawl.cpython-312.pyc +0 -0
- package/tools/browser_providers/base.py +59 -0
- package/tools/browser_providers/browser_use.py +225 -0
- package/tools/browser_providers/browserbase.py +222 -0
- package/tools/browser_providers/firecrawl.py +112 -0
- package/tools/browser_supervisor.py +1457 -0
- package/tools/browser_tool.py +3676 -0
- package/tools/budget_config.py +51 -0
- package/tools/checkpoint_manager.py +1639 -0
- package/tools/clarify_gateway.py +278 -0
- package/tools/clarify_tool.py +141 -0
- package/tools/code_execution_tool.py +1782 -0
- package/tools/computer_use/__init__.py +43 -0
- package/tools/computer_use/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/computer_use/__pycache__/backend.cpython-312.pyc +0 -0
- package/tools/computer_use/__pycache__/schema.cpython-312.pyc +0 -0
- package/tools/computer_use/__pycache__/tool.cpython-312.pyc +0 -0
- package/tools/computer_use/backend.py +150 -0
- package/tools/computer_use/cua_backend.py +682 -0
- package/tools/computer_use/schema.py +191 -0
- package/tools/computer_use/tool.py +521 -0
- package/tools/computer_use_tool.py +39 -0
- package/tools/credential_files.py +437 -0
- package/tools/cronjob_tools.py +719 -0
- package/tools/debug_helpers.py +106 -0
- package/tools/delegate_tool.py +2797 -0
- package/tools/discord_tool.py +959 -0
- package/tools/env_passthrough.py +145 -0
- package/tools/environments/__init__.py +14 -0
- package/tools/environments/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/base.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/docker.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/file_sync.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/local.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/managed_modal.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/modal.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/modal_utils.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/singularity.cpython-312.pyc +0 -0
- package/tools/environments/__pycache__/ssh.cpython-312.pyc +0 -0
- package/tools/environments/base.py +844 -0
- package/tools/environments/daytona.py +270 -0
- package/tools/environments/docker.py +656 -0
- package/tools/environments/file_sync.py +400 -0
- package/tools/environments/local.py +658 -0
- package/tools/environments/managed_modal.py +282 -0
- package/tools/environments/modal.py +479 -0
- package/tools/environments/modal_utils.py +199 -0
- package/tools/environments/singularity.py +263 -0
- package/tools/environments/ssh.py +295 -0
- package/tools/environments/vercel_sandbox.py +655 -0
- package/tools/feishu_doc_tool.py +138 -0
- package/tools/feishu_drive_tool.py +431 -0
- package/tools/file_operations.py +1825 -0
- package/tools/file_state.py +332 -0
- package/tools/file_tools.py +1172 -0
- package/tools/fuzzy_match.py +703 -0
- package/tools/homeassistant_tool.py +513 -0
- package/tools/image_generation_tool.py +1098 -0
- package/tools/interrupt.py +98 -0
- package/tools/kanban_tools.py +1139 -0
- package/tools/lazy_deps.py +608 -0
- package/tools/managed_tool_gateway.py +168 -0
- package/tools/mcp_oauth.py +633 -0
- package/tools/mcp_oauth_manager.py +607 -0
- package/tools/mcp_tool.py +3483 -0
- package/tools/memory_tool.py +584 -0
- package/tools/microsoft_graph_auth.py +245 -0
- package/tools/microsoft_graph_client.py +408 -0
- package/tools/mixture_of_agents_tool.py +542 -0
- package/tools/neutts_samples/jo.txt +1 -0
- package/tools/neutts_samples/jo.wav +0 -0
- package/tools/neutts_synth.py +104 -0
- package/tools/openrouter_client.py +33 -0
- package/tools/osv_check.py +155 -0
- package/tools/patch_parser.py +592 -0
- package/tools/path_security.py +43 -0
- package/tools/process_registry.py +1534 -0
- package/tools/registry.py +589 -0
- package/tools/schema_sanitizer.py +370 -0
- package/tools/send_message_tool.py +1900 -0
- package/tools/session_search_tool.py +613 -0
- package/tools/skill_manager_tool.py +932 -0
- package/tools/skill_provenance.py +78 -0
- package/tools/skill_usage.py +610 -0
- package/tools/skills_guard.py +932 -0
- package/tools/skills_hub.py +3263 -0
- package/tools/skills_sync.py +432 -0
- package/tools/skills_tool.py +1569 -0
- package/tools/slash_confirm.py +167 -0
- package/tools/terminal_tool.py +2376 -0
- package/tools/tirith_security.py +775 -0
- package/tools/todo_tool.py +277 -0
- package/tools/tool_backend_helpers.py +144 -0
- package/tools/tool_output_limits.py +92 -0
- package/tools/tool_result_storage.py +232 -0
- package/tools/transcription_tools.py +936 -0
- package/tools/tts_tool.py +2285 -0
- package/tools/url_safety.py +330 -0
- package/tools/video_generation_tool.py +561 -0
- package/tools/vision_tools.py +1422 -0
- package/tools/voice_mode.py +1019 -0
- package/tools/web_tools.py +1551 -0
- package/tools/website_policy.py +283 -0
- package/tools/x_search_tool.py +424 -0
- package/tools/xai_http.py +83 -0
- package/tools/yuanbao_tools.py +736 -0
- package/toolset_distributions.py +364 -0
- package/toolsets.py +866 -0
- package/trajectory_compressor.py +1509 -0
- package/tui_gateway/__init__.py +0 -0
- package/tui_gateway/entry.py +251 -0
- package/tui_gateway/event_publisher.py +126 -0
- package/tui_gateway/render.py +49 -0
- package/tui_gateway/server.py +6623 -0
- package/tui_gateway/slash_worker.py +76 -0
- package/tui_gateway/transport.py +219 -0
- package/tui_gateway/ws.py +178 -0
- package/utils.py +361 -0
|
@@ -0,0 +1,1422 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Vision Tools Module
|
|
4
|
+
|
|
5
|
+
This module provides vision analysis tools that work with image URLs.
|
|
6
|
+
Uses the centralized auxiliary vision router, which can select OpenRouter,
|
|
7
|
+
Nous, Codex, native Anthropic, or a custom OpenAI-compatible endpoint.
|
|
8
|
+
|
|
9
|
+
Available tools:
|
|
10
|
+
- vision_analyze_tool: Analyze images from URLs with custom prompts
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Downloads images from URLs and converts to base64 for API compatibility
|
|
14
|
+
- Comprehensive image description
|
|
15
|
+
- Context-aware analysis based on user queries
|
|
16
|
+
- Automatic temporary file cleanup
|
|
17
|
+
- Proper error handling and validation
|
|
18
|
+
- Debug logging support
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
from vision_tools import vision_analyze_tool
|
|
22
|
+
import asyncio
|
|
23
|
+
|
|
24
|
+
# Analyze an image
|
|
25
|
+
result = await vision_analyze_tool(
|
|
26
|
+
image_url="https://example.com/image.jpg",
|
|
27
|
+
user_prompt="What architectural style is this building?"
|
|
28
|
+
)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import base64
|
|
32
|
+
import json
|
|
33
|
+
import logging
|
|
34
|
+
import os
|
|
35
|
+
import uuid
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any, Awaitable, Dict, Optional
|
|
38
|
+
from urllib.parse import urlparse
|
|
39
|
+
import httpx
|
|
40
|
+
from agent.auxiliary_client import async_call_llm, extract_content_or_reasoning
|
|
41
|
+
from calvyn_constants import get_hermes_dir
|
|
42
|
+
from tools.debug_helpers import DebugSession
|
|
43
|
+
from tools.website_policy import check_website_access
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
_debug = DebugSession("vision_tools", env_var="VISION_TOOLS_DEBUG")
|
|
49
|
+
|
|
50
|
+
# Configurable HTTP download timeout for _download_image().
|
|
51
|
+
# Separate from auxiliary.vision.timeout which governs the LLM API call.
|
|
52
|
+
# Resolution: config.yaml auxiliary.vision.download_timeout → env var → 30s default.
|
|
53
|
+
def _resolve_download_timeout() -> float:
|
|
54
|
+
env_val = os.getenv("HERMES_VISION_DOWNLOAD_TIMEOUT", "").strip()
|
|
55
|
+
if env_val:
|
|
56
|
+
try:
|
|
57
|
+
return float(env_val)
|
|
58
|
+
except ValueError:
|
|
59
|
+
pass
|
|
60
|
+
try:
|
|
61
|
+
from hermes_cli.config import cfg_get, load_config
|
|
62
|
+
cfg = load_config()
|
|
63
|
+
val = cfg_get(cfg, "auxiliary", "vision", "download_timeout")
|
|
64
|
+
if val is not None:
|
|
65
|
+
return float(val)
|
|
66
|
+
except Exception:
|
|
67
|
+
pass
|
|
68
|
+
return 30.0
|
|
69
|
+
|
|
70
|
+
_VISION_DOWNLOAD_TIMEOUT = _resolve_download_timeout()
|
|
71
|
+
|
|
72
|
+
# Hard cap on downloaded image file size (50 MB). Prevents OOM from
|
|
73
|
+
# attacker-hosted multi-gigabyte files or decompression bombs.
|
|
74
|
+
_VISION_MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _validate_image_url(url: str) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
Basic validation of image URL format.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
url (str): The URL to validate
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
bool: True if URL appears to be valid, False otherwise
|
|
86
|
+
"""
|
|
87
|
+
if not url or not isinstance(url, str):
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
# Basic HTTP/HTTPS URL check
|
|
91
|
+
if not url.startswith(("http://", "https://")):
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
# Parse to ensure we at least have a network location; still allow URLs
|
|
95
|
+
# without file extensions (e.g. CDN endpoints that redirect to images).
|
|
96
|
+
parsed = urlparse(url)
|
|
97
|
+
if not parsed.netloc:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
# Block private/internal addresses to prevent SSRF
|
|
101
|
+
from tools.url_safety import is_safe_url
|
|
102
|
+
if not is_safe_url(url):
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _detect_image_mime_type(image_path: Path) -> Optional[str]:
|
|
109
|
+
"""Return a MIME type when the file looks like a supported image."""
|
|
110
|
+
with image_path.open("rb") as f:
|
|
111
|
+
header = f.read(64)
|
|
112
|
+
|
|
113
|
+
if header.startswith(b"\x89PNG\r\n\x1a\n"):
|
|
114
|
+
return "image/png"
|
|
115
|
+
if header.startswith(b"\xff\xd8\xff"):
|
|
116
|
+
return "image/jpeg"
|
|
117
|
+
if header.startswith((b"GIF87a", b"GIF89a")):
|
|
118
|
+
return "image/gif"
|
|
119
|
+
if header.startswith(b"BM"):
|
|
120
|
+
return "image/bmp"
|
|
121
|
+
if len(header) >= 12 and header[:4] == b"RIFF" and header[8:12] == b"WEBP":
|
|
122
|
+
return "image/webp"
|
|
123
|
+
if image_path.suffix.lower() == ".svg":
|
|
124
|
+
head = image_path.read_text(encoding="utf-8", errors="ignore")[:4096].lower()
|
|
125
|
+
if "<svg" in head:
|
|
126
|
+
return "image/svg+xml"
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def _download_image(image_url: str, destination: Path, max_retries: int = 3) -> Path:
|
|
131
|
+
"""
|
|
132
|
+
Download an image from a URL to a local destination (async) with retry logic.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
image_url (str): The URL of the image to download
|
|
136
|
+
destination (Path): The path where the image should be saved
|
|
137
|
+
max_retries (int): Maximum number of retry attempts (default: 3)
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Path: The path to the downloaded image
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
Exception: If download fails after all retries
|
|
144
|
+
"""
|
|
145
|
+
import asyncio
|
|
146
|
+
|
|
147
|
+
# Create parent directories if they don't exist
|
|
148
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
|
|
150
|
+
async def _ssrf_redirect_guard(response):
|
|
151
|
+
"""Re-validate each redirect target to prevent redirect-based SSRF.
|
|
152
|
+
|
|
153
|
+
Without this, an attacker can host a public URL that 302-redirects
|
|
154
|
+
to http://169.254.169.254/ and bypass the pre-flight is_safe_url check.
|
|
155
|
+
|
|
156
|
+
Must be async because httpx.AsyncClient awaits event hooks.
|
|
157
|
+
"""
|
|
158
|
+
if response.is_redirect and response.next_request:
|
|
159
|
+
redirect_url = str(response.next_request.url)
|
|
160
|
+
from tools.url_safety import is_safe_url
|
|
161
|
+
if not is_safe_url(redirect_url):
|
|
162
|
+
raise ValueError(
|
|
163
|
+
f"Blocked redirect to private/internal address: {redirect_url}"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
last_error = None
|
|
167
|
+
for attempt in range(max_retries):
|
|
168
|
+
try:
|
|
169
|
+
blocked = check_website_access(image_url)
|
|
170
|
+
if blocked:
|
|
171
|
+
raise PermissionError(blocked["message"])
|
|
172
|
+
|
|
173
|
+
# Download the image with appropriate headers using async httpx
|
|
174
|
+
# Enable follow_redirects to handle image CDNs that redirect (e.g., Imgur, Picsum)
|
|
175
|
+
# SSRF: event_hooks validates each redirect target against private IP ranges
|
|
176
|
+
async with httpx.AsyncClient(
|
|
177
|
+
timeout=_VISION_DOWNLOAD_TIMEOUT,
|
|
178
|
+
follow_redirects=True,
|
|
179
|
+
event_hooks={"response": [_ssrf_redirect_guard]},
|
|
180
|
+
) as client:
|
|
181
|
+
response = await client.get(
|
|
182
|
+
image_url,
|
|
183
|
+
headers={
|
|
184
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
185
|
+
"Accept": "image/*,*/*;q=0.8",
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
response.raise_for_status()
|
|
189
|
+
|
|
190
|
+
# Reject overly large images early via Content-Length header.
|
|
191
|
+
cl = response.headers.get("content-length")
|
|
192
|
+
if cl and int(cl) > _VISION_MAX_DOWNLOAD_BYTES:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f"Image too large ({int(cl)} bytes, max {_VISION_MAX_DOWNLOAD_BYTES})"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
final_url = str(response.url)
|
|
198
|
+
blocked = check_website_access(final_url)
|
|
199
|
+
if blocked:
|
|
200
|
+
raise PermissionError(blocked["message"])
|
|
201
|
+
|
|
202
|
+
# Save the image content (double-check actual size)
|
|
203
|
+
body = response.content
|
|
204
|
+
if len(body) > _VISION_MAX_DOWNLOAD_BYTES:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"Image too large ({len(body)} bytes, max {_VISION_MAX_DOWNLOAD_BYTES})"
|
|
207
|
+
)
|
|
208
|
+
destination.write_bytes(body)
|
|
209
|
+
|
|
210
|
+
return destination
|
|
211
|
+
except Exception as e:
|
|
212
|
+
last_error = e
|
|
213
|
+
if attempt < max_retries - 1:
|
|
214
|
+
wait_time = 2 ** (attempt + 1) # 2s, 4s, 8s
|
|
215
|
+
logger.warning("Image download failed (attempt %s/%s): %s", attempt + 1, max_retries, str(e)[:50])
|
|
216
|
+
logger.warning("Retrying in %ss...", wait_time)
|
|
217
|
+
await asyncio.sleep(wait_time)
|
|
218
|
+
else:
|
|
219
|
+
logger.error(
|
|
220
|
+
"Image download failed after %s attempts: %s",
|
|
221
|
+
max_retries,
|
|
222
|
+
str(e)[:100],
|
|
223
|
+
exc_info=True,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if last_error is None:
|
|
227
|
+
raise RuntimeError(
|
|
228
|
+
f"_download_image exited retry loop without attempting (max_retries={max_retries})"
|
|
229
|
+
)
|
|
230
|
+
raise last_error
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _determine_mime_type(image_path: Path) -> str:
|
|
234
|
+
"""
|
|
235
|
+
Determine the MIME type of an image based on its file extension.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
image_path (Path): Path to the image file
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
str: The MIME type (defaults to image/jpeg if unknown)
|
|
242
|
+
"""
|
|
243
|
+
extension = image_path.suffix.lower()
|
|
244
|
+
mime_types = {
|
|
245
|
+
'.jpg': 'image/jpeg',
|
|
246
|
+
'.jpeg': 'image/jpeg',
|
|
247
|
+
'.png': 'image/png',
|
|
248
|
+
'.gif': 'image/gif',
|
|
249
|
+
'.bmp': 'image/bmp',
|
|
250
|
+
'.webp': 'image/webp',
|
|
251
|
+
'.svg': 'image/svg+xml'
|
|
252
|
+
}
|
|
253
|
+
return mime_types.get(extension, 'image/jpeg')
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _image_to_base64_data_url(image_path: Path, mime_type: Optional[str] = None) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Convert an image file to a base64-encoded data URL.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
image_path (Path): Path to the image file
|
|
262
|
+
mime_type (Optional[str]): MIME type of the image (auto-detected if None)
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
str: Base64-encoded data URL (e.g., "data:image/jpeg;base64,...")
|
|
266
|
+
"""
|
|
267
|
+
# Read the image as bytes
|
|
268
|
+
data = image_path.read_bytes()
|
|
269
|
+
|
|
270
|
+
# Encode to base64
|
|
271
|
+
encoded = base64.b64encode(data).decode("ascii")
|
|
272
|
+
|
|
273
|
+
# Determine MIME type
|
|
274
|
+
mime = mime_type or _determine_mime_type(image_path)
|
|
275
|
+
|
|
276
|
+
# Create data URL
|
|
277
|
+
data_url = f"data:{mime};base64,{encoded}"
|
|
278
|
+
|
|
279
|
+
return data_url
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# Hard limit for vision API payloads (20 MB) — matches the most restrictive
|
|
283
|
+
# major provider (Gemini inline data limit). Images above this are rejected.
|
|
284
|
+
_MAX_BASE64_BYTES = 20 * 1024 * 1024
|
|
285
|
+
|
|
286
|
+
# Target size when auto-resizing on API failure (5 MB). After a provider
|
|
287
|
+
# rejects an image, we downscale to this target and retry once.
|
|
288
|
+
_RESIZE_TARGET_BYTES = 5 * 1024 * 1024
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _is_image_size_error(error: Exception) -> bool:
|
|
292
|
+
"""Detect if an API error is related to image or payload size."""
|
|
293
|
+
err_str = str(error).lower()
|
|
294
|
+
return any(hint in err_str for hint in (
|
|
295
|
+
"too large", "payload", "413", "content_too_large",
|
|
296
|
+
"request_too_large", "image_url", "invalid_request",
|
|
297
|
+
"exceeds", "size limit",
|
|
298
|
+
))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _resize_image_for_vision(image_path: Path, mime_type: Optional[str] = None,
|
|
302
|
+
max_base64_bytes: int = _RESIZE_TARGET_BYTES) -> str:
|
|
303
|
+
"""Convert an image to a base64 data URL, auto-resizing if too large.
|
|
304
|
+
|
|
305
|
+
Tries Pillow first to progressively downscale oversized images. If Pillow
|
|
306
|
+
is not installed or resizing still exceeds the limit, falls back to the raw
|
|
307
|
+
bytes and lets the caller handle the size check.
|
|
308
|
+
|
|
309
|
+
Returns the base64 data URL string.
|
|
310
|
+
"""
|
|
311
|
+
# Quick file-size estimate: base64 expands by ~4/3, plus data URL header.
|
|
312
|
+
# Skip the expensive full-read + encode if Pillow can resize directly.
|
|
313
|
+
file_size = image_path.stat().st_size
|
|
314
|
+
estimated_b64 = (file_size * 4) // 3 + 100 # ~header overhead
|
|
315
|
+
if estimated_b64 <= max_base64_bytes:
|
|
316
|
+
# Small enough — just encode directly.
|
|
317
|
+
data_url = _image_to_base64_data_url(image_path, mime_type=mime_type)
|
|
318
|
+
if len(data_url) <= max_base64_bytes:
|
|
319
|
+
return data_url
|
|
320
|
+
else:
|
|
321
|
+
data_url = None # defer full encode; try Pillow resize first
|
|
322
|
+
|
|
323
|
+
# Attempt auto-resize with Pillow (soft dependency)
|
|
324
|
+
try:
|
|
325
|
+
from PIL import Image
|
|
326
|
+
import io as _io
|
|
327
|
+
except ImportError:
|
|
328
|
+
logger.info("Pillow not installed — cannot auto-resize oversized image")
|
|
329
|
+
if data_url is None:
|
|
330
|
+
data_url = _image_to_base64_data_url(image_path, mime_type=mime_type)
|
|
331
|
+
return data_url # caller will raise the size error
|
|
332
|
+
|
|
333
|
+
logger.info("Image file is %.1f MB (estimated base64 %.1f MB, limit %.1f MB), auto-resizing...",
|
|
334
|
+
file_size / (1024 * 1024), estimated_b64 / (1024 * 1024),
|
|
335
|
+
max_base64_bytes / (1024 * 1024))
|
|
336
|
+
|
|
337
|
+
mime = mime_type or _determine_mime_type(image_path)
|
|
338
|
+
# Choose output format: JPEG for photos (smaller), PNG for transparency
|
|
339
|
+
pil_format = "PNG" if mime == "image/png" else "JPEG"
|
|
340
|
+
out_mime = "image/png" if pil_format == "PNG" else "image/jpeg"
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
img = Image.open(image_path)
|
|
344
|
+
except Exception as exc:
|
|
345
|
+
logger.info("Pillow cannot open image for resizing: %s", exc)
|
|
346
|
+
if data_url is None:
|
|
347
|
+
data_url = _image_to_base64_data_url(image_path, mime_type=mime_type)
|
|
348
|
+
return data_url # fall through to size-check in caller
|
|
349
|
+
# Convert RGBA to RGB for JPEG output
|
|
350
|
+
if pil_format == "JPEG" and img.mode in {"RGBA", "P"}:
|
|
351
|
+
img = img.convert("RGB")
|
|
352
|
+
|
|
353
|
+
# Strategy: halve dimensions until base64 fits, up to 4 rounds.
|
|
354
|
+
# For JPEG, also try reducing quality at each size step.
|
|
355
|
+
# For PNG, quality is irrelevant — only dimension reduction helps.
|
|
356
|
+
quality_steps = (85, 70, 50) if pil_format == "JPEG" else (None,)
|
|
357
|
+
prev_dims = (img.width, img.height)
|
|
358
|
+
candidate = None # will be set on first loop iteration
|
|
359
|
+
|
|
360
|
+
for attempt in range(5):
|
|
361
|
+
if attempt > 0:
|
|
362
|
+
# Proportional scaling: halve the longer side and scale the
|
|
363
|
+
# shorter side to preserve aspect ratio (min dimension 64).
|
|
364
|
+
scale = 0.5
|
|
365
|
+
new_w = max(int(img.width * scale), 64)
|
|
366
|
+
new_h = max(int(img.height * scale), 64)
|
|
367
|
+
# Re-derive the scale from whichever dimension hit the floor
|
|
368
|
+
# so both axes shrink by the same factor.
|
|
369
|
+
if new_w == 64 and img.width > 0:
|
|
370
|
+
effective_scale = 64 / img.width
|
|
371
|
+
new_h = max(int(img.height * effective_scale), 64)
|
|
372
|
+
elif new_h == 64 and img.height > 0:
|
|
373
|
+
effective_scale = 64 / img.height
|
|
374
|
+
new_w = max(int(img.width * effective_scale), 64)
|
|
375
|
+
# Stop if dimensions can't shrink further
|
|
376
|
+
if (new_w, new_h) == prev_dims:
|
|
377
|
+
break
|
|
378
|
+
img = img.resize((new_w, new_h), Image.LANCZOS)
|
|
379
|
+
prev_dims = (new_w, new_h)
|
|
380
|
+
logger.info("Resized to %dx%d (attempt %d)", new_w, new_h, attempt)
|
|
381
|
+
|
|
382
|
+
for q in quality_steps:
|
|
383
|
+
buf = _io.BytesIO()
|
|
384
|
+
save_kwargs = {"format": pil_format}
|
|
385
|
+
if q is not None:
|
|
386
|
+
save_kwargs["quality"] = q
|
|
387
|
+
img.save(buf, **save_kwargs)
|
|
388
|
+
encoded = base64.b64encode(buf.getvalue()).decode("ascii")
|
|
389
|
+
candidate = f"data:{out_mime};base64,{encoded}"
|
|
390
|
+
if len(candidate) <= max_base64_bytes:
|
|
391
|
+
logger.info("Auto-resized image fits: %.1f MB (quality=%s, %dx%d)",
|
|
392
|
+
len(candidate) / (1024 * 1024), q,
|
|
393
|
+
img.width, img.height)
|
|
394
|
+
return candidate
|
|
395
|
+
|
|
396
|
+
# If we still can't get it small enough, return the best attempt
|
|
397
|
+
# and let the caller decide
|
|
398
|
+
if candidate is not None:
|
|
399
|
+
logger.warning("Auto-resize could not fit image under %.1f MB (best: %.1f MB)",
|
|
400
|
+
max_base64_bytes / (1024 * 1024), len(candidate) / (1024 * 1024))
|
|
401
|
+
return candidate
|
|
402
|
+
|
|
403
|
+
# Shouldn't reach here, but fall back to full encode
|
|
404
|
+
return data_url or _image_to_base64_data_url(image_path, mime_type=mime_type)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ---------------------------------------------------------------------------
|
|
408
|
+
# Native fast path: short-circuit the auxiliary LLM when the active main model
|
|
409
|
+
# supports native vision. Instead of asking a separate LLM to describe the
|
|
410
|
+
# image and returning text, we load the image, base64-encode it, and return a
|
|
411
|
+
# multimodal tool-result envelope. The agent loop unwraps the envelope into an
|
|
412
|
+
# OpenAI-style content list on the `tool` role; provider adapters (anthropic,
|
|
413
|
+
# codex_responses, chat_completions) translate that into Anthropic
|
|
414
|
+
# tool_result image blocks / Responses input_image / OpenAI image_url tool
|
|
415
|
+
# content. The main model then "sees" the pixels directly on its next turn.
|
|
416
|
+
# ---------------------------------------------------------------------------
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _supports_media_in_tool_results(provider: str, model: str) -> bool:
|
|
420
|
+
"""Whether the given provider+model combination accepts image content
|
|
421
|
+
inside a tool-result message.
|
|
422
|
+
|
|
423
|
+
Providers covered today (per spec docs verified Apr-2026):
|
|
424
|
+
|
|
425
|
+
* Anthropic Messages API (``anthropic`` provider, plus aggregators that
|
|
426
|
+
proxy Claude — ``openrouter``, ``nous``, ``vertex``, ``bedrock``):
|
|
427
|
+
``tool_result`` blocks accept ``image`` content blocks.
|
|
428
|
+
* OpenAI Chat Completions: tool messages accept array content with
|
|
429
|
+
``image_url`` parts.
|
|
430
|
+
* OpenAI Responses (``openai-codex``): ``function_call_output.output``
|
|
431
|
+
accepts an array of ``input_text``/``input_image`` items.
|
|
432
|
+
* Gemini 3 (and proxied via aggregators): supports multimodal tool
|
|
433
|
+
results. Older Gemini does NOT.
|
|
434
|
+
|
|
435
|
+
For unknown / legacy providers we conservatively return False — the
|
|
436
|
+
caller falls back to the legacy aux-LLM text path.
|
|
437
|
+
"""
|
|
438
|
+
if not isinstance(provider, str):
|
|
439
|
+
return False
|
|
440
|
+
p = provider.strip().lower()
|
|
441
|
+
if not p:
|
|
442
|
+
return False
|
|
443
|
+
|
|
444
|
+
# Aggregators that route to multiple vendors — assume support since
|
|
445
|
+
# users on these aggregators are typically using vision-capable
|
|
446
|
+
# frontier models. Falling back to text would be a regression for
|
|
447
|
+
# them.
|
|
448
|
+
_AGGREGATORS = {
|
|
449
|
+
"openrouter", "nous", "vertex", "bedrock", "anthropic-vertex",
|
|
450
|
+
"google-vertex",
|
|
451
|
+
}
|
|
452
|
+
if p in _AGGREGATORS:
|
|
453
|
+
return True
|
|
454
|
+
|
|
455
|
+
# Native Anthropic
|
|
456
|
+
if p in {"anthropic", "claude", "anthropic-direct"}:
|
|
457
|
+
return True
|
|
458
|
+
|
|
459
|
+
# OpenAI Chat Completions and Responses
|
|
460
|
+
if p in {"openai", "openai-chat", "openai-codex", "azure-openai"}:
|
|
461
|
+
return True
|
|
462
|
+
|
|
463
|
+
# Gemini — gate on model name; older Gemini variants did not support
|
|
464
|
+
# multimodal functionResponse. Gemini 3.x does.
|
|
465
|
+
if p in {"google", "gemini", "google-gemini", "google-vertex-gemini"}:
|
|
466
|
+
if not isinstance(model, str):
|
|
467
|
+
return False
|
|
468
|
+
m = model.strip().lower()
|
|
469
|
+
if "gemini-3" in m or "gemini-pro-3" in m or "gemini-flash-3" in m:
|
|
470
|
+
return True
|
|
471
|
+
return False
|
|
472
|
+
|
|
473
|
+
# Other vision-capable provider stacks. Conservative default: False.
|
|
474
|
+
# Add explicit entries here as we verify each provider's tool-result
|
|
475
|
+
# multimodal support empirically.
|
|
476
|
+
return False
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _build_native_vision_tool_result(
|
|
480
|
+
image_url: str,
|
|
481
|
+
question: str,
|
|
482
|
+
image_data_url: str,
|
|
483
|
+
image_size_bytes: int,
|
|
484
|
+
) -> Dict[str, Any]:
|
|
485
|
+
"""Build the multimodal tool-result envelope returned by the fast path.
|
|
486
|
+
|
|
487
|
+
Shape:
|
|
488
|
+
{
|
|
489
|
+
"_multimodal": True,
|
|
490
|
+
"content": [
|
|
491
|
+
{"type": "text", "text": "<short note + the user's question>"},
|
|
492
|
+
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
|
|
493
|
+
],
|
|
494
|
+
"text_summary": "<plain-text fallback>",
|
|
495
|
+
"meta": {"image_url": ..., "size_bytes": N},
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
The text part exists for two reasons: (1) it gives the model an
|
|
499
|
+
instruction to act on now that the pixels are in context, and
|
|
500
|
+
(2) providers that don't support multimodal tool results can fall back
|
|
501
|
+
to ``text_summary``.
|
|
502
|
+
"""
|
|
503
|
+
# The tool-result text part is intentionally minimal. The model already
|
|
504
|
+
# has the user's original question in context; this just acknowledges
|
|
505
|
+
# the image is now visible and reminds it what it was asked.
|
|
506
|
+
text_part = (
|
|
507
|
+
"Image loaded into your context — you can see it natively now. "
|
|
508
|
+
"Use your built-in vision to answer the user."
|
|
509
|
+
)
|
|
510
|
+
if isinstance(question, str) and question.strip():
|
|
511
|
+
text_part += f"\n\nQuestion: {question.strip()}"
|
|
512
|
+
|
|
513
|
+
summary = (
|
|
514
|
+
f"Image attached natively for the main model "
|
|
515
|
+
f"({image_size_bytes / 1024:.1f} KB). "
|
|
516
|
+
"Answer using built-in vision."
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
"_multimodal": True,
|
|
521
|
+
"content": [
|
|
522
|
+
{"type": "text", "text": text_part},
|
|
523
|
+
{"type": "image_url", "image_url": {"url": image_data_url}},
|
|
524
|
+
],
|
|
525
|
+
"text_summary": summary,
|
|
526
|
+
"meta": {
|
|
527
|
+
"image_url": image_url[:200],
|
|
528
|
+
"size_bytes": image_size_bytes,
|
|
529
|
+
"native_vision": True,
|
|
530
|
+
},
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
async def _vision_analyze_native(
|
|
535
|
+
image_url: str,
|
|
536
|
+
question: str,
|
|
537
|
+
) -> Any:
|
|
538
|
+
"""Fast path for vision-capable main models.
|
|
539
|
+
|
|
540
|
+
Loads the image (local file OR remote URL), base64-encodes it, and
|
|
541
|
+
returns a multimodal tool-result envelope. The agent loop unwraps it;
|
|
542
|
+
provider adapters serialize it into the right tool-result-with-image
|
|
543
|
+
shape for each backend.
|
|
544
|
+
|
|
545
|
+
Returns:
|
|
546
|
+
A ``_multimodal`` envelope dict on success.
|
|
547
|
+
A JSON error string on failure (matches the existing tool-result
|
|
548
|
+
contract so the agent loop displays errors normally).
|
|
549
|
+
"""
|
|
550
|
+
if not isinstance(image_url, str) or not image_url.strip():
|
|
551
|
+
return tool_error("image_url is required", success=False)
|
|
552
|
+
|
|
553
|
+
temp_image_path: Optional[Path] = None
|
|
554
|
+
should_cleanup = False
|
|
555
|
+
try:
|
|
556
|
+
from tools.interrupt import is_interrupted
|
|
557
|
+
if is_interrupted():
|
|
558
|
+
return tool_error("Interrupted", success=False)
|
|
559
|
+
|
|
560
|
+
# Resolve the image source (mirrors vision_analyze_tool's logic
|
|
561
|
+
# exactly so behaviour is consistent).
|
|
562
|
+
resolved_url = image_url
|
|
563
|
+
if resolved_url.startswith("file://"):
|
|
564
|
+
resolved_url = resolved_url[len("file://"):]
|
|
565
|
+
local_path = Path(os.path.expanduser(resolved_url))
|
|
566
|
+
|
|
567
|
+
if local_path.is_file():
|
|
568
|
+
temp_image_path = local_path
|
|
569
|
+
should_cleanup = False
|
|
570
|
+
elif _validate_image_url(image_url):
|
|
571
|
+
blocked = check_website_access(image_url)
|
|
572
|
+
if blocked:
|
|
573
|
+
return tool_error(blocked["message"], success=False)
|
|
574
|
+
temp_dir = get_hermes_dir("cache/vision", "temp_vision_images")
|
|
575
|
+
temp_image_path = temp_dir / f"temp_image_{uuid.uuid4()}.jpg"
|
|
576
|
+
await _download_image(image_url, temp_image_path)
|
|
577
|
+
should_cleanup = True
|
|
578
|
+
else:
|
|
579
|
+
return tool_error(
|
|
580
|
+
"Invalid image source. Provide an HTTP/HTTPS URL or a "
|
|
581
|
+
"valid local file path.",
|
|
582
|
+
success=False,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
image_size_bytes = temp_image_path.stat().st_size
|
|
586
|
+
detected_mime_type = _detect_image_mime_type(temp_image_path)
|
|
587
|
+
if not detected_mime_type:
|
|
588
|
+
return tool_error(
|
|
589
|
+
"Only real image files are supported for vision analysis.",
|
|
590
|
+
success=False,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
image_data_url = _image_to_base64_data_url(
|
|
594
|
+
temp_image_path, mime_type=detected_mime_type,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
# Honour the same hard cap as the legacy path. Resize if needed.
|
|
598
|
+
if len(image_data_url) > _MAX_BASE64_BYTES:
|
|
599
|
+
image_data_url = _resize_image_for_vision(
|
|
600
|
+
temp_image_path, mime_type=detected_mime_type,
|
|
601
|
+
)
|
|
602
|
+
if len(image_data_url) > _MAX_BASE64_BYTES:
|
|
603
|
+
return tool_error(
|
|
604
|
+
f"Image too large for vision API: base64 payload is "
|
|
605
|
+
f"{len(image_data_url) / (1024 * 1024):.1f} MB "
|
|
606
|
+
f"(limit {_MAX_BASE64_BYTES / (1024 * 1024):.0f} MB) "
|
|
607
|
+
f"even after resizing. Install Pillow "
|
|
608
|
+
f"(`pip install Pillow`) for better auto-resize, "
|
|
609
|
+
f"or compress the image manually.",
|
|
610
|
+
success=False,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
return _build_native_vision_tool_result(
|
|
614
|
+
image_url=image_url,
|
|
615
|
+
question=question,
|
|
616
|
+
image_data_url=image_data_url,
|
|
617
|
+
image_size_bytes=image_size_bytes,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
except Exception as exc:
|
|
621
|
+
logger.warning("Native vision fast path failed: %s", exc)
|
|
622
|
+
return tool_error(f"Native vision failed: {exc}", success=False)
|
|
623
|
+
finally:
|
|
624
|
+
# Only delete temp files we created — never user-provided paths.
|
|
625
|
+
if should_cleanup and temp_image_path is not None:
|
|
626
|
+
try:
|
|
627
|
+
if temp_image_path.exists():
|
|
628
|
+
temp_image_path.unlink()
|
|
629
|
+
except Exception:
|
|
630
|
+
pass
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
async def vision_analyze_tool(
|
|
634
|
+
image_url: str,
|
|
635
|
+
user_prompt: str,
|
|
636
|
+
model: str = None,
|
|
637
|
+
) -> str:
|
|
638
|
+
"""
|
|
639
|
+
Analyze an image from a URL or local file path using vision AI.
|
|
640
|
+
|
|
641
|
+
This tool accepts either an HTTP/HTTPS URL or a local file path. For URLs,
|
|
642
|
+
it downloads the image first. In both cases, the image is converted to base64
|
|
643
|
+
and processed using Gemini 3 Flash Preview via OpenRouter API.
|
|
644
|
+
|
|
645
|
+
The user_prompt parameter is expected to be pre-formatted by the calling
|
|
646
|
+
function (typically model_tools.py) to include both full description
|
|
647
|
+
requests and specific questions.
|
|
648
|
+
|
|
649
|
+
Args:
|
|
650
|
+
image_url (str): The URL or local file path of the image to analyze.
|
|
651
|
+
Accepts http://, https:// URLs or absolute/relative file paths.
|
|
652
|
+
user_prompt (str): The pre-formatted prompt for the vision model
|
|
653
|
+
model (str): The vision model to use (default: google/gemini-3-flash-preview)
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
str: JSON string containing the analysis results with the following structure:
|
|
657
|
+
{
|
|
658
|
+
"success": bool,
|
|
659
|
+
"analysis": str (defaults to error message if None)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
Raises:
|
|
663
|
+
Exception: If download fails, analysis fails, or API key is not set
|
|
664
|
+
|
|
665
|
+
Note:
|
|
666
|
+
- For URLs, temporary images are stored under $HERMES_HOME/cache/vision/ and cleaned up
|
|
667
|
+
- For local file paths, the file is used directly and NOT deleted
|
|
668
|
+
- Supports common image formats (JPEG, PNG, GIF, WebP, etc.)
|
|
669
|
+
"""
|
|
670
|
+
if not isinstance(user_prompt, str):
|
|
671
|
+
user_prompt = str(user_prompt) if user_prompt is not None else ""
|
|
672
|
+
debug_call_data = {
|
|
673
|
+
"parameters": {
|
|
674
|
+
"image_url": image_url,
|
|
675
|
+
"user_prompt": user_prompt[:200] + "..." if len(user_prompt) > 200 else user_prompt,
|
|
676
|
+
"model": model
|
|
677
|
+
},
|
|
678
|
+
"error": None,
|
|
679
|
+
"success": False,
|
|
680
|
+
"analysis_length": 0,
|
|
681
|
+
"model_used": model,
|
|
682
|
+
"image_size_bytes": 0
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
temp_image_path = None
|
|
686
|
+
# Track whether we should clean up the file after processing.
|
|
687
|
+
# Local files (e.g. from the image cache) should NOT be deleted.
|
|
688
|
+
should_cleanup = True
|
|
689
|
+
detected_mime_type = None
|
|
690
|
+
|
|
691
|
+
try:
|
|
692
|
+
from tools.interrupt import is_interrupted
|
|
693
|
+
if is_interrupted():
|
|
694
|
+
return tool_error("Interrupted", success=False)
|
|
695
|
+
|
|
696
|
+
logger.info("Analyzing image: %s", image_url[:60])
|
|
697
|
+
logger.info("User prompt: %s", user_prompt[:100])
|
|
698
|
+
|
|
699
|
+
# Determine if this is a local file path or a remote URL
|
|
700
|
+
# Strip file:// scheme so file URIs resolve as local paths.
|
|
701
|
+
resolved_url = image_url
|
|
702
|
+
if resolved_url.startswith("file://"):
|
|
703
|
+
resolved_url = resolved_url[len("file://"):]
|
|
704
|
+
local_path = Path(os.path.expanduser(resolved_url))
|
|
705
|
+
if local_path.is_file():
|
|
706
|
+
# Local file path (e.g. from platform image cache) -- skip download
|
|
707
|
+
logger.info("Using local image file: %s", image_url)
|
|
708
|
+
temp_image_path = local_path
|
|
709
|
+
should_cleanup = False # Don't delete cached/local files
|
|
710
|
+
elif _validate_image_url(image_url):
|
|
711
|
+
# Remote URL -- download to a temporary location
|
|
712
|
+
blocked = check_website_access(image_url)
|
|
713
|
+
if blocked:
|
|
714
|
+
raise PermissionError(blocked["message"])
|
|
715
|
+
logger.info("Downloading image from URL...")
|
|
716
|
+
temp_dir = get_hermes_dir("cache/vision", "temp_vision_images")
|
|
717
|
+
temp_image_path = temp_dir / f"temp_image_{uuid.uuid4()}.jpg"
|
|
718
|
+
await _download_image(image_url, temp_image_path)
|
|
719
|
+
should_cleanup = True
|
|
720
|
+
else:
|
|
721
|
+
raise ValueError(
|
|
722
|
+
"Invalid image source. Provide an HTTP/HTTPS URL or a valid local file path."
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
# Get image file size for logging
|
|
726
|
+
image_size_bytes = temp_image_path.stat().st_size
|
|
727
|
+
image_size_kb = image_size_bytes / 1024
|
|
728
|
+
logger.info("Image ready (%.1f KB)", image_size_kb)
|
|
729
|
+
|
|
730
|
+
detected_mime_type = _detect_image_mime_type(temp_image_path)
|
|
731
|
+
if not detected_mime_type:
|
|
732
|
+
raise ValueError("Only real image files are supported for vision analysis.")
|
|
733
|
+
|
|
734
|
+
# Convert image to base64 — send at full resolution first.
|
|
735
|
+
# If the provider rejects it as too large, we auto-resize and retry.
|
|
736
|
+
logger.info("Converting image to base64...")
|
|
737
|
+
image_data_url = _image_to_base64_data_url(temp_image_path, mime_type=detected_mime_type)
|
|
738
|
+
data_size_kb = len(image_data_url) / 1024
|
|
739
|
+
logger.info("Image converted to base64 (%.1f KB)", data_size_kb)
|
|
740
|
+
|
|
741
|
+
# Hard limit (20 MB) — no provider accepts payloads this large.
|
|
742
|
+
if len(image_data_url) > _MAX_BASE64_BYTES:
|
|
743
|
+
# Try to resize down to 5 MB before giving up.
|
|
744
|
+
image_data_url = _resize_image_for_vision(
|
|
745
|
+
temp_image_path, mime_type=detected_mime_type)
|
|
746
|
+
if len(image_data_url) > _MAX_BASE64_BYTES:
|
|
747
|
+
raise ValueError(
|
|
748
|
+
f"Image too large for vision API: base64 payload is "
|
|
749
|
+
f"{len(image_data_url) / (1024 * 1024):.1f} MB "
|
|
750
|
+
f"(limit {_MAX_BASE64_BYTES / (1024 * 1024):.0f} MB) "
|
|
751
|
+
f"even after resizing. "
|
|
752
|
+
f"Install Pillow (`pip install Pillow`) for better auto-resize, "
|
|
753
|
+
f"or compress the image manually."
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
debug_call_data["image_size_bytes"] = image_size_bytes
|
|
757
|
+
|
|
758
|
+
# Use the prompt as provided (model_tools.py now handles full description formatting)
|
|
759
|
+
comprehensive_prompt = user_prompt
|
|
760
|
+
|
|
761
|
+
# Prepare the message with base64-encoded image
|
|
762
|
+
messages = [
|
|
763
|
+
{
|
|
764
|
+
"role": "user",
|
|
765
|
+
"content": [
|
|
766
|
+
{
|
|
767
|
+
"type": "text",
|
|
768
|
+
"text": comprehensive_prompt
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
"type": "image_url",
|
|
772
|
+
"image_url": {
|
|
773
|
+
"url": image_data_url
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
]
|
|
777
|
+
}
|
|
778
|
+
]
|
|
779
|
+
|
|
780
|
+
logger.info("Processing image with vision model...")
|
|
781
|
+
|
|
782
|
+
# Call the vision API via centralized router.
|
|
783
|
+
# Read timeout from config.yaml (auxiliary.vision.timeout), default 120s.
|
|
784
|
+
# Local vision models (llama.cpp, ollama) can take well over 30s.
|
|
785
|
+
vision_timeout = 120.0
|
|
786
|
+
vision_temperature = 0.1
|
|
787
|
+
try:
|
|
788
|
+
from hermes_cli.config import cfg_get, load_config
|
|
789
|
+
_cfg = load_config()
|
|
790
|
+
_vision_cfg = cfg_get(_cfg, "auxiliary", "vision", default={})
|
|
791
|
+
_vt = _vision_cfg.get("timeout")
|
|
792
|
+
if _vt is not None:
|
|
793
|
+
vision_timeout = float(_vt)
|
|
794
|
+
_vtemp = _vision_cfg.get("temperature")
|
|
795
|
+
if _vtemp is not None:
|
|
796
|
+
vision_temperature = float(_vtemp)
|
|
797
|
+
except Exception:
|
|
798
|
+
pass
|
|
799
|
+
call_kwargs = {
|
|
800
|
+
"task": "vision",
|
|
801
|
+
"messages": messages,
|
|
802
|
+
"temperature": vision_temperature,
|
|
803
|
+
"max_tokens": 2000,
|
|
804
|
+
"timeout": vision_timeout,
|
|
805
|
+
}
|
|
806
|
+
if model:
|
|
807
|
+
call_kwargs["model"] = model
|
|
808
|
+
# Try full-size image first; on size-related rejection, downscale and retry.
|
|
809
|
+
try:
|
|
810
|
+
response = await async_call_llm(**call_kwargs)
|
|
811
|
+
except Exception as _api_err:
|
|
812
|
+
if (_is_image_size_error(_api_err)
|
|
813
|
+
and len(image_data_url) > _RESIZE_TARGET_BYTES):
|
|
814
|
+
logger.info(
|
|
815
|
+
"API rejected image (%.1f MB, likely too large); "
|
|
816
|
+
"auto-resizing to ~%.0f MB and retrying...",
|
|
817
|
+
len(image_data_url) / (1024 * 1024),
|
|
818
|
+
_RESIZE_TARGET_BYTES / (1024 * 1024),
|
|
819
|
+
)
|
|
820
|
+
image_data_url = _resize_image_for_vision(
|
|
821
|
+
temp_image_path, mime_type=detected_mime_type)
|
|
822
|
+
messages[0]["content"][1]["image_url"]["url"] = image_data_url
|
|
823
|
+
response = await async_call_llm(**call_kwargs)
|
|
824
|
+
else:
|
|
825
|
+
raise
|
|
826
|
+
|
|
827
|
+
# Extract the analysis — fall back to reasoning if content is empty
|
|
828
|
+
analysis = extract_content_or_reasoning(response)
|
|
829
|
+
|
|
830
|
+
# Retry once on empty content (reasoning-only response)
|
|
831
|
+
if not analysis:
|
|
832
|
+
logger.warning("Vision LLM returned empty content, retrying once")
|
|
833
|
+
response = await async_call_llm(**call_kwargs)
|
|
834
|
+
analysis = extract_content_or_reasoning(response)
|
|
835
|
+
|
|
836
|
+
analysis_length = len(analysis)
|
|
837
|
+
|
|
838
|
+
logger.info("Image analysis completed (%s characters)", analysis_length)
|
|
839
|
+
|
|
840
|
+
# Prepare successful response
|
|
841
|
+
result = {
|
|
842
|
+
"success": True,
|
|
843
|
+
"analysis": analysis or "There was a problem with the request and the image could not be analyzed."
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
debug_call_data["success"] = True
|
|
847
|
+
debug_call_data["analysis_length"] = analysis_length
|
|
848
|
+
|
|
849
|
+
# Log debug information
|
|
850
|
+
_debug.log_call("vision_analyze_tool", debug_call_data)
|
|
851
|
+
_debug.save()
|
|
852
|
+
|
|
853
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
854
|
+
|
|
855
|
+
except Exception as e:
|
|
856
|
+
error_msg = f"Error analyzing image: {str(e)}"
|
|
857
|
+
logger.error("%s", error_msg, exc_info=True)
|
|
858
|
+
|
|
859
|
+
# Detect vision capability errors — give the model a clear message
|
|
860
|
+
# so it can inform the user instead of a cryptic API error.
|
|
861
|
+
err_str = str(e).lower()
|
|
862
|
+
if any(hint in err_str for hint in (
|
|
863
|
+
"402", "insufficient", "payment required", "credits", "billing",
|
|
864
|
+
)):
|
|
865
|
+
analysis = (
|
|
866
|
+
"Insufficient credits or payment required. Please top up your "
|
|
867
|
+
f"API provider account and try again. Error: {e}"
|
|
868
|
+
)
|
|
869
|
+
elif any(hint in err_str for hint in (
|
|
870
|
+
"does not support", "not support image",
|
|
871
|
+
"content_policy", "multimodal",
|
|
872
|
+
"unrecognized request argument", "image input",
|
|
873
|
+
)):
|
|
874
|
+
analysis = (
|
|
875
|
+
f"{model} does not support vision or our request was not "
|
|
876
|
+
f"accepted by the server. Error: {e}"
|
|
877
|
+
)
|
|
878
|
+
elif "invalid_request" in err_str or "image_url" in err_str:
|
|
879
|
+
analysis = (
|
|
880
|
+
"The vision API rejected the image. This can happen when the "
|
|
881
|
+
"image is in an unsupported format, corrupted, or still too "
|
|
882
|
+
"large after auto-resize. Try a smaller JPEG/PNG and retry. "
|
|
883
|
+
f"Error: {e}"
|
|
884
|
+
)
|
|
885
|
+
else:
|
|
886
|
+
analysis = (
|
|
887
|
+
"There was a problem with the request and the image could not "
|
|
888
|
+
f"be analyzed. Error: {e}"
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
# Prepare error response
|
|
892
|
+
result = {
|
|
893
|
+
"success": False,
|
|
894
|
+
"error": error_msg,
|
|
895
|
+
"analysis": analysis,
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
debug_call_data["error"] = error_msg
|
|
899
|
+
_debug.log_call("vision_analyze_tool", debug_call_data)
|
|
900
|
+
_debug.save()
|
|
901
|
+
|
|
902
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
903
|
+
|
|
904
|
+
finally:
|
|
905
|
+
# Clean up temporary image file (but NOT local/cached files)
|
|
906
|
+
if should_cleanup and temp_image_path and temp_image_path.exists():
|
|
907
|
+
try:
|
|
908
|
+
temp_image_path.unlink()
|
|
909
|
+
logger.debug("Cleaned up temporary image file")
|
|
910
|
+
except Exception as cleanup_error:
|
|
911
|
+
logger.warning(
|
|
912
|
+
"Could not delete temporary file: %s", cleanup_error, exc_info=True
|
|
913
|
+
)
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def check_vision_requirements() -> bool:
|
|
917
|
+
"""Check if the configured runtime vision path can resolve a client."""
|
|
918
|
+
try:
|
|
919
|
+
from agent.auxiliary_client import resolve_vision_provider_client
|
|
920
|
+
|
|
921
|
+
_provider, client, _model = resolve_vision_provider_client()
|
|
922
|
+
return client is not None
|
|
923
|
+
except Exception:
|
|
924
|
+
return False
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
if __name__ == "__main__":
|
|
929
|
+
"""
|
|
930
|
+
Simple test/demo when run directly
|
|
931
|
+
"""
|
|
932
|
+
print("👁️ Vision Tools Module")
|
|
933
|
+
print("=" * 40)
|
|
934
|
+
|
|
935
|
+
# Check if vision model is available
|
|
936
|
+
api_available = check_vision_requirements()
|
|
937
|
+
|
|
938
|
+
if not api_available:
|
|
939
|
+
print("❌ No auxiliary vision model available")
|
|
940
|
+
print("Configure a supported multimodal backend (OpenRouter, Nous, Codex, Anthropic, or a custom OpenAI-compatible endpoint).")
|
|
941
|
+
sys.exit(1)
|
|
942
|
+
else:
|
|
943
|
+
print("✅ Vision model available")
|
|
944
|
+
|
|
945
|
+
print("🛠️ Vision tools ready for use!")
|
|
946
|
+
|
|
947
|
+
# Show debug mode status
|
|
948
|
+
if _debug.active:
|
|
949
|
+
print(f"🐛 Debug mode ENABLED - Session ID: {_debug.session_id}")
|
|
950
|
+
print(f" Debug logs will be saved to: ./logs/vision_tools_debug_{_debug.session_id}.json")
|
|
951
|
+
else:
|
|
952
|
+
print("🐛 Debug mode disabled (set VISION_TOOLS_DEBUG=true to enable)")
|
|
953
|
+
|
|
954
|
+
print("\nBasic usage:")
|
|
955
|
+
print(" from vision_tools import vision_analyze_tool")
|
|
956
|
+
print(" import asyncio")
|
|
957
|
+
print("")
|
|
958
|
+
print(" async def main():")
|
|
959
|
+
print(" result = await vision_analyze_tool(")
|
|
960
|
+
print(" image_url='https://example.com/image.jpg',")
|
|
961
|
+
print(" user_prompt='What do you see in this image?'")
|
|
962
|
+
print(" )")
|
|
963
|
+
print(" print(result)")
|
|
964
|
+
print(" asyncio.run(main())")
|
|
965
|
+
|
|
966
|
+
print("\nExample prompts:")
|
|
967
|
+
print(" - 'What architectural style is this building?'")
|
|
968
|
+
print(" - 'Describe the emotions and mood in this image'")
|
|
969
|
+
print(" - 'What text can you read in this image?'")
|
|
970
|
+
print(" - 'Identify any safety hazards visible'")
|
|
971
|
+
print(" - 'What products or brands are shown?'")
|
|
972
|
+
|
|
973
|
+
print("\nDebug mode:")
|
|
974
|
+
print(" # Enable debug logging")
|
|
975
|
+
print(" export VISION_TOOLS_DEBUG=true")
|
|
976
|
+
print(" # Debug logs capture all vision analysis calls and results")
|
|
977
|
+
print(" # Logs saved to: ./logs/vision_tools_debug_UUID.json")
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
# ---------------------------------------------------------------------------
|
|
981
|
+
# Registry
|
|
982
|
+
# ---------------------------------------------------------------------------
|
|
983
|
+
from tools.registry import registry, tool_error
|
|
984
|
+
|
|
985
|
+
VISION_ANALYZE_SCHEMA = {
|
|
986
|
+
"name": "vision_analyze",
|
|
987
|
+
"description": (
|
|
988
|
+
"Load an image into the conversation so you can see it. Accepts a "
|
|
989
|
+
"URL, local file path, or data URL. When your active model has "
|
|
990
|
+
"native vision, the image is attached to your context directly "
|
|
991
|
+
"and you read the pixels yourself on the next turn — call this "
|
|
992
|
+
"any time the user references an image (filepath in their message, "
|
|
993
|
+
"URL in tool output, screenshot from the browser, etc.). For "
|
|
994
|
+
"non-vision models, falls back to an auxiliary vision model that "
|
|
995
|
+
"returns a text description."
|
|
996
|
+
),
|
|
997
|
+
"parameters": {
|
|
998
|
+
"type": "object",
|
|
999
|
+
"properties": {
|
|
1000
|
+
"image_url": {
|
|
1001
|
+
"type": "string",
|
|
1002
|
+
"description": "Image URL (http/https), local file path, or data: URL to load."
|
|
1003
|
+
},
|
|
1004
|
+
"question": {
|
|
1005
|
+
"type": "string",
|
|
1006
|
+
"description": "Your specific question or request about the image. Optional context the model uses on the next turn after seeing the image."
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
"required": ["image_url", "question"]
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
def _handle_vision_analyze(args: Dict[str, Any], **kw: Any) -> Awaitable[str]:
|
|
1015
|
+
image_url = args.get("image_url", "")
|
|
1016
|
+
question = args.get("question", "")
|
|
1017
|
+
|
|
1018
|
+
# Fast path: when the active main model supports native vision AND the
|
|
1019
|
+
# provider supports image content inside tool results, short-circuit
|
|
1020
|
+
# the auxiliary LLM and return the image bytes as a multimodal
|
|
1021
|
+
# tool-result envelope. The main model sees the pixels directly on its
|
|
1022
|
+
# next turn — no aux call, no information loss, no extra latency.
|
|
1023
|
+
try:
|
|
1024
|
+
from agent.auxiliary_client import _read_main_provider, _read_main_model
|
|
1025
|
+
from agent.image_routing import decide_image_input_mode
|
|
1026
|
+
from hermes_cli.config import load_config
|
|
1027
|
+
|
|
1028
|
+
_provider = _read_main_provider()
|
|
1029
|
+
_model = _read_main_model()
|
|
1030
|
+
_cfg = load_config()
|
|
1031
|
+
_mode = decide_image_input_mode(_provider, _model, _cfg)
|
|
1032
|
+
if _mode == "native" and _supports_media_in_tool_results(_provider, _model):
|
|
1033
|
+
logger.info(
|
|
1034
|
+
"vision_analyze: native fast path (provider=%s, model=%s)",
|
|
1035
|
+
_provider, _model,
|
|
1036
|
+
)
|
|
1037
|
+
return _vision_analyze_native(image_url, question)
|
|
1038
|
+
except Exception as exc:
|
|
1039
|
+
logger.debug("Native vision fast-path check failed; using aux LLM: %s", exc)
|
|
1040
|
+
|
|
1041
|
+
# Legacy path: aux LLM describes the image and we return its text.
|
|
1042
|
+
full_prompt = (
|
|
1043
|
+
"Fully describe and explain everything about this image, then answer the "
|
|
1044
|
+
f"following question:\n\n{question}"
|
|
1045
|
+
)
|
|
1046
|
+
model = os.getenv("AUXILIARY_VISION_MODEL", "").strip() or None
|
|
1047
|
+
return vision_analyze_tool(image_url, full_prompt, model)
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
registry.register(
|
|
1051
|
+
name="vision_analyze",
|
|
1052
|
+
toolset="vision",
|
|
1053
|
+
schema=VISION_ANALYZE_SCHEMA,
|
|
1054
|
+
handler=_handle_vision_analyze,
|
|
1055
|
+
check_fn=check_vision_requirements,
|
|
1056
|
+
is_async=True,
|
|
1057
|
+
emoji="👁️",
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
# ---------------------------------------------------------------------------
|
|
1062
|
+
# Video Analysis Tool
|
|
1063
|
+
# ---------------------------------------------------------------------------
|
|
1064
|
+
|
|
1065
|
+
# Extension → MIME. avi/mkv fall back to mp4.
|
|
1066
|
+
_VIDEO_MIME_TYPES = {
|
|
1067
|
+
".mp4": "video/mp4",
|
|
1068
|
+
".webm": "video/webm",
|
|
1069
|
+
".mov": "video/mov",
|
|
1070
|
+
".avi": "video/mp4",
|
|
1071
|
+
".mkv": "video/mp4",
|
|
1072
|
+
".mpeg": "video/mpeg",
|
|
1073
|
+
".mpg": "video/mpeg",
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
_MAX_VIDEO_BASE64_BYTES = 50 * 1024 * 1024 # 50 MB hard cap
|
|
1077
|
+
_VIDEO_SIZE_WARN_BYTES = 20 * 1024 * 1024
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
def _detect_video_mime_type(video_path: Path) -> Optional[str]:
|
|
1081
|
+
"""Return a video MIME type based on file extension, or None if unsupported."""
|
|
1082
|
+
ext = video_path.suffix.lower()
|
|
1083
|
+
return _VIDEO_MIME_TYPES.get(ext)
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
def _video_to_base64_data_url(video_path: Path, mime_type: Optional[str] = None) -> str:
|
|
1087
|
+
"""Convert a video file to a base64-encoded data URL."""
|
|
1088
|
+
data = video_path.read_bytes()
|
|
1089
|
+
encoded = base64.b64encode(data).decode("ascii")
|
|
1090
|
+
mime = mime_type or _VIDEO_MIME_TYPES.get(video_path.suffix.lower(), "video/mp4")
|
|
1091
|
+
return f"data:{mime};base64,{encoded}"
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
async def _download_video(video_url: str, destination: Path, max_retries: int = 3) -> Path:
|
|
1095
|
+
"""Download video from URL with SSRF protection and retry."""
|
|
1096
|
+
import asyncio
|
|
1097
|
+
|
|
1098
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
1099
|
+
|
|
1100
|
+
async def _ssrf_redirect_guard(response):
|
|
1101
|
+
if response.is_redirect and response.next_request:
|
|
1102
|
+
redirect_url = str(response.next_request.url)
|
|
1103
|
+
from tools.url_safety import is_safe_url
|
|
1104
|
+
if not is_safe_url(redirect_url):
|
|
1105
|
+
raise ValueError(
|
|
1106
|
+
f"Blocked redirect to private/internal address: {redirect_url}"
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
last_error = None
|
|
1110
|
+
for attempt in range(max_retries):
|
|
1111
|
+
try:
|
|
1112
|
+
blocked = check_website_access(video_url)
|
|
1113
|
+
if blocked:
|
|
1114
|
+
raise PermissionError(blocked["message"])
|
|
1115
|
+
|
|
1116
|
+
async with httpx.AsyncClient(
|
|
1117
|
+
timeout=60.0,
|
|
1118
|
+
follow_redirects=True,
|
|
1119
|
+
event_hooks={"response": [_ssrf_redirect_guard]},
|
|
1120
|
+
) as client:
|
|
1121
|
+
response = await client.get(
|
|
1122
|
+
video_url,
|
|
1123
|
+
headers={
|
|
1124
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
1125
|
+
"Accept": "video/*,*/*;q=0.8",
|
|
1126
|
+
},
|
|
1127
|
+
)
|
|
1128
|
+
response.raise_for_status()
|
|
1129
|
+
|
|
1130
|
+
cl = response.headers.get("content-length")
|
|
1131
|
+
if cl and int(cl) > _MAX_VIDEO_BASE64_BYTES:
|
|
1132
|
+
raise ValueError(
|
|
1133
|
+
f"Video too large ({int(cl)} bytes, max {_MAX_VIDEO_BASE64_BYTES})"
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
final_url = str(response.url)
|
|
1137
|
+
blocked = check_website_access(final_url)
|
|
1138
|
+
if blocked:
|
|
1139
|
+
raise PermissionError(blocked["message"])
|
|
1140
|
+
|
|
1141
|
+
body = response.content
|
|
1142
|
+
if len(body) > _MAX_VIDEO_BASE64_BYTES:
|
|
1143
|
+
raise ValueError(
|
|
1144
|
+
f"Video too large ({len(body)} bytes, max {_MAX_VIDEO_BASE64_BYTES})"
|
|
1145
|
+
)
|
|
1146
|
+
destination.write_bytes(body)
|
|
1147
|
+
|
|
1148
|
+
return destination
|
|
1149
|
+
except Exception as e:
|
|
1150
|
+
last_error = e
|
|
1151
|
+
if attempt < max_retries - 1:
|
|
1152
|
+
wait_time = 2 ** (attempt + 1)
|
|
1153
|
+
logger.warning("Video download failed (attempt %s/%s): %s", attempt + 1, max_retries, str(e)[:50])
|
|
1154
|
+
await asyncio.sleep(wait_time)
|
|
1155
|
+
else:
|
|
1156
|
+
logger.error(
|
|
1157
|
+
"Video download failed after %s attempts: %s",
|
|
1158
|
+
max_retries, str(e)[:100], exc_info=True,
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
if last_error is None:
|
|
1162
|
+
raise RuntimeError(
|
|
1163
|
+
f"_download_video exited retry loop without attempting (max_retries={max_retries})"
|
|
1164
|
+
)
|
|
1165
|
+
raise last_error
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
async def video_analyze_tool(
|
|
1169
|
+
video_url: str,
|
|
1170
|
+
user_prompt: str,
|
|
1171
|
+
model: str = None,
|
|
1172
|
+
) -> str:
|
|
1173
|
+
"""Analyze a video via multimodal LLM. Returns JSON {success, analysis}."""
|
|
1174
|
+
if not isinstance(user_prompt, str):
|
|
1175
|
+
user_prompt = str(user_prompt) if user_prompt is not None else ""
|
|
1176
|
+
debug_call_data = {
|
|
1177
|
+
"parameters": {
|
|
1178
|
+
"video_url": video_url,
|
|
1179
|
+
"user_prompt": user_prompt[:200] + "..." if len(user_prompt) > 200 else user_prompt,
|
|
1180
|
+
"model": model,
|
|
1181
|
+
},
|
|
1182
|
+
"error": None,
|
|
1183
|
+
"success": False,
|
|
1184
|
+
"analysis_length": 0,
|
|
1185
|
+
"model_used": model,
|
|
1186
|
+
"video_size_bytes": 0,
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
temp_video_path = None
|
|
1190
|
+
should_cleanup = True
|
|
1191
|
+
|
|
1192
|
+
try:
|
|
1193
|
+
from tools.interrupt import is_interrupted
|
|
1194
|
+
if is_interrupted():
|
|
1195
|
+
return tool_error("Interrupted", success=False)
|
|
1196
|
+
|
|
1197
|
+
logger.info("Analyzing video: %s", video_url[:60])
|
|
1198
|
+
logger.info("User prompt: %s", user_prompt[:100])
|
|
1199
|
+
|
|
1200
|
+
# Resolve local path vs remote URL
|
|
1201
|
+
resolved_url = video_url
|
|
1202
|
+
if resolved_url.startswith("file://"):
|
|
1203
|
+
resolved_url = resolved_url[len("file://"):]
|
|
1204
|
+
local_path = Path(os.path.expanduser(resolved_url))
|
|
1205
|
+
|
|
1206
|
+
if local_path.is_file():
|
|
1207
|
+
logger.info("Using local video file: %s", video_url)
|
|
1208
|
+
temp_video_path = local_path
|
|
1209
|
+
should_cleanup = False
|
|
1210
|
+
elif _validate_image_url(video_url):
|
|
1211
|
+
blocked = check_website_access(video_url)
|
|
1212
|
+
if blocked:
|
|
1213
|
+
raise PermissionError(blocked["message"])
|
|
1214
|
+
temp_dir = get_hermes_dir("cache/video", "temp_video_files")
|
|
1215
|
+
temp_video_path = temp_dir / f"temp_video_{uuid.uuid4()}.mp4"
|
|
1216
|
+
await _download_video(video_url, temp_video_path)
|
|
1217
|
+
should_cleanup = True
|
|
1218
|
+
else:
|
|
1219
|
+
raise ValueError(
|
|
1220
|
+
"Invalid video source. Provide an HTTP/HTTPS URL or a valid local file path."
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
video_size_bytes = temp_video_path.stat().st_size
|
|
1224
|
+
video_size_mb = video_size_bytes / (1024 * 1024)
|
|
1225
|
+
logger.info("Video ready (%.1f MB)", video_size_mb)
|
|
1226
|
+
|
|
1227
|
+
detected_mime = _detect_video_mime_type(temp_video_path)
|
|
1228
|
+
if not detected_mime:
|
|
1229
|
+
raise ValueError(
|
|
1230
|
+
f"Unsupported video format: '{temp_video_path.suffix}'. "
|
|
1231
|
+
f"Supported: {', '.join(sorted(_VIDEO_MIME_TYPES.keys()))}"
|
|
1232
|
+
)
|
|
1233
|
+
|
|
1234
|
+
if video_size_bytes > _VIDEO_SIZE_WARN_BYTES:
|
|
1235
|
+
logger.warning("Video is %.1f MB — may be slow or rejected", video_size_mb)
|
|
1236
|
+
|
|
1237
|
+
video_data_url = _video_to_base64_data_url(temp_video_path, mime_type=detected_mime)
|
|
1238
|
+
data_size_mb = len(video_data_url) / (1024 * 1024)
|
|
1239
|
+
|
|
1240
|
+
if len(video_data_url) > _MAX_VIDEO_BASE64_BYTES:
|
|
1241
|
+
raise ValueError(
|
|
1242
|
+
f"Video too large for API: base64 payload is {data_size_mb:.1f} MB "
|
|
1243
|
+
f"(limit {_MAX_VIDEO_BASE64_BYTES / (1024 * 1024):.0f} MB). "
|
|
1244
|
+
f"Compress or trim the video and retry."
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
debug_call_data["video_size_bytes"] = video_size_bytes
|
|
1248
|
+
|
|
1249
|
+
messages = [
|
|
1250
|
+
{
|
|
1251
|
+
"role": "user",
|
|
1252
|
+
"content": [
|
|
1253
|
+
{
|
|
1254
|
+
"type": "text",
|
|
1255
|
+
"text": user_prompt,
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
"type": "video_url",
|
|
1259
|
+
"video_url": {
|
|
1260
|
+
"url": video_data_url,
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
],
|
|
1264
|
+
}
|
|
1265
|
+
]
|
|
1266
|
+
|
|
1267
|
+
vision_timeout = 180.0
|
|
1268
|
+
vision_temperature = 0.1
|
|
1269
|
+
try:
|
|
1270
|
+
from hermes_cli.config import cfg_get, load_config
|
|
1271
|
+
_cfg = load_config()
|
|
1272
|
+
_vision_cfg = cfg_get(_cfg, "auxiliary", "vision", default={})
|
|
1273
|
+
_vt = _vision_cfg.get("timeout")
|
|
1274
|
+
if _vt is not None:
|
|
1275
|
+
vision_timeout = max(float(_vt), 180.0)
|
|
1276
|
+
_vtemp = _vision_cfg.get("temperature")
|
|
1277
|
+
if _vtemp is not None:
|
|
1278
|
+
vision_temperature = float(_vtemp)
|
|
1279
|
+
except Exception:
|
|
1280
|
+
pass
|
|
1281
|
+
|
|
1282
|
+
call_kwargs = {
|
|
1283
|
+
"task": "vision",
|
|
1284
|
+
"messages": messages,
|
|
1285
|
+
"temperature": vision_temperature,
|
|
1286
|
+
"max_tokens": 4000,
|
|
1287
|
+
"timeout": vision_timeout,
|
|
1288
|
+
}
|
|
1289
|
+
if model:
|
|
1290
|
+
call_kwargs["model"] = model
|
|
1291
|
+
|
|
1292
|
+
response = await async_call_llm(**call_kwargs)
|
|
1293
|
+
analysis = extract_content_or_reasoning(response)
|
|
1294
|
+
|
|
1295
|
+
if not analysis:
|
|
1296
|
+
logger.warning("Empty video response, retrying once")
|
|
1297
|
+
response = await async_call_llm(**call_kwargs)
|
|
1298
|
+
analysis = extract_content_or_reasoning(response)
|
|
1299
|
+
|
|
1300
|
+
analysis_length = len(analysis) if analysis else 0
|
|
1301
|
+
logger.info("Video analysis completed (%s characters)", analysis_length)
|
|
1302
|
+
|
|
1303
|
+
result = {
|
|
1304
|
+
"success": True,
|
|
1305
|
+
"analysis": analysis or "There was a problem with the request and the video could not be analyzed.",
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
debug_call_data["success"] = True
|
|
1309
|
+
debug_call_data["analysis_length"] = analysis_length
|
|
1310
|
+
_debug.log_call("video_analyze_tool", debug_call_data)
|
|
1311
|
+
_debug.save()
|
|
1312
|
+
|
|
1313
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
1314
|
+
|
|
1315
|
+
except Exception as e:
|
|
1316
|
+
error_msg = f"Error analyzing video: {str(e)}"
|
|
1317
|
+
logger.error("%s", error_msg, exc_info=True)
|
|
1318
|
+
|
|
1319
|
+
err_str = str(e).lower()
|
|
1320
|
+
if any(hint in err_str for hint in (
|
|
1321
|
+
"402", "insufficient", "payment required", "credits", "billing",
|
|
1322
|
+
)):
|
|
1323
|
+
analysis = (
|
|
1324
|
+
"Insufficient credits or payment required. Please top up your "
|
|
1325
|
+
f"API provider account and try again. Error: {e}"
|
|
1326
|
+
)
|
|
1327
|
+
elif any(hint in err_str for hint in (
|
|
1328
|
+
"does not support", "not support video",
|
|
1329
|
+
"content_policy", "multimodal",
|
|
1330
|
+
"unrecognized request argument", "video input",
|
|
1331
|
+
"video_url",
|
|
1332
|
+
)):
|
|
1333
|
+
analysis = (
|
|
1334
|
+
f"The model does not support video analysis or the request was "
|
|
1335
|
+
f"rejected. Ensure you're using a video-capable model "
|
|
1336
|
+
f"(e.g. google/gemini-2.5-flash). Error: {e}"
|
|
1337
|
+
)
|
|
1338
|
+
elif any(hint in err_str for hint in (
|
|
1339
|
+
"too large", "payload", "413", "content_too_large",
|
|
1340
|
+
"request_too_large", "exceeds", "size limit",
|
|
1341
|
+
)):
|
|
1342
|
+
analysis = (
|
|
1343
|
+
"The video is too large for the API. Try compressing or trimming "
|
|
1344
|
+
f"the video (max ~50 MB). Error: {e}"
|
|
1345
|
+
)
|
|
1346
|
+
else:
|
|
1347
|
+
analysis = (
|
|
1348
|
+
"There was a problem with the request and the video could not "
|
|
1349
|
+
f"be analyzed. Error: {e}"
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
result = {
|
|
1353
|
+
"success": False,
|
|
1354
|
+
"error": error_msg,
|
|
1355
|
+
"analysis": analysis,
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
debug_call_data["error"] = error_msg
|
|
1359
|
+
_debug.log_call("video_analyze_tool", debug_call_data)
|
|
1360
|
+
_debug.save()
|
|
1361
|
+
|
|
1362
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
1363
|
+
|
|
1364
|
+
finally:
|
|
1365
|
+
if should_cleanup and temp_video_path and temp_video_path.exists():
|
|
1366
|
+
try:
|
|
1367
|
+
temp_video_path.unlink()
|
|
1368
|
+
logger.debug("Cleaned up temporary video file")
|
|
1369
|
+
except Exception as cleanup_error:
|
|
1370
|
+
logger.warning(
|
|
1371
|
+
"Could not delete temporary file: %s", cleanup_error, exc_info=True
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
VIDEO_ANALYZE_SCHEMA = {
|
|
1376
|
+
"name": "video_analyze",
|
|
1377
|
+
"description": (
|
|
1378
|
+
"Analyze a video from a URL or local file path using a multimodal AI model. "
|
|
1379
|
+
"Sends the video to a video-capable model (e.g. Gemini) for understanding. "
|
|
1380
|
+
"Use this for video files — for images, use vision_analyze instead. "
|
|
1381
|
+
"Supports mp4, webm, mov, avi, mkv, mpeg formats. "
|
|
1382
|
+
"Note: large videos (>20 MB) may be slow; max ~50 MB."
|
|
1383
|
+
),
|
|
1384
|
+
"parameters": {
|
|
1385
|
+
"type": "object",
|
|
1386
|
+
"properties": {
|
|
1387
|
+
"video_url": {
|
|
1388
|
+
"type": "string",
|
|
1389
|
+
"description": "Video URL (http/https) or local file path to analyze.",
|
|
1390
|
+
},
|
|
1391
|
+
"question": {
|
|
1392
|
+
"type": "string",
|
|
1393
|
+
"description": "Your specific question about the video. The AI will describe what happens in the video and answer your question.",
|
|
1394
|
+
},
|
|
1395
|
+
},
|
|
1396
|
+
"required": ["video_url", "question"],
|
|
1397
|
+
},
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
|
|
1401
|
+
def _handle_video_analyze(args: Dict[str, Any], **kw: Any) -> Awaitable[str]:
|
|
1402
|
+
video_url = args.get("video_url", "")
|
|
1403
|
+
question = args.get("question", "")
|
|
1404
|
+
full_prompt = (
|
|
1405
|
+
"Fully describe and explain everything happening in this video, "
|
|
1406
|
+
"including visual content, motion, audio cues, text overlays, and scene "
|
|
1407
|
+
f"transitions. Then answer the following question:\n\n{question}"
|
|
1408
|
+
)
|
|
1409
|
+
model = os.getenv("AUXILIARY_VIDEO_MODEL", "").strip() or os.getenv("AUXILIARY_VISION_MODEL", "").strip() or None
|
|
1410
|
+
return video_analyze_tool(video_url, full_prompt, model)
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
registry.register(
|
|
1414
|
+
name="video_analyze",
|
|
1415
|
+
toolset="video",
|
|
1416
|
+
schema=VIDEO_ANALYZE_SCHEMA,
|
|
1417
|
+
handler=_handle_video_analyze,
|
|
1418
|
+
check_fn=check_vision_requirements,
|
|
1419
|
+
is_async=True,
|
|
1420
|
+
emoji="🎬",
|
|
1421
|
+
)
|
|
1422
|
+
|