feat(cli): up/down arrows move cursor within multiline text#4
Open
StefanIsMe wants to merge 518 commits intomainfrom
Open
feat(cli): up/down arrows move cursor within multiline text#4StefanIsMe wants to merge 518 commits intomainfrom
StefanIsMe wants to merge 518 commits intomainfrom
Conversation
* fix(gateway): honor default for invalid bool-like config values * refactor: simplify web backend priority detection Replace cascading boolean conditions with a priority-ordered loop. Same behavior (verified against all 16 env var combinations), half the lines, trivially extensible for new backends. --------- Co-authored-by: aydnOktay <xaydinoktay@gmail.com>
* fix(gateway): honor default for invalid bool-like config values * refactor: simplify web backend priority detection Replace cascading boolean conditions with a priority-ordered loop. Same behavior (verified against all 16 env var combinations), half the lines, trivially extensible for new backends. * fix(tools): show browser and TTS in reconfigure menu _toolset_has_keys() returned False for toolsets with no-key providers (Local Browser, Edge TTS) because it only checked providers with env_vars. Users couldn't find these tools in the reconfigure list and had no obvious way to switch browser/TTS backends. Now treats providers with empty env_vars as always-configured, so toolsets with free/local options always appear in the reconfigure menu. --------- Co-authored-by: aydnOktay <xaydinoktay@gmail.com>
…le pattern - composition.md: add text backdrop (gaussian dark mask behind glyphs) and external layout oracle pattern (browser-based text layout → JSON → Python renderer pipeline for obstacle-aware text reflow) - shaders.md: add reverse vignette shader (center-darkening for text readability) - troubleshooting.md: add diagnostic entries for text-over-busy-background readability and kaleidoscope-destroys-text pitfall
…xt-readability-and-layout-oracle ascii-video skill: text readability techniques and external layout oracle
NousResearch#4037) Multiple agents/profiles running 'hermes honcho setup' all wrote to the shared global ~/.honcho/config.json, overwriting each other's configuration. Root cause: _write_config() defaulted to resolve_config_path() which returns the global path when no instance-local file exists yet (i.e. on first setup). Fix: _write_config() now defaults to _local_config_path() which always returns $HERMES_HOME/honcho.json. Each profile gets its own config file. Reading still falls back to global for cross-app interop and seeding. Also updates cmd_setup and cmd_status messaging to show the actual write path. Includes 10 new tests verifying profile isolation, global fallback reads, and multi-profile independence.
…rch#4081) * fix: rate-limit pairing rejection messages to prevent spam When generate_code() returns None (rate limited or max pending), the "Too many pairing requests" message was sent on every subsequent DM with no cooldown. A user sending 30 messages would get 30 rejection replies — reported as potential hack on WhatsApp. Now check _is_rate_limited() before any pairing response, and record rate limit after sending a rejection. Subsequent messages from the same user are silently ignored until the rate limit window expires. * test: add coverage for pairing response rate limiting Follow-up to cherry-picked PR NousResearch#4042 — adds tests verifying: - Rate-limited users get silently ignored (no response sent) - Rejection messages record rate limit for subsequent suppression --------- Co-authored-by: 0xbyt4 <35742124+0xbyt4@users.noreply.github.com>
…sion (NousResearch#4085) Gateway hygiene pre-compression only checked model.context_length from the top-level config, missing per-model context_length defined in custom_providers entries. This caused premature compression for custom provider users (e.g. 128K default instead of 200K configured). The AIAgent's own compressor already reads custom_providers correctly (run_agent.py lines 1171-1189). This adds the same fallback to the gateway hygiene path, running after runtime provider resolution so the base_url is available for matching.
Display a brief status message before the heavy agent initialization (OpenAI client setup, tool loading, memory init, etc.) so users aren't staring at a blank screen for several seconds. Only prints when self.agent is None (first use or after model switch). Closes NousResearch#4060 Co-authored-by: SHL0MS <SHL0MS@users.noreply.github.com>
Add should_use_color() function to hermes_cli/colors.py that checks NO_COLOR (https://no-color.org/) and TERM=dumb before emitting ANSI escapes. The existing color() helper now uses this function instead of a bare isatty() check. This is the foundation — cli.py and banner.py still have inline ANSI constants that bypass this module (tracked in NousResearch#4071). Closes NousResearch#4066 Co-authored-by: SHL0MS <SHL0MS@users.noreply.github.com>
…NousResearch#4087) Reference docs fixes: - cli-commands.md: remove non-existent --provider alibaba, add hermes profile/completion/plugins/mcp to top-level table, add --profile/-p global flag, add --source chat option - slash-commands.md: add /yolo and /commands, fix /q alias conflict (resolves to /queue not /quit), add missing aliases (/bg, /set-home, /reload_mcp, /gateway) - toolsets-reference.md: fix hermes-api-server (not same as hermes-cli, omits clarify/send_message/text_to_speech) - profile-commands.md: fix show name required not optional, --clone-from not --from, add --remove/--name to alias, fix alias path, fix export/ import arg types, remove non-existent fish completion - tools-reference.md: add EXA_API_KEY to web tools requires_env - mcp-config-reference.md: add auth key for OAuth, tool name sanitization - environment-variables.md: add EXA_API_KEY, update provider values - plugins.md: remove non-existent ctx.register_command(), add ctx.inject_message() Feature docs additions: - security.md: add /yolo mode, approval modes (manual/smart/off), configurable timeout, expanded dangerous patterns table - cron.md: add wrap_response config, [SILENT] suppression - mcp.md: add dynamic tool discovery, MCP sampling support - cli.md: add Ctrl+Z suspend, busy_input_mode, tool_preview_length - docker.md: add skills/credential file mounting Messaging platform docs: - telegram.md: add webhook mode, DoH fallback IPs - slack.md: add multi-workspace OAuth support - discord.md: add DISCORD_IGNORE_NO_MENTION - matrix.md: add MSC3245 native voice messages - feishu.md: expand from 129 to 365 lines (encrypt key, verification token, group policy, card actions, media, rate limiting, markdown, troubleshooting) - wecom.md: expand from 86 to 264 lines (per-group allowlists, media, AES decryption, stream replies, reconnection, troubleshooting) Configuration docs: - quickstart.md: add DeepSeek, Copilot, Copilot ACP providers - configuration.md: add DeepSeek provider, Exa web backend, terminal env_passthrough/images, browser.command_timeout, compression params, discord config, security/tirith config, timezone, auxiliary models 21 files changed, ~1000 lines added
…y buffered events (NousResearch#4083) When the Matrix adapter receives encrypted events it can't decrypt (MegolmEvent), it now: 1. Requests the missing room key from other devices via client.request_room_key(event) instead of silently dropping the message 2. Buffers undecrypted events (bounded to 100, 5 min TTL) and retries decryption after each E2EE maintenance cycle when new keys arrive 3. Auto-trusts/verifies all devices after key queries so other clients share session keys with the bot proactively 4. Exports Megolm keys on disconnect and imports them on connect, so session keys survive gateway restarts This addresses the 'could not decrypt event' warnings that caused the bot to miss messages in encrypted rooms.
Documents the Telegram webhook mode from NousResearch#3880: - New 'Webhook Mode' section in telegram.md with polling vs webhook comparison, config table, Fly.io deployment example, troubleshooting - Add TELEGRAM_WEBHOOK_URL/PORT/SECRET to environment-variables.md - Add Telegram section to .env.example (existing + webhook vars) Co-authored-by: raulbcs <raulbcs@users.noreply.github.com>
Co-authored-by: Yabuku-xD <78594762+Yabuku-xD@users.noreply.github.com>
…sResearch#4107) Add github.repository checks to docker-publish and deploy-site workflows so they skip on forks where upstream-specific resources (Docker Hub org, custom domain) are unavailable. Co-authored-by: StreamOfRon <StreamOfRon@users.noreply.github.com>
…ch#4100) After migrating from OpenClaw, leftover workspace directories contain state files (todo.json, sessions, logs) that confuse the agent — it discovers them and reads/writes to stale locations instead of the Hermes state directory, causing issues like cron jobs reading a different todo list than interactive sessions. Changes: - hermes claw migrate now offers to archive the source directory after successful migration (rename to .pre-migration, not delete) - New `hermes claw cleanup` subcommand for users who already migrated and need to archive leftover OpenClaw directories - Migration notes updated with explicit cleanup guidance - 42 tests covering all new functionality Reported by SteveSkedasticity — multiple todo.json files across ~/.hermes/, ~/.openclaw/workspace/, and ~/.openclaw/workspace-assistant/ caused cron jobs to read from wrong locations.
…usResearch#4093) * fix: treat non-sk-ant- prefixed keys (Azure AI Foundry) as regular API keys, not OAuth tokens * fix: treat non-sk-ant- keys as regular API keys, not OAuth tokens _is_oauth_token() returned True for any key not starting with sk-ant-api, misclassifying Azure AI Foundry keys as OAuth tokens and sending Bearer auth instead of x-api-key → 401 rejection. Real Anthropic OAuth tokens all start with sk-ant-oat (confirmed from live .credentials.json). Non-sk-ant- keys are third-party provider keys that should use x-api-key. Test fixtures updated to use realistic sk-ant-oat01- prefixed tokens instead of fake strings. Salvaged from PR NousResearch#4075 by @HangGlidersRule. --------- Co-authored-by: Clawdbot <clawdbot@openclaw.ai>
…n refresh (NousResearch#4126) Claude Code >=2.1.81 checks for a 'scopes' array containing 'user:inference' in ~/.claude/.credentials.json before accepting stored OAuth tokens as valid. When Hermes refreshes the token, it writes only accessToken, refreshToken, and expiresAt — omitting the scopes field. This causes Claude Code to report 'loggedIn: false' and refuse to start, even though the token is valid. This commit: - Parses the 'scope' field from the OAuth refresh response - Passes it to _write_claude_code_credentials() as a keyword argument - Persists the scopes array in the claudeAiOauth credential store - Preserves existing scopes when the refresh response omits the field Tested against Claude Code v2.1.87 on Linux — auth status correctly reports loggedIn: true and claude --print works after this fix. Co-authored-by: Nick <git@flybynight.io>
… top-level (NousResearch#4116) Major reorganization of the documentation site for better discoverability and navigation. 94 pages across 8 top-level sections (was 5). Structural changes: - Promote Features from 3-level-deep subcategory to top-level section with new Overview hub page categorizing all 26 feature pages - Promote Messaging Platforms from User Guide subcategory to top-level section, add platform comparison matrix (13 platforms x 7 features) - Create new Integrations section with hub page, grouping MCP, ACP, API Server, Honcho, Provider Routing, Fallback Providers - Extract AI provider content (626 lines) from configuration.md into dedicated integrations/providers.md — configuration.md drops from 1803 to 1178 lines - Subcategorize Developer Guide into Architecture, Extending, Internals - Rename "User Guide" to "Using Hermes" for top-level items Orphan fixes (7 pages now reachable via sidebar): - build-a-hermes-plugin.md added to Guides - sms.md added to Messaging Platforms - context-references.md added to Features > Core - plugins.md added to Features > Core - git-worktrees.md added to Using Hermes - checkpoints-and-rollback.md added to Using Hermes - checkpoints.md (30-line stub) deleted, superseded by checkpoints-and-rollback.md (203 lines) New files: - integrations/index.md — Integrations hub page - integrations/providers.md — AI provider setup (extracted) - user-guide/features/overview.md — Features hub page Broken link fixes: - quickstart.md, faq.md: update context-length-detection anchors - configuration.md: update checkpoints link - overview.md: fix checkpoint link path Docusaurus build verified clean (zero broken links/anchors).
…ression When context compression fires during run_conversation() in the gateway, the compressed messages were silently lost on the next turn. Two bugs: 1. Agent-side: _flush_messages_to_session_db() calculated flush_from = max(len(conversation_history), _last_flushed_db_idx). After compression, _last_flushed_db_idx was correctly reset to 0, but conversation_history still had its original pre-compression length (e.g. 200). Since compressed messages are shorter (~30), messages[200:] was empty — nothing written to the new session's SQLite. Fix: Set conversation_history = None after each _compress_context() call so start_idx = 0 and all compressed messages are flushed. 2. Gateway-side: history_offset was always len(agent_history) — the original pre-compression length. After compression shortened the message list, agent_messages[200:] was empty, causing the gateway to fall back to writing only a user/assistant pair, losing the compressed summary and tail context. Fix: Detect session splits (agent.session_id != original) and set history_offset = 0 so all compressed messages are written to JSONL.
Wire the existing tool_progress_callback through the API server's streaming handler so Open WebUI users see what tool is running. Uses the existing 3-arg callback signature (name, preview, args) that fires at tool start — no changes to run_agent.py needed. Progress appears as inline markdown in the SSE content stream. Inspired by PR NousResearch#4032 by sroecker, reimplemented to avoid breaking the callback signature used by CLI and gateway consumers.
…ck (NousResearch#4129) Co-authored-by: Maymun <139681654+maymuneth@users.noreply.github.com>
Remove [:8] truncation from hermes cron list output. Job IDs are 12 hex chars — truncating to 8 makes them unusable for cron run/pause/remove which require the full ID. Co-authored-by: vitobotta <vitobotta@users.noreply.github.com>
…arch#4133) * fix(alibaba): use standard DashScope international endpoint The Alibaba Cloud provider was hardcoded to the coding-intl endpoint (https://coding-intl.dashscope.aliyuncs.com/v1) which only accepts Alibaba Coding Plan API keys. Standard DashScope API keys fail with invalid_api_key error against this endpoint. Changed to the international compatible-mode endpoint (https://dashscope-intl.aliyuncs.com/compatible-mode/v1) which works with standard DashScope keys. Users with Coding Plan keys or China-region keys can still override via DASHSCOPE_BASE_URL or config.yaml base_url. Fixes NousResearch#3912 * fix: update test to match new DashScope default endpoint --------- Co-authored-by: kagura-agent <kagura.chen28@gmail.com>
) Auto-compression still runs silently in the background with server-side logging, but no longer sends messages to the user's chat about it. Removed: - 'Session is large... Auto-compressing' pre-compression notification - 'Compressed: N → M messages' post-compression notification - 'Session is still very large after compression' warning - 'Auto-compression failed' warning - Rate-limit tracking (only existed for these warnings)
…ousResearch#4138) Update docs to reflect that tool progress now streams inline during SSE responses. Previously docs said tool calls were invisible. - api-server.md: add 'Tool progress in streams' note to streaming docs - open-webui.md: update 'How It Works' steps, add Tool Progress tip
…iour The '📨 Queued:' label was misleading — it looked like the message was silently deferred when it was actually being sent immediately after the interrupt. Changed to '⚡ Sending after interrupt:' with multi-message count when the user typed several messages during agent execution. Added comment documenting that this code path only applies when busy_input_mode == 'interrupt' (the default). Based on PR NousResearch#4821 by iRonin. Co-authored-by: iRonin <iRonin@users.noreply.github.com>
Fix double file read bug in google_api.py _missing_scopes(), consolidate redundant _normalize_scope_values into callers, merge duplicate except blocks.
…to terminal_tool - Add contextvars.Token[str] type hints to set/reset_current_session_key - Use get_current_session_key(default='') in terminal_tool.py for background process session tracking, fixing the same env var race for concurrent gateway sessions spawning background processes
…opics
Reads config.extra['group_topics'] to bind skills to specific thread_ids
in supergroup/forum chats. Mirrors the dm_topics skill injection pattern
but for group chat_type. Enables per-topic skill auto-loading in Falcon HQ.
Config format:
platforms.telegram.extra.group_topics:
- chat_id: -1003853746818
topics:
- name: FalconConnect
thread_id: 5
skill: falconconnect-architecture
- 7 new tests covering skill binding, fallthrough, coercion - Docs section in telegram.md with config format, field reference, comparison table, and thread_id discovery tip
…ls (NousResearch#4918) The Anthropic SDK appends /v1/messages to the base_url, so OpenCode's base URL https://opencode.ai/zen/go/v1 produced a double /v1 path (https://opencode.ai/zen/go/v1/v1/messages), causing 404s for MiniMax models. Strip trailing /v1 when api_mode is anthropic_messages. Also adds MiMo-V2-Pro, MiMo-V2-Omni, and MiniMax-M2.5 to the OpenCode Go model lists per their updated docs. Fixes NousResearch#4890
NousResearch#4878) * fix(gateway): add message deduplication to Discord and Slack adapters (NousResearch#4777) Discord RESUME replays events after reconnects (~7/day observed), and Slack Socket Mode can redeliver events if the ack was lost. Neither adapter tracked which messages were already processed, causing duplicate bot responses. Add _seen_messages dedup cache (message ID → timestamp) with 5-min TTL and 2000-entry cap to both adapters, matching the pattern already used by Mattermost, Matrix, WeCom, Feishu, DingTalk, and Email. The check goes at the very top of the message handler, before any other logic, so replayed events are silently dropped. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent duplicate messages on partial stream delivery When streaming fails after tokens are already delivered to the platform, _interruptible_streaming_api_call re-raised the error into the outer retry loop, which would make a new API call — creating a duplicate message. Now checks deltas_were_sent before re-raising: if partial content was already streamed, returns a stub response instead. The outer loop treats the turn as complete (no retry, no fallback, no duplicate). Inspired by PR NousResearch#4871 (@trevorgordon981) which identified the bug. This implementation avoids monkey-patching exception objects and keeps the fix within the streaming call boundary. --------- Co-authored-by: Mibayy <mibayy@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ands (NousResearch#4926) The base adapter's active-session guard queues all messages when an agent is running. This creates a deadlock for /approve and /deny: the agent thread is blocked on threading.Event.wait() in tools/approval.py waiting for resolve_gateway_approval(), but the /approve command is queued waiting for the agent to finish. Dispatch /approve and /deny directly to the message handler (which routes to gateway/run.py's _handle_approve_command) without going through _process_message_background — avoids spawning a competing background task that would mess with session lifecycle/guards. Fixes NousResearch#4898 Co-authored-by: mechovation (original diagnosis in PR NousResearch#4904)
Input like /Users/ironin/file.md:45-46 was routed to process_command() because it starts with /. Added _looks_like_slash_command() which checks whether the first word contains additional / characters — commands never do (/help, /model), paths always do (/Users/foo/bar.md). Applied to both process_loop routing and handle_enter interrupt bypass. Preserves prefix matching (/h → /help) since short prefixes still pass the check. Based on PR NousResearch#4782 by iRonin. Co-authored-by: iRonin <iRonin@users.noreply.github.com>
…ess modes (NousResearch#4935) The tool_preview_length: 0 (unlimited) config change from e314833 removed truncation from gateway progress messages in all/new modes. This caused full terminal commands, code blocks, and file paths to appear as permanent messages in Telegram -- the old 40-char truncation was the correct behavior for messaging platforms. Now: - all/new modes: always truncate previews to 40 chars (old behavior) - verbose mode: respects tool_preview_length config for JSON args cap Reported by Paulclgro and socialsurfer on Discord.
…search#4825) OpenViking is multi-tenant and requires X-OpenViking-Account and X-OpenViking-User headers. Without them, API calls like POST /api/v1/search/find fail on authenticated servers. Add both headers to _VikingClient._headers(), read from env vars OPENVIKING_ACCOUNT (default: root) and OPENVIKING_USER (default: default). All instantiation sites inherit the fix automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When mem0.json exists but is missing the api_key (e.g. after running `hermes memory setup`), the plugin reports "not available" even though MEM0_API_KEY is set in .env. This happens because _load_config() returns the JSON file contents verbatim, never falling back to env vars. Use env vars as the base config and let mem0.json override individual keys on top, so both config sources work together. Fixes: mem0 plugin shows "not available" despite valid MEM0_API_KEY in .env
…merge The original filter (if v) silently drops False and 0, so 'rerank: false' in mem0.json would be ignored. Use explicit None/empty-string check to preserve intentional falsy values.
…esearch#4940) When a skill is active, user_message contains the full SKILL.md content injected by the skill system. This bloated string was being passed to memory provider sync_all(), queue_prefetch_all(), and prefetch_all(), causing providers with query size limits (e.g. Honcho's 10K char limit) to fail. Both call sites now use original_user_message (the clean user input, already defined at line 6516) instead of the skill-inflated user_message: - Pre-turn prefetch (line ~6695): prefetch_all() query - Post-turn sync (line ~8672): sync_all() + queue_prefetch_all() Fixes NousResearch#4889
The config key skills.external_dirs and core resolution (get_all_skills_dirs,
get_external_skills_dirs in agent/skill_utils.py) already existed but several
code paths still only scanned SKILLS_DIR. Now external dirs are respected
everywhere:
- skills_categories(): scan all dirs for category discovery
- _get_category_from_path(): resolve categories against any skills root
- skill_manager_tool._find_skill(): search all dirs for edit/patch/delete
- credential_files.get_skills_directory_mount(): mount all dirs into
Docker/Singularity containers (external dirs at external_skills/<idx>)
- credential_files.iter_skills_files(): list files from all dirs for
Modal/Daytona upload
- tools/environments/ssh.py: rsync all skill dirs to remote hosts
- gateway _check_unavailable_skill(): check disabled skills across all dirs
Usage in config.yaml:
skills:
external_dirs:
- ~/repos/agent-skills/hermes
- /shared/team-skills
Clearer workflow with validation/chunking steps, expanded description with trigger terms for better agent matching, tightened error handling. Fixed stray pipe character in original PR diff. Based on PR NousResearch#4778 by fernandezbaptiste. Co-authored-by: fernandezbaptiste <fernandezbaptiste@users.noreply.github.com>
…ionable error (NousResearch#4959) - HERMES_AGENT_TIMEOUT=0 now means no limit (infinite execution) - Periodic 'still working' notifications every 10 minutes for long tasks - Timeout error message now tells users how to increase the limit - Stale-lock eviction handles infinite timeout correctly (float inf TTL)
…ile read guard
Two pre-existing issues causing test_file_read_guards timeouts on CI:
1. agent/redact.py: _ENV_ASSIGN_RE used unbounded [A-Z_]* with
IGNORECASE, matching any letter/underscore to end-of-string at
each position → O(n²) backtracking on 100K+ char inputs.
Bounded to {0,50} since env var names are never that long.
2. tools/file_tools.py: redact_sensitive_text() ran BEFORE the
character-count guard, so oversized content (that would be rejected
anyway) went through the expensive regex first. Reordered to check
size limit before redaction.
Add MiniMax as a fifth TTS provider alongside Edge TTS, ElevenLabs,
OpenAI, and NeuTTS. Supports speech-2.8-hd (recommended default) and
speech-2.8-turbo models via the MiniMax T2A HTTP API.
Changes:
- Add _generate_minimax_tts() with hex-encoded audio decoding
- Add MiniMax to provider dispatch, requirements check, and Telegram
Opus compatibility handling
- Add MiniMax to interactive setup wizard with API key prompt
- Update TTS documentation and config example
Configuration:
tts:
provider: "minimax"
minimax:
model: "speech-2.8-hd"
voice_id: "English_Graceful_Lady"
Requires MINIMAX_API_KEY environment variable.
API reference: https://platform.minimax.io/docs/api-reference/speech-t2a-http
* feat: add /branch (/fork) command for session branching Inspired by Claude Code's /branch command. Creates a copy of the current session's conversation history in a new session, allowing the user to explore a different approach without losing the original. Works like 'git checkout -b' for conversations: - /branch — auto-generates a title from the parent session - /branch my-idea — uses a custom title - /fork — alias for /branch Implementation: - CLI: _handle_branch_command() in cli.py - Gateway: _handle_branch_command() in gateway/run.py - CommandDef with 'fork' alias in commands.py - Uses existing parent_session_id field in session DB - Uses get_next_title_in_lineage() for auto-numbered branches - 14 tests covering session creation, history copy, parent links, title generation, edge cases, and agent sync * fix: clear ghost status-bar lines on terminal resize When the terminal shrinks (e.g. un-maximize), the emulator reflows previously full-width rows (status bar, input rules) into multiple narrower rows. prompt_toolkit's _on_resize only cursor_up()s by the stored layout height, missing the extra rows from reflow — leaving ghost duplicates of the status bar visible. Fix: monkey-patch Application._on_resize to detect width shrinks, calculate the extra rows created by reflow, and inflate the renderer's cursor_pos.y so the erase moves up far enough to clear ghosts.
OAuth refresh tokens are single-use. When multiple consumers share the same Anthropic OAuth session (credential pool entries, Claude Code CLI, multiple Hermes profiles), whichever refreshes first invalidates the refresh token for all others. This causes a cascade: 1. Pool entry tries to refresh with a consumed refresh token → 400 2. Pool marks the credential as "exhausted" with a 24-hour cooldown 3. All subsequent heartbeats skip the credential entirely 4. The fallback to resolve_anthropic_token() only works while the access token in ~/.claude/.credentials.json hasn't expired 5. Once it expires, nothing can auto-recover without manual re-login Fix: - Add _sync_anthropic_entry_from_credentials_file() to detect when ~/.claude/.credentials.json has a newer refresh token and sync it into the pool entry, clearing exhaustion status - After a successful pool refresh, write the new tokens back to ~/.claude/.credentials.json so other consumers stay in sync - On refresh failure, check if the credentials file has a different (newer) refresh token and retry once before marking exhausted - In _available_entries(), sync exhausted claude_code entries from the credentials file before applying the 24-hour cooldown, so a manual re-login or external refresh immediately unblocks agents Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review feedback: replace bare `except: pass` with a debug log when the post-retry write-back to ~/.claude/.credentials.json fails. The write-back is best-effort (token is already resolved), but logging helps troubleshooting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…les (NousResearch#4738) Add docker_env option to terminal config — a dict of key-value pairs that get set inside Docker containers via -e flags at both container creation (docker run) and per-command execution (docker exec) time. This complements docker_forward_env (which reads values dynamically from the host process environment). docker_env is useful when Hermes runs as a systemd service without access to the user's shell environment — e.g. setting SSH_AUTH_SOCK or GNUPGHOME to known stable paths for SSH/GPG agent socket forwarding. Precedence: docker_env provides baseline values; docker_forward_env overrides for the same key. Config example: terminal: docker_env: SSH_AUTH_SOCK: /run/user/1000/ssh-agent.sock GNUPGHOME: /root/.gnupg docker_volumes: - /run/user/1000/ssh-agent.sock:/run/user/1000/ssh-agent.sock - /run/user/1000/gnupg/S.gpg-agent:/root/.gnupg/S.gpg-agent
Audit found 24+ discrepancies between docs and code. Fixed: HIGH severity: - Remove honcho toolset from tools-reference, toolsets-reference, and tools.md (converted to memory provider plugin, not a built-in toolset) - Add note that Honcho is available via plugin MEDIUM severity: - Add hermes memory command family to cli-commands.md (setup/status/off) - Add --clone-all, --clone-from to profile create in cli-commands.md - Add --max-turns option to hermes chat in cli-commands.md - Add /btw slash command to slash-commands.md - Fix profile show example output (remove nonexistent disk usage, add .env and SOUL.md status lines) - Add missing hermes-webhook toolset to toolsets-reference.md - Add 5 missing providers to fallback-providers.md table - Add 7 missing providers to providers.md fallback list - Fix outdated model examples: glm-4-plus→glm-5, moonshot-v1-auto→kimi-for-coding
- Replaces auto_up/auto_down with unconditional history_previous/next - auto_up/auto_down only triggered history at document boundaries (first/last line), so in multiline input (cursor mid-text), up/down just moved the cursor instead of browsing history — unexpected REPL behavior - Fix: up/down now always browse history; Ctrl+up/Ctrl+down handle cursor movement within multiline text when needed - Adds Ctrl+up and Ctrl+down bindings as escape hatch for multiline editing
This reverts commit daa6e65.
Replaces auto_up/auto_down with explicit cursor position movement via get_cursor_position_up/get_cursor_position_down + set_cursor_position. Problem: auto_up/auto_down fall back to history browsing when cursor is at the first/last character of the document. In a multiline prompt where the cursor is mid-text, pressing up/down would unexpectedly browse history instead of moving the cursor to the line above/below. Fix: up/down now ALWAYS move cursor within the text. History browsing is available via Ctrl+up / Ctrl+down as an escape hatch. Files: cli.py (+47/-7 lines)
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.
Summary
Fixes cursor movement in the TUI prompt box so up/down arrows always move the cursor vertically within multiline text, instead of sometimes triggering history browsing.
Problem
The TUI prompt box is multiline. The previous implementation used
buffer.auto_up/auto_downfor up/down arrow keys. These methods have a dual behavior:When typing a multi-line prompt, the cursor is typically mid-text (not at boundaries), but pressing up/down would sometimes still trigger history browsing instead of moving the cursor to the line above/below — unexpected and inconsistent.
Solution
Uses explicit
get_cursor_position_up()/get_cursor_position_down()+set_cursor_position()instead ofauto_up/auto_downto eliminate the history-browsing fallback.Files changed
cli.py: 1 file, +40/-9 linesTesting