Feature/payg#6
Merged
Merged
Conversation
Copilot on NousResearch#14145 flagged PEP 8 / Black convention — two blank lines between top-level class and next top-level function.
Makes Ctrl+K and Ctrl+W work in hermes --tui mode in macOS
…ution Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box), corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract, browser, vision URL fetching, and gateway media downloads. Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites inherit automatically. Cached for process lifetime. Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle: 169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200 (Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16 link-local range, and the metadata.google.internal / metadata.goog hostnames (checked pre-DNS so they can't be bypassed on networks where those names resolve to local IPs). Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of users). Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
Resolve Feishu @_user_N / @_all placeholders into display names plus a structured [Mentioned: Name (open_id=...), ...] hint so agents can both reason about who was mentioned and call Feishu OpenAPI tools with stable open_ids. Strip bot self-mentions only at message edges (leading unconditionally, trailing only before whitespace/terminal punctuation) so commands parse cleanly while mid-text references are preserved. Covers both plain-text and rich-post payloads. Also fixes a pre-existing hydration bug: Client.request no longer accepts the 'method' kwarg on lark-oapi 1.5.3, so bot identity silently failed to hydrate and self-filtering never worked. Migrate to the BaseRequest.builder() pattern and accept the 'app_name' field the API actually returns. Tighten identity matching precedence so open_id is authoritative when present on both sides.
…lama timeouts `is_local_endpoint()` leaned on `ipaddress.is_private`, which classifies RFC-1918 ranges and link-local as private but deliberately excludes the RFC 6598 CGNAT block (100.64.0.0/10) — the range Tailscale uses for its mesh IPs. As a result, Ollama reached over Tailscale (e.g. `http://100.77.243.5:11434`) was treated as remote and missed the automatic stream-read / stale-stream timeout bumps, so cold model load plus long prefill would trip the 300 s watchdog before the first token. Add a module-level `_TAILSCALE_CGNAT = ipaddress.IPv4Network("100.64.0.0/10")` (built once) and extend `is_local_endpoint()` to match the block both via the parsed-`IPv4Address` path and the existing bare-string fallback (for symmetry with the 10/172/192 checks). Also hoist the previously function-local `import ipaddress` to module scope now that it's used by the constant. Extend `TestIsLocalEndpoint` with a CGNAT positive set (lower bound, representative host, MagicDNS anchor, upper bound) and a near-miss negative set (just below 100.64.0.0, just above 100.127.255.255, well outside the block, and first-octet-wrong).
- appLayout.tsx: restore the 1-row placeholder when `showStickyPrompt` is false. Dropping it saved a row but the composer height shifted by one as the prompt appeared/disappeared, jumping the input vertically on scroll. - useInputHandlers: gateway.rpc (from useMainApp) already catches errors with its own sys() message and resolves to null. The previous `.catch` was dead code and on RPC failures the user saw both 'error: ...' (from rpc) and 'failed to toggle yolo'. Drop the catch and gate 'failed to toggle yolo' on a non-null response so null (= rpc already spoke) stays silent.
fix(tui): input wrap, shift-tab yolo, statusline, clean boot
…nous (NousResearch#14184) Replace xiaomi/mimo-v2-pro with xiaomi/mimo-v2.5-pro and xiaomi/mimo-v2.5 in the OpenRouter fallback catalog and the nous provider model list. Add matching DEFAULT_CONTEXT_LENGTHS entries (1M tokens each).
… + decision-capable command hook
Plugin slash commands now surface as first-class commands in every gateway
enumerator — Discord native slash picker, Telegram BotCommand menu, Slack
/hermes subcommand map — without a separate per-platform plugin API.
The existing 'command:<name>' gateway hook gains a decision protocol via
HookRegistry.emit_collect(): handlers that return a dict with
{'decision': 'deny'|'handled'|'rewrite'|'allow'} can intercept slash
command dispatch before core handling runs, unifying what would otherwise
have been a parallel 'pre_gateway_command' hook surface.
Changes:
- gateway/hooks.py: add HookRegistry.emit_collect() that fires the same
handler set as emit() but collects non-None return values. Backward
compatible — fire-and-forget telemetry hooks still work via emit().
- hermes_cli/plugins.py: add optional 'args_hint' param to
register_command() so plugins can opt into argument-aware native UI
registration (Discord arg picker, future platforms).
- hermes_cli/commands.py: add _iter_plugin_command_entries() helper and
merge plugin commands into telegram_bot_commands() and
slack_subcommand_map(). New is_gateway_known_command() recognizes both
built-in and plugin commands so the gateway hook fires for either.
- gateway/platforms/discord.py: extract _build_auto_slash_command helper
from the COMMAND_REGISTRY auto-register loop and reuse it for
plugin-registered commands. Built-in name conflicts are skipped.
- gateway/run.py: before normal slash dispatch, call emit_collect on
command:<canonical> and honor deny/handled/rewrite/allow decisions.
Hook now fires for plugin commands too.
- scripts/release.py: AUTHOR_MAP entry for @Magaav.
- Tests: emit_collect semantics, plugin command surfacing per platform,
decision protocol (deny/handled/rewrite/allow + non-dict tolerance),
Discord plugin auto-registration + conflict skipping, is_gateway_known_command.
Salvaged from NousResearch#14131 (@Magaav). Original PR added a parallel
'pre_gateway_command' hook and a platform-keyed plugin command
registry; this re-implementation reuses the existing 'command:<name>'
hook and treats plugin commands as platform-agnostic so the same
capability reaches Telegram and Slack without new API surface.
Co-authored-by: Magaav <73175452+Magaav@users.noreply.github.com>
Fixes NousResearch#12976 The generic "gemma": 8192 fallback was incorrectly matching gemma4:31b-cloud before the more specific Gemma 4 entries could match, causing Hermes to assign only 8K context instead of 262K. Added "gemma-4" and "gemma4" entries before the fallback to correctly handle Gemma 4 model naming conventions.
…imeout Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of daemon reliability fixes: - Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (NousResearch#1098) - Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (NousResearch#1157) - Orphaned Chrome processes via process-group kill on shutdown (NousResearch#1137) - Stale daemon after upgrade via .version sidecar and auto-restart (NousResearch#1134) - Idle timeout not firing (sleep future recreated each loop) (NousResearch#1110) - Navigation hanging on lifecycle events that never fire (NousResearch#1059, NousResearch#1092) - CDP attach hang on Chrome 144+ (NousResearch#1133) - Windows daemon TCP bind with Hyper-V port conflicts (NousResearch#1041) - Shadow DOM traversal in accessibility tree snapshots - doctor command for user self-diagnosis Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess environment so the daemon self-terminates after our configured inactivity timeout (default 300s). This is the daemon-side counterpart to the Python-side inactivity reaper — the daemon kills itself and its Chrome children when no commands arrive, preventing orphan accumulation even when the Python process dies without running atexit handlers. Addresses NousResearch#7343 (daemon socket hangs, shadow DOM) and NousResearch#13793 (orphan accumulation from force-killed sessions).
Follow-up for salvaged PR NousResearch#14179. `_cleanup_invalid_pid_path` previously called `remove_pid_file()` for the default PID path, but that helper defensively refuses to delete a PID file whose pid field differs from `os.getpid()` (to protect --replace handoffs). Every realistic stale-PID scenario is exactly that case: a crashed/Ctrl+C'd gateway left behind a PID file owned by a now-dead foreign PID. Once `get_running_pid()` has confirmed the runtime lock is inactive, the on-disk metadata is known to belong to a dead process, so we can force-unlink both the PID file and the sibling `gateway.lock` directly instead of going through the defensive helper. Also adds a regression test with a dead foreign PID that would have failed against the previous cleanup logic.
The _looks_like_gateway_process function was missing the hermes-gateway script pattern, causing dashboard to report gateway as not running even when the process was active. Patterns now cover all entry points: - hermes_cli.main gateway - hermes_cli/main.py gateway - hermes gateway - hermes-gateway (new) - gateway/run.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Adds 'ctx_size' field to _CONTEXT_LENGTH_KEYS tuple - Enables hermes agent to correctly detect context size from custom LLMs running on Lemonade server that use this field name instead of the standard keys (max_seq_len, n_ctx_train, n_ctx)
system has been deprecated for a while and emits a deprecation warning when evaluated
The code execution sandbox creates a Unix domain socket in /tmp with default permissions, allowing any local user to connect and execute tool calls. Restrict to 0o600 after bind. Closes NousResearch#6230
…usResearch#14221) New and newer models from models.dev now surface automatically in /model (both hermes model CLI and the gateway Telegram/Discord picker) for a curated set of secondary providers — no Hermes release required when the registry publishes a new model. Primary user-visible fix: on OpenCode Go, typing '/model mimo-v2.5-pro' no longer silently fuzzy-corrects to 'mimo-v2-pro'. The exact match against the merged models.dev catalog wins. Scope (opt-in frozenset _MODELS_DEV_PREFERRED in hermes_cli/models.py): opencode-go, opencode-zen, deepseek, kilocode, fireworks, mistral, togetherai, cohere, perplexity, groq, nvidia, huggingface, zai, gemini, google. Explicitly NOT merged: - openrouter and nous (never): curated list is already a hand-picked subset / Portal is source of truth. - xai, xiaomi, minimax, minimax-cn, kimi-coding, kimi-coding-cn, alibaba, qwen-oauth (per-project decision to keep curated-only). - providers with dedicated live-endpoint paths (copilot, anthropic, ai-gateway, ollama-cloud, custom, stepfun, openai-codex) — those paths already handle freshness themselves. Changes: - hermes_cli/models.py: add _MODELS_DEV_PREFERRED + _merge_with_models_dev helper. provider_model_ids() branches on the set at its curated-fallback return. Merge is models.dev-first, curated-only extras appended, case-insensitive dedup, graceful fallback when models.dev is offline. - hermes_cli/model_switch.py: list_authenticated_providers() calls the same merge in both its code paths (PROVIDER_TO_MODELS_DEV loop + HERMES_OVERLAYS loop). Picker AND validation-fallback both see fresh entries. - tests/hermes_cli/test_models_dev_preferred_merge.py (new): 13 tests — merge-helper unit tests (empty/raise/order/dedup), opencode-go/zen behavior, openrouter+nous explicitly guarded from merge. - tests/hermes_cli/test_opencode_go_in_model_list.py: converted from snapshot-style assertion to a behavior-based floor check, so it doesn't break when models.dev publishes additional opencode-go entries. Addresses a report from @pfanis via Telegram: newer Xiaomi variants on OpenCode Go weren't appearing in the /model picker, and /model was silently routing requests for new variants to older ones.
Replaces blind tree.sync() on every Discord reconnect with a diff-based reconcile. In safe mode (default), fetch existing global commands, compare desired vs existing payloads, skip unchanged, PATCH changed, recreate when non-patchable metadata differs, POST missing, and delete stale commands one-by-one. Keeps 'bulk' for legacy behavior and 'off' to skip startup sync entirely. Fixes restart-heavy workflows that burn Discord's command write budget and can surface 429s when iterating on native slash commands. Env var: DISCORD_COMMAND_SYNC_POLICY (safe|bulk|off), default 'safe'. Co-authored-by: Codex <codex@openai.invalid>
…lize contexts Follow-up to Magaav's safe sync policy. Two gaps in the canonicalizer caused false diffs or silent drift: 1. discord.py's AppCommand.to_dict() omits nsfw, dm_permission, and default_member_permissions — those live only on attributes. The canonicalizer was reading them via payload.get() and getting defaults (False/True/None), while the desired side from Command.to_dict(tree) had the real values. Any command using non-default permissions false-diffed on every startup. Pull them from the AppCommand attributes via _existing_command_to_payload(). 2. contexts and integration_types weren't canonicalized at all, so drift in either was silently ignored. Added both to _canonicalize_app_command_payload (sorted for stable compare). Also normalized default_member_permissions to str-or-None since the server emits strings but discord.py stores ints locally. Added regression tests for both gaps.
…test runner (NousResearch#14758) - Drop broken tinker-atropos submodule instructions: no .gitmodules exists, tinker-atropos/ is empty, and atroposlib + tinker are regular pip deps in pyproject.toml pulled in by .[all,dev]. Replace with a one-line note. - CLI vs Messaging table: /skills is cli_only=True in COMMAND_REGISTRY, so remove it from the messaging column. /<skill-name> still works there. - Point contributors at scripts/run_tests.sh (the canonical runner enforcing CI-parity env) instead of bare pytest.
…lls sections (NousResearch#14763) Fixes several outright-wrong facts and gaps vs current main: - venv activation: .venv is preferred, venv is fallback (per run_tests.sh) - AIAgent default model is "" (empty, resolved from config), not hardcoded opus - Test suite is ~15k tests / ~700 files, not ~3000 - tools/mcp_tool.py is 2.6k LOC, not 1050 - Remove stale "currently 5" config_version note; the real bump-trigger rule is migration-only, not every new key - Remove MESSAGING_CWD as the messaging cwd — it's been removed in favor of terminal.cwd in config.yaml (gateway bridges to TERMINAL_CWD env var) - .env is secrets-only; non-secret settings belong in config.yaml - simple_term_menu pitfall: existing sites are legacy fallback, rule is no new usage Incomplete/missing sections filled in: - Gateway platforms list updated to reflect actual adapters (matrix, mattermost, email, sms, dingtalk, wecom, weixin, feishu, bluebubbles, webhook, api_server, etc.) - New 'Plugins' section covering general plugins, memory-provider plugins, and dashboard/context-engine/image-gen plugin directories — including the May 2026 rule that plugins must not touch core files - New 'Skills' section covering skills/ vs optional-skills/ split and SKILL.md frontmatter fields - Logs section pointing at ~/.hermes/logs/ and 'hermes logs' CLI - Prompt-cache policy now explicitly mentions --now / deferred slash-command invalidation pattern - Two new pitfalls: gateway two-guard dispatch rule, squash-merge-from-stale branch silent revert, don't-wire-dead-code rule Tree layout trimmed to load-bearing entry points — per-file subtrees were ~70% stale so replaced with directory-level notes pointing readers at the filesystem as the source of truth.
Add xAI as a plugin-based image generation backend using grok-imagine-image. Follows the existing ImageGenProvider ABC pattern used by OpenAI and FAL. Changes: - plugins/image_gen/xai/__init__.py: xAI provider implementation - Uses xAI /images/generations endpoint - Supports text-to-image and image editing with reference images - Multiple aspect ratios (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3) - Multiple resolutions (1K, 2K) - Base64 output saved to cache - Config via config.yaml image_gen.xai section - plugins/image_gen/xai/plugin.yaml: plugin metadata - tests/plugins/image_gen/test_xai_provider.py: 19 unit tests - Provider class (name, display_name, is_available, list_models, setup_schema) - Config (default model, resolution, custom model) - Generate (missing key, success b64/url, API error, timeout, empty response, reference images, auth header) - Registration Requires XAI_API_KEY in ~/.hermes/.env. To use: set image_gen.provider: xai in config.yaml.
The agent-facing image_generate tool only passes prompt + aspect_ratio to provider.generate() (see tools/image_generation_tool.py:953). The editing block (reference_images / edit_image kwargs) could never fire from the tool surface, and the xAI edits endpoint is /images/edits with a different payload shape anyway — not /images/generations as submitted. - Remove reference_images / edit_image kwargs handling from generate() - Remove matching test_with_reference_images case - Update docstring + plugin.yaml description to text-to-image only - Surface resolution in the success extras Follow-up to PR NousResearch#14547. Tests: 18/18 pass.
…max_tokens to OpenRouter (NousResearch#6621)
…ariant - AUTHOR_MAP entry for 130918800+devorun for NousResearch#6636 attribution - test_moa_defaults: was a change-detector tied to the exact frontier model list — flips red every OpenRouter churn. Rewritten as an invariant (non-empty, valid vendor/model slugs).
…Research#14776) Themes and plugins can now pull off arbitrary dashboard reskins (cockpit HUD, retro terminal, etc.) without touching core code. Themes gain four new fields: - layoutVariant: standard | cockpit | tiled — shell layout selector - assets: {bg, hero, logo, crest, sidebar, header, custom: {...}} — artwork URLs exposed as --theme-asset-* CSS vars - customCSS: raw CSS injected as a scoped <style> tag on theme apply (32 KiB cap, cleaned up on theme switch) - componentStyles: per-component CSS-var overrides (clipPath, borderImage, background, boxShadow, ...) for card/header/sidebar/ backdrop/tab/progress/badge/footer/page Plugin manifests gain three new fields: - tab.override: replaces a built-in route instead of adding a tab - tab.hidden: register component + slots without adding a nav entry - slots: declares shell slots the plugin populates 10 named shell slots: backdrop, header-left/right/banner, sidebar, pre-main, post-main, footer-left/right, overlay. Plugins register via window.__HERMES_PLUGINS__.registerSlot(name, slot, Component). A <PluginSlot> React helper is exported on the plugin SDK. Ships a full demo at plugins/strike-freedom-cockpit/ — theme YAML + slot-only plugin that reproduces a Gundam cockpit dashboard: MS-STATUS sidebar with live telemetry, COMPASS crest in header, notched card corners via componentStyles, scanline overlay via customCSS, gold/cyan palette, Orbitron typography. Validation: - 15 new tests in test_web_server.py covering every extended field - tests/hermes_cli/: 2615 passed (3 pre-existing unrelated failures) - tsc -b --noEmit: clean - vite build: 418 kB bundle, ~2 kB delta for slots/theme extensions Co-authored-by: Teknium <p@nousresearch.com>
The Interface release — new Ink-based TUI, pluggable transport architecture, native AWS Bedrock, five new inference paths (NVIDIA NIM, Arcee, Step Plan, Gemini CLI OAuth, ai-gateway), GPT-5.5 via Codex OAuth, QQBot (17th platform), expanded plugin surface, dashboard plugin system + live theme switching, /steer mid-run nudges, shell hooks, webhook direct-delivery, smarter delegation, and auxiliary models config UI. Also folds in the v0.10.0 deferred batch (v0.10.0 shipped only the Nous Tool Gateway). 1,556 commits · 761 PRs · 290 contributors since v0.9.0.
# Conflicts: # README.md # gateway/platforms/api_server.py # run_agent.py # tools/mcp_tool.py # uv.lock
… if available - Changed the return value in APIServerAdapter to return self._model_name or "hermes-agent" as the default. - Refactored test_api_extensions.py to streamline mock setup for API config tests. - Updated mock skills structure in test_api_extensions.py for consistency. - Added coder_profile fixture in test_api_server.py to facilitate profile existence checks. - Marked several tests as skipped due to upstream behavior not being ported to the AtomicBot fork.
- Added support for Atomic Pay-as-you-go mode in the application. - Updated environment configuration to include VITE_ATOMIC_BACKEND_URL. - Enhanced the setup flow to include Atomic sign-in and model selection. - Introduced new Redux slices and actions for managing Atomic authentication state. - Modified UI components to accommodate Atomic-specific features and options. - Updated connection mode options to include Atomic Pay-as-you-go. - Refactored existing components to streamline integration with Atomic services.
- Removed IPC handlers for Atomic authentication, transitioning to localStorage for JWT persistence. - Updated related Redux slice to read/write JWT from localStorage instead of the main process. - Cleaned up unused code and tests related to the previous authentication method. - Enhanced tests to verify localStorage integration for authentication state management.
fix: payg ui fixes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Related Issue
Fixes #
Type of Change
Changes Made
How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AFor New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs