diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7a49a62203..bae4299b1b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -178,3 +178,9 @@ jobs: - name: Run footgun checker run: python scripts/check-windows-footguns.py --all + + # The Sep 2026 decomposition kept old import paths alive for external plugins + # (PLUGIN-COMPAT blocks, see COMPAT_MANIFEST.md). They are removed on schedule by + # reverting one commit, so in-tree code must never depend on them. + - name: Forbid in-tree use of plugin-compat pointers + run: python scripts/check_compat_pointers.py diff --git a/COMPAT_MANIFEST.md b/COMPAT_MANIFEST.md new file mode 100644 index 0000000000..8d9abc25b0 --- /dev/null +++ b/COMPAT_MANIFEST.md @@ -0,0 +1,3843 @@ +# Plugin compatibility manifest (temporary) + +The September 2026 decomposition (PR #102117) split the large modules of Hermes Agent into focused +files. **Internal import paths are not a stable API**, and after that PR the names below are no +longer defined where they used to be. To give external plugin authors time to update, every name +is still importable from its OLD module through a `PLUGIN-COMPAT` block appended to that module. + +**This layer is temporary.** It was added as a single commit and will be removed, on the announced +date, by reverting that commit. Update your plugin to import from the `new location` column now. +Nothing inside this repository is allowed to use these pointers (`scripts/check_compat_pointers.py` +fails CI if it does). + +| kind | count | meaning | +|---|---|---| +| moved | 0 | name now defined in `new location`; re-exported from the old module | +| moved-lazy | 1172 | same, resolved lazily via `__getattr__` to avoid an import cycle | +| import | 592 | a third-party/stdlib name the old module used to expose; original import restored | +| restored-def | 266 | public name that was deleted as unused; its pre-decomposition definition is restored verbatim | +| restored-helper | 40 | private helper restored only because a restored-def above depends on it | +| restored-import | 16 | import re-added only because a restored-def above depends on it | +| module-stub | 3 | whole module deleted; stub re-exports from its replacement | +| unrestorable | 34 | not restorable (e.g. leaked loop variables); listed for completeness | + +## Names by old module + + +### `acp_adapter.auth` + +| name | kind | new location | +|---|---|---| +| `has_provider` | restored-def | `(deleted; BASE body restored)` | + +### `acp_adapter.edit_approval` + +| name | kind | new location | +|---|---|---| +| `FutureTimeout` | import | `concurrent.futures` | +| `clear_edit_approval_requester` | restored-def | `(deleted; BASE body restored)` | +| `get_edit_approval_requester` | restored-def | `(deleted; BASE body restored)` | + +### `acp_adapter.events` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `acp_adapter.server` + +| name | kind | new location | +|---|---|---| +| `ACP_MAX_MODELS_PER_PROVIDER` | moved-lazy | `acp_adapter.model_catalog` | +| `AgentThoughtChunk` | import | `acp.schema` | +| `AudioContentBlock` | import | `acp.schema` | +| `AvailableCommand` | import | `acp.schema` | +| `AvailableCommandsUpdate` | import | `acp.schema` | +| `BlobResourceContents` | import | `acp.schema` | +| `EmbeddedResourceContentBlock` | import | `acp.schema` | +| `ImageContentBlock` | import | `acp.schema` | +| `Path` | import | `pathlib` | +| `ResourceContentBlock` | import | `acp.schema` | +| `TextResourceContents` | import | `acp.schema` | +| `UnstructuredCommandInput` | import | `acp.schema` | +| `base64` | import | `base64` | +| `json` | import | `json` | +| `unquote` | import | `urllib.parse` | +| `urlparse` | import | `urllib.parse` | + +### `acp_adapter.session` + +| name | kind | new location | +|---|---|---| +| `Lock` | import | `threading` | + +### `agent` + +| name | kind | new location | +|---|---|---| +| `message_sanitization` | unrestorable | `no top-level definition on BASE` | + +### `agent.agent_init` + +| name | kind | new location | +|---|---|---| +| `ToolGuardrailDecision` | moved-lazy | `agent.tool_guardrails` | + +### `agent.agent_runtime_helpers` + +| name | kind | new location | +|---|---|---| +| `agent_runtime_owns_post_tool_hook` | restored-def | `(deleted; BASE body restored)` | +| `intent_ack_continuation_enabled` | restored-def | `(deleted; BASE body restored)` | + +### `agent.anthropic_adapter` + +| name | kind | new location | +|---|---|---| +| `CredentialPersistError` | moved-lazy | `agent.anthropic_credentials` | +| `Path` | import | `pathlib` | +| `Tuple` | import | `typing` | +| `base_url_host_matches` | moved-lazy | `utils` | +| `base_url_hostname` | moved-lazy | `utils` | +| `claude_code_credentials_path` | moved-lazy | `agent.anthropic_credentials` | +| `copy` | import | `copy` | +| `get_hermes_home` | moved-lazy | `hermes_constants` | +| `is_claude_code_token_valid` | moved-lazy | `agent.anthropic_credentials` | +| `is_rotation_consumed_uncommitted` | moved-lazy | `agent.anthropic_credentials` | +| `json` | import | `json` | +| `mark_rotation_consumed_uncommitted` | moved-lazy | `agent.anthropic_credentials` | +| `os` | import | `os` | +| `platform` | import | `platform` | +| `read_claude_code_credentials` | moved-lazy | `agent.anthropic_credentials` | +| `read_hermes_oauth_credentials` | moved-lazy | `agent.anthropic_credentials` | +| `refresh_anthropic_oauth_pure` | moved-lazy | `agent.anthropic_credentials` | +| `resolve_anthropic_token` | moved-lazy | `agent.anthropic_credentials` | +| `run_hermes_oauth_login_pure` | moved-lazy | `agent.anthropic_credentials` | +| `run_oauth_setup_token` | moved-lazy | `agent.anthropic_credentials` | +| `secrets` | import | `secrets` | +| `stat` | import | `stat` | +| `urlparse` | import | `urllib.parse` | + +### `agent.aux_accounting` + +| name | kind | new location | +|---|---|---| +| `get_accounting_context` | restored-def | `(deleted; BASE body restored)` | + +### `agent.auxiliary_client` + +| name | kind | new location | +|---|---|---| +| `NOUS_EXTRA_BODY` | restored-def | `(deleted; BASE body restored)` | +| `Path` | import | `pathlib` | +| `copy` | import | `copy` | +| `get_async_text_auxiliary_client` | restored-def | `(deleted; BASE body restored)` | + +### `agent.backend_identity` + +| name | kind | new location | +|---|---|---| +| `_REASON_SCOPES` | restored-helper | `(deleted; restored as a dependency of classify_failure_scope)` | +| `classify_failure_scope` | restored-def | `(deleted; BASE body restored)` | + +### `agent.background_review` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `is_background_review_enabled` | restored-def | `(deleted; BASE body restored)` | + +### `agent.bedrock_adapter` + +| name | kind | new location | +|---|---|---| +| `CONTEXT_OVERFLOW_PATTERNS` | restored-def | `(deleted; BASE body restored)` | +| `OVERLOAD_PATTERNS` | restored-def | `(deleted; BASE body restored)` | +| `THROTTLE_PATTERNS` | restored-def | `(deleted; BASE body restored)` | +| `call_converse_stream` | restored-def | `(deleted; BASE body restored)` | +| `classify_bedrock_error` | restored-def | `(deleted; BASE body restored)` | +| `is_context_overflow_error` | restored-helper | `(deleted; restored as a dependency of classify_bedrock_error)` | +| `is_context_overflow_error` | restored-def | `(deleted; BASE body restored)` | + +### `agent.bounded_response` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | +| `read_error_body_or_default` | restored-def | `(deleted; BASE body restored)` | + +### `agent.browser_provider` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `Optional` | import | `typing` | + +### `agent.browser_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.codex_runtime` + +| name | kind | new location | +|---|---|---| +| `run_codex_create_stream_fallback` | restored-def | `(deleted; BASE body restored)` | + +### `agent.coding_context` + +| name | kind | new location | +|---|---|---| +| `_PROFILES` | restored-helper | `(deleted; restored as a dependency of get_profile)` | +| `coding_system_blocks` | restored-def | `(deleted; BASE body restored)` | +| `get_profile` | restored-def | `(deleted; BASE body restored)` | + +### `agent.context_compressor` + +| name | kind | new location | +|---|---|---| +| `tool_result_id_variants` | moved-lazy | `agent.message_sanitization` | + +### `agent.conversation_compression` + +| name | kind | new location | +|---|---|---| +| `CompressionExecutorSaturatedError` | restored-def | `(deleted; BASE body restored)` | + +### `agent.conversation_loop` + +| name | kind | new location | +|---|---|---| +| `COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `FailoverReason` | moved-lazy | `agent.error_classifier` | +| `KawaiiSpinner` | moved-lazy | `agent.display` | +| `PARTIAL_STREAM_STUB_ID` | moved-lazy | `hermes_constants` | +| `PRE_API_COMPRESSION_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `adaptive_rate_limit_backoff` | moved-lazy | `agent.retry_utils` | +| `anchored_context_tokens` | moved-lazy | `agent.model_metadata` | +| `automatic_compaction_status_message` | moved-lazy | `agent.context_engine` | +| `capture_usage_anchor` | moved-lazy | `agent.model_metadata` | +| `classify_api_error` | moved-lazy | `agent.error_classifier` | +| `close_interrupted_tool_sequence` | moved-lazy | `agent.message_sanitization` | +| `coalesce_tool_call_id` | moved-lazy | `agent.message_sanitization` | +| `compose_user_api_content` | moved-lazy | `agent.turn_context` | +| `compression_blocked_transiently` | moved-lazy | `agent.conversation_compression` | +| `compression_skipped_due_to_lock` | moved-lazy | `agent.conversation_compression` | +| `context_compression_timed_out` | moved-lazy | `agent.conversation_compression` | +| `conversation_history_after_compression` | moved-lazy | `agent.conversation_compression` | +| `env_var_enabled` | moved-lazy | `utils` | +| `estimate_messages_tokens_rough` | moved-lazy | `agent.model_metadata` | +| `estimate_request_tokens_rough` | moved-lazy | `agent.model_metadata` | +| `estimate_usage_cost` | moved-lazy | `agent.usage_pricing` | +| `get_context_length_from_provider_error` | moved-lazy | `agent.model_metadata` | +| `has_incomplete_scratchpad` | moved-lazy | `agent.trajectory` | +| `is_output_cap_error` | moved-lazy | `agent.model_metadata` | +| `is_repetition_dominated` | moved-lazy | `agent.repetition_guard` | +| `is_zai_coding_overload_error` | moved-lazy | `agent.retry_utils` | +| `jittered_backoff` | moved-lazy | `agent.retry_utils` | +| `normalize_usage` | moved-lazy | `agent.usage_pricing` | +| `os` | import | `os` | +| `parse_available_output_tokens_from_error` | moved-lazy | `agent.model_metadata` | +| `random` | import | `random` | +| `reanchor_current_turn_user_idx` | moved-lazy | `agent.turn_context` | +| `save_context_length` | moved-lazy | `agent.model_metadata` | +| `serialized_messages_bytes` | moved-lazy | `agent.message_sanitization` | +| `splice_provider_projection` | moved-lazy | `agent.provider_projection` | +| `ssl` | import | `ssl` | +| `sys` | import | `sys` | +| `zai_coding_overload_retry_ceiling` | moved-lazy | `agent.retry_utils` | + +### `agent.credential_sources` + +| name | kind | new location | +|---|---|---| +| `register` | moved-lazy | `tools.slash_confirm` | + +### `agent.display` + +| name | kind | new location | +|---|---|---| +| `get_friendly_tool_labels` | restored-def | `(deleted; BASE body restored)` | + +### `agent.error_surface` + +| name | kind | new location | +|---|---|---| +| `LAYER_RUNTIME` | restored-def | `(deleted; BASE body restored)` | + +### `agent.estop` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `agent.file_safety` + +| name | kind | new location | +|---|---|---| +| `PROFILE_SCOPED_AREAS` | restored-def | `(deleted; BASE body restored)` | +| `classify_cross_profile_target` | restored-def | `(deleted; BASE body restored)` | +| `get_cross_profile_warning` | restored-def | `(deleted; BASE body restored)` | + +### `agent.image_gen_provider` + +| name | kind | new location | +|---|---|---| +| `base64` | import | `base64` | +| `datetime` | import | `datetime` | +| `uuid` | import | `uuid` | + +### `agent.image_gen_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.learning_graph_render` + +| name | kind | new location | +|---|---|---| +| `Grid` | restored-def | `(deleted; BASE body restored)` | +| `Run` | moved-lazy | `hermes_cli.kanban_db` | + +### `agent.lsp.eventlog` + +| name | kind | new location | +|---|---|---| +| `_announce_once` | restored-helper | `(deleted; restored as a dependency of log_no_server_configured)` | +| `_announced_no_server` | restored-helper | `(deleted; restored as a dependency of log_no_server_configured)` | +| `log_no_server_configured` | restored-def | `(deleted; BASE body restored)` | + +### `agent.lsp.protocol` + +| name | kind | new location | +|---|---|---| +| `ERROR_REQUEST_CANCELLED` | restored-def | `(deleted; BASE body restored)` | + +### `agent.monitoring.cron_health` + +| name | kind | new location | +|---|---|---| +| `GatewayHealthSnapshot` | moved-lazy | `agent.monitoring.gateway_health` | +| `hashlib` | import | `hashlib` | + +### `agent.monitoring.emitter` + +| name | kind | new location | +|---|---|---| +| `TelemetryEmitter` | restored-def | `(deleted; BASE body restored)` | + +### `agent.monitoring.gateway_health` + +| name | kind | new location | +|---|---|---| +| `redact_gateway_message` | restored-def | `(deleted; BASE body restored)` | + +### `agent.monitoring.gateway_health_export` + +| name | kind | new location | +|---|---|---| +| `re` | import | `re` | + +### `agent.nous_rate_guard` + +| name | kind | new location | +|---|---|---| +| `atomic_replace` | moved-lazy | `utils` | +| `tempfile` | import | `tempfile` | + +### `agent.outbound_webhooks` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `datetime` | import | `datetime` | +| `timezone` | import | `datetime` | + +### `agent.pet.generate.atlas` + +| name | kind | new location | +|---|---|---| +| `COLUMNS` | restored-def | `(deleted; BASE body restored)` | +| `FRAME_COUNTS` | restored-def | `(deleted; BASE body restored)` | +| `ROWS` | restored-def | `(deleted; BASE body restored)` | +| `atlas_to_webp_bytes` | restored-def | `(deleted; BASE body restored)` | +| `io` | restored-import | `io` | +| `io` | import | `io` | + +### `agent.prompt_builder` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | +| `org_id_of_path` | moved-lazy | `agent.skill_utils` | + +### `agent.prompt_cache_boundary` + +| name | kind | new location | +|---|---|---| +| `clear_stable_prefixes` | restored-def | `(deleted; BASE body restored)` | + +### `agent.proxy_sources.iron_proxy` + +| name | kind | new location | +|---|---|---| +| `stat` | import | `stat` | + +### `agent.reasoning_effort` + +| name | kind | new location | +|---|---|---| +| `CODEX_RESPONSES_EFFORTS` | restored-def | `(deleted; BASE body restored)` | + +### `agent.relay_llm` + +| name | kind | new location | +|---|---|---| +| `dataclass` | import | `dataclasses` | + +### `agent.relay_runtime` + +| name | kind | new location | +|---|---|---| +| `auto` | import | `enum` | +| `emit_mark` | restored-def | `(deleted; BASE body restored)` | +| `ensure_session` | restored-def | `(deleted; BASE body restored)` | +| `get_host` | restored-def | `(deleted; BASE body restored)` | +| `get_session_handle` | restored-def | `(deleted; BASE body restored)` | +| `run_in_session` | restored-def | `(deleted; BASE body restored)` | +| `run_in_session_async` | restored-def | `(deleted; BASE body restored)` | + +### `agent.relay_tools` + +| name | kind | new location | +|---|---|---| +| `asyncio` | import | `asyncio` | +| `inspect` | import | `inspect` | + +### `agent.review_idle_queue` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | + +### `agent.secret_sources._cache` + +| name | kind | new location | +|---|---|---| +| `FetchResult` | moved-lazy | `agent.secret_sources.base` | +| `is_valid_env_name` | moved-lazy | `agent.secret_sources.base` | + +### `agent.secret_sources.bitwarden` + +| name | kind | new location | +|---|---|---| +| `DiskCache` | moved-lazy | `agent.secret_sources._cache` | +| `apply_bitwarden_secrets` | restored-def | `(deleted; BASE body restored)` | +| `stat` | import | `stat` | + +### `agent.secret_sources.command` + +| name | kind | new location | +|---|---|---| +| `apply_command_secrets` | restored-def | `(deleted; BASE body restored)` | +| `get_command_secret` | restored-def | `(deleted; BASE body restored)` | +| `get_source_environment` | moved-lazy | `agent.secret_sources.base` | +| `list_command_secrets` | restored-def | `(deleted; BASE body restored)` | +| `parse_secret_output` | restored-helper | `(deleted; restored as a dependency of get_command_secret)` | +| `parse_secret_output` | restored-def | `(deleted; BASE body restored)` | + +### `agent.secret_sources.onepassword` + +| name | kind | new location | +|---|---|---| +| `DiskCache` | moved-lazy | `agent.secret_sources._cache` | +| `hashlib` | import | `hashlib` | + +### `agent.shell_hooks` + +| name | kind | new location | +|---|---|---| +| `shlex` | import | `shlex` | + +### `agent.skill_bundles` + +| name | kind | new location | +|---|---|---| +| `re` | import | `re` | + +### `agent.skill_utils` + +| name | kind | new location | +|---|---|---| +| `get_scan_ordered_skills_dirs` | restored-def | `(deleted; BASE body restored)` | + +### `agent.ssl_guard` + +| name | kind | new location | +|---|---|---| +| `verify_ca_bundle_with_fallback` | restored-def | `(deleted; BASE body restored)` | + +### `agent.system_prompt` + +| name | kind | new location | +|---|---|---| +| `OPENAI_MODEL_EXECUTION_GUIDANCE` | moved-lazy | `agent.prompt_builder` | + +### `agent.terminal_env_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.transcript_repair` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `agent.transcription_provider` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | +| `logger` | moved-lazy | `agent.i18n` | +| `logging` | import | `logging` | + +### `agent.transcription_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `Optional` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.transports.chat_completions` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | + +### `agent.transports.codex` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `Tuple` | import | `typing` | + +### `agent.transports.codex_app_server` + +| name | kind | new location | +|---|---|---| +| `field` | import | `dataclasses` | +| `time` | import | `time` | + +### `agent.tts_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `Optional` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.turn_context` + +| name | kind | new location | +|---|---|---| +| `IDLE_COMPACTION_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `PREFLIGHT_COMPRESSION_STATUS_TEMPLATE` | moved-lazy | `agent.conversation_compression` | +| `automatic_compaction_status_message` | moved-lazy | `agent.context_engine` | +| `compression_skipped_due_to_lock` | moved-lazy | `agent.conversation_compression` | +| `conversation_history_after_compression` | moved-lazy | `agent.conversation_compression` | + +### `agent.turn_retry_state` + +| name | kind | new location | +|---|---|---| +| `fields` | import | `dataclasses` | + +### `agent.usage_pricing` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_PRICING` | restored-def | `(deleted; BASE body restored)` | + +### `agent.video_gen_provider` + +| name | kind | new location | +|---|---|---| +| `base64` | import | `base64` | +| `datetime` | import | `datetime` | +| `uuid` | import | `uuid` | + +### `agent.video_gen_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `agent.web_search_provider` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `agent.web_search_registry` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `List` | import | `typing` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `threading` | import | `threading` | + +### `cli` + +| name | kind | new location | +|---|---|---| +| `AIAgent` | moved-lazy | `run_agent` | +| `CanonicalUsage` | moved-lazy | `agent.usage_pricing` | +| `CompletionsMenu` | import | `prompt_toolkit.layout.menus` | +| `Condition` | import | `prompt_toolkit.filters` | +| `ConditionalContainer` | import | `prompt_toolkit.layout` | +| `ConditionalProcessor` | import | `prompt_toolkit.layout.processors` | +| `DEFAULT_BROWSER_CDP_URL` | moved-lazy | `hermes_cli.browser_connect` | +| `Dimension` | import | `prompt_toolkit.layout.dimension` | +| `FileHistory` | import | `prompt_toolkit.history` | +| `FormattedTextControl` | import | `prompt_toolkit.layout` | +| `HERMES_AGENT_LOGO` | moved-lazy | `hermes_cli.banner` | +| `HERMES_CADUCEUS` | moved-lazy | `hermes_cli.banner` | +| `HSplit` | import | `prompt_toolkit.layout` | +| `KeyBindings` | import | `prompt_toolkit.key_binding` | +| `Layout` | import | `prompt_toolkit.layout` | +| `PTStyle` | import | `prompt_toolkit.styles` | +| `Panel` | import | `rich.panel` | +| `PasswordProcessor` | import | `prompt_toolkit.layout.processors` | +| `Processor` | import | `prompt_toolkit.layout.processors` | +| `SlashCommandAutoSuggest` | moved-lazy | `hermes_cli.commands_completion` | +| `SlashCommandCompleter` | moved-lazy | `hermes_cli.commands_completion` | +| `TextArea` | import | `prompt_toolkit.widgets` | +| `Transformation` | import | `prompt_toolkit.layout.processors` | +| `Window` | import | `prompt_toolkit.layout` | +| `WindowAlign` | import | `prompt_toolkit.layout` | +| `base64` | import | `base64` | +| `build_welcome_banner` | moved-lazy | `hermes_cli.banner` | +| `concurrent` | import | `concurrent.futures` | +| `copy` | import | `copy` | +| `display_hermes_home` | moved-lazy | `hermes_constants` | +| `estimate_usage_cost` | moved-lazy | `agent.usage_pricing` | +| `get_all_toolsets` | moved-lazy | `toolsets` | +| `get_job` | moved-lazy | `cron.jobs` | +| `get_toolset_for_tool` | moved-lazy | `model_tools` | +| `get_toolset_info` | moved-lazy | `toolsets` | +| `init_skin_from_config` | moved-lazy | `hermes_cli.skin_engine` | +| `is_browser_debug_ready` | moved-lazy | `hermes_cli.browser_connect` | +| `is_table_divider` | moved-lazy | `agent.markdown_tables` | +| `looks_like_table_row` | moved-lazy | `agent.markdown_tables` | +| `manual_chrome_debug_command` | moved-lazy | `hermes_cli.browser_connect` | +| `print_config_warnings` | moved-lazy | `hermes_cli.config` | +| `prompt_for_secret` | moved-lazy | `hermes_cli.callbacks` | +| `rich_box` | import | `rich` | +| `set_friendly_tool_labels` | moved-lazy | `agent.display` | +| `set_tool_preview_max_len` | moved-lazy | `agent.display` | +| `setup_logging` | moved-lazy | `hermes_logging` | +| `tempfile` | import | `tempfile` | +| `try_launch_chrome_debug` | unrestorable | `no top-level definition on BASE` | + +### `cron.jobs` + +| name | kind | new location | +|---|---|---| +| `clear_drift_alerted` | restored-def | `(deleted; BASE body restored)` | + +### `cron.scheduler` + +| name | kind | new location | +|---|---|---| +| `BOT_CHAT_PLATFORM` | moved-lazy | `cron.scheduler_delivery` | +| `SharedRouteAdapters` | moved-lazy | `cron.scheduler_preflight` | +| `asyncio` | import | `asyncio` | +| `cron_delivery_targets` | moved-lazy | `cron.scheduler_delivery` | +| `parse_bot_chat_deliver_token` | moved-lazy | `cron.scheduler_delivery` | +| `shutil` | import | `shutil` | +| `signal` | import | `signal` | + +### `cron.scheduler_provider` + +| name | kind | new location | +|---|---|---| +| `provider_supports_fire_cancel` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.browser_control_artifacts` + +| name | kind | new location | +|---|---|---| +| `ArtifactOverwrite` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.browser_control_broker` + +| name | kind | new location | +|---|---|---| +| `BROWSER_CONTROL_ALL_CAPABILITIES` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.config` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `gateway.delivery_ledger` + +| name | kind | new location | +|---|---|---| +| `debug_rows` | restored-def | `(deleted; BASE body restored)` | +| `json` | restored-import | `json` | +| `json` | import | `json` | + +### `gateway.disk_status` + +| name | kind | new location | +|---|---|---| +| `logger` | moved-lazy | `gateway.run` | +| `logging` | import | `logging` | + +### `gateway.hosted_room_discussion` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `gateway.hosted_room_driver` + +| name | kind | new location | +|---|---|---| +| `Iterator` | import | `typing` | +| `Path` | import | `pathlib` | +| `contextmanager` | import | `contextlib` | +| `re` | import | `re` | + +### `gateway.hosted_room_execution_policy` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `re` | import | `re` | + +### `gateway.hosted_room_peer` + +| name | kind | new location | +|---|---|---| +| `RoomLinkProbe` | restored-def | `(deleted; BASE body restored)` | +| `RoomLinkProbe` | restored-helper | `(deleted; restored as a dependency of select_room_link)` | +| `_LINK_PRIORITY` | restored-helper | `(deleted; restored as a dependency of select_room_link)` | +| `select_room_link` | restored-def | `(deleted; BASE body restored)` | +| `time` | import | `time` | + +### `gateway.hosted_room_replicas` + +| name | kind | new location | +|---|---|---| +| `MAX_EVENT_JSON_BYTES` | moved-lazy | `gateway.hosted_rooms` | +| `MAX_ROOM_ID_CHARS` | moved-lazy | `gateway.hosted_rooms` | +| `Path` | import | `pathlib` | +| `time` | import | `time` | + +### `gateway.hosted_rooms` + +| name | kind | new location | +|---|---|---| +| `Iterator` | import | `typing` | +| `NoReturn` | import | `typing` | +| `contextmanager` | import | `contextlib` | +| `time` | import | `time` | + +### `gateway.kanban_watchers` + +| name | kind | new location | +|---|---|---| +| `Callable` | import | `typing` | +| `Context` | import | `contextvars` | +| `logging` | import | `logging` | +| `re` | import | `re` | +| `sqlite3` | import | `sqlite3` | +| `t` | moved-lazy | `agent.i18n` | + +### `gateway.memory_status` + +| name | kind | new location | +|---|---|---| +| `logger` | moved-lazy | `gateway.run` | +| `logging` | import | `logging` | + +### `gateway.platforms.helpers` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | +| `TYPE_CHECKING` | import | `typing` | +| `TextBatchAggregator` | restored-def | `(deleted; BASE body restored)` | +| `asyncio` | restored-import | `asyncio` | +| `asyncio` | import | `asyncio` | + +### `gateway.platforms.qqbot` + +| name | kind | new location | +|---|---|---| +| `ApprovalSender` | unrestorable | `no top-level definition on BASE` | + +### `gateway.platforms.qqbot.adapter` + +| name | kind | new location | +|---|---|---| +| `base64` | import | `base64` | +| `mimetypes` | import | `mimetypes` | + +### `gateway.platforms.qqbot.chunked_upload` + +| name | kind | new location | +|---|---|---| +| `ApiRequestFn` | restored-def | `(deleted; BASE body restored)` | +| `Optional` | import | `typing` | +| `functools` | import | `functools` | + +### `gateway.platforms.qqbot.keyboards` + +| name | kind | new location | +|---|---|---| +| `ApprovalSender` | restored-def | `(deleted; BASE body restored)` | +| `Awaitable` | restored-import | `typing` | +| `Awaitable` | import | `typing` | +| `Callable` | restored-import | `typing` | +| `Callable` | import | `typing` | +| `PostMessageFn` | restored-helper | `(deleted; restored as a dependency of ApprovalSender)` | +| `PostMessageFn` | restored-def | `(deleted; BASE body restored)` | +| `logger` | restored-helper | `(deleted; restored as a dependency of ApprovalSender)` | +| `logger` | moved-lazy | `gateway.platforms.qqbot.adapter` | +| `logging` | restored-import | `logging` | +| `logging` | import | `logging` | + +### `gateway.platforms.signal` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_EXT_TO_MIME` | moved-lazy | `gateway.platforms.media_cache` | +| `TYPING_INTERVAL` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.platforms.weixin` + +| name | kind | new location | +|---|---|---| +| `MSG_TYPE_USER` | restored-def | `(deleted; BASE body restored)` | +| `struct` | import | `struct` | + +### `gateway.platforms.yuanbao` + +| name | kind | new location | +|---|---|---| +| `AUTH_FAILED_CODES` | restored-def | `(deleted; BASE body restored)` | +| `AUTH_RETRYABLE_CODES` | restored-def | `(deleted; BASE body restored)` | +| `FileUrlHandler` | restored-def | `(deleted; BASE body restored)` | +| `GroupQueryService` | restored-def | `(deleted; BASE body restored)` | +| `REPLY_REF_TTL_S` | restored-def | `(deleted; BASE body restored)` | +| `get_active_adapter` | restored-def | `(deleted; BASE body restored)` | +| `send_yuanbao_direct` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.platforms.yuanbao_media` + +| name | kind | new location | +|---|---|---| +| `COS_USE_ACCELERATE` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.platforms.yuanbao_proto` + +| name | kind | new location | +|---|---|---| +| `DEBUG_MODE` | restored-def | `(deleted; BASE body restored)` | +| `_encode_forward_msg` | restored-helper | `(deleted; restored as a dependency of encode_forward_msg_data)` | +| `_encode_forward_msg_content` | restored-helper | `(deleted; restored as a dependency of encode_forward_msg_data)` | +| `_encode_forward_multimedia` | restored-helper | `(deleted; restored as a dependency of encode_forward_msg_data)` | +| `encode_forward_msg_data` | restored-def | `(deleted; BASE body restored)` | +| `logger` | moved-lazy | `gateway.platforms.base` | +| `logging` | import | `logging` | + +### `gateway.relay` + +| name | kind | new location | +|---|---|---| +| `relay_bot_username` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.relay.adapter` + +| name | kind | new location | +|---|---|---| +| `cast` | import | `typing` | + +### `gateway.relay.auth` + +| name | kind | new location | +|---|---|---| +| `DELIVERY_SIG_HEADER` | restored-def | `(deleted; BASE body restored)` | +| `DELIVERY_TS_HEADER` | restored-def | `(deleted; BASE body restored)` | +| `Optional` | import | `typing` | +| `Sequence` | import | `typing` | +| `_DEFAULT_MAX_SKEW_SECONDS` | restored-helper | `(deleted; restored as a dependency of verify_delivery_signature)` | +| `_delivery_payload` | restored-helper | `(deleted; restored as a dependency of verify_delivery_signature)` | +| `_hmac_hex` | restored-helper | `(deleted; restored as a dependency of verify_delivery_signature)` | +| `verify_delivery_signature` | restored-def | `(deleted; BASE body restored)` | +| `verify_signature` | restored-helper | `(deleted; restored as a dependency of verify_delivery_signature)` | +| `verify_signature` | restored-def | `(deleted; BASE body restored)` | +| `verify_token` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.response_filters` + +| name | kind | new location | +|---|---|---| +| `SILENT_REPLY_TOKEN` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.restart_loop_guard` + +| name | kind | new location | +|---|---|---| +| `is_restart_loop_tripped` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.run` + +| name | kind | new location | +|---|---|---| +| `Awaitable` | import | `typing` | +| `Context` | import | `contextvars` | +| `DEFAULT_GATEWAY_POST_INTERRUPT_GRACE_TIMEOUT` | moved-lazy | `gateway.restart` | +| `DEFAULT_HEARTBEAT_INTERVAL_S` | moved-lazy | `gateway.shutdown_watchdog` | +| `DEFAULT_LEASE_WAIT` | moved-lazy | `gateway.turn_lease` | +| `DEFAULT_LOOP_WATCHDOG_INTERVAL_S` | moved-lazy | `gateway.shutdown_watchdog` | +| `DEFAULT_LOOP_WATCHDOG_MAX_STRIKES` | moved-lazy | `gateway.shutdown_watchdog` | +| `DEFAULT_LOOP_WATCHDOG_TIMEOUT_S` | moved-lazy | `gateway.shutdown_watchdog` | +| `EphemeralReply` | moved-lazy | `gateway.platforms.base` | +| `GATEWAY_FATAL_CONFIG_EXIT_CODE` | moved-lazy | `gateway.restart` | +| `GATEWAY_SERVICE_RESTART_EXIT_CODE` | moved-lazy | `gateway.restart` | +| `SessionEntry` | moved-lazy | `gateway.session` | +| `TranscriptReadError` | moved-lazy | `gateway.session_transcript` | +| `TurnContext` | moved-lazy | `gateway.turn_context` | +| `TurnLeaseTimeoutError` | moved-lazy | `gateway.turn_lease` | +| `TurnRunner` | moved-lazy | `gateway.run_turn_runner` | +| `Union` | import | `typing` | +| `arm_shutdown_watchdog` | moved-lazy | `gateway.shutdown_watchdog` | +| `atomic_json_write` | moved-lazy | `utils` | +| `base_url_hostname` | moved-lazy | `utils` | +| `build_auto_tts_output_path` | moved-lazy | `gateway.platforms.base` | +| `build_channel_continuity_note` | moved-lazy | `gateway.session` | +| `build_session_context` | moved-lazy | `gateway.session` | +| `build_session_context_prompt` | moved-lazy | `gateway.session` | +| `consume_detached_task_result` | moved-lazy | `agent.async_utils` | +| `faulthandler` | import | `faulthandler` | +| `functools` | import | `functools` | +| `inspect` | import | `inspect` | +| `is_global_startup_conflict` | moved-lazy | `gateway.restart` | +| `is_shared_multi_user_session` | moved-lazy | `gateway.session` | +| `is_truthy_value` | moved-lazy | `utils` | +| `load_dotenv` | import | `dotenv` | +| `looks_like_telegram_private_chat_id` | moved-lazy | `gateway.delivery` | +| `loop_heartbeat_forever` | moved-lazy | `gateway.shutdown_watchdog` | +| `merge_pending_message_event` | moved-lazy | `gateway.platforms.base` | +| `neutralize_untrusted_inline_text` | moved-lazy | `gateway.session` | +| `parse_cron_drain_timeout` | moved-lazy | `gateway.restart` | +| `parse_restart_after_turn_timeout` | moved-lazy | `gateway.restart` | +| `parse_restart_drain_timeout` | moved-lazy | `gateway.restart` | +| `parse_signal_interrupt_grace_timeout` | moved-lazy | `gateway.restart` | +| `project_compaction_message_for_display` | moved-lazy | `agent.compaction_display` | +| `queue` | import | `queue` | +| `repair_explicit_computer_use_media_paths` | moved-lazy | `gateway.media_repair` | +| `resolve_cron_drain_budget` | moved-lazy | `gateway.restart` | +| `resolve_delivery_transport` | moved-lazy | `gateway.delivery` | +| `resolve_shutdown_watchdog_delay` | moved-lazy | `gateway.shutdown_watchdog` | +| `start_loop_liveness_watchdog` | moved-lazy | `gateway.shutdown_watchdog` | +| `t` | moved-lazy | `agent.i18n` | +| `timedelta` | import | `datetime` | +| `timezone` | import | `datetime` | +| `utf16_len` | moved-lazy | `gateway.platforms.base` | + +### `gateway.session` + +| name | kind | new location | +|---|---|---| +| `SessionResetPolicy` | moved-lazy | `gateway.config` | +| `TranscriptReadError` | moved-lazy | `gateway.session_transcript` | +| `atomic_replace` | moved-lazy | `utils` | +| `auto_continue_freshness_window` | moved-lazy | `gateway.session_lifecycle` | +| `extract_api_content_sidecar` | moved-lazy | `agent.turn_context` | +| `normalize_whatsapp_identifier` | moved-lazy | `gateway.whatsapp_identity` | +| `replace` | import | `dataclasses` | +| `uuid` | import | `uuid` | + +### `gateway.slash_access` + +| name | kind | new location | +|---|---|---| +| `Tuple` | import | `typing` | + +### `gateway.slash_commands` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `HISTORY_UNREADABLE` | moved-lazy | `gateway.slash_commands_status` | +| `MessageType` | moved-lazy | `gateway.platforms.base` | +| `SessionSource` | moved-lazy | `gateway.session` | +| `base_url_host_matches` | moved-lazy | `utils` | +| `build_session_key` | moved-lazy | `gateway.session` | +| `clear_model_endpoint_credentials` | moved-lazy | `hermes_cli.config` | +| `extract_api_content_sidecar` | moved-lazy | `agent.turn_context` | +| `fetch_account_usage` | moved-lazy | `agent.account_usage` | +| `hashlib` | import | `hashlib` | +| `is_shared_multi_user_session` | moved-lazy | `gateway.session` | +| `render_account_usage_lines` | moved-lazy | `agent.account_usage` | + +### `gateway.startup_watchdog` + +| name | kind | new location | +|---|---|---| +| `*` | module-stub | `gateway.shutdown_watchdog` | + +### `gateway.status` + +| name | kind | new location | +|---|---|---| +| `clear_planned_stop_marker` | restored-def | `(deleted; BASE body restored)` | +| `is_gateway_running` | restored-def | `(deleted; BASE body restored)` | + +### `gateway.sticker_cache` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | +| `tempfile` | import | `tempfile` | + +### `gateway.stream_consumer` + +| name | kind | new location | +|---|---|---| +| `MEDIA_TAG_CLEANUP_RE` | moved-lazy | `gateway.platforms.base` | +| `escape_code_fences_for_display` | moved-lazy | `gateway.stream_consumer_fences` | + +### `gateway.stream_dispatch` + +| name | kind | new location | +|---|---|---| +| `ToolCallFinished` | moved-lazy | `gateway.stream_events` | + +### `gateway.systemd_notify` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `hermes_cli.agent_import` + +| name | kind | new location | +|---|---|---| +| `backup_memory_file` | restored-def | `(deleted; BASE body restored)` | +| `default_source_dir` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.auth` + +| name | kind | new location | +|---|---|---| +| `BaseHTTPRequestHandler` | import | `http.server` | +| `CODEX_OAUTH_USER_AGENT` | moved-lazy | `hermes_cli.auth_constants` | +| `CODEX_QUOTA_PROBE_MIN_INTERVAL_SECONDS` | moved-lazy | `hermes_cli.auth_codex` | +| `DEFAULT_SPOTIFY_REDIRECT_URI` | moved-lazy | `hermes_cli.auth_constants` | +| `DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDS` | moved-lazy | `hermes_cli.auth_constants` | +| `HTTPServer` | import | `http.server` | +| `MINIMAX_OAUTH_GRANT_TYPE` | moved-lazy | `hermes_cli.auth_constants` | +| `NOUS_INFERENCE_INVOKE_SCOPE` | moved-lazy | `hermes_cli.auth_constants` | +| `NOUS_SHARED_STORE_FILENAME` | moved-lazy | `hermes_cli.auth_nous` | +| `OAUTH_OVER_SSH_DOCS_URL` | moved-lazy | `hermes_cli.auth_constants` | +| `QWEN_OAUTH_CLIENT_ID` | moved-lazy | `hermes_cli.auth_constants` | +| `QWEN_OAUTH_TOKEN_URL` | moved-lazy | `hermes_cli.auth_constants` | +| `SINGLE_USE_OAUTH_SINGLETON_FILES` | moved-lazy | `hermes_cli.auth_oauth_grants` | +| `SPOTIFY_ACCESS_TOKEN_REFRESH_SKEW_SECONDS` | moved-lazy | `hermes_cli.auth_constants` | +| `SPOTIFY_DASHBOARD_URL` | moved-lazy | `hermes_cli.auth_constants` | +| `TYPE_CHECKING` | import | `typing` | +| `XAI_OAUTH_DEVICE_CODE_URL` | moved-lazy | `hermes_cli.auth_constants` | +| `XAI_OAUTH_DISCOVERY_URL` | moved-lazy | `hermes_cli.auth_constants` | +| `XAI_OAUTH_ISSUER` | moved-lazy | `hermes_cli.auth_constants` | +| `base64` | import | `base64` | +| `hashlib` | import | `hashlib` | +| `parse_qs` | import | `urllib.parse` | +| `refresh_nous_oauth_pure` | moved-lazy | `hermes_cli.auth_nous` | +| `ssl` | import | `ssl` | +| `subprocess` | import | `subprocess` | +| `sys` | import | `sys` | +| `urlencode` | import | `urllib.parse` | + +### `hermes_cli.backup` + +| name | kind | new location | +|---|---|---| +| `copy_db_and_verify` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.browser_connect` + +| name | kind | new location | +|---|---|---| +| `try_launch_chrome_debug` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.callbacks` + +| name | kind | new location | +|---|---|---| +| `approval_callback` | restored-def | `(deleted; BASE body restored)` | +| `clarify_callback` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.checkpoints` + +| name | kind | new location | +|---|---|---| +| `cmd_list` | moved-lazy | `hermes_cli.plugins_cmd` | +| `datetime` | import | `datetime` | + +### `hermes_cli.commands` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `AutoSuggest` | unrestorable | `no top-level definition on BASE` | +| `Callable` | import | `collections.abc` | +| `Completer` | unrestorable | `no top-level definition on BASE` | +| `Completion` | unrestorable | `no top-level definition on BASE` | +| `Dict` | import | `typing` | +| `Mapping` | import | `collections.abc` | +| `Optional` | import | `typing` | +| `Sequence` | import | `collections.abc` | +| `SlashCommandAutoSuggest` | moved-lazy | `hermes_cli.commands_completion` | +| `SlashCommandCompleter` | moved-lazy | `hermes_cli.commands_completion` | +| `Suggestion` | unrestorable | `no top-level definition on BASE` | +| `Tuple` | import | `typing` | +| `_CMD_NAME_LIMIT` | restored-helper | `(deleted; restored as a dependency of discord_skill_commands)` | +| `_clamp_command_names` | restored-helper | `(deleted; restored as a dependency of discord_skill_commands)` | +| `_collect_gateway_skill_entries` | restored-helper | `(deleted; restored as a dependency of discord_skill_commands)` | +| `_requires_argument` | restored-helper | `(deleted; restored as a dependency of discord_skill_commands)` | +| `discord_skill_commands` | restored-def | `(deleted; BASE body restored)` | +| `discord_skill_commands_by_category` | moved-lazy | `hermes_cli.commands_platforms` | +| `field` | import | `dataclasses` | +| `key` | unrestorable | `no top-level definition on BASE` | +| `m` | unrestorable | `no top-level definition on BASE` | +| `os` | import | `os` | +| `shutil` | import | `shutil` | +| `slack_app_manifest` | moved-lazy | `hermes_cli.commands_platforms` | +| `slack_native_slashes` | moved-lazy | `hermes_cli.commands_platforms` | +| `slack_subcommand_map` | moved-lazy | `hermes_cli.commands_platforms` | +| `subprocess` | import | `subprocess` | +| `telegram_bot_commands` | moved-lazy | `hermes_cli.commands_platforms` | +| `telegram_menu_commands` | moved-lazy | `hermes_cli.commands_platforms` | +| `telegram_menu_max_commands` | moved-lazy | `hermes_cli.commands_platforms` | +| `time` | import | `time` | + +### `hermes_cli.config` + +| name | kind | new location | +|---|---|---| +| `_install_method_project_root` | restored-helper | `(deleted; restored as a dependency of stamp_install_method)` | +| `normalize_route_base_url` | moved-lazy | `hermes_cli.route_identity` | +| `stamp_install_method` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.console_engine` + +| name | kind | new location | +|---|---|---| +| `shlex` | import | `shlex` | + +### `hermes_cli.curses_ui` + +| name | kind | new location | +|---|---|---| +| `Protocol` | import | `typing` | + +### `hermes_cli.dashboard_auth.audit` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `hermes_cli.dashboard_auth.middleware` + +| name | kind | new location | +|---|---|---| +| `DashboardAuthProvider` | moved-lazy | `hermes_cli.dashboard_auth.base` | + +### `hermes_cli.dingtalk_auth` + +| name | kind | new location | +|---|---|---| +| `logger` | moved-lazy | `hermes_cli.auth` | +| `logging` | import | `logging` | + +### `hermes_cli.doctor` + +| name | kind | new location | +|---|---|---| +| `FTS_STORAGE_VERSION` | moved-lazy | `hermes_state_common` | +| `OPENROUTER_MODELS_URL` | moved-lazy | `hermes_constants` | +| `Path` | import | `pathlib` | +| `STATE_DB_SIZE_WARN_BYTES` | moved-lazy | `hermes_cli.doctor_state` | +| `agent_browser_runnable` | moved-lazy | `hermes_constants` | +| `base_url_host_matches` | moved-lazy | `utils` | +| `check_certificates` | moved-lazy | `hermes_cli.doctor_platform` | +| `check_fail` | restored-def | `(deleted; BASE body restored)` | +| `check_macos_full_disk_access` | moved-lazy | `hermes_cli.doctor_platform` | +| `check_macos_tcc_anchor` | moved-lazy | `hermes_cli.doctor_platform` | +| `check_macos_tcc_grants` | moved-lazy | `hermes_cli.doctor_platform` | +| `check_ok` | restored-def | `(deleted; BASE body restored)` | +| `check_warn` | restored-def | `(deleted; BASE body restored)` | +| `collect_deprecated_config_keys` | moved-lazy | `hermes_cli.doctor_config` | +| `collect_deprecated_env_vars` | moved-lazy | `hermes_cli.doctor_config` | +| `collect_relay_plugin_cutover_findings` | moved-lazy | `hermes_cli.doctor_config` | +| `describe_vercel_auth` | moved-lazy | `hermes_cli.vercel_auth` | +| `detect_install_method` | moved-lazy | `hermes_cli.config` | +| `importlib` | import | `importlib.util` | +| `is_nix_install_method` | moved-lazy | `hermes_cli.config` | +| `managed_scope_check` | moved-lazy | `hermes_cli.doctor_config` | +| `recommended_update_command_for_method` | moved-lazy | `hermes_cli.config` | +| `report_deprecated_config_and_env` | moved-lazy | `hermes_cli.doctor_config` | +| `shutil` | import | `shutil` | +| `subprocess` | import | `subprocess` | + +### `hermes_cli.doctor_live` + +| name | kind | new location | +|---|---|---| +| `ELEVENLABS_VOICES_URL` | restored-def | `(deleted; BASE body restored)` | +| `FAL_MODELS_URL` | restored-def | `(deleted; BASE body restored)` | +| `FIRECRAWL_HEALTH_URL` | restored-def | `(deleted; BASE body restored)` | +| `GROQ_MODELS_URL` | restored-def | `(deleted; BASE body restored)` | +| `OPENAI_MODELS_URL` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.focus_view` + +| name | kind | new location | +|---|---|---| +| `FOCUS_USAGE` | restored-def | `(deleted; BASE body restored)` | +| `effective_tool_progress_mode` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.foreign_sessions` + +| name | kind | new location | +|---|---|---| +| `list_claude_sessions` | restored-def | `(deleted; BASE body restored)` | +| `list_codex_sessions` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.gateway` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT` | moved-lazy | `gateway.restart` | +| `print_systemd_linger_guidance` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.gitlock` + +| name | kind | new location | +|---|---|---| +| `is_ancestor_of_head` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.heartbeat` + +| name | kind | new location | +|---|---|---| +| `Dict` | import | `typing` | + +### `hermes_cli.journey` + +| name | kind | new location | +|---|---|---| +| `cmd_journey` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.kanban` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | + +### `hermes_cli.kanban_db` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_BUSY_TIMEOUT_MS` | moved-lazy | `hermes_cli.kanban_db_connect` | +| `DEFAULT_LOG_BACKUP_COUNT` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `DEFAULT_LOG_ROTATE_BYTES` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `DEFAULT_SPAWN_FAILURE_LIMIT` | restored-def | `(deleted; BASE body restored)` | +| `DERIVED_MAX_IN_PROGRESS_CEILING` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `DERIVED_MAX_IN_PROGRESS_FLOOR` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `KANBAN_TERMINAL_TIMEOUT_GRACE_SECONDS` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `KanbanDbCorruptError` | moved-lazy | `hermes_cli.kanban_db_connect` | +| `MEMORY_GUARD_MB_PER_WORKER` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `Mapping` | import | `typing` | +| `RepairResult` | moved-lazy | `hermes_cli.kanban_db_connect` | +| `add_notify_sub` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `advance_notify_cursor` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `check_respawn_guard` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `claim_unseen_events_for_sub` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `configured_max_in_progress` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `connect` | moved-lazy | `hermes_cli.projects_db` | +| `connect_closing` | moved-lazy | `hermes_cli.projects_db` | +| `count_notify_subs` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `count_running_tasks` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `count_running_tasks_other_boards` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `derive_default_max_in_progress` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `detect_crashed_workers` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `detect_stale_running` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `dispatch_once` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `enforce_max_runtime` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `field` | import | `dataclasses` | +| `has_spawnable_ready` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `has_spawnable_review` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `hashlib` | import | `hashlib` | +| `heartbeat_worker` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `list_notify_subs` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `parent_results` | restored-def | `(deleted; BASE body restored)` | +| `purge_stale_done_notify_subs` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `random` | import | `random` | +| `reap_worker_zombies` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `reconcile_orphaned_running` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `remove_notify_sub` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `repair_db` | moved-lazy | `hermes_cli.kanban_db_connect` | +| `resolve_max_in_progress` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `resolve_workspace` | moved-lazy | `hermes_cli.kanban_db_workspace` | +| `review_dispatch_enabled` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `rewind_notify_cursor` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `run_daemon` | moved-lazy | `hermes_cli.kanban_db_dispatch` | +| `set_branch_name` | moved-lazy | `hermes_cli.kanban_db_workspace` | +| `set_workspace_path` | moved-lazy | `hermes_cli.kanban_db_workspace` | +| `shutil` | import | `shutil` | +| `threading` | import | `threading` | +| `unseen_events_for_sub` | moved-lazy | `hermes_cli.kanban_db_notify` | +| `worker_log_rotation_config` | moved-lazy | `hermes_cli.kanban_db_dispatch` | + +### `hermes_cli.kanban_decompose` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `os` | import | `os` | + +### `hermes_cli.kanban_diagnostics` + +| name | kind | new location | +|---|---|---| +| `DIAGNOSTIC_KINDS` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.local_runtime.binaries` + +| name | kind | new location | +|---|---|---| +| `get_hermes_home` | moved-lazy | `hermes_constants` | + +### `hermes_cli.local_runtime.bootstrap` + +| name | kind | new location | +|---|---|---| +| `get_hermes_home` | moved-lazy | `hermes_constants` | + +### `hermes_cli.local_runtime.capabilities` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `urllib` | import | `urllib.request` | + +### `hermes_cli.local_runtime.catalog` + +| name | kind | new location | +|---|---|---| +| `find_variant` | restored-def | `(deleted; BASE body restored)` | +| `re` | import | `re` | +| `recommended_id` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.local_runtime.hf_browse` + +| name | kind | new location | +|---|---|---| +| `field` | import | `dataclasses` | + +### `hermes_cli.main` + +| name | kind | new location | +|---|---|---| +| `hashlib` | import | `hashlib` | +| `line_input` | moved-lazy | `hermes_cli.cli_output` | +| `shlex` | import | `shlex` | +| `stat` | import | `stat` | +| `tempfile` | import | `tempfile` | + +### `hermes_cli.mcp_picker` + +| name | kind | new location | +|---|---|---| +| `color` | moved-lazy | `hermes_cli.colors` | + +### `hermes_cli.mcp_security` + +| name | kind | new location | +|---|---|---| +| `is_mcp_server_entry_suspicious` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.middleware` + +| name | kind | new location | +|---|---|---| +| `API_EXECUTION_MIDDLEWARE` | restored-def | `(deleted; BASE body restored)` | +| `API_REQUEST_MIDDLEWARE` | restored-def | `(deleted; BASE body restored)` | +| `apply_api_request_middleware` | restored-def | `(deleted; BASE body restored)` | +| `run_api_execution_middleware` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.moa_config` + +| name | kind | new location | +|---|---|---| +| `build_moa_turn_prompt` | restored-def | `(deleted; BASE body restored)` | +| `encode_moa_turn` | restored-helper | `(deleted; restored as a dependency of build_moa_turn_prompt)` | +| `encode_moa_turn` | restored-def | `(deleted; BASE body restored)` | +| `list_moa_presets` | restored-def | `(deleted; BASE body restored)` | +| `set_active_moa_preset` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.model_setup_flows` + +| name | kind | new location | +|---|---|---| +| `BEDROCK_GEO_PREFIXES` | moved-lazy | `hermes_cli.model_setup_flows_bedrock` | +| `bedrock_model_routable_from_region` | moved-lazy | `hermes_cli.model_setup_flows_bedrock` | +| `bedrock_region_geo_prefix` | moved-lazy | `hermes_cli.model_setup_flows_bedrock` | +| `custom_provider_slug` | moved-lazy | `hermes_cli.providers` | +| `line_input` | moved-lazy | `hermes_cli.cli_output` | +| `subprocess` | import | `subprocess` | +| `urllib` | import | `urllib.parse` | + +### `hermes_cli.model_switch` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | +| `base_url_host_matches` | moved-lazy | `utils` | +| `custom_provider_slug` | moved-lazy | `hermes_cli.providers` | +| `http` | import | `http.client` | +| `list_picker_providers` | moved-lazy | `hermes_cli.model_switch_providers` | +| `prewarm_picker_cache_async` | moved-lazy | `hermes_cli.model_switch_providers` | +| `time` | import | `time` | + +### `hermes_cli.models` + +| name | kind | new location | +|---|---|---| +| `LMStudioLoadResult` | moved-lazy | `hermes_cli.models_local` | +| `NamedTuple` | import | `typing` | +| `PROVIDER_GROUPS` | moved-lazy | `hermes_cli.models_catalog_static` | +| `ProviderEntry` | moved-lazy | `hermes_cli.models_catalog_static` | +| `_OPENCODE_KEYLESS_EXTRA_SLUGS` | restored-helper | `(deleted; restored as a dependency of is_opencode_zen_free_model)` | +| `atomic_json_write` | moved-lazy | `utils` | +| `base_url_host_matches` | moved-lazy | `utils` | +| `compute_sale_discount` | moved-lazy | `hermes_cli.models_pricing` | +| `ensure_lmstudio_model_loaded` | moved-lazy | `hermes_cli.models_local` | +| `fetch_ai_gateway_pricing` | moved-lazy | `hermes_cli.models_pricing` | +| `fetch_lmstudio_models` | moved-lazy | `hermes_cli.models_local` | +| `fetch_models_with_pricing` | moved-lazy | `hermes_cli.models_pricing` | +| `fetch_ollama_local_models` | moved-lazy | `hermes_cli.models_local` | +| `get_cached_nous_inference_base_url` | moved-lazy | `hermes_cli.models_pricing` | +| `get_close_matches` | import | `difflib` | +| `get_pricing_for_provider` | moved-lazy | `hermes_cli.models_pricing` | +| `group_providers` | moved-lazy | `hermes_cli.models_catalog_static` | +| `http` | import | `http.client` | +| `is_nous_free_tier` | restored-def | `(deleted; BASE body restored)` | +| `is_opencode_zen_free_model` | restored-def | `(deleted; BASE body restored)` | +| `lmstudio_model_reasoning_options` | moved-lazy | `hermes_cli.models_local` | +| `nous_catalog_url` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `nous_model_reasoning_capabilities` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `nous_policy_allowed_ids` | moved-lazy | `hermes_cli.models_pricing` | +| `ollama_model_supports_thinking` | moved-lazy | `hermes_cli.models_local` | +| `openrouter_model_reasoning_capabilities` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `parse_openrouter_reasoning_capabilities` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `peek_cached_pricing` | moved-lazy | `hermes_cli.models_pricing` | +| `pricing_cache_scope` | moved-lazy | `hermes_cli.models_pricing` | +| `probe_lmstudio_models` | moved-lazy | `hermes_cli.models_local` | +| `probe_ollama_local_models` | moved-lazy | `hermes_cli.models_local` | +| `provider_group_for_slug` | moved-lazy | `hermes_cli.models_catalog_static` | +| `refresh_reasoning_caps_async` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `restrict_to_nous_policy` | moved-lazy | `hermes_cli.models_pricing` | +| `should_use_ollama_native_catalog` | moved-lazy | `hermes_cli.models_local` | +| `url_origin` | moved-lazy | `hermes_cli.urllib_security` | +| `validate_requested_model` | moved-lazy | `hermes_cli.models_validate` | +| `warm_nous_reasoning_caps_async` | moved-lazy | `hermes_cli.models_reasoning_caps` | +| `warm_openrouter_reasoning_caps_async` | moved-lazy | `hermes_cli.models_reasoning_caps` | + +### `hermes_cli.nous_billing` + +| name | kind | new location | +|---|---|---| +| `BILLING_MANAGE_SCOPE` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.nous_subscription` + +| name | kind | new location | +|---|---|---| +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | + +### `hermes_cli.observability.relay_runtime` + +| name | kind | new location | +|---|---|---| +| `*` | module-stub | `agent.relay_runtime` | + +### `hermes_cli.observability.relay_shared_metrics` + +| name | kind | new location | +|---|---|---| +| `CLIENT_ACTIVE_MARK` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `MODEL_CALL_PROFILE_MODEL` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `SCHEMA_KEY` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `SCHEMA_VERSION` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `SKILL_LIFECYCLE_MARK` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `SKILL_LOAD_MARK` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `TOOL_APPROVAL_MARK` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `TOOL_CALL_SCOPE` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `model_call_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `prepare_session_start` | restored-def | `(deleted; BASE body restored)` | +| `skill_lifecycle_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `skill_load_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `task_start_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `task_terminal_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `task_terminal_state` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `tool_approval_outcome` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `tool_category` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | +| `tool_terminal_fields` | moved-lazy | `hermes_cli.observability.shared_metrics_contract` | + +### `hermes_cli.onepassword_secrets_cli` + +| name | kind | new location | +|---|---|---| +| `Table` | import | `rich.table` | +| `masked_secret_prompt` | moved-lazy | `hermes_cli.secret_prompt` | +| `sys` | import | `sys` | + +### `hermes_cli.platform_actions` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `hermes_cli.plugins` + +| name | kind | new location | +|---|---|---| +| `CAPABILITY_REGISTRY` | moved-lazy | `hermes_cli.plugin_capabilities` | +| `ENTRY_POINT_CAPABILITIES_GROUP` | moved-lazy | `hermes_cli.plugins_discovery` | +| `Iterable` | import | `typing` | +| `LEGACY_RELAY_PLUGIN_KEYS` | moved-lazy | `hermes_cli.relay_plugin_cutover` | +| `MAX_SYSTEM_PROMPT_SECTIONS` | moved-lazy | `hermes_cli.plugins_dispatch` | +| `OBSERVER_SCHEMA_VERSION` | moved-lazy | `hermes_cli.middleware` | +| `Type` | import | `typing` | +| `VALID_CAPABILITY_IDS` | moved-lazy | `hermes_cli.plugin_capabilities` | +| `cfg_get` | moved-lazy | `hermes_cli.config` | +| `contextmanager` | import | `contextlib` | +| `contextvars` | import | `contextvars` | +| `copy` | import | `copy` | +| `fast_safe_load` | moved-lazy | `utils` | +| `format_system_prompt_section` | moved-lazy | `hermes_cli.plugins_dispatch` | +| `get_plugin_subscriptions` | restored-def | `(deleted; BASE body restored)` | +| `hashlib` | import | `hashlib` | +| `reset_hermes_home_override` | moved-lazy | `hermes_constants` | +| `set_hermes_home_override` | moved-lazy | `hermes_constants` | +| `time` | import | `time` | +| `unload_plugins` | restored-def | `(deleted; BASE body restored)` | +| `wraps` | import | `functools` | +| `yaml` | unrestorable | `no top-level definition on BASE` | + +### `hermes_cli.plugins_cmd` + +| name | kind | new location | +|---|---|---| +| `importlib` | import | `importlib.metadata` | + +### `hermes_cli.profile_describer` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `hermes_cli.profile_distribution` + +| name | kind | new location | +|---|---|---| +| `is_excluded_skill_path` | moved-lazy | `agent.skill_utils` | + +### `hermes_cli.profiles` + +| name | kind | new location | +|---|---|---| +| `has_bundled_skills_opt_out` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.runtime_provider` + +| name | kind | new location | +|---|---|---| +| `custom_provider_aliases` | moved-lazy | `hermes_cli.providers` | +| `custom_provider_slug` | moved-lazy | `hermes_cli.providers` | +| `os` | import | `os` | + +### `hermes_cli.security_advisories` + +| name | kind | new location | +|---|---|---| +| `render_doctor_section` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.setup` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `Dict` | import | `typing` | +| `Optional` | import | `typing` | +| `get_nous_subscription_features` | moved-lazy | `hermes_cli.nous_subscription` | +| `get_optional_skills_dir` | moved-lazy | `hermes_constants` | +| `json` | import | `json` | +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | +| `shutil` | import | `shutil` | + +### `hermes_cli.slack_cli` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `hermes_cli.sqlite_safe_read` + +| name | kind | new location | +|---|---|---| +| `SQLITE_HEADER_MAGIC` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.status` + +| name | kind | new location | +|---|---|---| +| `format_nous_portal_entitlement_message` | moved-lazy | `hermes_cli.nous_account` | +| `get_nous_portal_account_info` | moved-lazy | `hermes_cli.nous_account` | +| `get_nous_subscription_features` | moved-lazy | `hermes_cli.nous_subscription` | +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | +| `redact_key` | moved-lazy | `hermes_cli.config` | +| `subprocess` | import | `subprocess` | + +### `hermes_cli.telegram_managed_bot` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_MANAGER_BOT` | restored-def | `(deleted; BASE body restored)` | +| `_USERNAME_SLUG_ALPHABET` | restored-helper | `(deleted; restored as a dependency of generate_bot_username)` | +| `auto_setup_telegram_bot` | restored-def | `(deleted; BASE body restored)` | +| `generate_bot_username` | restored-def | `(deleted; BASE body restored)` | +| `generate_deep_link` | restored-def | `(deleted; BASE body restored)` | +| `generate_pairing_nonce` | restored-def | `(deleted; BASE body restored)` | +| `generate_username_slug` | restored-helper | `(deleted; restored as a dependency of generate_bot_username)` | +| `generate_username_slug` | restored-def | `(deleted; BASE body restored)` | +| `poll_for_token` | moved-lazy | `plugins.platforms.photon.auth` | +| `poll_pairing_once` | restored-def | `(deleted; BASE body restored)` | +| `secrets` | restored-import | `secrets` | +| `secrets` | import | `secrets` | +| `urllib` | restored-import | `urllib.parse` | +| `urllib` | import | `urllib.parse` | + +### `hermes_cli.tools_config` + +| name | kind | new location | +|---|---|---| +| `MANAGED_FEATURE_COVERAGE_CATEGORY` | moved-lazy | `hermes_cli.nous_subscription` | +| `NOUS_MANAGED_PROVIDER` | moved-lazy | `tools.tool_backend_helpers` | +| `base_url_hostname` | moved-lazy | `utils` | +| `fal_key_is_configured` | moved-lazy | `tools.tool_backend_helpers` | +| `format_nous_portal_entitlement_message` | moved-lazy | `hermes_cli.nous_account` | +| `is_truthy_value` | moved-lazy | `utils` | +| `save_env_value` | moved-lazy | `hermes_cli.config` | +| `shutil` | import | `shutil` | +| `subprocess` | import | `subprocess` | +| `sys` | import | `sys` | + +### `hermes_cli.uninstall` + +| name | kind | new location | +|---|---|---| +| `find_shell_configs` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.update_cmd` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | +| `datetime` | import | `datetime` | +| `hashlib` | import | `hashlib` | +| `json` | import | `json` | + +### `hermes_cli.update_inventory` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `os` | import | `os` | + +### `hermes_cli.web_deps` + +| name | kind | new location | +|---|---|---| +| `get_dashboard_health` | restored-def | `(deleted; BASE body restored)` | +| `get_session_token` | restored-def | `(deleted; BASE body restored)` | +| `has_valid_session_token` | restored-def | `(deleted; BASE body restored)` | +| `late_attr` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_cli.web_routers.cron` + +| name | kind | new location | +|---|---|---| +| `logging` | import | `logging` | + +### `hermes_cli.web_routers.mcp` + +| name | kind | new location | +|---|---|---| +| `logging` | import | `logging` | + +### `hermes_cli.web_routers.sessions` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `Dict` | import | `typing` | +| `logging` | import | `logging` | + +### `hermes_cli.web_routers.skills` + +| name | kind | new location | +|---|---|---| +| `LateState` | moved-lazy | `hermes_cli.web_deps` | +| `logging` | import | `logging` | + +### `hermes_cli.web_routers.tools` + +| name | kind | new location | +|---|---|---| +| `LateState` | moved-lazy | `hermes_cli.web_deps` | +| `logging` | import | `logging` | + +### `hermes_cli.web_server` + +| name | kind | new location | +|---|---|---| +| `AudioTranscriptionRequest` | moved-lazy | `hermes_cli.web_models` | +| `AutomationBlueprintInstantiate` | moved-lazy | `hermes_cli.web_models` | +| `BackupRequest` | moved-lazy | `hermes_cli.web_models` | +| `BaseModel` | unrestorable | `no top-level definition on BASE` | +| `BulkDeleteSessions` | moved-lazy | `hermes_cli.web_models` | +| `CONFIG_SCHEMA` | moved-lazy | `hermes_cli.web_server_config` | +| `ChatImageUpload` | moved-lazy | `hermes_cli.web_models` | +| `ConfigUpdate` | moved-lazy | `hermes_cli.web_models` | +| `CredentialPoolAdd` | moved-lazy | `hermes_cli.web_models` | +| `CronJobCreate` | moved-lazy | `hermes_cli.web_models` | +| `CronJobUpdate` | moved-lazy | `hermes_cli.web_models` | +| `CuratorPause` | moved-lazy | `hermes_cli.web_models` | +| `CustomEndpointUpdate` | moved-lazy | `hermes_cli.web_models` | +| `DEFAULT_CONFIG` | moved-lazy | `hermes_cli.config` | +| `DebugShareRequest` | moved-lazy | `hermes_cli.web_models` | +| `EnvVarDelete` | moved-lazy | `hermes_cli.web_models` | +| `EnvVarReveal` | moved-lazy | `hermes_cli.web_models` | +| `EnvVarUpdate` | moved-lazy | `hermes_cli.web_models` | +| `File` | unrestorable | `no top-level definition on BASE` | +| `FileResponse` | unrestorable | `no top-level definition on BASE` | +| `FontSetBody` | moved-lazy | `hermes_cli.web_models` | +| `Form` | unrestorable | `no top-level definition on BASE` | +| `FsWriteText` | moved-lazy | `hermes_cli.web_models` | +| `GitBranchSwitchBody` | moved-lazy | `hermes_cli.web_models` | +| `GitCommitBody` | moved-lazy | `hermes_cli.web_models` | +| `GitFileBody` | moved-lazy | `hermes_cli.web_models` | +| `GitPathBody` | moved-lazy | `hermes_cli.web_models` | +| `GitWorktreeAddBody` | moved-lazy | `hermes_cli.web_models` | +| `GitWorktreeRemoveBody` | moved-lazy | `hermes_cli.web_models` | +| `HTMLResponse` | unrestorable | `no top-level definition on BASE` | +| `HookCreate` | moved-lazy | `hermes_cli.web_models` | +| `HookDelete` | moved-lazy | `hermes_cli.web_models` | +| `ImportRequest` | moved-lazy | `hermes_cli.web_models` | +| `LearningNodeEdit` | moved-lazy | `hermes_cli.web_models` | +| `LearningNodeRef` | moved-lazy | `hermes_cli.web_models` | +| `List` | import | `typing` | +| `Literal` | import | `typing` | +| `MCPCatalogInstall` | moved-lazy | `hermes_cli.web_models` | +| `MCPEnabledToggle` | moved-lazy | `hermes_cli.web_models` | +| `MCPServerCreate` | moved-lazy | `hermes_cli.web_models` | +| `MCPServersReplace` | moved-lazy | `hermes_cli.web_models` | +| `ManagedDirectoryCreate` | moved-lazy | `hermes_cli.web_models` | +| `ManagedFileDelete` | moved-lazy | `hermes_cli.web_models` | +| `ManagedFileUpload` | moved-lazy | `hermes_cli.web_models` | +| `ManagedFilesPolicy` | moved-lazy | `hermes_cli.web_server_files` | +| `MemoryProviderConfigUpdate` | moved-lazy | `hermes_cli.web_models` | +| `MemoryProviderSelect` | moved-lazy | `hermes_cli.web_models` | +| `MemoryProviderSetupRequest` | moved-lazy | `hermes_cli.web_models` | +| `MemoryReset` | moved-lazy | `hermes_cli.web_models` | +| `MessagingPlatformUpdate` | moved-lazy | `hermes_cli.web_models` | +| `MoaConfigPayload` | moved-lazy | `hermes_cli.web_models` | +| `MoaModelSlot` | moved-lazy | `hermes_cli.web_models` | +| `MoaPresetPayload` | moved-lazy | `hermes_cli.web_models` | +| `ModelAssignment` | moved-lazy | `hermes_cli.web_models` | +| `OAuthSubmitBody` | moved-lazy | `hermes_cli.web_models` | +| `OPTIONAL_ENV_VARS` | moved-lazy | `hermes_cli.config` | +| `PairingApprove` | moved-lazy | `hermes_cli.web_models` | +| `PairingRevoke` | moved-lazy | `hermes_cli.web_models` | +| `ProfileActiveUpdate` | moved-lazy | `hermes_cli.web_models` | +| `ProfileCreate` | moved-lazy | `hermes_cli.web_models` | +| `ProfileDescribeAuto` | moved-lazy | `hermes_cli.web_models` | +| `ProfileDescriptionUpdate` | moved-lazy | `hermes_cli.web_models` | +| `ProfileModelUpdate` | moved-lazy | `hermes_cli.web_models` | +| `ProfileRename` | moved-lazy | `hermes_cli.web_models` | +| `ProfileSoulUpdate` | moved-lazy | `hermes_cli.web_models` | +| `ProviderConfigSchema` | moved-lazy | `plugins.memory.config_schema` | +| `ProviderField` | moved-lazy | `plugins.memory.config_schema` | +| `PtyBridge` | moved-lazy | `hermes_cli.pty_bridge` | +| `PtySessionRegistry` | moved-lazy | `hermes_cli.pty_session` | +| `PtyUnavailableError` | moved-lazy | `hermes_cli.pty_bridge` | +| `Query` | unrestorable | `no top-level definition on BASE` | +| `RawConfigUpdate` | moved-lazy | `hermes_cli.web_models` | +| `RegistryFull` | moved-lazy | `hermes_cli.pty_session` | +| `Response` | unrestorable | `no top-level definition on BASE` | +| `STORAGE_HONCHO_HOST_BLOCK` | moved-lazy | `plugins.memory.config_schema` | +| `SecretStr` | unrestorable | `no top-level definition on BASE` | +| `SessionImport` | moved-lazy | `hermes_cli.web_models` | +| `SessionPrune` | moved-lazy | `hermes_cli.web_models` | +| `SessionRename` | moved-lazy | `hermes_cli.web_models` | +| `SkillContentUpdate` | moved-lazy | `hermes_cli.web_models` | +| `SkillCreate` | moved-lazy | `hermes_cli.web_models` | +| `SkillInstallRequest` | moved-lazy | `hermes_cli.web_models` | +| `SkillToggle` | moved-lazy | `hermes_cli.web_models` | +| `SkillUninstallRequest` | moved-lazy | `hermes_cli.web_models` | +| `SkillsUpdateRequest` | moved-lazy | `hermes_cli.web_models` | +| `StaticFiles` | unrestorable | `no top-level definition on BASE` | +| `TTSLeaseRequest` | moved-lazy | `hermes_cli.web_models` | +| `TTSSpeakRequest` | moved-lazy | `hermes_cli.web_models` | +| `TelegramOnboardingApply` | moved-lazy | `hermes_cli.web_models` | +| `TelegramOnboardingStart` | moved-lazy | `hermes_cli.web_models` | +| `TerminalBackendSelect` | moved-lazy | `hermes_cli.web_models` | +| `ThemeSetBody` | moved-lazy | `hermes_cli.web_models` | +| `ToolsetEnvUpdate` | moved-lazy | `hermes_cli.web_models` | +| `ToolsetModelSelect` | moved-lazy | `hermes_cli.web_models` | +| `ToolsetPostSetup` | moved-lazy | `hermes_cli.web_models` | +| `ToolsetProviderSelect` | moved-lazy | `hermes_cli.web_models` | +| `ToolsetToggle` | moved-lazy | `hermes_cli.web_models` | +| `UploadFile` | unrestorable | `no top-level definition on BASE` | +| `WebSocket` | unrestorable | `no top-level definition on BASE` | +| `WebSocketDisconnect` | unrestorable | `no top-level definition on BASE` | +| `WebhookCreate` | moved-lazy | `hermes_cli.web_models` | +| `WebhookEnabledToggle` | moved-lazy | `hermes_cli.web_models` | +| `WhatsAppOnboardingApply` | moved-lazy | `hermes_cli.web_models` | +| `WhatsAppOnboardingStart` | moved-lazy | `hermes_cli.web_models` | +| `activate_custom_endpoint` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `add_credential_pool_entry` | moved-lazy | `hermes_cli.web_routers.ops` | +| `add_mcp_server` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `apply_telegram_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `apply_whatsapp_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `approve_pairing` | moved-lazy | `hermes_cli.web_routers.ops` | +| `atexit` | import | `atexit` | +| `auth_mcp_server` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `base64` | import | `base64` | +| `binascii` | import | `binascii` | +| `build_cron_model_impact` | moved-lazy | `hermes_cli.config` | +| `bulk_delete_sessions_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `cancel_oauth_session` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `cancel_telegram_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `cancel_whatsapp_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `cfg_get` | moved-lazy | `hermes_cli.config` | +| `check_config_version` | moved-lazy | `hermes_cli.config` | +| `check_hermes_update` | moved-lazy | `hermes_cli.web_routers.actions` | +| `clear_model_endpoint_credentials` | moved-lazy | `hermes_cli.config` | +| `clear_pending_pairing` | moved-lazy | `hermes_cli.web_routers.ops` | +| `coerce_provider_id` | moved-lazy | `hermes_cli.config` | +| `concurrent` | import | `concurrent.futures` | +| `console_ws` | moved-lazy | `hermes_cli.web_routers.chat_ws` | +| `contextlib` | import | `contextlib` | +| `contextmanager` | import | `contextlib` | +| `count_empty_sessions_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `create_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `create_hook` | moved-lazy | `hermes_cli.web_routers.ops` | +| `create_managed_directory` | moved-lazy | `hermes_cli.web_routers.files` | +| `create_profile_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `create_skill` | moved-lazy | `hermes_cli.web_routers.skills` | +| `create_webhook` | moved-lazy | `hermes_cli.web_routers.ops` | +| `cron_fire_webhook` | moved-lazy | `hermes_cli.web_routers.cron` | +| `custom_endpoint_key_env` | moved-lazy | `hermes_cli.config` | +| `dataclass` | import | `dataclasses` | +| `datetime` | import | `datetime` | +| `delete_agent_plugin` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `delete_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `delete_custom_endpoint` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `delete_empty_sessions_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `delete_hook` | moved-lazy | `hermes_cli.web_routers.ops` | +| `delete_learning_node` | moved-lazy | `hermes_cli.web_routers.status` | +| `delete_managed_file` | moved-lazy | `hermes_cli.web_routers.files` | +| `delete_profile_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `delete_session_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `delete_webhook` | moved-lazy | `hermes_cli.web_routers.ops` | +| `derive_gateway_busy` | moved-lazy | `gateway.status` | +| `derive_gateway_drainable` | moved-lazy | `gateway.status` | +| `describe_profile_auto_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `detect_install_method` | moved-lazy | `hermes_cli.config` | +| `disconnect_oauth_provider` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `download_dashboard_backup` | moved-lazy | `hermes_cli.web_routers.ops` | +| `download_managed_file` | moved-lazy | `hermes_cli.web_routers.files` | +| `enable_webhooks` | moved-lazy | `hermes_cli.web_routers.ops` | +| `env_var_enabled` | moved-lazy | `utils` | +| `events_ws` | moved-lazy | `hermes_cli.web_routers.chat_ws` | +| `export_session_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `field_validator` | unrestorable | `no top-level definition on BASE` | +| `find_provider_entry` | moved-lazy | `hermes_cli.config` | +| `format_docker_update_message` | moved-lazy | `hermes_cli.config` | +| `fs_default_cwd` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_download` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_git_root` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_list` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_read_data_url` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_read_text` | moved-lazy | `hermes_cli.web_routers.files` | +| `fs_write_text` | moved-lazy | `hermes_cli.web_routers.files` | +| `functools` | import | `functools` | +| `gateway_drain` | moved-lazy | `hermes_cli.web_routers.actions` | +| `gateway_ws` | moved-lazy | `hermes_cli.web_routers.chat_ws` | +| `get_action_status` | moved-lazy | `hermes_cli.web_routers.actions` | +| `get_active_profile_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `get_auxiliary_models` | moved-lazy | `hermes_cli.web_routers.models` | +| `get_client_voice_config` | moved-lazy | `hermes_cli.web_routers.audio` | +| `get_computer_use_status` | moved-lazy | `hermes_cli.web_routers.tools` | +| `get_config` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `get_config_path` | moved-lazy | `hermes_cli.config` | +| `get_config_raw` | moved-lazy | `hermes_cli.web_routers.analytics` | +| `get_cron_delivery_targets` | moved-lazy | `hermes_cli.web_routers.cron` | +| `get_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `get_curator_status` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_dashboard_font` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `get_dashboard_plugins` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `get_dashboard_themes` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `get_defaults` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `get_egress_status` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `get_elevenlabs_voices` | moved-lazy | `hermes_cli.web_routers.audio` | +| `get_env_path` | moved-lazy | `hermes_cli.config` | +| `get_env_vars` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `get_health` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_hermes_home` | moved-lazy | `hermes_cli.config` | +| `get_learning_graph` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_learning_node` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_logs` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_media` | moved-lazy | `hermes_cli.web_routers.files` | +| `get_memory_provider_config` | moved-lazy | `hermes_cli.web_routers.memory_providers` | +| `get_memory_status` | moved-lazy | `hermes_cli.web_routers.ops` | +| `get_messaging_platforms` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `get_moa_models` | moved-lazy | `hermes_cli.web_routers.models` | +| `get_model_info` | moved-lazy | `hermes_cli.web_routers.models` | +| `get_model_options` | moved-lazy | `hermes_cli.web_routers.models` | +| `get_models_analytics` | moved-lazy | `hermes_cli.web_routers.analytics` | +| `get_plugins_hub` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `get_portal_status` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_process_hermes_home` | moved-lazy | `hermes_cli.config` | +| `get_profile_setup_command` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `get_profile_soul` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `get_profiles_sessions` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `get_profiles_sessions_sidebar` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `get_provider_config_schema` | moved-lazy | `plugins.memory.config_schema` | +| `get_recommended_default_model` | moved-lazy | `hermes_cli.web_routers.models` | +| `get_running_pid` | moved-lazy | `gateway.status` | +| `get_running_pid_cached` | moved-lazy | `gateway.status` | +| `get_runtime_status_running_pid` | moved-lazy | `gateway.status` | +| `get_schema` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `get_session_detail` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `get_session_latest_descendant` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `get_session_messages` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `get_session_stats` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `get_sessions` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `get_skill_content` | moved-lazy | `hermes_cli.web_routers.skills` | +| `get_skills` | moved-lazy | `hermes_cli.web_routers.skills` | +| `get_ssh_ownership` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_status` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_system_stats` | moved-lazy | `hermes_cli.web_routers.status` | +| `get_telegram_onboarding_status` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `get_terminal_backends` | moved-lazy | `hermes_cli.web_routers.tools` | +| `get_toolset_config` | moved-lazy | `hermes_cli.web_routers.tools` | +| `get_toolset_models` | moved-lazy | `hermes_cli.web_routers.tools` | +| `get_toolsets` | moved-lazy | `hermes_cli.web_routers.tools` | +| `get_update_receipt` | moved-lazy | `hermes_cli.web_routers.actions` | +| `get_usage_analytics` | moved-lazy | `hermes_cli.web_routers.analytics` | +| `get_whatsapp_onboarding_status` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `git_base_branches_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_branch_switch_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_branches_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_commit_context_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_commit_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_create_pr_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_file_diff_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_push_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_rev_parse_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_revert_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_review_diff_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_review_list_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_ship_info_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_stage_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_status_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_unstage_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_worktree_add_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_worktree_remove_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `git_worktrees_route` | moved-lazy | `hermes_cli.web_routers.git` | +| `grant_computer_use_permissions` | moved-lazy | `hermes_cli.web_routers.tools` | +| `hashlib` | import | `hashlib` | +| `import_sessions_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `importlib` | import | `importlib.util` | +| `inspect` | import | `inspect` | +| `install_mcp_catalog_entry` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `install_skill_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `instantiate_blueprint` | moved-lazy | `hermes_cli.web_routers.cron` | +| `ipaddress` | import | `ipaddress` | +| `is_nix_install_method` | moved-lazy | `hermes_cli.config` | +| `json` | import | `json` | +| `list_checkpoints` | moved-lazy | `hermes_cli.web_routers.ops` | +| `list_credential_pool` | moved-lazy | `hermes_cli.web_routers.ops` | +| `list_cron_blueprints` | moved-lazy | `hermes_cli.web_routers.cron` | +| `list_cron_job_runs` | moved-lazy | `hermes_cli.web_routers.cron` | +| `list_cron_jobs` | moved-lazy | `hermes_cli.web_routers.cron` | +| `list_custom_endpoints` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `list_hooks` | moved-lazy | `hermes_cli.web_routers.ops` | +| `list_managed_files` | moved-lazy | `hermes_cli.web_routers.files` | +| `list_mcp_catalog` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `list_mcp_servers` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `list_oauth_providers` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `list_pairing` | moved-lazy | `hermes_cli.web_routers.ops` | +| `list_profiles_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `list_skills_hub_sources` | moved-lazy | `hermes_cli.web_routers.skills` | +| `list_webhooks` | moved-lazy | `hermes_cli.web_routers.ops` | +| `load_env` | moved-lazy | `hermes_cli.config` | +| `math` | import | `math` | +| `mcp_oauth_callback` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `mcp_oauth_flow_status` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `mimetypes` | import | `mimetypes` | +| `normalize_updated_at` | moved-lazy | `gateway.status` | +| `open_profile_terminal_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `parse_active_agents` | moved-lazy | `gateway.status` | +| `pause_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `poll_oauth_session` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `post_agent_plugin_disable` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `post_agent_plugin_enable` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `post_agent_plugin_install` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `post_agent_plugin_update` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `post_plugin_visibility` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `preview_skill_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `prune_checkpoints` | moved-lazy | `hermes_cli.web_routers.ops` | +| `prune_sessions_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `pty_ws` | moved-lazy | `hermes_cli.web_routers.chat_ws` | +| `pub_ws` | moved-lazy | `hermes_cli.web_routers.chat_ws` | +| `put_plugin_providers` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `queue` | import | `queue` | +| `read_managed_file` | moved-lazy | `hermes_cli.web_routers.files` | +| `read_raw_config` | moved-lazy | `hermes_cli.config` | +| `read_runtime_status` | moved-lazy | `gateway.status` | +| `recommended_update_command_for_method` | moved-lazy | `hermes_cli.config` | +| `redact_key` | moved-lazy | `hermes_cli.config` | +| `remove_credential_pool_entry` | moved-lazy | `hermes_cli.web_routers.ops` | +| `remove_env_value` | moved-lazy | `hermes_cli.config` | +| `remove_env_var` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `remove_mcp_server` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `rename_profile_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `rename_session_endpoint` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `replace_mcp_servers` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `rescan_dashboard_plugins` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `reset_memory` | moved-lazy | `hermes_cli.web_routers.ops` | +| `resolve_cron_model_drift_defaults` | moved-lazy | `hermes_cli.config` | +| `resolve_gateway_liveness` | moved-lazy | `gateway.status` | +| `restart_gateway` | moved-lazy | `hermes_cli.web_routers.actions` | +| `resume_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `reveal_env_var` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `revoke_pairing` | moved-lazy | `hermes_cli.web_routers.ops` | +| `run_backup` | moved-lazy | `hermes_cli.backup` | +| `run_config_migrate` | moved-lazy | `hermes_cli.web_routers.status` | +| `run_curator` | moved-lazy | `hermes_cli.web_routers.status` | +| `run_debug_share_endpoint` | moved-lazy | `hermes_cli.web_routers.status` | +| `run_doctor` | moved-lazy | `hermes_cli.doctor` | +| `run_dump` | moved-lazy | `hermes_cli.dump` | +| `run_import` | moved-lazy | `hermes_cli.backup` | +| `run_import_upload` | moved-lazy | `hermes_cli.web_routers.ops` | +| `run_in_threadpool` | unrestorable | `no top-level definition on BASE` | +| `run_prompt_size` | moved-lazy | `hermes_cli.web_routers.status` | +| `run_security_audit` | moved-lazy | `hermes_cli.web_routers.ops` | +| `run_toolset_post_setup` | moved-lazy | `hermes_cli.web_routers.tools` | +| `save_config` | moved-lazy | `hermes_cli.config` | +| `save_env_value` | moved-lazy | `hermes_cli.config` | +| `save_toolset_env` | moved-lazy | `hermes_cli.web_routers.tools` | +| `scan_skill_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `search_sessions` | moved-lazy | `hermes_cli.web_routers.sessions` | +| `search_skills_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `select_terminal_backend` | moved-lazy | `hermes_cli.web_routers.tools` | +| `select_toolset_model` | moved-lazy | `hermes_cli.web_routers.tools` | +| `select_toolset_provider` | moved-lazy | `hermes_cli.web_routers.tools` | +| `serve_plugin_asset` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `set_active_profile_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `set_curator_paused` | moved-lazy | `hermes_cli.web_routers.status` | +| `set_dashboard_font` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `set_dashboard_theme` | moved-lazy | `hermes_cli.web_routers.dashboard_ui` | +| `set_env_var` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `set_mcp_server_enabled` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `set_memory_provider` | moved-lazy | `hermes_cli.web_routers.ops` | +| `set_moa_models` | moved-lazy | `hermes_cli.web_routers.models` | +| `set_model_assignment` | moved-lazy | `hermes_cli.web_routers.models` | +| `set_webhook_enabled` | moved-lazy | `hermes_cli.web_routers.ops` | +| `setup_memory_provider` | moved-lazy | `hermes_cli.web_routers.memory_providers` | +| `shlex` | import | `shlex` | +| `shutil` | import | `shutil` | +| `speak_stream_ws` | moved-lazy | `hermes_cli.web_routers.audio` | +| `speak_text` | moved-lazy | `hermes_cli.voice` | +| `start_gateway` | moved-lazy | `hermes_cli.web_routers.ops` | +| `start_oauth_login` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `start_telegram_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `start_whatsapp_onboarding` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `stat` | import | `stat` | +| `stop_gateway` | moved-lazy | `hermes_cli.web_routers.ops` | +| `stream_managed_file` | moved-lazy | `hermes_cli.web_routers.files` | +| `submit_oauth_code` | moved-lazy | `hermes_cli.web_routers.oauth` | +| `tempfile` | import | `tempfile` | +| `test_mcp_server` | moved-lazy | `hermes_cli.web_routers.mcp` | +| `test_messaging_platform` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `timezone` | import | `datetime` | +| `toggle_skill` | moved-lazy | `hermes_cli.web_routers.skills` | +| `toggle_toolset` | moved-lazy | `hermes_cli.web_routers.tools` | +| `transcribe_audio_upload` | moved-lazy | `hermes_cli.web_routers.audio` | +| `trigger_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `tts_lease` | moved-lazy | `hermes_cli.web_routers.audio` | +| `uninstall_skill_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `update_config` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `update_config_raw` | moved-lazy | `hermes_cli.web_routers.analytics` | +| `update_cron_job` | moved-lazy | `hermes_cli.web_routers.cron` | +| `update_hermes` | moved-lazy | `hermes_cli.web_routers.actions` | +| `update_learning_node` | moved-lazy | `hermes_cli.web_routers.status` | +| `update_memory_provider_config` | moved-lazy | `hermes_cli.web_routers.memory_providers` | +| `update_messaging_platform` | moved-lazy | `hermes_cli.web_routers.messaging` | +| `update_profile_description_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `update_profile_model_endpoint` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `update_profile_soul` | moved-lazy | `hermes_cli.web_routers.profiles` | +| `update_skill_content` | moved-lazy | `hermes_cli.web_routers.skills` | +| `update_skills_hub` | moved-lazy | `hermes_cli.web_routers.skills` | +| `upload_chat_image` | moved-lazy | `hermes_cli.web_routers.files` | +| `upload_managed_file` | moved-lazy | `hermes_cli.web_routers.files` | +| `upload_managed_file_stream` | moved-lazy | `hermes_cli.web_routers.files` | +| `upsert_custom_endpoint` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `validate_custom_endpoint` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `validate_provider_credential` | moved-lazy | `hermes_cli.web_routers.config_env` | +| `windows_detach_flags` | moved-lazy | `hermes_cli._subprocess_compat` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | +| `write_platform_config_field` | moved-lazy | `hermes_cli.config` | +| `yaml` | import | `yaml` | +| `zipfile` | import | `zipfile` | + +### `hermes_cli.webhook` + +| name | kind | new location | +|---|---|---| +| `atomic_replace` | moved-lazy | `utils` | +| `os` | import | `os` | +| `tempfile` | import | `tempfile` | + +### `hermes_cli.win_pty_bridge` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `hermes_logging` + +| name | kind | new location | +|---|---|---| +| `rotating_file_handlers` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_state` + +| name | kind | new location | +|---|---|---| +| `AUTO_VACUUM_MIN_FREELIST_RATIO` | moved-lazy | `hermes_state_common` | +| `ActivityProvenance` | moved-lazy | `agent.session_activity` | +| `CompressionSessionBusyError` | moved-lazy | `hermes_state_errors` | +| `CompressionSessionClosedError` | moved-lazy | `hermes_state_errors` | +| `DEFERRED_INDEX_SQL` | moved-lazy | `hermes_state_common` | +| `FTS_CJK_STALE_KEY` | moved-lazy | `hermes_state_common` | +| `FTS_CJK_TABLE_SQL` | moved-lazy | `hermes_state_fts` | +| `FTS_CJK_TRIGGER_SQL` | moved-lazy | `hermes_state_fts` | +| `FTS_REBUILD_DEFERRAL_KEY` | moved-lazy | `hermes_state_common` | +| `FTS_SQL` | moved-lazy | `hermes_state_common` | +| `FTS_STALE_KEY` | moved-lazy | `hermes_state_common` | +| `FTS_STORAGE_VERSION` | moved-lazy | `hermes_state_common` | +| `FTS_TRIGRAM_SQL` | moved-lazy | `hermes_state_common` | +| `LEGACY_FTS_SQL` | moved-lazy | `hermes_state_common` | +| `LEGACY_FTS_TRIGRAM_SQL` | moved-lazy | `hermes_state_common` | +| `MAX_FTS5_QUERY_CHARS` | moved-lazy | `hermes_state_common` | +| `MAX_SAFE_EXPORT_MESSAGES` | restored-def | `(deleted; BASE body restored)` | +| `MAX_SAFE_RESUME_MESSAGES` | restored-def | `(deleted; BASE body restored)` | +| `PERSISTENCE_ERROR_CAUSES` | moved-lazy | `hermes_state_errors` | +| `SCHEMA_SQL` | moved-lazy | `hermes_state_common` | +| `SCHEMA_VERSION` | moved-lazy | `hermes_state_common` | +| `SESSION_STATUS_COMPLETE` | moved-lazy | `hermes_state_sessions` | +| `SESSION_STATUS_EMPTY` | moved-lazy | `hermes_state_sessions` | +| `SESSION_STATUS_ERROR` | moved-lazy | `hermes_state_sessions` | +| `SESSION_STATUS_INTERRUPTED` | moved-lazy | `hermes_state_sessions` | +| `SKILL_EXCERPT_JOINT` | moved-lazy | `agent.skill_commands` | +| `SKILL_SCAFFOLD_SQL_LIKE` | moved-lazy | `agent.skill_commands` | +| `SessionTurnLeaseLostError` | moved-lazy | `hermes_state_errors` | +| `Set` | import | `typing` | +| `WalUnsupportedError` | moved-lazy | `hermes_state_wal` | +| `apply_durability_barriers` | moved-lazy | `hermes_state_repair` | +| `classify_session_status` | moved-lazy | `hermes_state_sessions` | +| `close_shared_session_dbs` | unrestorable | `no top-level definition on BASE` | +| `collect_state_db_stats` | moved-lazy | `hermes_state_dbfile` | +| `contextlib` | import | `contextlib` | +| `count_db_holders` | moved-lazy | `hermes_state_dbfile` | +| `describe_skill_invocation` | moved-lazy | `agent.skill_commands` | +| `errno` | import | `errno` | +| `fts5_cjk_so_path` | moved-lazy | `hermes_state_fts` | +| `get_shared_session_db` | unrestorable | `no top-level definition on BASE` | +| `is_advisory_lock_contention` | moved-lazy | `hermes_state_common` | +| `is_automatic_end_reason` | moved-lazy | `hermes_state_common` | +| `is_disk_full_error` | moved-lazy | `hermes_state_errors` | +| `is_sqlite_wal_reset_vulnerable` | moved-lazy | `hermes_state_wal` | +| `is_transient_sqlite_error` | moved-lazy | `hermes_state_errors` | +| `iter_deleted_sqlite_sidecar_holders` | moved-lazy | `hermes_state_dbfile` | +| `release_or_close` | moved-lazy | `hermes_state_registry` | +| `release_shared_session_db` | unrestorable | `no top-level definition on BASE` | +| `report_startup_progress` | moved-lazy | `hermes_startup_watchdog` | +| `resolve_journal_mode` | moved-lazy | `hermes_state_wal` | +| `resolve_synchronous_level` | moved-lazy | `hermes_state_wal` | +| `sanitize_context` | moved-lazy | `agent.memory_manager` | +| `sqlite_source_id` | moved-lazy | `hermes_state_wal` | +| `struct` | import | `struct` | +| `weakref` | import | `weakref` | +| `workspace_key` | moved-lazy | `hermes_state_sessions` | + +### `hermes_state_registry` + +| name | kind | new location | +|---|---|---| +| `close_shared_session_dbs` | restored-def | `(deleted; BASE body restored)` | +| `get_shared_session_db` | restored-def | `(deleted; BASE body restored)` | +| `release_shared_session_db` | restored-def | `(deleted; BASE body restored)` | + +### `hermes_state_search` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `os` | import | `os` | + +### `plugins.browser.browser_use.provider` + +| name | kind | new location | +|---|---|---| +| `BrowserProvider` | moved-lazy | `agent.browser_provider` | +| `os` | import | `os` | + +### `plugins.browser.browserbase.provider` + +| name | kind | new location | +|---|---|---| +| `BrowserProvider` | moved-lazy | `agent.browser_provider` | +| `requests` | import | `requests` | +| `uuid` | import | `uuid` | + +### `plugins.browser.firecrawl.provider` + +| name | kind | new location | +|---|---|---| +| `BrowserProvider` | moved-lazy | `agent.browser_provider` | +| `requests` | import | `requests` | +| `uuid` | import | `uuid` | + +### `plugins.context_engine` + +| name | kind | new location | +|---|---|---| +| `importlib` | import | `importlib.util` | +| `sys` | import | `sys` | + +### `plugins.cron_providers` + +| name | kind | new location | +|---|---|---| +| `importlib` | import | `importlib.util` | +| `sys` | import | `sys` | + +### `plugins.cron_providers.chronos` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `plugins.cron_providers.chronos._nas_client` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | + +### `plugins.dashboard_auth.basic` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `LoginStart` | moved-lazy | `hermes_cli.dashboard_auth` | + +### `plugins.dashboard_auth.drain` + +| name | kind | new location | +|---|---|---| +| `LoginStart` | moved-lazy | `hermes_cli.dashboard_auth` | + +### `plugins.dashboard_auth.nous` + +| name | kind | new location | +|---|---|---| +| `DashboardAuthProvider` | moved-lazy | `hermes_cli.dashboard_auth` | +| `InvalidCodeError` | moved-lazy | `hermes_cli.dashboard_auth` | +| `RefreshExpiredError` | moved-lazy | `hermes_cli.dashboard_auth` | +| `base64` | import | `base64` | +| `classify_jwks_lookup_error` | moved-lazy | `hermes_cli.dashboard_auth` | +| `hashlib` | import | `hashlib` | +| `httpx` | import | `httpx` | +| `os` | import | `os` | +| `secrets` | import | `secrets` | +| `urllib` | import | `urllib.parse` | + +### `plugins.dashboard_auth.self_hosted` + +| name | kind | new location | +|---|---|---| +| `DashboardAuthProvider` | moved-lazy | `hermes_cli.dashboard_auth` | +| `InvalidCodeError` | moved-lazy | `hermes_cli.dashboard_auth` | +| `RefreshExpiredError` | moved-lazy | `hermes_cli.dashboard_auth` | +| `classify_jwks_lookup_error` | moved-lazy | `hermes_cli.dashboard_auth` | +| `hashlib` | import | `hashlib` | +| `os` | import | `os` | +| `secrets` | import | `secrets` | + +### `plugins.google_meet.audio_bridge` + +| name | kind | new location | +|---|---|---| +| `chrome_fake_audio_flags` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.google_meet.meet_bot` + +| name | kind | new location | +|---|---|---| +| `SAY_PCM_FILENAME` | restored-def | `(deleted; BASE body restored)` | +| `SAY_QUEUE_FILENAME` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | + +### `plugins.google_meet.node.cli` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | + +### `plugins.google_meet.node.registry` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `plugins.image_gen.deepinfra` + +| name | kind | new location | +|---|---|---| +| `ImageGenProvider` | moved-lazy | `agent.image_gen_provider` | +| `error_response` | moved-lazy | `agent.image_gen_provider` | +| `save_b64_image` | moved-lazy | `agent.image_gen_provider` | +| `save_url_image` | moved-lazy | `agent.image_gen_provider` | + +### `plugins.image_gen.fal` + +| name | kind | new location | +|---|---|---| +| `ImageGenProvider` | moved-lazy | `agent.image_gen_provider` | +| `os` | import | `os` | + +### `plugins.image_gen.krea` + +| name | kind | new location | +|---|---|---| +| `ImageGenProvider` | moved-lazy | `agent.image_gen_provider` | +| `error_response` | moved-lazy | `agent.image_gen_provider` | +| `normalize_reference_images` | moved-lazy | `agent.image_gen_provider` | +| `os` | import | `os` | + +### `plugins.image_gen.openai` + +| name | kind | new location | +|---|---|---| +| `ImageGenProvider` | moved-lazy | `agent.image_gen_provider` | +| `error_response` | moved-lazy | `agent.image_gen_provider` | +| `normalize_reference_images` | moved-lazy | `agent.image_gen_provider` | +| `save_b64_image` | moved-lazy | `agent.image_gen_provider` | +| `save_url_image` | moved-lazy | `agent.image_gen_provider` | + +### `plugins.image_gen.xai` + +| name | kind | new location | +|---|---|---| +| `ImageGenProvider` | moved-lazy | `agent.image_gen_provider` | +| `error_response` | moved-lazy | `agent.image_gen_provider` | +| `normalize_reference_images` | moved-lazy | `agent.image_gen_provider` | +| `save_b64_image` | moved-lazy | `agent.image_gen_provider` | +| `save_url_image` | moved-lazy | `agent.image_gen_provider` | + +### `plugins.memory.hindsight` + +| name | kind | new location | +|---|---|---| +| `dataclass` | import | `dataclasses` | +| `importlib` | import | `importlib` | + +### `plugins.memory.honcho` + +| name | kind | new location | +|---|---|---| +| `CONCLUDE_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `CONTEXT_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `PROFILE_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `REASONING_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `SEARCH_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `TRIVIAL_PROMPT_RE` | moved-lazy | `agent.memory_provider` | + +### `plugins.memory.honcho.client` + +| name | kind | new location | +|---|---|---| +| `SingletonSlot` | moved-lazy | `plugins.plugin_utils` | + +### `plugins.memory.honcho.oauth` + +| name | kind | new location | +|---|---|---| +| `Callable` | import | `typing` | + +### `plugins.memory.honcho.session` + +| name | kind | new location | +|---|---|---| +| `Callable` | import | `typing` | +| `Path` | import | `pathlib` | +| `hashlib` | import | `hashlib` | +| `re` | import | `re` | + +### `plugins.memory.mem0` + +| name | kind | new location | +|---|---|---| +| `ADD_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `DELETE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `SEARCH_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `UPDATE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.memory.mem0._setup` + +| name | kind | new location | +|---|---|---| +| `has_oss_flags` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.memory.retaindb` + +| name | kind | new location | +|---|---|---| +| `CONTEXT_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `Dict` | import | `typing` | +| `FILE_DELETE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `FILE_INGEST_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `FILE_LIST_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `FILE_READ_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `FILE_UPLOAD_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `FORGET_SCHEMA` | moved-lazy | `plugins.memory.openviking` | +| `List` | import | `typing` | +| `PROFILE_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `REMEMBER_SCHEMA` | moved-lazy | `plugins.memory.openviking` | +| `SEARCH_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | + +### `plugins.memory.supermemory` + +| name | kind | new location | +|---|---|---| +| `FORGET_SCHEMA` | moved-lazy | `plugins.memory.openviking` | +| `PROFILE_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `SEARCH_SCHEMA` | moved-lazy | `plugins.memory.honcho.tool_schemas` | +| `STORE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.a2a.protocol` + +| name | kind | new location | +|---|---|---| +| `ERR_PUSH_NOT_SUPPORTED` | restored-def | `(deleted; BASE body restored)` | +| `STATE_AUTH_REQUIRED` | restored-def | `(deleted; BASE body restored)` | +| `copy` | import | `copy` | +| `data_part` | restored-def | `(deleted; BASE body restored)` | +| `file_part` | restored-def | `(deleted; BASE body restored)` | +| `message_with_parts` | restored-def | `(deleted; BASE body restored)` | +| `stream_message` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.a2a.security` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `authenticate` | restored-def | `(deleted; BASE body restored)` | +| `get_bearer_token` | restored-def | `(deleted; BASE body restored)` | +| `get_peer_tokens` | restored-def | `(deleted; BASE body restored)` | +| `get_push_secret` | restored-def | `(deleted; BASE body restored)` | +| `get_trusted_peers` | restored-def | `(deleted; BASE body restored)` | +| `is_trusted_peer` | restored-def | `(deleted; BASE body restored)` | +| `resolve_bind_host` | restored-def | `(deleted; BASE body restored)` | +| `sign_push_payload` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.a2a.tools` + +| name | kind | new location | +|---|---|---| +| `TypedDict` | import | `typing` | + +### `plugins.platforms.dingtalk.adapter` + +| name | kind | new location | +|---|---|---| +| `DINGTALK_TYPE_MAPPING` | moved-lazy | `plugins.platforms.dingtalk.inbound` | +| `EXT_MAP` | moved-lazy | `plugins.platforms.dingtalk.inbound` | +| `MessageType` | moved-lazy | `gateway.platforms.base` | + +### `plugins.platforms.discord.adapter` + +| name | kind | new location | +|---|---|---| +| `env_int` | moved-lazy | `utils` | + +### `plugins.platforms.feishu.feishu_comment` + +| name | kind | new location | +|---|---|---| +| `add_comment_reaction` | restored-def | `(deleted; BASE body restored)` | +| `delete_comment_reaction` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.google_chat.oauth` + +| name | kind | new location | +|---|---|---| +| `atomic_replace` | moved-lazy | `utils` | +| `secrets` | import | `secrets` | +| `subprocess` | import | `subprocess` | + +### `plugins.platforms.irc.adapter` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `plugins.platforms.line.adapter` + +| name | kind | new location | +|---|---|---| +| `field` | import | `dataclasses` | + +### `plugins.platforms.matrix.adapter` + +| name | kind | new location | +|---|---|---| +| `MAX_MESSAGE_LENGTH` | moved-lazy | `gateway.platforms.signal` | +| `PaginationDirection` | unrestorable | `no top-level definition on BASE` | +| `SyncToken` | unrestorable | `no top-level definition on BASE` | +| `_MATRIX_CAPABILITIES` | restored-helper | `(deleted; restored as a dependency of get_matrix_capabilities)` | +| `get_matrix_capabilities` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.ntfy.adapter` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `plugins.platforms.photon.adapter` + +| name | kind | new location | +|---|---|---| +| `ProcessingOutcome` | moved-lazy | `gateway.platforms.base` | +| `resolve_sidecar_dir` | moved-lazy | `plugins.platforms.photon.sidecar_paths` | + +### `plugins.platforms.photon.auth` + +| name | kind | new location | +|---|---|---| +| `credential_summary` | restored-def | `(deleted; BASE body restored)` | +| `get_session` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.photon.cli` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `resolve_sidecar_dir` | moved-lazy | `plugins.platforms.photon.sidecar_paths` | + +### `plugins.platforms.raft.adapter` + +| name | kind | new location | +|---|---|---| +| `asyncio` | import | `asyncio` | + +### `plugins.platforms.teams.adapter` + +| name | kind | new location | +|---|---|---| +| `TeamsSummaryWriter` | moved-lazy | `plugins.platforms.teams.summary_writer` | +| `html` | import | `html` | +| `quote` | import | `urllib.parse` | + +### `plugins.platforms.telegram.adapter` + +| name | kind | new location | +|---|---|---| +| `atomic_replace` | moved-lazy | `utils` | +| `cache_document_from_bytes` | moved-lazy | `gateway.platforms.base` | +| `threading` | import | `threading` | + +### `plugins.platforms.telegram.telegram_ids` + +| name | kind | new location | +|---|---|---| +| `telegram_chat_id_key` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.platforms.wecom.adapter` + +| name | kind | new location | +|---|---|---| +| `ABSOLUTE_MAX_BYTES` | moved-lazy | `plugins.platforms.wecom.media` | +| `APP_CMD_UPLOAD_MEDIA_CHUNK` | moved-lazy | `plugins.platforms.wecom.media` | +| `APP_CMD_UPLOAD_MEDIA_FINISH` | moved-lazy | `plugins.platforms.wecom.media` | +| `APP_CMD_UPLOAD_MEDIA_INIT` | moved-lazy | `plugins.platforms.wecom.media` | +| `FILE_MAX_BYTES` | moved-lazy | `plugins.platforms.wecom.media` | +| `IMAGE_MAX_BYTES` | moved-lazy | `plugins.platforms.wecom.media` | +| `MAX_INTERMEDIATE_FRAMES` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `MAX_UPLOAD_CHUNKS` | moved-lazy | `plugins.platforms.wecom.media` | +| `Path` | import | `pathlib` | +| `ReplyFrame` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `STREAM_EXPIRED_ERRCODE` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `STREAM_REQUEST_EXPIRED_ERRCODE` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `STREAM_VERSION_CONFLICT_ERRCODE` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `UPLOAD_CHUNK_SIZE` | moved-lazy | `plugins.platforms.wecom.media` | +| `VIDEO_MAX_BYTES` | moved-lazy | `plugins.platforms.wecom.media` | +| `VOICE_MAX_BYTES` | moved-lazy | `plugins.platforms.wecom.media` | +| `VOICE_SUPPORTED_MIMES` | moved-lazy | `plugins.platforms.wecom.media` | +| `WeComStreamExpiredError` | moved-lazy | `plugins.platforms.wecom.streaming` | +| `base64` | import | `base64` | +| `cache_document_from_bytes_async` | moved-lazy | `gateway.platforms.base` | +| `cache_image_from_bytes_async` | moved-lazy | `gateway.platforms.base` | +| `dataclass` | import | `dataclasses` | +| `deque` | import | `collections` | +| `hashlib` | import | `hashlib` | +| `mimetypes` | import | `mimetypes` | +| `os` | import | `os` | +| `unquote` | import | `urllib.parse` | +| `urlparse` | import | `urllib.parse` | + +### `plugins.spotify` + +| name | kind | new location | +|---|---|---| +| `SPOTIFY_ALBUMS_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_DEVICES_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_LIBRARY_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_PLAYBACK_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_PLAYLISTS_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_QUEUE_SCHEMA` | moved-lazy | `plugins.spotify.tools` | +| `SPOTIFY_SEARCH_SCHEMA` | moved-lazy | `plugins.spotify.tools` | + +### `plugins.spotify.client` + +| name | kind | new location | +|---|---|---| +| `compact_json` | moved-lazy | `gateway.hosted_rooms_common` | +| `json` | import | `json` | + +### `plugins.spotify.tools` + +| name | kind | new location | +|---|---|---| +| `SpotifyAPIError` | moved-lazy | `plugins.spotify.client` | +| `SpotifyAuthRequiredError` | moved-lazy | `plugins.spotify.client` | + +### `plugins.teams_pipeline.cli` + +| name | kind | new location | +|---|---|---| +| `GraphSubscription` | moved-lazy | `plugins.teams_pipeline.models` | +| `Path` | import | `pathlib` | +| `datetime` | import | `datetime` | +| `timedelta` | import | `datetime` | +| `timezone` | import | `datetime` | + +### `plugins.teams_pipeline.meetings` + +| name | kind | new location | +|---|---|---| +| `fetch_call_record_artifact` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.teams_pipeline.pipeline` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `plugins.teams_pipeline.subscriptions` + +| name | kind | new location | +|---|---|---| +| `build_store` | restored-def | `(deleted; BASE body restored)` | +| `resolve_store_path` | restored-helper | `(deleted; restored as a dependency of build_store)` | +| `resolve_store_path` | restored-def | `(deleted; BASE body restored)` | +| `resolve_teams_pipeline_store_path` | restored-import | `plugins.teams_pipeline.store` | +| `resolve_teams_pipeline_store_path` | moved-lazy | `plugins.teams_pipeline.store` | + +### `plugins.video_gen.fal` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `plugins.video_gen.xai` + +| name | kind | new location | +|---|---|---| +| `_run_xai_video_coroutine` | restored-helper | `(deleted; restored as a dependency of run_xai_video_generation)` | +| `run_xai_video_generation` | restored-def | `(deleted; BASE body restored)` | + +### `plugins.web.brave_free.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | +| `os` | import | `os` | + +### `plugins.web.ddgs.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | + +### `plugins.web.exa.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | +| `os` | import | `os` | + +### `plugins.web.firecrawl.provider` + +| name | kind | new location | +|---|---|---| +| `NoReturn` | import | `typing` | +| `TYPE_CHECKING` | import | `typing` | +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | +| `os` | import | `os` | + +### `plugins.web.keenable.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | + +### `plugins.web.parallel.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | + +### `plugins.web.searxng.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | +| `os` | import | `os` | + +### `plugins.web.tavily.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | + +### `plugins.web.xai.provider` + +| name | kind | new location | +|---|---|---| +| `WebSearchProvider` | moved-lazy | `agent.web_search_provider` | + +### `providers` + +| name | kind | new location | +|---|---|---| +| `OMIT_TEMPERATURE` | moved-lazy | `providers.base` | + +### `run_agent` + +| name | kind | new location | +|---|---|---| +| `COMPRESSED_SUMMARY_METADATA_KEY` | moved-lazy | `agent.context_compressor` | +| `ContextCompressor` | moved-lazy | `agent.context_compressor` | +| `DEFAULT_AGENT_IDENTITY` | moved-lazy | `agent.prompt_builder` | +| `FailoverReason` | moved-lazy | `agent.error_classifier` | +| `OpenAI` | moved-lazy | `agent.process_bootstrap` | +| `SimpleNamespace` | import | `types` | +| `asyncio` | import | `asyncio` | +| `atomic_json_write` | moved-lazy | `utils` | +| `base64` | import | `base64` | +| `build_context_files_prompt` | moved-lazy | `agent.prompt_builder` | +| `build_environment_hints` | moved-lazy | `agent.prompt_builder` | +| `build_skills_system_prompt` | moved-lazy | `agent.prompt_builder` | +| `check_toolset_requirements` | moved-lazy | `model_tools` | +| `convert_scratchpad_to_think` | moved-lazy | `agent.trajectory` | +| `copy` | import | `copy` | +| `estimate_request_tokens_rough` | moved-lazy | `agent.model_metadata` | +| `file_mutation_result_landed` | moved-lazy | `agent.tool_result_classification` | +| `flatten_message_text` | moved-lazy | `agent.message_content` | +| `get_tool_definitions` | moved-lazy | `model_tools` | +| `handle_function_call` | moved-lazy | `model_tools` | +| `hashlib` | import | `hashlib` | +| `is_truthy_value` | moved-lazy | `utils` | +| `jittered_backoff` | moved-lazy | `agent.retry_utils` | +| `load_soul_md` | moved-lazy | `agent.prompt_builder` | +| `normalize_usage` | moved-lazy | `agent.usage_pricing` | +| `redact_sensitive_text` | moved-lazy | `agent.redact` | +| `request_hard_interrupt` | moved-lazy | `agent.interrupt_compat` | +| `sanitize_context` | moved-lazy | `agent.memory_manager` | +| `tempfile` | import | `tempfile` | +| `user_originated_turn_view` | moved-lazy | `agent.context_compressor` | + +### `tools` + +| name | kind | new location | +|---|---|---| +| `mcp_tool` | unrestorable | `no top-level definition on BASE` | + +### `tools.apply_layout_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `tools.approval` + +| name | kind | new location | +|---|---|---| +| `DANGEROUS_PATTERNS` | moved-lazy | `tools.approval_detection` | +| `DANGEROUS_PATTERNS_COMPILED` | moved-lazy | `tools.approval_detection` | +| `HARDLINE_PATTERNS` | moved-lazy | `tools.approval_detection` | +| `HARDLINE_PATTERNS_COMPILED` | moved-lazy | `tools.approval_detection` | +| `HUMAN_WAIT_MARGIN_S` | moved-lazy | `tools.approval_human_wait` | +| `cfg_get` | moved-lazy | `hermes_cli.config` | +| `contextlib` | import | `contextlib` | +| `contextvars` | import | `contextvars` | +| `fnmatch` | import | `fnmatch` | +| `functools` | import | `functools` | +| `get_plugin_manager` | moved-lazy | `tools.approval_prompt` | +| `human_wait_ceiling` | moved-lazy | `tools.approval_human_wait` | +| `human_wait_seconds` | moved-lazy | `tools.approval_human_wait` | +| `human_wait_window` | moved-lazy | `tools.approval_human_wait` | +| `is_interrupted` | moved-lazy | `tools.interrupt` | +| `re` | import | `re` | +| `request_elicitation_consent` | moved-lazy | `tools.approval_prompt` | +| `reset_current_observability_context` | moved-lazy | `tools.approval_context` | +| `reset_current_session_key` | moved-lazy | `tools.approval_context` | +| `reset_hermes_interactive_context` | moved-lazy | `tools.approval_context` | +| `set_current_observability_context` | moved-lazy | `tools.approval_context` | +| `set_current_session_key` | moved-lazy | `tools.approval_context` | +| `set_hermes_interactive_context` | moved-lazy | `tools.approval_context` | +| `shlex` | import | `shlex` | +| `sys` | import | `sys` | +| `tempfile` | import | `tempfile` | +| `time` | import | `time` | +| `unicodedata` | import | `unicodedata` | +| `uuid` | import | `uuid` | + +### `tools.async_delegation` + +| name | kind | new location | +|---|---|---| +| `active_for_session` | restored-def | `(deleted; BASE body restored)` | + +### `tools.browser_camofox_state` + +| name | kind | new location | +|---|---|---| +| `CAMOFOX_STATE_DIR_NAME` | restored-def | `(deleted; BASE body restored)` | +| `CAMOFOX_STATE_SUBDIR` | restored-def | `(deleted; BASE body restored)` | + +### `tools.browser_dialog_tool` + +| name | kind | new location | +|---|---|---| +| `logger` | moved-lazy | `tools.approval` | +| `logging` | import | `logging` | + +### `tools.browser_supervisor` + +| name | kind | new location | +|---|---|---| +| `CONSOLE_HISTORY_MAX` | restored-def | `(deleted; BASE body restored)` | +| `ConsoleEvent` | restored-def | `(deleted; BASE body restored)` | +| `DIALOG_BRIDGE_HOST` | moved-lazy | `tools.browser_supervisor_dialogs` | +| `DIALOG_BRIDGE_URL_PATTERN` | moved-lazy | `tools.browser_supervisor_dialogs` | +| `DIALOG_POLICY_AUTO_ACCEPT` | moved-lazy | `tools.browser_supervisor_dialogs` | +| `DIALOG_POLICY_AUTO_DISMISS` | moved-lazy | `tools.browser_supervisor_dialogs` | +| `DIALOG_POLICY_MUST_RESPOND` | moved-lazy | `tools.browser_supervisor_dialogs` | +| `FRAME_TREE_MAX_ENTRIES` | moved-lazy | `tools.browser_supervisor_frames` | +| `FRAME_TREE_MAX_OOPIF_DEPTH` | moved-lazy | `tools.browser_supervisor_frames` | + +### `tools.browser_tool` + +| name | kind | new location | +|---|---|---| +| `BrowserUseProvider` | moved-lazy | `plugins.browser.browser_use.provider.BrowserUseBrowserProvider` | +| `BrowserbaseProvider` | moved-lazy | `plugins.browser.browserbase.provider.BrowserbaseBrowserProvider` | +| `CloudBrowserProvider` | moved-lazy | `agent.browser_provider.BrowserProvider` | +| `FirecrawlProvider` | moved-lazy | `plugins.browser.firecrawl.provider.FirecrawlBrowserProvider` | +| `List` | import | `typing` | +| `SNAPSHOT_SUMMARIZE_THRESHOLD` | restored-def | `(deleted; BASE body restored)` | +| `Tuple` | import | `typing` | +| `agent_browser_runnable` | moved-lazy | `hermes_constants` | +| `check_browser_requirements` | moved-lazy | `tools.browser_tool_install` | +| `check_browser_vision_requirements` | moved-lazy | `tools.browser_tool_install` | +| `cleanup_all_browsers` | moved-lazy | `tools.browser_tool_lifecycle` | +| `cleanup_browser` | moved-lazy | `tools.browser_tool_lifecycle` | +| `contextlib` | import | `contextlib` | +| `datetime` | import | `datetime` | +| `functools` | import | `functools` | +| `get_hermes_home_override` | moved-lazy | `hermes_constants` | +| `hermes_home_key` | moved-lazy | `hermes_constants` | +| `is_truthy_value` | moved-lazy | `utils` | +| `lightpanda_engine_status` | moved-lazy | `tools.browser_tool_lightpanda_fallback` | +| `node_tool_runnable` | moved-lazy | `hermes_constants` | +| `normalize_browser_cloud_provider` | moved-lazy | `tools.tool_backend_helpers` | +| `re` | import | `re` | +| `reset_hermes_home_override` | moved-lazy | `hermes_constants` | +| `set_hermes_home_override` | moved-lazy | `hermes_constants` | +| `shutil` | import | `shutil` | +| `signal` | import | `signal` | +| `timezone` | import | `datetime` | +| `warm_agent_browser_npx_cache` | moved-lazy | `tools.browser_tool_install` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | + +### `tools.clarify_gateway` + +| name | kind | new location | +|---|---|---| +| `get_notify` | restored-def | `(deleted; BASE body restored)` | +| `register_notify` | restored-def | `(deleted; BASE body restored)` | +| `unregister_notify` | restored-def | `(deleted; BASE body restored)` | + +### `tools.close_preview_tool` + +| name | kind | new location | +|---|---|---| +| `CLOSE_PREVIEW_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | +| `registry` | moved-lazy | `tools.registry` | +| `tool_error` | moved-lazy | `tools.registry` | + +### `tools.code_execution_tool` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_KERNEL_MODE` | restored-def | `(deleted; BASE body restored)` | +| `KERNEL_MODES` | restored-def | `(deleted; BASE body restored)` | +| `platform` | import | `platform` | +| `socket` | import | `socket` | +| `sys` | import | `sys` | +| `thread_scoped_silence` | moved-lazy | `agent.thread_scoped_output` | + +### `tools.code_kernel_remote` + +| name | kind | new location | +|---|---|---| +| `base64` | import | `base64` | + +### `tools.computer_use` + +| name | kind | new location | +|---|---|---| +| `annotations` | import | `__future__` | +| `check_computer_use_requirements` | moved-lazy | `tools.computer_use.tool` | +| `get_computer_use_schema` | moved-lazy | `tools.computer_use.tool` | +| `handle_computer_use` | moved-lazy | `tools.computer_use.tool` | +| `release_computer_use_session` | moved-lazy | `tools.computer_use.tool` | +| `set_approval_callback` | moved-lazy | `tools.computer_use.tool` | + +### `tools.computer_use.cua_backend` + +| name | kind | new location | +|---|---|---| +| `CaptureResult` | moved-lazy | `tools.computer_use.backend` | +| `PureWindowsPath` | import | `pathlib` | +| `Tuple` | import | `typing` | +| `UIElement` | moved-lazy | `tools.computer_use.backend` | +| `asyncio` | import | `asyncio` | +| `base64` | import | `base64` | +| `concurrent` | import | `concurrent.futures` | +| `cua_driver_install_hint` | moved-lazy | `tools.computer_use.cua_backend_driver` | +| `cua_driver_update_check` | moved-lazy | `tools.computer_use.cua_backend_driver` | +| `deque` | import | `collections` | +| `functools` | import | `functools` | +| `json` | import | `json` | +| `re` | import | `re` | +| `shutil` | import | `shutil` | +| `tempfile` | import | `tempfile` | +| `time` | import | `time` | + +### `tools.computer_use.permissions` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | + +### `tools.computer_use.tool` + +| name | kind | new location | +|---|---|---| +| `struct` | import | `struct` | + +### `tools.cronjob_tools` + +| name | kind | new location | +|---|---|---| +| `effective_job_state` | moved-lazy | `cron.jobs` | +| `re` | import | `re` | + +### `tools.delegate_tool` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_CHILD_TIMEOUT` | moved-lazy | `tools.delegate_tool_config` | +| `DEFAULT_MAX_SUMMARY_CHARS` | moved-lazy | `tools.delegate_tool_results` | +| `DEFAULT_TOOLSETS` | moved-lazy | `tools.delegate_tool_toolsets` | +| `FuturesTimeoutError` | import | `concurrent.futures` | +| `MAX_DEPTH` | moved-lazy | `tools.delegate_tool_config` | +| `TOOLSETS` | moved-lazy | `toolsets` | +| `base_url_hostname` | moved-lazy | `utils` | +| `contextvars` | import | `contextvars` | +| `enum` | import | `enum` | +| `file_state` | moved-lazy | `tools` | +| `json` | import | `json` | +| `os` | import | `os` | +| `re` | import | `re` | +| `request_hard_interrupt` | moved-lazy | `agent.interrupt_compat` | +| `threading` | import | `threading` | +| `urlsplit` | import | `urllib.parse` | +| `urlunsplit` | import | `urllib.parse` | + +### `tools.delegation_live_log` + +| name | kind | new location | +|---|---|---| +| `new_live_delegation_id` | restored-def | `(deleted; BASE body restored)` | + +### `tools.delegation_output_schema` + +| name | kind | new location | +|---|---|---| +| `MAX_SCHEMA_RETRIES` | restored-def | `(deleted; BASE body restored)` | + +### `tools.discord_tool` + +| name | kind | new location | +|---|---|---| +| `TYPE_CHECKING` | import | `typing` | +| `Tuple` | import | `typing` | +| `get_dynamic_schema` | restored-def | `(deleted; BASE body restored)` | + +### `tools.drive_preview_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `tools.env_probe` + +| name | kind | new location | +|---|---|---| +| `os` | import | `os` | + +### `tools.environments` + +| name | kind | new location | +|---|---|---| +| `modal_utils` | unrestorable | `no top-level definition on BASE` | + +### `tools.environments.base` + +| name | kind | new location | +|---|---|---| +| `IO` | import | `typing` | +| `Protocol` | import | `typing` | +| `codecs` | import | `codecs` | +| `deque` | import | `collections` | +| `re` | import | `re` | +| `sanitize_task_id_for_path` | moved-lazy | `tools.environments.path_utils` | +| `select` | import | `select` | +| `subprocess` | import | `subprocess` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | + +### `tools.environments.managed_modal` + +| name | kind | new location | +|---|---|---| +| `BaseModalExecutionEnvironment` | unrestorable | `no top-level definition on BASE` | +| `ModalExecStart` | unrestorable | `no top-level definition on BASE` | +| `PreparedModalExec` | unrestorable | `no top-level definition on BASE` | +| `dataclass` | import | `dataclasses` | + +### `tools.environments.modal_utils` + +| name | kind | new location | +|---|---|---| +| `*` | module-stub | `(deleted)` | + +### `tools.environments.vercel_sandbox` + +| name | kind | new location | +|---|---|---| +| `dataclass` | import | `dataclasses` | + +### `tools.feishu_doc_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `logger` | moved-lazy | `tools.approval` | +| `logging` | import | `logging` | +| `threading` | import | `threading` | + +### `tools.feishu_drive_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `threading` | import | `threading` | + +### `tools.file_operations` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `ClassVar` | import | `typing` | +| `DEFAULT_READ_LIMIT` | moved-lazy | `tools.file_operations_common` | +| `DEFAULT_READ_OFFSET` | moved-lazy | `tools.file_operations_common` | +| `DEFAULT_SEARCH_LIMIT` | moved-lazy | `tools.file_operations_common` | +| `DEFAULT_SEARCH_OFFSET` | moved-lazy | `tools.file_operations_common` | +| `LINTERS` | moved-lazy | `tools.file_operations_lint` | +| `LintResult` | moved-lazy | `tools.file_operations_common` | +| `List` | import | `typing` | +| `MAX_FILE_SIZE` | moved-lazy | `tools.transcription_common` | +| `MAX_LINES` | restored-def | `(deleted; BASE body restored)` | +| `MAX_LINE_LENGTH` | restored-def | `(deleted; BASE body restored)` | +| `SEARCH_PRUNE_DIR_NAMES` | moved-lazy | `agent.search_policy` | +| `SearchMatch` | moved-lazy | `tools.file_operations_common` | +| `WRITE_DENIED_PATHS` | restored-def | `(deleted; BASE body restored)` | +| `WRITE_DENIED_PREFIXES` | restored-def | `(deleted; BASE body restored)` | +| `build_write_denied_paths` | restored-import | `agent.file_safety` | +| `build_write_denied_paths` | moved-lazy | `agent.file_safety` | +| `build_write_denied_prefixes` | restored-import | `agent.file_safety` | +| `build_write_denied_prefixes` | moved-lazy | `agent.file_safety` | +| `dataclass` | import | `dataclasses` | +| `field` | import | `dataclasses` | +| `posixpath` | import | `posixpath` | +| `threading` | import | `threading` | +| `tool_interrupt` | moved-lazy | `tools.interrupt` | + +### `tools.file_tools` + +| name | kind | new location | +|---|---|---| +| `PurePosixPath` | import | `pathlib` | +| `has_opaque_document_extension` | moved-lazy | `tools.binary_extensions` | +| `is_pdf_path` | moved-lazy | `tools.binary_extensions` | +| `notify_other_tool_call` | moved-lazy | `tools.file_tools_read_tracking` | +| `posixpath` | import | `posixpath` | +| `reset_file_dedup` | moved-lazy | `tools.file_tools_read_tracking` | +| `sys` | import | `sys` | + +### `tools.focus_pane_tool` + +| name | kind | new location | +|---|---|---| +| `FOCUS_PANE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | + +### `tools.fuzzy_match` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | +| `Tuple` | import | `typing` | + +### `tools.image_generation_tool` + +| name | kind | new location | +|---|---|---| +| `is_krea_model` | restored-def | `(deleted; BASE body restored)` | + +### `tools.lazy_deps` + +| name | kind | new location | +|---|---|---| +| `feature_specs` | restored-def | `(deleted; BASE body restored)` | + +### `tools.managed_tool_gateway` + +| name | kind | new location | +|---|---|---| +| `_MANAGED_GATEWAY_VENDOR` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `_MEDIA_UPLOAD_PRESIGN_TIMEOUT_SECONDS` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `_MEDIA_UPLOAD_PUT_READ_TIMEOUT_SECONDS` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `_MEDIA_UPLOAD_PUT_WRITE_TIMEOUT_SECONDS` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `_describe_media_upload_refusal` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `build_managed_media_uploader` | restored-def | `(deleted; BASE body restored)` | +| `is_managed_nous_gateway_url` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `is_managed_nous_gateway_url` | restored-def | `(deleted; BASE body restored)` | +| `managed_gateway_auth_headers` | restored-helper | `(deleted; restored as a dependency of build_managed_media_uploader)` | +| `managed_gateway_auth_headers` | restored-def | `(deleted; BASE body restored)` | +| `managed_vendor_base_path` | restored-def | `(deleted; BASE body restored)` | +| `managed_vendor_endpoints` | restored-def | `(deleted; BASE body restored)` | +| `managed_vendor_upload_path` | restored-helper | `(deleted; restored as a dependency of managed_vendor_endpoints)` | +| `managed_vendor_upload_path` | restored-def | `(deleted; BASE body restored)` | +| `urlsplit` | restored-import | `urllib.parse` | +| `urlsplit` | import | `urllib.parse` | + +### `tools.mcp_oauth` + +| name | kind | new location | +|---|---|---| +| `OAuthClientInformationFull` | restored-def | `(deleted; BASE body restored)` | +| `OAuthClientMetadata` | restored-def | `(deleted; BASE body restored)` | +| `OAuthClientProvider` | restored-def | `(deleted; BASE body restored)` | +| `OAuthMetadata` | restored-def | `(deleted; BASE body restored)` | +| `OAuthToken` | restored-def | `(deleted; BASE body restored)` | +| `contextmanager` | import | `contextlib` | + +### `tools.mcp_schema_cache` + +| name | kind | new location | +|---|---|---| +| `clear_cache_entry` | restored-def | `(deleted; BASE body restored)` | +| `has_cached_entry` | restored-def | `(deleted; BASE body restored)` | + +### `tools.mcp_tool` + +| name | kind | new location | +|---|---|---| +| `Coroutine` | import | `typing` | +| `InvalidMcpUrlError` | moved-lazy | `tools.mcp_tool_errors` | +| `MCP_TOOL_NAME_PREFIX` | moved-lazy | `tools.mcp_tool_schema` | +| `NonMcpEndpointError` | moved-lazy | `tools.mcp_tool_errors` | +| `SimpleNamespace` | import | `types` | +| `Tuple` | import | `typing` | +| `asynccontextmanager` | import | `contextlib` | +| `concurrent` | import | `concurrent.futures` | +| `datetime` | import | `datetime` | +| `discover_mcp_tools` | moved-lazy | `tools.mcp_tool_discovery` | +| `errno` | import | `errno` | +| `fnmatch` | import | `fnmatch` | +| `get_mcp_status` | moved-lazy | `tools.mcp_tool_discovery` | +| `get_registered_mcp_server_names` | moved-lazy | `tools.mcp_tool_discovery` | +| `has_registered_mcp_tools` | moved-lazy | `tools.mcp_tool_discovery` | +| `is_mcp_tool_parallel_safe` | moved-lazy | `tools.mcp_tool_discovery` | +| `json` | import | `json` | +| `matches_name_filter` | moved-lazy | `tools.mcp_tool_schema` | +| `math` | import | `math` | +| `mcp_prefixed_tool_name` | moved-lazy | `tools.mcp_tool_schema` | +| `persist_agent_tool_names` | moved-lazy | `tools.mcp_tool_agent` | +| `probe_mcp_server_tools` | moved-lazy | `tools.mcp_tool_discovery` | +| `random` | import | `random` | +| `re` | import | `re` | +| `reconnect_mcp_server` | moved-lazy | `tools.mcp_tool_loop` | +| `refresh_agent_mcp_tools` | moved-lazy | `tools.mcp_tool_agent` | +| `register_mcp_servers` | moved-lazy | `tools.mcp_tool_discovery` | +| `reprobe_tool_availability` | moved-lazy | `tools.mcp_tool_agent` | +| `restore_agent_tool_prefix` | moved-lazy | `tools.mcp_tool_agent` | +| `sanitize_mcp_name_component` | moved-lazy | `tools.mcp_tool_schema` | +| `shutdown_mcp_servers` | moved-lazy | `tools.mcp_tool_lifecycle` | +| `shutil` | import | `shutil` | +| `strip_unicode_tags` | moved-lazy | `tools.ansi_strip` | +| `tool_error` | moved-lazy | `tools.registry` | +| `urlparse` | import | `urllib.parse` | + +### `tools.memory_tool` + +| name | kind | new location | +|---|---|---| +| `atomic_write_text` | moved-lazy | `utils` | +| `contextmanager` | import | `contextlib` | +| `time` | import | `time` | + +### `tools.microsoft_graph_client` + +| name | kind | new location | +|---|---|---| +| `AsyncIterator` | import | `typing` | +| `GraphCredentials` | moved-lazy | `tools.microsoft_graph_auth` | + +### `tools.open_preview_tool` + +| name | kind | new location | +|---|---|---| +| `OPEN_PREVIEW_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | +| `registry` | moved-lazy | `tools.registry` | + +### `tools.openrouter_client` + +| name | kind | new location | +|---|---|---| +| `get_async_client` | restored-def | `(deleted; BASE body restored)` | + +### `tools.path_security` + +| name | kind | new location | +|---|---|---| +| `logger` | moved-lazy | `tools.approval` | +| `logging` | import | `logging` | + +### `tools.preview_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `preview_open` | restored-def | `(deleted; BASE body restored)` | + +### `tools.process_registry` + +| name | kind | new location | +|---|---|---| +| `MAX_ACTIVE_PROCESS_AGE` | restored-def | `(deleted; BASE body restored)` | + +### `tools.react_to_message_tool` + +| name | kind | new location | +|---|---|---| +| `env_var_enabled` | moved-lazy | `utils` | + +### `tools.read_extract` + +| name | kind | new location | +|---|---|---| +| `MAX_XLSX_BYTES` | restored-def | `(deleted; BASE body restored)` | + +### `tools.read_preview_tool` + +| name | kind | new location | +|---|---|---| +| `READ_PREVIEW_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | +| `registry` | moved-lazy | `tools.registry` | +| `tool_error` | moved-lazy | `tools.registry` | + +### `tools.read_terminal_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | + +### `tools.read_window_tool` + +| name | kind | new location | +|---|---|---| +| `json` | import | `json` | +| `tool_error` | moved-lazy | `tools.registry` | + +### `tools.send_message_tool` + +| name | kind | new location | +|---|---|---| +| `SEND_MESSAGE_SCHEMA` | restored-def | `(deleted; BASE body restored)` | +| `re` | import | `re` | +| `redact_sensitive_text` | moved-lazy | `agent.redact` | +| `time` | import | `time` | + +### `tools.skill_ledger` + +| name | kind | new location | +|---|---|---| +| `ACTOR_AGENT` | restored-def | `(deleted; BASE body restored)` | +| `ACTOR_CURATOR` | restored-def | `(deleted; BASE body restored)` | +| `ACTOR_USER` | restored-def | `(deleted; BASE body restored)` | + +### `tools.skill_linter` + +| name | kind | new location | +|---|---|---| +| `format_findings` | restored-def | `(deleted; BASE body restored)` | +| `has_errors` | restored-def | `(deleted; BASE body restored)` | + +### `tools.skill_manager_tool` + +| name | kind | new location | +|---|---|---| +| `mark_background_review_skill_read` | moved-lazy | `tools.skill_manager_guards` | + +### `tools.skill_usage` + +| name | kind | new location | +|---|---|---| +| `_suppressed_file` | restored-helper | `(deleted; restored as a dependency of add_suppressed_name)` | +| `_write_suppressed_names` | restored-helper | `(deleted; restored as a dependency of add_suppressed_name)` | +| `add_suppressed_name` | restored-def | `(deleted; BASE body restored)` | +| `agent_created_report` | restored-def | `(deleted; BASE body restored)` | +| `os` | restored-import | `os` | +| `os` | import | `os` | +| `remove_suppressed_name` | restored-def | `(deleted; BASE body restored)` | +| `tempfile` | restored-import | `tempfile` | +| `tempfile` | import | `tempfile` | + +### `tools.skillevaluator_scan` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | +| `SCANNER_NAME` | restored-def | `(deleted; BASE body restored)` | +| `scanner_available` | restored-def | `(deleted; BASE body restored)` | + +### `tools.skills_guard` + +| name | kind | new location | +|---|---|---| +| `full_content_hash` | restored-def | `(deleted; BASE body restored)` | + +### `tools.skills_hub` + +| name | kind | new location | +|---|---|---| +| `ABC` | import | `abc` | +| `BrowseShSource` | moved-lazy | `tools.skills_hub_sources` | +| `ClawHubSource` | moved-lazy | `tools.skills_hub_clawhub` | +| `GITHUB_TAP_PROVIDERS` | moved-lazy | `tools.skills_hub_github` | +| `GitHubAuth` | moved-lazy | `tools.skills_hub_github` | +| `GitHubSource` | moved-lazy | `tools.skills_hub_github` | +| `HERMES_INDEX_TTL` | moved-lazy | `tools.skills_hub_search` | +| `HERMES_INDEX_URL` | moved-lazy | `tools.skills_hub_search` | +| `HermesIndexSource` | moved-lazy | `tools.skills_hub_official` | +| `LobeHubSource` | moved-lazy | `tools.skills_hub_sources` | +| `OptionalSkillSource` | moved-lazy | `tools.skills_hub_official` | +| `PurePosixPath` | import | `pathlib` | +| `ScanResult` | moved-lazy | `tools.skills_guard` | +| `SkillBundle` | moved-lazy | `tools.skills_hub_models` | +| `SkillMeta` | moved-lazy | `tools.skills_hub_models` | +| `SkillSource` | moved-lazy | `tools.skills_hub_models` | +| `SkillsShSource` | moved-lazy | `tools.skills_hub_skillssh` | +| `TRUSTED_REPOS` | moved-lazy | `tools.skills_guard` | +| `Tuple` | import | `typing` | +| `Union` | import | `typing` | +| `UrlSource` | moved-lazy | `tools.skills_hub_sources` | +| `WellKnownSkillSource` | moved-lazy | `tools.skills_hub_sources` | +| `abstractmethod` | import | `abc` | +| `bundle_content_hash` | moved-lazy | `tools.skills_hub_install` | +| `check_for_skill_updates` | moved-lazy | `tools.skills_hub_install` | +| `content_hash` | moved-lazy | `tools.skills_guard` | +| `create_source_router` | moved-lazy | `tools.skills_hub_search` | +| `dataclass` | import | `dataclasses` | +| `field` | import | `dataclasses` | +| `github_provider_for` | moved-lazy | `tools.skills_hub_github` | +| `hashlib` | import | `hashlib` | +| `install_from_quarantine` | moved-lazy | `tools.skills_hub_install` | +| `is_excluded_skill_path` | moved-lazy | `agent.skill_utils` | +| `os` | import | `os` | +| `parallel_search_sources` | moved-lazy | `tools.skills_hub_search` | +| `quarantine_bundle` | moved-lazy | `tools.skills_hub_install` | +| `quote` | import | `urllib.parse` | +| `re` | import | `re` | +| `shutil` | import | `shutil` | +| `source_url_for_bundle` | moved-lazy | `tools.skills_hub_models` | +| `subprocess` | import | `subprocess` | +| `unified_search` | moved-lazy | `tools.skills_hub_search` | +| `uninstall_skill` | moved-lazy | `tools.skills_hub_install` | +| `unquote` | import | `urllib.parse` | +| `urlparse` | import | `urllib.parse` | +| `urlsplit` | import | `urllib.parse` | +| `urlunparse` | import | `urllib.parse` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | +| `yaml` | import | `yaml` | + +### `tools.skills_sync` + +| name | kind | new location | +|---|---|---| +| `PurePosixPath` | import | `pathlib` | +| `atomic_replace` | moved-lazy | `utils` | +| `datetime` | import | `datetime` | +| `diff_bundled_skill` | moved-lazy | `tools.skills_sync_bundled_ops` | +| `is_bundled_skills_opt_out` | restored-def | `(deleted; BASE body restored)` | +| `json` | import | `json` | +| `list_user_modified_bundled_skills` | moved-lazy | `tools.skills_sync_bundled_ops` | +| `remove_pristine_bundled_skills` | moved-lazy | `tools.skills_sync_bundled_ops` | +| `reset_bundled_skill` | moved-lazy | `tools.skills_sync_bundled_ops` | +| `restore_official_optional_skill` | moved-lazy | `tools.skills_sync_optional` | +| `set_bundled_skills_opt_out` | moved-lazy | `tools.skills_sync_bundled_ops` | +| `timezone` | import | `datetime` | + +### `tools.skills_sync_client` + +| name | kind | new location | +|---|---|---| +| `ARTIFACT_TYPE_SKILL` | moved-lazy | `tools.skills_sync_client_wire` | +| `KIND_COMMIT` | restored-def | `(deleted; BASE body restored)` | +| `KIND_TREE` | restored-def | `(deleted; BASE body restored)` | +| `MODE_DIR` | restored-def | `(deleted; BASE body restored)` | +| `MODE_EXEC` | restored-def | `(deleted; BASE body restored)` | +| `MODE_FILE` | restored-def | `(deleted; BASE body restored)` | +| `SYNC_MANIFEST_ENTRY_NAME` | moved-lazy | `tools.skills_sync_client_wire` | +| `SYNC_MANIFEST_TYPE` | restored-def | `(deleted; BASE body restored)` | +| `SYNC_MANIFEST_VERSION` | moved-lazy | `tools.skills_sync_client_wire` | +| `WIRE_VERSION` | moved-lazy | `tools.skills_sync_client_wire` | +| `canonical_json_bytes` | moved-lazy | `tools.skills_sync_client_wire` | +| `datetime` | import | `datetime` | +| `dev_gate_open` | restored-def | `(deleted; BASE body restored)` | +| `hashlib` | import | `hashlib` | +| `maybe_pull_org_skills` | moved-lazy | `tools.skills_sync_client_org` | +| `org_head_ref` | moved-lazy | `tools.skills_sync_client_org` | +| `org_skill_is_locally_modified` | moved-lazy | `tools.skills_sync_client_org` | +| `org_sync_available` | restored-def | `(deleted; BASE body restored)` | +| `parse_sync_manifest` | moved-lazy | `tools.skills_sync_client_wire` | +| `propose_skill` | moved-lazy | `tools.skills_sync_client_org` | +| `pull_org_skills` | moved-lazy | `tools.skills_sync_client_org` | +| `time` | import | `time` | +| `timezone` | import | `datetime` | +| `user_conflict_ref` | restored-def | `(deleted; BASE body restored)` | +| `wire_address` | moved-lazy | `tools.skills_sync_client_wire` | + +### `tools.skills_tool` + +| name | kind | new location | +|---|---|---| +| `Enum` | import | `enum` | +| `Set` | import | `typing` | +| `display_hermes_home` | moved-lazy | `hermes_constants` | +| `env_var_enabled` | moved-lazy | `utils` | +| `re` | import | `re` | +| `threading` | import | `threading` | + +### `tools.slash_confirm` + +| name | kind | new location | +|---|---|---| +| `asyncio` | import | `asyncio` | +| `resolve_sync_compat` | restored-def | `(deleted; BASE body restored)` | + +### `tools.terminal_scope` + +| name | kind | new location | +|---|---|---| +| `install_refusal_scope` | restored-def | `(deleted; BASE body restored)` | +| `terminal_scope` | restored-def | `(deleted; BASE body restored)` | + +### `tools.terminal_tool` + +| name | kind | new location | +|---|---|---| +| `Path` | import | `pathlib` | +| `cleanup_vm` | moved-lazy | `tools.terminal_tool_lifecycle` | +| `env_var_enabled` | moved-lazy | `utils` | +| `get_active_env` | moved-lazy | `tools.terminal_tool_lifecycle` | +| `has_direct_modal_credentials` | moved-lazy | `tools.tool_backend_helpers` | +| `importlib` | import | `importlib.util` | +| `is_interrupted` | moved-lazy | `tools.interrupt` | +| `is_managed_tool_gateway_ready` | moved-lazy | `tools.managed_tool_gateway` | +| `is_persistent_env` | moved-lazy | `tools.terminal_tool_lifecycle` | +| `nous_tool_gateway_unavailable_message` | moved-lazy | `tools.tool_backend_helpers` | +| `platform` | import | `platform` | +| `re` | import | `re` | +| `resolve_modal_backend_state` | moved-lazy | `tools.tool_backend_helpers` | +| `shlex` | import | `shlex` | +| `shutil` | import | `shutil` | +| `stat` | import | `stat` | +| `strip_inert_heredoc_bodies` | moved-lazy | `tools.shell_heredoc` | +| `subprocess` | import | `subprocess` | +| `sys` | import | `sys` | + +### `tools.tool_result_storage` + +| name | kind | new location | +|---|---|---| +| `HEREDOC_MARKER` | restored-def | `(deleted; BASE body restored)` | +| `uuid` | import | `uuid` | + +### `tools.tool_search` + +| name | kind | new location | +|---|---|---| +| `Literal` | import | `typing` | +| `build_catalog_listing` | restored-def | `(deleted; BASE body restored)` | +| `copy` | import | `copy` | +| `field` | import | `dataclasses` | +| `re` | import | `re` | +| `snowballstemmer` | import | `snowballstemmer` | +| `threading` | import | `threading` | + +### `tools.transcription_tools` + +| name | kind | new location | +|---|---|---| +| `COMMAND_STT_OUTPUT_FORMATS` | moved-lazy | `tools.transcription_command` | +| `COMMON_LOCAL_BIN_DIRS` | moved-lazy | `tools.transcription_common` | +| `DEFAULT_COMMAND_STT_LANGUAGE` | moved-lazy | `tools.transcription_command` | +| `DEFAULT_COMMAND_STT_OUTPUT_FORMAT` | moved-lazy | `tools.transcription_command` | +| `DEFAULT_COMMAND_STT_TIMEOUT_SECONDS` | moved-lazy | `tools.transcription_command` | +| `DEFAULT_LOCAL_STT_LANGUAGE` | moved-lazy | `tools.transcription_common` | +| `ELEVENLABS_STT_BASE_URL` | moved-lazy | `tools.transcription_common` | +| `GROQ_BASE_URL` | moved-lazy | `tools.transcription_common` | +| `GROQ_MODELS` | moved-lazy | `tools.transcription_common` | +| `LOCAL_NATIVE_AUDIO_FORMATS` | moved-lazy | `tools.transcription_common` | +| `MAX_FILE_SIZE` | moved-lazy | `tools.transcription_common` | +| `OPENAI_BASE_URL` | moved-lazy | `tools.transcription_common` | +| `OPENAI_MODELS` | moved-lazy | `tools.transcription_common` | +| `SUPPORTED_FORMATS` | moved-lazy | `tools.transcription_common` | +| `XAI_STT_BASE_URL` | moved-lazy | `tools.transcription_common` | +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | +| `nous_tool_gateway_unavailable_message` | moved-lazy | `tools.tool_backend_helpers` | +| `platform` | import | `platform` | +| `queue` | import | `queue` | +| `re` | import | `re` | +| `resolve_managed_tool_gateway` | moved-lazy | `tools.managed_tool_gateway` | +| `resolve_openai_audio_api_key` | moved-lazy | `tools.tool_backend_helpers` | +| `shlex` | import | `shlex` | +| `subprocess` | import | `subprocess` | +| `tempfile` | import | `tempfile` | +| `urljoin` | import | `urllib.parse` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | + +### `tools.tts_tool` + +| name | kind | new location | +|---|---|---| +| `AudioDeliveryProfile` | moved-lazy | `tools.tts_tool_delivery` | +| `COMMAND_TTS_OUTPUT_FORMATS` | moved-lazy | `tools.tts_command_provider` | +| `DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTH` | moved-lazy | `tools.tts_command_provider` | +| `DEFAULT_COMMAND_TTS_OUTPUT_FORMAT` | moved-lazy | `tools.tts_command_provider` | +| `DEFAULT_COMMAND_TTS_TIMEOUT_SECONDS` | moved-lazy | `tools.tts_command_provider` | +| `DEFAULT_DEEPINFRA_TTS_VOICE` | moved-lazy | `tools.tts_tool_openai` | +| `DEFAULT_EDGE_VOICE` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_ELEVENLABS_MODEL_ID` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_ELEVENLABS_STREAMING_MODEL_ID` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_ELEVENLABS_VOICE_ID` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_GEMINI_AUDIO_TAGS` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_GEMINI_TTS_BASE_URL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_GEMINI_TTS_MODEL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_GEMINI_TTS_VOICE` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_KITTENTTS_MODEL` | moved-lazy | `tools.tts_tool_local` | +| `DEFAULT_KITTENTTS_VOICE` | moved-lazy | `tools.tts_tool_local` | +| `DEFAULT_MINIMAX_BASE_URL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_MINIMAX_CN_BASE_URL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_MINIMAX_MODEL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_MINIMAX_VOICE_ID` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_MISTRAL_TTS_MODEL` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_MISTRAL_TTS_VOICE_ID` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_OPENAI_BASE_URL` | moved-lazy | `tools.tts_tool_openai` | +| `DEFAULT_OPENAI_MODEL` | moved-lazy | `tools.tts_tool_openai` | +| `DEFAULT_OPENAI_VOICE` | moved-lazy | `tools.tts_tool_openai` | +| `DEFAULT_PIPER_VOICE` | moved-lazy | `tools.tts_tool_local` | +| `DEFAULT_XAI_AUTO_SPEECH_TAGS` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_BASE_URL` | moved-lazy | `tools.xai_http` | +| `DEFAULT_XAI_BIT_RATE` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_LANGUAGE` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_OPTIMIZE_STREAMING_LATENCY_DEFAULT` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_SAMPLE_RATE` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_SPEED_DEFAULT` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_SPEED_MAX` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_SPEED_MIN` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_TEXT_NORMALIZATION_DEFAULT` | moved-lazy | `tools.tts_tool_providers` | +| `DEFAULT_XAI_VOICE_ID` | moved-lazy | `tools.tts_tool_providers` | +| `ELEVENLABS_MODEL_MAX_TEXT_LENGTH` | moved-lazy | `tools.tts_tool_delivery` | +| `FALLBACK_MAX_TEXT_LENGTH` | moved-lazy | `tools.tts_tool_delivery` | +| `Future` | import | `concurrent.futures` | +| `GEMINI_AUDIO_TAG_REWRITE_TASK` | moved-lazy | `tools.tts_tool_providers` | +| `GEMINI_TTS_CHANNELS` | restored-def | `(deleted; BASE body restored)` | +| `GEMINI_TTS_SAMPLE_RATE` | restored-def | `(deleted; BASE body restored)` | +| `GEMINI_TTS_SAMPLE_WIDTH` | restored-def | `(deleted; BASE body restored)` | +| `Iterator` | import | `typing` | +| `MANAGED_OPENAI_TTS_MODELS` | moved-lazy | `tools.tts_tool_openai` | +| `MAX_TEXT_LENGTH` | moved-lazy | `gateway.platforms.bluebubbles` | +| `PROVIDER_MAX_TEXT_LENGTH` | moved-lazy | `tools.tts_tool_delivery` | +| `TTS_RESPONSE_BODY_CHUNK_BYTES` | moved-lazy | `tools.tts_tool_providers` | +| `TTS_RESPONSE_BODY_LIMIT_BYTES` | moved-lazy | `tools.tts_tool_providers` | +| `ThreadPoolExecutor` | import | `concurrent.futures` | +| `Tuple` | import | `typing` | +| `acquire_tts_lease` | moved-lazy | `tools.tts_tool_lifecycle` | +| `base64` | import | `base64` | +| `dataclass` | import | `dataclasses` | +| `field` | import | `dataclasses` | +| `hermes_xai_user_agent` | moved-lazy | `tools.xai_http` | +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | +| `nous_tool_gateway_unavailable_message` | moved-lazy | `tools.tool_backend_helpers` | +| `platform` | import | `platform` | +| `queue` | import | `queue` | +| `re` | import | `re` | +| `read_selection` | moved-lazy | `tools.tool_backend_helpers` | +| `release_tts_lease` | moved-lazy | `tools.tts_tool_lifecycle` | +| `release_tts_provider` | moved-lazy | `tools.tts_tool_lifecycle` | +| `resolve_managed_tool_gateway` | moved-lazy | `tools.managed_tool_gateway` | +| `resolve_openai_audio_api_key` | moved-lazy | `tools.tool_backend_helpers` | +| `selection_error` | moved-lazy | `tools.tool_backend_helpers` | +| `shlex` | import | `shlex` | +| `shutil` | import | `shutil` | +| `stream_tts_to_speaker` | moved-lazy | `tools.tts_tool_speaker` | +| `subprocess` | import | `subprocess` | +| `threading` | import | `threading` | +| `time` | import | `time` | +| `tts_lease_holders` | moved-lazy | `tools.tts_tool_lifecycle` | +| `urljoin` | import | `urllib.parse` | +| `urlparse` | import | `urllib.parse` | +| `uuid` | import | `uuid` | +| `warm_tts_provider` | moved-lazy | `tools.tts_tool_lifecycle` | +| `windows_hide_flags` | moved-lazy | `hermes_cli._subprocess_compat` | + +### `tools.url_safety` + +| name | kind | new location | +|---|---|---| +| `has_sensitive_query_params` | restored-def | `(deleted; BASE body restored)` | +| `ssrf_safe_async_http_transport` | restored-def | `(deleted; BASE body restored)` | +| `ssrf_safe_http_transport` | restored-def | `(deleted; BASE body restored)` | + +### `tools.vision_tools` + +| name | kind | new location | +|---|---|---| +| `contextlib` | import | `contextlib` | +| `sys` | import | `sys` | +| `threading` | import | `threading` | + +### `tools.voice_mode` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_TTS_ECHO_SIMILARITY_THRESHOLD` | moved-lazy | `tools.voice_mode_transcript` | +| `DEFAULT_VOICE_STOP_PHRASES` | moved-lazy | `tools.voice_mode_transcript` | +| `MIN_FRAGMENT_LENGTH_FOR_ECHO` | moved-lazy | `tools.voice_mode_transcript` | +| `WHISPER_HALLUCINATIONS` | moved-lazy | `tools.voice_mode_transcript` | +| `difflib` | import | `difflib` | +| `is_tts_echo` | moved-lazy | `tools.voice_mode_transcript` | +| `re` | import | `re` | +| `voice_stop_hint` | moved-lazy | `tools.voice_mode_transcript` | + +### `tools.web_result_cache` + +| name | kind | new location | +|---|---|---| +| `Any` | import | `typing` | +| `List` | import | `typing` | + +### `tools.web_tools` + +| name | kind | new location | +|---|---|---| +| `DEFAULT_EXTRACT_CHAR_LIMIT` | moved-lazy | `tools.web_tools_truncate` | +| `Dict` | import | `typing` | +| `Firecrawl` | moved-lazy | `plugins.web.firecrawl.provider` | +| `MAX_STORED_TEXT_CHARS` | moved-lazy | `tools.web_tools_truncate` | +| `TYPE_CHECKING` | import | `typing` | +| `asyncio` | import | `asyncio` | +| `build_vendor_gateway_url` | moved-lazy | `tools.managed_tool_gateway` | +| `httpx` | import | `httpx` | +| `managed_nous_tools_enabled` | moved-lazy | `tools.tool_backend_helpers` | +| `normalize_url_for_request` | moved-lazy | `tools.url_safety` | +| `nous_tool_gateway_unavailable_message` | moved-lazy | `tools.tool_backend_helpers` | +| `prefers_gateway` | moved-lazy | `tools.tool_backend_helpers` | +| `re` | import | `re` | +| `resolve_managed_tool_gateway` | moved-lazy | `tools.managed_tool_gateway` | +| `sensitive_query_param_name` | moved-lazy | `tools.url_safety` | +| `sys` | import | `sys` | + +### `tools.website_policy` + +| name | kind | new location | +|---|---|---| +| `invalidate_cache` | restored-def | `(deleted; BASE body restored)` | + +### `tools.write_approval` + +| name | kind | new location | +|---|---|---| +| `is_background` | restored-def | `(deleted; BASE body restored)` | + +### `tools.yuanbao_tools` + +| name | kind | new location | +|---|---|---| +| `List` | import | `typing` | +| `Optional` | import | `typing` | + +### `toolsets` + +| name | kind | new location | +|---|---|---| +| `resolve_multiple_toolsets` | restored-def | `(deleted; BASE body restored)` | + +### `tui_gateway.compute_host` + +| name | kind | new location | +|---|---|---| +| `HostSession` | restored-def | `(deleted; BASE body restored)` | +| `SpikeAgent` | restored-helper | `(deleted; restored as a dependency of HostSession)` | +| `SpikeAgent` | restored-def | `(deleted; BASE body restored)` | +| `dataclass` | restored-import | `dataclasses` | +| `dataclass` | import | `dataclasses` | +| `field` | restored-import | `dataclasses` | +| `field` | import | `dataclasses` | +| `request_hard_interrupt` | moved-lazy | `agent.interrupt_compat` | + +### `tui_gateway.hosted_room_driver` + +| name | kind | new location | +|---|---|---| +| `contextlib` | import | `contextlib` | +| `null_turn_lock` | restored-def | `(deleted; BASE body restored)` | + +### `tui_gateway.hosted_room_service` + +| name | kind | new location | +|---|---|---| +| `hashlib` | import | `hashlib` | + +### `tui_gateway.mcp_rpc_helpers` + +| name | kind | new location | +|---|---|---| +| `Optional` | import | `typing` | +| `Tuple` | import | `typing` | +| `resolve_profile` | restored-def | `(deleted; BASE body restored)` | + +### `tui_gateway.methods_browser_control` + +| name | kind | new location | +|---|---|---| +| `BROWSER_CONTROL_PROTOCOL_VERSION` | moved-lazy | `gateway.browser_control_broker` | +| `browser_control_protocol_supported` | moved-lazy | `gateway.browser_control_broker` | +| `filter_browser_control_capabilities` | moved-lazy | `gateway.browser_control_broker` | + +### `tui_gateway.methods_prompt` + +| name | kind | new location | +|---|---|---| +| `types` | import | `types` | diff --git a/acp_adapter/auth.py b/acp_adapter/auth.py index a1ad9982d4..a56a76179b 100644 --- a/acp_adapter/auth.py +++ b/acp_adapter/auth.py @@ -49,3 +49,14 @@ def build_auth_methods() -> list[Any]: "Use this when Hermes has not been configured on this machine yet."), )) return methods + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def has_provider() -> bool: + """Return True if Hermes can resolve any runtime provider credentials.""" + return detect_provider() is not None +# ---- END PLUGIN-COMPAT ---- diff --git a/acp_adapter/edit_approval.py b/acp_adapter/edit_approval.py index 5c38efc9e0..0e78915848 100644 --- a/acp_adapter/edit_approval.py +++ b/acp_adapter/edit_approval.py @@ -226,3 +226,19 @@ def make_acp_edit_approval_requester( return getattr(outcome, "outcome", None) == "selected" and getattr(outcome, "option_id", None) == "allow_once" return _requester + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from concurrent.futures import TimeoutError as FutureTimeout # noqa: F401,E402 + +def clear_edit_approval_requester() -> None: + """Clear the current requester; primarily used by tests.""" + + _EDIT_APPROVAL_REQUESTER.set(None) + +def get_edit_approval_requester() -> EditApprovalRequester | None: + return _EDIT_APPROVAL_REQUESTER.get() +# ---- END PLUGIN-COMPAT ---- diff --git a/acp_adapter/events.py b/acp_adapter/events.py index 020a0b1b40..88d1b351ac 100644 --- a/acp_adapter/events.py +++ b/acp_adapter/events.py @@ -174,3 +174,11 @@ def make_step_cb( tool_call_ids.pop(tool_name, None) return _step + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/acp_adapter/server.py b/acp_adapter/server.py index 083b1c45bc..c8dceaf94f 100644 --- a/acp_adapter/server.py +++ b/acp_adapter/server.py @@ -972,3 +972,38 @@ class HermesACPAgent(SlashCommandsMixin, acp.Agent): self.session_manager.save_session(session_id) logger.info("Session %s: config option %s updated", session_id, config_id) return SetSessionConfigOptionResponse(config_options=[]) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from acp.schema import AgentThoughtChunk # noqa: F401,E402 +from acp.schema import AudioContentBlock # noqa: F401,E402 +from acp.schema import AvailableCommand # noqa: F401,E402 +from acp.schema import AvailableCommandsUpdate # noqa: F401,E402 +from acp.schema import BlobResourceContents # noqa: F401,E402 +from acp.schema import EmbeddedResourceContentBlock # noqa: F401,E402 +from acp.schema import ImageContentBlock # noqa: F401,E402 +from pathlib import Path # noqa: F401,E402 +from acp.schema import ResourceContentBlock # noqa: F401,E402 +from acp.schema import TextResourceContents # noqa: F401,E402 +from acp.schema import UnstructuredCommandInput # noqa: F401,E402 +import base64 # noqa: F401,E402 +import json # noqa: F401,E402 +from urllib.parse import unquote # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'ACP_MAX_MODELS_PER_PROVIDER': ('acp_adapter.model_catalog', 'ACP_MAX_MODELS_PER_PROVIDER'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/acp_adapter/session.py b/acp_adapter/session.py index de52eeb628..4f28742011 100644 --- a/acp_adapter/session.py +++ b/acp_adapter/session.py @@ -422,3 +422,11 @@ class SessionManager: # ACP stdio: stdout is protocol-only JSON-RPC; agent chatter goes to stderr. agent._print_fn = _acp_stderr_print return agent + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from threading import Lock # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/agent_init.py b/agent/agent_init.py index cb393b21cc..5039763e36 100644 --- a/agent/agent_init.py +++ b/agent/agent_init.py @@ -2304,3 +2304,23 @@ def init_agent( __all__ = ["init_agent"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ToolGuardrailDecision': ('agent.tool_guardrails', 'ToolGuardrailDecision'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index 613a9b03e7..1d7e287c30 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -3210,3 +3210,30 @@ __all__ = [ "extract_api_error_context", "apply_pending_steer_to_tool_results", "_iter_pool_sockets", "force_close_tcp_sockets", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def agent_runtime_owns_post_tool_hook(agent: Any, function_name: str) -> bool: + """Return True when an agent-level tool path emits its own post hook.""" + if function_name in AGENT_RUNTIME_POST_HOOK_TOOL_NAMES: + return True + if getattr(agent, "_context_engine_tool_names", None) and function_name in agent._context_engine_tool_names: + return True + memory_manager = getattr(agent, "_memory_manager", None) + return bool(memory_manager and memory_manager.has_tool(function_name)) + +def intent_ack_continuation_enabled(agent) -> bool: + """Whether intent-ack continuation should fire at all for this turn. + + The ``codex_ack_continuations < 2`` per-turn cap and the + ``looks_like_codex_intermediate_ack`` detector are applied by the caller; + this only decides the on/off gate. Callers that also need to know whether + the workspace requirement applies should use ``intent_ack_continuation_mode`` + directly (``"codex_only"`` ⇒ require_workspace=True, ``"all"`` ⇒ False). + """ + return intent_ack_continuation_mode(agent) != "off" +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index 4db2e2ea9b..9bfd6952c7 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -673,3 +673,45 @@ def create_anthropic_message( "%sAnthropic Messages stream unavailable; falling back to messages.create(): %s", log_prefix, exc ) return messages_api.create(**{k: v for k, v in api_kwargs.items() if k != "stream"}) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +import copy # noqa: F401,E402 +import json # noqa: F401,E402 +import os # noqa: F401,E402 +import platform # noqa: F401,E402 +import secrets # noqa: F401,E402 +import stat # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'CredentialPersistError': ('agent.anthropic_credentials', 'CredentialPersistError'), + 'base_url_host_matches': ('utils', 'base_url_host_matches'), + 'base_url_hostname': ('utils', 'base_url_hostname'), + 'claude_code_credentials_path': ('agent.anthropic_credentials', 'claude_code_credentials_path'), + 'get_hermes_home': ('hermes_constants', 'get_hermes_home'), + 'is_claude_code_token_valid': ('agent.anthropic_credentials', 'is_claude_code_token_valid'), + 'is_rotation_consumed_uncommitted': ('agent.anthropic_credentials', 'is_rotation_consumed_uncommitted'), + 'mark_rotation_consumed_uncommitted': ('agent.anthropic_credentials', 'mark_rotation_consumed_uncommitted'), + 'read_claude_code_credentials': ('agent.anthropic_credentials', 'read_claude_code_credentials'), + 'read_hermes_oauth_credentials': ('agent.anthropic_credentials', 'read_hermes_oauth_credentials'), + 'refresh_anthropic_oauth_pure': ('agent.anthropic_credentials', 'refresh_anthropic_oauth_pure'), + 'resolve_anthropic_token': ('agent.anthropic_credentials', 'resolve_anthropic_token'), + 'run_hermes_oauth_login_pure': ('agent.anthropic_credentials', 'run_hermes_oauth_login_pure'), + 'run_oauth_setup_token': ('agent.anthropic_credentials', 'run_oauth_setup_token'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/aux_accounting.py b/agent/aux_accounting.py index 052c026afc..81d648128a 100644 --- a/agent/aux_accounting.py +++ b/agent/aux_accounting.py @@ -89,3 +89,14 @@ def record_aux_usage( ) except Exception: logger.debug("Aux usage recording failed (non-fatal)", exc_info=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_accounting_context() -> Optional[tuple]: + """Return ``(session_db, session_id)`` for the active turn, or ``None``.""" + return _accounting.get() +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 4172e4f515..b62809ab4c 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -7319,3 +7319,32 @@ async def _async_call_llm_impl( if result is _RERAISE_ORIGINAL: raise return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import copy # noqa: F401,E402 + +NOUS_EXTRA_BODY = _nous_extra_body() + +def get_async_text_auxiliary_client(task: str = "", *, main_runtime: Optional[Dict[str, Any]] = None): + """Return (async_client, model_slug) for async consumers. + + For standard providers returns (AsyncOpenAI, model). For Codex returns + (AsyncCodexAuxiliaryClient, model) which wraps the Responses API. + Returns (None, None) when no provider is available. + """ + provider, model, base_url, api_key, api_mode = _resolve_task_provider_model(task or None) + return resolve_provider_client( + provider, + model=model, + async_mode=True, + explicit_base_url=base_url, + explicit_api_key=api_key, + api_mode=api_mode, + main_runtime=main_runtime, + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/backend_identity.py b/agent/backend_identity.py index 5ba5556068..ecf9f2f66b 100644 --- a/agent/backend_identity.py +++ b/agent/backend_identity.py @@ -128,3 +128,24 @@ def should_skip_candidate( True when it is the same backend as ``failed`` along the axis ``scope`` invalidated. Every fallback/dedup/skip site must call this.""" return _SCOPE_PREDICATES.get(scope, same_deployment)(candidate, failed) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +_REASON_SCOPES = { + "auth error": FailureScope.CREDENTIAL, + "payment error": FailureScope.CREDENTIAL, + "rate limit": FailureScope.MODEL, + "model incompatible with route": FailureScope.MODEL, + "invalid provider response": FailureScope.MODEL, + "connection error": FailureScope.MODEL, + "timeout": FailureScope.MODEL, +} + +def classify_failure_scope(reason: Optional[str]) -> FailureScope: + """Map a human-readable failure reason to the identity axis it kills.""" + return _REASON_SCOPES.get((reason or "").strip().lower(), FailureScope.MODEL) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/background_review.py b/agent/background_review.py index 2daf9ced40..6342c17579 100644 --- a/agent/background_review.py +++ b/agent/background_review.py @@ -1120,3 +1120,39 @@ __all__ = [ "_MEMORY_REVIEW_PROMPT", "_SKILL_REVIEW_PROMPT", "_COMBINED_REVIEW_PROMPT", "load_background_review_settings", "spawn_background_review_thread", "summarize_background_review_actions", "build_memory_write_metadata", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 + +def is_background_review_enabled( + task_cfg: Optional[Dict[str, Any]] = None, +) -> bool: + """Return whether automatic post-turn background review may spawn. + + Controlled by ``auxiliary.background_review.enabled`` (default ``true``). + Explicit ``/refine`` (``focus`` set) bypasses this gate — same contract as + zeroing the nudge intervals, which stops automatic forks but leaves manual + refine working (issue #87250). + + Prefer :func:`load_background_review_settings` at the spawn call site so + the task block is not re-read on the same turn. + """ + if task_cfg is not None: + try: + from utils import is_truthy_value + + return is_truthy_value(task_cfg.get("enabled"), default=True) + except Exception: + logger.warning( + "Failed to interpret background_review.enabled; leaving " + "automatic review enabled (fail-open)", + exc_info=True, + ) + return True + enabled, _ = load_background_review_settings() + return enabled +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/bedrock_adapter.py b/agent/bedrock_adapter.py index f02ecffe30..9c19a8a789 100644 --- a/agent/bedrock_adapter.py +++ b/agent/bedrock_adapter.py @@ -998,3 +998,109 @@ def get_bedrock_context_length(model_id: str, region: str = "", probe: bool = Tr return probed matches = [key for key in BEDROCK_CONTEXT_LENGTHS if key in model_id.lower()] return BEDROCK_CONTEXT_LENGTHS[max(matches, key=len)] if matches else BEDROCK_DEFAULT_CONTEXT_LENGTH + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +CONTEXT_OVERFLOW_PATTERNS = [ + re.compile(r"ValidationException.*(?:input is too long|max input token|input token.*exceed)", re.IGNORECASE), + re.compile(r"ValidationException.*(?:exceeds? the (?:maximum|max) (?:number of )?(?:input )?tokens)", re.IGNORECASE), + re.compile(r"ModelStreamErrorException.*(?:Input is too long|too many input tokens)", re.IGNORECASE), +] + +OVERLOAD_PATTERNS = [ + re.compile(r"ModelNotReadyException", re.IGNORECASE), + re.compile(r"ModelTimeoutException", re.IGNORECASE), + re.compile(r"InternalServerException", re.IGNORECASE), +] + +THROTTLE_PATTERNS = [ + re.compile(r"ThrottlingException", re.IGNORECASE), + re.compile(r"Too many concurrent requests", re.IGNORECASE), + re.compile(r"ServiceQuotaExceededException", re.IGNORECASE), +] + +def call_converse_stream( + region: str, + model: str, + messages: List[Dict], + tools: Optional[List[Dict]] = None, + max_tokens: Optional[int] = 4096, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + stop_sequences: Optional[List[str]] = None, + guardrail_config: Optional[Dict] = None, +) -> SimpleNamespace: + """Call Bedrock ConverseStream API and return an OpenAI-compatible response. + + Consumes the full stream and returns the assembled response. For true + streaming with delta callbacks, use ``iter_converse_stream()`` instead. + """ + client = _get_bedrock_runtime_client(region) + kwargs = build_converse_kwargs( + model=model, + messages=messages, + tools=tools, + max_tokens=max_tokens, + temperature=temperature, + top_p=top_p, + stop_sequences=stop_sequences, + guardrail_config=guardrail_config, + ) + + try: + response = client.converse_stream(**kwargs) + except Exception as exc: + retry_kwargs = recover_from_cache_point_rejection(exc, kwargs) + if retry_kwargs is not None: + return normalize_converse_stream_events( + client.converse_stream(**retry_kwargs) + ) + if is_streaming_access_denied_error(exc): + # IAM allows bedrock:InvokeModel but not + # InvokeModelWithResponseStream — permanent for this session. + # Fall back to the non-streaming converse() path. + logger.info( + "bedrock: converse_stream denied by IAM on (region=%s, model=%s) — " + "falling back to non-streaming converse().", + region, model, + ) + return normalize_converse_response(client.converse(**kwargs)) + if is_stale_connection_error(exc): + logger.warning( + "bedrock: stale-connection error on converse_stream(region=%s, " + "model=%s): %s — evicting cached client so the next call reconnects.", + region, model, type(exc).__name__, + ) + invalidate_runtime_client(region) + raise + return normalize_converse_stream_events(response) + +def is_context_overflow_error(error_message: str) -> bool: + """Return True if the error indicates the input context was too large. + + When this returns True, the agent should compress context and retry + rather than treating it as a fatal error. + """ + return any(p.search(error_message) for p in CONTEXT_OVERFLOW_PATTERNS) + +def classify_bedrock_error(error_message: str) -> str: + """Classify a Bedrock error for retry/failover decisions. + + Returns: + - ``"context_overflow"`` — input too long, compress and retry + - ``"rate_limit"`` — throttled, backoff and retry + - ``"overloaded"`` — model temporarily unavailable, retry with delay + - ``"unknown"`` — unclassified error + """ + if is_context_overflow_error(error_message): + return "context_overflow" + if any(p.search(error_message) for p in THROTTLE_PATTERNS): + return "rate_limit" + if any(p.search(error_message) for p in OVERLOAD_PATTERNS): + return "overloaded" + return "unknown" +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/bounded_response.py b/agent/bounded_response.py index d0d5ac8ed8..8114b8ff7c 100644 --- a/agent/bounded_response.py +++ b/agent/bounded_response.py @@ -77,3 +77,26 @@ def read_streaming_error_body( "bounded error-body read: capped at %d bytes (max=%d)", sum(len(c) for c in chunks), max_bytes, ) return b"".join(chunks).decode("utf-8", errors="replace") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 + +def read_error_body_or_default( + response: httpx.Response, + *, + max_bytes: int = DEFAULT_ERROR_BODY_MAX_BYTES, + timeout_s: float = DEFAULT_ERROR_BODY_TIMEOUT_S, +) -> Optional[str]: + """Like ``read_streaming_error_body`` but returns ``None`` on empty body. + + Convenience for callers that distinguish "no body" from "empty string". + """ + text = read_streaming_error_body( + response, max_bytes=max_bytes, timeout_s=timeout_s + ) + return text or None +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/browser_provider.py b/agent/browser_provider.py index cf9989bcd3..62180f2c23 100644 --- a/agent/browser_provider.py +++ b/agent/browser_provider.py @@ -55,3 +55,12 @@ class BrowserProvider(ProviderBase): def emergency_cleanup(self, session_id: str) -> None: """Best-effort teardown from atexit / signal handlers. Must tolerate missing credentials and network errors; must not raise.""" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/browser_registry.py b/agent/browser_registry.py index cde417c5f0..51ae1117d0 100644 --- a/agent/browser_registry.py +++ b/agent/browser_registry.py @@ -59,3 +59,26 @@ def _resolve(configured: Optional[str]) -> Optional[BrowserProvider]: ): return provider return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/codex_runtime.py b/agent/codex_runtime.py index 12460c2132..e5b2820458 100644 --- a/agent/codex_runtime.py +++ b/agent/codex_runtime.py @@ -969,3 +969,21 @@ __all__ = [ "run_codex_app_server_turn", "run_codex_stream", "_consume_codex_event_stream", "make_codex_app_server_event_bridge", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def run_codex_create_stream_fallback(agent, api_kwargs: dict, client: Any = None): + """Backward-compatible alias for the unified event-driven path. + + Historically this was the fallback when the SDK's high-level + ``responses.stream(...)`` helper raised on shape drift. The primary + path now does exactly what the fallback did, so this just forwards. + Kept as a public symbol because tests and a small number of call sites + still reference it by name. + """ + return run_codex_stream(agent, api_kwargs, client=client) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/coding_context.py b/agent/coding_context.py index dfdf546a4e..5a6b3be08d 100644 --- a/agent/coding_context.py +++ b/agent/coding_context.py @@ -554,3 +554,34 @@ def build_coding_workspace_block(cwd: Optional[str | Path] = None) -> str: if f.context_files: lines.append(f"- Context files: {', '.join(f.context_files)}") return "\n".join(lines) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def coding_system_blocks( + *, + platform: Optional[str] = None, + cwd: Optional[str | Path] = None, + config: Optional[dict[str, Any]] = None, + model: Optional[str] = None, +) -> list[str]: + """Stable system-prompt blocks for the current posture (empty when general). + + ``model`` steers the brief's edit-format nudge toward the model's family. + """ + return resolve_runtime_mode( + platform=platform, cwd=cwd, config=config, model=model + ).system_blocks() + +_PROFILES: dict[str, ContextProfile] = { + GENERAL_PROFILE.name: GENERAL_PROFILE, + CODING_PROFILE.name: CODING_PROFILE, +} + +def get_profile(name: str) -> ContextProfile: + """Return a registered profile, falling back to ``general``.""" + return _PROFILES.get(name, GENERAL_PROFILE) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/context_compressor.py b/agent/context_compressor.py index d53770436b..a151697c0b 100644 --- a/agent/context_compressor.py +++ b/agent/context_compressor.py @@ -4894,3 +4894,23 @@ def reference_handoff_would_drive_next_model_call(messages: Optional[List[Dict[s def is_user_originated_turn(message: Any) -> bool: """True for human-authored user turns (not compaction scaffolding); dispatchers must use this, not a bare role check.""" return user_originated_turn_view(message) is not None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'tool_result_id_variants': ('agent.message_sanitization', 'tool_result_id_variants'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/conversation_compression.py b/agent/conversation_compression.py index e691015e72..18465dc71f 100644 --- a/agent/conversation_compression.py +++ b/agent/conversation_compression.py @@ -4013,3 +4013,13 @@ __all__ = [ "check_compression_model_feasibility", "replay_compression_warning", "compress_context", "try_shrink_image_parts_in_messages", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +class CompressionExecutorSaturatedError(RuntimeError): + """All compression pool slots are occupied; submission was refused.""" +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index f855c28fc5..a7776cd5c7 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -1536,3 +1536,63 @@ def run_conversation( __all__ = ["run_conversation"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +import random # noqa: F401,E402 +import ssl # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE': ('agent.conversation_compression', 'COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE'), + 'COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE': ('agent.conversation_compression', 'COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE'), + 'COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE': ('agent.conversation_compression', 'COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE'), + 'COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE': ('agent.conversation_compression', 'COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE'), + 'FailoverReason': ('agent.error_classifier', 'FailoverReason'), + 'KawaiiSpinner': ('agent.display', 'KawaiiSpinner'), + 'PARTIAL_STREAM_STUB_ID': ('hermes_constants', 'PARTIAL_STREAM_STUB_ID'), + 'PRE_API_COMPRESSION_STATUS_TEMPLATE': ('agent.conversation_compression', 'PRE_API_COMPRESSION_STATUS_TEMPLATE'), + 'adaptive_rate_limit_backoff': ('agent.retry_utils', 'adaptive_rate_limit_backoff'), + 'anchored_context_tokens': ('agent.model_metadata', 'anchored_context_tokens'), + 'automatic_compaction_status_message': ('agent.context_engine', 'automatic_compaction_status_message'), + 'capture_usage_anchor': ('agent.model_metadata', 'capture_usage_anchor'), + 'classify_api_error': ('agent.error_classifier', 'classify_api_error'), + 'close_interrupted_tool_sequence': ('agent.message_sanitization', 'close_interrupted_tool_sequence'), + 'coalesce_tool_call_id': ('agent.message_sanitization', 'coalesce_tool_call_id'), + 'compose_user_api_content': ('agent.turn_context', 'compose_user_api_content'), + 'compression_blocked_transiently': ('agent.conversation_compression', 'compression_blocked_transiently'), + 'compression_skipped_due_to_lock': ('agent.conversation_compression', 'compression_skipped_due_to_lock'), + 'context_compression_timed_out': ('agent.conversation_compression', 'context_compression_timed_out'), + 'conversation_history_after_compression': ('agent.conversation_compression', 'conversation_history_after_compression'), + 'env_var_enabled': ('utils', 'env_var_enabled'), + 'estimate_messages_tokens_rough': ('agent.model_metadata', 'estimate_messages_tokens_rough'), + 'estimate_request_tokens_rough': ('agent.model_metadata', 'estimate_request_tokens_rough'), + 'estimate_usage_cost': ('agent.usage_pricing', 'estimate_usage_cost'), + 'get_context_length_from_provider_error': ('agent.model_metadata', 'get_context_length_from_provider_error'), + 'has_incomplete_scratchpad': ('agent.trajectory', 'has_incomplete_scratchpad'), + 'is_output_cap_error': ('agent.model_metadata', 'is_output_cap_error'), + 'is_repetition_dominated': ('agent.repetition_guard', 'is_repetition_dominated'), + 'is_zai_coding_overload_error': ('agent.retry_utils', 'is_zai_coding_overload_error'), + 'jittered_backoff': ('agent.retry_utils', 'jittered_backoff'), + 'normalize_usage': ('agent.usage_pricing', 'normalize_usage'), + 'parse_available_output_tokens_from_error': ('agent.model_metadata', 'parse_available_output_tokens_from_error'), + 'reanchor_current_turn_user_idx': ('agent.turn_context', 'reanchor_current_turn_user_idx'), + 'save_context_length': ('agent.model_metadata', 'save_context_length'), + 'serialized_messages_bytes': ('agent.message_sanitization', 'serialized_messages_bytes'), + 'splice_provider_projection': ('agent.provider_projection', 'splice_provider_projection'), + 'zai_coding_overload_retry_ceiling': ('agent.retry_utils', 'zai_coding_overload_retry_ceiling'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/credential_sources.py b/agent/credential_sources.py index 0d728c4813..1dc2947700 100644 --- a/agent/credential_sources.py +++ b/agent/credential_sources.py @@ -259,3 +259,23 @@ _REGISTRY: List[RemovalStep] = [ description="Custom provider config.yaml api_key field", ), ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'register': ('tools.slash_confirm', 'register'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/display.py b/agent/display.py index 598553bfae..26aff9c146 100644 --- a/agent/display.py +++ b/agent/display.py @@ -1083,3 +1083,14 @@ def get_cute_tool_message(tool_name: str, args: dict, duration: float, result: s safe_name = tool_name[:9] if isinstance(tool_name, str) and tool_name else "tool" safe_duration = f"{duration:.1f}s" if isinstance(duration, (int, float)) else "done" return f"┊ ⚡ {safe_name:9} completed {safe_duration}" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_friendly_tool_labels() -> bool: + """Return whether friendly tool labels are enabled.""" + return _friendly_tool_labels +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/error_surface.py b/agent/error_surface.py index 089c649ced..77e7cd6393 100644 --- a/agent/error_surface.py +++ b/agent/error_surface.py @@ -155,3 +155,12 @@ def build_error_surface_from_exception(exc: BaseException, provider: str = "", m except Exception: # pragma: no cover — never break the error path logger.debug("error_surface: exception classification failed", exc_info=True) return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +LAYER_RUNTIME = "runtime" +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/estop.py b/agent/estop.py index 86c5d585ee..b7cc51cc5e 100644 --- a/agent/estop.py +++ b/agent/estop.py @@ -136,3 +136,11 @@ def check_paused(component: str, logger: logging.Logger) -> bool: component, suffix, sentinel_path(), ) return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/file_safety.py b/agent/file_safety.py index d3a981d090..937a73cc09 100644 --- a/agent/file_safety.py +++ b/agent/file_safety.py @@ -358,3 +358,86 @@ def get_container_mirror_warning(path: str, mirror_prefix: str | None = None) -> "the real HERMES_HOME.", "after explicit user direction, retry", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +PROFILE_SCOPED_AREAS = ("skills", "plugins", "cron", "memories") + +def classify_cross_profile_target(path: str) -> Optional[dict]: + """Classify a write target as cross-profile if it lands in another + profile's scoped area (skills/plugins/cron/memories). + + Returns ``None`` when the target is outside Hermes scope, or is inside + the ACTIVE profile, or doesn't hit a profile-scoped area. Otherwise + returns a dict with: + + * ``active_profile``: name of the profile the agent is running as + * ``target_profile``: name of the profile the path belongs to + * ``area``: which scoped area (``"skills"``, ``"plugins"``, etc.) + * ``target_path``: the resolved path string + + The caller decides what to do with the result — surface a warning to + the model, prompt the user, or (with explicit consent / + ``cross_profile=True``) proceed anyway. + """ + try: + target = Path(os.path.expanduser(str(path))).resolve() + root_real = _hermes_root_path().resolve() + except (OSError, RuntimeError): + return None + + target_profile: Optional[str] = None + area: Optional[str] = None + + try: + rel = target.relative_to(root_real) + except ValueError: + return None + + parts = rel.parts + if not parts: + return None + + if parts[0] in PROFILE_SCOPED_AREAS: + # ``//...`` → default profile. + target_profile = "default" + area = parts[0] + elif ( + parts[0] == "profiles" + and len(parts) >= 3 + and parts[2] in PROFILE_SCOPED_AREAS + ): + # ``/profiles///...`` → named profile. + target_profile = parts[1] + area = parts[2] + else: + return None + + active_profile = _resolve_active_profile_name() + if target_profile == active_profile: + # In-profile write — not a cross-profile event. + return None + + return { + "active_profile": active_profile, + "target_profile": target_profile, + "area": area, + "target_path": str(target), + } + +def get_cross_profile_warning(path: str) -> Optional[str]: + """RETIRED (maintainer decision): always returns ``None``. + + The cross-profile write guard was removed — profiles were never + isolated (same OS user; the terminal tool writes anywhere), so the + block was ceremony that cost every schema real tokens and taught a + bypass arg. The system prompt's active-profile hint remains the only + steering; the classifier below survives for that hint and for + diagnostics. Kept as a stub so external callers/plugins fail soft. + """ + return None +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/image_gen_provider.py b/agent/image_gen_provider.py index 250c825ad0..4b49e899c0 100644 --- a/agent/image_gen_provider.py +++ b/agent/image_gen_provider.py @@ -129,3 +129,13 @@ def error_response( "success": False, "image": None, "error": error, "error_type": error_type, "model": model, "prompt": prompt, "aspect_ratio": aspect_ratio, "provider": provider, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import base64 # noqa: F401,E402 +import datetime # noqa: F401,E402 +import uuid # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/image_gen_registry.py b/agent/image_gen_registry.py index 7c6d2b3365..87d66c5c60 100644 --- a/agent/image_gen_registry.py +++ b/agent/image_gen_registry.py @@ -45,3 +45,26 @@ def get_active_provider() -> Optional[ImageGenProvider]: return available[0] fal = snapshot.get("fal") return fal if fal is not None and _available(fal) else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/learning_graph_render.py b/agent/learning_graph_render.py index dd3fe3eaba..8200680c8d 100644 --- a/agent/learning_graph_render.py +++ b/agent/learning_graph_render.py @@ -417,3 +417,25 @@ def render_frames(payload: dict[str, Any], *, cols: int = 80, rows: int = 16, fr "buckets": _bucket_rows(buckets, payload), "summary": build_summary(payload), "axis": axis_labels(payload), "count": len(payload.get("nodes", [])), "cols": cols, "rows": rows, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +Grid = list # list[Row] + + +_PLUGIN_COMPAT_LAZY = { + 'Run': ('hermes_cli.kanban_db', 'Run'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/lsp/eventlog.py b/agent/lsp/eventlog.py index d963c5f948..69d0b9f22a 100644 --- a/agent/lsp/eventlog.py +++ b/agent/lsp/eventlog.py @@ -128,3 +128,29 @@ __all__ = [ "log_server_unavailable", "log_timeout", "log_server_error", "log_spawn_failed", "log_reaped", "reset_announce_caches", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +_announced_no_server: set = set() # keys: (server_id,) + +def _announce_once(bucket: set, key: Tuple) -> bool: + """Return True if *key* has not been announced for *bucket* yet. + + Atomically marks the key as announced so concurrent callers + cannot both win the race and double-log. + """ + with _announce_lock: + if key in bucket: + return False + bucket.add(key) + return True + +def log_no_server_configured(server_id: str) -> None: + """No spawn recipe for this language. WARNING once.""" + if _announce_once(_announced_no_server, (server_id,)): + _emit(server_id, logging.WARNING, "no server configured") +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/lsp/protocol.py b/agent/lsp/protocol.py index 9596e26b82..dc2d7380a0 100644 --- a/agent/lsp/protocol.py +++ b/agent/lsp/protocol.py @@ -136,3 +136,12 @@ __all__ = [ "encode_message", "read_message", "make_request", "make_notification", "make_response", "make_error_response", "classify_message", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +ERROR_REQUEST_CANCELLED = -32800 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/monitoring/cron_health.py b/agent/monitoring/cron_health.py index ecd9e739fd..8d87e0c6f5 100644 --- a/agent/monitoring/cron_health.py +++ b/agent/monitoring/cron_health.py @@ -168,3 +168,24 @@ __all__ = [ "CronHealthSnapshot", "build_cron_health_snapshot", "classify_cron_error", "emit_execution_state", "project_execution_event", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import hashlib # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'GatewayHealthSnapshot': ('agent.monitoring.gateway_health', 'GatewayHealthSnapshot'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/monitoring/emitter.py b/agent/monitoring/emitter.py index d3cf44a27d..326e83570a 100644 --- a/agent/monitoring/emitter.py +++ b/agent/monitoring/emitter.py @@ -165,3 +165,12 @@ def reset_emitter_for_tests(emitter: Optional[MonitoringEmitter] = None) -> None __all__ = ["MonitoringEmitter", "get_emitter", "emit", "reset_emitter_for_tests"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +TelemetryEmitter = MonitoringEmitter +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/monitoring/gateway_health.py b/agent/monitoring/gateway_health.py index 6e6e016dc1..bd042e0b9a 100644 --- a/agent/monitoring/gateway_health.py +++ b/agent/monitoring/gateway_health.py @@ -329,3 +329,24 @@ __all__ = [ "GatewayMetric", "GatewayHealthSnapshot", "GatewayDiagnosticLogHandler", "build_gateway_health_snapshot", "classify_gateway_error", "source_logger_for_export", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def redact_gateway_message(message: Any) -> str: + """Redact gateway diagnostic free text for operator-owned export. + + Single scrub path: everything goes through + ``agent.monitoring.redaction.redact_for_export`` (unconditional + secrets + PII), then is length-bounded. + """ + try: + from agent.monitoring.redaction import redact_for_export + redacted = redact_for_export(str(message or "")) or "" + except Exception: + redacted = "[redaction-unavailable]" + return redacted[:500] +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/monitoring/gateway_health_export.py b/agent/monitoring/gateway_health_export.py index e0f7a3cdf5..338d24b294 100644 --- a/agent/monitoring/gateway_health_export.py +++ b/agent/monitoring/gateway_health_export.py @@ -334,3 +334,11 @@ def start_gateway_health_export(config: Dict[str, Any]) -> GatewayHealthExportRu __all__ = ["GatewayHealthExportRuntime", "start_gateway_health_export"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import re # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/nous_rate_guard.py b/agent/nous_rate_guard.py index 3b87c8677a..3747dcb9ae 100644 --- a/agent/nous_rate_guard.py +++ b/agent/nous_rate_guard.py @@ -162,3 +162,24 @@ def _has_exhausted_bucket_in_object(state: Any) -> bool: if _is_exhausted(remaining, reset): return True return False + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import tempfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_replace': ('utils', 'atomic_replace'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/outbound_webhooks.py b/agent/outbound_webhooks.py index 14cb117e8e..2a637bae24 100644 --- a/agent/outbound_webhooks.py +++ b/agent/outbound_webhooks.py @@ -335,3 +335,13 @@ def _deliver(delivery: Dict[str, Any]) -> None: "outbound webhook delivery failed after %d attempt(s) (event=%s target=%s): %s", MAX_DELIVERY_ATTEMPTS, event, label, last_error, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/pet/generate/atlas.py b/agent/pet/generate/atlas.py index 0e456b65f9..f94ee5f399 100644 --- a/agent/pet/generate/atlas.py +++ b/agent/pet/generate/atlas.py @@ -619,3 +619,24 @@ def _check_atlas_cells(atlas) -> tuple[list[str], list[str], list[str]]: if residue: errors.append(f"{residue} transparent pixels retain RGB residue") return errors, warnings, filled_states + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import io # noqa: F401,E402 +import io # noqa: F401,E402 + +COLUMNS = max(count for _, _, count in ROW_SPECS) + +FRAME_COUNTS: dict[str, int] = {state: count for state, _, count in ROW_SPECS} + +ROWS = len(ROW_SPECS) + +def atlas_to_webp_bytes(atlas) -> bytes: + """Encode an atlas image to lossless WebP bytes (the on-disk pet format).""" + buf = io.BytesIO() + atlas.save(buf, format="WEBP", lossless=True, quality=100, method=6, exact=True) + return buf.getvalue() +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/prompt_builder.py b/agent/prompt_builder.py index 340547f973..a328c652e8 100644 --- a/agent/prompt_builder.py +++ b/agent/prompt_builder.py @@ -1586,3 +1586,24 @@ def build_context_files_prompt( return "" return ("# Project Context\n\nThe following project context files have been loaded and should be followed:\n\n" + "\n".join(sections)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'org_id_of_path': ('agent.skill_utils', 'org_id_of_path'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/prompt_cache_boundary.py b/agent/prompt_cache_boundary.py index 6d0fa76f54..dde1f0750f 100644 --- a/agent/prompt_cache_boundary.py +++ b/agent/prompt_cache_boundary.py @@ -56,3 +56,15 @@ def find_stable_prefix(content: str) -> Optional[str]: if best is not None: _prefixes.move_to_end(best) # after the scan: never mutate mid-iteration return best + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def clear_stable_prefixes() -> None: + """Test isolation helper.""" + with _lock: + _prefixes.clear() +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/proxy_sources/iron_proxy.py b/agent/proxy_sources/iron_proxy.py index f7c76df9d4..5807ef76e8 100644 --- a/agent/proxy_sources/iron_proxy.py +++ b/agent/proxy_sources/iron_proxy.py @@ -1036,3 +1036,11 @@ __all__ = [ "merge_mappings", "mint_proxy_token", "reload_proxy", "start_proxy", "stop_proxy", "write_mappings", "write_proxy_config", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import stat # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/reasoning_effort.py b/agent/reasoning_effort.py index 8c67bbd1aa..711517f8fe 100644 --- a/agent/reasoning_effort.py +++ b/agent/reasoning_effort.py @@ -133,3 +133,12 @@ def requested_effort(reasoning_config: Optional[dict]) -> Optional[str]: if not isinstance(reasoning_config, dict) or reasoning_config.get("enabled") is False: return None return str(reasoning_config.get("effort") or "").strip().lower() or None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +CODEX_RESPONSES_EFFORTS: tuple[str, ...] = CODEX_GPT56_EFFORTS +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/relay_llm.py b/agent/relay_llm.py index d61b2d84f8..01a52e4208 100644 --- a/agent/relay_llm.py +++ b/agent/relay_llm.py @@ -880,3 +880,11 @@ def _run_awaitable( if _has_running_event_loop(): raise RuntimeError(loop_error) return asyncio.run(value) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import dataclass # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/relay_runtime.py b/agent/relay_runtime.py index 3102f61fed..a204ec5b76 100644 --- a/agent/relay_runtime.py +++ b/agent/relay_runtime.py @@ -1248,3 +1248,91 @@ def _reset_for_tests() -> None: HOST_REGISTRY.shutdown_all() _PLUGIN_CONFIGURATION.reset_for_tests() _PROFILE_KEY_CACHE.clear() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from enum import auto # noqa: F401,E402 + +def emit_mark( + name: str, + *, + session_id: str, + data: Any = None, + metadata: Any = None, +) -> bool: + """Emit a fail-open Relay mark under a Hermes session.""" + runtime = get_runtime(create=False) + if runtime is None: + return False + try: + return runtime.emit_mark( + name, + {"session_id": session_id}, + data=data, + metadata=metadata, + ) + except Exception: + logger.warning("Hermes Relay mark failed: %s", name, exc_info=True) + return False + +def ensure_session(*, session_id: str, **context: Any) -> RelaySession | None: + """Create or return the shared Relay session used by Hermes core.""" + runtime = get_runtime() + if runtime is None: + return None + try: + return runtime.ensure_session({"session_id": session_id, **context}) + except Exception: + logger.warning("Hermes Relay session initialization failed", exc_info=True) + return None + +def get_host( + *, + create: bool = True, + profile_key: str | None = None, +) -> RelayHost | None: + """Return the explicit real or reduced-capability host for a profile.""" + return HOST_REGISTRY.for_profile(profile_key, create=create) + +def get_session_handle(session_id: str) -> Any: + """Return the shared Relay handle for direct core instrumentation.""" + runtime = get_runtime(create=False) + return None if runtime is None else runtime.get_session_handle(session_id) + +def run_in_session( + session_id: str, + callback: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> Any: + """Run a scope, LLM, or tool API against a shared Hermes session.""" + runtime = get_runtime() + if runtime is None: + raise RuntimeError("Hermes Relay runtime is unavailable") + session = runtime.get_session(session_id) + if session is None: + session = runtime.ensure_session({"session_id": session_id}) + if session is None: + raise RuntimeError("Hermes Relay session is unavailable") + return runtime.run_in_session(session, callback, *args, **kwargs) + +async def run_in_session_async( + session_id: str, + callback: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> Any: + """Await a Relay operation inside a shared Hermes session context.""" + runtime = get_runtime() + if runtime is None: + raise RuntimeError("Hermes Relay runtime is unavailable") + session = runtime.get_session(session_id) + if session is None: + session = runtime.ensure_session({"session_id": session_id}) + if session is None: + raise RuntimeError("Hermes Relay session is unavailable") + return await runtime.run_in_session_async(session, callback, *args, **kwargs) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/relay_tools.py b/agent/relay_tools.py index ce00788b99..44d06a1653 100644 --- a/agent/relay_tools.py +++ b/agent/relay_tools.py @@ -104,3 +104,12 @@ def _run_awaitable(value: Any) -> Any: return relay_llm._run_awaitable( value, loop_error="Synchronous Hermes Relay tool execution cannot run on an active event-loop thread", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import asyncio # noqa: F401,E402 +import inspect # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/review_idle_queue.py b/agent/review_idle_queue.py index 8d3fb52f4c..8a4d35328b 100644 --- a/agent/review_idle_queue.py +++ b/agent/review_idle_queue.py @@ -201,3 +201,11 @@ def _managed_server_idle() -> bool: # Module singleton — one queue per process, like the load-progress watcher. QUEUE = ReviewIdleQueue() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/secret_sources/_cache.py b/agent/secret_sources/_cache.py index aaddb87bd3..33cdd826ef 100644 --- a/agent/secret_sources/_cache.py +++ b/agent/secret_sources/_cache.py @@ -186,3 +186,24 @@ class SecretCache(Generic[K]): def clear(self, home_path: Optional[Path] = None) -> None: self.memory.clear() self.disk.clear(home_path) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'FetchResult': ('agent.secret_sources.base', 'FetchResult'), + 'is_valid_env_name': ('agent.secret_sources.base', 'is_valid_env_name'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/secret_sources/bitwarden.py b/agent/secret_sources/bitwarden.py index a219a30d13..118f370765 100644 --- a/agent/secret_sources/bitwarden.py +++ b/agent/secret_sources/bitwarden.py @@ -526,3 +526,112 @@ def clear_caches(home_path: Optional[Path] = None) -> None: _reset_cache_for_tests = clear_caches + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import stat # noqa: F401,E402 + +def apply_bitwarden_secrets( + *, + enabled: bool, + access_token_env: str = "BWS_ACCESS_TOKEN", + project_id: str = "", + override_existing: bool = False, + cache_ttl_seconds: float = 300, + auto_install: bool = True, + server_url: str = "", + home_path: Optional[Path] = None, + encrypted_cache_enabled: bool = False, + encrypted_cache_max_stale_seconds: float = 0, +) -> FetchResult: + """Pull secrets from BSM and set them on ``os.environ``. + + This is the function ``load_hermes_dotenv()`` calls after the .env + files have loaded. It is intentionally defensive — any failure + returns a :class:`FetchResult` with ``error`` set; it never raises. + + ``server_url`` selects the Bitwarden region or self-hosted endpoint + (e.g. ``https://vault.bitwarden.eu`` for EU Cloud). Empty string + means use ``bws``'s default (US Cloud). + + Parameters mirror the ``secrets.bitwarden.*`` config keys so the + caller can just splat the dict in. + """ + result = FetchResult() + + if not enabled: + return result + + access_token = os.environ.get(access_token_env, "").strip() + if not access_token: + result.error = ( + f"secrets.bitwarden.enabled is true but {access_token_env} is " + "not set. Run `hermes secrets bitwarden setup`." + ) + return result + + if not project_id: + result.error = ( + "secrets.bitwarden.project_id is empty. " + "Run `hermes secrets bitwarden setup`." + ) + return result + + binary = find_bws(install_if_missing=auto_install) + result.binary_path = binary + if binary is None: + result.error = ( + "bws binary not available and auto-install is disabled. " + "Run `hermes secrets bitwarden setup` to install." + ) + return result + + try: + secrets, warnings = fetch_bitwarden_secrets( + access_token=access_token, + project_id=project_id, + binary=binary, + cache_ttl_seconds=cache_ttl_seconds, + server_url=server_url, + home_path=home_path, + encrypted_cache_enabled=encrypted_cache_enabled, + encrypted_cache_max_stale_seconds=encrypted_cache_max_stale_seconds, + ) + except RuntimeError as exc: + result.error = str(exc) + return result + + result.secrets = secrets + result.warnings.extend(warnings) + + for key, value in secrets.items(): + if key == access_token_env: + # Don't let BSM clobber the very token we used to fetch + # itself — that would be a footgun if someone stored the + # token as a BSM secret too. + result.skipped.append(key) + continue + if not override_existing and os.environ.get(key): + result.skipped.append(key) + continue + os.environ[key] = value + result.applied.append(key) + + return result + + +_PLUGIN_COMPAT_LAZY = { + 'DiskCache': ('agent.secret_sources._cache', 'DiskCache'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/secret_sources/command.py b/agent/secret_sources/command.py index 43af583071..f2043f5277 100644 --- a/agent/secret_sources/command.py +++ b/agent/secret_sources/command.py @@ -190,3 +190,192 @@ class CommandSource(SecretSource): return result result.secrets = secrets return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def apply_command_secrets( + *, + command: str, + override_existing: bool = False, + timeout_seconds: float = _COMMAND_TIMEOUT_SECONDS, + max_output_bytes: int = _MAX_OUTPUT_BYTES, + home_path: Optional[Path] = None, +) -> FetchResult: + """Run the helper once at startup and set its KEY=VALUE output on + ``os.environ``. + + LEGACY shim retained for API symmetry with ``apply_bitwarden_secrets``; + the startup path goes through :class:`CommandSource` + the registry + orchestrator instead (which owns precedence and the environ writes). + """ + result = FetchResult() + + command = (command or "").strip() + if not command: + result.error = ( + "secrets.command.enabled is true but secrets.command.command is " + "empty. Set the helper command in config.yaml." + ) + return result + + if _is_windows(): + result.warnings.append( + "the 'command' secret source is POSIX-only (needs /bin/sh); " + "skipping on Windows" + ) + return result + + # The list/enumerate path: run the helper exactly ONCE with an empty + # HERMES_SECRET_KEY and parse its stdout as a dotenv blob. + stdout = _run_helper(command, "", timeout_seconds, max_output_bytes) + if stdout is None: + # _run_helper already logged structured fields to stderr. + result.warnings.append( + "helper command failed at startup; no secrets applied " + "(process env / .env values remain in effect)" + ) + return result + + secrets = _parse_dotenv_map(stdout) + result.secrets = secrets + if not secrets: + result.warnings.append( + "helper output was not a KEY=VALUE map; nothing applied at " + "startup (a bare-value helper still resolves single keys on demand)" + ) + return result + + for key, value in secrets.items(): + if value.strip() == "": + # Whitespace-only placeholder entries are "no value" — applying + # them would flow into an Authorization header → guaranteed 401. + result.skipped.append(key) + continue + if not override_existing and os.environ.get(key): + # Process env / .env win — same precedence as bitwarden. + result.skipped.append(key) + continue + os.environ[key] = value + result.applied.append(key) + + return result + +def parse_secret_output(stdout: str, wanted_key: str) -> Optional[str]: + """Parse a secret-fetch helper's stdout. Supports BOTH shapes: + + * a bare value (single secret): the whole trimmed stdout is the value. + * a dotenv blob (KEY=VALUE lines): parse them and return the entry for + ``wanted_key``. + + Mirrors the TS ``parseSecretOutput`` exactly, including the cross-key + misroute guard and the base64-padding disambiguation. + """ + text = stdout.replace("\r\n", "\n") + lines = text.split("\n") + + # 1. Exact dotenv match wins: scan for a `wanted_key=...` line. This + # is deterministic and never returns another key's value. + dotenv_lines = [ + line + for line in (raw.strip() for raw in lines) + if line and not line.startswith("#") and _ENV_LINE.match(line) + ] + for line in dotenv_lines: + m = _ENV_LINE.match(line) + assert m is not None # filtered above + if m.group(1) == wanted_key: + value = unquote_dotenv_value(m.group(2)) + # Whitespace-only (e.g. a quoted `K=" "` placeholder) is "no + # value": it would otherwise flow into an Authorization header + # → guaranteed 401. + return value if value.strip() != "" else None + + # 2. The output is a multi-key dotenv dump that does NOT contain the + # wanted key → None, rather than mis-returning an unrelated line as + # a bare value. Only >=2 env-shaped lines count as a dump: a SINGLE + # non-matching env-shaped line falls through to the bare-value + # branch, because a bare secret can itself match the KEY=VALUE shape + # (e.g. base64 with '=' padding, "dGVzdA==") and must not be + # misclassified as a dump. + if len(dotenv_lines) > 1: + return None + + # 3. Otherwise treat the whole output as a single bare value (a per-key + # helper that printed just the secret). Trim first so whitespace-only + # output (a ' '/'\t' placeholder entry) resolves to None, never a "key". + value = text.strip() + if value == "": + return None + + # SECURITY (S2): a single env-shaped line for a DIFFERENT key must not + # be returned as the wanted secret. A sloppy helper (e.g. `head -1 + # env-file`, or a grep that matched the wrong line) emitting + # `OTHER_KEY=realvalue` would otherwise flow — key name, '=' and the + # OTHER key's value — into an Authorization header sent to the WANTED + # key's endpoint: cross-provider credential leakage, not just a 401. + # Disambiguation from a bare base64 secret: base64 padding only ever + # produces an env-shaped line whose "value" part is empty or all '=' + # (`dGVzdA==` → key `dGVzdA`, value `=`), so a non-trivial value part + # after a non-matching key means a misrouted dotenv entry → None. + env_shaped = _ENV_LINE.match(value) + if ( + env_shaped + and env_shaped.group(1) != wanted_key + and re.fullmatch(r"=*", env_shaped.group(2).strip()) is None + ): + return None + return value + +def get_command_secret( + *, + command: str, + key: str, + timeout_seconds: float = _COMMAND_TIMEOUT_SECONDS, + max_output_bytes: int = _MAX_OUTPUT_BYTES, +) -> Optional[str]: + """Resolve a single secret by running the helper with the key in + ``HERMES_SECRET_KEY``. Returns None on any failure — never raises.""" + command = (command or "").strip() + if not command: + return None + stdout = _run_helper(command, key, timeout_seconds, max_output_bytes) + if stdout is None: + return None + return parse_secret_output(stdout, key) + +def list_command_secrets( + *, + command: str, + timeout_seconds: float = _COMMAND_TIMEOUT_SECONDS, + max_output_bytes: int = _MAX_OUTPUT_BYTES, +) -> Dict[str, str]: + """Enumerate secrets by running the helper ONCE with an empty key. + + Returns the dotenv map ONLY when the helper emits a KEY=VALUE blob; + a bare-value helper returns ``{}``. Never raises. + """ + command = (command or "").strip() + if not command: + return {} + stdout = _run_helper(command, "", timeout_seconds, max_output_bytes) + if stdout is None: + return {} + return _parse_dotenv_map(stdout) + + +_PLUGIN_COMPAT_LAZY = { + 'get_source_environment': ('agent.secret_sources.base', 'get_source_environment'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/secret_sources/onepassword.py b/agent/secret_sources/onepassword.py index ed39bb63e9..4a10914b37 100644 --- a/agent/secret_sources/onepassword.py +++ b/agent/secret_sources/onepassword.py @@ -335,3 +335,24 @@ def clear_caches(home_path: Optional[Path] = None) -> None: _reset_cache_for_tests = clear_caches + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import hashlib # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DiskCache': ('agent.secret_sources._cache', 'DiskCache'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/shell_hooks.py b/agent/shell_hooks.py index e552740cf6..38304130cd 100644 --- a/agent/shell_hooks.py +++ b/agent/shell_hooks.py @@ -590,3 +590,11 @@ def run_once(spec: ShellHookSpec, kwargs: Dict[str, Any]) -> Dict[str, Any]: result = _spawn(spec, _serialize_payload(spec.event, kwargs)) result["parsed"] = _evaluate_result(spec, result) return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import shlex # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/skill_bundles.py b/agent/skill_bundles.py index 8fcd581834..b1c922050e 100644 --- a/agent/skill_bundles.py +++ b/agent/skill_bundles.py @@ -204,3 +204,11 @@ def delete_bundle(name: str) -> Path: def get_bundle(name: str) -> Optional[Dict[str, Any]]: """Look up a bundle by name (slug-normalized).""" return get_skill_bundles().get(f"/{_slugify(name)}") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import re # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/skill_utils.py b/agent/skill_utils.py index 7c33210e7f..fe0b06c69f 100644 --- a/agent/skill_utils.py +++ b/agent/skill_utils.py @@ -767,3 +767,20 @@ def parse_qualified_name(name: str) -> Tuple[Optional[str], str]: def is_valid_namespace(candidate: Optional[str]) -> bool: """Check whether *candidate* is a valid namespace (``[a-zA-Z0-9_-]+``).""" return bool(candidate) and bool(_NAMESPACE_RE.match(candidate)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_scan_ordered_skills_dirs() -> List[Path]: + """All skill dirs in precedence order: project → local → external. + + First-wins name deduplication over this order gives project skills + priority over profile-local and external ones. + """ + dirs = list(get_project_skills_dirs()) + dirs.extend(get_all_skills_dirs()) + return dirs +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/ssl_guard.py b/agent/ssl_guard.py index 79b2b4e765..8b045a5577 100644 --- a/agent/ssl_guard.py +++ b/agent/ssl_guard.py @@ -60,3 +60,19 @@ def verify_ca_bundle() -> None: except Exception as exc: raise _ssl_err(f"certifi is not importable: {exc}") from exc _validate_bundle_path("certifi", str(certifi.where()), require_substantial=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def verify_ca_bundle_with_fallback() -> None: + """Backward-compatible wrapper for older call sites. + + The old PR name mentioned a platform fallback, but allowing startup with a + broken certifi bundle still leaves httpx/OpenAI and requests call sites + failing later. Keep the wrapper name but enforce the same check. + """ + verify_ca_bundle() +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/system_prompt.py b/agent/system_prompt.py index 3c47a66340..125aadaba0 100644 --- a/agent/system_prompt.py +++ b/agent/system_prompt.py @@ -709,3 +709,23 @@ def format_tools_for_system_message(agent: Any) -> str: __all__ = ["build_system_prompt_parts", "build_system_prompt", "invalidate_system_prompt", "restore_plugin_prompt_sections", "format_tools_for_system_message"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'OPENAI_MODEL_EXECUTION_GUIDANCE': ('agent.prompt_builder', 'OPENAI_MODEL_EXECUTION_GUIDANCE'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/terminal_env_registry.py b/agent/terminal_env_registry.py index e2619448c9..4b7b974800 100644 --- a/agent/terminal_env_registry.py +++ b/agent/terminal_env_registry.py @@ -81,3 +81,25 @@ def plugin_strip_env_keys() -> frozenset: except Exception: logger.debug("Terminal environment provider strip_env_keys raised", exc_info=True) return frozenset(keys) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transcript_repair.py b/agent/transcript_repair.py index 3b1c7ae3da..a1bed300f3 100644 --- a/agent/transcript_repair.py +++ b/agent/transcript_repair.py @@ -84,3 +84,11 @@ def sync_flushed_message_markers(batch_msgs: List[Dict[str, Any]], batch_rows: L written["_row_id"] = row["_row_id"] if "_canonical_content" in row: written["content"] = row["_canonical_content"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transcription_provider.py b/agent/transcription_provider.py index 647dbe644d..a9cf75bc29 100644 --- a/agent/transcription_provider.py +++ b/agent/transcription_provider.py @@ -34,3 +34,25 @@ class TranscriptionProvider(CatalogProviderBase): ``prompt`` (from ``stt.prompt`` or a ``pre_transcription`` hook) as a vocabulary hint; unknown keys must be ignored. """ + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('agent.i18n', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transcription_registry.py b/agent/transcription_registry.py index 365f91eb8e..68abb6e07a 100644 --- a/agent/transcription_registry.py +++ b/agent/transcription_registry.py @@ -43,3 +43,27 @@ _registry: ProviderRegistry[TranscriptionProvider] = ProviderRegistry( on_builtin_collision=_warn_builtin_collision, ) _registry.export(globals()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transports/chat_completions.py b/agent/transports/chat_completions.py index 3000dda5ee..723174f87d 100644 --- a/agent/transports/chat_completions.py +++ b/agent/transports/chat_completions.py @@ -565,3 +565,11 @@ class ChatCompletionsTransport(ProviderTransport): from agent.transports import register_transport # noqa: E402 register_transport("chat_completions", ChatCompletionsTransport) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transports/codex.py b/agent/transports/codex.py index 874aac658a..183c7f3a30 100644 --- a/agent/transports/codex.py +++ b/agent/transports/codex.py @@ -674,3 +674,13 @@ class ResponsesApiTransport(ProviderTransport): from agent.transports import register_transport # noqa: E402 register_transport("codex_responses", ResponsesApiTransport) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/transports/codex_app_server.py b/agent/transports/codex_app_server.py index 1d2d348057..def380dd42 100644 --- a/agent/transports/codex_app_server.py +++ b/agent/transports/codex_app_server.py @@ -276,3 +276,12 @@ def check_codex_binary( if version < min_version: return False, f"codex {have} is older than required {'.'.join(map(str, min_version))}. Run: npm i -g @openai/codex" return True, have + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import field # noqa: F401,E402 +import time # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/tts_registry.py b/agent/tts_registry.py index 9fbbfdff39..4a785e5f6f 100644 --- a/agent/tts_registry.py +++ b/agent/tts_registry.py @@ -43,3 +43,27 @@ _registry: ProviderRegistry[TTSProvider] = ProviderRegistry( builtin_names=_BUILTIN_NAMES, on_builtin_collision=_warn_builtin_collision, ) _registry.export(globals()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/turn_context.py b/agent/turn_context.py index df86b2746f..52d053ec88 100644 --- a/agent/turn_context.py +++ b/agent/turn_context.py @@ -1001,3 +1001,27 @@ def build_api_messages( if effective_system: api_messages = [{"role": "system", "content": effective_system}] + api_messages return api_messages, effective_system + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'IDLE_COMPACTION_STATUS_TEMPLATE': ('agent.conversation_compression', 'IDLE_COMPACTION_STATUS_TEMPLATE'), + 'PREFLIGHT_COMPRESSION_STATUS_TEMPLATE': ('agent.conversation_compression', 'PREFLIGHT_COMPRESSION_STATUS_TEMPLATE'), + 'automatic_compaction_status_message': ('agent.context_engine', 'automatic_compaction_status_message'), + 'compression_skipped_due_to_lock': ('agent.conversation_compression', 'compression_skipped_due_to_lock'), + 'conversation_history_after_compression': ('agent.conversation_compression', 'conversation_history_after_compression'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/turn_retry_state.py b/agent/turn_retry_state.py index 47ce71245d..24a28bffbe 100644 --- a/agent/turn_retry_state.py +++ b/agent/turn_retry_state.py @@ -50,3 +50,11 @@ class TurnRetryState: # A user correction cancelled the in-flight request: append a role-safe checkpoint + # user message, rebuild the payload, and retry the same logical iteration. restart_with_redirected_messages: bool = False + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import fields # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/usage_pricing.py b/agent/usage_pricing.py index 9d60c1a070..0f73ce36f9 100644 --- a/agent/usage_pricing.py +++ b/agent/usage_pricing.py @@ -618,3 +618,12 @@ def format_token_count_compact(value: int) -> str: if "." in text: text = text.rstrip("0").rstrip(".") return f"{sign}{text}{suffix}" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +DEFAULT_PRICING = {"input": 0.0, "output": 0.0} +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/video_gen_provider.py b/agent/video_gen_provider.py index 14b0092286..31ec67e105 100644 --- a/agent/video_gen_provider.py +++ b/agent/video_gen_provider.py @@ -262,3 +262,13 @@ class OpenAICompatibleVideoGenProvider(VideoGenProvider): close = getattr(client, "close", None) if callable(close): close() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import base64 # noqa: F401,E402 +import datetime # noqa: F401,E402 +import uuid # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/video_gen_registry.py b/agent/video_gen_registry.py index 04738f7a45..935aa69f61 100644 --- a/agent/video_gen_registry.py +++ b/agent/video_gen_registry.py @@ -44,3 +44,26 @@ def get_active_provider() -> Optional[VideoGenProvider]: if is_available_safe(p, logger, "video_gen provider %s.is_available() raised %s") ] return available[0] if len(available) == 1 else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/web_search_provider.py b/agent/web_search_provider.py index 48b24c7f1b..4c5e393241 100644 --- a/agent/web_search_provider.py +++ b/agent/web_search_provider.py @@ -78,3 +78,11 @@ class WebSearchProvider(ProviderBase): raise NotImplementedError( f"{self.name} does not support extract (override supports_extract)" ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/agent/web_search_registry.py b/agent/web_search_registry.py index a62c5f26da..df1643193b 100644 --- a/agent/web_search_registry.py +++ b/agent/web_search_registry.py @@ -199,3 +199,26 @@ def get_active_search_provider() -> Optional[WebSearchProvider]: def get_active_extract_provider() -> Optional[WebSearchProvider]: """Resolve the currently-active web extract provider.""" return _resolve(_configured_backend("extract"), capability="extract") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/cli.py b/cli.py index 6ee26558a6..240f795b4b 100644 --- a/cli.py +++ b/cli.py @@ -4574,3 +4574,69 @@ if __name__ == "__main__": import fire fire.Fire(main) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from prompt_toolkit.layout.menus import CompletionsMenu # noqa: F401,E402 +from prompt_toolkit.filters import Condition # noqa: F401,E402 +from prompt_toolkit.layout import ConditionalContainer # noqa: F401,E402 +from prompt_toolkit.layout.processors import ConditionalProcessor # noqa: F401,E402 +from prompt_toolkit.layout.dimension import Dimension # noqa: F401,E402 +from prompt_toolkit.history import FileHistory # noqa: F401,E402 +from prompt_toolkit.layout import FormattedTextControl # noqa: F401,E402 +from prompt_toolkit.layout import HSplit # noqa: F401,E402 +from prompt_toolkit.key_binding import KeyBindings # noqa: F401,E402 +from prompt_toolkit.layout import Layout # noqa: F401,E402 +from prompt_toolkit.styles import Style as PTStyle # noqa: F401,E402 +from rich.panel import Panel # noqa: F401,E402 +from prompt_toolkit.layout.processors import PasswordProcessor # noqa: F401,E402 +from prompt_toolkit.layout.processors import Processor # noqa: F401,E402 +from prompt_toolkit.widgets import TextArea # noqa: F401,E402 +from prompt_toolkit.layout.processors import Transformation # noqa: F401,E402 +from prompt_toolkit.layout import Window # noqa: F401,E402 +from prompt_toolkit.layout import WindowAlign # noqa: F401,E402 +import base64 # noqa: F401,E402 +import concurrent.futures # noqa: F401,E402 +import copy # noqa: F401,E402 +from rich import box as rich_box # noqa: F401,E402 +import tempfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'AIAgent': ('run_agent', 'AIAgent'), + 'CanonicalUsage': ('agent.usage_pricing', 'CanonicalUsage'), + 'DEFAULT_BROWSER_CDP_URL': ('hermes_cli.browser_connect', 'DEFAULT_BROWSER_CDP_URL'), + 'HERMES_AGENT_LOGO': ('hermes_cli.banner', 'HERMES_AGENT_LOGO'), + 'HERMES_CADUCEUS': ('hermes_cli.banner', 'HERMES_CADUCEUS'), + 'SlashCommandAutoSuggest': ('hermes_cli.commands_completion', 'SlashCommandAutoSuggest'), + 'SlashCommandCompleter': ('hermes_cli.commands_completion', 'SlashCommandCompleter'), + 'build_welcome_banner': ('hermes_cli.banner', 'build_welcome_banner'), + 'display_hermes_home': ('hermes_constants', 'display_hermes_home'), + 'estimate_usage_cost': ('agent.usage_pricing', 'estimate_usage_cost'), + 'get_all_toolsets': ('toolsets', 'get_all_toolsets'), + 'get_job': ('cron.jobs', 'get_job'), + 'get_toolset_for_tool': ('model_tools', 'get_toolset_for_tool'), + 'get_toolset_info': ('toolsets', 'get_toolset_info'), + 'init_skin_from_config': ('hermes_cli.skin_engine', 'init_skin_from_config'), + 'is_browser_debug_ready': ('hermes_cli.browser_connect', 'is_browser_debug_ready'), + 'is_table_divider': ('agent.markdown_tables', 'is_table_divider'), + 'looks_like_table_row': ('agent.markdown_tables', 'looks_like_table_row'), + 'manual_chrome_debug_command': ('hermes_cli.browser_connect', 'manual_chrome_debug_command'), + 'print_config_warnings': ('hermes_cli.config', 'print_config_warnings'), + 'prompt_for_secret': ('hermes_cli.callbacks', 'prompt_for_secret'), + 'set_friendly_tool_labels': ('agent.display', 'set_friendly_tool_labels'), + 'set_tool_preview_max_len': ('agent.display', 'set_tool_preview_max_len'), + 'setup_logging': ('hermes_logging', 'setup_logging'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/compat_manifest.json b/compat_manifest.json new file mode 100644 index 0000000000..724d740760 --- /dev/null +++ b/compat_manifest.json @@ -0,0 +1,12540 @@ +{ + "schema": 1, + "note": "Plugin-compat pointers kept for external plugins; removed on schedule by reverting the commit that added them. scripts/check_compat_pointers.py forbids in-tree use.", + "entries": [ + { + "facade": "acp_adapter.auth", + "name": "has_provider", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "acp_adapter.edit_approval", + "name": "FutureTimeout", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "acp_adapter.edit_approval", + "name": "clear_edit_approval_requester", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "acp_adapter.edit_approval", + "name": "get_edit_approval_requester", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "acp_adapter.events", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "acp_adapter.server", + "name": "ACP_MAX_MODELS_PER_PROVIDER", + "kind": "moved-lazy", + "target": "acp_adapter.model_catalog" + }, + { + "facade": "acp_adapter.server", + "name": "AgentThoughtChunk", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "AudioContentBlock", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "AvailableCommand", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "AvailableCommandsUpdate", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "BlobResourceContents", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "EmbeddedResourceContentBlock", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "ImageContentBlock", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "acp_adapter.server", + "name": "ResourceContentBlock", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "TextResourceContents", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "UnstructuredCommandInput", + "kind": "import", + "target": "acp.schema" + }, + { + "facade": "acp_adapter.server", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "acp_adapter.server", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "acp_adapter.server", + "name": "unquote", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "acp_adapter.server", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "acp_adapter.session", + "name": "Lock", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.agent_init", + "name": "ToolGuardrailDecision", + "kind": "moved-lazy", + "target": "agent.tool_guardrails" + }, + { + "facade": "agent.agent_runtime_helpers", + "name": "agent_runtime_owns_post_tool_hook", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.agent_runtime_helpers", + "name": "intent_ack_continuation_enabled", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.anthropic_adapter", + "name": "CredentialPersistError", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "agent.anthropic_adapter", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.anthropic_adapter", + "name": "base_url_host_matches", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "agent.anthropic_adapter", + "name": "base_url_hostname", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "agent.anthropic_adapter", + "name": "claude_code_credentials_path", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "agent.anthropic_adapter", + "name": "get_hermes_home", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.anthropic_adapter", + "name": "is_claude_code_token_valid", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "is_rotation_consumed_uncommitted", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "agent.anthropic_adapter", + "name": "mark_rotation_consumed_uncommitted", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "agent.anthropic_adapter", + "name": "platform", + "kind": "import", + "target": "platform" + }, + { + "facade": "agent.anthropic_adapter", + "name": "read_claude_code_credentials", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "read_hermes_oauth_credentials", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "refresh_anthropic_oauth_pure", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "resolve_anthropic_token", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "run_hermes_oauth_login_pure", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "run_oauth_setup_token", + "kind": "moved-lazy", + "target": "agent.anthropic_credentials" + }, + { + "facade": "agent.anthropic_adapter", + "name": "secrets", + "kind": "import", + "target": "secrets" + }, + { + "facade": "agent.anthropic_adapter", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "agent.anthropic_adapter", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "agent.aux_accounting", + "name": "get_accounting_context", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.auxiliary_client", + "name": "NOUS_EXTRA_BODY", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.auxiliary_client", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "agent.auxiliary_client", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "agent.auxiliary_client", + "name": "get_async_text_auxiliary_client", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.backend_identity", + "name": "_REASON_SCOPES", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of classify_failure_scope)" + }, + { + "facade": "agent.backend_identity", + "name": "classify_failure_scope", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.background_review", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "agent.background_review", + "name": "is_background_review_enabled", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "CONTEXT_OVERFLOW_PATTERNS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "OVERLOAD_PATTERNS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "THROTTLE_PATTERNS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "call_converse_stream", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "is_context_overflow_error", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of classify_bedrock_error)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "classify_bedrock_error", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bedrock_adapter", + "name": "is_context_overflow_error", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.bounded_response", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.bounded_response", + "name": "read_error_body_or_default", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.browser_provider", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.browser_provider", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.browser_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.browser_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.browser_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.browser_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.codex_runtime", + "name": "run_codex_create_stream_fallback", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.coding_context", + "name": "coding_system_blocks", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.coding_context", + "name": "_PROFILES", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of get_profile)" + }, + { + "facade": "agent.coding_context", + "name": "get_profile", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.context_compressor", + "name": "tool_result_id_variants", + "kind": "moved-lazy", + "target": "agent.message_sanitization" + }, + { + "facade": "agent.conversation_compression", + "name": "CompressionExecutorSaturatedError", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.conversation_loop", + "name": "COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "FailoverReason", + "kind": "moved-lazy", + "target": "agent.error_classifier" + }, + { + "facade": "agent.conversation_loop", + "name": "KawaiiSpinner", + "kind": "moved-lazy", + "target": "agent.display" + }, + { + "facade": "agent.conversation_loop", + "name": "PARTIAL_STREAM_STUB_ID", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.conversation_loop", + "name": "PRE_API_COMPRESSION_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "adaptive_rate_limit_backoff", + "kind": "moved-lazy", + "target": "agent.retry_utils" + }, + { + "facade": "agent.conversation_loop", + "name": "anchored_context_tokens", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "automatic_compaction_status_message", + "kind": "moved-lazy", + "target": "agent.context_engine" + }, + { + "facade": "agent.conversation_loop", + "name": "capture_usage_anchor", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "classify_api_error", + "kind": "moved-lazy", + "target": "agent.error_classifier" + }, + { + "facade": "agent.conversation_loop", + "name": "close_interrupted_tool_sequence", + "kind": "moved-lazy", + "target": "agent.message_sanitization" + }, + { + "facade": "agent.conversation_loop", + "name": "coalesce_tool_call_id", + "kind": "moved-lazy", + "target": "agent.message_sanitization" + }, + { + "facade": "agent.conversation_loop", + "name": "compose_user_api_content", + "kind": "moved-lazy", + "target": "agent.turn_context" + }, + { + "facade": "agent.conversation_loop", + "name": "compression_blocked_transiently", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "compression_skipped_due_to_lock", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "context_compression_timed_out", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "conversation_history_after_compression", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.conversation_loop", + "name": "env_var_enabled", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "agent.conversation_loop", + "name": "estimate_messages_tokens_rough", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "estimate_request_tokens_rough", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "estimate_usage_cost", + "kind": "moved-lazy", + "target": "agent.usage_pricing" + }, + { + "facade": "agent.conversation_loop", + "name": "get_context_length_from_provider_error", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "has_incomplete_scratchpad", + "kind": "moved-lazy", + "target": "agent.trajectory" + }, + { + "facade": "agent.conversation_loop", + "name": "is_output_cap_error", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "is_repetition_dominated", + "kind": "moved-lazy", + "target": "agent.repetition_guard" + }, + { + "facade": "agent.conversation_loop", + "name": "is_zai_coding_overload_error", + "kind": "moved-lazy", + "target": "agent.retry_utils" + }, + { + "facade": "agent.conversation_loop", + "name": "jittered_backoff", + "kind": "moved-lazy", + "target": "agent.retry_utils" + }, + { + "facade": "agent.conversation_loop", + "name": "normalize_usage", + "kind": "moved-lazy", + "target": "agent.usage_pricing" + }, + { + "facade": "agent.conversation_loop", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "agent.conversation_loop", + "name": "parse_available_output_tokens_from_error", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "random", + "kind": "import", + "target": "random" + }, + { + "facade": "agent.conversation_loop", + "name": "reanchor_current_turn_user_idx", + "kind": "moved-lazy", + "target": "agent.turn_context" + }, + { + "facade": "agent.conversation_loop", + "name": "save_context_length", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "agent.conversation_loop", + "name": "serialized_messages_bytes", + "kind": "moved-lazy", + "target": "agent.message_sanitization" + }, + { + "facade": "agent.conversation_loop", + "name": "splice_provider_projection", + "kind": "moved-lazy", + "target": "agent.provider_projection" + }, + { + "facade": "agent.conversation_loop", + "name": "ssl", + "kind": "import", + "target": "ssl" + }, + { + "facade": "agent.conversation_loop", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "agent.conversation_loop", + "name": "zai_coding_overload_retry_ceiling", + "kind": "moved-lazy", + "target": "agent.retry_utils" + }, + { + "facade": "agent.credential_sources", + "name": "register", + "kind": "moved-lazy", + "target": "tools.slash_confirm" + }, + { + "facade": "agent.display", + "name": "get_friendly_tool_labels", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.error_surface", + "name": "LAYER_RUNTIME", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.estop", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "agent.file_safety", + "name": "PROFILE_SCOPED_AREAS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.file_safety", + "name": "classify_cross_profile_target", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.file_safety", + "name": "get_cross_profile_warning", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.image_gen_provider", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "agent.image_gen_provider", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "agent.image_gen_provider", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "agent.image_gen_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.image_gen_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.image_gen_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.image_gen_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.learning_graph_render", + "name": "Grid", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.learning_graph_render", + "name": "Run", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db" + }, + { + "facade": "agent.lsp.eventlog", + "name": "_announced_no_server", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of log_no_server_configured)" + }, + { + "facade": "agent.lsp.eventlog", + "name": "_announce_once", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of log_no_server_configured)" + }, + { + "facade": "agent.lsp.eventlog", + "name": "log_no_server_configured", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.lsp.protocol", + "name": "ERROR_REQUEST_CANCELLED", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.monitoring.cron_health", + "name": "GatewayHealthSnapshot", + "kind": "moved-lazy", + "target": "agent.monitoring.gateway_health" + }, + { + "facade": "agent.monitoring.cron_health", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "agent.monitoring.emitter", + "name": "TelemetryEmitter", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.monitoring.gateway_health", + "name": "redact_gateway_message", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.monitoring.gateway_health_export", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "agent.nous_rate_guard", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "agent.nous_rate_guard", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "agent.outbound_webhooks", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "agent.outbound_webhooks", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "agent.outbound_webhooks", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "COLUMNS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "FRAME_COUNTS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "ROWS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "io", + "kind": "restored-import", + "target": "io" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "atlas_to_webp_bytes", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.pet.generate.atlas", + "name": "io", + "kind": "import", + "target": "io" + }, + { + "facade": "agent.prompt_builder", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.prompt_builder", + "name": "org_id_of_path", + "kind": "moved-lazy", + "target": "agent.skill_utils" + }, + { + "facade": "agent.prompt_cache_boundary", + "name": "clear_stable_prefixes", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.proxy_sources.iron_proxy", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "agent.reasoning_effort", + "name": "CODEX_RESPONSES_EFFORTS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_llm", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "agent.relay_runtime", + "name": "auto", + "kind": "import", + "target": "enum" + }, + { + "facade": "agent.relay_runtime", + "name": "emit_mark", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_runtime", + "name": "ensure_session", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_runtime", + "name": "get_host", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_runtime", + "name": "get_session_handle", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_runtime", + "name": "run_in_session", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_runtime", + "name": "run_in_session_async", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.relay_tools", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "agent.relay_tools", + "name": "inspect", + "kind": "import", + "target": "inspect" + }, + { + "facade": "agent.review_idle_queue", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.secret_sources._cache", + "name": "FetchResult", + "kind": "moved-lazy", + "target": "agent.secret_sources.base" + }, + { + "facade": "agent.secret_sources._cache", + "name": "is_valid_env_name", + "kind": "moved-lazy", + "target": "agent.secret_sources.base" + }, + { + "facade": "agent.secret_sources.bitwarden", + "name": "DiskCache", + "kind": "moved-lazy", + "target": "agent.secret_sources._cache" + }, + { + "facade": "agent.secret_sources.bitwarden", + "name": "apply_bitwarden_secrets", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.secret_sources.bitwarden", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "agent.secret_sources.command", + "name": "apply_command_secrets", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.secret_sources.command", + "name": "parse_secret_output", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of get_command_secret)" + }, + { + "facade": "agent.secret_sources.command", + "name": "get_command_secret", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.secret_sources.command", + "name": "get_source_environment", + "kind": "moved-lazy", + "target": "agent.secret_sources.base" + }, + { + "facade": "agent.secret_sources.command", + "name": "list_command_secrets", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.secret_sources.command", + "name": "parse_secret_output", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.secret_sources.onepassword", + "name": "DiskCache", + "kind": "moved-lazy", + "target": "agent.secret_sources._cache" + }, + { + "facade": "agent.secret_sources.onepassword", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "agent.shell_hooks", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "agent.skill_bundles", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "agent.skill_utils", + "name": "get_scan_ordered_skills_dirs", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.ssl_guard", + "name": "verify_ca_bundle_with_fallback", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.system_prompt", + "name": "OPENAI_MODEL_EXECUTION_GUIDANCE", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "agent.terminal_env_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.terminal_env_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.terminal_env_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.transcript_repair", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transcription_provider", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transcription_provider", + "name": "logger", + "kind": "moved-lazy", + "target": "agent.i18n" + }, + { + "facade": "agent.transcription_provider", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "agent.transcription_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transcription_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transcription_registry", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transcription_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.transcription_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.transports.chat_completions", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transports.codex", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transports.codex", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transports.codex", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.transports.codex_app_server", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "agent.transports.codex_app_server", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "agent.tts_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.tts_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.tts_registry", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.tts_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.tts_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.turn_context", + "name": "IDLE_COMPACTION_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.turn_context", + "name": "PREFLIGHT_COMPRESSION_STATUS_TEMPLATE", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.turn_context", + "name": "automatic_compaction_status_message", + "kind": "moved-lazy", + "target": "agent.context_engine" + }, + { + "facade": "agent.turn_context", + "name": "compression_skipped_due_to_lock", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.turn_context", + "name": "conversation_history_after_compression", + "kind": "moved-lazy", + "target": "agent.conversation_compression" + }, + { + "facade": "agent.turn_retry_state", + "name": "fields", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "agent.usage_pricing", + "name": "DEFAULT_PRICING", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "agent.video_gen_provider", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "agent.video_gen_provider", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "agent.video_gen_provider", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "agent.video_gen_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.video_gen_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.video_gen_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.video_gen_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "agent.web_search_provider", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.web_search_registry", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.web_search_registry", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "agent.web_search_registry", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "agent.web_search_registry", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "cli", + "name": "AIAgent", + "kind": "moved-lazy", + "target": "run_agent" + }, + { + "facade": "cli", + "name": "CanonicalUsage", + "kind": "moved-lazy", + "target": "agent.usage_pricing" + }, + { + "facade": "cli", + "name": "CompletionsMenu", + "kind": "import", + "target": "prompt_toolkit.layout.menus" + }, + { + "facade": "cli", + "name": "Condition", + "kind": "import", + "target": "prompt_toolkit.filters" + }, + { + "facade": "cli", + "name": "ConditionalContainer", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "ConditionalProcessor", + "kind": "import", + "target": "prompt_toolkit.layout.processors" + }, + { + "facade": "cli", + "name": "DEFAULT_BROWSER_CDP_URL", + "kind": "moved-lazy", + "target": "hermes_cli.browser_connect" + }, + { + "facade": "cli", + "name": "Dimension", + "kind": "import", + "target": "prompt_toolkit.layout.dimension" + }, + { + "facade": "cli", + "name": "FileHistory", + "kind": "import", + "target": "prompt_toolkit.history" + }, + { + "facade": "cli", + "name": "FormattedTextControl", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "HERMES_AGENT_LOGO", + "kind": "moved-lazy", + "target": "hermes_cli.banner" + }, + { + "facade": "cli", + "name": "HERMES_CADUCEUS", + "kind": "moved-lazy", + "target": "hermes_cli.banner" + }, + { + "facade": "cli", + "name": "HSplit", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "KeyBindings", + "kind": "import", + "target": "prompt_toolkit.key_binding" + }, + { + "facade": "cli", + "name": "Layout", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "PTStyle", + "kind": "import", + "target": "prompt_toolkit.styles" + }, + { + "facade": "cli", + "name": "Panel", + "kind": "import", + "target": "rich.panel" + }, + { + "facade": "cli", + "name": "PasswordProcessor", + "kind": "import", + "target": "prompt_toolkit.layout.processors" + }, + { + "facade": "cli", + "name": "Processor", + "kind": "import", + "target": "prompt_toolkit.layout.processors" + }, + { + "facade": "cli", + "name": "SlashCommandAutoSuggest", + "kind": "moved-lazy", + "target": "hermes_cli.commands_completion" + }, + { + "facade": "cli", + "name": "SlashCommandCompleter", + "kind": "moved-lazy", + "target": "hermes_cli.commands_completion" + }, + { + "facade": "cli", + "name": "TextArea", + "kind": "import", + "target": "prompt_toolkit.widgets" + }, + { + "facade": "cli", + "name": "Transformation", + "kind": "import", + "target": "prompt_toolkit.layout.processors" + }, + { + "facade": "cli", + "name": "Window", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "WindowAlign", + "kind": "import", + "target": "prompt_toolkit.layout" + }, + { + "facade": "cli", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "cli", + "name": "build_welcome_banner", + "kind": "moved-lazy", + "target": "hermes_cli.banner" + }, + { + "facade": "cli", + "name": "concurrent", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "cli", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "cli", + "name": "display_hermes_home", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "cli", + "name": "estimate_usage_cost", + "kind": "moved-lazy", + "target": "agent.usage_pricing" + }, + { + "facade": "cli", + "name": "get_all_toolsets", + "kind": "moved-lazy", + "target": "toolsets" + }, + { + "facade": "cli", + "name": "get_job", + "kind": "moved-lazy", + "target": "cron.jobs" + }, + { + "facade": "cli", + "name": "get_toolset_for_tool", + "kind": "moved-lazy", + "target": "model_tools" + }, + { + "facade": "cli", + "name": "get_toolset_info", + "kind": "moved-lazy", + "target": "toolsets" + }, + { + "facade": "cli", + "name": "init_skin_from_config", + "kind": "moved-lazy", + "target": "hermes_cli.skin_engine" + }, + { + "facade": "cli", + "name": "is_browser_debug_ready", + "kind": "moved-lazy", + "target": "hermes_cli.browser_connect" + }, + { + "facade": "cli", + "name": "is_table_divider", + "kind": "moved-lazy", + "target": "agent.markdown_tables" + }, + { + "facade": "cli", + "name": "looks_like_table_row", + "kind": "moved-lazy", + "target": "agent.markdown_tables" + }, + { + "facade": "cli", + "name": "manual_chrome_debug_command", + "kind": "moved-lazy", + "target": "hermes_cli.browser_connect" + }, + { + "facade": "cli", + "name": "print_config_warnings", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "cli", + "name": "prompt_for_secret", + "kind": "moved-lazy", + "target": "hermes_cli.callbacks" + }, + { + "facade": "cli", + "name": "rich_box", + "kind": "import", + "target": "rich" + }, + { + "facade": "cli", + "name": "set_friendly_tool_labels", + "kind": "moved-lazy", + "target": "agent.display" + }, + { + "facade": "cli", + "name": "set_tool_preview_max_len", + "kind": "moved-lazy", + "target": "agent.display" + }, + { + "facade": "cli", + "name": "setup_logging", + "kind": "moved-lazy", + "target": "hermes_logging" + }, + { + "facade": "cli", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "cron.jobs", + "name": "clear_drift_alerted", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "cron.scheduler", + "name": "BOT_CHAT_PLATFORM", + "kind": "moved-lazy", + "target": "cron.scheduler_delivery" + }, + { + "facade": "cron.scheduler", + "name": "SharedRouteAdapters", + "kind": "moved-lazy", + "target": "cron.scheduler_preflight" + }, + { + "facade": "cron.scheduler", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "cron.scheduler", + "name": "cron_delivery_targets", + "kind": "moved-lazy", + "target": "cron.scheduler_delivery" + }, + { + "facade": "cron.scheduler", + "name": "parse_bot_chat_deliver_token", + "kind": "moved-lazy", + "target": "cron.scheduler_delivery" + }, + { + "facade": "cron.scheduler", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "cron.scheduler", + "name": "signal", + "kind": "import", + "target": "signal" + }, + { + "facade": "cron.scheduler_provider", + "name": "provider_supports_fire_cancel", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.browser_control_artifacts", + "name": "ArtifactOverwrite", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.browser_control_broker", + "name": "BROWSER_CONTROL_ALL_CAPABILITIES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.config", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "gateway.delivery_ledger", + "name": "json", + "kind": "restored-import", + "target": "json" + }, + { + "facade": "gateway.delivery_ledger", + "name": "debug_rows", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.delivery_ledger", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "gateway.disk_status", + "name": "logger", + "kind": "moved-lazy", + "target": "gateway.run" + }, + { + "facade": "gateway.disk_status", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "gateway.hosted_room_discussion", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "gateway.hosted_room_driver", + "name": "Iterator", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.hosted_room_driver", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "gateway.hosted_room_driver", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "gateway.hosted_room_driver", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "gateway.hosted_room_execution_policy", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "gateway.hosted_room_execution_policy", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "gateway.hosted_room_peer", + "name": "RoomLinkProbe", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.hosted_room_peer", + "name": "_LINK_PRIORITY", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of select_room_link)" + }, + { + "facade": "gateway.hosted_room_peer", + "name": "RoomLinkProbe", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of select_room_link)" + }, + { + "facade": "gateway.hosted_room_peer", + "name": "select_room_link", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.hosted_room_peer", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "gateway.hosted_room_replicas", + "name": "MAX_EVENT_JSON_BYTES", + "kind": "moved-lazy", + "target": "gateway.hosted_rooms" + }, + { + "facade": "gateway.hosted_room_replicas", + "name": "MAX_ROOM_ID_CHARS", + "kind": "moved-lazy", + "target": "gateway.hosted_rooms" + }, + { + "facade": "gateway.hosted_room_replicas", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "gateway.hosted_room_replicas", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "gateway.hosted_rooms", + "name": "Iterator", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.hosted_rooms", + "name": "NoReturn", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.hosted_rooms", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "gateway.hosted_rooms", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "gateway.kanban_watchers", + "name": "Callable", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.kanban_watchers", + "name": "Context", + "kind": "import", + "target": "contextvars" + }, + { + "facade": "gateway.kanban_watchers", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "gateway.kanban_watchers", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "gateway.kanban_watchers", + "name": "sqlite3", + "kind": "import", + "target": "sqlite3" + }, + { + "facade": "gateway.kanban_watchers", + "name": "t", + "kind": "moved-lazy", + "target": "agent.i18n" + }, + { + "facade": "gateway.memory_status", + "name": "logger", + "kind": "moved-lazy", + "target": "gateway.run" + }, + { + "facade": "gateway.memory_status", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "gateway.platforms.helpers", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.platforms.helpers", + "name": "TYPE_CHECKING", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.platforms.helpers", + "name": "asyncio", + "kind": "restored-import", + "target": "asyncio" + }, + { + "facade": "gateway.platforms.helpers", + "name": "TextBatchAggregator", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.helpers", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "gateway.platforms.qqbot.adapter", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "gateway.platforms.qqbot.adapter", + "name": "mimetypes", + "kind": "import", + "target": "mimetypes" + }, + { + "facade": "gateway.platforms.qqbot.chunked_upload", + "name": "ApiRequestFn", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.qqbot.chunked_upload", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.platforms.qqbot.chunked_upload", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "logging", + "kind": "restored-import", + "target": "logging" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "Awaitable", + "kind": "restored-import", + "target": "typing" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "Callable", + "kind": "restored-import", + "target": "typing" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "logger", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of ApprovalSender)" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "PostMessageFn", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of ApprovalSender)" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "ApprovalSender", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "Awaitable", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "Callable", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "PostMessageFn", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "logger", + "kind": "moved-lazy", + "target": "gateway.platforms.qqbot.adapter" + }, + { + "facade": "gateway.platforms.qqbot.keyboards", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "gateway.platforms.signal", + "name": "DEFAULT_EXT_TO_MIME", + "kind": "moved-lazy", + "target": "gateway.platforms.media_cache" + }, + { + "facade": "gateway.platforms.signal", + "name": "TYPING_INTERVAL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.weixin", + "name": "MSG_TYPE_USER", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.weixin", + "name": "struct", + "kind": "import", + "target": "struct" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "AUTH_FAILED_CODES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "AUTH_RETRYABLE_CODES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "FileUrlHandler", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "GroupQueryService", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "REPLY_REF_TTL_S", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "get_active_adapter", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao", + "name": "send_yuanbao_direct", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao_media", + "name": "COS_USE_ACCELERATE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "DEBUG_MODE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "_encode_forward_multimedia", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of encode_forward_msg_data)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "_encode_forward_msg_content", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of encode_forward_msg_data)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "_encode_forward_msg", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of encode_forward_msg_data)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "encode_forward_msg_data", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "logger", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.platforms.yuanbao_proto", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "gateway.relay", + "name": "relay_bot_username", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.relay.adapter", + "name": "cast", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.relay.auth", + "name": "DELIVERY_SIG_HEADER", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.relay.auth", + "name": "DELIVERY_TS_HEADER", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.relay.auth", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.relay.auth", + "name": "Sequence", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.relay.auth", + "name": "_DEFAULT_MAX_SKEW_SECONDS", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of verify_delivery_signature)" + }, + { + "facade": "gateway.relay.auth", + "name": "_hmac_hex", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of verify_delivery_signature)" + }, + { + "facade": "gateway.relay.auth", + "name": "verify_signature", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of verify_delivery_signature)" + }, + { + "facade": "gateway.relay.auth", + "name": "_delivery_payload", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of verify_delivery_signature)" + }, + { + "facade": "gateway.relay.auth", + "name": "verify_delivery_signature", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.relay.auth", + "name": "verify_signature", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.relay.auth", + "name": "verify_token", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.response_filters", + "name": "SILENT_REPLY_TOKEN", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.restart_loop_guard", + "name": "is_restart_loop_tripped", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.run", + "name": "Awaitable", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.run", + "name": "Context", + "kind": "import", + "target": "contextvars" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_GATEWAY_POST_INTERRUPT_GRACE_TIMEOUT", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_HEARTBEAT_INTERVAL_S", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_LEASE_WAIT", + "kind": "moved-lazy", + "target": "gateway.turn_lease" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_LOOP_WATCHDOG_INTERVAL_S", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_LOOP_WATCHDOG_MAX_STRIKES", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "DEFAULT_LOOP_WATCHDOG_TIMEOUT_S", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "EphemeralReply", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.run", + "name": "GATEWAY_FATAL_CONFIG_EXIT_CODE", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "GATEWAY_SERVICE_RESTART_EXIT_CODE", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "SessionEntry", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "TranscriptReadError", + "kind": "moved-lazy", + "target": "gateway.session_transcript" + }, + { + "facade": "gateway.run", + "name": "TurnContext", + "kind": "moved-lazy", + "target": "gateway.turn_context" + }, + { + "facade": "gateway.run", + "name": "TurnLeaseTimeoutError", + "kind": "moved-lazy", + "target": "gateway.turn_lease" + }, + { + "facade": "gateway.run", + "name": "TurnRunner", + "kind": "moved-lazy", + "target": "gateway.run_turn_runner" + }, + { + "facade": "gateway.run", + "name": "Union", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.run", + "name": "arm_shutdown_watchdog", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "atomic_json_write", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "gateway.run", + "name": "base_url_hostname", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "gateway.run", + "name": "build_auto_tts_output_path", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.run", + "name": "build_channel_continuity_note", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "build_session_context", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "build_session_context_prompt", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "consume_detached_task_result", + "kind": "moved-lazy", + "target": "agent.async_utils" + }, + { + "facade": "gateway.run", + "name": "faulthandler", + "kind": "import", + "target": "faulthandler" + }, + { + "facade": "gateway.run", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "gateway.run", + "name": "inspect", + "kind": "import", + "target": "inspect" + }, + { + "facade": "gateway.run", + "name": "is_global_startup_conflict", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "is_shared_multi_user_session", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "is_truthy_value", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "gateway.run", + "name": "load_dotenv", + "kind": "import", + "target": "dotenv" + }, + { + "facade": "gateway.run", + "name": "looks_like_telegram_private_chat_id", + "kind": "moved-lazy", + "target": "gateway.delivery" + }, + { + "facade": "gateway.run", + "name": "loop_heartbeat_forever", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "merge_pending_message_event", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.run", + "name": "neutralize_untrusted_inline_text", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.run", + "name": "parse_cron_drain_timeout", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "parse_restart_after_turn_timeout", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "parse_restart_drain_timeout", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "parse_signal_interrupt_grace_timeout", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "project_compaction_message_for_display", + "kind": "moved-lazy", + "target": "agent.compaction_display" + }, + { + "facade": "gateway.run", + "name": "queue", + "kind": "import", + "target": "queue" + }, + { + "facade": "gateway.run", + "name": "repair_explicit_computer_use_media_paths", + "kind": "moved-lazy", + "target": "gateway.media_repair" + }, + { + "facade": "gateway.run", + "name": "resolve_cron_drain_budget", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "gateway.run", + "name": "resolve_delivery_transport", + "kind": "moved-lazy", + "target": "gateway.delivery" + }, + { + "facade": "gateway.run", + "name": "resolve_shutdown_watchdog_delay", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "start_loop_liveness_watchdog", + "kind": "moved-lazy", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "gateway.run", + "name": "t", + "kind": "moved-lazy", + "target": "agent.i18n" + }, + { + "facade": "gateway.run", + "name": "timedelta", + "kind": "import", + "target": "datetime" + }, + { + "facade": "gateway.run", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "gateway.run", + "name": "utf16_len", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.session", + "name": "SessionResetPolicy", + "kind": "moved-lazy", + "target": "gateway.config" + }, + { + "facade": "gateway.session", + "name": "TranscriptReadError", + "kind": "moved-lazy", + "target": "gateway.session_transcript" + }, + { + "facade": "gateway.session", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "gateway.session", + "name": "auto_continue_freshness_window", + "kind": "moved-lazy", + "target": "gateway.session_lifecycle" + }, + { + "facade": "gateway.session", + "name": "extract_api_content_sidecar", + "kind": "moved-lazy", + "target": "agent.turn_context" + }, + { + "facade": "gateway.session", + "name": "normalize_whatsapp_identifier", + "kind": "moved-lazy", + "target": "gateway.whatsapp_identity" + }, + { + "facade": "gateway.session", + "name": "replace", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "gateway.session", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "gateway.slash_access", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.slash_commands", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "gateway.slash_commands", + "name": "HISTORY_UNREADABLE", + "kind": "moved-lazy", + "target": "gateway.slash_commands_status" + }, + { + "facade": "gateway.slash_commands", + "name": "MessageType", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.slash_commands", + "name": "SessionSource", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.slash_commands", + "name": "base_url_host_matches", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "gateway.slash_commands", + "name": "build_session_key", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.slash_commands", + "name": "clear_model_endpoint_credentials", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "gateway.slash_commands", + "name": "extract_api_content_sidecar", + "kind": "moved-lazy", + "target": "agent.turn_context" + }, + { + "facade": "gateway.slash_commands", + "name": "fetch_account_usage", + "kind": "moved-lazy", + "target": "agent.account_usage" + }, + { + "facade": "gateway.slash_commands", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "gateway.slash_commands", + "name": "is_shared_multi_user_session", + "kind": "moved-lazy", + "target": "gateway.session" + }, + { + "facade": "gateway.slash_commands", + "name": "render_account_usage_lines", + "kind": "moved-lazy", + "target": "agent.account_usage" + }, + { + "facade": "gateway.status", + "name": "clear_planned_stop_marker", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.status", + "name": "is_gateway_running", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "gateway.sticker_cache", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "gateway.sticker_cache", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "gateway.stream_consumer", + "name": "MEDIA_TAG_CLEANUP_RE", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "gateway.stream_consumer", + "name": "escape_code_fences_for_display", + "kind": "moved-lazy", + "target": "gateway.stream_consumer_fences" + }, + { + "facade": "gateway.stream_dispatch", + "name": "ToolCallFinished", + "kind": "moved-lazy", + "target": "gateway.stream_events" + }, + { + "facade": "gateway.systemd_notify", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.agent_import", + "name": "backup_memory_file", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.agent_import", + "name": "default_source_dir", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.auth", + "name": "BaseHTTPRequestHandler", + "kind": "import", + "target": "http.server" + }, + { + "facade": "hermes_cli.auth", + "name": "CODEX_OAUTH_USER_AGENT", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "CODEX_QUOTA_PROBE_MIN_INTERVAL_SECONDS", + "kind": "moved-lazy", + "target": "hermes_cli.auth_codex" + }, + { + "facade": "hermes_cli.auth", + "name": "DEFAULT_SPOTIFY_REDIRECT_URI", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDS", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "HTTPServer", + "kind": "import", + "target": "http.server" + }, + { + "facade": "hermes_cli.auth", + "name": "MINIMAX_OAUTH_GRANT_TYPE", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "NOUS_INFERENCE_INVOKE_SCOPE", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "NOUS_SHARED_STORE_FILENAME", + "kind": "moved-lazy", + "target": "hermes_cli.auth_nous" + }, + { + "facade": "hermes_cli.auth", + "name": "OAUTH_OVER_SSH_DOCS_URL", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "QWEN_OAUTH_CLIENT_ID", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "QWEN_OAUTH_TOKEN_URL", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "SINGLE_USE_OAUTH_SINGLETON_FILES", + "kind": "moved-lazy", + "target": "hermes_cli.auth_oauth_grants" + }, + { + "facade": "hermes_cli.auth", + "name": "SPOTIFY_ACCESS_TOKEN_REFRESH_SKEW_SECONDS", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "SPOTIFY_DASHBOARD_URL", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "TYPE_CHECKING", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.auth", + "name": "XAI_OAUTH_DEVICE_CODE_URL", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "XAI_OAUTH_DISCOVERY_URL", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "XAI_OAUTH_ISSUER", + "kind": "moved-lazy", + "target": "hermes_cli.auth_constants" + }, + { + "facade": "hermes_cli.auth", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "hermes_cli.auth", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.auth", + "name": "parse_qs", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "hermes_cli.auth", + "name": "refresh_nous_oauth_pure", + "kind": "moved-lazy", + "target": "hermes_cli.auth_nous" + }, + { + "facade": "hermes_cli.auth", + "name": "ssl", + "kind": "import", + "target": "ssl" + }, + { + "facade": "hermes_cli.auth", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.auth", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "hermes_cli.auth", + "name": "urlencode", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "hermes_cli.backup", + "name": "copy_db_and_verify", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.browser_connect", + "name": "try_launch_chrome_debug", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.callbacks", + "name": "approval_callback", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.callbacks", + "name": "clarify_callback", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.checkpoints", + "name": "cmd_list", + "kind": "moved-lazy", + "target": "hermes_cli.plugins_cmd" + }, + { + "facade": "hermes_cli.checkpoints", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "hermes_cli.commands", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.commands", + "name": "Callable", + "kind": "import", + "target": "collections.abc" + }, + { + "facade": "hermes_cli.commands", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.commands", + "name": "Mapping", + "kind": "import", + "target": "collections.abc" + }, + { + "facade": "hermes_cli.commands", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.commands", + "name": "Sequence", + "kind": "import", + "target": "collections.abc" + }, + { + "facade": "hermes_cli.commands", + "name": "SlashCommandAutoSuggest", + "kind": "moved-lazy", + "target": "hermes_cli.commands_completion" + }, + { + "facade": "hermes_cli.commands", + "name": "SlashCommandCompleter", + "kind": "moved-lazy", + "target": "hermes_cli.commands_completion" + }, + { + "facade": "hermes_cli.commands", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.commands", + "name": "_requires_argument", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of discord_skill_commands)" + }, + { + "facade": "hermes_cli.commands", + "name": "_CMD_NAME_LIMIT", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of discord_skill_commands)" + }, + { + "facade": "hermes_cli.commands", + "name": "_clamp_command_names", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of discord_skill_commands)" + }, + { + "facade": "hermes_cli.commands", + "name": "_collect_gateway_skill_entries", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of discord_skill_commands)" + }, + { + "facade": "hermes_cli.commands", + "name": "discord_skill_commands", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.commands", + "name": "discord_skill_commands_by_category", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "hermes_cli.commands", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.commands", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.commands", + "name": "slack_app_manifest", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "slack_native_slashes", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "slack_subcommand_map", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.commands", + "name": "telegram_bot_commands", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "telegram_menu_commands", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "telegram_menu_max_commands", + "kind": "moved-lazy", + "target": "hermes_cli.commands_platforms" + }, + { + "facade": "hermes_cli.commands", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "hermes_cli.config", + "name": "normalize_route_base_url", + "kind": "moved-lazy", + "target": "hermes_cli.route_identity" + }, + { + "facade": "hermes_cli.config", + "name": "_install_method_project_root", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of stamp_install_method)" + }, + { + "facade": "hermes_cli.config", + "name": "stamp_install_method", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.console_engine", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "hermes_cli.curses_ui", + "name": "Protocol", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.dashboard_auth.audit", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.dashboard_auth.middleware", + "name": "DashboardAuthProvider", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth.base" + }, + { + "facade": "hermes_cli.dingtalk_auth", + "name": "logger", + "kind": "moved-lazy", + "target": "hermes_cli.auth" + }, + { + "facade": "hermes_cli.dingtalk_auth", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.doctor", + "name": "FTS_STORAGE_VERSION", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_cli.doctor", + "name": "OPENROUTER_MODELS_URL", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.doctor", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "hermes_cli.doctor", + "name": "STATE_DB_SIZE_WARN_BYTES", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_state" + }, + { + "facade": "hermes_cli.doctor", + "name": "agent_browser_runnable", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.doctor", + "name": "base_url_host_matches", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_certificates", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_platform" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_fail", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_macos_full_disk_access", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_platform" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_macos_tcc_anchor", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_platform" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_macos_tcc_grants", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_platform" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_ok", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor", + "name": "check_warn", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor", + "name": "collect_deprecated_config_keys", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_config" + }, + { + "facade": "hermes_cli.doctor", + "name": "collect_deprecated_env_vars", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_config" + }, + { + "facade": "hermes_cli.doctor", + "name": "collect_relay_plugin_cutover_findings", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_config" + }, + { + "facade": "hermes_cli.doctor", + "name": "describe_vercel_auth", + "kind": "moved-lazy", + "target": "hermes_cli.vercel_auth" + }, + { + "facade": "hermes_cli.doctor", + "name": "detect_install_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.doctor", + "name": "importlib", + "kind": "import", + "target": "importlib.util" + }, + { + "facade": "hermes_cli.doctor", + "name": "is_nix_install_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.doctor", + "name": "managed_scope_check", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_config" + }, + { + "facade": "hermes_cli.doctor", + "name": "recommended_update_command_for_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.doctor", + "name": "report_deprecated_config_and_env", + "kind": "moved-lazy", + "target": "hermes_cli.doctor_config" + }, + { + "facade": "hermes_cli.doctor", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.doctor", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.doctor_live", + "name": "ELEVENLABS_VOICES_URL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor_live", + "name": "FAL_MODELS_URL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor_live", + "name": "FIRECRAWL_HEALTH_URL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor_live", + "name": "GROQ_MODELS_URL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.doctor_live", + "name": "OPENAI_MODELS_URL", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.focus_view", + "name": "FOCUS_USAGE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.focus_view", + "name": "effective_tool_progress_mode", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.foreign_sessions", + "name": "list_claude_sessions", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.foreign_sessions", + "name": "list_codex_sessions", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.gateway", + "name": "DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT", + "kind": "moved-lazy", + "target": "gateway.restart" + }, + { + "facade": "hermes_cli.gateway", + "name": "print_systemd_linger_guidance", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.gitlock", + "name": "is_ancestor_of_head", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.heartbeat", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.journey", + "name": "cmd_journey", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.kanban", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DEFAULT_BUSY_TIMEOUT_MS", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_connect" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DEFAULT_LOG_BACKUP_COUNT", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DEFAULT_LOG_ROTATE_BYTES", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DEFAULT_SPAWN_FAILURE_LIMIT", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DERIVED_MAX_IN_PROGRESS_CEILING", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "DERIVED_MAX_IN_PROGRESS_FLOOR", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "KANBAN_TERMINAL_TIMEOUT_GRACE_SECONDS", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "KanbanDbCorruptError", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_connect" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "MEMORY_GUARD_MB_PER_WORKER", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "Mapping", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "RepairResult", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_connect" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "add_notify_sub", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "advance_notify_cursor", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "check_respawn_guard", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "claim_unseen_events_for_sub", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "configured_max_in_progress", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "connect", + "kind": "moved-lazy", + "target": "hermes_cli.projects_db" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "connect_closing", + "kind": "moved-lazy", + "target": "hermes_cli.projects_db" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "count_notify_subs", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "count_running_tasks", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "count_running_tasks_other_boards", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "derive_default_max_in_progress", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "detect_crashed_workers", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "detect_stale_running", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "dispatch_once", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "enforce_max_runtime", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "has_spawnable_ready", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "has_spawnable_review", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "heartbeat_worker", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "list_notify_subs", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "parent_results", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "purge_stale_done_notify_subs", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "random", + "kind": "import", + "target": "random" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "reap_worker_zombies", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "reconcile_orphaned_running", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "remove_notify_sub", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "repair_db", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_connect" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "resolve_max_in_progress", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "resolve_workspace", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_workspace" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "review_dispatch_enabled", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "rewind_notify_cursor", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "run_daemon", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "set_branch_name", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_workspace" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "set_workspace_path", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_workspace" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "unseen_events_for_sub", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_notify" + }, + { + "facade": "hermes_cli.kanban_db", + "name": "worker_log_rotation_config", + "kind": "moved-lazy", + "target": "hermes_cli.kanban_db_dispatch" + }, + { + "facade": "hermes_cli.kanban_decompose", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.kanban_decompose", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.kanban_diagnostics", + "name": "DIAGNOSTIC_KINDS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.local_runtime.binaries", + "name": "get_hermes_home", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.local_runtime.bootstrap", + "name": "get_hermes_home", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.local_runtime.capabilities", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.local_runtime.capabilities", + "name": "urllib", + "kind": "import", + "target": "urllib.request" + }, + { + "facade": "hermes_cli.local_runtime.catalog", + "name": "find_variant", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.local_runtime.catalog", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "hermes_cli.local_runtime.catalog", + "name": "recommended_id", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.local_runtime.hf_browse", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "hermes_cli.main", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.main", + "name": "line_input", + "kind": "moved-lazy", + "target": "hermes_cli.cli_output" + }, + { + "facade": "hermes_cli.main", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "hermes_cli.main", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "hermes_cli.main", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "hermes_cli.mcp_picker", + "name": "color", + "kind": "moved-lazy", + "target": "hermes_cli.colors" + }, + { + "facade": "hermes_cli.mcp_security", + "name": "is_mcp_server_entry_suspicious", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.middleware", + "name": "API_EXECUTION_MIDDLEWARE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.middleware", + "name": "API_REQUEST_MIDDLEWARE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.middleware", + "name": "apply_api_request_middleware", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.middleware", + "name": "run_api_execution_middleware", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.moa_config", + "name": "encode_moa_turn", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_moa_turn_prompt)" + }, + { + "facade": "hermes_cli.moa_config", + "name": "build_moa_turn_prompt", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.moa_config", + "name": "encode_moa_turn", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.moa_config", + "name": "list_moa_presets", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.moa_config", + "name": "set_active_moa_preset", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "BEDROCK_GEO_PREFIXES", + "kind": "moved-lazy", + "target": "hermes_cli.model_setup_flows_bedrock" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "bedrock_model_routable_from_region", + "kind": "moved-lazy", + "target": "hermes_cli.model_setup_flows_bedrock" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "bedrock_region_geo_prefix", + "kind": "moved-lazy", + "target": "hermes_cli.model_setup_flows_bedrock" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "custom_provider_slug", + "kind": "moved-lazy", + "target": "hermes_cli.providers" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "line_input", + "kind": "moved-lazy", + "target": "hermes_cli.cli_output" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.model_setup_flows", + "name": "urllib", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "hermes_cli.model_switch", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.model_switch", + "name": "base_url_host_matches", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.model_switch", + "name": "custom_provider_slug", + "kind": "moved-lazy", + "target": "hermes_cli.providers" + }, + { + "facade": "hermes_cli.model_switch", + "name": "http", + "kind": "import", + "target": "http.client" + }, + { + "facade": "hermes_cli.model_switch", + "name": "list_picker_providers", + "kind": "moved-lazy", + "target": "hermes_cli.model_switch_providers" + }, + { + "facade": "hermes_cli.model_switch", + "name": "prewarm_picker_cache_async", + "kind": "moved-lazy", + "target": "hermes_cli.model_switch_providers" + }, + { + "facade": "hermes_cli.model_switch", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "hermes_cli.models", + "name": "LMStudioLoadResult", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "NamedTuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.models", + "name": "PROVIDER_GROUPS", + "kind": "moved-lazy", + "target": "hermes_cli.models_catalog_static" + }, + { + "facade": "hermes_cli.models", + "name": "ProviderEntry", + "kind": "moved-lazy", + "target": "hermes_cli.models_catalog_static" + }, + { + "facade": "hermes_cli.models", + "name": "atomic_json_write", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.models", + "name": "base_url_host_matches", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.models", + "name": "compute_sale_discount", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "ensure_lmstudio_model_loaded", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "fetch_ai_gateway_pricing", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "fetch_lmstudio_models", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "fetch_models_with_pricing", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "fetch_ollama_local_models", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "get_cached_nous_inference_base_url", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "get_close_matches", + "kind": "import", + "target": "difflib" + }, + { + "facade": "hermes_cli.models", + "name": "get_pricing_for_provider", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "group_providers", + "kind": "moved-lazy", + "target": "hermes_cli.models_catalog_static" + }, + { + "facade": "hermes_cli.models", + "name": "http", + "kind": "import", + "target": "http.client" + }, + { + "facade": "hermes_cli.models", + "name": "is_nous_free_tier", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.models", + "name": "_OPENCODE_KEYLESS_EXTRA_SLUGS", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of is_opencode_zen_free_model)" + }, + { + "facade": "hermes_cli.models", + "name": "is_opencode_zen_free_model", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.models", + "name": "lmstudio_model_reasoning_options", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "nous_catalog_url", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "nous_model_reasoning_capabilities", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "nous_policy_allowed_ids", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "ollama_model_supports_thinking", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "openrouter_model_reasoning_capabilities", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "parse_openrouter_reasoning_capabilities", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "peek_cached_pricing", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "pricing_cache_scope", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "probe_lmstudio_models", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "probe_ollama_local_models", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "provider_group_for_slug", + "kind": "moved-lazy", + "target": "hermes_cli.models_catalog_static" + }, + { + "facade": "hermes_cli.models", + "name": "refresh_reasoning_caps_async", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "restrict_to_nous_policy", + "kind": "moved-lazy", + "target": "hermes_cli.models_pricing" + }, + { + "facade": "hermes_cli.models", + "name": "should_use_ollama_native_catalog", + "kind": "moved-lazy", + "target": "hermes_cli.models_local" + }, + { + "facade": "hermes_cli.models", + "name": "url_origin", + "kind": "moved-lazy", + "target": "hermes_cli.urllib_security" + }, + { + "facade": "hermes_cli.models", + "name": "validate_requested_model", + "kind": "moved-lazy", + "target": "hermes_cli.models_validate" + }, + { + "facade": "hermes_cli.models", + "name": "warm_nous_reasoning_caps_async", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.models", + "name": "warm_openrouter_reasoning_caps_async", + "kind": "moved-lazy", + "target": "hermes_cli.models_reasoning_caps" + }, + { + "facade": "hermes_cli.nous_billing", + "name": "BILLING_MANAGE_SCOPE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.nous_subscription", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "CLIENT_ACTIVE_MARK", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "MODEL_CALL_PROFILE_MODEL", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "SCHEMA_KEY", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "SCHEMA_VERSION", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "SKILL_LIFECYCLE_MARK", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "SKILL_LOAD_MARK", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "TOOL_APPROVAL_MARK", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "TOOL_CALL_SCOPE", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "model_call_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "prepare_session_start", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "skill_lifecycle_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "skill_load_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "task_start_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "task_terminal_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "task_terminal_state", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "tool_approval_outcome", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "tool_category", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.observability.relay_shared_metrics", + "name": "tool_terminal_fields", + "kind": "moved-lazy", + "target": "hermes_cli.observability.shared_metrics_contract" + }, + { + "facade": "hermes_cli.onepassword_secrets_cli", + "name": "Table", + "kind": "import", + "target": "rich.table" + }, + { + "facade": "hermes_cli.onepassword_secrets_cli", + "name": "masked_secret_prompt", + "kind": "moved-lazy", + "target": "hermes_cli.secret_prompt" + }, + { + "facade": "hermes_cli.onepassword_secrets_cli", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "hermes_cli.platform_actions", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.plugins", + "name": "CAPABILITY_REGISTRY", + "kind": "moved-lazy", + "target": "hermes_cli.plugin_capabilities" + }, + { + "facade": "hermes_cli.plugins", + "name": "ENTRY_POINT_CAPABILITIES_GROUP", + "kind": "moved-lazy", + "target": "hermes_cli.plugins_discovery" + }, + { + "facade": "hermes_cli.plugins", + "name": "Iterable", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.plugins", + "name": "LEGACY_RELAY_PLUGIN_KEYS", + "kind": "moved-lazy", + "target": "hermes_cli.relay_plugin_cutover" + }, + { + "facade": "hermes_cli.plugins", + "name": "MAX_SYSTEM_PROMPT_SECTIONS", + "kind": "moved-lazy", + "target": "hermes_cli.plugins_dispatch" + }, + { + "facade": "hermes_cli.plugins", + "name": "OBSERVER_SCHEMA_VERSION", + "kind": "moved-lazy", + "target": "hermes_cli.middleware" + }, + { + "facade": "hermes_cli.plugins", + "name": "Type", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.plugins", + "name": "VALID_CAPABILITY_IDS", + "kind": "moved-lazy", + "target": "hermes_cli.plugin_capabilities" + }, + { + "facade": "hermes_cli.plugins", + "name": "cfg_get", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.plugins", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "hermes_cli.plugins", + "name": "contextvars", + "kind": "import", + "target": "contextvars" + }, + { + "facade": "hermes_cli.plugins", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "hermes_cli.plugins", + "name": "fast_safe_load", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.plugins", + "name": "format_system_prompt_section", + "kind": "moved-lazy", + "target": "hermes_cli.plugins_dispatch" + }, + { + "facade": "hermes_cli.plugins", + "name": "get_plugin_subscriptions", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.plugins", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.plugins", + "name": "reset_hermes_home_override", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.plugins", + "name": "set_hermes_home_override", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.plugins", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "hermes_cli.plugins", + "name": "unload_plugins", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.plugins", + "name": "wraps", + "kind": "import", + "target": "functools" + }, + { + "facade": "hermes_cli.plugins_cmd", + "name": "importlib", + "kind": "import", + "target": "importlib.metadata" + }, + { + "facade": "hermes_cli.profile_describer", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.profile_distribution", + "name": "is_excluded_skill_path", + "kind": "moved-lazy", + "target": "agent.skill_utils" + }, + { + "facade": "hermes_cli.profiles", + "name": "has_bundled_skills_opt_out", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.runtime_provider", + "name": "custom_provider_aliases", + "kind": "moved-lazy", + "target": "hermes_cli.providers" + }, + { + "facade": "hermes_cli.runtime_provider", + "name": "custom_provider_slug", + "kind": "moved-lazy", + "target": "hermes_cli.providers" + }, + { + "facade": "hermes_cli.runtime_provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.security_advisories", + "name": "render_doctor_section", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.setup", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.setup", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.setup", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.setup", + "name": "get_nous_subscription_features", + "kind": "moved-lazy", + "target": "hermes_cli.nous_subscription" + }, + { + "facade": "hermes_cli.setup", + "name": "get_optional_skills_dir", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "hermes_cli.setup", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.setup", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "hermes_cli.setup", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.slack_cli", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.sqlite_safe_read", + "name": "SQLITE_HEADER_MAGIC", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.status", + "name": "format_nous_portal_entitlement_message", + "kind": "moved-lazy", + "target": "hermes_cli.nous_account" + }, + { + "facade": "hermes_cli.status", + "name": "get_nous_portal_account_info", + "kind": "moved-lazy", + "target": "hermes_cli.nous_account" + }, + { + "facade": "hermes_cli.status", + "name": "get_nous_subscription_features", + "kind": "moved-lazy", + "target": "hermes_cli.nous_subscription" + }, + { + "facade": "hermes_cli.status", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "hermes_cli.status", + "name": "redact_key", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.status", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "DEFAULT_MANAGER_BOT", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "auto_setup_telegram_bot", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "secrets", + "kind": "restored-import", + "target": "secrets" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "_USERNAME_SLUG_ALPHABET", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of generate_bot_username)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "generate_username_slug", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of generate_bot_username)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "generate_bot_username", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "urllib", + "kind": "restored-import", + "target": "urllib.parse" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "generate_deep_link", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "generate_pairing_nonce", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "generate_username_slug", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "poll_for_token", + "kind": "moved-lazy", + "target": "plugins.platforms.photon.auth" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "poll_pairing_once", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "secrets", + "kind": "import", + "target": "secrets" + }, + { + "facade": "hermes_cli.telegram_managed_bot", + "name": "urllib", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "hermes_cli.tools_config", + "name": "MANAGED_FEATURE_COVERAGE_CATEGORY", + "kind": "moved-lazy", + "target": "hermes_cli.nous_subscription" + }, + { + "facade": "hermes_cli.tools_config", + "name": "NOUS_MANAGED_PROVIDER", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "hermes_cli.tools_config", + "name": "base_url_hostname", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.tools_config", + "name": "fal_key_is_configured", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "hermes_cli.tools_config", + "name": "format_nous_portal_entitlement_message", + "kind": "moved-lazy", + "target": "hermes_cli.nous_account" + }, + { + "facade": "hermes_cli.tools_config", + "name": "is_truthy_value", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.tools_config", + "name": "save_env_value", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.tools_config", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.tools_config", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "hermes_cli.tools_config", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "hermes_cli.uninstall", + "name": "find_shell_configs", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.update_cmd", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.update_cmd", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "hermes_cli.update_cmd", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.update_cmd", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.update_inventory", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "hermes_cli.update_inventory", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.web_deps", + "name": "get_dashboard_health", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.web_deps", + "name": "get_session_token", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.web_deps", + "name": "has_valid_session_token", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.web_deps", + "name": "late_attr", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_cli.web_routers.cron", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.web_routers.mcp", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.web_routers.sessions", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.web_routers.sessions", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.web_routers.sessions", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.web_routers.skills", + "name": "LateState", + "kind": "moved-lazy", + "target": "hermes_cli.web_deps" + }, + { + "facade": "hermes_cli.web_routers.skills", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.web_routers.tools", + "name": "LateState", + "kind": "moved-lazy", + "target": "hermes_cli.web_deps" + }, + { + "facade": "hermes_cli.web_routers.tools", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "hermes_cli.web_server", + "name": "AudioTranscriptionRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "AutomationBlueprintInstantiate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "BackupRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "BulkDeleteSessions", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CONFIG_SCHEMA", + "kind": "moved-lazy", + "target": "hermes_cli.web_server_config" + }, + { + "facade": "hermes_cli.web_server", + "name": "ChatImageUpload", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ConfigUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CredentialPoolAdd", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CronJobCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CronJobUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CuratorPause", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "CustomEndpointUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "DEFAULT_CONFIG", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "DebugShareRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "EnvVarDelete", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "EnvVarReveal", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "EnvVarUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "FontSetBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "FsWriteText", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitBranchSwitchBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitCommitBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitFileBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitPathBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitWorktreeAddBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "GitWorktreeRemoveBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "HookCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "HookDelete", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ImportRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "LearningNodeEdit", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "LearningNodeRef", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.web_server", + "name": "Literal", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_cli.web_server", + "name": "MCPCatalogInstall", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MCPEnabledToggle", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MCPServerCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MCPServersReplace", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ManagedDirectoryCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ManagedFileDelete", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ManagedFileUpload", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ManagedFilesPolicy", + "kind": "moved-lazy", + "target": "hermes_cli.web_server_files" + }, + { + "facade": "hermes_cli.web_server", + "name": "MemoryProviderConfigUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MemoryProviderSelect", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MemoryProviderSetupRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MemoryReset", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MessagingPlatformUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MoaConfigPayload", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MoaModelSlot", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "MoaPresetPayload", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ModelAssignment", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "OAuthSubmitBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "OPTIONAL_ENV_VARS", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "PairingApprove", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "PairingRevoke", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileActiveUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileDescribeAuto", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileDescriptionUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileModelUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileRename", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProfileSoulUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProviderConfigSchema", + "kind": "moved-lazy", + "target": "plugins.memory.config_schema" + }, + { + "facade": "hermes_cli.web_server", + "name": "ProviderField", + "kind": "moved-lazy", + "target": "plugins.memory.config_schema" + }, + { + "facade": "hermes_cli.web_server", + "name": "PtyBridge", + "kind": "moved-lazy", + "target": "hermes_cli.pty_bridge" + }, + { + "facade": "hermes_cli.web_server", + "name": "PtySessionRegistry", + "kind": "moved-lazy", + "target": "hermes_cli.pty_session" + }, + { + "facade": "hermes_cli.web_server", + "name": "PtyUnavailableError", + "kind": "moved-lazy", + "target": "hermes_cli.pty_bridge" + }, + { + "facade": "hermes_cli.web_server", + "name": "RawConfigUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "RegistryFull", + "kind": "moved-lazy", + "target": "hermes_cli.pty_session" + }, + { + "facade": "hermes_cli.web_server", + "name": "STORAGE_HONCHO_HOST_BLOCK", + "kind": "moved-lazy", + "target": "plugins.memory.config_schema" + }, + { + "facade": "hermes_cli.web_server", + "name": "SessionImport", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SessionPrune", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SessionRename", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillContentUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillInstallRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillToggle", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillUninstallRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "SkillsUpdateRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "TTSLeaseRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "TTSSpeakRequest", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "TelegramOnboardingApply", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "TelegramOnboardingStart", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "TerminalBackendSelect", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ThemeSetBody", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ToolsetEnvUpdate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ToolsetModelSelect", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ToolsetPostSetup", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ToolsetProviderSelect", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "ToolsetToggle", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "WebhookCreate", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "WebhookEnabledToggle", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "WhatsAppOnboardingApply", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "WhatsAppOnboardingStart", + "kind": "moved-lazy", + "target": "hermes_cli.web_models" + }, + { + "facade": "hermes_cli.web_server", + "name": "activate_custom_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "add_credential_pool_entry", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "add_mcp_server", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "apply_telegram_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "apply_whatsapp_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "approve_pairing", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "atexit", + "kind": "import", + "target": "atexit" + }, + { + "facade": "hermes_cli.web_server", + "name": "auth_mcp_server", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "hermes_cli.web_server", + "name": "binascii", + "kind": "import", + "target": "binascii" + }, + { + "facade": "hermes_cli.web_server", + "name": "build_cron_model_impact", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "bulk_delete_sessions_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "cancel_oauth_session", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "cancel_telegram_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "cancel_whatsapp_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "cfg_get", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "check_config_version", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "check_hermes_update", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "clear_model_endpoint_credentials", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "clear_pending_pairing", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "coerce_provider_id", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "concurrent", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "hermes_cli.web_server", + "name": "console_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.chat_ws" + }, + { + "facade": "hermes_cli.web_server", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "hermes_cli.web_server", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "hermes_cli.web_server", + "name": "count_empty_sessions_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_hook", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_managed_directory", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_profile_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_skill", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "create_webhook", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "cron_fire_webhook", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "custom_endpoint_key_env", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "hermes_cli.web_server", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_agent_plugin", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_custom_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_empty_sessions_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_hook", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_learning_node", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_managed_file", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_profile_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_session_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "delete_webhook", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "derive_gateway_busy", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "derive_gateway_drainable", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "describe_profile_auto_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "detect_install_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "disconnect_oauth_provider", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "download_dashboard_backup", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "download_managed_file", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "enable_webhooks", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "env_var_enabled", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.web_server", + "name": "events_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.chat_ws" + }, + { + "facade": "hermes_cli.web_server", + "name": "export_session_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "find_provider_entry", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "format_docker_update_message", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_default_cwd", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_download", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_git_root", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_list", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_read_data_url", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_read_text", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "fs_write_text", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "hermes_cli.web_server", + "name": "gateway_drain", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "gateway_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.chat_ws" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_action_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_active_profile_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_auxiliary_models", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_client_voice_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.audio" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_computer_use_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_config_path", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_config_raw", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.analytics" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_cron_delivery_targets", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_curator_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_dashboard_font", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_dashboard_plugins", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_dashboard_themes", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_defaults", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_egress_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_elevenlabs_voices", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.audio" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_env_path", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_env_vars", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_health", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_hermes_home", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_learning_graph", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_learning_node", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_logs", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_media", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_memory_provider_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.memory_providers" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_memory_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_messaging_platforms", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_moa_models", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_model_info", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_model_options", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_models_analytics", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.analytics" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_plugins_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_portal_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_process_hermes_home", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_profile_setup_command", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_profile_soul", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_profiles_sessions", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_profiles_sessions_sidebar", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_provider_config_schema", + "kind": "moved-lazy", + "target": "plugins.memory.config_schema" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_recommended_default_model", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_running_pid", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_running_pid_cached", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_runtime_status_running_pid", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_schema", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_session_detail", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_session_latest_descendant", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_session_messages", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_session_stats", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_sessions", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_skill_content", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_skills", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_ssh_ownership", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_system_stats", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_telegram_onboarding_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_terminal_backends", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_toolset_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_toolset_models", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_toolsets", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_update_receipt", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_usage_analytics", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.analytics" + }, + { + "facade": "hermes_cli.web_server", + "name": "get_whatsapp_onboarding_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_base_branches_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_branch_switch_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_branches_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_commit_context_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_commit_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_create_pr_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_file_diff_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_push_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_rev_parse_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_revert_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_review_diff_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_review_list_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_ship_info_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_stage_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_status_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_unstage_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_worktree_add_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_worktree_remove_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "git_worktrees_route", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.git" + }, + { + "facade": "hermes_cli.web_server", + "name": "grant_computer_use_permissions", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "hermes_cli.web_server", + "name": "import_sessions_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "importlib", + "kind": "import", + "target": "importlib.util" + }, + { + "facade": "hermes_cli.web_server", + "name": "inspect", + "kind": "import", + "target": "inspect" + }, + { + "facade": "hermes_cli.web_server", + "name": "install_mcp_catalog_entry", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "install_skill_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "instantiate_blueprint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "ipaddress", + "kind": "import", + "target": "ipaddress" + }, + { + "facade": "hermes_cli.web_server", + "name": "is_nix_install_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_checkpoints", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_credential_pool", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_cron_blueprints", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_cron_job_runs", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_cron_jobs", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_custom_endpoints", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_hooks", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_managed_files", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_mcp_catalog", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_mcp_servers", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_oauth_providers", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_pairing", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_profiles_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_skills_hub_sources", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "list_webhooks", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "load_env", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "math", + "kind": "import", + "target": "math" + }, + { + "facade": "hermes_cli.web_server", + "name": "mcp_oauth_callback", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "mcp_oauth_flow_status", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "mimetypes", + "kind": "import", + "target": "mimetypes" + }, + { + "facade": "hermes_cli.web_server", + "name": "normalize_updated_at", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "open_profile_terminal_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "parse_active_agents", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "pause_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "poll_oauth_session", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "post_agent_plugin_disable", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "post_agent_plugin_enable", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "post_agent_plugin_install", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "post_agent_plugin_update", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "post_plugin_visibility", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "preview_skill_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "prune_checkpoints", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "prune_sessions_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "pty_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.chat_ws" + }, + { + "facade": "hermes_cli.web_server", + "name": "pub_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.chat_ws" + }, + { + "facade": "hermes_cli.web_server", + "name": "put_plugin_providers", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "queue", + "kind": "import", + "target": "queue" + }, + { + "facade": "hermes_cli.web_server", + "name": "read_managed_file", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "read_raw_config", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "read_runtime_status", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "recommended_update_command_for_method", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "redact_key", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "remove_credential_pool_entry", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "remove_env_value", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "remove_env_var", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "remove_mcp_server", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "rename_profile_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "rename_session_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "replace_mcp_servers", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "rescan_dashboard_plugins", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "reset_memory", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "resolve_cron_model_drift_defaults", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "resolve_gateway_liveness", + "kind": "moved-lazy", + "target": "gateway.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "restart_gateway", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "resume_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "reveal_env_var", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "revoke_pairing", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_backup", + "kind": "moved-lazy", + "target": "hermes_cli.backup" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_config_migrate", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_curator", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_debug_share_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_doctor", + "kind": "moved-lazy", + "target": "hermes_cli.doctor" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_dump", + "kind": "moved-lazy", + "target": "hermes_cli.dump" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_import", + "kind": "moved-lazy", + "target": "hermes_cli.backup" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_import_upload", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_prompt_size", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_security_audit", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "run_toolset_post_setup", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "save_config", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "save_env_value", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "save_toolset_env", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "scan_skill_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "search_sessions", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.sessions" + }, + { + "facade": "hermes_cli.web_server", + "name": "search_skills_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "select_terminal_backend", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "select_toolset_model", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "select_toolset_provider", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "serve_plugin_asset", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_active_profile_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_curator_paused", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_dashboard_font", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_dashboard_theme", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.dashboard_ui" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_env_var", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_mcp_server_enabled", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_memory_provider", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_moa_models", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_model_assignment", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.models" + }, + { + "facade": "hermes_cli.web_server", + "name": "set_webhook_enabled", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "setup_memory_provider", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.memory_providers" + }, + { + "facade": "hermes_cli.web_server", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "hermes_cli.web_server", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "hermes_cli.web_server", + "name": "speak_stream_ws", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.audio" + }, + { + "facade": "hermes_cli.web_server", + "name": "speak_text", + "kind": "moved-lazy", + "target": "hermes_cli.voice" + }, + { + "facade": "hermes_cli.web_server", + "name": "start_gateway", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "start_oauth_login", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "start_telegram_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "start_whatsapp_onboarding", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "hermes_cli.web_server", + "name": "stop_gateway", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.ops" + }, + { + "facade": "hermes_cli.web_server", + "name": "stream_managed_file", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "submit_oauth_code", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.oauth" + }, + { + "facade": "hermes_cli.web_server", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "hermes_cli.web_server", + "name": "test_mcp_server", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.mcp" + }, + { + "facade": "hermes_cli.web_server", + "name": "test_messaging_platform", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "hermes_cli.web_server", + "name": "toggle_skill", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "toggle_toolset", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.tools" + }, + { + "facade": "hermes_cli.web_server", + "name": "transcribe_audio_upload", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.audio" + }, + { + "facade": "hermes_cli.web_server", + "name": "trigger_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "tts_lease", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.audio" + }, + { + "facade": "hermes_cli.web_server", + "name": "uninstall_skill_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_config_raw", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.analytics" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_cron_job", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.cron" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_hermes", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.actions" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_learning_node", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.status" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_memory_provider_config", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.memory_providers" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_messaging_platform", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.messaging" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_profile_description_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_profile_model_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_profile_soul", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.profiles" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_skill_content", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "update_skills_hub", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.skills" + }, + { + "facade": "hermes_cli.web_server", + "name": "upload_chat_image", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "upload_managed_file", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "upload_managed_file_stream", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.files" + }, + { + "facade": "hermes_cli.web_server", + "name": "upsert_custom_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "validate_custom_endpoint", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "validate_provider_credential", + "kind": "moved-lazy", + "target": "hermes_cli.web_routers.config_env" + }, + { + "facade": "hermes_cli.web_server", + "name": "windows_detach_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "hermes_cli.web_server", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "hermes_cli.web_server", + "name": "write_platform_config_field", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "hermes_cli.web_server", + "name": "yaml", + "kind": "import", + "target": "yaml" + }, + { + "facade": "hermes_cli.web_server", + "name": "zipfile", + "kind": "import", + "target": "zipfile" + }, + { + "facade": "hermes_cli.webhook", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "hermes_cli.webhook", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_cli.webhook", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "hermes_cli.win_pty_bridge", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "hermes_logging", + "name": "rotating_file_handlers", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state", + "name": "AUTO_VACUUM_MIN_FREELIST_RATIO", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "ActivityProvenance", + "kind": "moved-lazy", + "target": "agent.session_activity" + }, + { + "facade": "hermes_state", + "name": "CompressionSessionBusyError", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "CompressionSessionClosedError", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "DEFERRED_INDEX_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_CJK_STALE_KEY", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_CJK_TABLE_SQL", + "kind": "moved-lazy", + "target": "hermes_state_fts" + }, + { + "facade": "hermes_state", + "name": "FTS_CJK_TRIGGER_SQL", + "kind": "moved-lazy", + "target": "hermes_state_fts" + }, + { + "facade": "hermes_state", + "name": "FTS_REBUILD_DEFERRAL_KEY", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_STALE_KEY", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_STORAGE_VERSION", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "FTS_TRIGRAM_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "LEGACY_FTS_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "LEGACY_FTS_TRIGRAM_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "MAX_FTS5_QUERY_CHARS", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "MAX_SAFE_EXPORT_MESSAGES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state", + "name": "MAX_SAFE_RESUME_MESSAGES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state", + "name": "PERSISTENCE_ERROR_CAUSES", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "SCHEMA_SQL", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "SCHEMA_VERSION", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "SESSION_STATUS_COMPLETE", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state", + "name": "SESSION_STATUS_EMPTY", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state", + "name": "SESSION_STATUS_ERROR", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state", + "name": "SESSION_STATUS_INTERRUPTED", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state", + "name": "SKILL_EXCERPT_JOINT", + "kind": "moved-lazy", + "target": "agent.skill_commands" + }, + { + "facade": "hermes_state", + "name": "SKILL_SCAFFOLD_SQL_LIKE", + "kind": "moved-lazy", + "target": "agent.skill_commands" + }, + { + "facade": "hermes_state", + "name": "SessionTurnLeaseLostError", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "Set", + "kind": "import", + "target": "typing" + }, + { + "facade": "hermes_state", + "name": "WalUnsupportedError", + "kind": "moved-lazy", + "target": "hermes_state_wal" + }, + { + "facade": "hermes_state", + "name": "apply_durability_barriers", + "kind": "moved-lazy", + "target": "hermes_state_repair" + }, + { + "facade": "hermes_state", + "name": "classify_session_status", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state", + "name": "collect_state_db_stats", + "kind": "moved-lazy", + "target": "hermes_state_dbfile" + }, + { + "facade": "hermes_state", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "hermes_state", + "name": "count_db_holders", + "kind": "moved-lazy", + "target": "hermes_state_dbfile" + }, + { + "facade": "hermes_state", + "name": "describe_skill_invocation", + "kind": "moved-lazy", + "target": "agent.skill_commands" + }, + { + "facade": "hermes_state", + "name": "errno", + "kind": "import", + "target": "errno" + }, + { + "facade": "hermes_state", + "name": "fts5_cjk_so_path", + "kind": "moved-lazy", + "target": "hermes_state_fts" + }, + { + "facade": "hermes_state", + "name": "is_advisory_lock_contention", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "is_automatic_end_reason", + "kind": "moved-lazy", + "target": "hermes_state_common" + }, + { + "facade": "hermes_state", + "name": "is_disk_full_error", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "is_sqlite_wal_reset_vulnerable", + "kind": "moved-lazy", + "target": "hermes_state_wal" + }, + { + "facade": "hermes_state", + "name": "is_transient_sqlite_error", + "kind": "moved-lazy", + "target": "hermes_state_errors" + }, + { + "facade": "hermes_state", + "name": "iter_deleted_sqlite_sidecar_holders", + "kind": "moved-lazy", + "target": "hermes_state_dbfile" + }, + { + "facade": "hermes_state", + "name": "release_or_close", + "kind": "moved-lazy", + "target": "hermes_state_registry" + }, + { + "facade": "hermes_state", + "name": "report_startup_progress", + "kind": "moved-lazy", + "target": "hermes_startup_watchdog" + }, + { + "facade": "hermes_state", + "name": "resolve_journal_mode", + "kind": "moved-lazy", + "target": "hermes_state_wal" + }, + { + "facade": "hermes_state", + "name": "resolve_synchronous_level", + "kind": "moved-lazy", + "target": "hermes_state_wal" + }, + { + "facade": "hermes_state", + "name": "sanitize_context", + "kind": "moved-lazy", + "target": "agent.memory_manager" + }, + { + "facade": "hermes_state", + "name": "sqlite_source_id", + "kind": "moved-lazy", + "target": "hermes_state_wal" + }, + { + "facade": "hermes_state", + "name": "struct", + "kind": "import", + "target": "struct" + }, + { + "facade": "hermes_state", + "name": "weakref", + "kind": "import", + "target": "weakref" + }, + { + "facade": "hermes_state", + "name": "workspace_key", + "kind": "moved-lazy", + "target": "hermes_state_sessions" + }, + { + "facade": "hermes_state_registry", + "name": "close_shared_session_dbs", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state_registry", + "name": "get_shared_session_db", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state_registry", + "name": "release_shared_session_db", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "hermes_state_search", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "hermes_state_search", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.browser.browser_use.provider", + "name": "BrowserProvider", + "kind": "moved-lazy", + "target": "agent.browser_provider" + }, + { + "facade": "plugins.browser.browser_use.provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.browser.browserbase.provider", + "name": "BrowserProvider", + "kind": "moved-lazy", + "target": "agent.browser_provider" + }, + { + "facade": "plugins.browser.browserbase.provider", + "name": "requests", + "kind": "import", + "target": "requests" + }, + { + "facade": "plugins.browser.browserbase.provider", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "plugins.browser.firecrawl.provider", + "name": "BrowserProvider", + "kind": "moved-lazy", + "target": "agent.browser_provider" + }, + { + "facade": "plugins.browser.firecrawl.provider", + "name": "requests", + "kind": "import", + "target": "requests" + }, + { + "facade": "plugins.browser.firecrawl.provider", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "plugins.context_engine", + "name": "importlib", + "kind": "import", + "target": "importlib.util" + }, + { + "facade": "plugins.context_engine", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "plugins.cron_providers", + "name": "importlib", + "kind": "import", + "target": "importlib.util" + }, + { + "facade": "plugins.cron_providers", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "plugins.cron_providers.chronos", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.cron_providers.chronos._nas_client", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.dashboard_auth.basic", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.dashboard_auth.basic", + "name": "LoginStart", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.drain", + "name": "LoginStart", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "DashboardAuthProvider", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "InvalidCodeError", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "RefreshExpiredError", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "classify_jwks_lookup_error", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "httpx", + "kind": "import", + "target": "httpx" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "secrets", + "kind": "import", + "target": "secrets" + }, + { + "facade": "plugins.dashboard_auth.nous", + "name": "urllib", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "DashboardAuthProvider", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "InvalidCodeError", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "RefreshExpiredError", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "classify_jwks_lookup_error", + "kind": "moved-lazy", + "target": "hermes_cli.dashboard_auth" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.dashboard_auth.self_hosted", + "name": "secrets", + "kind": "import", + "target": "secrets" + }, + { + "facade": "plugins.google_meet.audio_bridge", + "name": "chrome_fake_audio_flags", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.google_meet.meet_bot", + "name": "SAY_PCM_FILENAME", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.google_meet.meet_bot", + "name": "SAY_QUEUE_FILENAME", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.google_meet.meet_bot", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "plugins.google_meet.node.cli", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.google_meet.node.registry", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "plugins.image_gen.deepinfra", + "name": "ImageGenProvider", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.deepinfra", + "name": "error_response", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.deepinfra", + "name": "save_b64_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.deepinfra", + "name": "save_url_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.fal", + "name": "ImageGenProvider", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.fal", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.image_gen.krea", + "name": "ImageGenProvider", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.krea", + "name": "error_response", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.krea", + "name": "normalize_reference_images", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.krea", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.image_gen.openai", + "name": "ImageGenProvider", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.openai", + "name": "error_response", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.openai", + "name": "normalize_reference_images", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.openai", + "name": "save_b64_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.openai", + "name": "save_url_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.xai", + "name": "ImageGenProvider", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.xai", + "name": "error_response", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.xai", + "name": "normalize_reference_images", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.xai", + "name": "save_b64_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.image_gen.xai", + "name": "save_url_image", + "kind": "moved-lazy", + "target": "agent.image_gen_provider" + }, + { + "facade": "plugins.memory.hindsight", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "plugins.memory.hindsight", + "name": "importlib", + "kind": "import", + "target": "importlib" + }, + { + "facade": "plugins.memory.honcho", + "name": "CONCLUDE_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.honcho", + "name": "CONTEXT_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.honcho", + "name": "PROFILE_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.honcho", + "name": "REASONING_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.honcho", + "name": "SEARCH_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.honcho", + "name": "TRIVIAL_PROMPT_RE", + "kind": "moved-lazy", + "target": "agent.memory_provider" + }, + { + "facade": "plugins.memory.honcho.client", + "name": "SingletonSlot", + "kind": "moved-lazy", + "target": "plugins.plugin_utils" + }, + { + "facade": "plugins.memory.honcho.oauth", + "name": "Callable", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.memory.honcho.session", + "name": "Callable", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.memory.honcho.session", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "plugins.memory.honcho.session", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "plugins.memory.honcho.session", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "plugins.memory.mem0", + "name": "ADD_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.mem0", + "name": "DELETE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.mem0", + "name": "SEARCH_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.mem0", + "name": "UPDATE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.mem0._setup", + "name": "has_oss_flags", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "CONTEXT_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.retaindb", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FILE_DELETE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FILE_INGEST_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FILE_LIST_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FILE_READ_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FILE_UPLOAD_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.memory.retaindb", + "name": "FORGET_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.openviking" + }, + { + "facade": "plugins.memory.retaindb", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.memory.retaindb", + "name": "PROFILE_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.retaindb", + "name": "REMEMBER_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.openviking" + }, + { + "facade": "plugins.memory.retaindb", + "name": "SEARCH_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.supermemory", + "name": "FORGET_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.openviking" + }, + { + "facade": "plugins.memory.supermemory", + "name": "PROFILE_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.supermemory", + "name": "SEARCH_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.memory.honcho.tool_schemas" + }, + { + "facade": "plugins.memory.supermemory", + "name": "STORE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "ERR_PUSH_NOT_SUPPORTED", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "STATE_AUTH_REQUIRED", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "data_part", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "file_part", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "message_with_parts", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.protocol", + "name": "stream_message", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "authenticate", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "get_bearer_token", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "get_peer_tokens", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "get_push_secret", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "get_trusted_peers", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "is_trusted_peer", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "resolve_bind_host", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.security", + "name": "sign_push_payload", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.a2a.tools", + "name": "TypedDict", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.platforms.dingtalk.adapter", + "name": "DINGTALK_TYPE_MAPPING", + "kind": "moved-lazy", + "target": "plugins.platforms.dingtalk.inbound" + }, + { + "facade": "plugins.platforms.dingtalk.adapter", + "name": "EXT_MAP", + "kind": "moved-lazy", + "target": "plugins.platforms.dingtalk.inbound" + }, + { + "facade": "plugins.platforms.dingtalk.adapter", + "name": "MessageType", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "plugins.platforms.discord.adapter", + "name": "env_int", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "plugins.platforms.feishu.feishu_comment", + "name": "add_comment_reaction", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.feishu.feishu_comment", + "name": "delete_comment_reaction", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.google_chat.oauth", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "plugins.platforms.google_chat.oauth", + "name": "secrets", + "kind": "import", + "target": "secrets" + }, + { + "facade": "plugins.platforms.google_chat.oauth", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "plugins.platforms.irc.adapter", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.platforms.line.adapter", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "plugins.platforms.matrix.adapter", + "name": "MAX_MESSAGE_LENGTH", + "kind": "moved-lazy", + "target": "gateway.platforms.signal" + }, + { + "facade": "plugins.platforms.matrix.adapter", + "name": "_MATRIX_CAPABILITIES", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of get_matrix_capabilities)" + }, + { + "facade": "plugins.platforms.matrix.adapter", + "name": "get_matrix_capabilities", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.ntfy.adapter", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.platforms.photon.adapter", + "name": "ProcessingOutcome", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "plugins.platforms.photon.adapter", + "name": "resolve_sidecar_dir", + "kind": "moved-lazy", + "target": "plugins.platforms.photon.sidecar_paths" + }, + { + "facade": "plugins.platforms.photon.auth", + "name": "credential_summary", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.photon.auth", + "name": "get_session", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.photon.cli", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "plugins.platforms.photon.cli", + "name": "resolve_sidecar_dir", + "kind": "moved-lazy", + "target": "plugins.platforms.photon.sidecar_paths" + }, + { + "facade": "plugins.platforms.raft.adapter", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "plugins.platforms.teams.adapter", + "name": "TeamsSummaryWriter", + "kind": "moved-lazy", + "target": "plugins.platforms.teams.summary_writer" + }, + { + "facade": "plugins.platforms.teams.adapter", + "name": "html", + "kind": "import", + "target": "html" + }, + { + "facade": "plugins.platforms.teams.adapter", + "name": "quote", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "plugins.platforms.telegram.adapter", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "plugins.platforms.telegram.adapter", + "name": "cache_document_from_bytes", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "plugins.platforms.telegram.adapter", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "plugins.platforms.telegram.telegram_ids", + "name": "telegram_chat_id_key", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "ABSOLUTE_MAX_BYTES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "APP_CMD_UPLOAD_MEDIA_CHUNK", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "APP_CMD_UPLOAD_MEDIA_FINISH", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "APP_CMD_UPLOAD_MEDIA_INIT", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "FILE_MAX_BYTES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "IMAGE_MAX_BYTES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "MAX_INTERMEDIATE_FRAMES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "MAX_UPLOAD_CHUNKS", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "ReplyFrame", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "STREAM_EXPIRED_ERRCODE", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "STREAM_REQUEST_EXPIRED_ERRCODE", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "STREAM_VERSION_CONFLICT_ERRCODE", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "UPLOAD_CHUNK_SIZE", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "VIDEO_MAX_BYTES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "VOICE_MAX_BYTES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "VOICE_SUPPORTED_MIMES", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.media" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "WeComStreamExpiredError", + "kind": "moved-lazy", + "target": "plugins.platforms.wecom.streaming" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "cache_document_from_bytes_async", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "cache_image_from_bytes_async", + "kind": "moved-lazy", + "target": "gateway.platforms.base" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "deque", + "kind": "import", + "target": "collections" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "mimetypes", + "kind": "import", + "target": "mimetypes" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "unquote", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "plugins.platforms.wecom.adapter", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_ALBUMS_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_DEVICES_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_LIBRARY_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_PLAYBACK_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_PLAYLISTS_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_QUEUE_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify", + "name": "SPOTIFY_SEARCH_SCHEMA", + "kind": "moved-lazy", + "target": "plugins.spotify.tools" + }, + { + "facade": "plugins.spotify.client", + "name": "compact_json", + "kind": "moved-lazy", + "target": "gateway.hosted_rooms_common" + }, + { + "facade": "plugins.spotify.client", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "plugins.spotify.tools", + "name": "SpotifyAPIError", + "kind": "moved-lazy", + "target": "plugins.spotify.client" + }, + { + "facade": "plugins.spotify.tools", + "name": "SpotifyAuthRequiredError", + "kind": "moved-lazy", + "target": "plugins.spotify.client" + }, + { + "facade": "plugins.teams_pipeline.cli", + "name": "GraphSubscription", + "kind": "moved-lazy", + "target": "plugins.teams_pipeline.models" + }, + { + "facade": "plugins.teams_pipeline.cli", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "plugins.teams_pipeline.cli", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "plugins.teams_pipeline.cli", + "name": "timedelta", + "kind": "import", + "target": "datetime" + }, + { + "facade": "plugins.teams_pipeline.cli", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "plugins.teams_pipeline.meetings", + "name": "fetch_call_record_artifact", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.teams_pipeline.pipeline", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.teams_pipeline.subscriptions", + "name": "resolve_teams_pipeline_store_path", + "kind": "restored-import", + "target": "plugins.teams_pipeline.store" + }, + { + "facade": "plugins.teams_pipeline.subscriptions", + "name": "resolve_store_path", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_store)" + }, + { + "facade": "plugins.teams_pipeline.subscriptions", + "name": "build_store", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.teams_pipeline.subscriptions", + "name": "resolve_store_path", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.teams_pipeline.subscriptions", + "name": "resolve_teams_pipeline_store_path", + "kind": "moved-lazy", + "target": "plugins.teams_pipeline.store" + }, + { + "facade": "plugins.video_gen.fal", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.video_gen.xai", + "name": "_run_xai_video_coroutine", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of run_xai_video_generation)" + }, + { + "facade": "plugins.video_gen.xai", + "name": "run_xai_video_generation", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "plugins.web.brave_free.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.brave_free.provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.web.ddgs.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.exa.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.exa.provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.web.firecrawl.provider", + "name": "NoReturn", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.web.firecrawl.provider", + "name": "TYPE_CHECKING", + "kind": "import", + "target": "typing" + }, + { + "facade": "plugins.web.firecrawl.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.firecrawl.provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.web.keenable.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.parallel.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.searxng.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.searxng.provider", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "plugins.web.tavily.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "plugins.web.xai.provider", + "name": "WebSearchProvider", + "kind": "moved-lazy", + "target": "agent.web_search_provider" + }, + { + "facade": "providers", + "name": "OMIT_TEMPERATURE", + "kind": "moved-lazy", + "target": "providers.base" + }, + { + "facade": "run_agent", + "name": "COMPRESSED_SUMMARY_METADATA_KEY", + "kind": "moved-lazy", + "target": "agent.context_compressor" + }, + { + "facade": "run_agent", + "name": "ContextCompressor", + "kind": "moved-lazy", + "target": "agent.context_compressor" + }, + { + "facade": "run_agent", + "name": "DEFAULT_AGENT_IDENTITY", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "run_agent", + "name": "FailoverReason", + "kind": "moved-lazy", + "target": "agent.error_classifier" + }, + { + "facade": "run_agent", + "name": "OpenAI", + "kind": "moved-lazy", + "target": "agent.process_bootstrap" + }, + { + "facade": "run_agent", + "name": "SimpleNamespace", + "kind": "import", + "target": "types" + }, + { + "facade": "run_agent", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "run_agent", + "name": "atomic_json_write", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "run_agent", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "run_agent", + "name": "build_context_files_prompt", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "run_agent", + "name": "build_environment_hints", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "run_agent", + "name": "build_skills_system_prompt", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "run_agent", + "name": "check_toolset_requirements", + "kind": "moved-lazy", + "target": "model_tools" + }, + { + "facade": "run_agent", + "name": "convert_scratchpad_to_think", + "kind": "moved-lazy", + "target": "agent.trajectory" + }, + { + "facade": "run_agent", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "run_agent", + "name": "estimate_request_tokens_rough", + "kind": "moved-lazy", + "target": "agent.model_metadata" + }, + { + "facade": "run_agent", + "name": "file_mutation_result_landed", + "kind": "moved-lazy", + "target": "agent.tool_result_classification" + }, + { + "facade": "run_agent", + "name": "flatten_message_text", + "kind": "moved-lazy", + "target": "agent.message_content" + }, + { + "facade": "run_agent", + "name": "get_tool_definitions", + "kind": "moved-lazy", + "target": "model_tools" + }, + { + "facade": "run_agent", + "name": "handle_function_call", + "kind": "moved-lazy", + "target": "model_tools" + }, + { + "facade": "run_agent", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "run_agent", + "name": "is_truthy_value", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "run_agent", + "name": "jittered_backoff", + "kind": "moved-lazy", + "target": "agent.retry_utils" + }, + { + "facade": "run_agent", + "name": "load_soul_md", + "kind": "moved-lazy", + "target": "agent.prompt_builder" + }, + { + "facade": "run_agent", + "name": "normalize_usage", + "kind": "moved-lazy", + "target": "agent.usage_pricing" + }, + { + "facade": "run_agent", + "name": "redact_sensitive_text", + "kind": "moved-lazy", + "target": "agent.redact" + }, + { + "facade": "run_agent", + "name": "request_hard_interrupt", + "kind": "moved-lazy", + "target": "agent.interrupt_compat" + }, + { + "facade": "run_agent", + "name": "sanitize_context", + "kind": "moved-lazy", + "target": "agent.memory_manager" + }, + { + "facade": "run_agent", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "run_agent", + "name": "user_originated_turn_view", + "kind": "moved-lazy", + "target": "agent.context_compressor" + }, + { + "facade": "tools.apply_layout_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.approval", + "name": "DANGEROUS_PATTERNS", + "kind": "moved-lazy", + "target": "tools.approval_detection" + }, + { + "facade": "tools.approval", + "name": "DANGEROUS_PATTERNS_COMPILED", + "kind": "moved-lazy", + "target": "tools.approval_detection" + }, + { + "facade": "tools.approval", + "name": "HARDLINE_PATTERNS", + "kind": "moved-lazy", + "target": "tools.approval_detection" + }, + { + "facade": "tools.approval", + "name": "HARDLINE_PATTERNS_COMPILED", + "kind": "moved-lazy", + "target": "tools.approval_detection" + }, + { + "facade": "tools.approval", + "name": "HUMAN_WAIT_MARGIN_S", + "kind": "moved-lazy", + "target": "tools.approval_human_wait" + }, + { + "facade": "tools.approval", + "name": "cfg_get", + "kind": "moved-lazy", + "target": "hermes_cli.config" + }, + { + "facade": "tools.approval", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.approval", + "name": "contextvars", + "kind": "import", + "target": "contextvars" + }, + { + "facade": "tools.approval", + "name": "fnmatch", + "kind": "import", + "target": "fnmatch" + }, + { + "facade": "tools.approval", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "tools.approval", + "name": "get_plugin_manager", + "kind": "moved-lazy", + "target": "tools.approval_prompt" + }, + { + "facade": "tools.approval", + "name": "human_wait_ceiling", + "kind": "moved-lazy", + "target": "tools.approval_human_wait" + }, + { + "facade": "tools.approval", + "name": "human_wait_seconds", + "kind": "moved-lazy", + "target": "tools.approval_human_wait" + }, + { + "facade": "tools.approval", + "name": "human_wait_window", + "kind": "moved-lazy", + "target": "tools.approval_human_wait" + }, + { + "facade": "tools.approval", + "name": "is_interrupted", + "kind": "moved-lazy", + "target": "tools.interrupt" + }, + { + "facade": "tools.approval", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.approval", + "name": "request_elicitation_consent", + "kind": "moved-lazy", + "target": "tools.approval_prompt" + }, + { + "facade": "tools.approval", + "name": "reset_current_observability_context", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "reset_current_session_key", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "reset_hermes_interactive_context", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "set_current_observability_context", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "set_current_session_key", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "set_hermes_interactive_context", + "kind": "moved-lazy", + "target": "tools.approval_context" + }, + { + "facade": "tools.approval", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "tools.approval", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.approval", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "tools.approval", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.approval", + "name": "unicodedata", + "kind": "import", + "target": "unicodedata" + }, + { + "facade": "tools.approval", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "tools.async_delegation", + "name": "active_for_session", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_camofox_state", + "name": "CAMOFOX_STATE_DIR_NAME", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_camofox_state", + "name": "CAMOFOX_STATE_SUBDIR", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_dialog_tool", + "name": "logger", + "kind": "moved-lazy", + "target": "tools.approval" + }, + { + "facade": "tools.browser_dialog_tool", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "tools.browser_supervisor", + "name": "CONSOLE_HISTORY_MAX", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_supervisor", + "name": "ConsoleEvent", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_supervisor", + "name": "DIALOG_BRIDGE_HOST", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_dialogs" + }, + { + "facade": "tools.browser_supervisor", + "name": "DIALOG_BRIDGE_URL_PATTERN", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_dialogs" + }, + { + "facade": "tools.browser_supervisor", + "name": "DIALOG_POLICY_AUTO_ACCEPT", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_dialogs" + }, + { + "facade": "tools.browser_supervisor", + "name": "DIALOG_POLICY_AUTO_DISMISS", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_dialogs" + }, + { + "facade": "tools.browser_supervisor", + "name": "DIALOG_POLICY_MUST_RESPOND", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_dialogs" + }, + { + "facade": "tools.browser_supervisor", + "name": "FRAME_TREE_MAX_ENTRIES", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_frames" + }, + { + "facade": "tools.browser_supervisor", + "name": "FRAME_TREE_MAX_OOPIF_DEPTH", + "kind": "moved-lazy", + "target": "tools.browser_supervisor_frames" + }, + { + "facade": "tools.browser_tool", + "name": "BrowserUseProvider", + "kind": "moved-lazy", + "target": "plugins.browser.browser_use.provider.BrowserUseBrowserProvider" + }, + { + "facade": "tools.browser_tool", + "name": "BrowserbaseProvider", + "kind": "moved-lazy", + "target": "plugins.browser.browserbase.provider.BrowserbaseBrowserProvider" + }, + { + "facade": "tools.browser_tool", + "name": "CloudBrowserProvider", + "kind": "moved-lazy", + "target": "agent.browser_provider.BrowserProvider" + }, + { + "facade": "tools.browser_tool", + "name": "FirecrawlProvider", + "kind": "moved-lazy", + "target": "plugins.browser.firecrawl.provider.FirecrawlBrowserProvider" + }, + { + "facade": "tools.browser_tool", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.browser_tool", + "name": "SNAPSHOT_SUMMARIZE_THRESHOLD", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.browser_tool", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.browser_tool", + "name": "agent_browser_runnable", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "check_browser_requirements", + "kind": "moved-lazy", + "target": "tools.browser_tool_install" + }, + { + "facade": "tools.browser_tool", + "name": "check_browser_vision_requirements", + "kind": "moved-lazy", + "target": "tools.browser_tool_install" + }, + { + "facade": "tools.browser_tool", + "name": "cleanup_all_browsers", + "kind": "moved-lazy", + "target": "tools.browser_tool_lifecycle" + }, + { + "facade": "tools.browser_tool", + "name": "cleanup_browser", + "kind": "moved-lazy", + "target": "tools.browser_tool_lifecycle" + }, + { + "facade": "tools.browser_tool", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.browser_tool", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.browser_tool", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "tools.browser_tool", + "name": "get_hermes_home_override", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "hermes_home_key", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "is_truthy_value", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.browser_tool", + "name": "lightpanda_engine_status", + "kind": "moved-lazy", + "target": "tools.browser_tool_lightpanda_fallback" + }, + { + "facade": "tools.browser_tool", + "name": "node_tool_runnable", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "normalize_browser_cloud_provider", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.browser_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.browser_tool", + "name": "reset_hermes_home_override", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "set_hermes_home_override", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.browser_tool", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.browser_tool", + "name": "signal", + "kind": "import", + "target": "signal" + }, + { + "facade": "tools.browser_tool", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.browser_tool", + "name": "warm_agent_browser_npx_cache", + "kind": "moved-lazy", + "target": "tools.browser_tool_install" + }, + { + "facade": "tools.browser_tool", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "tools.clarify_gateway", + "name": "get_notify", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.clarify_gateway", + "name": "register_notify", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.clarify_gateway", + "name": "unregister_notify", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.close_preview_tool", + "name": "CLOSE_PREVIEW_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.close_preview_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.close_preview_tool", + "name": "registry", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.close_preview_tool", + "name": "tool_error", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.code_execution_tool", + "name": "DEFAULT_KERNEL_MODE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.code_execution_tool", + "name": "KERNEL_MODES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.code_execution_tool", + "name": "platform", + "kind": "import", + "target": "platform" + }, + { + "facade": "tools.code_execution_tool", + "name": "socket", + "kind": "import", + "target": "socket" + }, + { + "facade": "tools.code_execution_tool", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.code_execution_tool", + "name": "thread_scoped_silence", + "kind": "moved-lazy", + "target": "agent.thread_scoped_output" + }, + { + "facade": "tools.code_kernel_remote", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "tools.computer_use", + "name": "annotations", + "kind": "import", + "target": "__future__" + }, + { + "facade": "tools.computer_use", + "name": "check_computer_use_requirements", + "kind": "moved-lazy", + "target": "tools.computer_use.tool" + }, + { + "facade": "tools.computer_use", + "name": "get_computer_use_schema", + "kind": "moved-lazy", + "target": "tools.computer_use.tool" + }, + { + "facade": "tools.computer_use", + "name": "handle_computer_use", + "kind": "moved-lazy", + "target": "tools.computer_use.tool" + }, + { + "facade": "tools.computer_use", + "name": "release_computer_use_session", + "kind": "moved-lazy", + "target": "tools.computer_use.tool" + }, + { + "facade": "tools.computer_use", + "name": "set_approval_callback", + "kind": "moved-lazy", + "target": "tools.computer_use.tool" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "CaptureResult", + "kind": "moved-lazy", + "target": "tools.computer_use.backend" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "PureWindowsPath", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "UIElement", + "kind": "moved-lazy", + "target": "tools.computer_use.backend" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "concurrent", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "cua_driver_install_hint", + "kind": "moved-lazy", + "target": "tools.computer_use.cua_backend_driver" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "cua_driver_update_check", + "kind": "moved-lazy", + "target": "tools.computer_use.cua_backend_driver" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "deque", + "kind": "import", + "target": "collections" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "functools", + "kind": "import", + "target": "functools" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "tools.computer_use.cua_backend", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.computer_use.permissions", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.computer_use.tool", + "name": "struct", + "kind": "import", + "target": "struct" + }, + { + "facade": "tools.cronjob_tools", + "name": "effective_job_state", + "kind": "moved-lazy", + "target": "cron.jobs" + }, + { + "facade": "tools.cronjob_tools", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.delegate_tool", + "name": "DEFAULT_CHILD_TIMEOUT", + "kind": "moved-lazy", + "target": "tools.delegate_tool_config" + }, + { + "facade": "tools.delegate_tool", + "name": "DEFAULT_MAX_SUMMARY_CHARS", + "kind": "moved-lazy", + "target": "tools.delegate_tool_results" + }, + { + "facade": "tools.delegate_tool", + "name": "DEFAULT_TOOLSETS", + "kind": "moved-lazy", + "target": "tools.delegate_tool_toolsets" + }, + { + "facade": "tools.delegate_tool", + "name": "FuturesTimeoutError", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "tools.delegate_tool", + "name": "MAX_DEPTH", + "kind": "moved-lazy", + "target": "tools.delegate_tool_config" + }, + { + "facade": "tools.delegate_tool", + "name": "TOOLSETS", + "kind": "moved-lazy", + "target": "toolsets" + }, + { + "facade": "tools.delegate_tool", + "name": "base_url_hostname", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.delegate_tool", + "name": "contextvars", + "kind": "import", + "target": "contextvars" + }, + { + "facade": "tools.delegate_tool", + "name": "enum", + "kind": "import", + "target": "enum" + }, + { + "facade": "tools.delegate_tool", + "name": "file_state", + "kind": "moved-lazy", + "target": "tools" + }, + { + "facade": "tools.delegate_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.delegate_tool", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "tools.delegate_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.delegate_tool", + "name": "request_hard_interrupt", + "kind": "moved-lazy", + "target": "agent.interrupt_compat" + }, + { + "facade": "tools.delegate_tool", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.delegate_tool", + "name": "urlsplit", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.delegate_tool", + "name": "urlunsplit", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.delegation_live_log", + "name": "new_live_delegation_id", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.delegation_output_schema", + "name": "MAX_SCHEMA_RETRIES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.discord_tool", + "name": "TYPE_CHECKING", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.discord_tool", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.discord_tool", + "name": "get_dynamic_schema", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.drive_preview_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.env_probe", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "tools.environments.base", + "name": "IO", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.environments.base", + "name": "Protocol", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.environments.base", + "name": "codecs", + "kind": "import", + "target": "codecs" + }, + { + "facade": "tools.environments.base", + "name": "deque", + "kind": "import", + "target": "collections" + }, + { + "facade": "tools.environments.base", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.environments.base", + "name": "sanitize_task_id_for_path", + "kind": "moved-lazy", + "target": "tools.environments.path_utils" + }, + { + "facade": "tools.environments.base", + "name": "select", + "kind": "import", + "target": "select" + }, + { + "facade": "tools.environments.base", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "tools.environments.base", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "tools.environments.managed_modal", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.environments.vercel_sandbox", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.feishu_doc_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.feishu_doc_tool", + "name": "logger", + "kind": "moved-lazy", + "target": "tools.approval" + }, + { + "facade": "tools.feishu_doc_tool", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "tools.feishu_doc_tool", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.feishu_drive_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.feishu_drive_tool", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.file_operations", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.file_operations", + "name": "ClassVar", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.file_operations", + "name": "DEFAULT_READ_LIMIT", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "DEFAULT_READ_OFFSET", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "DEFAULT_SEARCH_LIMIT", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "DEFAULT_SEARCH_OFFSET", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "LINTERS", + "kind": "moved-lazy", + "target": "tools.file_operations_lint" + }, + { + "facade": "tools.file_operations", + "name": "LintResult", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.file_operations", + "name": "MAX_FILE_SIZE", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.file_operations", + "name": "MAX_LINES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.file_operations", + "name": "MAX_LINE_LENGTH", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.file_operations", + "name": "SEARCH_PRUNE_DIR_NAMES", + "kind": "moved-lazy", + "target": "agent.search_policy" + }, + { + "facade": "tools.file_operations", + "name": "SearchMatch", + "kind": "moved-lazy", + "target": "tools.file_operations_common" + }, + { + "facade": "tools.file_operations", + "name": "build_write_denied_paths", + "kind": "restored-import", + "target": "agent.file_safety" + }, + { + "facade": "tools.file_operations", + "name": "WRITE_DENIED_PATHS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.file_operations", + "name": "build_write_denied_prefixes", + "kind": "restored-import", + "target": "agent.file_safety" + }, + { + "facade": "tools.file_operations", + "name": "WRITE_DENIED_PREFIXES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.file_operations", + "name": "build_write_denied_paths", + "kind": "moved-lazy", + "target": "agent.file_safety" + }, + { + "facade": "tools.file_operations", + "name": "build_write_denied_prefixes", + "kind": "moved-lazy", + "target": "agent.file_safety" + }, + { + "facade": "tools.file_operations", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.file_operations", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.file_operations", + "name": "posixpath", + "kind": "import", + "target": "posixpath" + }, + { + "facade": "tools.file_operations", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.file_operations", + "name": "tool_interrupt", + "kind": "moved-lazy", + "target": "tools.interrupt" + }, + { + "facade": "tools.file_tools", + "name": "PurePosixPath", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "tools.file_tools", + "name": "has_opaque_document_extension", + "kind": "moved-lazy", + "target": "tools.binary_extensions" + }, + { + "facade": "tools.file_tools", + "name": "is_pdf_path", + "kind": "moved-lazy", + "target": "tools.binary_extensions" + }, + { + "facade": "tools.file_tools", + "name": "notify_other_tool_call", + "kind": "moved-lazy", + "target": "tools.file_tools_read_tracking" + }, + { + "facade": "tools.file_tools", + "name": "posixpath", + "kind": "import", + "target": "posixpath" + }, + { + "facade": "tools.file_tools", + "name": "reset_file_dedup", + "kind": "moved-lazy", + "target": "tools.file_tools_read_tracking" + }, + { + "facade": "tools.file_tools", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.focus_pane_tool", + "name": "FOCUS_PANE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.focus_pane_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.fuzzy_match", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.fuzzy_match", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.image_generation_tool", + "name": "is_krea_model", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.lazy_deps", + "name": "feature_specs", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "urlsplit", + "kind": "restored-import", + "target": "urllib.parse" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "_MANAGED_GATEWAY_VENDOR", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "is_managed_nous_gateway_url", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_gateway_auth_headers", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "_MEDIA_UPLOAD_PRESIGN_TIMEOUT_SECONDS", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "_MEDIA_UPLOAD_PUT_READ_TIMEOUT_SECONDS", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "_MEDIA_UPLOAD_PUT_WRITE_TIMEOUT_SECONDS", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "_describe_media_upload_refusal", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of build_managed_media_uploader)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "build_managed_media_uploader", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "is_managed_nous_gateway_url", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_gateway_auth_headers", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_vendor_base_path", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_vendor_upload_path", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of managed_vendor_endpoints)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_vendor_endpoints", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "managed_vendor_upload_path", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.managed_tool_gateway", + "name": "urlsplit", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.mcp_oauth", + "name": "OAuthClientInformationFull", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_oauth", + "name": "OAuthClientMetadata", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_oauth", + "name": "OAuthClientProvider", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_oauth", + "name": "OAuthMetadata", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_oauth", + "name": "OAuthToken", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_oauth", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.mcp_schema_cache", + "name": "clear_cache_entry", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_schema_cache", + "name": "has_cached_entry", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.mcp_tool", + "name": "Coroutine", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.mcp_tool", + "name": "InvalidMcpUrlError", + "kind": "moved-lazy", + "target": "tools.mcp_tool_errors" + }, + { + "facade": "tools.mcp_tool", + "name": "MCP_TOOL_NAME_PREFIX", + "kind": "moved-lazy", + "target": "tools.mcp_tool_schema" + }, + { + "facade": "tools.mcp_tool", + "name": "NonMcpEndpointError", + "kind": "moved-lazy", + "target": "tools.mcp_tool_errors" + }, + { + "facade": "tools.mcp_tool", + "name": "SimpleNamespace", + "kind": "import", + "target": "types" + }, + { + "facade": "tools.mcp_tool", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.mcp_tool", + "name": "asynccontextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.mcp_tool", + "name": "concurrent", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "tools.mcp_tool", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.mcp_tool", + "name": "discover_mcp_tools", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "errno", + "kind": "import", + "target": "errno" + }, + { + "facade": "tools.mcp_tool", + "name": "fnmatch", + "kind": "import", + "target": "fnmatch" + }, + { + "facade": "tools.mcp_tool", + "name": "get_mcp_status", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "get_registered_mcp_server_names", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "has_registered_mcp_tools", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "is_mcp_tool_parallel_safe", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.mcp_tool", + "name": "matches_name_filter", + "kind": "moved-lazy", + "target": "tools.mcp_tool_schema" + }, + { + "facade": "tools.mcp_tool", + "name": "math", + "kind": "import", + "target": "math" + }, + { + "facade": "tools.mcp_tool", + "name": "mcp_prefixed_tool_name", + "kind": "moved-lazy", + "target": "tools.mcp_tool_schema" + }, + { + "facade": "tools.mcp_tool", + "name": "persist_agent_tool_names", + "kind": "moved-lazy", + "target": "tools.mcp_tool_agent" + }, + { + "facade": "tools.mcp_tool", + "name": "probe_mcp_server_tools", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "random", + "kind": "import", + "target": "random" + }, + { + "facade": "tools.mcp_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.mcp_tool", + "name": "reconnect_mcp_server", + "kind": "moved-lazy", + "target": "tools.mcp_tool_loop" + }, + { + "facade": "tools.mcp_tool", + "name": "refresh_agent_mcp_tools", + "kind": "moved-lazy", + "target": "tools.mcp_tool_agent" + }, + { + "facade": "tools.mcp_tool", + "name": "register_mcp_servers", + "kind": "moved-lazy", + "target": "tools.mcp_tool_discovery" + }, + { + "facade": "tools.mcp_tool", + "name": "reprobe_tool_availability", + "kind": "moved-lazy", + "target": "tools.mcp_tool_agent" + }, + { + "facade": "tools.mcp_tool", + "name": "restore_agent_tool_prefix", + "kind": "moved-lazy", + "target": "tools.mcp_tool_agent" + }, + { + "facade": "tools.mcp_tool", + "name": "sanitize_mcp_name_component", + "kind": "moved-lazy", + "target": "tools.mcp_tool_schema" + }, + { + "facade": "tools.mcp_tool", + "name": "shutdown_mcp_servers", + "kind": "moved-lazy", + "target": "tools.mcp_tool_lifecycle" + }, + { + "facade": "tools.mcp_tool", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.mcp_tool", + "name": "strip_unicode_tags", + "kind": "moved-lazy", + "target": "tools.ansi_strip" + }, + { + "facade": "tools.mcp_tool", + "name": "tool_error", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.mcp_tool", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.memory_tool", + "name": "atomic_write_text", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.memory_tool", + "name": "contextmanager", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.memory_tool", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.microsoft_graph_client", + "name": "AsyncIterator", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.microsoft_graph_client", + "name": "GraphCredentials", + "kind": "moved-lazy", + "target": "tools.microsoft_graph_auth" + }, + { + "facade": "tools.open_preview_tool", + "name": "OPEN_PREVIEW_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.open_preview_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.open_preview_tool", + "name": "registry", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.openrouter_client", + "name": "get_async_client", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.path_security", + "name": "logger", + "kind": "moved-lazy", + "target": "tools.approval" + }, + { + "facade": "tools.path_security", + "name": "logging", + "kind": "import", + "target": "logging" + }, + { + "facade": "tools.preview_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.preview_tool", + "name": "preview_open", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.process_registry", + "name": "MAX_ACTIVE_PROCESS_AGE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.react_to_message_tool", + "name": "env_var_enabled", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.read_extract", + "name": "MAX_XLSX_BYTES", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.read_preview_tool", + "name": "READ_PREVIEW_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.read_preview_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.read_preview_tool", + "name": "registry", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.read_preview_tool", + "name": "tool_error", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.read_terminal_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.read_window_tool", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.read_window_tool", + "name": "tool_error", + "kind": "moved-lazy", + "target": "tools.registry" + }, + { + "facade": "tools.send_message_tool", + "name": "SEND_MESSAGE_SCHEMA", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.send_message_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.send_message_tool", + "name": "redact_sensitive_text", + "kind": "moved-lazy", + "target": "agent.redact" + }, + { + "facade": "tools.send_message_tool", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.skill_ledger", + "name": "ACTOR_AGENT", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_ledger", + "name": "ACTOR_CURATOR", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_ledger", + "name": "ACTOR_USER", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_linter", + "name": "format_findings", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_linter", + "name": "has_errors", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_manager_tool", + "name": "mark_background_review_skill_read", + "kind": "moved-lazy", + "target": "tools.skill_manager_guards" + }, + { + "facade": "tools.skill_usage", + "name": "tempfile", + "kind": "restored-import", + "target": "tempfile" + }, + { + "facade": "tools.skill_usage", + "name": "os", + "kind": "restored-import", + "target": "os" + }, + { + "facade": "tools.skill_usage", + "name": "_suppressed_file", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of add_suppressed_name)" + }, + { + "facade": "tools.skill_usage", + "name": "_write_suppressed_names", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of add_suppressed_name)" + }, + { + "facade": "tools.skill_usage", + "name": "add_suppressed_name", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_usage", + "name": "agent_created_report", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_usage", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "tools.skill_usage", + "name": "remove_suppressed_name", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skill_usage", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "tools.skillevaluator_scan", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.skillevaluator_scan", + "name": "SCANNER_NAME", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skillevaluator_scan", + "name": "scanner_available", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_guard", + "name": "full_content_hash", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_hub", + "name": "ABC", + "kind": "import", + "target": "abc" + }, + { + "facade": "tools.skills_hub", + "name": "BrowseShSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_sources" + }, + { + "facade": "tools.skills_hub", + "name": "ClawHubSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_clawhub" + }, + { + "facade": "tools.skills_hub", + "name": "GITHUB_TAP_PROVIDERS", + "kind": "moved-lazy", + "target": "tools.skills_hub_github" + }, + { + "facade": "tools.skills_hub", + "name": "GitHubAuth", + "kind": "moved-lazy", + "target": "tools.skills_hub_github" + }, + { + "facade": "tools.skills_hub", + "name": "GitHubSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_github" + }, + { + "facade": "tools.skills_hub", + "name": "HERMES_INDEX_TTL", + "kind": "moved-lazy", + "target": "tools.skills_hub_search" + }, + { + "facade": "tools.skills_hub", + "name": "HERMES_INDEX_URL", + "kind": "moved-lazy", + "target": "tools.skills_hub_search" + }, + { + "facade": "tools.skills_hub", + "name": "HermesIndexSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_official" + }, + { + "facade": "tools.skills_hub", + "name": "LobeHubSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_sources" + }, + { + "facade": "tools.skills_hub", + "name": "OptionalSkillSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_official" + }, + { + "facade": "tools.skills_hub", + "name": "PurePosixPath", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "tools.skills_hub", + "name": "ScanResult", + "kind": "moved-lazy", + "target": "tools.skills_guard" + }, + { + "facade": "tools.skills_hub", + "name": "SkillBundle", + "kind": "moved-lazy", + "target": "tools.skills_hub_models" + }, + { + "facade": "tools.skills_hub", + "name": "SkillMeta", + "kind": "moved-lazy", + "target": "tools.skills_hub_models" + }, + { + "facade": "tools.skills_hub", + "name": "SkillSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_models" + }, + { + "facade": "tools.skills_hub", + "name": "SkillsShSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_skillssh" + }, + { + "facade": "tools.skills_hub", + "name": "TRUSTED_REPOS", + "kind": "moved-lazy", + "target": "tools.skills_guard" + }, + { + "facade": "tools.skills_hub", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.skills_hub", + "name": "Union", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.skills_hub", + "name": "UrlSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_sources" + }, + { + "facade": "tools.skills_hub", + "name": "WellKnownSkillSource", + "kind": "moved-lazy", + "target": "tools.skills_hub_sources" + }, + { + "facade": "tools.skills_hub", + "name": "abstractmethod", + "kind": "import", + "target": "abc" + }, + { + "facade": "tools.skills_hub", + "name": "bundle_content_hash", + "kind": "moved-lazy", + "target": "tools.skills_hub_install" + }, + { + "facade": "tools.skills_hub", + "name": "check_for_skill_updates", + "kind": "moved-lazy", + "target": "tools.skills_hub_install" + }, + { + "facade": "tools.skills_hub", + "name": "content_hash", + "kind": "moved-lazy", + "target": "tools.skills_guard" + }, + { + "facade": "tools.skills_hub", + "name": "create_source_router", + "kind": "moved-lazy", + "target": "tools.skills_hub_search" + }, + { + "facade": "tools.skills_hub", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.skills_hub", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.skills_hub", + "name": "github_provider_for", + "kind": "moved-lazy", + "target": "tools.skills_hub_github" + }, + { + "facade": "tools.skills_hub", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "tools.skills_hub", + "name": "install_from_quarantine", + "kind": "moved-lazy", + "target": "tools.skills_hub_install" + }, + { + "facade": "tools.skills_hub", + "name": "is_excluded_skill_path", + "kind": "moved-lazy", + "target": "agent.skill_utils" + }, + { + "facade": "tools.skills_hub", + "name": "os", + "kind": "import", + "target": "os" + }, + { + "facade": "tools.skills_hub", + "name": "parallel_search_sources", + "kind": "moved-lazy", + "target": "tools.skills_hub_search" + }, + { + "facade": "tools.skills_hub", + "name": "quarantine_bundle", + "kind": "moved-lazy", + "target": "tools.skills_hub_install" + }, + { + "facade": "tools.skills_hub", + "name": "quote", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.skills_hub", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.skills_hub", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.skills_hub", + "name": "source_url_for_bundle", + "kind": "moved-lazy", + "target": "tools.skills_hub_models" + }, + { + "facade": "tools.skills_hub", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "tools.skills_hub", + "name": "unified_search", + "kind": "moved-lazy", + "target": "tools.skills_hub_search" + }, + { + "facade": "tools.skills_hub", + "name": "uninstall_skill", + "kind": "moved-lazy", + "target": "tools.skills_hub_install" + }, + { + "facade": "tools.skills_hub", + "name": "unquote", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.skills_hub", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.skills_hub", + "name": "urlsplit", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.skills_hub", + "name": "urlunparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.skills_hub", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "tools.skills_hub", + "name": "yaml", + "kind": "import", + "target": "yaml" + }, + { + "facade": "tools.skills_sync", + "name": "PurePosixPath", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "tools.skills_sync", + "name": "atomic_replace", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.skills_sync", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.skills_sync", + "name": "diff_bundled_skill", + "kind": "moved-lazy", + "target": "tools.skills_sync_bundled_ops" + }, + { + "facade": "tools.skills_sync", + "name": "is_bundled_skills_opt_out", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync", + "name": "json", + "kind": "import", + "target": "json" + }, + { + "facade": "tools.skills_sync", + "name": "list_user_modified_bundled_skills", + "kind": "moved-lazy", + "target": "tools.skills_sync_bundled_ops" + }, + { + "facade": "tools.skills_sync", + "name": "remove_pristine_bundled_skills", + "kind": "moved-lazy", + "target": "tools.skills_sync_bundled_ops" + }, + { + "facade": "tools.skills_sync", + "name": "reset_bundled_skill", + "kind": "moved-lazy", + "target": "tools.skills_sync_bundled_ops" + }, + { + "facade": "tools.skills_sync", + "name": "restore_official_optional_skill", + "kind": "moved-lazy", + "target": "tools.skills_sync_optional" + }, + { + "facade": "tools.skills_sync", + "name": "set_bundled_skills_opt_out", + "kind": "moved-lazy", + "target": "tools.skills_sync_bundled_ops" + }, + { + "facade": "tools.skills_sync", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.skills_sync_client", + "name": "ARTIFACT_TYPE_SKILL", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "KIND_COMMIT", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "KIND_TREE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "MODE_DIR", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "MODE_EXEC", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "MODE_FILE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "SYNC_MANIFEST_ENTRY_NAME", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "SYNC_MANIFEST_TYPE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "SYNC_MANIFEST_VERSION", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "WIRE_VERSION", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "canonical_json_bytes", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "datetime", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.skills_sync_client", + "name": "dev_gate_open", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "tools.skills_sync_client", + "name": "maybe_pull_org_skills", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_org" + }, + { + "facade": "tools.skills_sync_client", + "name": "org_head_ref", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_org" + }, + { + "facade": "tools.skills_sync_client", + "name": "org_skill_is_locally_modified", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_org" + }, + { + "facade": "tools.skills_sync_client", + "name": "org_sync_available", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "parse_sync_manifest", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_sync_client", + "name": "propose_skill", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_org" + }, + { + "facade": "tools.skills_sync_client", + "name": "pull_org_skills", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_org" + }, + { + "facade": "tools.skills_sync_client", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.skills_sync_client", + "name": "timezone", + "kind": "import", + "target": "datetime" + }, + { + "facade": "tools.skills_sync_client", + "name": "user_conflict_ref", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.skills_sync_client", + "name": "wire_address", + "kind": "moved-lazy", + "target": "tools.skills_sync_client_wire" + }, + { + "facade": "tools.skills_tool", + "name": "Enum", + "kind": "import", + "target": "enum" + }, + { + "facade": "tools.skills_tool", + "name": "Set", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.skills_tool", + "name": "display_hermes_home", + "kind": "moved-lazy", + "target": "hermes_constants" + }, + { + "facade": "tools.skills_tool", + "name": "env_var_enabled", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.skills_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.skills_tool", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.slash_confirm", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "tools.slash_confirm", + "name": "resolve_sync_compat", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.terminal_scope", + "name": "install_refusal_scope", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.terminal_scope", + "name": "terminal_scope", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.terminal_tool", + "name": "Path", + "kind": "import", + "target": "pathlib" + }, + { + "facade": "tools.terminal_tool", + "name": "cleanup_vm", + "kind": "moved-lazy", + "target": "tools.terminal_tool_lifecycle" + }, + { + "facade": "tools.terminal_tool", + "name": "env_var_enabled", + "kind": "moved-lazy", + "target": "utils" + }, + { + "facade": "tools.terminal_tool", + "name": "get_active_env", + "kind": "moved-lazy", + "target": "tools.terminal_tool_lifecycle" + }, + { + "facade": "tools.terminal_tool", + "name": "has_direct_modal_credentials", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.terminal_tool", + "name": "importlib", + "kind": "import", + "target": "importlib.util" + }, + { + "facade": "tools.terminal_tool", + "name": "is_interrupted", + "kind": "moved-lazy", + "target": "tools.interrupt" + }, + { + "facade": "tools.terminal_tool", + "name": "is_managed_tool_gateway_ready", + "kind": "moved-lazy", + "target": "tools.managed_tool_gateway" + }, + { + "facade": "tools.terminal_tool", + "name": "is_persistent_env", + "kind": "moved-lazy", + "target": "tools.terminal_tool_lifecycle" + }, + { + "facade": "tools.terminal_tool", + "name": "nous_tool_gateway_unavailable_message", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.terminal_tool", + "name": "platform", + "kind": "import", + "target": "platform" + }, + { + "facade": "tools.terminal_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.terminal_tool", + "name": "resolve_modal_backend_state", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.terminal_tool", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "tools.terminal_tool", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.terminal_tool", + "name": "stat", + "kind": "import", + "target": "stat" + }, + { + "facade": "tools.terminal_tool", + "name": "strip_inert_heredoc_bodies", + "kind": "moved-lazy", + "target": "tools.shell_heredoc" + }, + { + "facade": "tools.terminal_tool", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "tools.terminal_tool", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.tool_result_storage", + "name": "HEREDOC_MARKER", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.tool_result_storage", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "tools.tool_search", + "name": "Literal", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.tool_search", + "name": "build_catalog_listing", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.tool_search", + "name": "copy", + "kind": "import", + "target": "copy" + }, + { + "facade": "tools.tool_search", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.tool_search", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.tool_search", + "name": "snowballstemmer", + "kind": "import", + "target": "snowballstemmer" + }, + { + "facade": "tools.tool_search", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.transcription_tools", + "name": "COMMAND_STT_OUTPUT_FORMATS", + "kind": "moved-lazy", + "target": "tools.transcription_command" + }, + { + "facade": "tools.transcription_tools", + "name": "COMMON_LOCAL_BIN_DIRS", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "DEFAULT_COMMAND_STT_LANGUAGE", + "kind": "moved-lazy", + "target": "tools.transcription_command" + }, + { + "facade": "tools.transcription_tools", + "name": "DEFAULT_COMMAND_STT_OUTPUT_FORMAT", + "kind": "moved-lazy", + "target": "tools.transcription_command" + }, + { + "facade": "tools.transcription_tools", + "name": "DEFAULT_COMMAND_STT_TIMEOUT_SECONDS", + "kind": "moved-lazy", + "target": "tools.transcription_command" + }, + { + "facade": "tools.transcription_tools", + "name": "DEFAULT_LOCAL_STT_LANGUAGE", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "ELEVENLABS_STT_BASE_URL", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "GROQ_BASE_URL", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "GROQ_MODELS", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "LOCAL_NATIVE_AUDIO_FORMATS", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "MAX_FILE_SIZE", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "OPENAI_BASE_URL", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "OPENAI_MODELS", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "SUPPORTED_FORMATS", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "XAI_STT_BASE_URL", + "kind": "moved-lazy", + "target": "tools.transcription_common" + }, + { + "facade": "tools.transcription_tools", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.transcription_tools", + "name": "nous_tool_gateway_unavailable_message", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.transcription_tools", + "name": "platform", + "kind": "import", + "target": "platform" + }, + { + "facade": "tools.transcription_tools", + "name": "queue", + "kind": "import", + "target": "queue" + }, + { + "facade": "tools.transcription_tools", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.transcription_tools", + "name": "resolve_managed_tool_gateway", + "kind": "moved-lazy", + "target": "tools.managed_tool_gateway" + }, + { + "facade": "tools.transcription_tools", + "name": "resolve_openai_audio_api_key", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.transcription_tools", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "tools.transcription_tools", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "tools.transcription_tools", + "name": "tempfile", + "kind": "import", + "target": "tempfile" + }, + { + "facade": "tools.transcription_tools", + "name": "urljoin", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.transcription_tools", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "tools.tts_tool", + "name": "AudioDeliveryProfile", + "kind": "moved-lazy", + "target": "tools.tts_tool_delivery" + }, + { + "facade": "tools.tts_tool", + "name": "COMMAND_TTS_OUTPUT_FORMATS", + "kind": "moved-lazy", + "target": "tools.tts_command_provider" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTH", + "kind": "moved-lazy", + "target": "tools.tts_command_provider" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_COMMAND_TTS_OUTPUT_FORMAT", + "kind": "moved-lazy", + "target": "tools.tts_command_provider" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_COMMAND_TTS_TIMEOUT_SECONDS", + "kind": "moved-lazy", + "target": "tools.tts_command_provider" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_DEEPINFRA_TTS_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_openai" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_EDGE_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_ELEVENLABS_MODEL_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_ELEVENLABS_STREAMING_MODEL_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_ELEVENLABS_VOICE_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_GEMINI_AUDIO_TAGS", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_GEMINI_TTS_BASE_URL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_GEMINI_TTS_MODEL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_GEMINI_TTS_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_KITTENTTS_MODEL", + "kind": "moved-lazy", + "target": "tools.tts_tool_local" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_KITTENTTS_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_local" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MINIMAX_BASE_URL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MINIMAX_CN_BASE_URL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MINIMAX_MODEL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MINIMAX_VOICE_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MISTRAL_TTS_MODEL", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_MISTRAL_TTS_VOICE_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_OPENAI_BASE_URL", + "kind": "moved-lazy", + "target": "tools.tts_tool_openai" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_OPENAI_MODEL", + "kind": "moved-lazy", + "target": "tools.tts_tool_openai" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_OPENAI_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_openai" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_PIPER_VOICE", + "kind": "moved-lazy", + "target": "tools.tts_tool_local" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_AUTO_SPEECH_TAGS", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_BASE_URL", + "kind": "moved-lazy", + "target": "tools.xai_http" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_BIT_RATE", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_LANGUAGE", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_OPTIMIZE_STREAMING_LATENCY_DEFAULT", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_SAMPLE_RATE", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_SPEED_DEFAULT", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_SPEED_MAX", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_SPEED_MIN", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_TEXT_NORMALIZATION_DEFAULT", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "DEFAULT_XAI_VOICE_ID", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "ELEVENLABS_MODEL_MAX_TEXT_LENGTH", + "kind": "moved-lazy", + "target": "tools.tts_tool_delivery" + }, + { + "facade": "tools.tts_tool", + "name": "FALLBACK_MAX_TEXT_LENGTH", + "kind": "moved-lazy", + "target": "tools.tts_tool_delivery" + }, + { + "facade": "tools.tts_tool", + "name": "Future", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "tools.tts_tool", + "name": "GEMINI_AUDIO_TAG_REWRITE_TASK", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "GEMINI_TTS_CHANNELS", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.tts_tool", + "name": "GEMINI_TTS_SAMPLE_RATE", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.tts_tool", + "name": "GEMINI_TTS_SAMPLE_WIDTH", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.tts_tool", + "name": "Iterator", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.tts_tool", + "name": "MANAGED_OPENAI_TTS_MODELS", + "kind": "moved-lazy", + "target": "tools.tts_tool_openai" + }, + { + "facade": "tools.tts_tool", + "name": "MAX_TEXT_LENGTH", + "kind": "moved-lazy", + "target": "gateway.platforms.bluebubbles" + }, + { + "facade": "tools.tts_tool", + "name": "PROVIDER_MAX_TEXT_LENGTH", + "kind": "moved-lazy", + "target": "tools.tts_tool_delivery" + }, + { + "facade": "tools.tts_tool", + "name": "TTS_RESPONSE_BODY_CHUNK_BYTES", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "TTS_RESPONSE_BODY_LIMIT_BYTES", + "kind": "moved-lazy", + "target": "tools.tts_tool_providers" + }, + { + "facade": "tools.tts_tool", + "name": "ThreadPoolExecutor", + "kind": "import", + "target": "concurrent.futures" + }, + { + "facade": "tools.tts_tool", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.tts_tool", + "name": "acquire_tts_lease", + "kind": "moved-lazy", + "target": "tools.tts_tool_lifecycle" + }, + { + "facade": "tools.tts_tool", + "name": "base64", + "kind": "import", + "target": "base64" + }, + { + "facade": "tools.tts_tool", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.tts_tool", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tools.tts_tool", + "name": "hermes_xai_user_agent", + "kind": "moved-lazy", + "target": "tools.xai_http" + }, + { + "facade": "tools.tts_tool", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.tts_tool", + "name": "nous_tool_gateway_unavailable_message", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.tts_tool", + "name": "platform", + "kind": "import", + "target": "platform" + }, + { + "facade": "tools.tts_tool", + "name": "queue", + "kind": "import", + "target": "queue" + }, + { + "facade": "tools.tts_tool", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.tts_tool", + "name": "read_selection", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.tts_tool", + "name": "release_tts_lease", + "kind": "moved-lazy", + "target": "tools.tts_tool_lifecycle" + }, + { + "facade": "tools.tts_tool", + "name": "release_tts_provider", + "kind": "moved-lazy", + "target": "tools.tts_tool_lifecycle" + }, + { + "facade": "tools.tts_tool", + "name": "resolve_managed_tool_gateway", + "kind": "moved-lazy", + "target": "tools.managed_tool_gateway" + }, + { + "facade": "tools.tts_tool", + "name": "resolve_openai_audio_api_key", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.tts_tool", + "name": "selection_error", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.tts_tool", + "name": "shlex", + "kind": "import", + "target": "shlex" + }, + { + "facade": "tools.tts_tool", + "name": "shutil", + "kind": "import", + "target": "shutil" + }, + { + "facade": "tools.tts_tool", + "name": "stream_tts_to_speaker", + "kind": "moved-lazy", + "target": "tools.tts_tool_speaker" + }, + { + "facade": "tools.tts_tool", + "name": "subprocess", + "kind": "import", + "target": "subprocess" + }, + { + "facade": "tools.tts_tool", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.tts_tool", + "name": "time", + "kind": "import", + "target": "time" + }, + { + "facade": "tools.tts_tool", + "name": "tts_lease_holders", + "kind": "moved-lazy", + "target": "tools.tts_tool_lifecycle" + }, + { + "facade": "tools.tts_tool", + "name": "urljoin", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.tts_tool", + "name": "urlparse", + "kind": "import", + "target": "urllib.parse" + }, + { + "facade": "tools.tts_tool", + "name": "uuid", + "kind": "import", + "target": "uuid" + }, + { + "facade": "tools.tts_tool", + "name": "warm_tts_provider", + "kind": "moved-lazy", + "target": "tools.tts_tool_lifecycle" + }, + { + "facade": "tools.tts_tool", + "name": "windows_hide_flags", + "kind": "moved-lazy", + "target": "hermes_cli._subprocess_compat" + }, + { + "facade": "tools.url_safety", + "name": "has_sensitive_query_params", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.url_safety", + "name": "ssrf_safe_async_http_transport", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.url_safety", + "name": "ssrf_safe_http_transport", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.vision_tools", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tools.vision_tools", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.vision_tools", + "name": "threading", + "kind": "import", + "target": "threading" + }, + { + "facade": "tools.voice_mode", + "name": "DEFAULT_TTS_ECHO_SIMILARITY_THRESHOLD", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.voice_mode", + "name": "DEFAULT_VOICE_STOP_PHRASES", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.voice_mode", + "name": "MIN_FRAGMENT_LENGTH_FOR_ECHO", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.voice_mode", + "name": "WHISPER_HALLUCINATIONS", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.voice_mode", + "name": "difflib", + "kind": "import", + "target": "difflib" + }, + { + "facade": "tools.voice_mode", + "name": "is_tts_echo", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.voice_mode", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.voice_mode", + "name": "voice_stop_hint", + "kind": "moved-lazy", + "target": "tools.voice_mode_transcript" + }, + { + "facade": "tools.web_result_cache", + "name": "Any", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.web_result_cache", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.web_tools", + "name": "DEFAULT_EXTRACT_CHAR_LIMIT", + "kind": "moved-lazy", + "target": "tools.web_tools_truncate" + }, + { + "facade": "tools.web_tools", + "name": "Dict", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.web_tools", + "name": "Firecrawl", + "kind": "moved-lazy", + "target": "plugins.web.firecrawl.provider" + }, + { + "facade": "tools.web_tools", + "name": "MAX_STORED_TEXT_CHARS", + "kind": "moved-lazy", + "target": "tools.web_tools_truncate" + }, + { + "facade": "tools.web_tools", + "name": "TYPE_CHECKING", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.web_tools", + "name": "asyncio", + "kind": "import", + "target": "asyncio" + }, + { + "facade": "tools.web_tools", + "name": "build_vendor_gateway_url", + "kind": "moved-lazy", + "target": "tools.managed_tool_gateway" + }, + { + "facade": "tools.web_tools", + "name": "httpx", + "kind": "import", + "target": "httpx" + }, + { + "facade": "tools.web_tools", + "name": "managed_nous_tools_enabled", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.web_tools", + "name": "normalize_url_for_request", + "kind": "moved-lazy", + "target": "tools.url_safety" + }, + { + "facade": "tools.web_tools", + "name": "nous_tool_gateway_unavailable_message", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.web_tools", + "name": "prefers_gateway", + "kind": "moved-lazy", + "target": "tools.tool_backend_helpers" + }, + { + "facade": "tools.web_tools", + "name": "re", + "kind": "import", + "target": "re" + }, + { + "facade": "tools.web_tools", + "name": "resolve_managed_tool_gateway", + "kind": "moved-lazy", + "target": "tools.managed_tool_gateway" + }, + { + "facade": "tools.web_tools", + "name": "sensitive_query_param_name", + "kind": "moved-lazy", + "target": "tools.url_safety" + }, + { + "facade": "tools.web_tools", + "name": "sys", + "kind": "import", + "target": "sys" + }, + { + "facade": "tools.website_policy", + "name": "invalidate_cache", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.write_approval", + "name": "is_background", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tools.yuanbao_tools", + "name": "List", + "kind": "import", + "target": "typing" + }, + { + "facade": "tools.yuanbao_tools", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "toolsets", + "name": "resolve_multiple_toolsets", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tui_gateway.compute_host", + "name": "field", + "kind": "restored-import", + "target": "dataclasses" + }, + { + "facade": "tui_gateway.compute_host", + "name": "dataclass", + "kind": "restored-import", + "target": "dataclasses" + }, + { + "facade": "tui_gateway.compute_host", + "name": "SpikeAgent", + "kind": "restored-helper", + "target": "(deleted; restored as a dependency of HostSession)" + }, + { + "facade": "tui_gateway.compute_host", + "name": "HostSession", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tui_gateway.compute_host", + "name": "SpikeAgent", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tui_gateway.compute_host", + "name": "dataclass", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tui_gateway.compute_host", + "name": "field", + "kind": "import", + "target": "dataclasses" + }, + { + "facade": "tui_gateway.compute_host", + "name": "request_hard_interrupt", + "kind": "moved-lazy", + "target": "agent.interrupt_compat" + }, + { + "facade": "tui_gateway.hosted_room_driver", + "name": "contextlib", + "kind": "import", + "target": "contextlib" + }, + { + "facade": "tui_gateway.hosted_room_driver", + "name": "null_turn_lock", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tui_gateway.hosted_room_service", + "name": "hashlib", + "kind": "import", + "target": "hashlib" + }, + { + "facade": "tui_gateway.mcp_rpc_helpers", + "name": "Optional", + "kind": "import", + "target": "typing" + }, + { + "facade": "tui_gateway.mcp_rpc_helpers", + "name": "Tuple", + "kind": "import", + "target": "typing" + }, + { + "facade": "tui_gateway.mcp_rpc_helpers", + "name": "resolve_profile", + "kind": "restored-def", + "target": "(deleted; BASE body restored)" + }, + { + "facade": "tui_gateway.methods_browser_control", + "name": "BROWSER_CONTROL_PROTOCOL_VERSION", + "kind": "moved-lazy", + "target": "gateway.browser_control_broker" + }, + { + "facade": "tui_gateway.methods_browser_control", + "name": "browser_control_protocol_supported", + "kind": "moved-lazy", + "target": "gateway.browser_control_broker" + }, + { + "facade": "tui_gateway.methods_browser_control", + "name": "filter_browser_control_capabilities", + "kind": "moved-lazy", + "target": "gateway.browser_control_broker" + }, + { + "facade": "tui_gateway.methods_prompt", + "name": "types", + "kind": "import", + "target": "types" + }, + { + "facade": "gateway.startup_watchdog", + "name": "*", + "kind": "module-stub", + "target": "gateway.shutdown_watchdog" + }, + { + "facade": "hermes_cli.observability.relay_runtime", + "name": "*", + "kind": "module-stub", + "target": "agent.relay_runtime" + }, + { + "facade": "tools.environments.modal_utils", + "name": "*", + "kind": "module-stub", + "target": "(deleted)" + } + ] +} \ No newline at end of file diff --git a/cron/jobs.py b/cron/jobs.py index 34a484f990..044d0bee54 100644 --- a/cron/jobs.py +++ b/cron/jobs.py @@ -3123,3 +3123,14 @@ def rewrite_skill_refs( save_jobs(jobs) logger.info("Curator rewrote skill references in %d cron job(s)", len(rewrites)) return {"rewrites": rewrites, "jobs_updated": len(rewrites), "jobs_scanned": len(jobs)} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def clear_drift_alerted(job_id: str) -> None: + """Clear the drift alert-dedup marker (resolution matches again).""" + _set_alert_flag(job_id, "drift_alerted", False) +# ---- END PLUGIN-COMPAT ---- diff --git a/cron/scheduler.py b/cron/scheduler.py index 9fcbdeea42..b2db71db52 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -3865,3 +3865,29 @@ if __name__ == "__main__": 0 if _run_external_worker_payload(args.external_worker_file, args.ack_file) else 1 ) tick(verbose=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import asyncio # noqa: F401,E402 +import shutil # noqa: F401,E402 +import signal # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BOT_CHAT_PLATFORM': ('cron.scheduler_delivery', 'BOT_CHAT_PLATFORM'), + 'SharedRouteAdapters': ('cron.scheduler_preflight', 'SharedRouteAdapters'), + 'cron_delivery_targets': ('cron.scheduler_delivery', 'cron_delivery_targets'), + 'parse_bot_chat_deliver_token': ('cron.scheduler_delivery', 'parse_bot_chat_deliver_token'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/cron/scheduler_provider.py b/cron/scheduler_provider.py index 1a1acd60d4..fc02d14fce 100644 --- a/cron/scheduler_provider.py +++ b/cron/scheduler_provider.py @@ -543,3 +543,26 @@ class InProcessCronScheduler(CronScheduler): if ok: consecutive_failures = 0 stop_event.wait(_backoff_wait_seconds(interval, consecutive_failures)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def provider_supports_fire_cancel(provider: Any) -> bool: + """Return whether ``fire_claimed`` accepts a ``cancel_event`` kwarg.""" + try: + parameters = inspect.signature(provider.fire_claimed).parameters.values() + except (TypeError, ValueError): + return False + return any( + parameter.kind is inspect.Parameter.VAR_KEYWORD + or ( + parameter.name == "cancel_event" + and parameter.kind + in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) + ) + for parameter in parameters + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/browser_control_artifacts.py b/gateway/browser_control_artifacts.py index cbd0c3c49d..8dcb51a5a0 100644 --- a/gateway/browser_control_artifacts.py +++ b/gateway/browser_control_artifacts.py @@ -321,3 +321,13 @@ class ArtifactRateLimiter: """Drop the recorded hits for ``key`` (tests/diagnostics).""" with self._lock: self._hits.pop(key, None) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +class ArtifactOverwrite(ArtifactError): + """An artifact id already exists and the store refuses to overwrite it.""" +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/browser_control_broker.py b/gateway/browser_control_broker.py index 58e2d7c672..20b0b79f09 100644 --- a/gateway/browser_control_broker.py +++ b/gateway/browser_control_broker.py @@ -510,3 +510,16 @@ _GLOBAL_BROKER = BrowserControlBroker() def get_browser_control_broker() -> BrowserControlBroker: """Process-local broker shared by API and dashboard Gateway transports.""" return _GLOBAL_BROKER + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +BROWSER_CONTROL_ALL_CAPABILITIES = frozenset( + BROWSER_CONTROL_CAPABILITIES + | BROWSER_CONTROL_ARTIFACT_CAPABILITIES + | BROWSER_CONTROL_DEVELOPER_CAPABILITIES +) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/config.py b/gateway/config.py index 0a7f36a605..f75324d67c 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -863,3 +863,11 @@ def _apply_env_overrides(config: GatewayConfig) -> None: """Apply environment variable overrides to config (see ``gateway.config_env``).""" from gateway.config_env import _apply_env_overrides as _impl _impl(config) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/delivery_ledger.py b/gateway/delivery_ledger.py index 585290555e..fdcfc232a4 100644 --- a/gateway/delivery_ledger.py +++ b/gateway/delivery_ledger.py @@ -352,3 +352,33 @@ def ledger_enabled(config: Optional[Dict[str, Any]] = None) -> bool: return value.strip().lower() not in {"false", "0", "no", "off"} if isinstance(value, str) else bool(value) except Exception: return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import json # noqa: F401,E402 + +def debug_rows(limit: int = 20) -> str: + """Human-readable dump for ad-hoc inspection (sqlite3-free path).""" + with _DB_LOCK, _transaction() as conn: + rows = conn.execute( + """SELECT obligation_id, session_key, state, attempts, + created_at, updated_at, last_error + FROM delivery_obligations + ORDER BY updated_at DESC LIMIT ?""", + (limit,), + ).fetchall() + return json.dumps( + [ + { + "id": r[0], "session": r[1], "state": r[2], "attempts": r[3], + "created_at": r[4], "updated_at": r[5], "last_error": r[6], + } + for r in rows + ], + indent=2, + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/disk_status.py b/gateway/disk_status.py index 897a251bc4..a5cda3a7f8 100644 --- a/gateway/disk_status.py +++ b/gateway/disk_status.py @@ -58,3 +58,24 @@ def collect_disk_status(home: Optional[Path] = None) -> Dict[str, Any]: status.update(total_mb=total_mb, free_mb=free_mb, used_percent=round((usage.used / usage.total) * 100, 1), pressure=classify_disk_pressure(free_mb, total_mb)) return status + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('gateway.run', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_room_discussion.py b/gateway/hosted_room_discussion.py index 0dc4e9b225..559583991e 100644 --- a/gateway/hosted_room_discussion.py +++ b/gateway/hosted_room_discussion.py @@ -782,3 +782,11 @@ def plan_publication( payload={**_turn_coordinates(task), "seen_through_seq": task.seen_through_seq, **extra}, authority_gateway_id=room.gateway_id, authority_epoch=room.authority_epoch)) return PublicationPlan(task_id=task.identity.task_id, terminal_kind=terminal_kind, events=tuple(effects)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_room_driver.py b/gateway/hosted_room_driver.py index fb7268767a..7cf44a075b 100644 --- a/gateway/hosted_room_driver.py +++ b/gateway/hosted_room_driver.py @@ -857,3 +857,14 @@ def prune_published_terminal_tasks( f"DELETE FROM hosted_room_driver_tasks WHERE room_id=? AND task_id IN ({','.join('?' * len(candidates))})", (room_id, *candidates)) return max(0, int(deleted.rowcount)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Iterator # noqa: F401,E402 +from pathlib import Path # noqa: F401,E402 +from contextlib import contextmanager # noqa: F401,E402 +import re # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_room_execution_policy.py b/gateway/hosted_room_execution_policy.py index 29fbf66496..c91cb7b54f 100644 --- a/gateway/hosted_room_execution_policy.py +++ b/gateway/hosted_room_execution_policy.py @@ -110,3 +110,12 @@ __all__ = [ "MAX_POLICY_ITERATIONS", "POLICY_VERSION", "RoomExecutionPolicy", "RoomExecutionPolicyError", "bind_room_execution_policy", "current_room_execution_policy", "execution_policy_mapping", "reset_room_execution_policy"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import re # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_room_peer.py b/gateway/hosted_room_peer.py index 4264ea0ec1..fdc8c7d36a 100644 --- a/gateway/hosted_room_peer.py +++ b/gateway/hosted_room_peer.py @@ -488,3 +488,57 @@ def room_grant_needs_dispatch_refresh(token: str, *, now: float | None = None, l return clock(now) + max(0.0, float(leeway_seconds)) >= expires_at except Exception: return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import time # noqa: F401,E402 + +@dataclass(frozen=True) +class RoomLinkProbe: + """One gateway-verified route candidate.""" + + mode: LinkMode + verified: bool + encrypted: bool + latency_ms: float + +_LINK_PRIORITY = { + "direct": 0, + "overlay": 1, + "relay": 2, + "pull": 3, + "desktop": 4, +} + +def select_room_link( + probes: Iterable[RoomLinkProbe], + *, + desktop_available: bool, +) -> RoomLinkProbe | None: + """Choose the fastest safe route without weakening encryption.""" + candidates = [ + probe + for probe in probes + if probe.verified + and probe.encrypted + and probe.mode != "desktop" + and math.isfinite(probe.latency_ms) + and probe.latency_ms >= 0 + ] + if candidates: + return min( + candidates, + key=lambda item: (_LINK_PRIORITY[item.mode], item.latency_ms), + ) + if desktop_available: + return RoomLinkProbe( + mode="desktop", + verified=True, + encrypted=True, + latency_ms=0, + ) + return None +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_room_replicas.py b/gateway/hosted_room_replicas.py index 3a786b2f31..384fcf03be 100644 --- a/gateway/hosted_room_replicas.py +++ b/gateway/hosted_room_replicas.py @@ -284,3 +284,26 @@ def demote_room( return { "room_id": room_id, "authority_gateway_id": observed_gateway_id, "authority_epoch": observed_epoch, "idempotent": False} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import time # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'MAX_EVENT_JSON_BYTES': ('gateway.hosted_rooms', 'MAX_EVENT_JSON_BYTES'), + 'MAX_ROOM_ID_CHARS': ('gateway.hosted_rooms', 'MAX_ROOM_ID_CHARS'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/hosted_rooms.py b/gateway/hosted_rooms.py index 4a823bf0fd..5246eb1d9f 100644 --- a/gateway/hosted_rooms.py +++ b/gateway/hosted_rooms.py @@ -1158,3 +1158,14 @@ def read_events( if not fits(events): raise HostedRoomError("hosted room event exceeds replay page limit") return build_page(events) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Iterator # noqa: F401,E402 +from typing import NoReturn # noqa: F401,E402 +from contextlib import contextmanager # noqa: F401,E402 +import time # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/kanban_watchers.py b/gateway/kanban_watchers.py index 84967ad559..1aa3d2ac3d 100644 --- a/gateway/kanban_watchers.py +++ b/gateway/kanban_watchers.py @@ -304,3 +304,28 @@ class GatewayKanbanWatchersMixin: await self._sleep_between_ticks(interval) self._release_kanban_dispatcher_lock() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Callable # noqa: F401,E402 +from contextvars import Context # noqa: F401,E402 +import logging # noqa: F401,E402 +import re # noqa: F401,E402 +import sqlite3 # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 't': ('agent.i18n', 't'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/memory_status.py b/gateway/memory_status.py index e6a4600a64..f600286113 100644 --- a/gateway/memory_status.py +++ b/gateway/memory_status.py @@ -110,3 +110,24 @@ def collect_memory_status( status["boot_id"] = started_at if isinstance(started_at, str) and started_at else None return status + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('gateway.run', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/helpers.py b/gateway/platforms/helpers.py index 4d6ed53fe6..32717f83c5 100644 --- a/gateway/platforms/helpers.py +++ b/gateway/platforms/helpers.py @@ -511,3 +511,98 @@ def _chunk_newline_preferred(text, limit, len_fn): if remaining: chunks.append(remaining) return chunks + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import TYPE_CHECKING # noqa: F401,E402 +import asyncio # noqa: F401,E402 +import asyncio # noqa: F401,E402 + +class TextBatchAggregator: + """Aggregates rapid-fire text events into single messages. + + Replaces the ``_enqueue_text_event`` / ``_flush_text_batch`` pattern + previously duplicated in telegram, discord, matrix, wecom, and feishu. + + Usage:: + + self._text_batcher = TextBatchAggregator( + handler=self._message_handler, + batch_delay=0.6, + split_threshold=1900, + ) + + # In message dispatch: + if msg_type == MessageType.TEXT and self._text_batcher.is_enabled(): + self._text_batcher.enqueue(event, session_key) + return + """ + + def __init__( + self, + handler, + *, + batch_delay: float = 0.6, + split_delay: float = 2.0, + split_threshold: int = 4000, + ): + self._handler = handler + self._batch_delay = batch_delay + self._split_delay = split_delay + self._split_threshold = split_threshold + self._pending: Dict[str, "MessageEvent"] = {} + self._pending_tasks: Dict[str, asyncio.Task] = {} + + def is_enabled(self) -> bool: + """Return True if batching is active (delay > 0).""" + return self._batch_delay > 0 + + def enqueue(self, event: "MessageEvent", key: str) -> None: + """Add *event* to the pending batch for *key*.""" + chunk_len = len(event.text or "") + existing = self._pending.get(key) + if not existing: + event._last_chunk_len = chunk_len # type: ignore[attr-defined] + self._pending[key] = event + else: + existing.text = f"{existing.text}\n{event.text}" + existing._last_chunk_len = chunk_len # type: ignore[attr-defined] + + # Cancel prior flush timer, start a new one + prior = self._pending_tasks.get(key) + if prior and not prior.done(): + prior.cancel() + self._pending_tasks[key] = asyncio.create_task(self._flush(key)) + + async def _flush(self, key: str) -> None: + """Wait then dispatch the batched event for *key*.""" + current_task = self._pending_tasks.get(key) + pending = self._pending.get(key) + last_len = getattr(pending, "_last_chunk_len", 0) if pending else 0 + + # Use longer delay when the last chunk looks like a split message + delay = self._split_delay if last_len >= self._split_threshold else self._batch_delay + await asyncio.sleep(delay) + + event = self._pending.pop(key, None) + if event: + try: + await self._handler(event) + except Exception: + logger.exception("[TextBatchAggregator] Error dispatching batched event for %s", key) + + if self._pending_tasks.get(key) is current_task: + self._pending_tasks.pop(key, None) + + def cancel_all(self) -> None: + """Cancel all pending flush tasks.""" + for task in self._pending_tasks.values(): + if not task.done(): + task.cancel() + self._pending_tasks.clear() + self._pending.clear() +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/qqbot/adapter.py b/gateway/platforms/qqbot/adapter.py index 5596b83eef..87f4c04857 100644 --- a/gateway/platforms/qqbot/adapter.py +++ b/gateway/platforms/qqbot/adapter.py @@ -1695,3 +1695,12 @@ class QQAdapter(BasePlatformAdapter): return True self._seen_messages[msg_id] = now return False + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import base64 # noqa: F401,E402 +import mimetypes # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/qqbot/chunked_upload.py b/gateway/platforms/qqbot/chunked_upload.py index 874a844819..2091d6aff1 100644 --- a/gateway/platforms/qqbot/chunked_upload.py +++ b/gateway/platforms/qqbot/chunked_upload.py @@ -290,3 +290,14 @@ def _compute_file_hashes(file_path: str, file_size: int) -> Dict[str, str]: bytes_read += len(chunk) full_md5 = md5.hexdigest() return {"md5": full_md5, "sha1": sha1.hexdigest(), "md5_10m": md5_10m.hexdigest() if need_10m else full_md5} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +import functools # noqa: F401,E402 + +ApiRequestFn = Callable[..., Awaitable[Dict[str, Any]]] +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/qqbot/keyboards.py b/gateway/platforms/qqbot/keyboards.py index 490b8e6ae7..9d31e2b517 100644 --- a/gateway/platforms/qqbot/keyboards.py +++ b/gateway/platforms/qqbot/keyboards.py @@ -189,3 +189,97 @@ def parse_interaction_event(raw: Dict[str, Any]) -> InteractionEvent: channel_id=str(raw.get("channel_id", "")), guild_id=str(raw.get("guild_id", "")), button_data=str(resolved.get("button_data", "")), button_id=str(resolved.get("button_id", "")), resolver_user_id=str(resolved.get("user_id", ""))) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 +from typing import Awaitable # noqa: F401,E402 +from typing import Callable # noqa: F401,E402 +from typing import Awaitable # noqa: F401,E402 +from typing import Callable # noqa: F401,E402 +import logging # noqa: F401,E402 + +logger = logging.getLogger(__name__) + +PostMessageFn = Callable[..., Awaitable[Dict[str, Any]]] + +class ApprovalSender: + """Send an approval-request message with an inline keyboard. + + Decoupled from the adapter via callables so it can be unit-tested in + isolation. Pass the adapter's ``_send_message_with_keyboard`` helper + (or any equivalent) as ``post_message``. + """ + + def __init__( + self, + post_c2c: PostMessageFn, + post_group: PostMessageFn, + log_tag: str = "QQBot", + ) -> None: + self._post_c2c = post_c2c + self._post_group = post_group + self._log_tag = log_tag + + async def send( + self, + chat_type: str, + chat_id: str, + req: ApprovalRequest, + msg_id: Optional[str] = None, + ) -> bool: + """Send an approval message to *chat_id*. + + :param chat_type: ``'c2c'`` or ``'group'``. + :param chat_id: User openid or group openid. + :param req: :class:`ApprovalRequest`. + :param msg_id: Reply-to message id (required for passive messages). + :returns: ``True`` on success, ``False`` on failure. + """ + text = build_approval_text(req) + keyboard = build_approval_keyboard(req.session_key) + + logger.info( + "[%s] Sending approval request to %s:%s (session=%.20s…)", + self._log_tag, chat_type, chat_id, req.session_key, + ) + + try: + if chat_type == "c2c": + await self._post_c2c(chat_id, text, msg_id, keyboard) + elif chat_type == "group": + await self._post_group(chat_id, text, msg_id, keyboard) + else: + logger.warning( + "[%s] Approval: unsupported chat_type %r", + self._log_tag, chat_type, + ) + return False + logger.info( + "[%s] Approval message sent to %s:%s", + self._log_tag, chat_type, chat_id, + ) + return True + except Exception as exc: + logger.error( + "[%s] Failed to send approval message to %s:%s: %s", + self._log_tag, chat_type, chat_id, exc, + ) + return False + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('gateway.platforms.qqbot.adapter', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/signal.py b/gateway/platforms/signal.py index e9ac35529d..254de671b7 100644 --- a/gateway/platforms/signal.py +++ b/gateway/platforms/signal.py @@ -974,3 +974,25 @@ class SignalAdapter(BasePlatformAdapter): result = await self._rpc("getContact", {"account": self.account, "contactAddress": chat_id}) name = (result.get("name") or result.get("profileName")) if isinstance(result, dict) else None return {"name": name or chat_id, "type": "dm", "chat_id": chat_id} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +TYPING_INTERVAL = 8.0 # seconds between typing indicator refreshes + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_EXT_TO_MIME': ('gateway.platforms.media_cache', 'DEFAULT_EXT_TO_MIME'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/weixin.py b/gateway/platforms/weixin.py index 1316af4c2d..2f4e97f53a 100644 --- a/gateway/platforms/weixin.py +++ b/gateway/platforms/weixin.py @@ -1265,3 +1265,13 @@ async def send_weixin_direct( adapter._send_session = adapter._session = session adapter._token_store = token_store return await _deliver_direct(adapter, chat_id, message, media_files, context_token) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import struct # noqa: F401,E402 + +MSG_TYPE_USER = 1 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/yuanbao.py b/gateway/platforms/yuanbao.py index cd913d2b14..02093b501d 100644 --- a/gateway/platforms/yuanbao.py +++ b/gateway/platforms/yuanbao.py @@ -2802,3 +2802,205 @@ class YuanbaoAdapter(BasePlatformAdapter): """Current valid sign token (module-level cache).""" return await SignManager.get_token(self._app_key, self._app_secret, self._api_domain, route_env=self._route_env) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +AUTH_FAILED_CODES = {4001, 4002, 4003} # permanent auth failure, re-sign token + +AUTH_RETRYABLE_CODES = {4010, 4011, 4099} # transient, can retry with same token + +class FileUrlHandler(MediaSendHandler): + """Strategy: send file from a URL (download → COS → TIMFileElem).""" + + async def acquire_file(self, adapter, **kwargs): + file_url: str = kwargs["file_url"] + logger.info("[%s] FileUrlHandler: downloading %s", adapter.name, file_url) + file_bytes, content_type = await media_download_url( + file_url, max_size_mb=adapter.MEDIA_MAX_SIZE_MB, + ) + filename = kwargs.get("filename") + if not filename: + path_part = file_url.split("?")[0] + filename = os.path.basename(path_part) or "file" + if not content_type or content_type == "application/octet-stream": + content_type = guess_mime_type(filename) or "application/octet-stream" + return file_bytes, filename, content_type + + def build_msg_body(self, upload_result, **kwargs): + return build_file_msg_body( + url=upload_result["url"], + filename=kwargs["filename"], + uuid=kwargs["file_uuid"], + size=upload_result["size"], + ) + +class GroupQueryService: + """Encapsulates all group query operations (both low-level WS calls and + higher-level AI-tool-facing wrappers). + + Responsibilities: + - Low-level WS encode/decode for group info and member list queries + - Chat-id parsing, error wrapping and result filtering for AI tools + - Member cache population on the adapter + """ + + def __init__(self, adapter: "YuanbaoAdapter") -> None: + self._adapter = adapter + + # ------------------------------------------------------------------ + # Low-level WS query methods + # ------------------------------------------------------------------ + + async def query_group_info_raw(self, group_code: str) -> Optional[dict]: + """Query group info via WS (group name, owner, member count, etc.). + + Returns: + Decoded dict or None on failure. + """ + adapter = self._adapter + if adapter._connection.ws is None: + return None + encoded = encode_query_group_info(group_code) + from gateway.platforms.yuanbao_proto import decode_conn_msg as _decode + decoded = _decode(encoded) + req_id = decoded["head"]["msg_id"] + try: + response = await adapter._connection.send_biz_request(encoded, req_id=req_id) + head = response.get("head", {}) + status = head.get("status", 0) + if status != 0: + logger.warning("[%s] query_group_info failed: status=%d", adapter.name, status) + return None + biz_data = response.get("data", b"") or response.get("body", b"") + if biz_data and isinstance(biz_data, bytes): + return decode_query_group_info_rsp(biz_data) + return {"group_code": group_code} + except asyncio.TimeoutError: + logger.warning("[%s] query_group_info timeout: group=%s", adapter.name, group_code) + return None + except Exception as exc: + logger.warning("[%s] query_group_info failed: %s", adapter.name, exc) + return None + + async def get_group_member_list_raw( + self, group_code: str, offset: int = 0, limit: int = 200 + ) -> Optional[dict]: + """Query group member list via WS. + + Returns: + Decoded dict or None on failure. Also populates adapter._member_cache. + """ + adapter = self._adapter + if adapter._connection.ws is None: + return None + encoded = encode_get_group_member_list(group_code, offset=offset, limit=limit) + from gateway.platforms.yuanbao_proto import decode_conn_msg as _decode + decoded = _decode(encoded) + req_id = decoded["head"]["msg_id"] + try: + response = await adapter._connection.send_biz_request(encoded, req_id=req_id) + head = response.get("head", {}) + status = head.get("status", 0) + if status != 0: + logger.warning("[%s] get_group_member_list failed: status=%d", adapter.name, status) + return None + biz_data = response.get("data", b"") or response.get("body", b"") + if biz_data and isinstance(biz_data, bytes): + result = decode_get_group_member_list_rsp(biz_data) + else: + result = {"members": [], "next_offset": 0, "is_complete": True} + if result and result.get("members"): + adapter._member_cache[group_code] = (time.time(), result["members"]) + return result + except asyncio.TimeoutError: + logger.warning("[%s] get_group_member_list timeout: group=%s", adapter.name, group_code) + return None + except Exception as exc: + logger.warning("[%s] get_group_member_list failed: %s", adapter.name, exc) + return None + + # ------------------------------------------------------------------ + # AI-tool-facing wrappers (chat_id parsing + filtering) + # ------------------------------------------------------------------ + + async def query_group_info(self, chat_id: str) -> dict: + """AI tool: Query current group info. + + No parameters needed (group_code extracted from session context). + Returns group name, owner, member count, etc. + """ + if not chat_id.startswith("group:"): + return {"error": "This command is only available in group chats"} + group_code = chat_id[len("group:"):] + result = await self.query_group_info_raw(group_code) + if result is None: + return {"error": "Failed to query group info"} + return result + + async def query_session_members( + self, + chat_id: str, + action: str = "list_all", + name: Optional[str] = None, + ) -> dict: + """AI tool: Query group member list. + + Args: + chat_id: Chat ID (extracted from session context) + action: 'find' (search by name) | 'list_bots' (list bots) | 'list_all' (list all) + name: Search keyword when action='find' + + Returns: + {"members": [...], "total": int, "mentionHint": str} + """ + if not chat_id.startswith("group:"): + return {"error": "This command is only available in group chats"} + group_code = chat_id[len("group:"):] + result = await self.get_group_member_list_raw(group_code) + if result is None: + return {"error": "Failed to query group members"} + + members = result.get("members", []) + + if action == "find" and name: + query = name.lower() + members = [ + m for m in members + if query in (m.get("nickname", "") or "").lower() + or query in (m.get("name_card", "") or "").lower() + or query in (m.get("user_id", "") or "").lower() + ] + elif action == "list_bots": + members = [m for m in members if "bot" in (m.get("nickname", "") or "").lower()] + + # Construct mentionHint + mention_hint = "" + if members and len(members) <= 10: + names = [m.get("name_card") or m.get("nickname") or m.get("user_id", "") for m in members] + mention_hint = "Mention with @name: " + ", ".join(names) + + return { + "members": members[:50], # Limit return count + "total": len(members), + "mentionHint": mention_hint, + } + +REPLY_REF_TTL_S = 300.0 # Reference dedup TTL (5 minutes) + +def get_active_adapter() -> Optional["YuanbaoAdapter"]: + """Delegate to ``YuanbaoAdapter.get_active()``.""" + return YuanbaoAdapter.get_active() + +async def send_yuanbao_direct( + adapter: "YuanbaoAdapter", + chat_id: str, + message: str, + media_files: Optional[List[Tuple[str, bool]]] = None, +) -> Dict[str, Any]: + """Delegate to ``OutboundManager.send_direct``.""" + return await adapter._outbound.send_direct(chat_id, message, media_files) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/yuanbao_media.py b/gateway/platforms/yuanbao_media.py index 9371e0868a..946e331cc3 100644 --- a/gateway/platforms/yuanbao_media.py +++ b/gateway/platforms/yuanbao_media.py @@ -317,3 +317,12 @@ def _basename_from_url(url: str) -> str: return os.path.basename(urllib.parse.urlparse(url).path) except Exception: return "" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +COS_USE_ACCELERATE = True +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/platforms/yuanbao_proto.py b/gateway/platforms/yuanbao_proto.py index 7937f29662..cb138c1e4b 100644 --- a/gateway/platforms/yuanbao_proto.py +++ b/gateway/platforms/yuanbao_proto.py @@ -538,3 +538,79 @@ def decode_get_group_member_list_rsp(data: bytes) -> Optional[dict]: } except Exception: return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + +DEBUG_MODE = False + +def _encode_forward_multimedia(media: dict) -> bytes: + buf = b"" + for fn, key in [(1, "type"), (2, "url"), (4, "file_name"), (15, "media_id")]: + v = media.get(key, "") + if v: + buf += _encode_field(fn, WT_LEN, _encode_string(str(v))) + for fn, key in [(5, "file_size"), (6, "width"), (7, "height")]: + v = media.get(key, 0) + if v: + buf += _encode_field(fn, WT_VARINT, _encode_varint(int(v))) + return buf + +def _encode_forward_msg_content(content: dict) -> bytes: + buf = _encode_field(1, WT_VARINT, _encode_varint(int(content.get("type", 0)))) + text = content.get("text", "") + if text: + buf += _encode_field(2, WT_LEN, _encode_string(str(text))) + for media in content.get("multimedia") or []: + buf += _encode_field(3, WT_LEN, _encode_message(_encode_forward_multimedia(media))) + return buf + +def _encode_forward_msg(msg: dict) -> bytes: + buf = b"" + sender = msg.get("sender", "") + if sender: + buf += _encode_field(1, WT_LEN, _encode_string(str(sender))) + time_val = msg.get("time", 0) + if time_val: + buf += _encode_field(2, WT_VARINT, _encode_varint(int(time_val))) + plain = msg.get("plainText", "") + if plain: + buf += _encode_field(3, WT_LEN, _encode_string(str(plain))) + for mc in msg.get("msgContent") or []: + buf += _encode_field(4, WT_LEN, _encode_message(_encode_forward_msg_content(mc))) + return buf + +def encode_forward_msg_data(data: dict) -> bytes: + """Encode ForwardMsgData protobuf bytes (inverse of ``decode_forward_msg_data``). + + Mainly used to build mock / test data; production code never needs to encode this. + """ + buf = _encode_field(1, WT_VARINT, _encode_varint(int(data.get("sub_type", 0)))) + for fn, key in [(2, "begin_time"), (3, "end_time")]: + v = data.get(key, 0) + if v: + buf += _encode_field(fn, WT_VARINT, _encode_varint(int(v))) + nick = data.get("nick_name", "") + if nick: + buf += _encode_field(4, WT_LEN, _encode_string(str(nick))) + for msg in data.get("msg") or []: + buf += _encode_field(5, WT_LEN, _encode_message(_encode_forward_msg(msg))) + return buf + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('gateway.platforms.base', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/relay/__init__.py b/gateway/relay/__init__.py index b49a240787..2366cf943c 100644 --- a/gateway/relay/__init__.py +++ b/gateway/relay/__init__.py @@ -650,3 +650,22 @@ def register_relay_adapter(force: bool = False, url: Optional[str] = None) -> bo ) ) return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def relay_bot_username(platform: str) -> Optional[str]: + """The bot's deep-link username/handle for a platform (e.g. Telegram's + ``@handle`` for ``t.me/``), read from the per-platform entry in + ``GATEWAY_RELAY_BOT_IDS``. None when absent (most platforms don't need one). + """ + entry = _relay_bot_ids_map().get(platform) + if isinstance(entry, dict): + username = entry.get("username") + if username: + return str(username).lstrip("@") + return None +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/relay/adapter.py b/gateway/relay/adapter.py index 021d723764..c680236deb 100644 --- a/gateway/relay/adapter.py +++ b/gateway/relay/adapter.py @@ -2139,3 +2139,11 @@ _PROMPT_RESOLVERS = { "slash_confirm": RelayAdapter._resolve_slash_confirm, "clarify": RelayAdapter._resolve_clarify, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import cast # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/relay/auth.py b/gateway/relay/auth.py index ca23ab1c5d..8a2e069a6d 100644 --- a/gateway/relay/auth.py +++ b/gateway/relay/auth.py @@ -38,3 +38,102 @@ def make_upgrade_token( ) -> str: """WS-upgrade bearer: ``payload = gateway_id`` (the connector peeks it to index its verify list).""" return make_token(gateway_id, secret, ttl_seconds) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +from typing import Sequence # noqa: F401,E402 + +DELIVERY_SIG_HEADER = "x-relay-signature" + +DELIVERY_TS_HEADER = "x-relay-timestamp" + +_DEFAULT_MAX_SKEW_SECONDS = 300 + +def _hmac_hex(payload: str, secret: str) -> str: + """HMAC-SHA256 hex digest of ``payload`` under ``secret`` (UTF-8).""" + return hmac.new(secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest() + +def verify_signature(payload: str, sig_hex: str, secrets: Sequence[str]) -> bool: + """Constant-time check that ``sig_hex`` is a valid HMAC of ``payload`` under + ANY of ``secrets`` (rotation window). Length-mismatched candidates are + skipped without a timing leak. Mirrors ``verifySignature``. + """ + try: + sig_buf = bytes.fromhex(sig_hex) + except (ValueError, TypeError): + return False + if len(sig_buf) == 0: + return False + for secret in secrets: + if not secret: + continue + expected = bytes.fromhex(_hmac_hex(payload, secret)) + if len(expected) != len(sig_buf): + continue + if hmac.compare_digest(sig_buf, expected): + return True + return False + +def _delivery_payload(ts: int, body_json: str) -> str: + """Signed material for an inbound delivery: ``f"{ts}.{body_json}"``.""" + return f"{ts}.{body_json}" + +def verify_delivery_signature( + body_json: str, + timestamp: Optional[str], + signature: Optional[str], + verify_keys: Sequence[str], + max_skew_seconds: int = _DEFAULT_MAX_SKEW_SECONDS, + *, + now: Optional[int] = None, +) -> bool: + """Verify a connector→gateway inbound delivery signature. + + ``body_json`` MUST be the exact request body bytes decoded as UTF-8 — the + connector signs over the literal serialized body, so the gateway verifies + over the literal received body (no re-serialization). Checks the timestamp + is within ``max_skew_seconds`` of now and the HMAC matches any key in the + rotation verify list. Mirrors the connector's ``verifyDeliverySignature``. + """ + if not timestamp or not signature: + return False + try: + ts = int(timestamp) + except (ValueError, TypeError): + return False + current = now if now is not None else int(time.time()) + if abs(current - ts) > max_skew_seconds: + return False + return verify_signature(_delivery_payload(ts, body_json), signature, verify_keys) + +def verify_token(token: str, secrets: Sequence[str]) -> Optional[str]: + """Verify a token built by ``make_token``; return the payload or None. + + Splits from the right so a payload may itself contain colons (mirrors the + connector's ``verifyToken``). Rejects an expired token and any signature + that doesn't match a secret in the verify list. + """ + try: + # base64url decode with padding restored. + padded = token + "=" * (-len(token) % 4) + decoded = base64.urlsafe_b64decode(padded.encode("ascii")).decode("utf-8") + except (ValueError, TypeError): + return None + parts = decoded.split(":") + if len(parts) < 3: + return None + sig = parts[-1] + try: + exp = int(parts[-2]) + except ValueError: + return None + payload = ":".join(parts[:-2]) + if exp != 0 and int(time.time()) > exp: + return None + signed = f"{payload}:{exp}" + return payload if verify_signature(signed, sig, secrets) else None +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/response_filters.py b/gateway/response_filters.py index 6f29393544..b465053248 100644 --- a/gateway/response_filters.py +++ b/gateway/response_filters.py @@ -92,3 +92,12 @@ def is_partial_silence_marker(text: Any) -> bool: c and any(marker.startswith(c) for marker in LIVE_GATEWAY_SILENT_MARKERS) for c in _canonical_silence_candidates(text) ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +SILENT_REPLY_TOKEN = "NO_REPLY" +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/restart_loop_guard.py b/gateway/restart_loop_guard.py index 09f47b3d4a..627ad0404a 100644 --- a/gateway/restart_loop_guard.py +++ b/gateway/restart_loop_guard.py @@ -112,3 +112,37 @@ def check_and_record( len(boots), int(_chain_gap(window_seconds, max_gap_seconds)), max_restarts, _state_path(), ) return tripped + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def is_restart_loop_tripped( + max_restarts: int = DEFAULT_MAX_RESTARTS, + window_seconds: int = DEFAULT_WINDOW_SECONDS, + *, + now: Optional[float] = None, + max_gap_seconds: int = DEFAULT_MAX_GAP_SECONDS, +) -> bool: + """Return True if the gateway has restarted ``>= max_restarts`` times with + restart-interrupted sessions in one unbroken chain ending at ``now``. + + Reads the persisted boot log written by + ``record_restart_interrupted_boot`` and counts the boots that still chain + together (consecutive gaps within ``max_gap_seconds``), so the verdict does + not depend on how fast the crash cycle happens to be. + Fails OPEN (returns False) on any error — a broken breaker must never + wedge a healthy gateway. + """ + if max_restarts <= 0: + return False + ts = time.time() if now is None else now + gap = _chain_gap(window_seconds, max_gap_seconds) + try: + recent = _chain_ending_at(_load_boots(), ts, gap) + except Exception: # pragma: no cover — _load_boots already guards + return False + return len(recent) >= max_restarts +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/run.py b/gateway/run.py index b45327d6ce..c3a2d89b04 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -5438,3 +5438,73 @@ def _exit_after_graceful_shutdown(exit_code: int) -> None: if __name__ == "__main__": main() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Awaitable # noqa: F401,E402 +from contextvars import Context # noqa: F401,E402 +from typing import Union # noqa: F401,E402 +import faulthandler # noqa: F401,E402 +import functools # noqa: F401,E402 +import inspect # noqa: F401,E402 +from dotenv import load_dotenv # noqa: F401,E402 +import queue # noqa: F401,E402 +from datetime import timedelta # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_GATEWAY_POST_INTERRUPT_GRACE_TIMEOUT': ('gateway.restart', 'DEFAULT_GATEWAY_POST_INTERRUPT_GRACE_TIMEOUT'), + 'DEFAULT_HEARTBEAT_INTERVAL_S': ('gateway.shutdown_watchdog', 'DEFAULT_HEARTBEAT_INTERVAL_S'), + 'DEFAULT_LEASE_WAIT': ('gateway.turn_lease', 'DEFAULT_LEASE_WAIT'), + 'DEFAULT_LOOP_WATCHDOG_INTERVAL_S': ('gateway.shutdown_watchdog', 'DEFAULT_LOOP_WATCHDOG_INTERVAL_S'), + 'DEFAULT_LOOP_WATCHDOG_MAX_STRIKES': ('gateway.shutdown_watchdog', 'DEFAULT_LOOP_WATCHDOG_MAX_STRIKES'), + 'DEFAULT_LOOP_WATCHDOG_TIMEOUT_S': ('gateway.shutdown_watchdog', 'DEFAULT_LOOP_WATCHDOG_TIMEOUT_S'), + 'EphemeralReply': ('gateway.platforms.base', 'EphemeralReply'), + 'GATEWAY_FATAL_CONFIG_EXIT_CODE': ('gateway.restart', 'GATEWAY_FATAL_CONFIG_EXIT_CODE'), + 'GATEWAY_SERVICE_RESTART_EXIT_CODE': ('gateway.restart', 'GATEWAY_SERVICE_RESTART_EXIT_CODE'), + 'SessionEntry': ('gateway.session', 'SessionEntry'), + 'TranscriptReadError': ('gateway.session_transcript', 'TranscriptReadError'), + 'TurnContext': ('gateway.turn_context', 'TurnContext'), + 'TurnLeaseTimeoutError': ('gateway.turn_lease', 'TurnLeaseTimeoutError'), + 'TurnRunner': ('gateway.run_turn_runner', 'TurnRunner'), + 'arm_shutdown_watchdog': ('gateway.shutdown_watchdog', 'arm_shutdown_watchdog'), + 'atomic_json_write': ('utils', 'atomic_json_write'), + 'base_url_hostname': ('utils', 'base_url_hostname'), + 'build_auto_tts_output_path': ('gateway.platforms.base', 'build_auto_tts_output_path'), + 'build_channel_continuity_note': ('gateway.session', 'build_channel_continuity_note'), + 'build_session_context': ('gateway.session', 'build_session_context'), + 'build_session_context_prompt': ('gateway.session', 'build_session_context_prompt'), + 'consume_detached_task_result': ('agent.async_utils', 'consume_detached_task_result'), + 'is_global_startup_conflict': ('gateway.restart', 'is_global_startup_conflict'), + 'is_shared_multi_user_session': ('gateway.session', 'is_shared_multi_user_session'), + 'is_truthy_value': ('utils', 'is_truthy_value'), + 'looks_like_telegram_private_chat_id': ('gateway.delivery', 'looks_like_telegram_private_chat_id'), + 'loop_heartbeat_forever': ('gateway.shutdown_watchdog', 'loop_heartbeat_forever'), + 'merge_pending_message_event': ('gateway.platforms.base', 'merge_pending_message_event'), + 'neutralize_untrusted_inline_text': ('gateway.session', 'neutralize_untrusted_inline_text'), + 'parse_cron_drain_timeout': ('gateway.restart', 'parse_cron_drain_timeout'), + 'parse_restart_after_turn_timeout': ('gateway.restart', 'parse_restart_after_turn_timeout'), + 'parse_restart_drain_timeout': ('gateway.restart', 'parse_restart_drain_timeout'), + 'parse_signal_interrupt_grace_timeout': ('gateway.restart', 'parse_signal_interrupt_grace_timeout'), + 'project_compaction_message_for_display': ('agent.compaction_display', 'project_compaction_message_for_display'), + 'repair_explicit_computer_use_media_paths': ('gateway.media_repair', 'repair_explicit_computer_use_media_paths'), + 'resolve_cron_drain_budget': ('gateway.restart', 'resolve_cron_drain_budget'), + 'resolve_delivery_transport': ('gateway.delivery', 'resolve_delivery_transport'), + 'resolve_shutdown_watchdog_delay': ('gateway.shutdown_watchdog', 'resolve_shutdown_watchdog_delay'), + 'start_loop_liveness_watchdog': ('gateway.shutdown_watchdog', 'start_loop_liveness_watchdog'), + 't': ('agent.i18n', 't'), + 'utf16_len': ('gateway.platforms.base', 'utf16_len'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/session.py b/gateway/session.py index 4cebdf9156..72a7c8e88f 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -1191,3 +1191,30 @@ def build_session_context( context.session_id = session_entry.session_id context.created_at, context.updated_at = session_entry.created_at, session_entry.updated_at return context + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import replace # noqa: F401,E402 +import uuid # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'SessionResetPolicy': ('gateway.config', 'SessionResetPolicy'), + 'TranscriptReadError': ('gateway.session_transcript', 'TranscriptReadError'), + 'atomic_replace': ('utils', 'atomic_replace'), + 'auto_continue_freshness_window': ('gateway.session_lifecycle', 'auto_continue_freshness_window'), + 'extract_api_content_sidecar': ('agent.turn_context', 'extract_api_content_sidecar'), + 'normalize_whatsapp_identifier': ('gateway.whatsapp_identity', 'normalize_whatsapp_identifier'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/slash_access.py b/gateway/slash_access.py index ed7b096f11..07204201e4 100644 --- a/gateway/slash_access.py +++ b/gateway/slash_access.py @@ -118,3 +118,11 @@ def policy_for_source(gateway_config: Any, source: Any) -> SlashAccessPolicy: __all__ = ["SlashAccessPolicy", "policy_from_extra", "policy_for_source"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Tuple # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/slash_commands.py b/gateway/slash_commands.py index bb215473b6..32121ca61f 100644 --- a/gateway/slash_commands.py +++ b/gateway/slash_commands.py @@ -1240,3 +1240,34 @@ class GatewaySlashCommandsMixin( return t("gateway.update.start_failed", error=e) self._schedule_update_notification_watch() return t("gateway.update.starting") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +import hashlib # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'HISTORY_UNREADABLE': ('gateway.slash_commands_status', 'HISTORY_UNREADABLE'), + 'MessageType': ('gateway.platforms.base', 'MessageType'), + 'SessionSource': ('gateway.session', 'SessionSource'), + 'base_url_host_matches': ('utils', 'base_url_host_matches'), + 'build_session_key': ('gateway.session', 'build_session_key'), + 'clear_model_endpoint_credentials': ('hermes_cli.config', 'clear_model_endpoint_credentials'), + 'extract_api_content_sidecar': ('agent.turn_context', 'extract_api_content_sidecar'), + 'fetch_account_usage': ('agent.account_usage', 'fetch_account_usage'), + 'is_shared_multi_user_session': ('gateway.session', 'is_shared_multi_user_session'), + 'render_account_usage_lines': ('agent.account_usage', 'render_account_usage_lines'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/startup_watchdog.py b/gateway/startup_watchdog.py new file mode 100644 index 0000000000..b9f629446e --- /dev/null +++ b/gateway/startup_watchdog.py @@ -0,0 +1,6 @@ +"""PLUGIN-COMPAT stub (revert-scheduled; see COMPAT_MANIFEST.md). + +``gateway.startup_watchdog`` was folded into ``gateway.shutdown_watchdog`` in the Sep 2026 decomposition. This stub keeps the +old import path alive for external plugins only; internal code must import ``gateway.shutdown_watchdog``. +""" +from gateway.shutdown_watchdog import * # noqa: F401,F403 diff --git a/gateway/status.py b/gateway/status.py index 440093e6a9..b356a938b6 100644 --- a/gateway/status.py +++ b/gateway/status.py @@ -1540,3 +1540,25 @@ def get_running_pid_cached( with _gateway_running_pid_cache_lock: _gateway_running_pid_cache[key] = (time.monotonic(), refreshed_signature, pid) return pid + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def clear_planned_stop_marker() -> None: + """Remove the planned-stop marker unconditionally.""" + try: + _get_planned_stop_marker_path().unlink(missing_ok=True) + except OSError: + pass + +def is_gateway_running( + pid_path: Optional[Path] = None, + *, + cleanup_stale: bool = True, +) -> bool: + """Check if the gateway daemon is currently running.""" + return get_running_pid(pid_path, cleanup_stale=cleanup_stale) is not None +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/sticker_cache.py b/gateway/sticker_cache.py index 2760ef030c..cc2a4f3d5a 100644 --- a/gateway/sticker_cache.py +++ b/gateway/sticker_cache.py @@ -61,3 +61,12 @@ def build_animated_sticker_injection(emoji: str = "") -> str: return (f"[The user sent an animated sticker {emoji}~ " f"I can't see animated ones yet, but the emoji suggests: {emoji}]") return "[The user sent an animated sticker~ I can't see animated ones yet]" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +import tempfile # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/stream_consumer.py b/gateway/stream_consumer.py index 603f1735b9..6d06275376 100644 --- a/gateway/stream_consumer.py +++ b/gateway/stream_consumer.py @@ -935,3 +935,24 @@ class GatewayStreamConsumer(StreamTransportMixin, StreamFallbackMixin, StreamThi def _clean_for_display(text: str) -> str: """Hide MEDIA: / [[audio_as_voice]] directives; media is delivered post-stream.""" return _BasePlatformAdapter.strip_media_directives_for_display(text) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'MEDIA_TAG_CLEANUP_RE': ('gateway.platforms.base', 'MEDIA_TAG_CLEANUP_RE'), + 'escape_code_fences_for_display': ('gateway.stream_consumer_fences', 'escape_code_fences_for_display'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/stream_dispatch.py b/gateway/stream_dispatch.py index 140dd11b05..fad35e71b0 100644 --- a/gateway/stream_dispatch.py +++ b/gateway/stream_dispatch.py @@ -82,3 +82,23 @@ class GatewayEventDispatcher: __all__ = ["GatewayEventDispatcher"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ToolCallFinished': ('gateway.stream_events', 'ToolCallFinished'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/gateway/systemd_notify.py b/gateway/systemd_notify.py index da66753533..4fbefeb65a 100644 --- a/gateway/systemd_notify.py +++ b/gateway/systemd_notify.py @@ -117,3 +117,11 @@ class SystemdWatchdog: if self.enabled and not self._stopping_notified: notify("STOPPING=1") self._stopping_notified = True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/agent_import.py b/hermes_cli/agent_import.py index 5b01e0fff3..aacb59361b 100644 --- a/hermes_cli/agent_import.py +++ b/hermes_cli/agent_import.py @@ -639,3 +639,28 @@ def print_import_report(report: Dict[str, Any], dry_run: bool) -> None: parts = [f"{summary[k]} {label}" for k, _, _, label in groups if summary.get(k)] if parts: print_info(f"Summary: {', '.join(parts)}") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def backup_memory_file(path: Path) -> Optional[Path]: + """Snapshot ``path`` before a destructive rewrite; return the backup path. + + Restores parity with the openclaw migration script this module was ported + from, which calls ``maybe_backup(destination)`` before rewriting a memory + store. Uses the same ``.bak.`` naming as + ``MemoryStore._backup_drifted_file``. Returns None when there is nothing + to back up. + """ + if not path.exists(): + return None + backup = path.with_suffix(path.suffix + f".bak.{int(time.time())}") + shutil.copy2(path, backup) + return backup + +def default_source_dir(agent: str) -> Path: + return Path.home() / _AGENT_DEFAULT_DIRS[agent] +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 7a7cf5880f..e8c711a219 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -2185,3 +2185,49 @@ def logout_command(args) -> None: print("Hermes will use OpenRouter for inference.") else: print("Run `hermes model` or configure an API key to use Hermes.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from http.server import BaseHTTPRequestHandler # noqa: F401,E402 +from http.server import HTTPServer # noqa: F401,E402 +from typing import TYPE_CHECKING # noqa: F401,E402 +import base64 # noqa: F401,E402 +import hashlib # noqa: F401,E402 +from urllib.parse import parse_qs # noqa: F401,E402 +import ssl # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import sys # noqa: F401,E402 +from urllib.parse import urlencode # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'CODEX_OAUTH_USER_AGENT': ('hermes_cli.auth_constants', 'CODEX_OAUTH_USER_AGENT'), + 'CODEX_QUOTA_PROBE_MIN_INTERVAL_SECONDS': ('hermes_cli.auth_codex', 'CODEX_QUOTA_PROBE_MIN_INTERVAL_SECONDS'), + 'DEFAULT_SPOTIFY_REDIRECT_URI': ('hermes_cli.auth_constants', 'DEFAULT_SPOTIFY_REDIRECT_URI'), + 'DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDS': ('hermes_cli.auth_constants', 'DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDS'), + 'MINIMAX_OAUTH_GRANT_TYPE': ('hermes_cli.auth_constants', 'MINIMAX_OAUTH_GRANT_TYPE'), + 'NOUS_INFERENCE_INVOKE_SCOPE': ('hermes_cli.auth_constants', 'NOUS_INFERENCE_INVOKE_SCOPE'), + 'NOUS_SHARED_STORE_FILENAME': ('hermes_cli.auth_nous', 'NOUS_SHARED_STORE_FILENAME'), + 'OAUTH_OVER_SSH_DOCS_URL': ('hermes_cli.auth_constants', 'OAUTH_OVER_SSH_DOCS_URL'), + 'QWEN_OAUTH_CLIENT_ID': ('hermes_cli.auth_constants', 'QWEN_OAUTH_CLIENT_ID'), + 'QWEN_OAUTH_TOKEN_URL': ('hermes_cli.auth_constants', 'QWEN_OAUTH_TOKEN_URL'), + 'SINGLE_USE_OAUTH_SINGLETON_FILES': ('hermes_cli.auth_oauth_grants', 'SINGLE_USE_OAUTH_SINGLETON_FILES'), + 'SPOTIFY_ACCESS_TOKEN_REFRESH_SKEW_SECONDS': ('hermes_cli.auth_constants', 'SPOTIFY_ACCESS_TOKEN_REFRESH_SKEW_SECONDS'), + 'SPOTIFY_DASHBOARD_URL': ('hermes_cli.auth_constants', 'SPOTIFY_DASHBOARD_URL'), + 'XAI_OAUTH_DEVICE_CODE_URL': ('hermes_cli.auth_constants', 'XAI_OAUTH_DEVICE_CODE_URL'), + 'XAI_OAUTH_DISCOVERY_URL': ('hermes_cli.auth_constants', 'XAI_OAUTH_DISCOVERY_URL'), + 'XAI_OAUTH_ISSUER': ('hermes_cli.auth_constants', 'XAI_OAUTH_ISSUER'), + 'refresh_nous_oauth_pure': ('hermes_cli.auth_nous', 'refresh_nous_oauth_pure'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/backup.py b/hermes_cli/backup.py index 55f7ccc366..9c4d4fe4da 100644 --- a/hermes_cli/backup.py +++ b/hermes_cli/backup.py @@ -1623,3 +1623,31 @@ def create_pre_migration_backup( found or the write failed. Never raises.""" return _create_prefixed_full_backup( hermes_home, _PRE_MIGRATION_PREFIX, max(keep, 0), "pre-migration", "pre-migration backup") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def copy_db_and_verify(src: Path, dst: Path) -> bool: + """Like :func:`_safe_copy_db` but verifies the destination after copy. + + Returns True only when the copy succeeded AND the destination is valid + SQLite (header + integrity check). Verification honours the default + size ceiling — a multi-GB destination gets the header + schema probe + rather than a full ``PRAGMA integrity_check`` that would page through + the whole file. + """ + if not _safe_copy_db(src, dst): + return False + integrity = verify_sqlite_integrity(dst, run_pragma=True) + if not integrity.get("valid"): + try: + dst.unlink(missing_ok=True) + except OSError: + pass + logger.warning("Backup of %s failed integrity verification: %s", src, integrity.get("message")) + return False + return True +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/browser_connect.py b/hermes_cli/browser_connect.py index fed82e3b20..c75f9890ad 100644 --- a/hermes_cli/browser_connect.py +++ b/hermes_cli/browser_connect.py @@ -920,3 +920,13 @@ def launch_chrome_debug( candidate, attempt.returncode, port, f"; stderr tail: {attempt.stderr_tail}" if attempt.stderr_tail else "") return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def try_launch_chrome_debug(port: int = DEFAULT_BROWSER_CDP_PORT, system: str | None = None) -> bool: + return launch_chrome_debug(port, system).launched +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/callbacks.py b/hermes_cli/callbacks.py index d7fa16fa2f..5c6bf21f75 100644 --- a/hermes_cli/callbacks.py +++ b/hermes_cli/callbacks.py @@ -92,3 +92,126 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict: _invalidate(cli) cprint(f"\n{_DIM} ⏱ Timeout — secret capture cancelled{_RST}") return _skipped(var_name, "timeout", "Secret setup timed out and was skipped.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def approval_callback(cli, command: str, description: str) -> str: + """Prompt for dangerous command approval through the TUI. + + Shows a selection UI with choices: once / session / always / deny. + When the command is longer than 70 characters, a "view" option is + included so the user can reveal the full text before deciding. + + Uses cli._approval_lock to serialize concurrent requests (e.g. from + parallel delegation subtasks) so each prompt gets its own turn. + """ + lock = getattr(cli, "_approval_lock", None) + if lock is None: + import threading + cli._approval_lock = threading.Lock() + lock = cli._approval_lock + + with lock: + from cli import CLI_CONFIG + timeout = CLI_CONFIG.get("approvals", {}).get("timeout", 300) + response_queue = queue.Queue() + choices = ["once", "session", "always", "deny"] + if len(command) > 70: + choices.append("view") + + cli._approval_state = { + "command": command, + "description": description, + "choices": choices, + "selected": 0, + "response_queue": response_queue, + } + cli._approval_deadline = _time.monotonic() + timeout + + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + + while True: + try: + result = response_queue.get(timeout=1) + cli._approval_state = None + cli._approval_deadline = 0 + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + return result + except queue.Empty: + remaining = cli._approval_deadline - _time.monotonic() + if remaining <= 0: + break + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + + cli._approval_state = None + cli._approval_deadline = 0 + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + cprint(f"\n{_DIM} ⏱ Timeout — denying command{_RST}") + return "timeout" + +def clarify_callback(cli, question, choices, multi_select=False): + """Prompt for clarifying question through the TUI. + + Sets up the interactive selection UI, then blocks until the user + responds. Returns the user's choice or a timeout message. + + When ``multi_select`` is True, shows checkboxes and the user can + select multiple options with Space, confirming with Enter. + """ + from cli import CLI_CONFIG + from tools.clarify_gateway import resolve_clarify_timeout + + # Canonical clarify timeout, shared with the gateway/TUI path. `<= 0` + # means unlimited (never auto-skip mid-think) → a null deadline. + timeout = resolve_clarify_timeout(CLI_CONFIG) + response_queue = queue.Queue() + is_open_ended = not choices + effective_multi = multi_select and not is_open_ended + + cli._clarify_state = { + "question": question, + "choices": choices if not is_open_ended else [], + "selected": 0, + "multi_select": effective_multi, + "selected_indices": set() if effective_multi else None, + "response_queue": response_queue, + } + cli._clarify_deadline = None if timeout <= 0 else _time.monotonic() + timeout + cli._clarify_freetext = is_open_ended + + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + + while True: + try: + result = response_queue.get(timeout=1) + cli._clarify_deadline = None + return result + except queue.Empty: + # None deadline = unlimited: never auto-skip, just keep polling. + if cli._clarify_deadline is not None: + remaining = cli._clarify_deadline - _time.monotonic() + if remaining <= 0: + break + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + + cli._clarify_state = None + cli._clarify_freetext = False + cli._clarify_deadline = None + if hasattr(cli, "_app") and cli._app: + cli._app.invalidate() + cprint(f"\n{_DIM}(clarify timed out after {timeout}s — agent will decide){_RST}") + return ( + "The user did not provide a response within the time limit. " + "Use your best judgement to make the choice and proceed." + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/checkpoints.py b/hermes_cli/checkpoints.py index 72388936da..403a130ed0 100644 --- a/hermes_cli/checkpoints.py +++ b/hermes_cli/checkpoints.py @@ -215,3 +215,24 @@ def register_cli(parser: argparse.ArgumentParser) -> None: p_clear = subs.add_parser(name, help=help_text) p_clear.add_argument("-f", "--force", action="store_true", help="Skip confirmation prompt") p_clear.set_defaults(func=func) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from datetime import datetime # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'cmd_list': ('hermes_cli.plugins_cmd', 'cmd_list'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index 7441606d0f..8dfa0f3716 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -467,3 +467,258 @@ def _iter_plugin_command_entries() -> list[tuple[str, str, str]]: return [(name, str(meta.get("description") or f"Run /{name}"), str(meta.get("args_hint") or "").strip()) for name, meta in commands.items() if isinstance(name, str) and isinstance(meta, dict)] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from collections.abc import Callable # noqa: F401,E402 +from typing import Dict # noqa: F401,E402 +from collections.abc import Mapping # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +from collections.abc import Sequence # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import os # noqa: F401,E402 +import shutil # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import time # noqa: F401,E402 + +def _requires_argument(args_hint: str) -> bool: + """Return True when selecting a command without text would be incomplete.""" + return args_hint.strip().startswith("<") + +_CMD_NAME_LIMIT = 32 + +def _clamp_command_names( + entries: Sequence[tuple[str, ...]], + reserved: set[str], +) -> list[tuple[str, ...]]: + """Enforce 32-char command name limit with collision avoidance. + + Both Telegram and Discord cap slash command names at 32 characters. + Names exceeding the limit are truncated. If truncation creates a duplicate + (against *reserved* names or earlier entries in the same batch), the name is + shortened to 31 chars and a digit ``0``-``9`` is appended to differentiate. + If all 10 digit slots are taken the entry is silently dropped. + + Accepts tuples of any length >= 2. Extra elements beyond ``(name, desc)`` + (e.g. ``cmd_key``) are passed through unchanged, so callers can attach + metadata that survives the rename. + """ + used: set[str] = set(reserved) + result: list[tuple] = [] + for entry in entries: + name, desc, *extra = entry + if len(name) > _CMD_NAME_LIMIT: + candidate = name[:_CMD_NAME_LIMIT] + if candidate in used: + prefix = name[:_CMD_NAME_LIMIT - 1] + for digit in range(10): + candidate = f"{prefix}{digit}" + if candidate not in used: + break + else: + # All 10 digit slots exhausted — skip entry + continue + name = candidate + if name in used: + continue + used.add(name) + result.append((name, desc, *extra)) + return result + +def _collect_gateway_skill_entries( + platform: str, + max_slots: int | None, + reserved_names: set[str], + desc_limit: int = 100, + sanitize_name: "Callable[[str], str] | None" = None, +) -> tuple[list[tuple[str, str, str, str]], int]: + """Collect plugin + skill entries for a gateway platform. + + Priority order: + 1. Plugin slash commands (take precedence over skills) + 2. Built-in skill commands (fill remaining slots, alphabetical) + + Only skills are trimmed when the cap is reached. + Hub-installed skills are excluded. Per-platform disabled skills are + excluded. + + Args: + platform: Platform identifier for per-platform skill filtering + (``"telegram"``, ``"discord"``, etc.). + max_slots: Maximum number of entries to return (remaining slots after + built-in/core commands), or ``None`` to return every eligible + plugin and skill candidate for a caller that applies a global cap. + reserved_names: Names already taken by built-in commands. Mutated + in-place as new names are added. + desc_limit: Max description length (40 for Telegram, 100 for Discord). + sanitize_name: Optional name transform applied before clamping, e.g. + :func:`_sanitize_telegram_name` for Telegram. May return an + empty string to signal "skip this entry". + + Returns: + ``(entries, hidden_count)`` where *entries* contains + ``(name, description, cmd_key, raw_name)`` tuples. ``cmd_key`` is the + original skill key (empty for plugins); ``raw_name`` is the sanitized + pre-clamp name used for configured priority matching. + """ + all_entries: list[tuple[str, str, str, str]] = [] + + # --- Tier 1: Plugin slash commands (never trimmed) --------------------- + plugin_pairs: list[tuple[str, str, str]] = [] + try: + from hermes_cli.plugins import get_plugin_commands + plugin_cmds = get_plugin_commands() + for cmd_name in sorted(plugin_cmds): + if platform == "telegram": + args_hint = str(plugin_cmds[cmd_name].get("args_hint") or "").strip() + if _requires_argument(args_hint): + continue + name = sanitize_name(cmd_name) if sanitize_name else cmd_name + if not name: + continue + desc = plugin_cmds[cmd_name].get("description", "Plugin command") + if len(desc) > desc_limit: + desc = desc[:desc_limit - 3] + "..." + plugin_pairs.append((name, desc, name)) + except Exception: + pass + + plugin_pairs = [ + (name, desc, raw_name) + for name, desc, raw_name in _clamp_command_names(plugin_pairs, reserved_names) + ] + reserved_names.update(n for n, _d, _raw_name in plugin_pairs) + # Plugins have no cmd_key — use empty string as placeholder. + for name, desc, raw_name in plugin_pairs: + all_entries.append((name, desc, "", raw_name)) + + # --- Tier 2: Built-in skill commands (trimmed at cap) ----------------- + _platform_disabled: set[str] = set() + try: + from agent.skill_utils import get_disabled_skill_names + _platform_disabled = get_disabled_skill_names(platform=platform) + except Exception: + pass + + skill_entries: list[tuple[str, str, str, str]] = [] + try: + from agent.skill_commands import get_skill_commands + from tools.skills_tool import SKILLS_DIR + from agent.skill_utils import get_external_skills_dirs, get_project_skills_dirs + _skills_dir = str(SKILLS_DIR.resolve()) + _hub_dir = str((SKILLS_DIR / ".hub").resolve()).rstrip("/") + "/" + # Build set of allowed directory prefixes: local skills dir + any + # user-configured ``skills.external_dirs`` + trusted project dirs. + # Ensure each prefix ends + # with ``/`` so ``/my-skills`` does not also match ``/my-skills-extra``. + # Without this widening, external skills are visible in + # ``hermes skills list`` and the agent's ``/skill-name`` dispatch but + # silently excluded from gateway slash menus (#8110). + _allowed_prefixes = [_skills_dir.rstrip("/") + "/"] + _allowed_prefixes.extend( + str(d).rstrip("/") + "/" for d in get_external_skills_dirs() + ) + _allowed_prefixes.extend( + str(d).rstrip("/") + "/" for d in get_project_skills_dirs() + ) + skill_cmds = get_skill_commands() + for cmd_key in sorted(skill_cmds): + info = skill_cmds[cmd_key] + skill_path = info.get("skill_md_path", "") + if not skill_path: + continue + if not any(skill_path.startswith(prefix) for prefix in _allowed_prefixes): + continue + if skill_path.startswith(_hub_dir): + continue + skill_name = info.get("name", "") + if skill_name in _platform_disabled: + continue + raw_name = cmd_key.lstrip("/") + name = sanitize_name(raw_name) if sanitize_name else raw_name + if not name: + continue + desc = info.get("description", "") + if len(desc) > desc_limit: + desc = desc[:desc_limit - 3] + "..." + skill_entries.append((name, desc, cmd_key, name)) + except Exception: + pass + + # Clamp names; cmd_key and raw_name survive any clamp-induced rename. + skill_entries = [ + (name, desc, cmd_key, raw_name) + for name, desc, cmd_key, raw_name in _clamp_command_names( + skill_entries, reserved_names + ) + ] + + if max_slots is None: + return all_entries + skill_entries, 0 + + # Skills fill remaining slots — only tier that gets trimmed + remaining = max(0, max_slots - len(all_entries)) + hidden_count = max(0, len(skill_entries) - remaining) + for name, desc, cmd_key, raw_name in skill_entries[:remaining]: + all_entries.append((name, desc, cmd_key, raw_name)) + + return all_entries[:max_slots], hidden_count + +def discord_skill_commands( + max_slots: int, + reserved_names: set[str], +) -> tuple[list[tuple[str, str, str]], int]: + """Return skill entries for Discord slash command registration. + + Same priority and filtering logic as :func:`telegram_menu_commands` + (plugins > skills, hub excluded, per-platform disabled excluded), but + adapted for Discord's constraints: + + - Hyphens are allowed in names (no ``-`` → ``_`` sanitization) + - Descriptions capped at 100 chars (Discord's per-field max) + + Args: + max_slots: Available command slots (100 minus existing built-in count). + reserved_names: Names of already-registered built-in commands. + + Returns: + ``(entries, hidden_count)`` where *entries* is a list of + ``(discord_name, description, cmd_key)`` triples. ``cmd_key`` is + the original ``/skill-name`` key needed for the slash handler callback. + """ + entries, hidden_count = _collect_gateway_skill_entries( + platform="discord", + max_slots=max_slots, + reserved_names=set(reserved_names), # copy — don't mutate caller's set + desc_limit=100, + ) + return [ + (name, desc, cmd_key) for name, desc, cmd_key, _raw_name in entries + ], hidden_count + + +_PLUGIN_COMPAT_LAZY = { + 'SlashCommandAutoSuggest': ('hermes_cli.commands_completion', 'SlashCommandAutoSuggest'), + 'SlashCommandCompleter': ('hermes_cli.commands_completion', 'SlashCommandCompleter'), + 'discord_skill_commands_by_category': ('hermes_cli.commands_platforms', 'discord_skill_commands_by_category'), + 'slack_app_manifest': ('hermes_cli.commands_platforms', 'slack_app_manifest'), + 'slack_native_slashes': ('hermes_cli.commands_platforms', 'slack_native_slashes'), + 'slack_subcommand_map': ('hermes_cli.commands_platforms', 'slack_subcommand_map'), + 'telegram_bot_commands': ('hermes_cli.commands_platforms', 'telegram_bot_commands'), + 'telegram_menu_commands': ('hermes_cli.commands_platforms', 'telegram_menu_commands'), + 'telegram_menu_max_commands': ('hermes_cli.commands_platforms', 'telegram_menu_max_commands'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 78848329ac..21c780b9ea 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -3854,3 +3854,56 @@ def _inject_platform_plugin_env_vars() -> None: _inject_platform_plugin_env_vars() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def _install_method_project_root(project_root: Optional[Path] = None) -> Path: + """Resolve the directory that holds the *running code* (the install tree). + + This is the parent of ``hermes_cli/`` — i.e. the git checkout for source + installs, ``/opt/hermes`` inside the published image. It is a property of + the running interpreter, NOT of ``$HERMES_HOME``, which is why a + code-scoped stamp here is immune to two installs sharing one data + directory. + """ + if project_root is not None: + return project_root + return Path(__file__).parent.parent.resolve() + +def stamp_install_method(method: str, project_root: Optional[Path] = None) -> None: + """Write the install method next to the running code (code-scoped stamp). + + The stamp lives in the install tree (``/.install_method``), + not in ``$HERMES_HOME``, so that two installs sharing one data directory + do not overwrite each other's marker. See ``detect_install_method`` for + the full rationale. + + Best-effort: if the install tree is read-only (e.g. the immutable + ``/opt/hermes`` in the published image, which instead bakes the stamp at + build time) the write silently no-ops and detection falls back to its + other signals. + """ + root = _install_method_project_root(project_root) + try: + root.mkdir(parents=True, exist_ok=True) + (root / ".install_method").write_text(method + "\n", encoding="utf-8") + except OSError: + pass + + +_PLUGIN_COMPAT_LAZY = { + 'normalize_route_base_url': ('hermes_cli.route_identity', 'normalize_route_base_url'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/console_engine.py b/hermes_cli/console_engine.py index 30ae31e606..db658435bb 100644 --- a/hermes_cli/console_engine.py +++ b/hermes_cli/console_engine.py @@ -853,3 +853,11 @@ def run_console_repl( print(result.output, file=stderr if result.status == "error" else stdout) if result.status == "exit": return 0 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import shlex # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/curses_ui.py b/hermes_cli/curses_ui.py index 2ab74b8086..4c23c8d6e3 100644 --- a/hermes_cli/curses_ui.py +++ b/hermes_cli/curses_ui.py @@ -769,3 +769,11 @@ def _numbered_fallback( if 0 <= idx < len(items): chosen.symmetric_difference_update({idx}) print() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Protocol # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/dashboard_auth/audit.py b/hermes_cli/dashboard_auth/audit.py index 7ebe401f53..101dfa40e4 100644 --- a/hermes_cli/dashboard_auth/audit.py +++ b/hermes_cli/dashboard_auth/audit.py @@ -63,3 +63,11 @@ def audit_log(event: AuditEvent, **fields: Any) -> None: f.write(line) except Exception as e: _log.warning("dashboard-auth audit log write failed: %s", e) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/dashboard_auth/middleware.py b/hermes_cli/dashboard_auth/middleware.py index 80f63a61bb..a48a36008a 100644 --- a/hermes_cli/dashboard_auth/middleware.py +++ b/hermes_cli/dashboard_auth/middleware.py @@ -225,3 +225,23 @@ def _attempt_refresh(request: Request, *, refresh_token, provider_hint: str | No provider_hint, _refresh, phase="refresh", log=_log, swallow=(RefreshExpiredError,), on_swallow=_audit_failure("refresh_expired"), on_unreachable=_audit_failure("provider_unreachable")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'DashboardAuthProvider': ('hermes_cli.dashboard_auth.base', 'DashboardAuthProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/dingtalk_auth.py b/hermes_cli/dingtalk_auth.py index 0c9cd1f9e3..0de0a58c71 100644 --- a/hermes_cli/dingtalk_auth.py +++ b/hermes_cli/dingtalk_auth.py @@ -179,3 +179,24 @@ def dingtalk_qr_auth() -> Optional[Tuple[str, str]]: print_success(f" Client ID: {client_id}") print_success(f" Client Secret: {client_secret[:8]}{'*' * (len(client_secret) - 8)}") return client_id, client_secret + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('hermes_cli.auth', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index 6057b2b034..ffc61b13f0 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -180,3 +180,53 @@ def run_doctor(args): from hermes_cli.doctor_live import maybe_run_live_checks maybe_run_live_checks(args, total.manual_issues) _print_summary(should_fix, total) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import importlib.util # noqa: F401,E402 +import shutil # noqa: F401,E402 +import subprocess # noqa: F401,E402 + +def check_fail(text: str, detail: str = ""): + print(f" {color('✗', Colors.RED)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else "")) + +def check_ok(text: str, detail: str = ""): + print(f" {color('✓', Colors.GREEN)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else "")) + +def check_warn(text: str, detail: str = ""): + print(f" {color('⚠', Colors.YELLOW)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else "")) + + +_PLUGIN_COMPAT_LAZY = { + 'FTS_STORAGE_VERSION': ('hermes_state_common', 'FTS_STORAGE_VERSION'), + 'OPENROUTER_MODELS_URL': ('hermes_constants', 'OPENROUTER_MODELS_URL'), + 'STATE_DB_SIZE_WARN_BYTES': ('hermes_cli.doctor_state', 'STATE_DB_SIZE_WARN_BYTES'), + 'agent_browser_runnable': ('hermes_constants', 'agent_browser_runnable'), + 'base_url_host_matches': ('utils', 'base_url_host_matches'), + 'check_certificates': ('hermes_cli.doctor_platform', 'check_certificates'), + 'check_macos_full_disk_access': ('hermes_cli.doctor_platform', 'check_macos_full_disk_access'), + 'check_macos_tcc_anchor': ('hermes_cli.doctor_platform', 'check_macos_tcc_anchor'), + 'check_macos_tcc_grants': ('hermes_cli.doctor_platform', 'check_macos_tcc_grants'), + 'collect_deprecated_config_keys': ('hermes_cli.doctor_config', 'collect_deprecated_config_keys'), + 'collect_deprecated_env_vars': ('hermes_cli.doctor_config', 'collect_deprecated_env_vars'), + 'collect_relay_plugin_cutover_findings': ('hermes_cli.doctor_config', 'collect_relay_plugin_cutover_findings'), + 'describe_vercel_auth': ('hermes_cli.vercel_auth', 'describe_vercel_auth'), + 'detect_install_method': ('hermes_cli.config', 'detect_install_method'), + 'is_nix_install_method': ('hermes_cli.config', 'is_nix_install_method'), + 'managed_scope_check': ('hermes_cli.doctor_config', 'managed_scope_check'), + 'recommended_update_command_for_method': ('hermes_cli.config', 'recommended_update_command_for_method'), + 'report_deprecated_config_and_env': ('hermes_cli.doctor_config', 'report_deprecated_config_and_env'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/doctor_live.py b/hermes_cli/doctor_live.py index d42bf54cab..08057cf1a7 100644 --- a/hermes_cli/doctor_live.py +++ b/hermes_cli/doctor_live.py @@ -213,3 +213,20 @@ def maybe_run_live_checks(args, issues: List[str]): except Exception as exc: # catch-all: doctor must survive check_warn("Live backend probes crashed", f"({exc})") return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +ELEVENLABS_VOICES_URL = "https://api.elevenlabs.io/v1/voices" + +FAL_MODELS_URL = "https://fal.ai/api/models?page=1" + +FIRECRAWL_HEALTH_URL = "https://api.firecrawl.dev/v2/team/credit-usage" + +GROQ_MODELS_URL = "https://api.groq.com/openai/v1/models" + +OPENAI_MODELS_URL = "https://api.openai.com/v1/models" +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/focus_view.py b/hermes_cli/focus_view.py index 6bcd6201b3..0f7799ac55 100644 --- a/hermes_cli/focus_view.py +++ b/hermes_cli/focus_view.py @@ -80,3 +80,25 @@ def format_focus_toggle_message(enabled: bool, configured_mode: object) -> str: if enabled: return "Focus view enabled — just your prompt and the final response" return f"Focus view disabled — tool progress: {normalize_tool_progress_mode(configured_mode).upper()}" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +FOCUS_USAGE = "Usage: /focus [on|off|status]" + +def effective_tool_progress_mode(focus_enabled: bool, configured_mode: object) -> str: + """Return the tool-progress mode that should actually be in force. + + Focus view wins while it is on (it *is* "tool progress off" plus reporting). + When focus is off the user's configured mode is returned untouched — this is + what makes ``/focus off`` restore ``/verbose verbose`` rather than clobbering + it to ``all``. + """ + normalized = normalize_tool_progress_mode(configured_mode) + if focus_enabled: + return FOCUS_TOOL_PROGRESS_MODE + return normalized +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/foreign_sessions.py b/hermes_cli/foreign_sessions.py index 696650417e..e34fca7505 100644 --- a/hermes_cli/foreign_sessions.py +++ b/hermes_cli/foreign_sessions.py @@ -293,3 +293,66 @@ def run_sessions_import(args, db=None) -> Optional[str]: print(f"✓ Imported {_SOURCE_LABELS.get(source, source)} session as {session_id}") print(f" Continue it with: hermes --resume {session_id}") return session_id + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def list_claude_sessions(root: Optional[Path] = None) -> List[ForeignSession]: + """Discover Claude Code sessions under ``~/.claude/projects``.""" + root = Path(root) if root else Path.home() / ".claude" / "projects" + results: List[ForeignSession] = [] + if not root.is_dir(): + return results + for jsonl in sorted(root.glob("*/*.jsonl")): + try: + mtime = jsonl.stat().st_mtime + except OSError: + continue + parsed = parse_claude_session(jsonl) + if not parsed["turns"]: + continue + results.append( + ForeignSession( + source="claude", + path=jsonl, + mtime=mtime, + cwd=parsed["cwd"], + title_guess=parsed["title_guess"], + turn_count=len(parsed["turns"]), + session_id=parsed["session_id"], + ) + ) + results.sort(key=lambda s: s.mtime, reverse=True) + return results + +def list_codex_sessions(root: Optional[Path] = None) -> List[ForeignSession]: + """Discover Codex CLI rollouts under ``~/.codex/sessions``.""" + root = Path(root) if root else Path.home() / ".codex" / "sessions" + results: List[ForeignSession] = [] + if not root.is_dir(): + return results + for jsonl in sorted(root.rglob("rollout-*.jsonl")): + try: + mtime = jsonl.stat().st_mtime + except OSError: + continue + parsed = parse_codex_session(jsonl) + if not parsed["turns"]: + continue + results.append( + ForeignSession( + source="codex", + path=jsonl, + mtime=mtime, + cwd=parsed["cwd"], + title_guess=parsed["title_guess"], + turn_count=len(parsed["turns"]), + session_id=parsed["session_id"], + ) + ) + results.sort(key=lambda s: s.mtime, reverse=True) + return results +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index 66a8f9aba4..a098154ff3 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -6159,3 +6159,36 @@ def _gateway_command_inner(args): handler = _GATEWAY_SUBCOMMANDS.get(getattr(args, "gateway_command", None)) if handler is not None: handler(args) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def print_systemd_linger_guidance() -> None: + """Print the current linger status and the fix when it is disabled.""" + linger_enabled, linger_detail = get_systemd_linger_status() + if linger_enabled is True: + print("✓ Systemd linger is enabled (service survives logout)") + elif linger_enabled is False: + print("⚠ Systemd linger is disabled (gateway may stop when you log out)") + print(" Run: sudo loginctl enable-linger $USER") + else: + print(f"⚠ Could not verify systemd linger ({linger_detail})") + print(" If you want the gateway user service to survive logout, run:") + print(" sudo loginctl enable-linger $USER") + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT': ('gateway.restart', 'DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/gitlock.py b/hermes_cli/gitlock.py index 850cbfdd12..3f714ad834 100644 --- a/hermes_cli/gitlock.py +++ b/hermes_cli/gitlock.py @@ -96,3 +96,33 @@ def clear_stale_tmp_packs(repo_root: Path, *, min_age_seconds: Optional[int] = N skip_msg="git process running; skipping tmp-pack sweep", log_removed=lambda p, size: logger.info("Removed aborted-fetch pack debris %s (%d bytes)", p, size), ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def is_ancestor_of_head(repo_root: Path, rev: str) -> bool: + """True when ``rev`` is an ancestor of (or equal to) HEAD. + + Wraps ``git merge-base --is-ancestor HEAD``. This is the correct + question for update checks: a local cherry-pick on top of the remote tip + makes HEAD *different* from ``origin/main`` but still *contains* it, so + the answer to "is there an update?" is no. + + Returns False on any probe failure (missing rev, shallow boundary, git + error) — callers treat that as "can't prove contained", which is the + conservative direction for an update check. + """ + try: + result = subprocess.run( + ["git", "merge-base", "--is-ancestor", rev, "HEAD"], + cwd=str(repo_root), + capture_output=True, text=True, timeout=10, + ) + return result.returncode == 0 + except Exception: + logger.debug("merge-base --is-ancestor probe failed for %s", rev, exc_info=True) + return False +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/heartbeat.py b/hermes_cli/heartbeat.py index a2aa3eb457..ed5dddbace 100644 --- a/hermes_cli/heartbeat.py +++ b/hermes_cli/heartbeat.py @@ -236,3 +236,11 @@ __all__ = [ "HeartbeatState", "HeartbeatManager", "parse_interval", "format_interval", "load_heartbeat", "save_heartbeat", "migrate_heartbeat_to_session", "HEARTBEAT_PROMPT_TEMPLATE", "MIN_INTERVAL_SECONDS", "POLL_SECONDS", ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/journey.py b/hermes_cli/journey.py index f6d8df1a3f..0fba1c1efd 100644 --- a/hermes_cli/journey.py +++ b/hermes_cli/journey.py @@ -378,3 +378,13 @@ if __name__ == "__main__": register_cli(_p) _a = _p.parse_args() sys.exit(_a.func(_a)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def cmd_journey(args: argparse.Namespace) -> int: + return _cmd_show(args) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/kanban.py b/hermes_cli/kanban.py index d1734d8a65..a4e76bd2fa 100644 --- a/hermes_cli/kanban.py +++ b/hermes_cli/kanban.py @@ -1322,3 +1322,11 @@ def run_slash(rest: str) -> str: if err and out: return f"{out}\n{err}" return err if err else (out or "(no output)") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/kanban_db.py b/hermes_cli/kanban_db.py index ca99bd4a0e..ea33f297cf 100644 --- a/hermes_cli/kanban_db.py +++ b/hermes_cli/kanban_db.py @@ -4195,3 +4195,86 @@ from hermes_cli.kanban_db_dispatch import ( # noqa: E402 _worker_survived_termination, _worker_terminal_timeout_env, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Mapping # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import random # noqa: F401,E402 +import shutil # noqa: F401,E402 +import threading # noqa: F401,E402 + +DEFAULT_SPAWN_FAILURE_LIMIT = DEFAULT_FAILURE_LIMIT + +def parent_results(conn: sqlite3.Connection, task_id: str) -> list[tuple[str, Optional[str]]]: + """Return ``(parent_id, result)`` for every done parent of ``task_id``.""" + rows = conn.execute( + """ + SELECT t.id AS id, t.result AS result + FROM tasks t + JOIN task_links l ON l.parent_id = t.id + WHERE l.child_id = ? AND t.status = 'done' + ORDER BY t.completed_at ASC + """, + (task_id,), + ).fetchall() + return [(r["id"], r["result"]) for r in rows] + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_BUSY_TIMEOUT_MS': ('hermes_cli.kanban_db_connect', 'DEFAULT_BUSY_TIMEOUT_MS'), + 'DEFAULT_LOG_BACKUP_COUNT': ('hermes_cli.kanban_db_dispatch', 'DEFAULT_LOG_BACKUP_COUNT'), + 'DEFAULT_LOG_ROTATE_BYTES': ('hermes_cli.kanban_db_dispatch', 'DEFAULT_LOG_ROTATE_BYTES'), + 'DERIVED_MAX_IN_PROGRESS_CEILING': ('hermes_cli.kanban_db_dispatch', 'DERIVED_MAX_IN_PROGRESS_CEILING'), + 'DERIVED_MAX_IN_PROGRESS_FLOOR': ('hermes_cli.kanban_db_dispatch', 'DERIVED_MAX_IN_PROGRESS_FLOOR'), + 'KANBAN_TERMINAL_TIMEOUT_GRACE_SECONDS': ('hermes_cli.kanban_db_dispatch', 'KANBAN_TERMINAL_TIMEOUT_GRACE_SECONDS'), + 'KanbanDbCorruptError': ('hermes_cli.kanban_db_connect', 'KanbanDbCorruptError'), + 'MEMORY_GUARD_MB_PER_WORKER': ('hermes_cli.kanban_db_dispatch', 'MEMORY_GUARD_MB_PER_WORKER'), + 'RepairResult': ('hermes_cli.kanban_db_connect', 'RepairResult'), + 'add_notify_sub': ('hermes_cli.kanban_db_notify', 'add_notify_sub'), + 'advance_notify_cursor': ('hermes_cli.kanban_db_notify', 'advance_notify_cursor'), + 'check_respawn_guard': ('hermes_cli.kanban_db_dispatch', 'check_respawn_guard'), + 'claim_unseen_events_for_sub': ('hermes_cli.kanban_db_notify', 'claim_unseen_events_for_sub'), + 'configured_max_in_progress': ('hermes_cli.kanban_db_dispatch', 'configured_max_in_progress'), + 'connect': ('hermes_cli.projects_db', 'connect'), + 'connect_closing': ('hermes_cli.projects_db', 'connect_closing'), + 'count_notify_subs': ('hermes_cli.kanban_db_notify', 'count_notify_subs'), + 'count_running_tasks': ('hermes_cli.kanban_db_dispatch', 'count_running_tasks'), + 'count_running_tasks_other_boards': ('hermes_cli.kanban_db_dispatch', 'count_running_tasks_other_boards'), + 'derive_default_max_in_progress': ('hermes_cli.kanban_db_dispatch', 'derive_default_max_in_progress'), + 'detect_crashed_workers': ('hermes_cli.kanban_db_dispatch', 'detect_crashed_workers'), + 'detect_stale_running': ('hermes_cli.kanban_db_dispatch', 'detect_stale_running'), + 'dispatch_once': ('hermes_cli.kanban_db_dispatch', 'dispatch_once'), + 'enforce_max_runtime': ('hermes_cli.kanban_db_dispatch', 'enforce_max_runtime'), + 'has_spawnable_ready': ('hermes_cli.kanban_db_dispatch', 'has_spawnable_ready'), + 'has_spawnable_review': ('hermes_cli.kanban_db_dispatch', 'has_spawnable_review'), + 'heartbeat_worker': ('hermes_cli.kanban_db_dispatch', 'heartbeat_worker'), + 'list_notify_subs': ('hermes_cli.kanban_db_notify', 'list_notify_subs'), + 'purge_stale_done_notify_subs': ('hermes_cli.kanban_db_notify', 'purge_stale_done_notify_subs'), + 'reap_worker_zombies': ('hermes_cli.kanban_db_dispatch', 'reap_worker_zombies'), + 'reconcile_orphaned_running': ('hermes_cli.kanban_db_dispatch', 'reconcile_orphaned_running'), + 'remove_notify_sub': ('hermes_cli.kanban_db_notify', 'remove_notify_sub'), + 'repair_db': ('hermes_cli.kanban_db_connect', 'repair_db'), + 'resolve_max_in_progress': ('hermes_cli.kanban_db_dispatch', 'resolve_max_in_progress'), + 'resolve_workspace': ('hermes_cli.kanban_db_workspace', 'resolve_workspace'), + 'review_dispatch_enabled': ('hermes_cli.kanban_db_dispatch', 'review_dispatch_enabled'), + 'rewind_notify_cursor': ('hermes_cli.kanban_db_notify', 'rewind_notify_cursor'), + 'run_daemon': ('hermes_cli.kanban_db_dispatch', 'run_daemon'), + 'set_branch_name': ('hermes_cli.kanban_db_workspace', 'set_branch_name'), + 'set_workspace_path': ('hermes_cli.kanban_db_workspace', 'set_workspace_path'), + 'unseen_events_for_sub': ('hermes_cli.kanban_db_notify', 'unseen_events_for_sub'), + 'worker_log_rotation_config': ('hermes_cli.kanban_db_dispatch', 'worker_log_rotation_config'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/kanban_decompose.py b/hermes_cli/kanban_decompose.py index ab11cf6dbd..d2a93ecacf 100644 --- a/hermes_cli/kanban_decompose.py +++ b/hermes_cli/kanban_decompose.py @@ -336,3 +336,12 @@ def list_triage_ids(*, tenant: Optional[str] = None) -> list[str]: with kbc.connect_closing() as conn: rows = kb.list_tasks(conn, status="triage", tenant=tenant, limit=1000) return [row.id for row in rows] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/kanban_diagnostics.py b/hermes_cli/kanban_diagnostics.py index 577339acb1..38ec59b440 100644 --- a/hermes_cli/kanban_diagnostics.py +++ b/hermes_cli/kanban_diagnostics.py @@ -773,3 +773,22 @@ def compute_task_diagnostics( severity_idx = {s: i for i, s in enumerate(SEVERITY_ORDER)} out.sort(key=lambda d: (-severity_idx.get(d.severity, -1), -(d.last_seen_at or 0))) return out + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +DIAGNOSTIC_KINDS = ( + "hallucinated_cards", + "triage_aux_unavailable", + "prose_phantom_refs", + "repeated_failures", + "repeated_crashes", + "review_dependency_deadlock", + "stuck_in_blocked", + "block_unblock_cycling", + "stranded_in_ready", +) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/local_runtime/binaries.py b/hermes_cli/local_runtime/binaries.py index 8c9257bea2..3ee4ef9f13 100644 --- a/hermes_cli/local_runtime/binaries.py +++ b/hermes_cli/local_runtime/binaries.py @@ -299,3 +299,23 @@ def ensure_runtime_installed(tag: str, backend: str, "verified_version": version}, indent=2), encoding="utf-8") logger.info("installed llama.cpp %s (%s): %s", tag, backend, version) return install_dir + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'get_hermes_home': ('hermes_constants', 'get_hermes_home'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/local_runtime/bootstrap.py b/hermes_cli/local_runtime/bootstrap.py index 105dcd29b2..280fdcbfa4 100644 --- a/hermes_cli/local_runtime/bootstrap.py +++ b/hermes_cli/local_runtime/bootstrap.py @@ -281,3 +281,23 @@ def _start_idle_sweeper(sup) -> None: logger.debug("idle sweep skipped: %s", exc) threading.Thread(target=_loop, daemon=True, name="local-runtime-idle-sweep").start() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'get_hermes_home': ('hermes_constants', 'get_hermes_home'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/local_runtime/capabilities.py b/hermes_cli/local_runtime/capabilities.py index f85a8b3253..ccb918483e 100644 --- a/hermes_cli/local_runtime/capabilities.py +++ b/hermes_cli/local_runtime/capabilities.py @@ -77,3 +77,12 @@ def managed_model_supports_vision(model_id: str) -> "bool | None": return (entry.mmproj is not None and (assets_dir() / entry.mmproj.local_name).exists()) return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import urllib.request # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/local_runtime/catalog.py b/hermes_cli/local_runtime/catalog.py index b13373fd6a..e73a336bd9 100644 --- a/hermes_cli/local_runtime/catalog.py +++ b/hermes_cli/local_runtime/catalog.py @@ -330,3 +330,22 @@ def find_entry_for_model(model_id: str) -> "tuple[CatalogEntry, QuantVariant] | def entry_for_model(model_id: str) -> "CatalogEntry | None": hit = find_entry_for_model(model_id) return hit[0] if hit is not None else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import re # noqa: F401,E402 + +def find_variant(entry_id: str, model_id: str) -> QuantVariant | None: + entry = catalog_by_id().get(entry_id) + if entry is None: + return None + return next((v for v in entry.variants if v.model_id == model_id), None) + +def recommended_id(budget: HardwareBudget, + entries: "tuple[CatalogEntry, ...] | None" = None) -> str | None: + picked = recommended_entry(budget, entries) + return picked[0].id if picked is not None else None +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/local_runtime/hf_browse.py b/hermes_cli/local_runtime/hf_browse.py index 2289871e2e..fb8776c03c 100644 --- a/hermes_cli/local_runtime/hf_browse.py +++ b/hermes_cli/local_runtime/hf_browse.py @@ -132,3 +132,11 @@ def rough_fit(total_bytes: int, budget) -> str: def priced_repo_files(repo: str, budget) -> list[HFFileGroup]: return [replace(g, fit=rough_fit(g.total_bytes, budget)) for g in repo_files(repo)] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import field # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/main.py b/hermes_cli/main.py index f624da99bf..813b5235fc 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -3403,3 +3403,29 @@ def main(): if __name__ == "__main__": main() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import hashlib # noqa: F401,E402 +import shlex # noqa: F401,E402 +import stat # noqa: F401,E402 +import tempfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'line_input': ('hermes_cli.cli_output', 'line_input'), +} + +_plugin_compat_prev_getattr = __getattr__ + + +def __getattr__(name): # PEP 562 — chained onto the module's own __getattr__ + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + return _plugin_compat_prev_getattr(name) + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/mcp_picker.py b/hermes_cli/mcp_picker.py index ea957a022d..24619a3d82 100644 --- a/hermes_cli/mcp_picker.py +++ b/hermes_cli/mcp_picker.py @@ -218,3 +218,23 @@ def install_by_name(identifier: str) -> int: ) return 1 return 0 if _install(entry, "install") else 1 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'color': ('hermes_cli.colors', 'color'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/mcp_security.py b/hermes_cli/mcp_security.py index 8365ea42e3..38bd405921 100644 --- a/hermes_cli/mcp_security.py +++ b/hermes_cli/mcp_security.py @@ -133,3 +133,13 @@ def validate_mcp_server_entry(name: str, entry: dict[str, Any]) -> list[str]: f"MCP server" ) return issues + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def is_mcp_server_entry_suspicious(name: str, entry: dict[str, Any]) -> bool: + return bool(validate_mcp_server_entry(name, entry)) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/middleware.py b/hermes_cli/middleware.py index 7f791aa93b..897e4afc07 100644 --- a/hermes_cli/middleware.py +++ b/hermes_cli/middleware.py @@ -211,3 +211,29 @@ def _run_execution_chain(kind: str, terminal_call: Callable[[Any], Any], **kwarg return call_at(index + 1, payload) return call_at(0, kwargs[payload_key]) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +API_EXECUTION_MIDDLEWARE = LLM_EXECUTION_MIDDLEWARE + +API_REQUEST_MIDDLEWARE = LLM_REQUEST_MIDDLEWARE + +def apply_api_request_middleware( + request: Dict[str, Any], + **context: Any, +) -> RequestMiddlewareResult: + """Compatibility wrapper for older ``api_request`` naming.""" + return apply_llm_request_middleware(request, **context) + +def run_api_execution_middleware( + request: Dict[str, Any], + next_call: Callable[[Dict[str, Any]], Any], + **context: Any, +) -> Any: + """Compatibility wrapper for older ``api_execution`` naming.""" + return run_llm_execution_middleware(request, next_call, **context) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/moa_config.py b/hermes_cli/moa_config.py index 0e35a866d0..4749ef0eac 100644 --- a/hermes_cli/moa_config.py +++ b/hermes_cli/moa_config.py @@ -329,3 +329,37 @@ def decode_moa_turn(message: Any) -> tuple[str, dict[str, Any] | None]: def moa_usage() -> str: return "Usage: /moa (runs one prompt through the default MoA preset, then restores your model; pick a preset from the model picker to switch for the session)" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def encode_moa_turn(prompt: str, config: Any = None, preset: str | None = None) -> str: + """Encode a /moa one-shot turn for frontends that can only send text.""" + payload = { + "prompt": str(prompt or ""), + "config": resolve_moa_preset(config or {}, preset), + } + encoded = base64.urlsafe_b64encode( + json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + ).decode("ascii") + return f"{MOA_MARKER_PREFIX}{encoded}" + +def build_moa_turn_prompt(user_prompt: str, config: Any = None, preset: str | None = None) -> str: + """Build the hidden one-shot payload used by TUI/gateway routing.""" + return encode_moa_turn(user_prompt, config, preset=preset) + +def list_moa_presets(config: Any) -> list[str]: + cfg = normalize_moa_config(config) + return list(cfg["presets"].keys()) + +def set_active_moa_preset(config: Any, name: str | None) -> dict[str, Any]: + cfg = normalize_moa_config(config) + clean = str(name or "").strip() + if clean and clean not in cfg["presets"]: + raise KeyError(clean) + cfg["active_preset"] = clean + return cfg +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/model_setup_flows.py b/hermes_cli/model_setup_flows.py index 00df4ce120..d046cd41a8 100644 --- a/hermes_cli/model_setup_flows.py +++ b/hermes_cli/model_setup_flows.py @@ -1047,3 +1047,29 @@ def _model_flow_anthropic(config, current_model=""): # Clear base_url: resolve_runtime_provider() always hardcodes Anthropic's URL, and a # stale value can contaminate other providers on a later switch. _finish_model(selected, "anthropic", f"Default model set to: {selected} (via Anthropic)", drop_base_url=True, drop_api_mode=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import subprocess # noqa: F401,E402 +import urllib.parse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BEDROCK_GEO_PREFIXES': ('hermes_cli.model_setup_flows_bedrock', 'BEDROCK_GEO_PREFIXES'), + 'bedrock_model_routable_from_region': ('hermes_cli.model_setup_flows_bedrock', 'bedrock_model_routable_from_region'), + 'bedrock_region_geo_prefix': ('hermes_cli.model_setup_flows_bedrock', 'bedrock_region_geo_prefix'), + 'custom_provider_slug': ('hermes_cli.providers', 'custom_provider_slug'), + 'line_input': ('hermes_cli.cli_output', 'line_input'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 6649653f60..02f2939a13 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -1494,3 +1494,29 @@ def _scoped_key_env(name: str) -> str: return (get_secret(name, "") or "").strip() if name else "" except Exception: return "" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +import http.client # noqa: F401,E402 +import time # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'base_url_host_matches': ('utils', 'base_url_host_matches'), + 'custom_provider_slug': ('hermes_cli.providers', 'custom_provider_slug'), + 'list_picker_providers': ('hermes_cli.model_switch_providers', 'list_picker_providers'), + 'prewarm_picker_cache_async': ('hermes_cli.model_switch_providers', 'prewarm_picker_cache_async'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 541d23f1e8..c43a4758b7 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -2372,3 +2372,101 @@ def cached_fetch_api_models( if _cache_entry_valid(entry, fp): return list(entry["models"]) return live + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import NamedTuple # noqa: F401,E402 +from difflib import get_close_matches # noqa: F401,E402 +import http.client # noqa: F401,E402 + +def is_nous_free_tier(account_info: dict[str, Any]) -> bool: + """Return True if the account info indicates a free (unpaid) tier. + + Prefer the Portal's explicit ``paid_service_access.allowed`` entitlement + decision. Legacy payloads fall back to ``subscription.monthly_charge == 0``. + Returns False when both signals are missing or unparseable. + """ + paid_access = account_info.get("paid_service_access") + if isinstance(paid_access, dict): + allowed = paid_access.get("allowed") + if isinstance(allowed, bool): + return not allowed + paid = paid_access.get("paid_access") + if isinstance(paid, bool): + return not paid + + sub = account_info.get("subscription") + if not isinstance(sub, dict): + return False + charge = sub.get("monthly_charge") + if charge is None: + return False + try: + return float(charge) == 0 + except (TypeError, ValueError): + return False + +_OPENCODE_KEYLESS_EXTRA_SLUGS = frozenset({"big-pickle"}) + +def is_opencode_zen_free_model(model_id: Optional[str]) -> bool: + """True when ``model_id`` is an OpenCode Zen free-tier slug. + + Matches the ``*-free`` suffix plus the known unsuffixed free slugs + (``big-pickle``). Tolerates provider-prefixed ids + (``opencode-zen/x-preview-f-free``). The Go catalog serves no free + models (verified 2026-08-21), so this identifies the Zen free tier + across the OpenCode family. + """ + bare = str(model_id or "").strip().rsplit("/", 1)[-1].lower() + if not bare: + return False + return bare.endswith("-free") or bare in _OPENCODE_KEYLESS_EXTRA_SLUGS + + +_PLUGIN_COMPAT_LAZY = { + 'LMStudioLoadResult': ('hermes_cli.models_local', 'LMStudioLoadResult'), + 'PROVIDER_GROUPS': ('hermes_cli.models_catalog_static', 'PROVIDER_GROUPS'), + 'ProviderEntry': ('hermes_cli.models_catalog_static', 'ProviderEntry'), + 'atomic_json_write': ('utils', 'atomic_json_write'), + 'base_url_host_matches': ('utils', 'base_url_host_matches'), + 'compute_sale_discount': ('hermes_cli.models_pricing', 'compute_sale_discount'), + 'ensure_lmstudio_model_loaded': ('hermes_cli.models_local', 'ensure_lmstudio_model_loaded'), + 'fetch_ai_gateway_pricing': ('hermes_cli.models_pricing', 'fetch_ai_gateway_pricing'), + 'fetch_lmstudio_models': ('hermes_cli.models_local', 'fetch_lmstudio_models'), + 'fetch_models_with_pricing': ('hermes_cli.models_pricing', 'fetch_models_with_pricing'), + 'fetch_ollama_local_models': ('hermes_cli.models_local', 'fetch_ollama_local_models'), + 'get_cached_nous_inference_base_url': ('hermes_cli.models_pricing', 'get_cached_nous_inference_base_url'), + 'get_pricing_for_provider': ('hermes_cli.models_pricing', 'get_pricing_for_provider'), + 'group_providers': ('hermes_cli.models_catalog_static', 'group_providers'), + 'lmstudio_model_reasoning_options': ('hermes_cli.models_local', 'lmstudio_model_reasoning_options'), + 'nous_catalog_url': ('hermes_cli.models_reasoning_caps', 'nous_catalog_url'), + 'nous_model_reasoning_capabilities': ('hermes_cli.models_reasoning_caps', 'nous_model_reasoning_capabilities'), + 'nous_policy_allowed_ids': ('hermes_cli.models_pricing', 'nous_policy_allowed_ids'), + 'ollama_model_supports_thinking': ('hermes_cli.models_local', 'ollama_model_supports_thinking'), + 'openrouter_model_reasoning_capabilities': ('hermes_cli.models_reasoning_caps', 'openrouter_model_reasoning_capabilities'), + 'parse_openrouter_reasoning_capabilities': ('hermes_cli.models_reasoning_caps', 'parse_openrouter_reasoning_capabilities'), + 'peek_cached_pricing': ('hermes_cli.models_pricing', 'peek_cached_pricing'), + 'pricing_cache_scope': ('hermes_cli.models_pricing', 'pricing_cache_scope'), + 'probe_lmstudio_models': ('hermes_cli.models_local', 'probe_lmstudio_models'), + 'probe_ollama_local_models': ('hermes_cli.models_local', 'probe_ollama_local_models'), + 'provider_group_for_slug': ('hermes_cli.models_catalog_static', 'provider_group_for_slug'), + 'refresh_reasoning_caps_async': ('hermes_cli.models_reasoning_caps', 'refresh_reasoning_caps_async'), + 'restrict_to_nous_policy': ('hermes_cli.models_pricing', 'restrict_to_nous_policy'), + 'should_use_ollama_native_catalog': ('hermes_cli.models_local', 'should_use_ollama_native_catalog'), + 'url_origin': ('hermes_cli.urllib_security', 'url_origin'), + 'validate_requested_model': ('hermes_cli.models_validate', 'validate_requested_model'), + 'warm_nous_reasoning_caps_async': ('hermes_cli.models_reasoning_caps', 'warm_nous_reasoning_caps_async'), + 'warm_openrouter_reasoning_caps_async': ('hermes_cli.models_reasoning_caps', 'warm_openrouter_reasoning_caps_async'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/nous_billing.py b/hermes_cli/nous_billing.py index ecb37b27b7..a431f72dc2 100644 --- a/hermes_cli/nous_billing.py +++ b/hermes_cli/nous_billing.py @@ -392,3 +392,12 @@ def post_subscription_upgrade( return _post_idempotent( "/api/billing/subscription/upgrade", {"subscriptionTypeId": subscription_type_id}, idempotency_key, "an upgrade", timeout ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +BILLING_MANAGE_SCOPE = "billing:manage" +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/nous_subscription.py b/hermes_cli/nous_subscription.py index f423cb14e2..bff6b08d3c 100644 --- a/hermes_cli/nous_subscription.py +++ b/hermes_cli/nous_subscription.py @@ -723,3 +723,23 @@ def _run_nous_portal_login_only(*, capability: str) -> bool: except Exception as exc: print(f" Nous Portal login failed: {exc}") return False + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/observability/relay_runtime.py b/hermes_cli/observability/relay_runtime.py new file mode 100644 index 0000000000..09810b3499 --- /dev/null +++ b/hermes_cli/observability/relay_runtime.py @@ -0,0 +1,6 @@ +"""PLUGIN-COMPAT stub (revert-scheduled; see COMPAT_MANIFEST.md). + +``hermes_cli.observability.relay_runtime`` was folded into ``agent.relay_runtime`` in the Sep 2026 decomposition. This stub keeps the +old import path alive for external plugins only; internal code must import ``agent.relay_runtime``. +""" +from agent.relay_runtime import * # noqa: F401,F403 diff --git a/hermes_cli/observability/relay_shared_metrics.py b/hermes_cli/observability/relay_shared_metrics.py index 129905743e..e496659a4a 100644 --- a/hermes_cli/observability/relay_shared_metrics.py +++ b/hermes_cli/observability/relay_shared_metrics.py @@ -1046,3 +1046,44 @@ def _reset_for_tests() -> None: for runtime in runtimes: if isinstance(runtime, _Runtime): runtime.shutdown() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def prepare_session_start() -> None: + """Register the subscriber before any producer opens the session scope.""" + if enabled(): + _get_runtime(retry_failed=True) + + +_PLUGIN_COMPAT_LAZY = { + 'CLIENT_ACTIVE_MARK': ('hermes_cli.observability.shared_metrics_contract', 'CLIENT_ACTIVE_MARK'), + 'MODEL_CALL_PROFILE_MODEL': ('hermes_cli.observability.shared_metrics_contract', 'MODEL_CALL_PROFILE_MODEL'), + 'SCHEMA_KEY': ('hermes_cli.observability.shared_metrics_contract', 'SCHEMA_KEY'), + 'SCHEMA_VERSION': ('hermes_cli.observability.shared_metrics_contract', 'SCHEMA_VERSION'), + 'SKILL_LIFECYCLE_MARK': ('hermes_cli.observability.shared_metrics_contract', 'SKILL_LIFECYCLE_MARK'), + 'SKILL_LOAD_MARK': ('hermes_cli.observability.shared_metrics_contract', 'SKILL_LOAD_MARK'), + 'TOOL_APPROVAL_MARK': ('hermes_cli.observability.shared_metrics_contract', 'TOOL_APPROVAL_MARK'), + 'TOOL_CALL_SCOPE': ('hermes_cli.observability.shared_metrics_contract', 'TOOL_CALL_SCOPE'), + 'model_call_fields': ('hermes_cli.observability.shared_metrics_contract', 'model_call_fields'), + 'skill_lifecycle_fields': ('hermes_cli.observability.shared_metrics_contract', 'skill_lifecycle_fields'), + 'skill_load_fields': ('hermes_cli.observability.shared_metrics_contract', 'skill_load_fields'), + 'task_start_fields': ('hermes_cli.observability.shared_metrics_contract', 'task_start_fields'), + 'task_terminal_fields': ('hermes_cli.observability.shared_metrics_contract', 'task_terminal_fields'), + 'task_terminal_state': ('hermes_cli.observability.shared_metrics_contract', 'task_terminal_state'), + 'tool_approval_outcome': ('hermes_cli.observability.shared_metrics_contract', 'tool_approval_outcome'), + 'tool_category': ('hermes_cli.observability.shared_metrics_contract', 'tool_category'), + 'tool_terminal_fields': ('hermes_cli.observability.shared_metrics_contract', 'tool_terminal_fields'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/onepassword_secrets_cli.py b/hermes_cli/onepassword_secrets_cli.py index 6d70bcd941..6accb552f3 100644 --- a/hermes_cli/onepassword_secrets_cli.py +++ b/hermes_cli/onepassword_secrets_cli.py @@ -393,3 +393,25 @@ def _op_whoami(binary: Path, account: str, *, token_value: str = "") -> Optional if res.returncode != 0: return None return (res.stdout or "").strip().replace("\n", " ")[:120] or "authenticated" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from rich.table import Table # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'masked_secret_prompt': ('hermes_cli.secret_prompt', 'masked_secret_prompt'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/platform_actions.py b/hermes_cli/platform_actions.py index 5cd1c36f90..192e2ac8ff 100644 --- a/hermes_cli/platform_actions.py +++ b/hermes_cli/platform_actions.py @@ -201,3 +201,11 @@ class PlatformActions: result.get("ok"), "" if result.get("ok") else f" error={result.get('error')}", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/plugins.py b/hermes_cli/plugins.py index 1cf03f9984..cef22a5b4a 100644 --- a/hermes_cli/plugins.py +++ b/hermes_cli/plugins.py @@ -2034,3 +2034,67 @@ def get_plugin_toolsets() -> List[tuple]: desc = (plugin.manifest.description if plugin else "") or ", ".join(sorted(toolset_tools[ts_key])) result.append((ts_key, f"🔌 {ts_key.replace('_', ' ').title()}", desc)) return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Iterable # noqa: F401,E402 +from typing import Type # noqa: F401,E402 +from contextlib import contextmanager # noqa: F401,E402 +import contextvars # noqa: F401,E402 +import copy # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import time # noqa: F401,E402 +from functools import wraps # noqa: F401,E402 + +def get_plugin_subscriptions() -> Dict[str, List[Callable]]: + """Return the inter-plugin event bus subscription registry. + + Returns a snapshot mapping each fully-qualified event name + (``:`` or ``hermes:``) to subscriber callbacks in + registration order. Owner ledger metadata stays private to the manager. + Triggers idempotent plugin discovery before reading the snapshot. + """ + manager = _ensure_plugins_discovered() + with manager._event_lock: + return { + event: [entry.callback for entry in entries] + for event, entries in manager._subscriptions.items() + } + +def unload_plugins( + plugin: Union[str, PluginManifest, LoadedPlugin, None] = None, +) -> bool: + """Unload one plugin or all plugins from the process-global manager. + + Wait for background discovery first so teardown cannot race an in-flight + registration sweep introduced by the warm-start discovery path. + """ + _join_background_discovery() + return get_plugin_manager().unload(plugin) + + +_PLUGIN_COMPAT_LAZY = { + 'CAPABILITY_REGISTRY': ('hermes_cli.plugin_capabilities', 'CAPABILITY_REGISTRY'), + 'ENTRY_POINT_CAPABILITIES_GROUP': ('hermes_cli.plugins_discovery', 'ENTRY_POINT_CAPABILITIES_GROUP'), + 'LEGACY_RELAY_PLUGIN_KEYS': ('hermes_cli.relay_plugin_cutover', 'LEGACY_RELAY_PLUGIN_KEYS'), + 'MAX_SYSTEM_PROMPT_SECTIONS': ('hermes_cli.plugins_dispatch', 'MAX_SYSTEM_PROMPT_SECTIONS'), + 'OBSERVER_SCHEMA_VERSION': ('hermes_cli.middleware', 'OBSERVER_SCHEMA_VERSION'), + 'VALID_CAPABILITY_IDS': ('hermes_cli.plugin_capabilities', 'VALID_CAPABILITY_IDS'), + 'cfg_get': ('hermes_cli.config', 'cfg_get'), + 'fast_safe_load': ('utils', 'fast_safe_load'), + 'format_system_prompt_section': ('hermes_cli.plugins_dispatch', 'format_system_prompt_section'), + 'reset_hermes_home_override': ('hermes_constants', 'reset_hermes_home_override'), + 'set_hermes_home_override': ('hermes_constants', 'set_hermes_home_override'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/plugins_cmd.py b/hermes_cli/plugins_cmd.py index 0f40a28095..6505a7f3f1 100644 --- a/hermes_cli/plugins_cmd.py +++ b/hermes_cli/plugins_cmd.py @@ -2035,3 +2035,11 @@ def plugins_command(args) -> None: if handler is None: _fail(_console(), f"[red]Unknown plugins action: {action}[/red]") handler(args) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import importlib.metadata # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/profile_describer.py b/hermes_cli/profile_describer.py index f0eda23821..97f04aa9f9 100644 --- a/hermes_cli/profile_describer.py +++ b/hermes_cli/profile_describer.py @@ -190,3 +190,11 @@ def list_describable_profiles(*, missing_only: bool = True) -> list[str]: p.name for p in profiles_mod.list_profiles() if not (missing_only and (p.description or "").strip() and not p.description_auto) ] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/profile_distribution.py b/hermes_cli/profile_distribution.py index 3c1a8a7f13..984aea9fd3 100644 --- a/hermes_cli/profile_distribution.py +++ b/hermes_cli/profile_distribution.py @@ -458,3 +458,23 @@ def describe_distribution(profile_name: str) -> Dict[str, Any]: """Return a structured view of a profile's distribution metadata ({} if not a distribution).""" manifest = read_manifest(_existing_profile(profile_name)[1]) return {} if manifest is None else manifest.to_dict() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'is_excluded_skill_path': ('agent.skill_utils', 'is_excluded_skill_path'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/profiles.py b/hermes_cli/profiles.py index 035cbfd3f8..e4a0d3b1a3 100644 --- a/hermes_cli/profiles.py +++ b/hermes_cli/profiles.py @@ -1702,3 +1702,17 @@ def resolve_profile_env(profile_name: str) -> str: if not profile_dir.is_dir() or named_profile_is_deleted(profile_dir): raise _missing_profile_error(canon) return str(profile_dir) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def has_bundled_skills_opt_out(profile_dir: Path) -> bool: + """Return True if the profile opted out of bundled-skill seeding.""" + try: + return (profile_dir / NO_BUNDLED_SKILLS_MARKER).exists() + except OSError: + return False +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index d1807bdc2c..6d62983e8e 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -867,3 +867,25 @@ def _ladder_rungs(requested_provider, explicit_api_key, explicit_base_url, targe def format_runtime_provider_error(error: Exception) -> str: return format_auth_error(error) if isinstance(error, AuthError) else str(error) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'custom_provider_aliases': ('hermes_cli.providers', 'custom_provider_aliases'), + 'custom_provider_slug': ('hermes_cli.providers', 'custom_provider_slug'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/security_advisories.py b/hermes_cli/security_advisories.py index 1d807e99a4..f1c4f6f708 100644 --- a/hermes_cli/security_advisories.py +++ b/hermes_cli/security_advisories.py @@ -273,3 +273,27 @@ def gateway_log_message(hits: list[AdvisoryHit]) -> Optional[str]: f"matches {h.advisory.title}. See {h.advisory.url}") return (f"{len(fresh)} security advisories active (IDs: {', '.join(h.advisory.id for h in fresh)}). " "Run `hermes doctor` on the gateway host for details.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def render_doctor_section(hits: list[AdvisoryHit]) -> tuple[bool, list[str]]: + """Render the security-advisory section for ``hermes doctor``. + + Returns ``(has_problems, lines)``. Caller is responsible for printing + with whatever color scheme it uses. + """ + fresh = filter_unacked(hits) + if not fresh: + return False, ["No active security advisories. ✓"] + + lines: list[str] = [] + for i, hit in enumerate(fresh): + if i: + lines.append("") + lines.extend(full_remediation_text(hit)) + return True, lines +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 0b444ba462..f5c0c98e74 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -782,3 +782,30 @@ def _run_setup_wizard_impl(args): "If setup changed a value you customized, restore it with:", f" cp {_backup_path} {config_path}") _print_setup_summary(config, hermes_home) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from typing import Dict # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +import json # noqa: F401,E402 +import shutil # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'get_nous_subscription_features': ('hermes_cli.nous_subscription', 'get_nous_subscription_features'), + 'get_optional_skills_dir': ('hermes_constants', 'get_optional_skills_dir'), + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/slack_cli.py b/hermes_cli/slack_cli.py index 1c89955d8e..fe52b4ddb5 100644 --- a/hermes_cli/slack_cli.py +++ b/hermes_cli/slack_cli.py @@ -145,3 +145,11 @@ def slack_manifest_command(args) -> int: " (xoxb-...) and app token (xapp-...) configured via\n" " `hermes setup`.\n", file=sys.stderr) return 0 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/sqlite_safe_read.py b/hermes_cli/sqlite_safe_read.py index fecbd03efb..154e43f31c 100644 --- a/hermes_cli/sqlite_safe_read.py +++ b/hermes_cli/sqlite_safe_read.py @@ -237,3 +237,12 @@ def offline_file_access(path: Path | str, *, what: str = "read"): "connection's POSIX advisory locks. Close all database " "handles (stop the gateway/dashboard) and retry.") yield + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +SQLITE_HEADER_MAGIC = b"SQLite format 3\x00" +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/status.py b/hermes_cli/status.py index a16f9520ff..7d6965e3a9 100644 --- a/hermes_cli/status.py +++ b/hermes_cli/status.py @@ -355,3 +355,28 @@ def show_status(args): nous_inference_present=False, nous_account_info=None) for render in _SECTIONS: render(ctx) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import subprocess # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'format_nous_portal_entitlement_message': ('hermes_cli.nous_account', 'format_nous_portal_entitlement_message'), + 'get_nous_portal_account_info': ('hermes_cli.nous_account', 'get_nous_portal_account_info'), + 'get_nous_subscription_features': ('hermes_cli.nous_subscription', 'get_nous_subscription_features'), + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), + 'redact_key': ('hermes_cli.config', 'redact_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/telegram_managed_bot.py b/hermes_cli/telegram_managed_bot.py index 79054f7c72..c124f49e85 100644 --- a/hermes_cli/telegram_managed_bot.py +++ b/hermes_cli/telegram_managed_bot.py @@ -182,3 +182,101 @@ def auto_setup_telegram_bot_result( print(" The bot may still be created — check Telegram.\n" " You can paste the token manually below, or re-run setup.") return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import secrets # noqa: F401,E402 +import urllib.parse # noqa: F401,E402 +import secrets # noqa: F401,E402 +import urllib.parse # noqa: F401,E402 + +DEFAULT_MANAGER_BOT = "HermesSetupBot" + +def auto_setup_telegram_bot( + api_url: str | None = None, + manager_bot: str = DEFAULT_MANAGER_BOT, + profile_name: Optional[str] = None, + poll_timeout: float = DEFAULT_POLL_TIMEOUT, +) -> Optional[str]: + """Run automatic Telegram bot creation and return only the bot token.""" + result = auto_setup_telegram_bot_result( + api_url=api_url, + manager_bot=manager_bot, + profile_name=profile_name, + poll_timeout=poll_timeout, + ) + return result.token if result else None + +_USERNAME_SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567" + +def generate_username_slug(length: int = 16) -> str: + """Generate a base32-ish slug for Telegram username correlation. + + Sixteen characters from a 32-symbol alphabet gives 80 bits of entropy while + keeping ``hermes__bot`` under Telegram's 32-character username limit. + """ + return "".join(secrets.choice(_USERNAME_SLUG_ALPHABET) for _ in range(length)) + +def generate_bot_username(profile_name: Optional[str] = None) -> str: + """Generate a secure suggested bot username like ``hermes__bot``. + + ``profile_name`` is accepted for backward compatibility with the original + PoC, but is intentionally not embedded in the username. The username has to + carry enough entropy for backend correlation. + """ + _ = profile_name + return f"hermes_{generate_username_slug()}_bot" + +def generate_deep_link( + manager_bot: str = DEFAULT_MANAGER_BOT, + suggested_username: Optional[str] = None, + suggested_name: Optional[str] = None, +) -> str: + """Build a ``t.me/newbot`` deep link for managed bot creation.""" + manager = manager_bot.lstrip("@") + username = suggested_username or generate_bot_username() + base_url = ( + "https://t.me/newbot/" + f"{urllib.parse.quote(manager)}/" + f"{urllib.parse.quote(username)}" + ) + + if suggested_name: + params = urllib.parse.urlencode({"name": suggested_name}) + return f"{base_url}?{params}" + return base_url + +def generate_pairing_nonce() -> str: + """Generate a legacy-compatible random nonce string. + + The new protocol uses service-created ``pairing_id`` + bearer + ``poll_token`` instead of a path nonce, but this helper is harmless and + still useful for callers/tests that need a generic random id. + """ + return secrets.token_hex(16) + +def poll_pairing_once( + api_url: str | None, + pairing: TelegramPairing, + timeout: float = 10.0, +) -> str | None: + """Poll the onboarding service once. Returns the token when ready.""" + result = poll_pairing_result_once(api_url, pairing, timeout=timeout) + return result.token if result else None + + +_PLUGIN_COMPAT_LAZY = { + 'poll_for_token': ('plugins.platforms.photon.auth', 'poll_for_token'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index b0dc86d113..8c2ab10d99 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -1055,3 +1055,32 @@ def tools_command(args=None, first_install: bool = False, config: dict = None): print(color(f" Tool configuration saved to {display_hermes_home()}/config.yaml", Colors.DIM)) print(color(" Changes take effect on next 'hermes' or gateway restart.", Colors.DIM)) print() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import shutil # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'MANAGED_FEATURE_COVERAGE_CATEGORY': ('hermes_cli.nous_subscription', 'MANAGED_FEATURE_COVERAGE_CATEGORY'), + 'NOUS_MANAGED_PROVIDER': ('tools.tool_backend_helpers', 'NOUS_MANAGED_PROVIDER'), + 'base_url_hostname': ('utils', 'base_url_hostname'), + 'fal_key_is_configured': ('tools.tool_backend_helpers', 'fal_key_is_configured'), + 'format_nous_portal_entitlement_message': ('hermes_cli.nous_account', 'format_nous_portal_entitlement_message'), + 'is_truthy_value': ('utils', 'is_truthy_value'), + 'save_env_value': ('hermes_cli.config', 'save_env_value'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/uninstall.py b/hermes_cli/uninstall.py index d4940f490e..a17ab7edc2 100644 --- a/hermes_cli/uninstall.py +++ b/hermes_cli/uninstall.py @@ -735,3 +735,29 @@ def main(argv=None) -> int: if __name__ == "__main__": sys.exit(main()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def find_shell_configs() -> list: + """Find shell configuration files that might have PATH entries.""" + home = Path.home() + configs = [] + + candidates = [ + home / ".bashrc", + home / ".bash_profile", + home / ".profile", + home / ".zshrc", + home / ".zprofile", + ] + + for config in candidates: + if config.exists(): + configs.append(config) + + return configs +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/update_cmd.py b/hermes_cli/update_cmd.py index d08f123438..14068d1cdd 100644 --- a/hermes_cli/update_cmd.py +++ b/hermes_cli/update_cmd.py @@ -1340,3 +1340,14 @@ def _cmd_update_impl(args, gateway_mode: bool): _refuse_update_for_contended_shims(e) except subprocess.CalledProcessError as e: _handle_update_called_process_error(e, args, gateway_mode, had_desktop_app_before_update) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/update_inventory.py b/hermes_cli/update_inventory.py index 97dbc937f5..912d66dcc3 100644 --- a/hermes_cli/update_inventory.py +++ b/hermes_cli/update_inventory.py @@ -389,3 +389,12 @@ def record_plan_in_receipt(plan: UpdatePlan) -> None: ur._current.data["plan"] = plan.to_dict() except Exception as exc: logger.debug("Could not record plan in receipt: %s", exc) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_deps.py b/hermes_cli/web_deps.py index 5c2b99ad56..c7f464656e 100644 --- a/hermes_cli/web_deps.py +++ b/hermes_cli/web_deps.py @@ -107,3 +107,26 @@ class LateState: def __repr__(self) -> str: # pragma: no cover - debugging aid return f" {self._target()!r}>" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_dashboard_health(): + """The ``DASHBOARD_HEALTH`` singleton owned by web_server.""" + return _server().DASHBOARD_HEALTH + +def get_session_token() -> str: + """Current dashboard session token (``web_server._SESSION_TOKEN``).""" + return _server()._SESSION_TOKEN + +def has_valid_session_token(request) -> bool: + """Late-bound alias for ``web_server._has_valid_session_token``.""" + return _server()._has_valid_session_token(request) + +def late_attr(name: str) -> Any: + """Read ``web_server.`` right now (for non-callable state reads).""" + return getattr(_server(), name) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_routers/cron.py b/hermes_cli/web_routers/cron.py index bb1e571735..43ed0ba76b 100644 --- a/hermes_cli/web_routers/cron.py +++ b/hermes_cli/web_routers/cron.py @@ -418,3 +418,11 @@ async def instantiate_blueprint(body: AutomationBlueprintInstantiate, profile: s _raise_if_cron_registration_error(e) _log.exception("POST /api/cron/blueprints/instantiate failed") raise HTTPException(status_code=400, detail=str(e)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_routers/mcp.py b/hermes_cli/web_routers/mcp.py index 3308d68e1d..8ab90c95fa 100644 --- a/hermes_cli/web_routers/mcp.py +++ b/hermes_cli/web_routers/mcp.py @@ -449,3 +449,11 @@ async def install_mcp_catalog_entry(body: MCPCatalogInstall, profile: Optional[s _log.exception("install_mcp_catalog_entry failed") raise HTTPException(status_code=400, detail=str(exc)) return {"ok": True, "name": name, "background": False} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_routers/sessions.py b/hermes_cli/web_routers/sessions.py index 4831d836ed..eb4450c0cc 100644 --- a/hermes_cli/web_routers/sessions.py +++ b/hermes_cli/web_routers/sessions.py @@ -691,3 +691,13 @@ async def export_session_endpoint(session_id: str, profile: Optional[str] = None async def prune_sessions_endpoint(body: SessionPrune): """Delete ended sessions matching filters without blocking the event loop.""" return await asyncio.to_thread(_prune_sessions, body) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from typing import Dict # noqa: F401,E402 +import logging # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_routers/skills.py b/hermes_cli/web_routers/skills.py index 84829a3212..9c78a1b956 100644 --- a/hermes_cli/web_routers/skills.py +++ b/hermes_cli/web_routers/skills.py @@ -436,3 +436,24 @@ async def update_skill_content(body: SkillContentUpdate): raise HTTPException(status_code=status, detail=err) _clear_skills_prompt_cache() return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'LateState': ('hermes_cli.web_deps', 'LateState'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_routers/tools.py b/hermes_cli/web_routers/tools.py index d09adb2a34..6340b3da01 100644 --- a/hermes_cli/web_routers/tools.py +++ b/hermes_cli/web_routers/tools.py @@ -683,3 +683,24 @@ async def grant_computer_use_permissions(profile: Optional[str] = None): profile, ["computer-use", "permissions", "grant"], "computer-use-grant", log_msg="Failed to spawn computer-use permissions grant", prefix="Failed to request permissions") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'LateState': ('hermes_cli.web_deps', 'LateState'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 422c67b8b0..d5fd2a3e13 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -1418,3 +1418,410 @@ def start_server( await server.shutdown() _run_serve(_serve, config, host, port) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +from typing import Literal # noqa: F401,E402 +import atexit # noqa: F401,E402 +import base64 # noqa: F401,E402 +import binascii # noqa: F401,E402 +import concurrent.futures # noqa: F401,E402 +import contextlib # noqa: F401,E402 +from contextlib import contextmanager # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +import functools # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import importlib.util # noqa: F401,E402 +import inspect # noqa: F401,E402 +import ipaddress # noqa: F401,E402 +import json # noqa: F401,E402 +import math # noqa: F401,E402 +import mimetypes # noqa: F401,E402 +import queue # noqa: F401,E402 +import shlex # noqa: F401,E402 +import shutil # noqa: F401,E402 +import stat # noqa: F401,E402 +import tempfile # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 +import yaml # noqa: F401,E402 +import zipfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'AudioTranscriptionRequest': ('hermes_cli.web_models', 'AudioTranscriptionRequest'), + 'AutomationBlueprintInstantiate': ('hermes_cli.web_models', 'AutomationBlueprintInstantiate'), + 'BackupRequest': ('hermes_cli.web_models', 'BackupRequest'), + 'BulkDeleteSessions': ('hermes_cli.web_models', 'BulkDeleteSessions'), + 'CONFIG_SCHEMA': ('hermes_cli.web_server_config', 'CONFIG_SCHEMA'), + 'ChatImageUpload': ('hermes_cli.web_models', 'ChatImageUpload'), + 'ConfigUpdate': ('hermes_cli.web_models', 'ConfigUpdate'), + 'CredentialPoolAdd': ('hermes_cli.web_models', 'CredentialPoolAdd'), + 'CronJobCreate': ('hermes_cli.web_models', 'CronJobCreate'), + 'CronJobUpdate': ('hermes_cli.web_models', 'CronJobUpdate'), + 'CuratorPause': ('hermes_cli.web_models', 'CuratorPause'), + 'CustomEndpointUpdate': ('hermes_cli.web_models', 'CustomEndpointUpdate'), + 'DEFAULT_CONFIG': ('hermes_cli.config', 'DEFAULT_CONFIG'), + 'DebugShareRequest': ('hermes_cli.web_models', 'DebugShareRequest'), + 'EnvVarDelete': ('hermes_cli.web_models', 'EnvVarDelete'), + 'EnvVarReveal': ('hermes_cli.web_models', 'EnvVarReveal'), + 'EnvVarUpdate': ('hermes_cli.web_models', 'EnvVarUpdate'), + 'FontSetBody': ('hermes_cli.web_models', 'FontSetBody'), + 'FsWriteText': ('hermes_cli.web_models', 'FsWriteText'), + 'GitBranchSwitchBody': ('hermes_cli.web_models', 'GitBranchSwitchBody'), + 'GitCommitBody': ('hermes_cli.web_models', 'GitCommitBody'), + 'GitFileBody': ('hermes_cli.web_models', 'GitFileBody'), + 'GitPathBody': ('hermes_cli.web_models', 'GitPathBody'), + 'GitWorktreeAddBody': ('hermes_cli.web_models', 'GitWorktreeAddBody'), + 'GitWorktreeRemoveBody': ('hermes_cli.web_models', 'GitWorktreeRemoveBody'), + 'HookCreate': ('hermes_cli.web_models', 'HookCreate'), + 'HookDelete': ('hermes_cli.web_models', 'HookDelete'), + 'ImportRequest': ('hermes_cli.web_models', 'ImportRequest'), + 'LearningNodeEdit': ('hermes_cli.web_models', 'LearningNodeEdit'), + 'LearningNodeRef': ('hermes_cli.web_models', 'LearningNodeRef'), + 'MCPCatalogInstall': ('hermes_cli.web_models', 'MCPCatalogInstall'), + 'MCPEnabledToggle': ('hermes_cli.web_models', 'MCPEnabledToggle'), + 'MCPServerCreate': ('hermes_cli.web_models', 'MCPServerCreate'), + 'MCPServersReplace': ('hermes_cli.web_models', 'MCPServersReplace'), + 'ManagedDirectoryCreate': ('hermes_cli.web_models', 'ManagedDirectoryCreate'), + 'ManagedFileDelete': ('hermes_cli.web_models', 'ManagedFileDelete'), + 'ManagedFileUpload': ('hermes_cli.web_models', 'ManagedFileUpload'), + 'ManagedFilesPolicy': ('hermes_cli.web_server_files', 'ManagedFilesPolicy'), + 'MemoryProviderConfigUpdate': ('hermes_cli.web_models', 'MemoryProviderConfigUpdate'), + 'MemoryProviderSelect': ('hermes_cli.web_models', 'MemoryProviderSelect'), + 'MemoryProviderSetupRequest': ('hermes_cli.web_models', 'MemoryProviderSetupRequest'), + 'MemoryReset': ('hermes_cli.web_models', 'MemoryReset'), + 'MessagingPlatformUpdate': ('hermes_cli.web_models', 'MessagingPlatformUpdate'), + 'MoaConfigPayload': ('hermes_cli.web_models', 'MoaConfigPayload'), + 'MoaModelSlot': ('hermes_cli.web_models', 'MoaModelSlot'), + 'MoaPresetPayload': ('hermes_cli.web_models', 'MoaPresetPayload'), + 'ModelAssignment': ('hermes_cli.web_models', 'ModelAssignment'), + 'OAuthSubmitBody': ('hermes_cli.web_models', 'OAuthSubmitBody'), + 'OPTIONAL_ENV_VARS': ('hermes_cli.config', 'OPTIONAL_ENV_VARS'), + 'PairingApprove': ('hermes_cli.web_models', 'PairingApprove'), + 'PairingRevoke': ('hermes_cli.web_models', 'PairingRevoke'), + 'ProfileActiveUpdate': ('hermes_cli.web_models', 'ProfileActiveUpdate'), + 'ProfileCreate': ('hermes_cli.web_models', 'ProfileCreate'), + 'ProfileDescribeAuto': ('hermes_cli.web_models', 'ProfileDescribeAuto'), + 'ProfileDescriptionUpdate': ('hermes_cli.web_models', 'ProfileDescriptionUpdate'), + 'ProfileModelUpdate': ('hermes_cli.web_models', 'ProfileModelUpdate'), + 'ProfileRename': ('hermes_cli.web_models', 'ProfileRename'), + 'ProfileSoulUpdate': ('hermes_cli.web_models', 'ProfileSoulUpdate'), + 'ProviderConfigSchema': ('plugins.memory.config_schema', 'ProviderConfigSchema'), + 'ProviderField': ('plugins.memory.config_schema', 'ProviderField'), + 'PtyBridge': ('hermes_cli.pty_bridge', 'PtyBridge'), + 'PtySessionRegistry': ('hermes_cli.pty_session', 'PtySessionRegistry'), + 'PtyUnavailableError': ('hermes_cli.pty_bridge', 'PtyUnavailableError'), + 'RawConfigUpdate': ('hermes_cli.web_models', 'RawConfigUpdate'), + 'RegistryFull': ('hermes_cli.pty_session', 'RegistryFull'), + 'STORAGE_HONCHO_HOST_BLOCK': ('plugins.memory.config_schema', 'STORAGE_HONCHO_HOST_BLOCK'), + 'SessionImport': ('hermes_cli.web_models', 'SessionImport'), + 'SessionPrune': ('hermes_cli.web_models', 'SessionPrune'), + 'SessionRename': ('hermes_cli.web_models', 'SessionRename'), + 'SkillContentUpdate': ('hermes_cli.web_models', 'SkillContentUpdate'), + 'SkillCreate': ('hermes_cli.web_models', 'SkillCreate'), + 'SkillInstallRequest': ('hermes_cli.web_models', 'SkillInstallRequest'), + 'SkillToggle': ('hermes_cli.web_models', 'SkillToggle'), + 'SkillUninstallRequest': ('hermes_cli.web_models', 'SkillUninstallRequest'), + 'SkillsUpdateRequest': ('hermes_cli.web_models', 'SkillsUpdateRequest'), + 'TTSLeaseRequest': ('hermes_cli.web_models', 'TTSLeaseRequest'), + 'TTSSpeakRequest': ('hermes_cli.web_models', 'TTSSpeakRequest'), + 'TelegramOnboardingApply': ('hermes_cli.web_models', 'TelegramOnboardingApply'), + 'TelegramOnboardingStart': ('hermes_cli.web_models', 'TelegramOnboardingStart'), + 'TerminalBackendSelect': ('hermes_cli.web_models', 'TerminalBackendSelect'), + 'ThemeSetBody': ('hermes_cli.web_models', 'ThemeSetBody'), + 'ToolsetEnvUpdate': ('hermes_cli.web_models', 'ToolsetEnvUpdate'), + 'ToolsetModelSelect': ('hermes_cli.web_models', 'ToolsetModelSelect'), + 'ToolsetPostSetup': ('hermes_cli.web_models', 'ToolsetPostSetup'), + 'ToolsetProviderSelect': ('hermes_cli.web_models', 'ToolsetProviderSelect'), + 'ToolsetToggle': ('hermes_cli.web_models', 'ToolsetToggle'), + 'WebhookCreate': ('hermes_cli.web_models', 'WebhookCreate'), + 'WebhookEnabledToggle': ('hermes_cli.web_models', 'WebhookEnabledToggle'), + 'WhatsAppOnboardingApply': ('hermes_cli.web_models', 'WhatsAppOnboardingApply'), + 'WhatsAppOnboardingStart': ('hermes_cli.web_models', 'WhatsAppOnboardingStart'), + 'activate_custom_endpoint': ('hermes_cli.web_routers.config_env', 'activate_custom_endpoint'), + 'add_credential_pool_entry': ('hermes_cli.web_routers.ops', 'add_credential_pool_entry'), + 'add_mcp_server': ('hermes_cli.web_routers.mcp', 'add_mcp_server'), + 'apply_telegram_onboarding': ('hermes_cli.web_routers.messaging', 'apply_telegram_onboarding'), + 'apply_whatsapp_onboarding': ('hermes_cli.web_routers.messaging', 'apply_whatsapp_onboarding'), + 'approve_pairing': ('hermes_cli.web_routers.ops', 'approve_pairing'), + 'auth_mcp_server': ('hermes_cli.web_routers.mcp', 'auth_mcp_server'), + 'build_cron_model_impact': ('hermes_cli.config', 'build_cron_model_impact'), + 'bulk_delete_sessions_endpoint': ('hermes_cli.web_routers.sessions', 'bulk_delete_sessions_endpoint'), + 'cancel_oauth_session': ('hermes_cli.web_routers.oauth', 'cancel_oauth_session'), + 'cancel_telegram_onboarding': ('hermes_cli.web_routers.messaging', 'cancel_telegram_onboarding'), + 'cancel_whatsapp_onboarding': ('hermes_cli.web_routers.messaging', 'cancel_whatsapp_onboarding'), + 'cfg_get': ('hermes_cli.config', 'cfg_get'), + 'check_config_version': ('hermes_cli.config', 'check_config_version'), + 'check_hermes_update': ('hermes_cli.web_routers.actions', 'check_hermes_update'), + 'clear_model_endpoint_credentials': ('hermes_cli.config', 'clear_model_endpoint_credentials'), + 'clear_pending_pairing': ('hermes_cli.web_routers.ops', 'clear_pending_pairing'), + 'coerce_provider_id': ('hermes_cli.config', 'coerce_provider_id'), + 'console_ws': ('hermes_cli.web_routers.chat_ws', 'console_ws'), + 'count_empty_sessions_endpoint': ('hermes_cli.web_routers.sessions', 'count_empty_sessions_endpoint'), + 'create_cron_job': ('hermes_cli.web_routers.cron', 'create_cron_job'), + 'create_hook': ('hermes_cli.web_routers.ops', 'create_hook'), + 'create_managed_directory': ('hermes_cli.web_routers.files', 'create_managed_directory'), + 'create_profile_endpoint': ('hermes_cli.web_routers.profiles', 'create_profile_endpoint'), + 'create_skill': ('hermes_cli.web_routers.skills', 'create_skill'), + 'create_webhook': ('hermes_cli.web_routers.ops', 'create_webhook'), + 'cron_fire_webhook': ('hermes_cli.web_routers.cron', 'cron_fire_webhook'), + 'custom_endpoint_key_env': ('hermes_cli.config', 'custom_endpoint_key_env'), + 'delete_agent_plugin': ('hermes_cli.web_routers.dashboard_ui', 'delete_agent_plugin'), + 'delete_cron_job': ('hermes_cli.web_routers.cron', 'delete_cron_job'), + 'delete_custom_endpoint': ('hermes_cli.web_routers.config_env', 'delete_custom_endpoint'), + 'delete_empty_sessions_endpoint': ('hermes_cli.web_routers.sessions', 'delete_empty_sessions_endpoint'), + 'delete_hook': ('hermes_cli.web_routers.ops', 'delete_hook'), + 'delete_learning_node': ('hermes_cli.web_routers.status', 'delete_learning_node'), + 'delete_managed_file': ('hermes_cli.web_routers.files', 'delete_managed_file'), + 'delete_profile_endpoint': ('hermes_cli.web_routers.profiles', 'delete_profile_endpoint'), + 'delete_session_endpoint': ('hermes_cli.web_routers.sessions', 'delete_session_endpoint'), + 'delete_webhook': ('hermes_cli.web_routers.ops', 'delete_webhook'), + 'derive_gateway_busy': ('gateway.status', 'derive_gateway_busy'), + 'derive_gateway_drainable': ('gateway.status', 'derive_gateway_drainable'), + 'describe_profile_auto_endpoint': ('hermes_cli.web_routers.profiles', 'describe_profile_auto_endpoint'), + 'detect_install_method': ('hermes_cli.config', 'detect_install_method'), + 'disconnect_oauth_provider': ('hermes_cli.web_routers.oauth', 'disconnect_oauth_provider'), + 'download_dashboard_backup': ('hermes_cli.web_routers.ops', 'download_dashboard_backup'), + 'download_managed_file': ('hermes_cli.web_routers.files', 'download_managed_file'), + 'enable_webhooks': ('hermes_cli.web_routers.ops', 'enable_webhooks'), + 'env_var_enabled': ('utils', 'env_var_enabled'), + 'events_ws': ('hermes_cli.web_routers.chat_ws', 'events_ws'), + 'export_session_endpoint': ('hermes_cli.web_routers.sessions', 'export_session_endpoint'), + 'find_provider_entry': ('hermes_cli.config', 'find_provider_entry'), + 'format_docker_update_message': ('hermes_cli.config', 'format_docker_update_message'), + 'fs_default_cwd': ('hermes_cli.web_routers.files', 'fs_default_cwd'), + 'fs_download': ('hermes_cli.web_routers.files', 'fs_download'), + 'fs_git_root': ('hermes_cli.web_routers.files', 'fs_git_root'), + 'fs_list': ('hermes_cli.web_routers.files', 'fs_list'), + 'fs_read_data_url': ('hermes_cli.web_routers.files', 'fs_read_data_url'), + 'fs_read_text': ('hermes_cli.web_routers.files', 'fs_read_text'), + 'fs_write_text': ('hermes_cli.web_routers.files', 'fs_write_text'), + 'gateway_drain': ('hermes_cli.web_routers.actions', 'gateway_drain'), + 'gateway_ws': ('hermes_cli.web_routers.chat_ws', 'gateway_ws'), + 'get_action_status': ('hermes_cli.web_routers.actions', 'get_action_status'), + 'get_active_profile_endpoint': ('hermes_cli.web_routers.profiles', 'get_active_profile_endpoint'), + 'get_auxiliary_models': ('hermes_cli.web_routers.models', 'get_auxiliary_models'), + 'get_client_voice_config': ('hermes_cli.web_routers.audio', 'get_client_voice_config'), + 'get_computer_use_status': ('hermes_cli.web_routers.tools', 'get_computer_use_status'), + 'get_config': ('hermes_cli.web_routers.config_env', 'get_config'), + 'get_config_path': ('hermes_cli.config', 'get_config_path'), + 'get_config_raw': ('hermes_cli.web_routers.analytics', 'get_config_raw'), + 'get_cron_delivery_targets': ('hermes_cli.web_routers.cron', 'get_cron_delivery_targets'), + 'get_cron_job': ('hermes_cli.web_routers.cron', 'get_cron_job'), + 'get_curator_status': ('hermes_cli.web_routers.status', 'get_curator_status'), + 'get_dashboard_font': ('hermes_cli.web_routers.dashboard_ui', 'get_dashboard_font'), + 'get_dashboard_plugins': ('hermes_cli.web_routers.dashboard_ui', 'get_dashboard_plugins'), + 'get_dashboard_themes': ('hermes_cli.web_routers.dashboard_ui', 'get_dashboard_themes'), + 'get_defaults': ('hermes_cli.web_routers.config_env', 'get_defaults'), + 'get_egress_status': ('hermes_cli.web_routers.config_env', 'get_egress_status'), + 'get_elevenlabs_voices': ('hermes_cli.web_routers.audio', 'get_elevenlabs_voices'), + 'get_env_path': ('hermes_cli.config', 'get_env_path'), + 'get_env_vars': ('hermes_cli.web_routers.config_env', 'get_env_vars'), + 'get_health': ('hermes_cli.web_routers.status', 'get_health'), + 'get_hermes_home': ('hermes_cli.config', 'get_hermes_home'), + 'get_learning_graph': ('hermes_cli.web_routers.status', 'get_learning_graph'), + 'get_learning_node': ('hermes_cli.web_routers.status', 'get_learning_node'), + 'get_logs': ('hermes_cli.web_routers.status', 'get_logs'), + 'get_media': ('hermes_cli.web_routers.files', 'get_media'), + 'get_memory_provider_config': ('hermes_cli.web_routers.memory_providers', 'get_memory_provider_config'), + 'get_memory_status': ('hermes_cli.web_routers.ops', 'get_memory_status'), + 'get_messaging_platforms': ('hermes_cli.web_routers.messaging', 'get_messaging_platforms'), + 'get_moa_models': ('hermes_cli.web_routers.models', 'get_moa_models'), + 'get_model_info': ('hermes_cli.web_routers.models', 'get_model_info'), + 'get_model_options': ('hermes_cli.web_routers.models', 'get_model_options'), + 'get_models_analytics': ('hermes_cli.web_routers.analytics', 'get_models_analytics'), + 'get_plugins_hub': ('hermes_cli.web_routers.dashboard_ui', 'get_plugins_hub'), + 'get_portal_status': ('hermes_cli.web_routers.status', 'get_portal_status'), + 'get_process_hermes_home': ('hermes_cli.config', 'get_process_hermes_home'), + 'get_profile_setup_command': ('hermes_cli.web_routers.profiles', 'get_profile_setup_command'), + 'get_profile_soul': ('hermes_cli.web_routers.profiles', 'get_profile_soul'), + 'get_profiles_sessions': ('hermes_cli.web_routers.profiles', 'get_profiles_sessions'), + 'get_profiles_sessions_sidebar': ('hermes_cli.web_routers.profiles', 'get_profiles_sessions_sidebar'), + 'get_provider_config_schema': ('plugins.memory.config_schema', 'get_provider_config_schema'), + 'get_recommended_default_model': ('hermes_cli.web_routers.models', 'get_recommended_default_model'), + 'get_running_pid': ('gateway.status', 'get_running_pid'), + 'get_running_pid_cached': ('gateway.status', 'get_running_pid_cached'), + 'get_runtime_status_running_pid': ('gateway.status', 'get_runtime_status_running_pid'), + 'get_schema': ('hermes_cli.web_routers.config_env', 'get_schema'), + 'get_session_detail': ('hermes_cli.web_routers.sessions', 'get_session_detail'), + 'get_session_latest_descendant': ('hermes_cli.web_routers.sessions', 'get_session_latest_descendant'), + 'get_session_messages': ('hermes_cli.web_routers.sessions', 'get_session_messages'), + 'get_session_stats': ('hermes_cli.web_routers.sessions', 'get_session_stats'), + 'get_sessions': ('hermes_cli.web_routers.sessions', 'get_sessions'), + 'get_skill_content': ('hermes_cli.web_routers.skills', 'get_skill_content'), + 'get_skills': ('hermes_cli.web_routers.skills', 'get_skills'), + 'get_ssh_ownership': ('hermes_cli.web_routers.status', 'get_ssh_ownership'), + 'get_status': ('hermes_cli.web_routers.status', 'get_status'), + 'get_system_stats': ('hermes_cli.web_routers.status', 'get_system_stats'), + 'get_telegram_onboarding_status': ('hermes_cli.web_routers.messaging', 'get_telegram_onboarding_status'), + 'get_terminal_backends': ('hermes_cli.web_routers.tools', 'get_terminal_backends'), + 'get_toolset_config': ('hermes_cli.web_routers.tools', 'get_toolset_config'), + 'get_toolset_models': ('hermes_cli.web_routers.tools', 'get_toolset_models'), + 'get_toolsets': ('hermes_cli.web_routers.tools', 'get_toolsets'), + 'get_update_receipt': ('hermes_cli.web_routers.actions', 'get_update_receipt'), + 'get_usage_analytics': ('hermes_cli.web_routers.analytics', 'get_usage_analytics'), + 'get_whatsapp_onboarding_status': ('hermes_cli.web_routers.messaging', 'get_whatsapp_onboarding_status'), + 'git_base_branches_route': ('hermes_cli.web_routers.git', 'git_base_branches_route'), + 'git_branch_switch_route': ('hermes_cli.web_routers.git', 'git_branch_switch_route'), + 'git_branches_route': ('hermes_cli.web_routers.git', 'git_branches_route'), + 'git_commit_context_route': ('hermes_cli.web_routers.git', 'git_commit_context_route'), + 'git_commit_route': ('hermes_cli.web_routers.git', 'git_commit_route'), + 'git_create_pr_route': ('hermes_cli.web_routers.git', 'git_create_pr_route'), + 'git_file_diff_route': ('hermes_cli.web_routers.git', 'git_file_diff_route'), + 'git_push_route': ('hermes_cli.web_routers.git', 'git_push_route'), + 'git_rev_parse_route': ('hermes_cli.web_routers.git', 'git_rev_parse_route'), + 'git_revert_route': ('hermes_cli.web_routers.git', 'git_revert_route'), + 'git_review_diff_route': ('hermes_cli.web_routers.git', 'git_review_diff_route'), + 'git_review_list_route': ('hermes_cli.web_routers.git', 'git_review_list_route'), + 'git_ship_info_route': ('hermes_cli.web_routers.git', 'git_ship_info_route'), + 'git_stage_route': ('hermes_cli.web_routers.git', 'git_stage_route'), + 'git_status_route': ('hermes_cli.web_routers.git', 'git_status_route'), + 'git_unstage_route': ('hermes_cli.web_routers.git', 'git_unstage_route'), + 'git_worktree_add_route': ('hermes_cli.web_routers.git', 'git_worktree_add_route'), + 'git_worktree_remove_route': ('hermes_cli.web_routers.git', 'git_worktree_remove_route'), + 'git_worktrees_route': ('hermes_cli.web_routers.git', 'git_worktrees_route'), + 'grant_computer_use_permissions': ('hermes_cli.web_routers.tools', 'grant_computer_use_permissions'), + 'import_sessions_endpoint': ('hermes_cli.web_routers.sessions', 'import_sessions_endpoint'), + 'install_mcp_catalog_entry': ('hermes_cli.web_routers.mcp', 'install_mcp_catalog_entry'), + 'install_skill_hub': ('hermes_cli.web_routers.skills', 'install_skill_hub'), + 'instantiate_blueprint': ('hermes_cli.web_routers.cron', 'instantiate_blueprint'), + 'is_nix_install_method': ('hermes_cli.config', 'is_nix_install_method'), + 'list_checkpoints': ('hermes_cli.web_routers.ops', 'list_checkpoints'), + 'list_credential_pool': ('hermes_cli.web_routers.ops', 'list_credential_pool'), + 'list_cron_blueprints': ('hermes_cli.web_routers.cron', 'list_cron_blueprints'), + 'list_cron_job_runs': ('hermes_cli.web_routers.cron', 'list_cron_job_runs'), + 'list_cron_jobs': ('hermes_cli.web_routers.cron', 'list_cron_jobs'), + 'list_custom_endpoints': ('hermes_cli.web_routers.config_env', 'list_custom_endpoints'), + 'list_hooks': ('hermes_cli.web_routers.ops', 'list_hooks'), + 'list_managed_files': ('hermes_cli.web_routers.files', 'list_managed_files'), + 'list_mcp_catalog': ('hermes_cli.web_routers.mcp', 'list_mcp_catalog'), + 'list_mcp_servers': ('hermes_cli.web_routers.mcp', 'list_mcp_servers'), + 'list_oauth_providers': ('hermes_cli.web_routers.oauth', 'list_oauth_providers'), + 'list_pairing': ('hermes_cli.web_routers.ops', 'list_pairing'), + 'list_profiles_endpoint': ('hermes_cli.web_routers.profiles', 'list_profiles_endpoint'), + 'list_skills_hub_sources': ('hermes_cli.web_routers.skills', 'list_skills_hub_sources'), + 'list_webhooks': ('hermes_cli.web_routers.ops', 'list_webhooks'), + 'load_env': ('hermes_cli.config', 'load_env'), + 'mcp_oauth_callback': ('hermes_cli.web_routers.mcp', 'mcp_oauth_callback'), + 'mcp_oauth_flow_status': ('hermes_cli.web_routers.mcp', 'mcp_oauth_flow_status'), + 'normalize_updated_at': ('gateway.status', 'normalize_updated_at'), + 'open_profile_terminal_endpoint': ('hermes_cli.web_routers.profiles', 'open_profile_terminal_endpoint'), + 'parse_active_agents': ('gateway.status', 'parse_active_agents'), + 'pause_cron_job': ('hermes_cli.web_routers.cron', 'pause_cron_job'), + 'poll_oauth_session': ('hermes_cli.web_routers.oauth', 'poll_oauth_session'), + 'post_agent_plugin_disable': ('hermes_cli.web_routers.dashboard_ui', 'post_agent_plugin_disable'), + 'post_agent_plugin_enable': ('hermes_cli.web_routers.dashboard_ui', 'post_agent_plugin_enable'), + 'post_agent_plugin_install': ('hermes_cli.web_routers.dashboard_ui', 'post_agent_plugin_install'), + 'post_agent_plugin_update': ('hermes_cli.web_routers.dashboard_ui', 'post_agent_plugin_update'), + 'post_plugin_visibility': ('hermes_cli.web_routers.dashboard_ui', 'post_plugin_visibility'), + 'preview_skill_hub': ('hermes_cli.web_routers.skills', 'preview_skill_hub'), + 'prune_checkpoints': ('hermes_cli.web_routers.ops', 'prune_checkpoints'), + 'prune_sessions_endpoint': ('hermes_cli.web_routers.sessions', 'prune_sessions_endpoint'), + 'pty_ws': ('hermes_cli.web_routers.chat_ws', 'pty_ws'), + 'pub_ws': ('hermes_cli.web_routers.chat_ws', 'pub_ws'), + 'put_plugin_providers': ('hermes_cli.web_routers.dashboard_ui', 'put_plugin_providers'), + 'read_managed_file': ('hermes_cli.web_routers.files', 'read_managed_file'), + 'read_raw_config': ('hermes_cli.config', 'read_raw_config'), + 'read_runtime_status': ('gateway.status', 'read_runtime_status'), + 'recommended_update_command_for_method': ('hermes_cli.config', 'recommended_update_command_for_method'), + 'redact_key': ('hermes_cli.config', 'redact_key'), + 'remove_credential_pool_entry': ('hermes_cli.web_routers.ops', 'remove_credential_pool_entry'), + 'remove_env_value': ('hermes_cli.config', 'remove_env_value'), + 'remove_env_var': ('hermes_cli.web_routers.config_env', 'remove_env_var'), + 'remove_mcp_server': ('hermes_cli.web_routers.mcp', 'remove_mcp_server'), + 'rename_profile_endpoint': ('hermes_cli.web_routers.profiles', 'rename_profile_endpoint'), + 'rename_session_endpoint': ('hermes_cli.web_routers.sessions', 'rename_session_endpoint'), + 'replace_mcp_servers': ('hermes_cli.web_routers.mcp', 'replace_mcp_servers'), + 'rescan_dashboard_plugins': ('hermes_cli.web_routers.dashboard_ui', 'rescan_dashboard_plugins'), + 'reset_memory': ('hermes_cli.web_routers.ops', 'reset_memory'), + 'resolve_cron_model_drift_defaults': ('hermes_cli.config', 'resolve_cron_model_drift_defaults'), + 'resolve_gateway_liveness': ('gateway.status', 'resolve_gateway_liveness'), + 'restart_gateway': ('hermes_cli.web_routers.actions', 'restart_gateway'), + 'resume_cron_job': ('hermes_cli.web_routers.cron', 'resume_cron_job'), + 'reveal_env_var': ('hermes_cli.web_routers.config_env', 'reveal_env_var'), + 'revoke_pairing': ('hermes_cli.web_routers.ops', 'revoke_pairing'), + 'run_backup': ('hermes_cli.backup', 'run_backup'), + 'run_config_migrate': ('hermes_cli.web_routers.status', 'run_config_migrate'), + 'run_curator': ('hermes_cli.web_routers.status', 'run_curator'), + 'run_debug_share_endpoint': ('hermes_cli.web_routers.status', 'run_debug_share_endpoint'), + 'run_doctor': ('hermes_cli.doctor', 'run_doctor'), + 'run_dump': ('hermes_cli.dump', 'run_dump'), + 'run_import': ('hermes_cli.backup', 'run_import'), + 'run_import_upload': ('hermes_cli.web_routers.ops', 'run_import_upload'), + 'run_prompt_size': ('hermes_cli.web_routers.status', 'run_prompt_size'), + 'run_security_audit': ('hermes_cli.web_routers.ops', 'run_security_audit'), + 'run_toolset_post_setup': ('hermes_cli.web_routers.tools', 'run_toolset_post_setup'), + 'save_config': ('hermes_cli.config', 'save_config'), + 'save_env_value': ('hermes_cli.config', 'save_env_value'), + 'save_toolset_env': ('hermes_cli.web_routers.tools', 'save_toolset_env'), + 'scan_skill_hub': ('hermes_cli.web_routers.skills', 'scan_skill_hub'), + 'search_sessions': ('hermes_cli.web_routers.sessions', 'search_sessions'), + 'search_skills_hub': ('hermes_cli.web_routers.skills', 'search_skills_hub'), + 'select_terminal_backend': ('hermes_cli.web_routers.tools', 'select_terminal_backend'), + 'select_toolset_model': ('hermes_cli.web_routers.tools', 'select_toolset_model'), + 'select_toolset_provider': ('hermes_cli.web_routers.tools', 'select_toolset_provider'), + 'serve_plugin_asset': ('hermes_cli.web_routers.dashboard_ui', 'serve_plugin_asset'), + 'set_active_profile_endpoint': ('hermes_cli.web_routers.profiles', 'set_active_profile_endpoint'), + 'set_curator_paused': ('hermes_cli.web_routers.status', 'set_curator_paused'), + 'set_dashboard_font': ('hermes_cli.web_routers.dashboard_ui', 'set_dashboard_font'), + 'set_dashboard_theme': ('hermes_cli.web_routers.dashboard_ui', 'set_dashboard_theme'), + 'set_env_var': ('hermes_cli.web_routers.config_env', 'set_env_var'), + 'set_mcp_server_enabled': ('hermes_cli.web_routers.mcp', 'set_mcp_server_enabled'), + 'set_memory_provider': ('hermes_cli.web_routers.ops', 'set_memory_provider'), + 'set_moa_models': ('hermes_cli.web_routers.models', 'set_moa_models'), + 'set_model_assignment': ('hermes_cli.web_routers.models', 'set_model_assignment'), + 'set_webhook_enabled': ('hermes_cli.web_routers.ops', 'set_webhook_enabled'), + 'setup_memory_provider': ('hermes_cli.web_routers.memory_providers', 'setup_memory_provider'), + 'speak_stream_ws': ('hermes_cli.web_routers.audio', 'speak_stream_ws'), + 'speak_text': ('hermes_cli.voice', 'speak_text'), + 'start_gateway': ('hermes_cli.web_routers.ops', 'start_gateway'), + 'start_oauth_login': ('hermes_cli.web_routers.oauth', 'start_oauth_login'), + 'start_telegram_onboarding': ('hermes_cli.web_routers.messaging', 'start_telegram_onboarding'), + 'start_whatsapp_onboarding': ('hermes_cli.web_routers.messaging', 'start_whatsapp_onboarding'), + 'stop_gateway': ('hermes_cli.web_routers.ops', 'stop_gateway'), + 'stream_managed_file': ('hermes_cli.web_routers.files', 'stream_managed_file'), + 'submit_oauth_code': ('hermes_cli.web_routers.oauth', 'submit_oauth_code'), + 'test_mcp_server': ('hermes_cli.web_routers.mcp', 'test_mcp_server'), + 'test_messaging_platform': ('hermes_cli.web_routers.messaging', 'test_messaging_platform'), + 'toggle_skill': ('hermes_cli.web_routers.skills', 'toggle_skill'), + 'toggle_toolset': ('hermes_cli.web_routers.tools', 'toggle_toolset'), + 'transcribe_audio_upload': ('hermes_cli.web_routers.audio', 'transcribe_audio_upload'), + 'trigger_cron_job': ('hermes_cli.web_routers.cron', 'trigger_cron_job'), + 'tts_lease': ('hermes_cli.web_routers.audio', 'tts_lease'), + 'uninstall_skill_hub': ('hermes_cli.web_routers.skills', 'uninstall_skill_hub'), + 'update_config': ('hermes_cli.web_routers.config_env', 'update_config'), + 'update_config_raw': ('hermes_cli.web_routers.analytics', 'update_config_raw'), + 'update_cron_job': ('hermes_cli.web_routers.cron', 'update_cron_job'), + 'update_hermes': ('hermes_cli.web_routers.actions', 'update_hermes'), + 'update_learning_node': ('hermes_cli.web_routers.status', 'update_learning_node'), + 'update_memory_provider_config': ('hermes_cli.web_routers.memory_providers', 'update_memory_provider_config'), + 'update_messaging_platform': ('hermes_cli.web_routers.messaging', 'update_messaging_platform'), + 'update_profile_description_endpoint': ('hermes_cli.web_routers.profiles', 'update_profile_description_endpoint'), + 'update_profile_model_endpoint': ('hermes_cli.web_routers.profiles', 'update_profile_model_endpoint'), + 'update_profile_soul': ('hermes_cli.web_routers.profiles', 'update_profile_soul'), + 'update_skill_content': ('hermes_cli.web_routers.skills', 'update_skill_content'), + 'update_skills_hub': ('hermes_cli.web_routers.skills', 'update_skills_hub'), + 'upload_chat_image': ('hermes_cli.web_routers.files', 'upload_chat_image'), + 'upload_managed_file': ('hermes_cli.web_routers.files', 'upload_managed_file'), + 'upload_managed_file_stream': ('hermes_cli.web_routers.files', 'upload_managed_file_stream'), + 'upsert_custom_endpoint': ('hermes_cli.web_routers.config_env', 'upsert_custom_endpoint'), + 'validate_custom_endpoint': ('hermes_cli.web_routers.config_env', 'validate_custom_endpoint'), + 'validate_provider_credential': ('hermes_cli.web_routers.config_env', 'validate_provider_credential'), + 'windows_detach_flags': ('hermes_cli._subprocess_compat', 'windows_detach_flags'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), + 'write_platform_config_field': ('hermes_cli.config', 'write_platform_config_field'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/webhook.py b/hermes_cli/webhook.py index ce5d59179b..fb7c1983e2 100644 --- a/hermes_cli/webhook.py +++ b/hermes_cli/webhook.py @@ -224,3 +224,25 @@ _ACTIONS = { "list": _cmd_list, "ls": _cmd_list, "remove": _cmd_remove, "rm": _cmd_remove, "test": _cmd_test} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +import tempfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_replace': ('utils', 'atomic_replace'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_cli/win_pty_bridge.py b/hermes_cli/win_pty_bridge.py index 2e7a65906c..05deb36a5c 100644 --- a/hermes_cli/win_pty_bridge.py +++ b/hermes_cli/win_pty_bridge.py @@ -124,3 +124,11 @@ class WinPtyBridge: def __exit__(self, *_exc) -> None: self.close() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_logging.py b/hermes_logging.py index 49990ff4ec..d94e9f70cb 100644 --- a/hermes_logging.py +++ b/hermes_logging.py @@ -627,3 +627,18 @@ def _read_logging_config(): except Exception: pass return (None, None, None) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def rotating_file_handlers() -> list: + """Return the live rotating file handlers. + + They are attached to the async ``QueueListener`` rather than the root + logger, so callers/tests must use this instead of scanning + ``logging.getLogger().handlers``.""" + return list(_queued_file_handlers) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_state.py b/hermes_state.py index d4bf1a6a90..2bb6c8667f 100644 --- a/hermes_state.py +++ b/hermes_state.py @@ -1292,3 +1292,77 @@ class AsyncSessionDB: async def _offloaded(*args, **kwargs): return await asyncio.to_thread(attr, *args, **kwargs) return _offloaded + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Set # noqa: F401,E402 +import contextlib # noqa: F401,E402 +import errno # noqa: F401,E402 +import struct # noqa: F401,E402 +import weakref # noqa: F401,E402 + +MAX_SAFE_EXPORT_MESSAGES = 20_000 + +MAX_SAFE_RESUME_MESSAGES = 20_000 + + +_PLUGIN_COMPAT_LAZY = { + 'AUTO_VACUUM_MIN_FREELIST_RATIO': ('hermes_state_common', 'AUTO_VACUUM_MIN_FREELIST_RATIO'), + 'ActivityProvenance': ('agent.session_activity', 'ActivityProvenance'), + 'CompressionSessionBusyError': ('hermes_state_errors', 'CompressionSessionBusyError'), + 'CompressionSessionClosedError': ('hermes_state_errors', 'CompressionSessionClosedError'), + 'DEFERRED_INDEX_SQL': ('hermes_state_common', 'DEFERRED_INDEX_SQL'), + 'FTS_CJK_STALE_KEY': ('hermes_state_common', 'FTS_CJK_STALE_KEY'), + 'FTS_CJK_TABLE_SQL': ('hermes_state_fts', 'FTS_CJK_TABLE_SQL'), + 'FTS_CJK_TRIGGER_SQL': ('hermes_state_fts', 'FTS_CJK_TRIGGER_SQL'), + 'FTS_REBUILD_DEFERRAL_KEY': ('hermes_state_common', 'FTS_REBUILD_DEFERRAL_KEY'), + 'FTS_SQL': ('hermes_state_common', 'FTS_SQL'), + 'FTS_STALE_KEY': ('hermes_state_common', 'FTS_STALE_KEY'), + 'FTS_STORAGE_VERSION': ('hermes_state_common', 'FTS_STORAGE_VERSION'), + 'FTS_TRIGRAM_SQL': ('hermes_state_common', 'FTS_TRIGRAM_SQL'), + 'LEGACY_FTS_SQL': ('hermes_state_common', 'LEGACY_FTS_SQL'), + 'LEGACY_FTS_TRIGRAM_SQL': ('hermes_state_common', 'LEGACY_FTS_TRIGRAM_SQL'), + 'MAX_FTS5_QUERY_CHARS': ('hermes_state_common', 'MAX_FTS5_QUERY_CHARS'), + 'PERSISTENCE_ERROR_CAUSES': ('hermes_state_errors', 'PERSISTENCE_ERROR_CAUSES'), + 'SCHEMA_SQL': ('hermes_state_common', 'SCHEMA_SQL'), + 'SCHEMA_VERSION': ('hermes_state_common', 'SCHEMA_VERSION'), + 'SESSION_STATUS_COMPLETE': ('hermes_state_sessions', 'SESSION_STATUS_COMPLETE'), + 'SESSION_STATUS_EMPTY': ('hermes_state_sessions', 'SESSION_STATUS_EMPTY'), + 'SESSION_STATUS_ERROR': ('hermes_state_sessions', 'SESSION_STATUS_ERROR'), + 'SESSION_STATUS_INTERRUPTED': ('hermes_state_sessions', 'SESSION_STATUS_INTERRUPTED'), + 'SKILL_EXCERPT_JOINT': ('agent.skill_commands', 'SKILL_EXCERPT_JOINT'), + 'SKILL_SCAFFOLD_SQL_LIKE': ('agent.skill_commands', 'SKILL_SCAFFOLD_SQL_LIKE'), + 'SessionTurnLeaseLostError': ('hermes_state_errors', 'SessionTurnLeaseLostError'), + 'WalUnsupportedError': ('hermes_state_wal', 'WalUnsupportedError'), + 'apply_durability_barriers': ('hermes_state_repair', 'apply_durability_barriers'), + 'classify_session_status': ('hermes_state_sessions', 'classify_session_status'), + 'collect_state_db_stats': ('hermes_state_dbfile', 'collect_state_db_stats'), + 'count_db_holders': ('hermes_state_dbfile', 'count_db_holders'), + 'describe_skill_invocation': ('agent.skill_commands', 'describe_skill_invocation'), + 'fts5_cjk_so_path': ('hermes_state_fts', 'fts5_cjk_so_path'), + 'is_advisory_lock_contention': ('hermes_state_common', 'is_advisory_lock_contention'), + 'is_automatic_end_reason': ('hermes_state_common', 'is_automatic_end_reason'), + 'is_disk_full_error': ('hermes_state_errors', 'is_disk_full_error'), + 'is_sqlite_wal_reset_vulnerable': ('hermes_state_wal', 'is_sqlite_wal_reset_vulnerable'), + 'is_transient_sqlite_error': ('hermes_state_errors', 'is_transient_sqlite_error'), + 'iter_deleted_sqlite_sidecar_holders': ('hermes_state_dbfile', 'iter_deleted_sqlite_sidecar_holders'), + 'release_or_close': ('hermes_state_registry', 'release_or_close'), + 'report_startup_progress': ('hermes_startup_watchdog', 'report_startup_progress'), + 'resolve_journal_mode': ('hermes_state_wal', 'resolve_journal_mode'), + 'resolve_synchronous_level': ('hermes_state_wal', 'resolve_synchronous_level'), + 'sanitize_context': ('agent.memory_manager', 'sanitize_context'), + 'sqlite_source_id': ('hermes_state_wal', 'sqlite_source_id'), + 'workspace_key': ('hermes_state_sessions', 'workspace_key'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_state_registry.py b/hermes_state_registry.py index 6f8d694f58..2a5614bc16 100644 --- a/hermes_state_registry.py +++ b/hermes_state_registry.py @@ -222,3 +222,19 @@ def release_or_close(db: "SessionDB") -> None: db.close() except Exception: logger.debug("release_or_close fallback close failed", exc_info=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def close_shared_session_dbs() -> int: + return close_all() + +def get_shared_session_db(db_path: Optional[Path] = None) -> "SessionDB": + return acquire(db_path) + +def release_shared_session_db(db: "SessionDB") -> bool: + return release(db) +# ---- END PLUGIN-COMPAT ---- diff --git a/hermes_state_search.py b/hermes_state_search.py index 53d0c9fc92..e2c72cfc4e 100644 --- a/hermes_state_search.py +++ b/hermes_state_search.py @@ -1269,3 +1269,12 @@ class SessionSearchMixin: break self._fts_usermerge_floor_applied = True return executed + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/browser/browser_use/provider.py b/plugins/browser/browser_use/provider.py index 0c09d019c1..9cbc21d17d 100644 --- a/plugins/browser/browser_use/provider.py +++ b/plugins/browser/browser_use/provider.py @@ -158,3 +158,24 @@ class BrowserUseBrowserProvider(CloudBrowserProvider): "features": {"browser_use": True}, "external_call_id": response.headers.get("x-external-call-id") if managed_mode else None, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BrowserProvider': ('agent.browser_provider', 'BrowserProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/browser/browserbase/provider.py b/plugins/browser/browserbase/provider.py index 99805dea7c..264e3a5ade 100644 --- a/plugins/browser/browserbase/provider.py +++ b/plugins/browser/browserbase/provider.py @@ -113,3 +113,25 @@ class BrowserbaseBrowserProvider(CloudBrowserProvider): "cdp_url": session_data["connectUrl"], "features": features_enabled, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import requests # noqa: F401,E402 +import uuid # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BrowserProvider': ('agent.browser_provider', 'BrowserProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/browser/firecrawl/provider.py b/plugins/browser/firecrawl/provider.py index d3732d7b55..f608842937 100644 --- a/plugins/browser/firecrawl/provider.py +++ b/plugins/browser/firecrawl/provider.py @@ -66,3 +66,25 @@ class FirecrawlBrowserProvider(CloudBrowserProvider): "cdp_url": data["cdpUrl"], "features": {"firecrawl": True}, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import requests # noqa: F401,E402 +import uuid # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BrowserProvider': ('agent.browser_provider', 'BrowserProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/context_engine/__init__.py b/plugins/context_engine/__init__.py index 1a07131679..dee2c5da37 100644 --- a/plugins/context_engine/__init__.py +++ b/plugins/context_engine/__init__.py @@ -82,3 +82,12 @@ class _EngineCollector(_loader.NoopPluginContext): logger.debug("Context engine '%s' registered command: /%s", self._engine_name, clean) except Exception as exc: logger.debug("Context engine '%s' could not register /%s: %s", self._engine_name, clean, exc) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import importlib.util # noqa: F401,E402 +import sys # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/cron_providers/__init__.py b/plugins/cron_providers/__init__.py index 7414097aae..c9416f5811 100644 --- a/plugins/cron_providers/__init__.py +++ b/plugins/cron_providers/__init__.py @@ -94,3 +94,12 @@ class _ProviderCollector(_loader.NoopPluginContext): def register_cron_scheduler(self, provider): self.provider = provider + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import importlib.util # noqa: F401,E402 +import sys # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/cron_providers/chronos/__init__.py b/plugins/cron_providers/chronos/__init__.py index 0ef53944c6..a485d76a6f 100644 --- a/plugins/cron_providers/chronos/__init__.py +++ b/plugins/cron_providers/chronos/__init__.py @@ -158,3 +158,11 @@ class ChronosCronScheduler(CronScheduler): def register(ctx) -> None: """Plugin entrypoint — plugins/cron_providers discovery collects the provider here.""" ctx.register_cron_scheduler(ChronosCronScheduler()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/cron_providers/chronos/_nas_client.py b/plugins/cron_providers/chronos/_nas_client.py index ab39c4a763..6dee94c1c6 100644 --- a/plugins/cron_providers/chronos/_nas_client.py +++ b/plugins/cron_providers/chronos/_nas_client.py @@ -63,3 +63,11 @@ class NasCronClient: data = self._request("GET", _LIST_PATH, params={}) items = data.get("armed") if isinstance(data, dict) else None return items if isinstance(items, list) else [] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/dashboard_auth/basic/__init__.py b/plugins/dashboard_auth/basic/__init__.py index 6abf5e7f18..749f7f2dcd 100644 --- a/plugins/dashboard_auth/basic/__init__.py +++ b/plugins/dashboard_auth/basic/__init__.py @@ -261,3 +261,24 @@ def register(ctx) -> None: kwargs, LAST_SKIP_REASON = register_provider(ctx, logger, _TAG, BasicAuthProvider, _settings) if kwargs is not None: logger.info("dashboard-auth-basic: registered password provider (username=%s)", kwargs["username"]) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'LoginStart': ('hermes_cli.dashboard_auth', 'LoginStart'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/dashboard_auth/drain/__init__.py b/plugins/dashboard_auth/drain/__init__.py index de8447855a..de645211d1 100644 --- a/plugins/dashboard_auth/drain/__init__.py +++ b/plugins/dashboard_auth/drain/__init__.py @@ -155,3 +155,23 @@ def register(ctx) -> None: logger.info( "dashboard-auth-drain: registered drain service-credential provider (scope=%s, route=%s)", kwargs["scope"], DRAIN_ROUTE_PATH) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'LoginStart': ('hermes_cli.dashboard_auth', 'LoginStart'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/dashboard_auth/nous/__init__.py b/plugins/dashboard_auth/nous/__init__.py index 3f56be7794..3eb7731408 100644 --- a/plugins/dashboard_auth/nous/__init__.py +++ b/plugins/dashboard_auth/nous/__init__.py @@ -153,3 +153,32 @@ def register(ctx) -> None: if kwargs is not None: logger.info( "dashboard-auth-nous: registered provider (client_id=%s, portal=%s)", kwargs["client_id"], kwargs["portal_url"]) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import base64 # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import httpx # noqa: F401,E402 +import os # noqa: F401,E402 +import secrets # noqa: F401,E402 +import urllib.parse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DashboardAuthProvider': ('hermes_cli.dashboard_auth', 'DashboardAuthProvider'), + 'InvalidCodeError': ('hermes_cli.dashboard_auth', 'InvalidCodeError'), + 'RefreshExpiredError': ('hermes_cli.dashboard_auth', 'RefreshExpiredError'), + 'classify_jwks_lookup_error': ('hermes_cli.dashboard_auth', 'classify_jwks_lookup_error'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/dashboard_auth/self_hosted/__init__.py b/plugins/dashboard_auth/self_hosted/__init__.py index 64e2c092c6..09fc38f22c 100644 --- a/plugins/dashboard_auth/self_hosted/__init__.py +++ b/plugins/dashboard_auth/self_hosted/__init__.py @@ -293,3 +293,29 @@ def register(ctx) -> None: logger.info( "dashboard-auth-self-hosted: registered provider (issuer=%s, client_id=%s, scopes=%r, confidential=%s)", kw["issuer"], kw["client_id"], kw["scopes"], bool(kw["client_secret"])) # never log the secret itself + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import hashlib # noqa: F401,E402 +import os # noqa: F401,E402 +import secrets # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DashboardAuthProvider': ('hermes_cli.dashboard_auth', 'DashboardAuthProvider'), + 'InvalidCodeError': ('hermes_cli.dashboard_auth', 'InvalidCodeError'), + 'RefreshExpiredError': ('hermes_cli.dashboard_auth', 'RefreshExpiredError'), + 'classify_jwks_lookup_error': ('hermes_cli.dashboard_auth', 'classify_jwks_lookup_error'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/google_meet/audio_bridge.py b/plugins/google_meet/audio_bridge.py index 678841a1a3..5cd50d8682 100644 --- a/plugins/google_meet/audio_bridge.py +++ b/plugins/google_meet/audio_bridge.py @@ -109,3 +109,33 @@ class AudioBridge: return int(token) except ValueError as exc: raise RuntimeError(f"could not parse pactl module id from: {stdout!r}") from exc + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def chrome_fake_audio_flags(bridge_info: dict) -> list[str]: + """Return Chrome flags for using the fake audio input. + + The PulseAudio source is selected via the ``PULSE_SOURCE`` env var, + which callers must set in Chrome's environment before launch: + + env["PULSE_SOURCE"] = bridge_info["device_name"] + + On macOS the caller must ensure the system default audio input is + set to the returned BlackHole device (we do not flip that switch). + """ + system = platform.system() + if system == "Linux": + # Chromium on Linux picks up the PulseAudio source selected via + # PULSE_SOURCE env var; the fake-ui flag skips the permission + # prompt so the bot can pick "use my mic" without user input. + return ["--use-fake-ui-for-media-stream"] + if system == "Darwin": + return ["--use-fake-ui-for-media-stream"] + if system == "Windows": + raise RuntimeError("windows not supported in v2") + raise RuntimeError(f"unsupported platform: {system}") +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/google_meet/meet_bot.py b/plugins/google_meet/meet_bot.py index 9deadc79cb..eddc2cc020 100644 --- a/plugins/google_meet/meet_bot.py +++ b/plugins/google_meet/meet_bot.py @@ -479,3 +479,15 @@ def _parse_duration(raw: str) -> Optional[float]: if __name__ == "__main__": # pragma: no cover — subprocess entry point sys.exit(run_bot()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +SAY_PCM_FILENAME = "speaker.pcm" + +SAY_QUEUE_FILENAME = "say_queue.jsonl" +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/google_meet/node/cli.py b/plugins/google_meet/node/cli.py index fd171ee98b..04638cea46 100644 --- a/plugins/google_meet/node/cli.py +++ b/plugins/google_meet/node/cli.py @@ -100,3 +100,11 @@ def node_command(args: argparse.Namespace) -> int: return 2 # ``run`` never touches the registry; constructing it is side-effect free. return handler(args, NodeRegistry()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/google_meet/node/registry.py b/plugins/google_meet/node/registry.py index 2a5fb08cbe..13015098f3 100644 --- a/plugins/google_meet/node/registry.py +++ b/plugins/google_meet/node/registry.py @@ -65,3 +65,11 @@ class NodeRegistry: return self.get(chrome_node) nodes = self.list_all() return nodes[0] if len(nodes) == 1 else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/image_gen/deepinfra/__init__.py b/plugins/image_gen/deepinfra/__init__.py index dfe36d0165..df90c313e9 100644 --- a/plugins/image_gen/deepinfra/__init__.py +++ b/plugins/image_gen/deepinfra/__init__.py @@ -153,3 +153,26 @@ class DeepInfraImageGenProvider(StaticImageGenProvider): def register(ctx) -> None: """Plugin entry point — wire ``DeepInfraImageGenProvider`` into the registry.""" ctx.register_image_gen_provider(DeepInfraImageGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ImageGenProvider': ('agent.image_gen_provider', 'ImageGenProvider'), + 'error_response': ('agent.image_gen_provider', 'error_response'), + 'save_b64_image': ('agent.image_gen_provider', 'save_b64_image'), + 'save_url_image': ('agent.image_gen_provider', 'save_url_image'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/image_gen/fal/__init__.py b/plugins/image_gen/fal/__init__.py index ba884373c7..b5d3cfc389 100644 --- a/plugins/image_gen/fal/__init__.py +++ b/plugins/image_gen/fal/__init__.py @@ -111,3 +111,24 @@ class FalImageGenProvider(StaticImageGenProvider): def register(ctx) -> None: """Plugin entry point — wire ``FalImageGenProvider`` into the registry.""" ctx.register_image_gen_provider(FalImageGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'ImageGenProvider': ('agent.image_gen_provider', 'ImageGenProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/image_gen/krea/__init__.py b/plugins/image_gen/krea/__init__.py index f64b219f14..ff36f004dc 100644 --- a/plugins/image_gen/krea/__init__.py +++ b/plugins/image_gen/krea/__init__.py @@ -517,3 +517,26 @@ class KreaImageGenProvider(StaticImageGenProvider): def register(ctx) -> None: """Plugin entry point — wire ``KreaImageGenProvider`` into the registry.""" ctx.register_image_gen_provider(KreaImageGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'ImageGenProvider': ('agent.image_gen_provider', 'ImageGenProvider'), + 'error_response': ('agent.image_gen_provider', 'error_response'), + 'normalize_reference_images': ('agent.image_gen_provider', 'normalize_reference_images'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/image_gen/openai/__init__.py b/plugins/image_gen/openai/__init__.py index 59bb92e1d3..e5b5e3baa0 100644 --- a/plugins/image_gen/openai/__init__.py +++ b/plugins/image_gen/openai/__init__.py @@ -141,3 +141,27 @@ class OpenAIImageGenProvider(StaticImageGenProvider): def register(ctx) -> None: """Plugin entry point — wire ``OpenAIImageGenProvider`` into the registry.""" ctx.register_image_gen_provider(OpenAIImageGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ImageGenProvider': ('agent.image_gen_provider', 'ImageGenProvider'), + 'error_response': ('agent.image_gen_provider', 'error_response'), + 'normalize_reference_images': ('agent.image_gen_provider', 'normalize_reference_images'), + 'save_b64_image': ('agent.image_gen_provider', 'save_b64_image'), + 'save_url_image': ('agent.image_gen_provider', 'save_url_image'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/image_gen/xai/__init__.py b/plugins/image_gen/xai/__init__.py index 32f4e23721..ca27cf7585 100644 --- a/plugins/image_gen/xai/__init__.py +++ b/plugins/image_gen/xai/__init__.py @@ -317,3 +317,27 @@ class XAIImageGenProvider(StaticImageGenProvider): def register(ctx: Any) -> None: """Register this provider with the image gen registry.""" ctx.register_image_gen_provider(XAIImageGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ImageGenProvider': ('agent.image_gen_provider', 'ImageGenProvider'), + 'error_response': ('agent.image_gen_provider', 'error_response'), + 'normalize_reference_images': ('agent.image_gen_provider', 'normalize_reference_images'), + 'save_b64_image': ('agent.image_gen_provider', 'save_b64_image'), + 'save_url_image': ('agent.image_gen_provider', 'save_url_image'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/hindsight/__init__.py b/plugins/memory/hindsight/__init__.py index 613e50fa22..c35b189493 100644 --- a/plugins/memory/hindsight/__init__.py +++ b/plugins/memory/hindsight/__init__.py @@ -1203,3 +1203,12 @@ class HindsightMemoryProvider(MemoryProvider): def register(ctx) -> None: """Register Hindsight as a memory provider plugin.""" ctx.register_memory_provider(HindsightMemoryProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import dataclass # noqa: F401,E402 +import importlib # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/honcho/__init__.py b/plugins/memory/honcho/__init__.py index 93723d9247..bfac07824c 100644 --- a/plugins/memory/honcho/__init__.py +++ b/plugins/memory/honcho/__init__.py @@ -803,3 +803,28 @@ def register(ctx) -> None: from plugins.memory.query_rewrite import rewrite_memory_query ctx.register_memory_provider(HonchoMemoryProvider(query_rewriter=rewrite_memory_query)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'CONCLUDE_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'CONCLUDE_SCHEMA'), + 'CONTEXT_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'CONTEXT_SCHEMA'), + 'PROFILE_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'PROFILE_SCHEMA'), + 'REASONING_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'REASONING_SCHEMA'), + 'SEARCH_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'SEARCH_SCHEMA'), + 'TRIVIAL_PROMPT_RE': ('agent.memory_provider', 'TRIVIAL_PROMPT_RE'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/honcho/client.py b/plugins/memory/honcho/client.py index 3a48c31f60..55bc707c3e 100644 --- a/plugins/memory/honcho/client.py +++ b/plugins/memory/honcho/client.py @@ -593,3 +593,23 @@ def reset_honcho_client() -> None: with _client_slots_lock: _client_slots.clear() _honcho_json_timeout_memo.clear() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'SingletonSlot': ('plugins.plugin_utils', 'SingletonSlot'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/honcho/oauth.py b/plugins/memory/honcho/oauth.py index 8e80571dd1..bcdac62f12 100644 --- a/plugins/memory/honcho/oauth.py +++ b/plugins/memory/honcho/oauth.py @@ -378,3 +378,11 @@ def apply_token_to_client(client: Any, token: str) -> bool: return False http.api_key = token return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Callable # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/honcho/session.py b/plugins/memory/honcho/session.py index ab257f98d4..0d94308124 100644 --- a/plugins/memory/honcho/session.py +++ b/plugins/memory/honcho/session.py @@ -373,3 +373,14 @@ class HonchoSessionManager(SessionAuthMixin, SessionPeersMixin, SessionContextMi """Return and clear the cached context result ({} if none ready yet).""" with self._prefetch_cache_lock: return self._context_cache.pop(session_key, {}) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Callable # noqa: F401,E402 +from pathlib import Path # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import re # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/mem0/__init__.py b/plugins/memory/mem0/__init__.py index d20660a356..ce208c1641 100644 --- a/plugins/memory/mem0/__init__.py +++ b/plugins/memory/mem0/__init__.py @@ -348,3 +348,74 @@ class Mem0MemoryProvider(MemoryProvider): def register(ctx) -> None: """Register Mem0 as a memory provider plugin.""" ctx.register_memory_provider(Mem0MemoryProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +ADD_SCHEMA = { + "name": "mem0_add", + "description": ( + "Store a durable fact about the user, verbatim (no LLM extraction). " + "Call this the moment the user states a lasting preference, correction, " + "decision, or personal detail worth recalling on future turns — don't " + "wait to be asked to remember. Skip transient chit-chat and facts you've " + "already stored." + ), + "parameters": { + "type": "object", + "properties": { + "content": {"type": "string", "description": "The fact to store."}, + }, + "required": ["content"], + }, +} + +DELETE_SCHEMA = { + "name": "mem0_delete", + "description": ( + "Delete a memory by its ID (take the ID from a mem0_search " + "result). Use when a stored fact is obsolete or the user asks you to " + "forget it; prefer mem0_update if the fact merely changed." + ), + "parameters": { + "type": "object", + "properties": { + "memory_id": {"type": "string", "description": "Memory UUID to delete."}, + }, + "required": ["memory_id"], + }, +} + +UPDATE_SCHEMA = { + "name": "mem0_update", + "description": ( + "Replace the text of an existing memory by its ID (take the ID from a " + "mem0_search result). Use when a stored fact has changed " + "or was wrong — correct it in place instead of adding a duplicate." + ), + "parameters": { + "type": "object", + "properties": { + "memory_id": {"type": "string", "description": "Memory UUID to update."}, + "text": {"type": "string", "description": "New text content."}, + }, + "required": ["memory_id", "text"], + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'SEARCH_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'SEARCH_SCHEMA'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/mem0/_setup.py b/plugins/memory/mem0/_setup.py index 390bf35473..1f0f25d261 100644 --- a/plugins/memory/mem0/_setup.py +++ b/plugins/memory/mem0/_setup.py @@ -508,3 +508,19 @@ def post_setup(hermes_home: str, config: dict) -> None: if handler is None: handler = _MODE_PICKER[_curses_select(" Select mode", _MODE_ITEMS, 0)] handler(hermes_home, config, flags) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def has_oss_flags() -> bool: + """Check if OSS-related flags are present in sys.argv.""" + flags = parse_flags(sys.argv[1:]) + if flags["mode"] == "oss": + return True + if any(flags.get(k) for k in ("oss_llm_key", "oss_vector_path", "oss_vector_url")): + return True + return False +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/retaindb/__init__.py b/plugins/memory/retaindb/__init__.py index fe8f2972c9..cf4b1f4be4 100644 --- a/plugins/memory/retaindb/__init__.py +++ b/plugins/memory/retaindb/__init__.py @@ -488,3 +488,93 @@ _TOOLS: dict[str, tuple[str | None, Callable[..., Any]]] = { def register(ctx) -> None: """Register RetainDB as a memory provider plugin.""" ctx.register_memory_provider(RetainDBMemoryProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import List # noqa: F401,E402 + +FILE_DELETE_SCHEMA = { + "name": "retaindb_delete_file", + "description": "Delete a stored file.", + "parameters": { + "type": "object", + "properties": { + "file_id": {"type": "string", "description": "File ID to delete."}, + }, + "required": ["file_id"], + }, +} + +FILE_INGEST_SCHEMA = { + "name": "retaindb_ingest_file", + "description": "Chunk, embed, and extract memories from a stored file. Makes its contents searchable.", + "parameters": { + "type": "object", + "properties": { + "file_id": {"type": "string", "description": "File ID to ingest."}, + }, + "required": ["file_id"], + }, +} + +FILE_LIST_SCHEMA = { + "name": "retaindb_list_files", + "description": "List files in the shared file store.", + "parameters": { + "type": "object", + "properties": { + "prefix": {"type": "string", "description": "Path prefix to filter by, e.g. /reports/"}, + "limit": {"type": "integer", "description": "Max results (default: 50)."}, + }, + "required": [], + }, +} + +FILE_READ_SCHEMA = { + "name": "retaindb_read_file", + "description": "Read the text content of a stored file by its file ID.", + "parameters": { + "type": "object", + "properties": { + "file_id": {"type": "string", "description": "File ID returned from upload or list."}, + }, + "required": ["file_id"], + }, +} + +FILE_UPLOAD_SCHEMA = { + "name": "retaindb_upload_file", + "description": "Upload a file to the shared RetainDB file store. Returns an rdb:// URI any agent can reference.", + "parameters": { + "type": "object", + "properties": { + "local_path": {"type": "string", "description": "Local file path to upload."}, + "remote_path": {"type": "string", "description": "Destination path, e.g. /reports/q1.pdf"}, + "scope": {"type": "string", "enum": ["USER", "PROJECT", "ORG"], "description": "Access scope (default: PROJECT)."}, + "ingest": {"type": "boolean", "description": "Also extract memories from file after upload (default: false)."}, + }, + "required": ["local_path"], + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'CONTEXT_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'CONTEXT_SCHEMA'), + 'FORGET_SCHEMA': ('plugins.memory.openviking', 'FORGET_SCHEMA'), + 'PROFILE_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'PROFILE_SCHEMA'), + 'REMEMBER_SCHEMA': ('plugins.memory.openviking', 'REMEMBER_SCHEMA'), + 'SEARCH_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'SEARCH_SCHEMA'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/memory/supermemory/__init__.py b/plugins/memory/supermemory/__init__.py index 0b339660b5..a5939722ae 100644 --- a/plugins/memory/supermemory/__init__.py +++ b/plugins/memory/supermemory/__init__.py @@ -536,3 +536,38 @@ class SupermemoryMemoryProvider(MemoryProvider): def register(ctx): ctx.register_memory_provider(SupermemoryMemoryProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +STORE_SCHEMA = { + "name": "supermemory_store", + "description": "Store an explicit memory for future recall.", + "parameters": { + "type": "object", + "properties": { + "content": {"type": "string", "description": "The memory content to store."}, + "metadata": {"type": "object", "description": "Optional metadata attached to the memory."}, + }, + "required": ["content"], + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'FORGET_SCHEMA': ('plugins.memory.openviking', 'FORGET_SCHEMA'), + 'PROFILE_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'PROFILE_SCHEMA'), + 'SEARCH_SCHEMA': ('plugins.memory.honcho.tool_schemas', 'SEARCH_SCHEMA'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/a2a/protocol.py b/plugins/platforms/a2a/protocol.py index 013682510e..1b3c2c5f35 100644 --- a/plugins/platforms/a2a/protocol.py +++ b/plugins/platforms/a2a/protocol.py @@ -470,3 +470,50 @@ def load_conversation(context_id: str, limit: int = 50) -> list[dict]: def list_conversations() -> list[str]: """Context-ids that have persisted conversations.""" return sorted(p.stem for p in (_hermes_home() / "a2a_conversations").glob("*.jsonl")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import copy # noqa: F401,E402 + +ERR_PUSH_NOT_SUPPORTED = -32003 # A2A spec: PushNotificationNotSupportedError + +STATE_AUTH_REQUIRED = "TASK_STATE_AUTH_REQUIRED" + +def data_part(data: Any, media_type: str = "application/json") -> dict: + """Build a v1.0 data Part (structured data, no ``kind`` field).""" + return {"data": data, "mediaType": media_type} + +def file_part(url: str = "", raw: str = "", filename: str = "", + media_type: str = "application/octet-stream") -> dict: + """Build a v1.0 file Part. + + Either ``url`` (file reference) or ``raw`` (base64-encoded bytes) must be + provided. Discrimination is by member presence — no ``kind`` field. + """ + part: dict[str, Any] = {"mediaType": media_type} + if filename: + part["filename"] = filename + if url: + part["url"] = url + elif raw: + part["raw"] = raw + return part + +def message_with_parts(role: str, parts: list[dict], context_id: str = "") -> dict: + """Build an A2A v1.0 Message with arbitrary Parts (text, file, data).""" + msg: dict[str, Any] = { + "role": role, + "parts": parts, + "messageId": uuid.uuid4().hex, + } + if context_id: + msg["contextId"] = context_id + return msg + +def stream_message(message: dict) -> dict: + """v1.0 StreamResponse with a message member.""" + return {"message": message} +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/a2a/security.py b/plugins/platforms/a2a/security.py index 68df198d7b..d37f7cf810 100644 --- a/plugins/platforms/a2a/security.py +++ b/plugins/platforms/a2a/security.py @@ -214,3 +214,86 @@ def audit(direction: str, peer: str, task_id: str, summary: str) -> None: fh.write(json.dumps(rec, ensure_ascii=False) + "\n") except Exception: logger.debug("A2A: audit write failed", exc_info=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 + +def authenticate(auth_header: Optional[str], client_ip: str = "") -> Optional[str]: + """Authenticate an inbound request; return the peer identity or None. + + - No tokens configured (localhost-only mode): identity is ``ip:``. + - Token matches an A2A_PEER_TOKENS entry: identity is that peer's name. + - Token matches the shared A2A_BEARER_TOKEN: identity is ``ip:``. + - Otherwise: None (reject with 401). + + Comparisons are constant-time (hmac.compare_digest). + """ + return A2ASecurityContext.capture().authenticate(auth_header, client_ip) + +def get_bearer_token() -> str: + """Return the configured shared inbound bearer token (empty if none).""" + return _startup_env("A2A_BEARER_TOKEN") + +def get_peer_tokens() -> dict[str, str]: + """Parse A2A_PEER_TOKENS ("alice:tok1,bob:tok2") into {token: peer_name}. + + Per-peer tokens give each remote agent its own credential, so the identity + used for rate limiting, trust, and audit is authenticated — not whatever + the request body claims. + """ + return _parse_peer_tokens(_startup_env("A2A_PEER_TOKENS")) + +def get_push_secret() -> str: + """Return the secret used for HMAC-SHA256 push notification signing. + + Falls back to the bearer token if no dedicated push secret is set. + If neither is configured, push notifications are unsigned (localhost-only mode). + """ + return A2ASecurityContext.capture().push_secret + +def get_trusted_peers() -> set[str]: + """Return the configured trusted-peer allow-list (empty = no restriction). + + Configured via A2A_TRUSTED_PEERS env var (comma-separated identities) or + config.yaml under a2a.trusted_peers. Identities are the *authenticated* + names from ``authenticate()`` — peer-token names, or ``ip:`` for + shared-token callers. + """ + return set(_configured_trusted_peers()) + +def is_trusted_peer(identity: str) -> bool: + """Check whether an authenticated identity may run tasks. + + Open when A2A_ALLOW_ALL_USERS is set or in localhost-only mode. When a + trusted-peer allow-list is configured, the identity must be on it; + otherwise any *authenticated* identity is allowed (authentication is the + primary gate — the allow-list is an optional restriction on top). + """ + return A2ASecurityContext.capture().is_trusted_peer(identity) + +def resolve_bind_host() -> str: + """Resolve the safe inbound bind host. + + Rule: localhost unless the operator BOTH configured a token (shared or + per-peer) AND explicitly asked for a wider host. A token alone does not + widen the bind — opting into remote exposure must be deliberate. + """ + return A2ASecurityContext.capture().resolve_bind_host() + +def sign_push_payload(payload: dict) -> str: + """HMAC-SHA256 sign a push notification payload. + + Returns hex-encoded signature. Empty string if no secret configured. + Receivers verify by HMAC-ing the JSON body (sorted keys) with the shared + secret and comparing against the X-A2A-Signature header. + """ + secret = get_push_secret() + if not secret: + return "" + body = json.dumps(payload, sort_keys=True, ensure_ascii=False).encode("utf-8") + return hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest() +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/a2a/tools.py b/plugins/platforms/a2a/tools.py index 1190b8f1dc..044d5153d6 100644 --- a/plugins/platforms/a2a/tools.py +++ b/plugins/platforms/a2a/tools.py @@ -361,3 +361,11 @@ def register_tools(ctx) -> None: ctx.register_tool(name=name, toolset="a2a", handler=handler, description=description, schema={"name": name, "description": description, "parameters": parameters}, emoji="\U0001f9e9", check_fn=_a2a_tools_available) # puzzle piece + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import TypedDict # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/dingtalk/adapter.py b/plugins/platforms/dingtalk/adapter.py index 1239870f23..5f5ef7035d 100644 --- a/plugins/platforms/dingtalk/adapter.py +++ b/plugins/platforms/dingtalk/adapter.py @@ -736,3 +736,25 @@ def register(ctx) -> None: allow_all_env="DINGTALK_ALLOW_ALL_USERS", cron_deliver_env_var="DINGTALK_HOME_CHANNEL", standalone_sender_fn=_standalone_send, emoji="🐳", allow_update_command=True, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'DINGTALK_TYPE_MAPPING': ('plugins.platforms.dingtalk.inbound', 'DINGTALK_TYPE_MAPPING'), + 'EXT_MAP': ('plugins.platforms.dingtalk.inbound', 'EXT_MAP'), + 'MessageType': ('gateway.platforms.base', 'MessageType'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index 09422b9693..a1281e22af 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -7352,3 +7352,23 @@ def register(ctx) -> None: emoji="🎮", allow_update_command=True, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'env_int': ('utils', 'env_int'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/feishu/feishu_comment.py b/plugins/platforms/feishu/feishu_comment.py index 3c67f8bc05..0ddf99b439 100644 --- a/plugins/platforms/feishu/feishu_comment.py +++ b/plugins/platforms/feishu/feishu_comment.py @@ -618,3 +618,98 @@ async def handle_drive_comment_event(client: Any, data: Any, *, self_open_id: st if reply_id: # best-effort cleanup of the OK reaction await update_comment_reaction(client, "delete", **reaction_kwargs) logger.info("[Feishu-Comment] ========== handle_drive_comment_event END ==========") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +async def add_comment_reaction( + client: Any, + *, + file_token: str, + file_type: str, + reply_id: str, + reaction_type: str = "OK", +) -> bool: + """Add an emoji reaction to a document comment reply. + + Uses the Drive v2 ``update_reaction`` endpoint:: + + POST /open-apis/drive/v2/files/{file_token}/comments/reaction?file_type=... + + Returns ``True`` on success, ``False`` on failure (errors are logged). + """ + try: + from lark_oapi import AccessTokenType # noqa: F401 + except ImportError: + logger.error("[Feishu-Comment] lark_oapi not available") + return False + + body = { + "action": "add", + "reply_id": reply_id, + "reaction_type": reaction_type, + } + + code, msg, _ = await _exec_request( + client, "POST", _REACTION_URI, + paths={"file_token": file_token}, + queries=[("file_type", file_type)], + body=body, + ) + + succeeded = code == 0 + if succeeded: + logger.info( + "[Feishu-Comment] Reaction '%s' added: file=%s:%s reply=%s", + reaction_type, file_type, file_token, reply_id, + ) + else: + logger.warning( + "[Feishu-Comment] Reaction API failed: code=%s msg=%s " + "file=%s:%s reply=%s", + code, msg, file_type, file_token, reply_id, + ) + return succeeded + +async def delete_comment_reaction( + client: Any, + *, + file_token: str, + file_type: str, + reply_id: str, + reaction_type: str = "OK", +) -> bool: + """Remove an emoji reaction from a document comment reply. + + Best-effort — errors are logged but not raised. + """ + body = { + "action": "delete", + "reply_id": reply_id, + "reaction_type": reaction_type, + } + + code, msg, _ = await _exec_request( + client, "POST", _REACTION_URI, + paths={"file_token": file_token}, + queries=[("file_type", file_type)], + body=body, + ) + + succeeded = code == 0 + if succeeded: + logger.info( + "[Feishu-Comment] Reaction '%s' deleted: file=%s:%s reply=%s", + reaction_type, file_type, file_token, reply_id, + ) + else: + logger.warning( + "[Feishu-Comment] Reaction API failed: code=%s msg=%s " + "file=%s:%s reply=%s", + code, msg, file_type, file_token, reply_id, + ) + return succeeded +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/google_chat/oauth.py b/plugins/platforms/google_chat/oauth.py index 35cd47bf09..1be1399a22 100644 --- a/plugins/platforms/google_chat/oauth.py +++ b/plugins/platforms/google_chat/oauth.py @@ -445,3 +445,25 @@ def main() -> None: if __name__ == "__main__": main() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import secrets # noqa: F401,E402 +import subprocess # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_replace': ('utils', 'atomic_replace'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/irc/adapter.py b/plugins/platforms/irc/adapter.py index 6435c301b0..8413ff34eb 100644 --- a/plugins/platforms/irc/adapter.py +++ b/plugins/platforms/irc/adapter.py @@ -624,3 +624,11 @@ def register(ctx): "line (long messages are automatically split). In channels, users " "address you by prefixing your nick. Keep responses concise and " "conversational.")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/line/adapter.py b/plugins/platforms/line/adapter.py index 462e6bab5d..42237b963b 100644 --- a/plugins/platforms/line/adapter.py +++ b/plugins/platforms/line/adapter.py @@ -973,3 +973,11 @@ def register(ctx) -> None: "requires LINE_PUBLIC_URL configured to a publicly reachable HTTPS " "host. Slow responses surface a 'Get answer' button the user taps " "to fetch the reply via a fresh free token.")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import field # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/matrix/adapter.py b/plugins/platforms/matrix/adapter.py index d97661413d..6ba4a36cda 100644 --- a/plugins/platforms/matrix/adapter.py +++ b/plugins/platforms/matrix/adapter.py @@ -3010,3 +3010,43 @@ def register(ctx) -> None: allow_all_env="MATRIX_ALLOW_ALL_USERS", cron_deliver_env_var="MATRIX_HOME_ROOM", standalone_sender_fn=_standalone_send, max_message_length=DEFAULT_MAX_MESSAGE_LENGTH, emoji="🔐", allow_update_command=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +_MATRIX_CAPABILITIES: Dict[str, str] = { + "text": "yes", + "threads": "yes", + "reactions": "yes", + "approvals": "yes", + "model picker": "yes", + "thinking panes": "yes", + "images": "yes", + "multiple images": "yes", + "files": "yes", + "voice/audio": "yes", + "video": "yes", + "E2EE": "off / optional / required", + "diagnostics": "yes", +} + +def get_matrix_capabilities() -> Dict[str, str]: + """Return Matrix gateway capabilities for docs and release checks.""" + return dict(_MATRIX_CAPABILITIES) + + +_PLUGIN_COMPAT_LAZY = { + 'MAX_MESSAGE_LENGTH': ('gateway.platforms.signal', 'MAX_MESSAGE_LENGTH'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/ntfy/adapter.py b/plugins/platforms/ntfy/adapter.py index 7eb318529d..db7d12ea24 100644 --- a/plugins/platforms/ntfy/adapter.py +++ b/plugins/platforms/ntfy/adapter.py @@ -397,3 +397,11 @@ def register(ctx) -> None: "Keep responses concise; ntfy is a push notification service " "with a 4096-character per-message limit." )) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/photon/adapter.py b/plugins/platforms/photon/adapter.py index 3c90c6f97a..faf344c4f7 100644 --- a/plugins/platforms/photon/adapter.py +++ b/plugins/platforms/photon/adapter.py @@ -1614,3 +1614,24 @@ def register(ctx) -> None: ctx.register_cli_command( name="photon", help="Set up and manage the Photon iMessage integration", setup_fn=_cli.register_cli, handler_fn=_cli.dispatch) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'ProcessingOutcome': ('gateway.platforms.base', 'ProcessingOutcome'), + 'resolve_sidecar_dir': ('plugins.platforms.photon.sidecar_paths', 'resolve_sidecar_dir'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/photon/auth.py b/plugins/platforms/photon/auth.py index d402048e7a..198bfa5b43 100644 --- a/plugins/platforms/photon/auth.py +++ b/plugins/platforms/photon/auth.py @@ -713,3 +713,51 @@ def print_credential_summary(emit: Any = print) -> None: " my number : " + (phone if phone else "✗ missing (run `hermes photon setup --phone ...`)"), " assigned number : " + (assigned if assigned else "✗ missing (run `hermes photon setup`)")] emit("\n".join(rows)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def credential_summary() -> Dict[str, str]: + """Return a fully pre-formatted credential status dict (no raw secrets).""" + def _present_token() -> str: + return ( + "✓ stored" if load_photon_token() + else "✗ missing (run `hermes photon setup`)" + ) + + def _present_project_id() -> str: + sid, _sec = load_project_credentials() + return sid or "✗ missing" + + def _present_secret() -> str: + _sid, sec = load_project_credentials() + return "✓ stored" if sec else "✗ missing" + + def _present_phone() -> str: + phone, _assigned = load_user_numbers() + return phone or "✗ missing (run `hermes photon setup --phone ...`)" + + def _present_assigned_phone() -> str: + _phone, assigned = load_user_numbers() + return assigned or "✗ missing (run `hermes photon setup`)" + + return { + "device_token": _present_token(), + "project_id": _present_project_id(), + "project_key": _present_secret(), + "phone_number": _present_phone(), + "assigned_phone_number": _present_assigned_phone(), + } + +def get_session(token: str) -> Dict[str, Any]: + """GET ``/api/auth/get-session`` — confirm the token + fetch the user.""" + if httpx is None: + raise RuntimeError("httpx is required for Photon") + url = f"{_dashboard_host()}/api/auth/get-session" + resp = httpx.get(url, headers=_bearer(token), timeout=30.0) + resp.raise_for_status() + return resp.json() or {} +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/photon/cli.py b/plugins/platforms/photon/cli.py index 8f704d6053..9f78b80650 100644 --- a/plugins/platforms/photon/cli.py +++ b/plugins/platforms/photon/cli.py @@ -343,3 +343,24 @@ def _prompt(prompt: str, *, secret: bool = False) -> str: except (KeyboardInterrupt, EOFError): print() return "" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'resolve_sidecar_dir': ('plugins.platforms.photon.sidecar_paths', 'resolve_sidecar_dir'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/raft/adapter.py b/plugins/platforms/raft/adapter.py index 617a4e07fc..9f38281eb9 100644 --- a/plugins/platforms/raft/adapter.py +++ b/plugins/platforms/raft/adapter.py @@ -571,3 +571,11 @@ def register(ctx) -> None: ("post_llm_call", _on_post_llm_call), ("on_session_end", _on_session_end), ("on_session_finalize", _on_session_finalize)): ctx.register_hook(hook_name, callback) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import asyncio # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/teams/adapter.py b/plugins/platforms/teams/adapter.py index 48a6295377..7dc0f728b8 100644 --- a/plugins/platforms/teams/adapter.py +++ b/plugins/platforms/teams/adapter.py @@ -801,3 +801,25 @@ def register(ctx) -> None: "markdown — bold (**text**), italic (*text*), and inline code " "(`code`) work, but complex tables or raw HTML do not. Keep " "responses clear and professional.")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import html # noqa: F401,E402 +from urllib.parse import quote # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'TeamsSummaryWriter': ('plugins.platforms.teams.summary_writer', 'TeamsSummaryWriter'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/telegram/adapter.py b/plugins/platforms/telegram/adapter.py index 36da8a65fb..c6406ae721 100644 --- a/plugins/platforms/telegram/adapter.py +++ b/plugins/platforms/telegram/adapter.py @@ -6499,3 +6499,25 @@ def register(ctx) -> None: install_hint="Run `hermes setup` to install Telegram support.", setup_fn=interactive_setup, apply_yaml_config_fn=_apply_yaml_config, allowed_users_env="TELEGRAM_ALLOWED_USERS", allow_all_env="TELEGRAM_ALLOW_ALL_USERS", cron_deliver_env_var="TELEGRAM_HOME_CHANNEL", standalone_sender_fn=_standalone_send, max_message_length=4096, emoji="✈️", allow_update_command=True) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_replace': ('utils', 'atomic_replace'), + 'cache_document_from_bytes': ('gateway.platforms.base', 'cache_document_from_bytes'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/telegram/telegram_ids.py b/plugins/platforms/telegram/telegram_ids.py index 90f4a715e6..97e3d4fdd0 100644 --- a/plugins/platforms/telegram/telegram_ids.py +++ b/plugins/platforms/telegram/telegram_ids.py @@ -29,3 +29,14 @@ def parse_telegram_username_target(target_ref: Any) -> Union[str, None]: """Return the value when it is an ``@username`` target, else ``None``.""" value = str(target_ref).strip() return value if looks_like_telegram_username(value) else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def telegram_chat_id_key(chat_id: Any) -> str: + """Stable string key for a chat_id (for dict keys / persisted state).""" + return str(normalize_telegram_chat_id(chat_id)) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/platforms/wecom/adapter.py b/plugins/platforms/wecom/adapter.py index b60733b372..289f41dfa8 100644 --- a/plugins/platforms/wecom/adapter.py +++ b/plugins/platforms/wecom/adapter.py @@ -887,3 +887,50 @@ def register(ctx) -> None: required_env=["WECOM_CALLBACK_CORP_ID", "WECOM_CALLBACK_CORP_SECRET"], allowed_users_env="WECOM_CALLBACK_ALLOWED_USERS", allow_all_env="WECOM_CALLBACK_ALLOW_ALL_USERS", **common, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import base64 # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from collections import deque # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import mimetypes # noqa: F401,E402 +import os # noqa: F401,E402 +from urllib.parse import unquote # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'ABSOLUTE_MAX_BYTES': ('plugins.platforms.wecom.media', 'ABSOLUTE_MAX_BYTES'), + 'APP_CMD_UPLOAD_MEDIA_CHUNK': ('plugins.platforms.wecom.media', 'APP_CMD_UPLOAD_MEDIA_CHUNK'), + 'APP_CMD_UPLOAD_MEDIA_FINISH': ('plugins.platforms.wecom.media', 'APP_CMD_UPLOAD_MEDIA_FINISH'), + 'APP_CMD_UPLOAD_MEDIA_INIT': ('plugins.platforms.wecom.media', 'APP_CMD_UPLOAD_MEDIA_INIT'), + 'FILE_MAX_BYTES': ('plugins.platforms.wecom.media', 'FILE_MAX_BYTES'), + 'IMAGE_MAX_BYTES': ('plugins.platforms.wecom.media', 'IMAGE_MAX_BYTES'), + 'MAX_INTERMEDIATE_FRAMES': ('plugins.platforms.wecom.streaming', 'MAX_INTERMEDIATE_FRAMES'), + 'MAX_UPLOAD_CHUNKS': ('plugins.platforms.wecom.media', 'MAX_UPLOAD_CHUNKS'), + 'ReplyFrame': ('plugins.platforms.wecom.streaming', 'ReplyFrame'), + 'STREAM_EXPIRED_ERRCODE': ('plugins.platforms.wecom.streaming', 'STREAM_EXPIRED_ERRCODE'), + 'STREAM_REQUEST_EXPIRED_ERRCODE': ('plugins.platforms.wecom.streaming', 'STREAM_REQUEST_EXPIRED_ERRCODE'), + 'STREAM_VERSION_CONFLICT_ERRCODE': ('plugins.platforms.wecom.streaming', 'STREAM_VERSION_CONFLICT_ERRCODE'), + 'UPLOAD_CHUNK_SIZE': ('plugins.platforms.wecom.media', 'UPLOAD_CHUNK_SIZE'), + 'VIDEO_MAX_BYTES': ('plugins.platforms.wecom.media', 'VIDEO_MAX_BYTES'), + 'VOICE_MAX_BYTES': ('plugins.platforms.wecom.media', 'VOICE_MAX_BYTES'), + 'VOICE_SUPPORTED_MIMES': ('plugins.platforms.wecom.media', 'VOICE_SUPPORTED_MIMES'), + 'WeComStreamExpiredError': ('plugins.platforms.wecom.streaming', 'WeComStreamExpiredError'), + 'cache_document_from_bytes_async': ('gateway.platforms.base', 'cache_document_from_bytes_async'), + 'cache_image_from_bytes_async': ('gateway.platforms.base', 'cache_image_from_bytes_async'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/spotify/__init__.py b/plugins/spotify/__init__.py index 230367c619..cd01225088 100644 --- a/plugins/spotify/__init__.py +++ b/plugins/spotify/__init__.py @@ -29,3 +29,29 @@ def register(ctx) -> None: """Register all Spotify tools. Called once by the plugin loader.""" for name, schema, handler, emoji in _TOOLS: ctx.register_tool(name=name, toolset="spotify", schema=schema, handler=handler, check_fn=_t._check_spotify_available, emoji=emoji) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'SPOTIFY_ALBUMS_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_ALBUMS_SCHEMA'), + 'SPOTIFY_DEVICES_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_DEVICES_SCHEMA'), + 'SPOTIFY_LIBRARY_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_LIBRARY_SCHEMA'), + 'SPOTIFY_PLAYBACK_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_PLAYBACK_SCHEMA'), + 'SPOTIFY_PLAYLISTS_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_PLAYLISTS_SCHEMA'), + 'SPOTIFY_QUEUE_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_QUEUE_SCHEMA'), + 'SPOTIFY_SEARCH_SCHEMA': ('plugins.spotify.tools', 'SPOTIFY_SEARCH_SCHEMA'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/spotify/client.py b/plugins/spotify/client.py index 50262abc04..b747d95324 100644 --- a/plugins/spotify/client.py +++ b/plugins/spotify/client.py @@ -159,3 +159,24 @@ def normalize_spotify_uris(values: Iterable[str], expected_type: Optional[str] = if not uris: raise SpotifyError("At least one Spotify item is required.") return uris + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'compact_json': ('gateway.hosted_rooms_common', 'compact_json'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/spotify/tools.py b/plugins/spotify/tools.py index f072f2694c..af803b3093 100644 --- a/plugins/spotify/tools.py +++ b/plugins/spotify/tools.py @@ -314,3 +314,24 @@ SPOTIFY_LIBRARY_SCHEMA = _schema("spotify_library", "List, save, or remove the u "action": _enum("list", "save", "remove"), "limit": _INT, "offset": _INT, "market": COMMON_STRING, "uris": _STR_ARRAY, "ids": _STR_ARRAY, "items": _STR_ARRAY, }, ("kind", "action")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'SpotifyAPIError': ('plugins.spotify.client', 'SpotifyAPIError'), + 'SpotifyAuthRequiredError': ('plugins.spotify.client', 'SpotifyAuthRequiredError'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/teams_pipeline/cli.py b/plugins/teams_pipeline/cli.py index c844e9c25d..b45c20e763 100644 --- a/plugins/teams_pipeline/cli.py +++ b/plugins/teams_pipeline/cli.py @@ -314,3 +314,27 @@ _REQUIRED_ARGS: dict[Callable[[Any], None], tuple[tuple[str, ...], str]] = { _cmd_run: (("job_id",), "job_id is required"), _cmd_renew_subscription: (("subscription_id", "expiration"), "subscription_id and --expiration are required"), _cmd_delete_subscription: (("subscription_id",), "subscription_id is required")} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +from datetime import timedelta # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'GraphSubscription': ('plugins.teams_pipeline.models', 'GraphSubscription'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/teams_pipeline/meetings.py b/plugins/teams_pipeline/meetings.py index 114f1849fe..6599da48be 100644 --- a/plugins/teams_pipeline/meetings.py +++ b/plugins/teams_pipeline/meetings.py @@ -248,3 +248,48 @@ async def enrich_meeting_with_call_record( artifact_type="call_record", artifact_id=str(payload["id"]), display_name=payload.get("type") or "call_record", source_url=payload.get("webUrl"), created_at=payload.get("startDateTime"), available_at=payload.get("endDateTime"), metadata={"call_record": payload, "metrics": metrics}) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +async def fetch_call_record_artifact( + client: MicrosoftGraphClient, + *, + call_record_id: str, + allow_permission_errors: bool = True, +) -> MeetingArtifact | None: + try: + payload = await client.get_json(f"/communications/callRecords/{quote(call_record_id, safe='')}") + except MicrosoftGraphAPIError as exc: + if exc.status_code in {401, 403} and allow_permission_errors: + return None + if exc.status_code == 404: + return None + raise _wrap_graph_error(exc, missing_message=f"Call record not found: {call_record_id}") from exc + + if not isinstance(payload, dict) or not payload.get("id"): + return None + + metrics = { + "version": payload.get("version"), + "modalities": payload.get("modalities"), + "participant_count": len(payload.get("participants") or []), + "organizer": _parse_organizer_user_id(payload), + } + sessions = payload.get("sessions") or [] + if sessions: + metrics["session_count"] = len(sessions) + + return MeetingArtifact( + artifact_type="call_record", + artifact_id=str(payload["id"]), + display_name=payload.get("type") or "call_record", + source_url=payload.get("webUrl"), + created_at=payload.get("startDateTime"), + available_at=payload.get("endDateTime"), + metadata={"call_record": payload, "metrics": metrics}, + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/teams_pipeline/pipeline.py b/plugins/teams_pipeline/pipeline.py index 0b370a1780..aab24a973c 100644 --- a/plugins/teams_pipeline/pipeline.py +++ b/plugins/teams_pipeline/pipeline.py @@ -496,3 +496,11 @@ def _render_summary_markdown(payload: TeamsMeetingSummaryPayload) -> str: lines += ["", f"## {heading}", body or ("No summary available." if heading == "Summary" else "- None")] lines += ["", f"Confidence: {payload.confidence or 'unknown'}", payload.confidence_notes or ""] return "\n".join(lines).strip() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/teams_pipeline/subscriptions.py b/plugins/teams_pipeline/subscriptions.py index 22ffc5f25f..745acda3f8 100644 --- a/plugins/teams_pipeline/subscriptions.py +++ b/plugins/teams_pipeline/subscriptions.py @@ -120,3 +120,30 @@ async def maintain_graph_subscriptions( "threshold_hours": threshold_hours, "extend_hours": extend_hours, "candidates": candidates, "renewed": renewed, "skipped": skipped, } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from plugins.teams_pipeline.store import resolve_teams_pipeline_store_path # noqa: F401,E402 + +def resolve_store_path(path: str | None) -> str: + return str(resolve_teams_pipeline_store_path(path)) + +def build_store(path: str | None = None) -> TeamsPipelineStore: + return TeamsPipelineStore(resolve_store_path(path)) + + +_PLUGIN_COMPAT_LAZY = { + 'resolve_teams_pipeline_store_path': ('plugins.teams_pipeline.store', 'resolve_teams_pipeline_store_path'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/video_gen/fal/__init__.py b/plugins/video_gen/fal/__init__.py index bc9e2a7f74..9760891966 100644 --- a/plugins/video_gen/fal/__init__.py +++ b/plugins/video_gen/fal/__init__.py @@ -364,3 +364,11 @@ class FALVideoGenProvider(VideoGenProvider): def register(ctx) -> None: """Plugin entry point — wire ``FALVideoGenProvider`` into the registry.""" ctx.register_video_gen_provider(FALVideoGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/video_gen/xai/__init__.py b/plugins/video_gen/xai/__init__.py index 7e59fe66f8..8365116bbc 100644 --- a/plugins/video_gen/xai/__init__.py +++ b/plugins/video_gen/xai/__init__.py @@ -316,3 +316,63 @@ async def _submit_xai_video_payload(api_key: str, base_url: str, endpoint: str, def register(ctx) -> None: """Plugin entry point — wire ``XAIVideoGenProvider`` into the registry.""" ctx.register_video_gen_provider(XAIVideoGenProvider()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def run_xai_video_generation( + *, + prompt: str, + model: Optional[str], + explicit_model: bool, + image_url: Optional[str], + reference_image_urls: Optional[List[str]], + duration: Optional[int], + aspect_ratio: str, + resolution: str, +) -> Dict[str, Any]: + return _run_xai_video_coroutine( + _generate_xai_video_async( + prompt=prompt, + model=model, + explicit_model=explicit_model, + image_url=image_url, + reference_image_urls=reference_image_urls, + duration=duration, + aspect_ratio=aspect_ratio, + resolution=resolution, + ), + operation_label="generation", + model=model, + prompt=prompt, + aspect_ratio=aspect_ratio, + ) + +def _run_xai_video_coroutine( + coro, + *, + operation_label: str, + model: Optional[str], + prompt: str, + aspect_ratio: str, +) -> Dict[str, Any]: + try: + loop = asyncio.new_event_loop() + try: + return loop.run_until_complete(coro) + finally: + loop.close() + except Exception as exc: + logger.warning("xAI video %s unexpected failure: %s", operation_label, exc, exc_info=True) + return error_response( + error=f"xAI video {operation_label} failed: {exc}", + error_type="api_error", + provider="xai", + model=model or DEFAULT_MODEL, + prompt=prompt, + aspect_ratio=aspect_ratio, + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/brave_free/provider.py b/plugins/web/brave_free/provider.py index 924763d4ba..f03488951f 100644 --- a/plugins/web/brave_free/provider.py +++ b/plugins/web/brave_free/provider.py @@ -46,3 +46,24 @@ class BraveFreeWebSearchProvider(BaseWebSearchProvider): "Brave Search (Free)", "free", "Free-tier API key — 2k queries/mo, search only.", "BRAVE_SEARCH_API_KEY", "Brave Search API key (free tier)", "https://brave.com/search/api/", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/ddgs/provider.py b/plugins/web/ddgs/provider.py index 48d7050854..68acbb3add 100644 --- a/plugins/web/ddgs/provider.py +++ b/plugins/web/ddgs/provider.py @@ -217,3 +217,23 @@ class DDGSWebSearchProvider(BaseWebSearchProvider): "DuckDuckGo (ddgs)", "free · no key · search only", "Search via the ddgs Python package — no API key (pair with any extract provider)", post_setup="ddgs", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/exa/provider.py b/plugins/web/exa/provider.py index 982598fc19..f4340fff72 100644 --- a/plugins/web/exa/provider.py +++ b/plugins/web/exa/provider.py @@ -67,3 +67,24 @@ class ExaWebSearchProvider(BaseWebSearchProvider): free_tag="Semantic + neural web search with content extraction on Exa's anonymous free tier. Rate-limited under burst load.", paid_tag="Semantic + neural web search with content extraction via the Exa SDK. Unthrottled, guaranteed service.", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/firecrawl/provider.py b/plugins/web/firecrawl/provider.py index 25f024eb20..1f338ec740 100644 --- a/plugins/web/firecrawl/provider.py +++ b/plugins/web/firecrawl/provider.py @@ -322,3 +322,26 @@ class FirecrawlWebSearchProvider(BaseWebSearchProvider): "Full search + extract; supports keyless cloud, direct API, and Nous tool-gateway routing.", "FIRECRAWL_API_KEY", "Firecrawl API key (optional; blank = keyless cloud or self-hosted)", "https://docs.firecrawl.dev/introduction", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import NoReturn # noqa: F401,E402 +from typing import TYPE_CHECKING # noqa: F401,E402 +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/keenable/provider.py b/plugins/web/keenable/provider.py index df704cb84a..324d1f873a 100644 --- a/plugins/web/keenable/provider.py +++ b/plugins/web/keenable/provider.py @@ -84,3 +84,23 @@ class KeenableWebSearchProvider(BaseWebSearchProvider): free_tag="Independent web index for AI apps — fast search + page fetch on Keenable's anonymous free tier.", paid_tag="Independent web index for AI apps. Keyed access with higher limits and guaranteed service.", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/parallel/provider.py b/plugins/web/parallel/provider.py index 96a9725f59..2be0b0f83c 100644 --- a/plugins/web/parallel/provider.py +++ b/plugins/web/parallel/provider.py @@ -86,3 +86,23 @@ class ParallelWebSearchProvider(BaseWebSearchProvider): free_tag="Objective-tuned search + page extraction on Parallel's anonymous free tier. Rate-limited under burst load.", paid_tag="Objective-tuned search + parallel page extraction via the Parallel SDK. Unthrottled, guaranteed service.", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/searxng/provider.py b/plugins/web/searxng/provider.py index 14f29a4630..187e8598b3 100644 --- a/plugins/web/searxng/provider.py +++ b/plugins/web/searxng/provider.py @@ -43,3 +43,24 @@ class SearXNGWebSearchProvider(BaseWebSearchProvider): "SearXNG", "free · self-hosted", "Free, privacy-respecting metasearch. Point SEARXNG_URL at your instance.", "SEARXNG_URL", "SearXNG instance URL (e.g. http://localhost:8080)", "https://searx.space/", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/tavily/provider.py b/plugins/web/tavily/provider.py index db31fc8566..6e689b450b 100644 --- a/plugins/web/tavily/provider.py +++ b/plugins/web/tavily/provider.py @@ -122,3 +122,23 @@ class TavilyWebSearchProvider(BaseWebSearchProvider): "Tavily", "free · key optional", "Search + extract. Opt-in keyless; set TAVILY_API_KEY for higher limits.", "TAVILY_API_KEY", "Tavily API key (optional — keyless works when Tavily is selected)", "https://app.tavily.com/home", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/plugins/web/xai/provider.py b/plugins/web/xai/provider.py index 18b12fc09d..30f68bb381 100644 --- a/plugins/web/xai/provider.py +++ b/plugins/web/xai/provider.py @@ -258,3 +258,23 @@ class XAIWebSearchProvider(BaseWebSearchProvider): "xAI Web Search (Grok)", "paid", "Agentic web search via Grok's web_search tool — uses xAI Grok OAuth or XAI_API_KEY.", post_setup="xai_grok", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'WebSearchProvider': ('agent.web_search_provider', 'WebSearchProvider'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/providers/__init__.py b/providers/__init__.py index 2a4ee8c536..c198273e9b 100644 --- a/providers/__init__.py +++ b/providers/__init__.py @@ -412,3 +412,23 @@ def _discover_providers() -> None: # (Pip entry-point providers are discovered in step 0, before the # filesystem plugins, so first-party profiles always win on name # collision — see _discover_entry_point_providers.) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'OMIT_TEMPERATURE': ('providers.base', 'OMIT_TEMPERATURE'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/run_agent.py b/run_agent.py index d64048251d..a3db668990 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1502,3 +1502,52 @@ def main( if __name__ == "__main__": import fire fire.Fire(main) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from types import SimpleNamespace # noqa: F401,E402 +import asyncio # noqa: F401,E402 +import base64 # noqa: F401,E402 +import copy # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import tempfile # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'COMPRESSED_SUMMARY_METADATA_KEY': ('agent.context_compressor', 'COMPRESSED_SUMMARY_METADATA_KEY'), + 'ContextCompressor': ('agent.context_compressor', 'ContextCompressor'), + 'DEFAULT_AGENT_IDENTITY': ('agent.prompt_builder', 'DEFAULT_AGENT_IDENTITY'), + 'FailoverReason': ('agent.error_classifier', 'FailoverReason'), + 'OpenAI': ('agent.process_bootstrap', 'OpenAI'), + 'atomic_json_write': ('utils', 'atomic_json_write'), + 'build_context_files_prompt': ('agent.prompt_builder', 'build_context_files_prompt'), + 'build_environment_hints': ('agent.prompt_builder', 'build_environment_hints'), + 'build_skills_system_prompt': ('agent.prompt_builder', 'build_skills_system_prompt'), + 'check_toolset_requirements': ('model_tools', 'check_toolset_requirements'), + 'convert_scratchpad_to_think': ('agent.trajectory', 'convert_scratchpad_to_think'), + 'estimate_request_tokens_rough': ('agent.model_metadata', 'estimate_request_tokens_rough'), + 'file_mutation_result_landed': ('agent.tool_result_classification', 'file_mutation_result_landed'), + 'flatten_message_text': ('agent.message_content', 'flatten_message_text'), + 'get_tool_definitions': ('model_tools', 'get_tool_definitions'), + 'handle_function_call': ('model_tools', 'handle_function_call'), + 'is_truthy_value': ('utils', 'is_truthy_value'), + 'jittered_backoff': ('agent.retry_utils', 'jittered_backoff'), + 'load_soul_md': ('agent.prompt_builder', 'load_soul_md'), + 'normalize_usage': ('agent.usage_pricing', 'normalize_usage'), + 'redact_sensitive_text': ('agent.redact', 'redact_sensitive_text'), + 'request_hard_interrupt': ('agent.interrupt_compat', 'request_hard_interrupt'), + 'sanitize_context': ('agent.memory_manager', 'sanitize_context'), + 'user_originated_turn_view': ('agent.context_compressor', 'user_originated_turn_view'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/scripts/check_compat_pointers.py b/scripts/check_compat_pointers.py new file mode 100644 index 0000000000..001d753141 --- /dev/null +++ b/scripts/check_compat_pointers.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Fail when in-tree code depends on a plugin-compat pointer. + +``compat_manifest.json`` lists every name the Sep 2026 decomposition kept importable from its OLD +module purely for external plugins (the `PLUGIN-COMPAT` blocks). Those blocks are removed on a +schedule by reverting the commit that added them, so nothing inside this repository may depend on +them — otherwise the revert breaks the tree. This check walks every first-party Python file (source +AND tests) and flags: + + from import # direct import through the old path + import ; . # attribute access through the old path + patch(".") / monkeypatch.setattr(, "") + +Exit 1 with a file:line list on any hit. Run: python scripts/check_compat_pointers.py +""" +from __future__ import annotations + +import ast +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +MANIFEST = ROOT / "compat_manifest.json" +SKIP_DIRS = {".git", "node_modules", "website", "skills", "optional-skills", "apps", "evals", "build", "MagicMock", ".worktrees", "__pycache__"} + + +def _py_files(): + for p in ROOT.rglob("*.py"): + parts = p.relative_to(ROOT).parts + if parts[0] in SKIP_DIRS or p.name == "check_compat_pointers.py": + continue + yield p + + +def main() -> int: + if not MANIFEST.exists(): + print("compat_manifest.json missing — nothing to check (compat layer already reverted?)") + return 0 + entries = json.loads(MANIFEST.read_text(encoding="utf-8"))["entries"] + compat: dict[str, set[str]] = {} + for e in entries: + compat.setdefault(e["facade"], set()).add(e["name"]) + facades = set(compat) + hits: list[str] = [] + str_pat = re.compile(r"""["']((?:[A-Za-z_][\w]*\.)+[A-Za-z_]\w*)["']""") + for path in _py_files(): + rel = path.relative_to(ROOT) + try: + src = path.read_text(encoding="utf-8", errors="ignore") + tree = ast.parse(src) + except SyntaxError: + continue + # module-level facade import aliases in this file: alias -> facade + aliases: dict[str, str] = {} + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module in facades and node.level == 0: + bad = [a.name for a in node.names if a.name in compat[node.module]] + for b in bad: + hits.append(f"{rel}:{node.lineno}: from {node.module} import {b}") + elif isinstance(node, ast.Import): + for a in node.names: + if a.name in facades: + aliases[a.asname or a.name] = a.name + elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module: + for a in node.names: + full = f"{node.module}.{a.name}" + if full in facades: + aliases[a.asname or a.name] = full + for node in ast.walk(tree): + if isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name): + fac = aliases.get(node.value.id) + if fac and node.attr in compat[fac]: + hits.append(f"{rel}:{node.lineno}: {node.value.id}.{node.attr} (via {fac})") + elif isinstance(node, ast.Constant) and isinstance(node.value, str): + m = str_pat.fullmatch(node.value.strip()) + if m: + dotted = m.group(1); fac, _, name = dotted.rpartition(".") + if fac in facades and name in compat[fac]: + hits.append(f"{rel}:{node.lineno}: \"{dotted}\" (string patch target)") + if hits: + print("❌ in-tree code depends on plugin-compat pointers (scheduled for removal):") + for h in sorted(set(hits)): + print(" " + h) + print(f"\n{len(set(hits))} site(s). Import from the defining module instead (see COMPAT_MANIFEST.md).") + return 1 + print(f"✅ no in-tree dependency on the {len(entries)} plugin-compat pointers") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/apply_layout_tool.py b/tools/apply_layout_tool.py index 167bca418c..8395a2d480 100644 --- a/tools/apply_layout_tool.py +++ b/tools/apply_layout_tool.py @@ -50,3 +50,11 @@ registry.register( handler=lambda args, **kw: apply_layout_tool(preset=args.get("preset", "")), emoji="🧱", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/approval.py b/tools/approval.py index ff53ea9c1d..9e1570caa4 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -1109,3 +1109,51 @@ def check_execute_code_guard(code: str, env_type: str, has_host_access: bool = F # Load permanent allowlist from config on module import load_permanent_allowlist() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import contextlib # noqa: F401,E402 +import contextvars # noqa: F401,E402 +import fnmatch # noqa: F401,E402 +import functools # noqa: F401,E402 +import re # noqa: F401,E402 +import shlex # noqa: F401,E402 +import sys # noqa: F401,E402 +import tempfile # noqa: F401,E402 +import time # noqa: F401,E402 +import unicodedata # noqa: F401,E402 +import uuid # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DANGEROUS_PATTERNS': ('tools.approval_detection', 'DANGEROUS_PATTERNS'), + 'DANGEROUS_PATTERNS_COMPILED': ('tools.approval_detection', 'DANGEROUS_PATTERNS_COMPILED'), + 'HARDLINE_PATTERNS': ('tools.approval_detection', 'HARDLINE_PATTERNS'), + 'HARDLINE_PATTERNS_COMPILED': ('tools.approval_detection', 'HARDLINE_PATTERNS_COMPILED'), + 'HUMAN_WAIT_MARGIN_S': ('tools.approval_human_wait', 'HUMAN_WAIT_MARGIN_S'), + 'cfg_get': ('hermes_cli.config', 'cfg_get'), + 'get_plugin_manager': ('tools.approval_prompt', 'get_plugin_manager'), + 'human_wait_ceiling': ('tools.approval_human_wait', 'human_wait_ceiling'), + 'human_wait_seconds': ('tools.approval_human_wait', 'human_wait_seconds'), + 'human_wait_window': ('tools.approval_human_wait', 'human_wait_window'), + 'is_interrupted': ('tools.interrupt', 'is_interrupted'), + 'request_elicitation_consent': ('tools.approval_prompt', 'request_elicitation_consent'), + 'reset_current_observability_context': ('tools.approval_context', 'reset_current_observability_context'), + 'reset_current_session_key': ('tools.approval_context', 'reset_current_session_key'), + 'reset_hermes_interactive_context': ('tools.approval_context', 'reset_hermes_interactive_context'), + 'set_current_observability_context': ('tools.approval_context', 'set_current_observability_context'), + 'set_current_session_key': ('tools.approval_context', 'set_current_session_key'), + 'set_hermes_interactive_context': ('tools.approval_context', 'set_hermes_interactive_context'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/async_delegation.py b/tools/async_delegation.py index 4fef9cc7e0..1ef7fbdc27 100644 --- a/tools/async_delegation.py +++ b/tools/async_delegation.py @@ -892,3 +892,23 @@ def _reset_for_tests() -> None: thread.join(timeout=2) with _records_lock: _records.clear() + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def active_for_session(origin_ui_session_id: str) -> int: + """Number of live async delegations owned by one UI session.""" + if not origin_ui_session_id: + return 0 + with _records_lock: + return sum( + 1 + for r in _records.values() + if r.get("status") in {"running", "stalling", "finalizing"} + and str(r.get("origin_ui_session_id") or "") + == origin_ui_session_id + ) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/browser_camofox_state.py b/tools/browser_camofox_state.py index f8517259ee..7f2a4c881e 100644 --- a/tools/browser_camofox_state.py +++ b/tools/browser_camofox_state.py @@ -25,3 +25,14 @@ def get_camofox_identity(task_id: Optional[str] = None) -> Dict[str, str]: user_digest = uuid.uuid5(uuid.NAMESPACE_URL, f"camofox-user:{scope_root}").hex[:10] session_digest = uuid.uuid5(uuid.NAMESPACE_URL, f"camofox-session:{scope_root}:{task_id or 'default'}").hex[:16] return {"user_id": f"hermes_{user_digest}", "session_key": f"task_{session_digest}"} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +CAMOFOX_STATE_DIR_NAME = "browser_auth" + +CAMOFOX_STATE_SUBDIR = "camofox" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/browser_dialog_tool.py b/tools/browser_dialog_tool.py index ecd6dffa1e..07873f71d3 100644 --- a/tools/browser_dialog_tool.py +++ b/tools/browser_dialog_tool.py @@ -107,3 +107,24 @@ registry.register( check_fn=_browser_dialog_check, emoji="💬", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('tools.approval', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/browser_supervisor.py b/tools/browser_supervisor.py index 362fb42bb1..9a612cf319 100644 --- a/tools/browser_supervisor.py +++ b/tools/browser_supervisor.py @@ -472,3 +472,40 @@ SUPERVISOR_REGISTRY = _SupervisorRegistry() __all__ = ["CDPSupervisor", "SUPERVISOR_REGISTRY", "SupervisorSnapshot", "_SupervisorRegistry"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +CONSOLE_HISTORY_MAX = 50 + +@dataclass +class ConsoleEvent: + """Ring buffer entry for console + exception traffic.""" + + ts: float + level: str # "log" | "error" | "warning" | "exception" + text: str + url: Optional[str] = None + + +_PLUGIN_COMPAT_LAZY = { + 'DIALOG_BRIDGE_HOST': ('tools.browser_supervisor_dialogs', 'DIALOG_BRIDGE_HOST'), + 'DIALOG_BRIDGE_URL_PATTERN': ('tools.browser_supervisor_dialogs', 'DIALOG_BRIDGE_URL_PATTERN'), + 'DIALOG_POLICY_AUTO_ACCEPT': ('tools.browser_supervisor_dialogs', 'DIALOG_POLICY_AUTO_ACCEPT'), + 'DIALOG_POLICY_AUTO_DISMISS': ('tools.browser_supervisor_dialogs', 'DIALOG_POLICY_AUTO_DISMISS'), + 'DIALOG_POLICY_MUST_RESPOND': ('tools.browser_supervisor_dialogs', 'DIALOG_POLICY_MUST_RESPOND'), + 'FRAME_TREE_MAX_ENTRIES': ('tools.browser_supervisor_frames', 'FRAME_TREE_MAX_ENTRIES'), + 'FRAME_TREE_MAX_OOPIF_DEPTH': ('tools.browser_supervisor_frames', 'FRAME_TREE_MAX_OOPIF_DEPTH'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/browser_tool.py b/tools/browser_tool.py index 62607b214a..41a047ffc1 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -1312,3 +1312,52 @@ for _name, _emoji, _check_fn, _defaults, *_extra in _BROWSER_TOOL_TABLE: registry.register(name=_name, toolset="browser", schema=_BROWSER_SCHEMA_MAP[_name], handler=_routed_handler(_name, _fallback_call(_name, _defaults, *_extra)), check_fn=_check_fn, emoji=_emoji) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +import contextlib # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +import functools # noqa: F401,E402 +import re # noqa: F401,E402 +import shutil # noqa: F401,E402 +import signal # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 + +SNAPSHOT_SUMMARIZE_THRESHOLD = DEFAULT_SNAPSHOT_THRESHOLD + + +_PLUGIN_COMPAT_LAZY = { + 'BrowserUseProvider': ('plugins.browser.browser_use.provider', 'BrowserUseBrowserProvider'), + 'BrowserbaseProvider': ('plugins.browser.browserbase.provider', 'BrowserbaseBrowserProvider'), + 'CloudBrowserProvider': ('agent.browser_provider', 'BrowserProvider'), + 'FirecrawlProvider': ('plugins.browser.firecrawl.provider', 'FirecrawlBrowserProvider'), + 'agent_browser_runnable': ('hermes_constants', 'agent_browser_runnable'), + 'check_browser_requirements': ('tools.browser_tool_install', 'check_browser_requirements'), + 'check_browser_vision_requirements': ('tools.browser_tool_install', 'check_browser_vision_requirements'), + 'cleanup_all_browsers': ('tools.browser_tool_lifecycle', 'cleanup_all_browsers'), + 'cleanup_browser': ('tools.browser_tool_lifecycle', 'cleanup_browser'), + 'get_hermes_home_override': ('hermes_constants', 'get_hermes_home_override'), + 'hermes_home_key': ('hermes_constants', 'hermes_home_key'), + 'is_truthy_value': ('utils', 'is_truthy_value'), + 'lightpanda_engine_status': ('tools.browser_tool_lightpanda_fallback', 'lightpanda_engine_status'), + 'node_tool_runnable': ('hermes_constants', 'node_tool_runnable'), + 'normalize_browser_cloud_provider': ('tools.tool_backend_helpers', 'normalize_browser_cloud_provider'), + 'reset_hermes_home_override': ('hermes_constants', 'reset_hermes_home_override'), + 'set_hermes_home_override': ('hermes_constants', 'set_hermes_home_override'), + 'warm_agent_browser_npx_cache': ('tools.browser_tool_install', 'warm_agent_browser_npx_cache'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/clarify_gateway.py b/tools/clarify_gateway.py index dd03b6e185..e33a5ff083 100644 --- a/tools/clarify_gateway.py +++ b/tools/clarify_gateway.py @@ -282,3 +282,27 @@ def get_clarify_timeout() -> int: return resolve_clarify_timeout(load_config() or {}) except Exception: return 3600 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_notify(session_key: str) -> Optional[Callable[[_ClarifyEntry], None]]: + with _lock: + return _notify_cbs.get(session_key) + +def register_notify(session_key: str, cb: Callable[[_ClarifyEntry], None]) -> None: + """Register a per-session notify callback used by ``clarify_callback``.""" + with _lock: + _notify_cbs[session_key] = cb + +def unregister_notify(session_key: str) -> None: + """Drop the per-session notify callback and cancel any pending clarify entries.""" + with _lock: + _notify_cbs.pop(session_key, None) + # Cancel any pending entries so blocked threads unwind when the run + # ends (interrupt, completion, gateway shutdown). + clear_session(session_key) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/close_preview_tool.py b/tools/close_preview_tool.py index 61687c76fd..12095c9231 100644 --- a/tools/close_preview_tool.py +++ b/tools/close_preview_tool.py @@ -13,3 +13,50 @@ def close_preview_tool(url: str = "") -> str: "preview.close", {"url": target}, "Failed to close the preview pane: ", "The preview pane is only available in the Hermes desktop app.", {"success": True, "url": target}, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +CLOSE_PREVIEW_SCHEMA = { + "name": "close_preview", + "description": ( + "Close the preview pane beside the chat in the Hermes desktop app, or one " + "tab inside it. Use this when the user asks to close, hide, or dismiss the " + "preview — e.g. \"close the preview pane\", \"close cnn.com\", \"hide the " + "preview\". Omit url to close the whole pane (every tab). Pass a web URL, " + "localhost address, or file path to close only that tab. Counterpart of " + "open_preview." + ), + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": ( + "Optional. The tab to close: a web URL (https://… or a bare " + "domain), a localhost URL, or a file path. Omit to close the " + "whole preview pane." + ), + }, + }, + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'registry': ('tools.registry', 'registry'), + 'tool_error': ('tools.registry', 'tool_error'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 26f2ab680f..1896fe2ab1 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -902,3 +902,30 @@ registry.register( handler=_execute_code_handler, check_fn=check_sandbox_requirements, emoji="🐍", max_result_size_chars=100_000, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import platform # noqa: F401,E402 +import socket # noqa: F401,E402 +import sys # noqa: F401,E402 + +DEFAULT_KERNEL_MODE = "session" + +KERNEL_MODES = ("per-call", "session") # legacy compat constant + + +_PLUGIN_COMPAT_LAZY = { + 'thread_scoped_silence': ('agent.thread_scoped_output', 'thread_scoped_silence'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/code_kernel_remote.py b/tools/code_kernel_remote.py index 5f5829efa0..47bd24a878 100644 --- a/tools/code_kernel_remote.py +++ b/tools/code_kernel_remote.py @@ -384,3 +384,11 @@ def _run_attached_cell(kernel: RemoteKernel, key: Tuple, code: str, *, env, task if cell_status == "error" and result["traceback"]: result["error"] = result["traceback"].strip().splitlines()[-1] return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import base64 # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/computer_use/__init__.py b/tools/computer_use/__init__.py index 44b5ec7bc0..9fa01d3039 100644 --- a/tools/computer_use/__init__.py +++ b/tools/computer_use/__init__.py @@ -10,3 +10,28 @@ Modules: `tool.py` (handler, approval gate, response shaping), `backend.py` (abs `ComputerUseBackend` + result dataclasses), `cua_backend.py` (default MCP-over-stdio backend + `cua_backend_parse`/`_session`/`_daemon` siblings), `schema.py` (byte-frozen). """ + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from __future__ import annotations # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'check_computer_use_requirements': ('tools.computer_use.tool', 'check_computer_use_requirements'), + 'get_computer_use_schema': ('tools.computer_use.tool', 'get_computer_use_schema'), + 'handle_computer_use': ('tools.computer_use.tool', 'handle_computer_use'), + 'release_computer_use_session': ('tools.computer_use.tool', 'release_computer_use_session'), + 'set_approval_callback': ('tools.computer_use.tool', 'set_approval_callback'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/computer_use/cua_backend.py b/tools/computer_use/cua_backend.py index bcf285bb04..109575b141 100644 --- a/tools/computer_use/cua_backend.py +++ b/tools/computer_use/cua_backend.py @@ -374,3 +374,38 @@ class CuaDriverBackend(_CaptureMixin, _InputMixin, ComputerUseBackend): meta = {k: v for part in (data, structured) if isinstance(part, dict) for k, v in part.items()} return _action_result_from(name, not out["isError"], message, meta, structured, requested_delivery=args.get("delivery_mode")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import PureWindowsPath # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +import asyncio # noqa: F401,E402 +import base64 # noqa: F401,E402 +import concurrent.futures # noqa: F401,E402 +from collections import deque # noqa: F401,E402 +import functools # noqa: F401,E402 +import json # noqa: F401,E402 +import re # noqa: F401,E402 +import shutil # noqa: F401,E402 +import tempfile # noqa: F401,E402 +import time # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'CaptureResult': ('tools.computer_use.backend', 'CaptureResult'), + 'UIElement': ('tools.computer_use.backend', 'UIElement'), + 'cua_driver_install_hint': ('tools.computer_use.cua_backend_driver', 'cua_driver_install_hint'), + 'cua_driver_update_check': ('tools.computer_use.cua_backend_driver', 'cua_driver_update_check'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/computer_use/permissions.py b/tools/computer_use/permissions.py index b1d2e5434d..b4a68acd7a 100644 --- a/tools/computer_use/permissions.py +++ b/tools/computer_use/permissions.py @@ -109,3 +109,11 @@ def request_permissions_grant(driver_cmd: Optional[str] = None) -> int: except Exception as exc: # pragma: no cover - defensive print(f"cua-driver permissions grant failed: {exc}", file=sys.stderr) return 2 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/computer_use/tool.py b/tools/computer_use/tool.py index 04401721c8..953bfda4b9 100644 --- a/tools/computer_use/tool.py +++ b/tools/computer_use/tool.py @@ -757,3 +757,11 @@ def check_computer_use_requirements() -> bool: def get_computer_use_schema() -> Dict[str, Any]: from tools.computer_use.schema import COMPUTER_USE_SCHEMA return COMPUTER_USE_SCHEMA + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import struct # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index 685bf9ad95..a728873945 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -1026,3 +1026,24 @@ registry.register( check_fn=check_cronjob_requirements, emoji="⏰", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import re # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'effective_job_state': ('cron.jobs', 'effective_job_state'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index 77b54d38a0..9eb979ded6 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -610,3 +610,39 @@ registry.register( emoji="🔀", dynamic_schema_overrides=_build_dynamic_schema_overrides, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from concurrent.futures import TimeoutError as FuturesTimeoutError # noqa: F401,E402 +import contextvars # noqa: F401,E402 +import enum # noqa: F401,E402 +import json # noqa: F401,E402 +import os # noqa: F401,E402 +import re # noqa: F401,E402 +import threading # noqa: F401,E402 +from urllib.parse import urlsplit # noqa: F401,E402 +from urllib.parse import urlunsplit # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_CHILD_TIMEOUT': ('tools.delegate_tool_config', 'DEFAULT_CHILD_TIMEOUT'), + 'DEFAULT_MAX_SUMMARY_CHARS': ('tools.delegate_tool_results', 'DEFAULT_MAX_SUMMARY_CHARS'), + 'DEFAULT_TOOLSETS': ('tools.delegate_tool_toolsets', 'DEFAULT_TOOLSETS'), + 'MAX_DEPTH': ('tools.delegate_tool_config', 'MAX_DEPTH'), + 'TOOLSETS': ('toolsets', 'TOOLSETS'), + 'base_url_hostname': ('utils', 'base_url_hostname'), + 'file_state': ('tools', 'file_state'), + 'request_hard_interrupt': ('agent.interrupt_compat', 'request_hard_interrupt'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/delegation_live_log.py b/tools/delegation_live_log.py index 40965d9764..d2d2e0325e 100644 --- a/tools/delegation_live_log.py +++ b/tools/delegation_live_log.py @@ -302,3 +302,14 @@ def prune_stale_live_dirs(max_age_days: int = LIVE_RETENTION_DAYS) -> int: except OSError: continue return removed + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def new_live_delegation_id() -> str: + """Same shape as async_delegation's ids so the dir name matches the handle.""" + return f"deleg_{uuid.uuid4().hex[:8]}" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/delegation_output_schema.py b/tools/delegation_output_schema.py index 0e1a55ff9f..ea9833b1d8 100644 --- a/tools/delegation_output_schema.py +++ b/tools/delegation_output_schema.py @@ -105,3 +105,12 @@ def build_retry_message(errors: List[str]) -> str: "validator. Validation errors:\n" f"{error_block}\n\n" "Reply with ONLY the corrected JSON object matching the OUTPUT " "CONTRACT schema from your task context. No prose, no explanations.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +MAX_SCHEMA_RETRIES = 1 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/discord_tool.py b/tools/discord_tool.py index 7e37899c3a..c5fd69af53 100644 --- a/tools/discord_tool.py +++ b/tools/discord_tool.py @@ -631,3 +631,16 @@ for _name, _actions, _handler in ( handler=lambda args, _h=_handler, **kw: _h(**{"action": "", **args}), check_fn=check_discord_tool_requirements, requires_env=["DISCORD_BOT_TOKEN"]) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import TYPE_CHECKING # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 + +def get_dynamic_schema() -> Optional[Dict[str, Any]]: + """Backward-compat wrapper — returns core schema.""" + return get_dynamic_schema_core() +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/drive_preview_tool.py b/tools/drive_preview_tool.py index 66cef662fe..436bb8febd 100644 --- a/tools/drive_preview_tool.py +++ b/tools/drive_preview_tool.py @@ -140,3 +140,11 @@ registry.register( **{k: args.get(k) for k in ("ref", "selector", "text", "key", "submit", "amount", "to", "full")}, ), emoji="🖱️") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/env_probe.py b/tools/env_probe.py index 75f7205833..6805eb8078 100644 --- a/tools/env_probe.py +++ b/tools/env_probe.py @@ -245,3 +245,11 @@ def _reset_cache_for_tests() -> None: _PROBE_THREAD = None _PROBE_GEN += 1 _WAIT_ALREADY_TIMED_OUT = False + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import os # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/environments/base.py b/tools/environments/base.py index fbb2a05ad1..014bfd1131 100644 --- a/tools/environments/base.py +++ b/tools/environments/base.py @@ -522,3 +522,31 @@ class BaseEnvironment(ABC): """Transform sudo commands if SUDO_PASSWORD is available.""" from tools.terminal_tool_sudo import _transform_sudo_command return _transform_sudo_command(command) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import IO # noqa: F401,E402 +from typing import Protocol # noqa: F401,E402 +import codecs # noqa: F401,E402 +from collections import deque # noqa: F401,E402 +import re # noqa: F401,E402 +import select # noqa: F401,E402 +import subprocess # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'sanitize_task_id_for_path': ('tools.environments.path_utils', 'sanitize_task_id_for_path'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/environments/managed_modal.py b/tools/environments/managed_modal.py index 0fd65dbf53..66936ea097 100644 --- a/tools/environments/managed_modal.py +++ b/tools/environments/managed_modal.py @@ -203,3 +203,11 @@ class ManagedModalEnvironment(BaseEnvironment): pass text = response.text.strip() return f"{prefix}: {text}" if text else f"{prefix}: HTTP {response.status_code}" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import dataclass # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/environments/modal_utils.py b/tools/environments/modal_utils.py new file mode 100644 index 0000000000..771d4d75ee --- /dev/null +++ b/tools/environments/modal_utils.py @@ -0,0 +1,6 @@ +"""PLUGIN-COMPAT stub (revert-scheduled; see COMPAT_MANIFEST.md). + +``tools.environments.modal_utils`` was deleted in the Sep 2026 decomposition (its callers were folded into the +execution backends). Importing it no longer provides anything; this stub exists only so an external +plugin's ``import tools.environments.modal_utils`` does not raise at import time. +""" diff --git a/tools/environments/vercel_sandbox.py b/tools/environments/vercel_sandbox.py index 41c492fa52..fc3d3fe2a2 100644 --- a/tools/environments/vercel_sandbox.py +++ b/tools/environments/vercel_sandbox.py @@ -333,3 +333,11 @@ class VercelSandboxEnvironment(BaseEnvironment): # Always stop the sandbox during cleanup to avoid resource leaks (matches Modal/Daytona). self._stop_sandbox(sandbox) self._close_sandbox_client(sandbox) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import dataclass # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/feishu_doc_tool.py b/tools/feishu_doc_tool.py index 5e0694b949..958ecb3874 100644 --- a/tools/feishu_doc_tool.py +++ b/tools/feishu_doc_tool.py @@ -68,3 +68,26 @@ registry.register( name="feishu_doc_read", toolset="feishu_doc", schema=FEISHU_DOC_READ_SCHEMA, handler=_handle_feishu_doc_read, check_fn=_check_feishu, requires_env=[], is_async=False, description="Read Feishu document content", emoji="\U0001f4c4") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import logging # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('tools.approval', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/feishu_drive_tool.py b/tools/feishu_drive_tool.py index e1ba075d54..0ae4da6d6f 100644 --- a/tools/feishu_drive_tool.py +++ b/tools/feishu_drive_tool.py @@ -192,3 +192,12 @@ for _schema, _handler, _desc, _emoji in ( registry.register( name=_schema["name"], toolset="feishu_drive", schema=_schema, handler=_handler, check_fn=_check_feishu, requires_env=[], is_async=False, description=_desc, emoji=_emoji) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +import threading # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/file_operations.py b/tools/file_operations.py index b057cd2911..3fb02295c4 100644 --- a/tools/file_operations.py +++ b/tools/file_operations.py @@ -1374,3 +1374,51 @@ class ShellFileOperations(LintMixin, SearchMixin, FileOperations): f"an unattended privacy prompt: {skipped}. Search a protected " "folder directly when access is intentional.") return result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from typing import ClassVar # noqa: F401,E402 +from typing import List # noqa: F401,E402 +from agent.file_safety import build_write_denied_paths # noqa: F401,E402 +from agent.file_safety import build_write_denied_prefixes # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import posixpath # noqa: F401,E402 +import threading # noqa: F401,E402 + +MAX_LINES = 2000 + +MAX_LINE_LENGTH = 2000 + +WRITE_DENIED_PATHS = build_write_denied_paths(_HOME) + +WRITE_DENIED_PREFIXES = build_write_denied_prefixes(_HOME) + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_READ_LIMIT': ('tools.file_operations_common', 'DEFAULT_READ_LIMIT'), + 'DEFAULT_READ_OFFSET': ('tools.file_operations_common', 'DEFAULT_READ_OFFSET'), + 'DEFAULT_SEARCH_LIMIT': ('tools.file_operations_common', 'DEFAULT_SEARCH_LIMIT'), + 'DEFAULT_SEARCH_OFFSET': ('tools.file_operations_common', 'DEFAULT_SEARCH_OFFSET'), + 'LINTERS': ('tools.file_operations_lint', 'LINTERS'), + 'LintResult': ('tools.file_operations_common', 'LintResult'), + 'MAX_FILE_SIZE': ('tools.transcription_common', 'MAX_FILE_SIZE'), + 'SEARCH_PRUNE_DIR_NAMES': ('agent.search_policy', 'SEARCH_PRUNE_DIR_NAMES'), + 'SearchMatch': ('tools.file_operations_common', 'SearchMatch'), + 'build_write_denied_paths': ('agent.file_safety', 'build_write_denied_paths'), + 'build_write_denied_prefixes': ('agent.file_safety', 'build_write_denied_prefixes'), + 'tool_interrupt': ('tools', 'interrupt'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/file_tools.py b/tools/file_tools.py index c2f2624d3b..6264044c8b 100644 --- a/tools/file_tools.py +++ b/tools/file_tools.py @@ -1223,3 +1223,29 @@ def _patch_schema_overrides(): registry.register(name="patch", toolset="file", schema=PATCH_SCHEMA, handler=_handle_patch, check_fn=_check_file_reqs, emoji="🔧", max_result_size_chars=100_000, dynamic_schema_overrides=_patch_schema_overrides) registry.register(name="search_files", toolset="file", schema=SEARCH_FILES_SCHEMA, handler=_handle_search_files, check_fn=_check_file_reqs, emoji="🔎", max_result_size_chars=100_000) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import PurePosixPath # noqa: F401,E402 +import posixpath # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'has_opaque_document_extension': ('tools.binary_extensions', 'has_opaque_document_extension'), + 'is_pdf_path': ('tools.binary_extensions', 'is_pdf_path'), + 'notify_other_tool_call': ('tools.file_tools_read_tracking', 'notify_other_tool_call'), + 'reset_file_dedup': ('tools.file_tools_read_tracking', 'reset_file_dedup'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/focus_pane_tool.py b/tools/focus_pane_tool.py index 2ff5477aa8..696681afb5 100644 --- a/tools/focus_pane_tool.py +++ b/tools/focus_pane_tool.py @@ -46,3 +46,31 @@ registry.register( handler=lambda args, **kw: focus_pane_tool(pane=args.get("pane", "")), emoji="🪟", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +FOCUS_PANE_SCHEMA = { + "name": "focus_pane", + "description": ( + "Reveal and focus a Hermes desktop pane when the user asks to see it: " + "chat, files, terminal, review (git diff), or sessions. For URLs/" + "files use the desktop_preview tool instead." + ), + "parameters": { + "type": "object", + "properties": { + "pane": { + "type": "string", + "enum": list(PANES), + "description": "Which pane to reveal.", + }, + }, + "required": ["pane"], + }, +} +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/fuzzy_match.py b/tools/fuzzy_match.py index ab9491e36f..5f08358b98 100644 --- a/tools/fuzzy_match.py +++ b/tools/fuzzy_match.py @@ -585,3 +585,12 @@ def format_no_match_hint(error: Optional[str], match_count: int, return "" hint = find_closest_lines(old_string, content) return "\n\nDid you mean one of these sections?\n" + hint if hint else "" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/image_generation_tool.py b/tools/image_generation_tool.py index 49ff3146e2..853f2e44f9 100644 --- a/tools/image_generation_tool.py +++ b/tools/image_generation_tool.py @@ -857,3 +857,14 @@ registry.register( is_async=False, # sync fal_client API to avoid "Event loop is closed" in gateway emoji="🎨", dynamic_schema_overrides=_build_dynamic_image_schema, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def is_krea_model(model_id: Optional[str]) -> bool: + """True when ``model_id`` is a native Krea plugin id (``krea-2-*``).""" + return _normalize_krea_model(model_id) is not None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/lazy_deps.py b/tools/lazy_deps.py index 234d61c79f..ffccf19761 100644 --- a/tools/lazy_deps.py +++ b/tools/lazy_deps.py @@ -726,3 +726,16 @@ def ensure_and_bind(feature: str, importer: Callable[[], dict[str, Any]], target logger.warning("Failed to import feature %r after install: %s", feature, exc) return False return True + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def feature_specs(feature: str) -> tuple[str, ...]: + """Return the registered specs for a feature, or raise KeyError.""" + if feature not in LAZY_DEPS: + raise KeyError(f"Unknown lazy feature: {feature!r}") + return LAZY_DEPS[feature] +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/managed_tool_gateway.py b/tools/managed_tool_gateway.py index b53a25f935..a7504d1d4c 100644 --- a/tools/managed_tool_gateway.py +++ b/tools/managed_tool_gateway.py @@ -140,3 +140,208 @@ def is_managed_tool_gateway_ready( :func:`peek_nous_access_token` (no OAuth refresh); callers about to make a real request use :func:`resolve_managed_tool_gateway` instead.""" return resolve_managed_tool_gateway(vendor, gateway_builder=gateway_builder, token_reader=token_reader or peek_nous_access_token) is not None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from urllib.parse import urlsplit # noqa: F401,E402 +from urllib.parse import urlsplit # noqa: F401,E402 + +_MANAGED_GATEWAY_VENDOR = "tool" + +def is_managed_nous_gateway_url( + url: object, + gateway_builder: Optional[Callable[[str], str]] = None, +) -> bool: + """True when ``url`` is on the Nous tool-gateway origin this client builds. + + Anything granting a URL extra trust — our bearer, reading files off disk to + upload — must gate on this rather than on a name, so an arbitrary URL can + never inherit that trust. + """ + if not isinstance(url, str) or not url.strip(): + return False + + builder = gateway_builder or build_vendor_gateway_url + try: + expected = urlsplit(builder(_MANAGED_GATEWAY_VENDOR)) + actual = urlsplit(url.strip()) + except ValueError: + return False + + return bool(actual.scheme) and (actual.scheme, actual.netloc) == (expected.scheme, expected.netloc) + +def managed_gateway_auth_headers( + url: object, + gateway_builder: Optional[Callable[[str], str]] = None, + token_reader: Optional[Callable[[], Optional[str]]] = None, +) -> dict: + """Live auth headers for a managed gateway URL, or ``{}`` when not managed. + + Read fresh on every call rather than cached: a Nous access token expires + within the hour, and a long session would otherwise keep presenting a dead + bearer. Returns ``{}`` rather than raising when no token is available, so a + caller can report "sign in" instead of sending an unauthenticated request. + """ + if not is_managed_nous_gateway_url(url, gateway_builder): + return {} + + resolved_token_reader = token_reader or read_nous_access_token + try: + token = resolved_token_reader() + except Exception as exc: # pragma: no cover — defensive + logger.debug("Managed gateway token read failed for %s: %s", url, exc) + return {} + if not isinstance(token, str) or not token.strip(): + return {} + + return {"Authorization": f"Bearer {token.strip()}"} + +_MEDIA_UPLOAD_PRESIGN_TIMEOUT_SECONDS = 15.0 + +_MEDIA_UPLOAD_PUT_READ_TIMEOUT_SECONDS = 60.0 + +_MEDIA_UPLOAD_PUT_WRITE_TIMEOUT_SECONDS = 300.0 + +def _describe_media_upload_refusal(response) -> str: + """A model-actionable reason from a gateway refusal, or a generic one. + + The gateway's 4xx bodies carry deliberate guidance (rate-limit waits, size + caps, "you could not submit anyway"), so surface `error.message` verbatim + rather than a bare status code. + """ + try: + payload = response.json() + message = payload.get("error", {}).get("message") + if isinstance(message, str) and message.strip(): + return message.strip() + except Exception: + pass + return f"the gateway refused the upload (HTTP {response.status_code})" + +def build_managed_media_uploader( + server_url: object, + upload_path: object, + gateway_builder: Optional[Callable[[str], str]] = None, + token_reader: Optional[Callable[[], Optional[str]]] = None, +) -> Optional[Callable]: + """Async ``(data, mime) -> argument value`` uploader for one managed vendor. + + Returns ``None`` when there is no usable upload endpoint (not a managed + Nous URL, or no ``upload_path``); callers then refuse local paths with a + clear message instead of silently forwarding them. + + The three steps of the protocol: + + 1. POST ``origin + upload_path`` with the declared content type and exact + byte length, using the same live auth headers as the vendor calls. + The gateway answers with a presigned single-object PUT URL (short + expiry; type and length are signed into it) and an upload token. + 2. PUT the bytes to that URL. This goes directly to storage — never + through the gateway — which is what removes the request-size ceiling. + 3. Return ``nous-upload:`` for the tool argument. The token is + bound to this Nous principal and is redeemable only through the + gateway, so it is inert anywhere else it might end up. + """ + if not is_managed_nous_gateway_url(server_url, gateway_builder): + return None + if not isinstance(upload_path, str) or not upload_path.startswith("/"): + return None + + parts = urlsplit(str(server_url).strip()) + origin = f"{parts.scheme}://{parts.netloc}" + presign_url = f"{origin}{upload_path}" + + async def upload(data: bytes, mime: str) -> str: + import httpx + + from tools.url_safety import create_ssrf_safe_async_client + + headers = managed_gateway_auth_headers(server_url, gateway_builder, token_reader) + if not headers: + raise RuntimeError("no Nous credential is available for the upload") + + # Two clients on purpose, split by whose address we are trusting. + # + # The presign POST goes to `presign_url`, which is entirely determined + # by the managed gateway origin (already validated by + # is_managed_nous_gateway_url) plus the pinned upload_path — the same + # first-party host the vendor calls go to freely. SSRF-guarding it + # protects against nothing and would reject a local gateway on + # 127.0.0.1, so it uses a plain client. The PUT target, by contrast, is + # a URL the gateway *returned*, so it keeps the SSRF-safe client as + # defense in depth (real presigned URLs are public R2, which it allows). + presign_timeout = httpx.Timeout(_MEDIA_UPLOAD_PRESIGN_TIMEOUT_SECONDS) + async with httpx.AsyncClient(timeout=presign_timeout) as client: + presign = await client.post( + presign_url, + headers=headers, + json={"contentType": mime, "contentLength": len(data)}, + ) + if presign.status_code != 200: + raise RuntimeError(_describe_media_upload_refusal(presign)) + + try: + payload = presign.json() + except Exception: + payload = None + upload_url = payload.get("uploadUrl") if isinstance(payload, dict) else None + token = payload.get("token") if isinstance(payload, dict) else None + if not (isinstance(upload_url, str) and upload_url and isinstance(token, str) and token): + raise RuntimeError("the gateway's upload response was malformed") + + put_timeout = httpx.Timeout( + _MEDIA_UPLOAD_PRESIGN_TIMEOUT_SECONDS, + read=_MEDIA_UPLOAD_PUT_READ_TIMEOUT_SECONDS, + write=_MEDIA_UPLOAD_PUT_WRITE_TIMEOUT_SECONDS, + ) + async with create_ssrf_safe_async_client(timeout=put_timeout) as client: + # The presigned URL signs the exact Content-Type and Content-Length, + # so this PUT must send precisely what was declared above. + put = await client.put(upload_url, content=data, headers={"Content-Type": mime}) + if put.status_code != 200: + raise RuntimeError(f"storage refused the upload (HTTP {put.status_code})") + + return f"nous-upload:{token}" + + return upload + +def managed_vendor_base_path(vendor: str) -> str: + """Base path for a managed vendor's REST routes on the gateway host.""" + return f"/api/{vendor}" + +def managed_vendor_upload_path(vendor: str) -> str: + """Media upload endpoint for a managed vendor, on the same host.""" + return f"/api/uploads/{vendor}" + +def managed_vendor_endpoints( + vendor: str, + gateway_builder: Optional[Callable[[str], str]] = None, +) -> Optional[dict]: + """Absolute URLs for a managed vendor, or ``None`` when none resolves. + + Address resolution only: entitlement is deliberately not consulted here. + What an account may spend on a managed vendor is the gateway's own + decision, stated in its refusals, and re-deciding it on the client can only + ever disagree with the server. A caller that wants to hide its tools from + users who could not call them at all does that in its ``check_fn``. + + ``None`` means no origin could be resolved — a misconfigured + ``TOOL_GATEWAY_SCHEME`` — so there is nothing to call. + """ + builder = gateway_builder or build_vendor_gateway_url + try: + origin = builder(_MANAGED_GATEWAY_VENDOR).rstrip("/") + except ValueError: + return None + if not origin: + return None + + return { + "origin": origin, + "base_url": f"{origin}{managed_vendor_base_path(vendor)}", + "upload_path": managed_vendor_upload_path(vendor), + } +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/mcp_oauth.py b/tools/mcp_oauth.py index dbd8917de3..99ccb081a1 100644 --- a/tools/mcp_oauth.py +++ b/tools/mcp_oauth.py @@ -986,3 +986,21 @@ def build_oauth_auth(server_name: str, server_url: str, oauth_config: dict | Non "__doc__": "SDK provider plus Hermes' token-endpoint fixes (see ``HermesProviderMixin``).", "__module__": __name__, "_hermes_logger": logger}) return HermesOAuthClientProvider(server_url=server_url, **kwargs) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from contextlib import contextmanager # noqa: F401,E402 + +OAuthClientInformationFull: Any = None + +OAuthClientMetadata: Any = None + +OAuthClientProvider: Any = None + +OAuthMetadata: Any = None + +OAuthToken: Any = None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/mcp_schema_cache.py b/tools/mcp_schema_cache.py index ba22dcb177..98e150edb1 100644 --- a/tools/mcp_schema_cache.py +++ b/tools/mcp_schema_cache.py @@ -107,3 +107,20 @@ def tools_from_cache_entry(entry: dict) -> List[dict]: def utility_tools_from_cache_entry(entry: dict) -> List[dict]: """Return cached ``{schema, handler_key}`` utility rows.""" return _list_field(entry, "utility_tools") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def clear_cache_entry(server_name: str) -> None: + with _cache_lock: + data = _load_all() + if server_name in data: + del data[server_name] + _save_all(data) + +def has_cached_entry(server_name: str, fingerprint: str) -> bool: + return get_cached_entry(server_name, fingerprint) is not None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index 2e0c3c2551..a67cc5a449 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -618,3 +618,59 @@ _LOCK_UNAVAILABLE: Any = object() # sentinel: locking broken/unavailable _MCP_DISCOVERY_LOCK_PATH: Optional[str] = None # resolved lazily # Bounded wait when another process holds the lock. _MCP_DISCOVERY_LOCK_MAX_RETRIES, _MCP_DISCOVERY_LOCK_RETRY_DELAY_S = 240, 0.5 + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Coroutine # noqa: F401,E402 +from types import SimpleNamespace # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +from contextlib import asynccontextmanager # noqa: F401,E402 +import concurrent.futures # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +import errno # noqa: F401,E402 +import fnmatch # noqa: F401,E402 +import json # noqa: F401,E402 +import math # noqa: F401,E402 +import random # noqa: F401,E402 +import re # noqa: F401,E402 +import shutil # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'InvalidMcpUrlError': ('tools.mcp_tool_errors', 'InvalidMcpUrlError'), + 'MCP_TOOL_NAME_PREFIX': ('tools.mcp_tool_schema', 'MCP_TOOL_NAME_PREFIX'), + 'NonMcpEndpointError': ('tools.mcp_tool_errors', 'NonMcpEndpointError'), + 'discover_mcp_tools': ('tools.mcp_tool_discovery', 'discover_mcp_tools'), + 'get_mcp_status': ('tools.mcp_tool_discovery', 'get_mcp_status'), + 'get_registered_mcp_server_names': ('tools.mcp_tool_discovery', 'get_registered_mcp_server_names'), + 'has_registered_mcp_tools': ('tools.mcp_tool_discovery', 'has_registered_mcp_tools'), + 'is_mcp_tool_parallel_safe': ('tools.mcp_tool_discovery', 'is_mcp_tool_parallel_safe'), + 'matches_name_filter': ('tools.mcp_tool_schema', 'matches_name_filter'), + 'mcp_prefixed_tool_name': ('tools.mcp_tool_schema', 'mcp_prefixed_tool_name'), + 'persist_agent_tool_names': ('tools.mcp_tool_agent', 'persist_agent_tool_names'), + 'probe_mcp_server_tools': ('tools.mcp_tool_discovery', 'probe_mcp_server_tools'), + 'reconnect_mcp_server': ('tools.mcp_tool_loop', 'reconnect_mcp_server'), + 'refresh_agent_mcp_tools': ('tools.mcp_tool_agent', 'refresh_agent_mcp_tools'), + 'register_mcp_servers': ('tools.mcp_tool_discovery', 'register_mcp_servers'), + 'reprobe_tool_availability': ('tools.mcp_tool_agent', 'reprobe_tool_availability'), + 'restore_agent_tool_prefix': ('tools.mcp_tool_agent', 'restore_agent_tool_prefix'), + 'sanitize_mcp_name_component': ('tools.mcp_tool_schema', 'sanitize_mcp_name_component'), + 'shutdown_mcp_servers': ('tools.mcp_tool_lifecycle', 'shutdown_mcp_servers'), + 'strip_unicode_tags': ('tools.ansi_strip', 'strip_unicode_tags'), + 'tool_error': ('tools.registry', 'tool_error'), +} + +_plugin_compat_prev_getattr = __getattr__ + + +def __getattr__(name): # PEP 562 — chained onto the module's own __getattr__ + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + return _plugin_compat_prev_getattr(name) + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/memory_tool.py b/tools/memory_tool.py index 8e42f22e57..79a56efcb6 100644 --- a/tools/memory_tool.py +++ b/tools/memory_tool.py @@ -324,3 +324,25 @@ registry.register( check_fn=check_memory_requirements, emoji="🧠", dynamic_schema_overrides=_build_memory_schema_overrides) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from contextlib import contextmanager # noqa: F401,E402 +import time # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_write_text': ('utils', 'atomic_write_text'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/microsoft_graph_client.py b/tools/microsoft_graph_client.py index faf29d7acb..6a7921097b 100644 --- a/tools/microsoft_graph_client.py +++ b/tools/microsoft_graph_client.py @@ -180,3 +180,24 @@ class MicrosoftGraphClient: return MicrosoftGraphAPIError( response.status_code, method, url, response.text.strip() or "unknown error" if detail is None else detail, retry_after_seconds=parse_retry_after_seconds(response.headers), payload=payload) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import AsyncIterator # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'GraphCredentials': ('tools.microsoft_graph_auth', 'GraphCredentials'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/open_preview_tool.py b/tools/open_preview_tool.py index 009c194313..7c0b9b760c 100644 --- a/tools/open_preview_tool.py +++ b/tools/open_preview_tool.py @@ -42,3 +42,54 @@ def open_preview_tool(url: str, label: str = "") -> str: "Failed to open the preview pane: ", "The preview pane is only available in the Hermes desktop app.", {"success": True, "url": target, "label": label}) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +OPEN_PREVIEW_SCHEMA = { + "name": "open_preview", + "description": ( + "Open something in the preview pane beside the chat in the Hermes desktop " + "app. Use this when the user asks to see a page, dev server, or file in the " + "preview pane — e.g. \"open cnn.com in the preview pane\" or \"preview " + "localhost:3000\". Accepts a web URL (a bare domain like www.cnn.com is fine), " + "a localhost dev-server URL, or a file path (HTML renders live; other files " + "show their contents). The pane opens for the current window only. To close " + "the pane or a tab, use close_preview." + ), + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": ( + "What to preview: a web URL (https://… or a bare domain), a " + "localhost URL (localhost:3000), or a file path." + ), + }, + "label": { + "type": "string", + "description": "Optional tab label; defaults to the target's name.", + }, + }, + "required": ["url"], + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'registry': ('tools.registry', 'registry'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/openrouter_client.py b/tools/openrouter_client.py index 262b4d73c7..9037f3ab0d 100644 --- a/tools/openrouter_client.py +++ b/tools/openrouter_client.py @@ -18,3 +18,26 @@ def check_api_key() -> bool: except Exception: pass return bool(os.getenv("OPENROUTER_API_KEY")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def get_async_client(): + """Return a shared async OpenAI-compatible client for OpenRouter. + + The client is created lazily on first call and reused thereafter. + Uses the centralized provider router for auth and client construction. + Raises ValueError if OPENROUTER_API_KEY is not set. + """ + global _client + if _client is None: + from agent.auxiliary_client import resolve_provider_client + client, _model = resolve_provider_client("openrouter", async_mode=True) + if client is None: + raise ValueError("OPENROUTER_API_KEY environment variable not set") + _client = client + return _client +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/path_security.py b/tools/path_security.py index 222df08fd0..8fee6dd694 100644 --- a/tools/path_security.py +++ b/tools/path_security.py @@ -16,3 +16,24 @@ def validate_within_dir(path: Path, root: Path) -> Optional[str]: def has_traversal_component(path_str: str) -> bool: """Cheap pre-check for a literal ``..`` component before full resolution.""" return ".." in Path(path_str).parts + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import logging # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'logger': ('tools.approval', 'logger'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/preview_tool.py b/tools/preview_tool.py index cba89ff8a5..a5271a7e04 100644 --- a/tools/preview_tool.py +++ b/tools/preview_tool.py @@ -76,3 +76,14 @@ registry.register( handler=_handle_preview, emoji="🖼️", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +def preview_open(url: str, label: str = "") -> str: + return open_preview_tool(url=url, label=label) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/process_registry.py b/tools/process_registry.py index 50ec32044e..75ae003b84 100644 --- a/tools/process_registry.py +++ b/tools/process_registry.py @@ -2089,3 +2089,12 @@ registry.register( handler=_handle_process, emoji="⚙️", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +MAX_ACTIVE_PROCESS_AGE = 86400 # 24h default — see session_reset.bg_process_max_age_hours (#29177) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/react_to_message_tool.py b/tools/react_to_message_tool.py index 0308119ba0..d4c21d638c 100644 --- a/tools/react_to_message_tool.py +++ b/tools/react_to_message_tool.py @@ -116,3 +116,23 @@ registry.register( messages_back=args.get("messages_back")), check_fn=check_react_requirements, emoji="💛", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'env_var_enabled': ('utils', 'env_var_enabled'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/read_extract.py b/tools/read_extract.py index b6ba1ab5fa..0ae70c8ab9 100644 --- a/tools/read_extract.py +++ b/tools/read_extract.py @@ -538,3 +538,12 @@ def _cell_value(cell: ET.Element, shared: list[str], s: str) -> str: # Extension -> stdlib extractor; anydoc formats fall through in extract_document_text. _STDLIB_EXTRACTORS: dict[str, Callable[[str], str]] = { ".ipynb": _extract_notebook, ".docx": _extract_docx, ".xlsx": _extract_xlsx} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +MAX_XLSX_BYTES = 50 * 1024 * 1024 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/read_preview_tool.py b/tools/read_preview_tool.py index 68e3f63b93..1a491d1325 100644 --- a/tools/read_preview_tool.py +++ b/tools/read_preview_tool.py @@ -21,3 +21,54 @@ def read_preview_tool( "start and count must be integers.", "Failed to read the preview pane: ", "No preview tab is open, or the read timed out.")) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + +READ_PREVIEW_SCHEMA = { + "name": "read_preview", + "description": ( + "Read what's currently shown in the in-app browser / preview pane of the " + "Hermes desktop GUI (the pane open_preview opens beside this chat). Call " + "with no arguments for the first window of the active tab's content. " + "Returns JSON {kind, url, title, text, start, end, total_chars, note?}: " + "a URL (Browser) tab's text is the rendered page's visible text — page " + "through longer pages with `start`/`count` (character offsets, capped " + "per read); a file tab answers identity only (read the file with " + "read_file); an artifact tab points back at the conversation. Use after " + "open_preview, or whenever the user refers to what's on screen in the " + "browser ('what does this page say?'). To close the pane, use close_preview." + ), + "parameters": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "description": "0-indexed character offset into the page text. Omit for the start.", + }, + "count": { + "type": "integer", + "description": "Characters to return from start. Defaults to (and is capped at) the per-read maximum.", + }, + }, + }, +} + + +_PLUGIN_COMPAT_LAZY = { + 'registry': ('tools.registry', 'registry'), + 'tool_error': ('tools.registry', 'tool_error'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/read_terminal_tool.py b/tools/read_terminal_tool.py index 41087a907d..16de488479 100644 --- a/tools/read_terminal_tool.py +++ b/tools/read_terminal_tool.py @@ -79,3 +79,11 @@ registry.register( ), emoji="🖥️", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/read_window_tool.py b/tools/read_window_tool.py index b41ab6db06..cae4f8cf95 100644 --- a/tools/read_window_tool.py +++ b/tools/read_window_tool.py @@ -46,3 +46,24 @@ registry.register( handler=lambda args, **kw: read_window_below_tool(callback=kw.get("callback")), emoji="🪟", ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import json # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'tool_error': ('tools.registry', 'tool_error'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index 0a1b08ce2d..f62e773a7c 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -525,3 +525,64 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, and not last_result.get("media_delivered")): last_result["warnings"] = [*last_result.get("warnings", []), warning] return last_result + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import re # noqa: F401,E402 +import time # noqa: F401,E402 + +SEND_MESSAGE_SCHEMA = { + "name": "send_message", + "description": ( + "Send a message to a connected messaging platform, or list available targets.\n\n" + "IMPORTANT: When the user asks to send to a specific channel or person " + "(not just a bare platform name), call send_message(action='list') FIRST to see " + "available targets, then send to the correct one.\n" + "If the user just says a platform name like 'send to telegram', send directly " + "to the home channel without listing first." + ), + "parameters": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["send", "list", "react", "unreact"], + "description": "Action to perform. 'send' (default) sends a message. 'list' returns all available channels/contacts across connected platforms. 'react' attaches an emoji reaction to a message (platforms that support it, e.g. photon/iMessage tapbacks). 'unreact' retracts a previously-added reaction." + }, + "target": { + "type": "string", + "description": "Delivery target. Format: 'platform' (uses home channel), 'platform:#channel-name', 'platform:chat_id', or 'platform:chat_id:thread_id' for Telegram topics and Discord threads. Examples: 'telegram', 'telegram:-1001234567890:17585', 'discord:999888777:555444333', 'discord:#bot-home', 'slack:#engineering', 'signal:+155****4567', 'matrix:!roomid:server.org', 'matrix:@user:server.org', 'ntfy:alerts-channel' (explicit ntfy topic), 'yuanbao:direct:' (DM), 'yuanbao:group:' (group chat)" + }, + "message": { + "type": "string", + "description": "The message text to send. To send an image or file, include MEDIA: (e.g. 'MEDIA:/tmp/report.pdf') in the message — the platform will deliver it as a native media attachment." + }, + "emoji": { + "type": "string", + "description": "For action='react': the emoji to react with (e.g. '❤️'). On iMessage, ❤️👍👎😂‼️❓ render as native tapbacks; other emoji use custom-emoji reactions." + }, + "message_id": { + "type": "string", + "description": "For action='react'/'unreact': id of the message to react to. Omit to target the most recent message received in that chat (usually the one being replied to)." + } + }, + "required": [] + } +} + + +_PLUGIN_COMPAT_LAZY = { + 'redact_sensitive_text': ('agent.redact', 'redact_sensitive_text'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skill_ledger.py b/tools/skill_ledger.py index 1c852abed4..cbe041ddff 100644 --- a/tools/skill_ledger.py +++ b/tools/skill_ledger.py @@ -400,3 +400,16 @@ def rollback_entry(entry_id: str) -> Tuple[bool, str]: f"rolled back entry {entry_id} ({entry.get('action')} on " f"'{entry.get('skill')}'): {restored} file(s) restored, {removed} removed. " f"Safety entry {safety_id} captured the pre-rollback state.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +ACTOR_AGENT = "agent" + +ACTOR_CURATOR = "curator" + +ACTOR_USER = "user" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skill_linter.py b/tools/skill_linter.py index 31cfb59232..e46de3806d 100644 --- a/tools/skill_linter.py +++ b/tools/skill_linter.py @@ -183,3 +183,17 @@ def lint_skill(skill_md_path: Path) -> List[LintFinding]: skill_md_path = Path(skill_md_path) content = skill_md_path.read_text(encoding="utf-8", errors="ignore") return lint_content(content, skill_dir=skill_md_path.parent) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def format_findings(findings: List[LintFinding]) -> str: + """Render findings as a newline-joined human-readable block.""" + return "\n".join(f.format() for f in findings) + +def has_errors(findings: List[LintFinding]) -> bool: + return any(f.severity == ERROR for f in findings) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skill_manager_tool.py b/tools/skill_manager_tool.py index 3463f4c330..70cffd3942 100644 --- a/tools/skill_manager_tool.py +++ b/tools/skill_manager_tool.py @@ -875,3 +875,23 @@ registry.register( name="skill_manage", toolset="skills", schema=SKILL_MANAGE_SCHEMA, emoji="📝", handler=lambda args, **kw: _skill_manage_from( args, task_id=kw.get("task_id"), session_id=kw.get("session_id"))) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'mark_background_review_skill_read': ('tools.skill_manager_guards', 'mark_background_review_skill_read'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skill_usage.py b/tools/skill_usage.py index ebf8128037..48d7edc9d3 100644 --- a/tools/skill_usage.py +++ b/tools/skill_usage.py @@ -677,3 +677,66 @@ def usage_report() -> List[Dict[str, Any]]: data = load_usage() return [_report_row(n, data.get(n), provenance=provenance(n), _persisted=n in data) for n in sorted({name for name, _md in _iter_skill_mds(base, local_only=False)})] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import tempfile # noqa: F401,E402 +import os # noqa: F401,E402 +import os # noqa: F401,E402 +import tempfile # noqa: F401,E402 + +def _suppressed_file() -> Path: + return _skills_dir() / ".curator_suppressed" + +def _write_suppressed_names(names: Set[str]) -> None: + path = _suppressed_file() + try: + path.parent.mkdir(parents=True, exist_ok=True) + data = "\n".join(sorted(names)) + ("\n" if names else "") + fd, tmp = tempfile.mkstemp(dir=str(path.parent), prefix=".curator_suppressed_", suffix=".tmp") + try: + with os.fdopen(fd, "w", encoding="utf-8") as f: + f.write(data) + f.flush() + os.fsync(f.fileno()) + os.replace(tmp, path) + except BaseException: + try: + os.unlink(tmp) + except OSError: + pass + raise + except Exception as e: + logger.debug("Failed to write curator suppression list: %s", e, exc_info=True) + +def add_suppressed_name(skill_name: str) -> None: + """Record that a built-in skill was pruned, so sync won't restore it.""" + if not skill_name: + return + names = read_suppressed_names() + if skill_name not in names: + names.add(skill_name) + _write_suppressed_names(names) + +def agent_created_report() -> List[Dict[str, Any]]: + """DEPRECATED — use :func:`curated_report` instead. + + Used to return everything :func:`curated_report` returns (including bundled + skills when ``curator.prune_builtins`` is enabled), which made the + "agent-created" name misleading. Kept as a compatibility alias for + external callers; new code should call ``curated_report()``. + """ + return curated_report() + +def remove_suppressed_name(skill_name: str) -> None: + """Clear a built-in's suppression entry (e.g. on restore).""" + if not skill_name: + return + names = read_suppressed_names() + if skill_name in names: + names.discard(skill_name) + _write_suppressed_names(names) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skillevaluator_scan.py b/tools/skillevaluator_scan.py index 73b20440f5..f48142c20a 100644 --- a/tools/skillevaluator_scan.py +++ b/tools/skillevaluator_scan.py @@ -139,3 +139,16 @@ def format_tier1_report(report: Tier1Report, limit: int = 10) -> str: if report.incomplete_checks: lines.append(f" (not run: {', '.join(report.incomplete_checks)} — no opinion from these checks)") return "\n".join(lines) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 + +SCANNER_NAME = "skillevaluator-tier1" + +def scanner_available() -> bool: + return shutil.which(SCANNER_BIN) is not None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skills_guard.py b/tools/skills_guard.py index c228978e20..275e742b01 100644 --- a/tools/skills_guard.py +++ b/tools/skills_guard.py @@ -633,3 +633,14 @@ def _build_summary(name: str, source: str, trust: str, verdict: str, findings: L if not findings: return f"{name}: clean scan, no threats detected" return f"{name}: {verdict} — {len(findings)} finding(s) in {', '.join(sorted({f.category for f in findings}))}" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def full_content_hash(skill_path: Path) -> str: + """Full canonical digest used to bind scanner attestations.""" + return f"sha256:{_content_digest(skill_path)}" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skills_hub.py b/tools/skills_hub.py index d0d9f5cd0c..65e8927a15 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -315,3 +315,73 @@ def ensure_hub_dirs() -> None: ): if not path.exists(): path.write_text(initial, encoding="utf-8") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from abc import ABC # noqa: F401,E402 +from pathlib import PurePosixPath # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +from typing import Union # noqa: F401,E402 +from abc import abstractmethod # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import os # noqa: F401,E402 +from urllib.parse import quote # noqa: F401,E402 +import re # noqa: F401,E402 +import shutil # noqa: F401,E402 +import subprocess # noqa: F401,E402 +from urllib.parse import unquote # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 +from urllib.parse import urlsplit # noqa: F401,E402 +from urllib.parse import urlunparse # noqa: F401,E402 +import yaml # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'BrowseShSource': ('tools.skills_hub_sources', 'BrowseShSource'), + 'ClawHubSource': ('tools.skills_hub_clawhub', 'ClawHubSource'), + 'GITHUB_TAP_PROVIDERS': ('tools.skills_hub_github', 'GITHUB_TAP_PROVIDERS'), + 'GitHubAuth': ('tools.skills_hub_github', 'GitHubAuth'), + 'GitHubSource': ('tools.skills_hub_github', 'GitHubSource'), + 'HERMES_INDEX_TTL': ('tools.skills_hub_search', 'HERMES_INDEX_TTL'), + 'HERMES_INDEX_URL': ('tools.skills_hub_search', 'HERMES_INDEX_URL'), + 'HermesIndexSource': ('tools.skills_hub_official', 'HermesIndexSource'), + 'LobeHubSource': ('tools.skills_hub_sources', 'LobeHubSource'), + 'OptionalSkillSource': ('tools.skills_hub_official', 'OptionalSkillSource'), + 'ScanResult': ('tools.skills_guard', 'ScanResult'), + 'SkillBundle': ('tools.skills_hub_models', 'SkillBundle'), + 'SkillMeta': ('tools.skills_hub_models', 'SkillMeta'), + 'SkillSource': ('tools.skills_hub_models', 'SkillSource'), + 'SkillsShSource': ('tools.skills_hub_skillssh', 'SkillsShSource'), + 'TRUSTED_REPOS': ('tools.skills_guard', 'TRUSTED_REPOS'), + 'UrlSource': ('tools.skills_hub_sources', 'UrlSource'), + 'WellKnownSkillSource': ('tools.skills_hub_sources', 'WellKnownSkillSource'), + 'bundle_content_hash': ('tools.skills_hub_install', 'bundle_content_hash'), + 'check_for_skill_updates': ('tools.skills_hub_install', 'check_for_skill_updates'), + 'content_hash': ('tools.skills_guard', 'content_hash'), + 'create_source_router': ('tools.skills_hub_search', 'create_source_router'), + 'github_provider_for': ('tools.skills_hub_github', 'github_provider_for'), + 'install_from_quarantine': ('tools.skills_hub_install', 'install_from_quarantine'), + 'is_excluded_skill_path': ('agent.skill_utils', 'is_excluded_skill_path'), + 'parallel_search_sources': ('tools.skills_hub_search', 'parallel_search_sources'), + 'quarantine_bundle': ('tools.skills_hub_install', 'quarantine_bundle'), + 'source_url_for_bundle': ('tools.skills_hub_models', 'source_url_for_bundle'), + 'unified_search': ('tools.skills_hub_search', 'unified_search'), + 'uninstall_skill': ('tools.skills_hub_install', 'uninstall_skill'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), +} + +_plugin_compat_prev_getattr = __getattr__ + + +def __getattr__(name): # PEP 562 — chained onto the module's own __getattr__ + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + return _plugin_compat_prev_getattr(name) + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skills_sync.py b/tools/skills_sync.py index ec6902a0e5..08861d6f87 100644 --- a/tools/skills_sync.py +++ b/tools/skills_sync.py @@ -433,3 +433,37 @@ if __name__ == "__main__": if backfilled := result.get("optional_provenance_backfilled"): parts.append(f"{len(backfilled)} official optional backfilled") print(f"\nDone: {', '.join(parts)}. {result['total_bundled']} total bundled.") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import PurePosixPath # noqa: F401,E402 +from datetime import datetime # noqa: F401,E402 +import json # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 + +def is_bundled_skills_opt_out() -> bool: + """Return True if the active profile carries the opt-out marker.""" + return (_hermes_home() / NO_BUNDLED_SKILLS_MARKER).exists() + + +_PLUGIN_COMPAT_LAZY = { + 'atomic_replace': ('utils', 'atomic_replace'), + 'diff_bundled_skill': ('tools.skills_sync_bundled_ops', 'diff_bundled_skill'), + 'list_user_modified_bundled_skills': ('tools.skills_sync_bundled_ops', 'list_user_modified_bundled_skills'), + 'remove_pristine_bundled_skills': ('tools.skills_sync_bundled_ops', 'remove_pristine_bundled_skills'), + 'reset_bundled_skill': ('tools.skills_sync_bundled_ops', 'reset_bundled_skill'), + 'restore_official_optional_skill': ('tools.skills_sync_optional', 'restore_official_optional_skill'), + 'set_bundled_skills_opt_out': ('tools.skills_sync_bundled_ops', 'set_bundled_skills_opt_out'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skills_sync_client.py b/tools/skills_sync_client.py index 20fe644ce3..d5a2ec291e 100644 --- a/tools/skills_sync_client.py +++ b/tools/skills_sync_client.py @@ -496,3 +496,71 @@ def sync_status() -> Dict[str, Any]: except Exception as e: logger.debug("skills_sync_client: sync_status org lookup failed: %s", e) return status + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from datetime import datetime # noqa: F401,E402 +import hashlib # noqa: F401,E402 +import time # noqa: F401,E402 +from datetime import timezone # noqa: F401,E402 + +KIND_COMMIT = "commit" + +KIND_TREE = "tree" + +MODE_DIR = "dir" + +MODE_EXEC = "exec" + +MODE_FILE = "file" + +SYNC_MANIFEST_TYPE = "sync-manifest" + +def dev_gate_open() -> bool: + """Whether the access gate permits sync. Never raises.""" + try: + return bool(resolve_identity().get("nous_admin")) + except SyncInertError: + return False + except Exception as e: + logger.debug("skills_sync_client: dev_gate_open check failed: %s", e) + return False + +def org_sync_available() -> bool: + """True iff this token can see the org-skill surface (multi-member org).""" + try: + resolve_org_identity() + return True + except Exception: + return False + +def user_conflict_ref(owner: str, n: int) -> str: + return f"refs/user/{owner}/conflict/{n}" + + +_PLUGIN_COMPAT_LAZY = { + 'ARTIFACT_TYPE_SKILL': ('tools.skills_sync_client_wire', 'ARTIFACT_TYPE_SKILL'), + 'SYNC_MANIFEST_ENTRY_NAME': ('tools.skills_sync_client_wire', 'SYNC_MANIFEST_ENTRY_NAME'), + 'SYNC_MANIFEST_VERSION': ('tools.skills_sync_client_wire', 'SYNC_MANIFEST_VERSION'), + 'WIRE_VERSION': ('tools.skills_sync_client_wire', 'WIRE_VERSION'), + 'canonical_json_bytes': ('tools.skills_sync_client_wire', 'canonical_json_bytes'), + 'maybe_pull_org_skills': ('tools.skills_sync_client_org', 'maybe_pull_org_skills'), + 'org_head_ref': ('tools.skills_sync_client_org', 'org_head_ref'), + 'org_skill_is_locally_modified': ('tools.skills_sync_client_org', 'org_skill_is_locally_modified'), + 'parse_sync_manifest': ('tools.skills_sync_client_wire', 'parse_sync_manifest'), + 'propose_skill': ('tools.skills_sync_client_org', 'propose_skill'), + 'pull_org_skills': ('tools.skills_sync_client_org', 'pull_org_skills'), + 'wire_address': ('tools.skills_sync_client_wire', 'wire_address'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/skills_tool.py b/tools/skills_tool.py index 431b02bf79..0e9563793b 100644 --- a/tools/skills_tool.py +++ b/tools/skills_tool.py @@ -662,3 +662,28 @@ def _skill_view_with_bump(args, **kw): registry.register( name="skill_view", toolset="skills", schema=SKILL_VIEW_SCHEMA, handler=_skill_view_with_bump, check_fn=check_skills_requirements, emoji="📚") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from enum import Enum # noqa: F401,E402 +from typing import Set # noqa: F401,E402 +import re # noqa: F401,E402 +import threading # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'display_hermes_home': ('hermes_constants', 'display_hermes_home'), + 'env_var_enabled': ('utils', 'env_var_enabled'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/slash_confirm.py b/tools/slash_confirm.py index a44b9d0dab..f3f3e78e17 100644 --- a/tools/slash_confirm.py +++ b/tools/slash_confirm.py @@ -87,3 +87,37 @@ async def resolve(session_key: str, confirm_id: str, choice: str, logger.error("Slash-confirm handler for /%s raised: %s", command, exc, exc_info=True) return f"❌ Error handling confirmation: {exc}" return result if isinstance(result, str) else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import asyncio # noqa: F401,E402 + +def resolve_sync_compat( + loop: asyncio.AbstractEventLoop, + session_key: str, + confirm_id: str, + choice: str, +) -> Optional[str]: + """Synchronous helper: schedule resolve() on a loop and wait for the result. + + Used by platform callback paths that run on a different thread than the + event loop (e.g. Discord's button click handler in some configurations). + Prefer the async ``resolve()`` from an async context. + """ + try: + from agent.async_utils import safe_schedule_threadsafe + fut = safe_schedule_threadsafe( + resolve(session_key, confirm_id, choice), loop, + logger=logger, + log_message="resolve_sync_compat scheduling failed", + ) + if fut is None: + return None + return fut.result(timeout=30) + except Exception as exc: + logger.error("resolve_sync_compat failed: %s", exc) + return None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/terminal_scope.py b/tools/terminal_scope.py index 6bef7819a9..64338b8055 100644 --- a/tools/terminal_scope.py +++ b/tools/terminal_scope.py @@ -159,3 +159,27 @@ def install_and_reset_profile_terminal_scope(hermes_home: "Any") -> Iterator[Non yield finally: reset_terminal_scope(token) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def install_refusal_scope(reason: str) -> Token: + """Install a refusal scope after :class:`TerminalPolicyUnavailable`. + + Terminal execution under this scope is rejected (fail closed) instead of + running under the launch process's ambient policy. + """ + return _terminal_scope_var.set(TerminalPolicyRefusal(reason)) + +@contextmanager +def terminal_scope(mapping: Optional[Dict[str, str]]) -> Iterator[None]: + """Context manager form of set/reset_terminal_scope.""" + token = set_terminal_scope(mapping) + try: + yield + finally: + reset_terminal_scope(token) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/terminal_tool.py b/tools/terminal_tool.py index 35b1b6d3ab..ed10535e5d 100644 --- a/tools/terminal_tool.py +++ b/tools/terminal_tool.py @@ -1296,3 +1296,41 @@ registry.register( emoji="💻", max_result_size_chars=100_000, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from pathlib import Path # noqa: F401,E402 +import importlib.util # noqa: F401,E402 +import platform # noqa: F401,E402 +import re # noqa: F401,E402 +import shlex # noqa: F401,E402 +import shutil # noqa: F401,E402 +import stat # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'cleanup_vm': ('tools.terminal_tool_lifecycle', 'cleanup_vm'), + 'env_var_enabled': ('utils', 'env_var_enabled'), + 'get_active_env': ('tools.terminal_tool_lifecycle', 'get_active_env'), + 'has_direct_modal_credentials': ('tools.tool_backend_helpers', 'has_direct_modal_credentials'), + 'is_interrupted': ('tools.interrupt', 'is_interrupted'), + 'is_managed_tool_gateway_ready': ('tools.managed_tool_gateway', 'is_managed_tool_gateway_ready'), + 'is_persistent_env': ('tools.terminal_tool_lifecycle', 'is_persistent_env'), + 'nous_tool_gateway_unavailable_message': ('tools.tool_backend_helpers', 'nous_tool_gateway_unavailable_message'), + 'resolve_modal_backend_state': ('tools.tool_backend_helpers', 'resolve_modal_backend_state'), + 'strip_inert_heredoc_bodies': ('tools.shell_heredoc', 'strip_inert_heredoc_bodies'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/tool_result_storage.py b/tools/tool_result_storage.py index c96619e6f2..216d843b5c 100644 --- a/tools/tool_result_storage.py +++ b/tools/tool_result_storage.py @@ -254,3 +254,13 @@ def enforce_turn_budget(tool_messages: list[dict], env=None, logger.info("Budget enforcement: persisted tool result %s (%d chars)", tool_use_id, size) return tool_messages + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import uuid # noqa: F401,E402 + +HEREDOC_MARKER = "HERMES_PERSIST_EOF" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/tool_search.py b/tools/tool_search.py index 04fb88b846..ab2af9b0e6 100644 --- a/tools/tool_search.py +++ b/tools/tool_search.py @@ -494,3 +494,48 @@ __all__ = [ "bridge_tool_schemas", "assemble_tool_defs", "is_bridge_tool", "dispatch_tool_search", "dispatch_tool_describe", "resolve_underlying_call", "scoped_deferrable_names", "validate_deferred_call_args"] + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Literal # noqa: F401,E402 +import copy # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import re # noqa: F401,E402 +import snowballstemmer # noqa: F401,E402 +import threading # noqa: F401,E402 + +def build_catalog_listing( + deferrable: List[Dict[str, Any]], + *, + max_tokens: int = 4000, +) -> Optional[str]: + """Render a skills-style manifest of the deferred catalog. + + One line per tool — ``name: short description`` — grouped under a + heading per source (MCP server / plugin toolset), exactly like the + bundled-skills listing in the system prompt: + + github tools: (44) + - create_issue: Open a new issue in a GitHub repository. + - merge_pull_request: Merge an open pull request. + ... + + Ordering is deterministic (groups and tools sorted by name) so the + rendered block is byte-stable across assemblies of the same catalog — + this keeps the request prefix cacheable across turns. + + Token-budget fallbacks (cheap chars/4 estimate, same rule as the + activation gate): + 1. full listing (names + short descriptions) + 2. names-only listing, still grouped + 3. server-level summary — one line per MCP server / plugin toolset + (name + tool count), so the model always knows WHICH domains are + reachable through the bridge even when per-tool names don't fit + 4. ``None`` — only when the summary itself exceeds the budget + """ + text, _form = build_catalog_listing_with_form(deferrable, max_tokens=max_tokens) + return text +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index 24657938f5..4ccac30161 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -552,3 +552,49 @@ def transcribe_audio_local_fallback(file_path: str, model: Optional[str] = None) if _has_local_command(): return _transcribe_local_command(file_path, _normalize_local_model(local_model)) return _error_result("No installed local STT backend is available.", provider="local") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import platform # noqa: F401,E402 +import queue # noqa: F401,E402 +import re # noqa: F401,E402 +import shlex # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import tempfile # noqa: F401,E402 +from urllib.parse import urljoin # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'COMMAND_STT_OUTPUT_FORMATS': ('tools.transcription_command', 'COMMAND_STT_OUTPUT_FORMATS'), + 'COMMON_LOCAL_BIN_DIRS': ('tools.transcription_common', 'COMMON_LOCAL_BIN_DIRS'), + 'DEFAULT_COMMAND_STT_LANGUAGE': ('tools.transcription_command', 'DEFAULT_COMMAND_STT_LANGUAGE'), + 'DEFAULT_COMMAND_STT_OUTPUT_FORMAT': ('tools.transcription_command', 'DEFAULT_COMMAND_STT_OUTPUT_FORMAT'), + 'DEFAULT_COMMAND_STT_TIMEOUT_SECONDS': ('tools.transcription_command', 'DEFAULT_COMMAND_STT_TIMEOUT_SECONDS'), + 'DEFAULT_LOCAL_STT_LANGUAGE': ('tools.transcription_common', 'DEFAULT_LOCAL_STT_LANGUAGE'), + 'ELEVENLABS_STT_BASE_URL': ('tools.transcription_common', 'ELEVENLABS_STT_BASE_URL'), + 'GROQ_BASE_URL': ('tools.transcription_common', 'GROQ_BASE_URL'), + 'GROQ_MODELS': ('tools.transcription_common', 'GROQ_MODELS'), + 'LOCAL_NATIVE_AUDIO_FORMATS': ('tools.transcription_common', 'LOCAL_NATIVE_AUDIO_FORMATS'), + 'MAX_FILE_SIZE': ('tools.transcription_common', 'MAX_FILE_SIZE'), + 'OPENAI_BASE_URL': ('tools.transcription_common', 'OPENAI_BASE_URL'), + 'OPENAI_MODELS': ('tools.transcription_common', 'OPENAI_MODELS'), + 'SUPPORTED_FORMATS': ('tools.transcription_common', 'SUPPORTED_FORMATS'), + 'XAI_STT_BASE_URL': ('tools.transcription_common', 'XAI_STT_BASE_URL'), + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), + 'nous_tool_gateway_unavailable_message': ('tools.tool_backend_helpers', 'nous_tool_gateway_unavailable_message'), + 'resolve_managed_tool_gateway': ('tools.managed_tool_gateway', 'resolve_managed_tool_gateway'), + 'resolve_openai_audio_api_key': ('tools.tool_backend_helpers', 'resolve_openai_audio_api_key'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/tts_tool.py b/tools/tts_tool.py index 3d2d189503..2c37e43596 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -573,3 +573,105 @@ registry.register( **{k: args.get(k) for k in ("output_path", "speed", "instructions", "provider")}), check_fn=check_tts_requirements, emoji="🔊") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from concurrent.futures import Future # noqa: F401,E402 +from typing import Iterator # noqa: F401,E402 +from concurrent.futures import ThreadPoolExecutor # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 +import base64 # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 +import platform # noqa: F401,E402 +import queue # noqa: F401,E402 +import re # noqa: F401,E402 +import shlex # noqa: F401,E402 +import shutil # noqa: F401,E402 +import subprocess # noqa: F401,E402 +import threading # noqa: F401,E402 +import time # noqa: F401,E402 +from urllib.parse import urljoin # noqa: F401,E402 +from urllib.parse import urlparse # noqa: F401,E402 +import uuid # noqa: F401,E402 + +GEMINI_TTS_CHANNELS = 1 + +GEMINI_TTS_SAMPLE_RATE = 24000 + +GEMINI_TTS_SAMPLE_WIDTH = 2 # 16-bit PCM (L16) + + +_PLUGIN_COMPAT_LAZY = { + 'AudioDeliveryProfile': ('tools.tts_tool_delivery', 'AudioDeliveryProfile'), + 'COMMAND_TTS_OUTPUT_FORMATS': ('tools.tts_command_provider', 'COMMAND_TTS_OUTPUT_FORMATS'), + 'DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTH': ('tools.tts_command_provider', 'DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTH'), + 'DEFAULT_COMMAND_TTS_OUTPUT_FORMAT': ('tools.tts_command_provider', 'DEFAULT_COMMAND_TTS_OUTPUT_FORMAT'), + 'DEFAULT_COMMAND_TTS_TIMEOUT_SECONDS': ('tools.tts_command_provider', 'DEFAULT_COMMAND_TTS_TIMEOUT_SECONDS'), + 'DEFAULT_DEEPINFRA_TTS_VOICE': ('tools.tts_tool_openai', 'DEFAULT_DEEPINFRA_TTS_VOICE'), + 'DEFAULT_EDGE_VOICE': ('tools.tts_tool_providers', 'DEFAULT_EDGE_VOICE'), + 'DEFAULT_ELEVENLABS_MODEL_ID': ('tools.tts_tool_providers', 'DEFAULT_ELEVENLABS_MODEL_ID'), + 'DEFAULT_ELEVENLABS_STREAMING_MODEL_ID': ('tools.tts_tool_providers', 'DEFAULT_ELEVENLABS_STREAMING_MODEL_ID'), + 'DEFAULT_ELEVENLABS_VOICE_ID': ('tools.tts_tool_providers', 'DEFAULT_ELEVENLABS_VOICE_ID'), + 'DEFAULT_GEMINI_AUDIO_TAGS': ('tools.tts_tool_providers', 'DEFAULT_GEMINI_AUDIO_TAGS'), + 'DEFAULT_GEMINI_TTS_BASE_URL': ('tools.tts_tool_providers', 'DEFAULT_GEMINI_TTS_BASE_URL'), + 'DEFAULT_GEMINI_TTS_MODEL': ('tools.tts_tool_providers', 'DEFAULT_GEMINI_TTS_MODEL'), + 'DEFAULT_GEMINI_TTS_VOICE': ('tools.tts_tool_providers', 'DEFAULT_GEMINI_TTS_VOICE'), + 'DEFAULT_KITTENTTS_MODEL': ('tools.tts_tool_local', 'DEFAULT_KITTENTTS_MODEL'), + 'DEFAULT_KITTENTTS_VOICE': ('tools.tts_tool_local', 'DEFAULT_KITTENTTS_VOICE'), + 'DEFAULT_MINIMAX_BASE_URL': ('tools.tts_tool_providers', 'DEFAULT_MINIMAX_BASE_URL'), + 'DEFAULT_MINIMAX_CN_BASE_URL': ('tools.tts_tool_providers', 'DEFAULT_MINIMAX_CN_BASE_URL'), + 'DEFAULT_MINIMAX_MODEL': ('tools.tts_tool_providers', 'DEFAULT_MINIMAX_MODEL'), + 'DEFAULT_MINIMAX_VOICE_ID': ('tools.tts_tool_providers', 'DEFAULT_MINIMAX_VOICE_ID'), + 'DEFAULT_MISTRAL_TTS_MODEL': ('tools.tts_tool_providers', 'DEFAULT_MISTRAL_TTS_MODEL'), + 'DEFAULT_MISTRAL_TTS_VOICE_ID': ('tools.tts_tool_providers', 'DEFAULT_MISTRAL_TTS_VOICE_ID'), + 'DEFAULT_OPENAI_BASE_URL': ('tools.tts_tool_openai', 'DEFAULT_OPENAI_BASE_URL'), + 'DEFAULT_OPENAI_MODEL': ('tools.tts_tool_openai', 'DEFAULT_OPENAI_MODEL'), + 'DEFAULT_OPENAI_VOICE': ('tools.tts_tool_openai', 'DEFAULT_OPENAI_VOICE'), + 'DEFAULT_PIPER_VOICE': ('tools.tts_tool_local', 'DEFAULT_PIPER_VOICE'), + 'DEFAULT_XAI_AUTO_SPEECH_TAGS': ('tools.tts_tool_providers', 'DEFAULT_XAI_AUTO_SPEECH_TAGS'), + 'DEFAULT_XAI_BASE_URL': ('tools.xai_http', 'DEFAULT_XAI_BASE_URL'), + 'DEFAULT_XAI_BIT_RATE': ('tools.tts_tool_providers', 'DEFAULT_XAI_BIT_RATE'), + 'DEFAULT_XAI_LANGUAGE': ('tools.tts_tool_providers', 'DEFAULT_XAI_LANGUAGE'), + 'DEFAULT_XAI_OPTIMIZE_STREAMING_LATENCY_DEFAULT': ('tools.tts_tool_providers', 'DEFAULT_XAI_OPTIMIZE_STREAMING_LATENCY_DEFAULT'), + 'DEFAULT_XAI_SAMPLE_RATE': ('tools.tts_tool_providers', 'DEFAULT_XAI_SAMPLE_RATE'), + 'DEFAULT_XAI_SPEED_DEFAULT': ('tools.tts_tool_providers', 'DEFAULT_XAI_SPEED_DEFAULT'), + 'DEFAULT_XAI_SPEED_MAX': ('tools.tts_tool_providers', 'DEFAULT_XAI_SPEED_MAX'), + 'DEFAULT_XAI_SPEED_MIN': ('tools.tts_tool_providers', 'DEFAULT_XAI_SPEED_MIN'), + 'DEFAULT_XAI_TEXT_NORMALIZATION_DEFAULT': ('tools.tts_tool_providers', 'DEFAULT_XAI_TEXT_NORMALIZATION_DEFAULT'), + 'DEFAULT_XAI_VOICE_ID': ('tools.tts_tool_providers', 'DEFAULT_XAI_VOICE_ID'), + 'ELEVENLABS_MODEL_MAX_TEXT_LENGTH': ('tools.tts_tool_delivery', 'ELEVENLABS_MODEL_MAX_TEXT_LENGTH'), + 'FALLBACK_MAX_TEXT_LENGTH': ('tools.tts_tool_delivery', 'FALLBACK_MAX_TEXT_LENGTH'), + 'GEMINI_AUDIO_TAG_REWRITE_TASK': ('tools.tts_tool_providers', 'GEMINI_AUDIO_TAG_REWRITE_TASK'), + 'MANAGED_OPENAI_TTS_MODELS': ('tools.tts_tool_openai', 'MANAGED_OPENAI_TTS_MODELS'), + 'MAX_TEXT_LENGTH': ('gateway.platforms.bluebubbles', 'MAX_TEXT_LENGTH'), + 'PROVIDER_MAX_TEXT_LENGTH': ('tools.tts_tool_delivery', 'PROVIDER_MAX_TEXT_LENGTH'), + 'TTS_RESPONSE_BODY_CHUNK_BYTES': ('tools.tts_tool_providers', 'TTS_RESPONSE_BODY_CHUNK_BYTES'), + 'TTS_RESPONSE_BODY_LIMIT_BYTES': ('tools.tts_tool_providers', 'TTS_RESPONSE_BODY_LIMIT_BYTES'), + 'acquire_tts_lease': ('tools.tts_tool_lifecycle', 'acquire_tts_lease'), + 'hermes_xai_user_agent': ('tools.xai_http', 'hermes_xai_user_agent'), + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), + 'nous_tool_gateway_unavailable_message': ('tools.tool_backend_helpers', 'nous_tool_gateway_unavailable_message'), + 'read_selection': ('tools.tool_backend_helpers', 'read_selection'), + 'release_tts_lease': ('tools.tts_tool_lifecycle', 'release_tts_lease'), + 'release_tts_provider': ('tools.tts_tool_lifecycle', 'release_tts_provider'), + 'resolve_managed_tool_gateway': ('tools.managed_tool_gateway', 'resolve_managed_tool_gateway'), + 'resolve_openai_audio_api_key': ('tools.tool_backend_helpers', 'resolve_openai_audio_api_key'), + 'selection_error': ('tools.tool_backend_helpers', 'selection_error'), + 'stream_tts_to_speaker': ('tools.tts_tool_speaker', 'stream_tts_to_speaker'), + 'tts_lease_holders': ('tools.tts_tool_lifecycle', 'tts_lease_holders'), + 'warm_tts_provider': ('tools.tts_tool_lifecycle', 'warm_tts_provider'), + 'windows_hide_flags': ('hermes_cli._subprocess_compat', 'windows_hide_flags'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/url_safety.py b/tools/url_safety.py index 88dbdc8b83..539362ffa5 100644 --- a/tools/url_safety.py +++ b/tools/url_safety.py @@ -496,3 +496,60 @@ def redirect_target_from_response(response: Any) -> Optional[str]: return urljoin(str(getattr(response, "url", "")), str(location)) next_request = getattr(response, "next_request", None) return str(next_request.url) if next_request else None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def has_sensitive_query_params(url: str) -> bool: + """Return True when ``url`` carries likely credential-bearing query params.""" + return sensitive_query_param_name(url) is not None + +def ssrf_safe_async_http_transport(**kwargs: Any) -> Any: + """Return an httpx async transport that pins direct TCP connects to vetted IPs.""" + import contextvars + import httpx + + schemes_by_origin_var = contextvars.ContextVar("hermes_ssrf_async_origin_schemes") + + class _Transport(httpx.AsyncHTTPTransport): + def __init__(self, **transport_kwargs: Any): + super().__init__(**transport_kwargs) + self._pool._network_backend = _SSRFGuardedAsyncNetworkBackend( # type: ignore[attr-defined] + schemes_by_origin_var + ) + + async def handle_async_request(self, request: Any) -> Any: + token = schemes_by_origin_var.set(_origin_scheme_context(request)) + try: + return await super().handle_async_request(request) + finally: + schemes_by_origin_var.reset(token) + + return _Transport(**kwargs) + +def ssrf_safe_http_transport(**kwargs: Any) -> Any: + """Return an httpx sync transport that pins direct TCP connects to vetted IPs.""" + import contextvars + import httpx + + schemes_by_origin_var = contextvars.ContextVar("hermes_ssrf_origin_schemes") + + class _Transport(httpx.HTTPTransport): + def __init__(self, **transport_kwargs: Any): + super().__init__(**transport_kwargs) + self._pool._network_backend = _SSRFGuardedNetworkBackend( # type: ignore[attr-defined] + schemes_by_origin_var + ) + + def handle_request(self, request: Any) -> Any: + token = schemes_by_origin_var.set(_origin_scheme_context(request)) + try: + return super().handle_request(request) + finally: + schemes_by_origin_var.reset(token) + + return _Transport(**kwargs) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/vision_tools.py b/tools/vision_tools.py index da2d1637f0..f15404af4a 100644 --- a/tools/vision_tools.py +++ b/tools/vision_tools.py @@ -1048,3 +1048,13 @@ registry.register( check_fn=check_vision_requirements, is_async=True, emoji="🎬") + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import contextlib # noqa: F401,E402 +import sys # noqa: F401,E402 +import threading # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/voice_mode.py b/tools/voice_mode.py index 8d50615433..f0f4d0cc20 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -1512,3 +1512,30 @@ def cleanup_temp_recordings(max_age_seconds: int = 3600) -> int: if deleted: logger.debug("Cleaned up %d old voice recordings", deleted) return deleted + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import difflib # noqa: F401,E402 +import re # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_TTS_ECHO_SIMILARITY_THRESHOLD': ('tools.voice_mode_transcript', 'DEFAULT_TTS_ECHO_SIMILARITY_THRESHOLD'), + 'DEFAULT_VOICE_STOP_PHRASES': ('tools.voice_mode_transcript', 'DEFAULT_VOICE_STOP_PHRASES'), + 'MIN_FRAGMENT_LENGTH_FOR_ECHO': ('tools.voice_mode_transcript', 'MIN_FRAGMENT_LENGTH_FOR_ECHO'), + 'WHISPER_HALLUCINATIONS': ('tools.voice_mode_transcript', 'WHISPER_HALLUCINATIONS'), + 'is_tts_echo': ('tools.voice_mode_transcript', 'is_tts_echo'), + 'voice_stop_hint': ('tools.voice_mode_transcript', 'voice_stop_hint'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/web_result_cache.py b/tools/web_result_cache.py index 1eda5ff315..1ed9ce9172 100644 --- a/tools/web_result_cache.py +++ b/tools/web_result_cache.py @@ -302,3 +302,12 @@ def extract_cache_put( _save_index(index) except Exception as exc: # noqa: BLE001 — cache writes are best-effort logger.debug("Failed to cache web extract for %s: %s", url, exc) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Any # noqa: F401,E402 +from typing import List # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/web_tools.py b/tools/web_tools.py index fb12f868ca..2895fffe9b 100644 --- a/tools/web_tools.py +++ b/tools/web_tools.py @@ -506,3 +506,38 @@ registry.register( check_fn=check_web_api_key, requires_env=_web_requires_env(), is_async=True, emoji="📄", max_result_size_chars=100_000, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Dict # noqa: F401,E402 +from typing import TYPE_CHECKING # noqa: F401,E402 +import asyncio # noqa: F401,E402 +import httpx # noqa: F401,E402 +import re # noqa: F401,E402 +import sys # noqa: F401,E402 + + +_PLUGIN_COMPAT_LAZY = { + 'DEFAULT_EXTRACT_CHAR_LIMIT': ('tools.web_tools_truncate', 'DEFAULT_EXTRACT_CHAR_LIMIT'), + 'Firecrawl': ('plugins.web.firecrawl.provider', 'Firecrawl'), + 'MAX_STORED_TEXT_CHARS': ('tools.web_tools_truncate', 'MAX_STORED_TEXT_CHARS'), + 'build_vendor_gateway_url': ('tools.managed_tool_gateway', 'build_vendor_gateway_url'), + 'managed_nous_tools_enabled': ('tools.tool_backend_helpers', 'managed_nous_tools_enabled'), + 'normalize_url_for_request': ('tools.url_safety', 'normalize_url_for_request'), + 'nous_tool_gateway_unavailable_message': ('tools.tool_backend_helpers', 'nous_tool_gateway_unavailable_message'), + 'prefers_gateway': ('tools.tool_backend_helpers', 'prefers_gateway'), + 'resolve_managed_tool_gateway': ('tools.managed_tool_gateway', 'resolve_managed_tool_gateway'), + 'sensitive_query_param_name': ('tools.url_safety', 'sensitive_query_param_name'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/website_policy.py b/tools/website_policy.py index 9710a1d617..be0af25fd3 100644 --- a/tools/website_policy.py +++ b/tools/website_policy.py @@ -183,3 +183,16 @@ def check_website_access(url: str, config_path: Optional[Path] = None) -> Option "message": f"Blocked by website policy: '{host}' matched rule '{pattern}' from {source}", } return None + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def invalidate_cache() -> None: + """Force the next ``check_website_access`` call to re-read config.""" + global _cached_policy + with _cache_lock: + _cached_policy = None +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/write_approval.py b/tools/write_approval.py index 5f84aeb72c..f1e99b46e9 100644 --- a/tools/write_approval.py +++ b/tools/write_approval.py @@ -279,3 +279,13 @@ def skill_pending_diff(record: Dict[str, Any]) -> str: diff = difflib.unified_diff(current.splitlines(keepends=True), new.splitlines(keepends=True), fromfile=f"a/{target_label}", tofile=f"b/{target_label}") return "".join(diff) or "(no textual change)" + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def is_background() -> bool: + return current_origin() == "background_review" +# ---- END PLUGIN-COMPAT ---- diff --git a/tools/yuanbao_tools.py b/tools/yuanbao_tools.py index b7aec9813b..13bf308380 100644 --- a/tools/yuanbao_tools.py +++ b/tools/yuanbao_tools.py @@ -493,3 +493,12 @@ for _schema, _handler, _emoji in _TOOLS: name=_schema["name"], toolset="hermes-yuanbao", schema=_schema, handler=_handler, check_fn=_check_yuanbao, is_async=True, emoji=_emoji, ) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import List # noqa: F401,E402 +from typing import Optional # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/toolsets.py b/toolsets.py index 23a8ab8e2d..7e87f49dd8 100644 --- a/toolsets.py +++ b/toolsets.py @@ -438,3 +438,28 @@ def get_toolset_info(name: str) -> Dict[str, Any]: "resolved_tools": resolved_tools, "tool_count": len(resolved_tools), "is_composite": bool(toolset["includes"]), } + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + +def resolve_multiple_toolsets(toolset_names: List[str]) -> List[str]: + """ + Resolve multiple toolsets and combine their tools. + + Args: + toolset_names (List[str]): List of toolset names to resolve + + Returns: + List[str]: Combined list of all tool names (deduplicated) + """ + all_tools = set() + + for name in toolset_names: + tools = resolve_toolset(name) + all_tools.update(tools) + + return sorted(all_tools) +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/compute_host.py b/tui_gateway/compute_host.py index b5189353ab..eec7ec24fc 100644 --- a/tui_gateway/compute_host.py +++ b/tui_gateway/compute_host.py @@ -515,3 +515,84 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": # pragma: no cover raise SystemExit(main()) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from dataclasses import field # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from dataclasses import dataclass # noqa: F401,E402 +from dataclasses import field # noqa: F401,E402 + +@dataclass +class SpikeAgent: + """A deterministic AIAgent-shaped object for pipe/interrupt measurements.""" + + session_id: str + history: list[dict[str, str]] = field(default_factory=list) + _interrupt: threading.Event = field(default_factory=threading.Event) + + def clear_interrupt(self) -> None: + self._interrupt.clear() + + def interrupt(self, *, hard_cancel: bool = False) -> None: + self._interrupt.set() + + def run_conversation( + self, + prompt: str, + *, + conversation_history: list[dict[str, str]] | None = None, + stream_callback: Callable[[str], None] | None = None, + delta_count: int = 24, + delay_s: float = 0.001, + ) -> dict[str, Any]: + base_history = list(conversation_history if conversation_history is not None else self.history) + chunks: list[str] = [] + interrupted = False + for index in range(max(0, int(delta_count))): + if self._interrupt.is_set(): + interrupted = True + break + chunk = f"{self.session_id}:{prompt}:{index:04d} " + chunks.append(chunk) + if stream_callback is not None: + stream_callback(chunk) + if delay_s > 0: + time.sleep(delay_s) + if self._interrupt.is_set(): + interrupted = True + final = "".join(chunks) + if interrupted: + final += "[interrupted]" + messages = [ + *base_history, + {"role": "user", "content": prompt}, + {"role": "assistant", "content": final}, + ] + self.history = messages + return {"final_response": final, "messages": messages, "interrupted": interrupted} + +@dataclass +class HostSession: + sid: str + agent: SpikeAgent + history_version: int = 0 + running: bool = False + lock: threading.Lock = field(default_factory=threading.Lock) + + +_PLUGIN_COMPAT_LAZY = { + 'request_hard_interrupt': ('agent.interrupt_compat', 'request_hard_interrupt'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/hosted_room_driver.py b/tui_gateway/hosted_room_driver.py index 1867c02f4b..5e4f5eab46 100644 --- a/tui_gateway/hosted_room_driver.py +++ b/tui_gateway/hosted_room_driver.py @@ -968,3 +968,16 @@ def _info_is_active_for( info: Mapping[str, Any], identity: state.TaskIdentity, *, require_exact: bool = False) -> bool: accepted = (identity.task_id,) if require_exact else (None, identity.task_id) return _info_active(info) and info.get("task_id") in accepted + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import contextlib # noqa: F401,E402 + +@contextlib.contextmanager +def null_turn_lock(_profile: str) -> Any: + """Provide an explicit no-op lock for narrow embedding tests.""" + yield +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/hosted_room_service.py b/tui_gateway/hosted_room_service.py index 0246d75e89..ca23d955ed 100644 --- a/tui_gateway/hosted_room_service.py +++ b/tui_gateway/hosted_room_service.py @@ -613,3 +613,11 @@ class _RouteStatusPeerClient: self._on_ready() return result return tracked + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import hashlib # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/mcp_rpc_helpers.py b/tui_gateway/mcp_rpc_helpers.py index 34b6e17ca0..aa69566f99 100644 --- a/tui_gateway/mcp_rpc_helpers.py +++ b/tui_gateway/mcp_rpc_helpers.py @@ -44,3 +44,31 @@ def summarize_server(name: str, cfg: dict) -> Dict[str, Any]: "oauth_tokens_present": _oauth_tokens_present(name) if auth == "oauth" else None, "enabled": cfg.get("enabled", True) is not False, "tools": cfg.get("tools")} + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +from typing import Optional # noqa: F401,E402 +from typing import Tuple # noqa: F401,E402 + +def resolve_profile(rid, params, err_fn) -> Tuple[Optional[Any], Optional[dict]]: + """Resolve the optional ``profile`` param to a HERMES_HOME override token. + + Returns ``(token, error)``: ``token`` is None for the launch profile (no + override) or an opaque reset token; ``error`` is a JSON-RPC error dict + (built via ``err_fn``) when the named profile doesn't exist. Callers reset + ``token`` in a finally via :func:`reset_profile`. + """ + profile = str(params.get("profile") or "").strip() + if not profile: + return None, None + from hermes_cli.profiles import get_profile_dir + from hermes_constants import set_hermes_home_override + + profile_dir = get_profile_dir(profile) + if not profile_dir or not profile_dir.is_dir(): + return None, err_fn(rid, 4064, f"profile '{profile}' not found") + return set_hermes_home_override(str(profile_dir)), None +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/methods_browser_control.py b/tui_gateway/methods_browser_control.py index cff73ec875..e1a12bdc14 100644 --- a/tui_gateway/methods_browser_control.py +++ b/tui_gateway/methods_browser_control.py @@ -204,3 +204,25 @@ def _(rid, params: dict, transport, _identity, _session_id, broker, scope, _sess def register(server) -> None: """Publish helpers/constants onto ``server`` and install handlers (rebound to its globals).""" bind_module(globals(), server, skip=("_",)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. + + +_PLUGIN_COMPAT_LAZY = { + 'BROWSER_CONTROL_PROTOCOL_VERSION': ('gateway.browser_control_broker', 'BROWSER_CONTROL_PROTOCOL_VERSION'), + 'browser_control_protocol_supported': ('gateway.browser_control_broker', 'browser_control_protocol_supported'), + 'filter_browser_control_capabilities': ('gateway.browser_control_broker', 'filter_browser_control_capabilities'), +} + + +def __getattr__(name): # PEP 562 — lazy so no import cycles + target = _PLUGIN_COMPAT_LAZY.get(name) + if target is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + import importlib + return getattr(importlib.import_module(target[0]), target[1]) +# ---- END PLUGIN-COMPAT ---- diff --git a/tui_gateway/methods_prompt.py b/tui_gateway/methods_prompt.py index 0b4af43ed9..b37588e7b0 100644 --- a/tui_gateway/methods_prompt.py +++ b/tui_gateway/methods_prompt.py @@ -1171,3 +1171,11 @@ def _(rid, params: dict) -> dict: def register(server) -> None: """Publish this module's helpers + handlers onto ``server``, rebound to its globals.""" bind_module(globals(), server, skip=("_",)) + + +# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ---- +# Names external plugins imported from this module before the Sep 2026 decomposition. +# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does). +# The whole block is removed by reverting the commit that added it. +import types # noqa: F401,E402 +# ---- END PLUGIN-COMPAT ----