hermes - ✅(Solved) Fix [Bug]: resolve_api_key_provider_credentials() uses os.getenv for base_url_env_var — misses ~/.hermes/.env values [19 pull requests, 1 participants]
ON THIS PAGE
Recommended Tools
×6Utilities matched from this issue’s tags and category — try them while you read without losing context.
GitHub issue graph ai analysis
Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.
The report is written in English Markdown for sharing and archival.
Helpful · Quick feedback
Error Message
Additional Logs / Traceback
Title generation failed: Error code: 401 - {'error': {'message': 'Invalid API Key', 'param': 'Please provide valid API Key', 'code': '401', 'type': 'invalid_key'}}
Root Cause
hermes_cli/auth.py L3529-3531 — resolve_api_key_provider_credentials():
# API key — uses get_env_value() ✅ (reads .env file)
for env_var in pconfig.api_key_env_vars:
val = (get_env_value(env_var) or "").strip()
# base_url — uses os.getenv() ❌ (does NOT read .env file)
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip()hermes_cli/runtime_provider.py — same pattern:
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip().rstrip("/")Fix Action
Fixed
- Fixed by PR: fix: resolve 7 identified issues [automated] (https://github.com/NousResearch/hermes-agent/pull/17246)
- Fixed by PR: fix(auth): use get_env_value for base_url_env_var instead of os.getenv (#18757) (https://github.com/NousResearch/hermes-agent/pull/18797)
- Fixed by PR: fix: use get_env_value() for base_url_env_var resolution (https://github.com/NousResearch/hermes-agent/pull/18908)
- Fixed by PR: fix(doctor): read env vars from .env and default to China DashScope endpoint (https://github.com/NousResearch/hermes-agent/pull/18910)
- Fixed by PR: fix(auth): resolve base_url_env_var via get_env_value everywhere (closes #18757) (https://github.com/NousResearch/hermes-agent/pull/18948)
PR fix notes
PR #17246: fix: resolve 7 identified issues [automated]
- Repository: NousResearch/hermes-agent
- Author: Sldark23
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/17246
Description (problem / solution / changelog)
Summary
This automated maintenance PR resolves six high-priority open issues (bug fixes, cross-platform robustness, and security/config hardening paths) identified in NousResearch/hermes-agent.
Note: The job target was 7 issues. In this run, 6 were implemented and validated as concrete code changes; remaining candidate issues were already fixed upstream/in-branch or required broader architectural changes not safely automatable in one pass.
Issues resolved
-
#18757 -
resolve_api_key_provider_credentials()misses~/.hermes/.envforbase_url_env_var- Replaced
os.getenv(...)withget_env_value(...)in API-key provider credential resolution. - Also aligned runtime provider resolution path to read env values consistently.
- Replaced
-
#18705 -
load_hermes_dotenv()overrides runtime env vars (override=True)- Switched user env loading to
override=Falseso runtime-injected env vars keep precedence. - Updated function docstring behavior notes accordingly.
- Switched user env loading to
-
#18722 - Cron jobs with
next_run_at: nullskipped forever; non-dictorigincrash- Added recovery for recurring
cron/intervaljobs by recomputingnext_run_at. - Hardened
_resolve_origin()to tolerate non-dict origin payloads.
- Added recovery for recurring
-
#18742 - Kimi/Moonshot via aggregators misses reasoning-mode detection
_needs_kimi_tool_reasoning()now also detects Moonshot/Kimi model slugs viais_moonshot_model(...).
-
#18744 -
constraints_pathdead config (not loaded)- Implemented optional loading of
constraints_pathcontent into system prompt composition.
- Implemented optional loading of
-
#18778 - Gateway scoped lock stale detection no-op on macOS/Windows
- Added cross-platform process start time/cmdline detection using
psutilfallback. - Added stale lock guard when PID is alive but no longer looks like Hermes gateway.
- Added cross-platform process start time/cmdline detection using
Files modified
hermes_cli/auth.pyhermes_cli/runtime_provider.pyhermes_cli/env_loader.pycron/jobs.pycron/scheduler.pyrun_agent.pygateway/status.py
Commit list
fix(auth): resolve base_url_env_var via get_env_value in provider credentialsfix(env): preserve runtime environment precedence over .env valuesfix(cron): recover missing next_run_at for recurring jobs and guard origin typefix(agent): improve moonshot model detection and load constraints_path prompt blockfix(gateway): harden scoped lock stale detection on macOS/windows
Changed files
Dockerfile(modified, +3/-2)acp_adapter/session.py(modified, +12/-0)agent/auxiliary_client.py(modified, +280/-28)agent/context_compressor.py(modified, +496/-52)agent/title_generator.py(modified, +2/-2)agent/transports/chat_completions.py(modified, +14/-0)agent/usage_pricing.py(modified, +4/-0)cli-config.yaml.example(modified, +5/-0)cli.py(modified, +27/-3)cron/jobs.py(modified, +10/-2)cron/scheduler.py(modified, +14/-4)docker/entrypoint.sh(modified, +9/-1)gateway/channel_directory.py(modified, +14/-4)gateway/platforms/discord.py(modified, +33/-7)gateway/platforms/email.py(modified, +12/-2)gateway/platforms/feishu.py(modified, +34/-1)gateway/platforms/qqbot/adapter.py(modified, +8/-2)gateway/platforms/telegram_network.py(modified, +7/-2)gateway/platforms/weixin.py(modified, +10/-1)gateway/run.py(modified, +129/-32)gateway/status.py(modified, +37/-2)hermes_cli/auth.py(modified, +4/-4)hermes_cli/commands.py(modified, +1/-1)hermes_cli/config.py(modified, +271/-40)hermes_cli/copilot_auth.py(modified, +1/-1)hermes_cli/doctor.py(modified, +6/-1)hermes_cli/env_loader.py(modified, +5/-4)hermes_cli/gateway.py(modified, +16/-13)hermes_cli/main.py(modified, +69/-3)hermes_cli/memory_setup.py(modified, +1/-1)hermes_cli/model_switch.py(modified, +6/-1)hermes_cli/models.py(modified, +60/-2)hermes_cli/profiles.py(modified, +16/-3)hermes_cli/runtime_provider.py(modified, +17/-14)hermes_cli/setup.py(modified, +8/-2)hermes_cli/slack_cli.py(modified, +1/-2)hermes_cli/status.py(modified, +17/-2)hermes_cli/web_server.py(modified, +1/-1)hermes_constants.py(modified, +16/-3)model_tools.py(modified, +44/-13)run_agent.py(modified, +413/-82)setup-hermes.sh(modified, +23/-12)skills/red-teaming/godmode/scripts/load_godmode.py(modified, +9/-8)tests/agent/test_context_compressor.py(modified, +389/-0)tests/agent/transports/test_chat_completions.py(modified, +11/-0)tests/gateway/test_compress_command.py(modified, +49/-0)tests/hermes_cli/test_api_key_providers.py(modified, +5/-5)tests/hermes_cli/test_config.py(modified, +17/-0)tests/run_agent/test_413_compression.py(modified, +81/-1)tests/run_agent/test_compression_boundary_hook.py(modified, +42/-0)tests/run_agent/test_run_agent.py(modified, +100/-13)tests/tools/test_skill_manager_tool.py(modified, +270/-0)tools/approval.py(modified, +1/-1)tools/delegate_tool.py(modified, +4/-1)tools/environments/docker.py(modified, +36/-5)tools/environments/local.py(modified, +8/-1)tools/file_operations.py(modified, +70/-67)tools/file_tools.py(modified, +13/-2)tools/send_message_tool.py(modified, +72/-2)tools/session_search_tool.py(modified, +2/-2)tools/skill_manager_tool.py(modified, +82/-21)tools/skills_tool.py(modified, +13/-1)tools/terminal_tool.py(modified, +6/-0)tools/tool_backend_helpers.py(modified, +15/-5)tools/tts_tool.py(modified, +27/-16)tools/voice_mode.py(modified, +23/-10)toolsets.py(modified, +14/-1)tui_gateway/server.py(modified, +5/-3)ui-tui/src/app/turnController.ts(modified, +1/-1)ui-tui/src/app/useInputHandlers.ts(modified, +8/-3)ui-tui/src/app/useSessionLifecycle.ts(modified, +1/-1)ui-tui/src/gatewayTypes.ts(modified, +1/-0)utils.py(modified, +9/-0)uv.lock(modified, +161/-2)website/docs/reference/environment-variables.md(modified, +1/-1)
PR #18797: fix(auth): use get_env_value for base_url_env_var instead of os.getenv (#18757)
- Repository: NousResearch/hermes-agent
- Author: shellybotmoyer
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18797
Description (problem / solution / changelog)
Fix #18757: Use get_env_value() for base_url_env_var instead of os.getenv()
Problem
resolve_api_key_provider_credentials() in hermes_cli/auth.py resolves base_url_env_var using os.getenv(), which does not read from ~/.hermes/.env. Providers with a custom base URL stored only in .env (not exported in the shell environment) silently fall back to the wrong endpoint — the default inference_base_url from PROVIDER_REGISTRY.
Fix
- Replace
os.getenv()withget_env_value()forbase_url_env_varinauth.py(4 sites) andruntime_provider.py(1 site) - This ensures custom base URLs stored in
~/.hermes/.envare properly resolved
Testing
- Verified that providers with
base_url_env_varset in~/.hermes/.envnow correctly resolve their custom endpoints get_env_value()already handles bothos.environand.envfile lookups
Changed files
hermes_cli/auth.py(modified, +11/-4)hermes_cli/runtime_provider.py(modified, +3/-1)
PR #18908: fix: use get_env_value() for base_url_env_var resolution
- Repository: NousResearch/hermes-agent
- Author: zons-zhaozhy
- State: closed | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18908
Description (problem / solution / changelog)
Summary
resolve_api_key_provider_credentials() and related functions used
os.getenv() for base_url_env_var, which does NOT read
~/.hermes/.env. Providers with custom base URLs stored only in .env
hit the wrong endpoint.
Root Cause
API key resolution already correctly uses get_env_value() — this was
an inconsistency where keys were found but base URLs were not.
Fix
Replace os.getenv() with get_env_value() at all 5 occurrences:
| File | Function |
|---|---|
| auth.py | get_api_key_provider_status() |
| auth.py | get_external_process_provider_status() |
| auth.py | resolve_api_key_provider_credentials() |
| auth.py | resolve_external_process_provider_credentials() |
| runtime_provider.py | _resolve_explicit_runtime() |
Same Bug Class
- #15914 → PR #16101 (api_key + credential_pool)
- #17140 → PR #17434 (TTS/STT tools)
The base_url_env_var resolution was missed in both prior fixes.
Scope
- 2 files, +14 / -5 lines
- No behavioral change for env vars already in
os.environ - Purely fixes the .env file reading gap
Fixes #18757
Changed files
hermes_cli/auth.py(modified, +12/-4)hermes_cli/runtime_provider.py(modified, +2/-1)
PR #18910: fix(doctor): read env vars from .env and default to China DashScope endpoint
- Repository: NousResearch/hermes-agent
- Author: zons-zhaozhy
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18910
Description (problem / solution / changelog)
Summary
hermes doctor API-key health checks had two bugs:
Bug 1: env vars from .env invisible to doctor
os.getenv() does not read ~/.hermes/.env. Keys and base URLs
stored only in .env (not exported to the shell) were invisible to all
16 api-key provider health checks.
Fix: Replace os.getenv() with get_env_value() for both API key
and base URL resolution.
Bug 2: DashScope default URL is international-only
The default health-check URL was
dashscope-intl.aliyuncs.com (international). China-region keys —
the vast majority of DashScope users — are valid only on
dashscope.aliyuncs.com. Doctor reported these as invalid.
Fix: Default to dashscope.aliyuncs.com (China). Users with
DASHSCOPE_BASE_URL set are unaffected.
Same Bug Class
- #14134 (PR #18906) — api_key drift on provider switch
- #15914 (PR #16101) — api_key + credential_pool
- #17140 (PR #17434) — TTS/STT tools
- #18757 (PR #18908) — base_url_env_var in auth.py/runtime_provider.py
Testing
23 existing doctor tests: all pass, zero regression.
Scope
- 1 file:
hermes_cli/doctor.py(+5 / -4) - Affects all 16 api-key provider health checks (env var reading)
- DashScope default URL change
Fixes #18904
Changed files
hermes_cli/doctor.py(modified, +28/-6)
PR #18948: fix(auth): resolve base_url_env_var via get_env_value everywhere (closes #18757)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18948
Description (problem / solution / changelog)
Closes #18757
resolve_api_key_provider_credentials() and friends read base_url_env_var via os.getenv(), which never consults ~/.hermes/.env. A user who sets, e.g., XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 only in the dotenv file silently falls back to the registry default and gets 401s on auxiliary tasks. API keys are read correctly via get_env_value() — base URLs are not. This is the same bug class fixed for API keys in #16101 and for TTS/STT in #17434.
Why this PR rather than #17246
#17246 (diff) targets the same issue but only patches 2 of 6 buggy spots, and despite the PR description claiming runtime_provider.py was "aligned", the diff doesn't touch that file at all.
| File | Function | #17246 | This PR |
|---|---|---|---|
hermes_cli/auth.py | get_api_key_provider_status | ✅ | ✅ |
hermes_cli/auth.py | resolve_api_key_provider_credentials | ✅ | ✅ |
hermes_cli/auth.py | get_external_process_provider_status (Copilot ACP) | ❌ | ✅ |
hermes_cli/auth.py | resolve_external_process_provider_credentials | ❌ | ✅ |
hermes_cli/runtime_provider.py | resolve_runtime_provider (api_key branch) | ❌ (claimed but absent) | ✅ |
hermes_cli/model_switch.py | _refresh_curated_models builtin endpoint dedup | ❌ | ✅ |
#17246 also bundles 5 unrelated fixes (cron, env precedence, moonshot detection, gateway lock, etc.) — this PR is scoped solely to #18757 so it can land without dragging the rest along.
Behavior
get_env_value() already preserves shell-export precedence — os.environ wins when the variable is exported, dotenv is the fallback. Existing deployments are unaffected; users who relied on the dotenv file finally get the right endpoint.
Tests
New tests/hermes_cli/test_base_url_dotenv_resolution.py (7 tests):
resolve_api_key_provider_credentials/get_api_key_provider_statusread base URL from~/.hermes/.env(Xiaomi)resolve_external_process_provider_credentials/*_statusread base URL from~/.hermes/.env(Copilot ACP)resolve_runtime_providerhonours dotenv on the api_key branchmodel_switchdedup helper is wired throughget_env_value- Regression guard: shell exports still beat dotenv values
522 passed, 522 warnings in 6.24sRun on the full intersection of test files touching the changed modules — full green, no regressions.
Files changed
hermes_cli/auth.py— top-levelget_env_valueimport + 4 call siteshermes_cli/runtime_provider.py— 1 call sitehermes_cli/model_switch.py— 1 call site (withos.environfallback for safety)tests/hermes_cli/test_base_url_dotenv_resolution.py— new
Closes #18757.
Changed files
hermes_cli/auth.py(modified, +20/-5)hermes_cli/model_switch.py(modified, +7/-1)hermes_cli/runtime_provider.py(modified, +5/-1)tests/hermes_cli/test_base_url_dotenv_resolution.py(added, +192/-0)
PR #18788: fix(web/dashboard): skip xterm.js WebGL renderer on Safari to fix Unicode box-drawing glyphs
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18788
Description (problem / solution / changelog)
Summary
Fixes #18773. In the dashboard Chat tab, Safari's WebGL renderer mangles Unicode box-drawing characters (╔╗║╚╝, ██╗, etc.) used by the HERMES AGENT banner and TUI borders — they fragment into blocks instead of forming proper shapes. Chrome and Firefox WebGL render the same glyphs correctly.
This is a known xterm.js + Safari WebKit interaction (also affects VS Code Server, JupyterLab, etc.).
Fix
web/src/pages/ChatPage.tsx: skip the WebglAddon on Safari and let xterm.js fall back to the default DOM renderer, which renders the box-drawing glyphs faithfully on Safari.
A short isSafariBrowser() helper detects macOS/iOS Safari without false positives on Chromium derivatives:
- Requires the
Safari/UA token. - Excludes Chromium fingerprints (
Chrome/,Chromium/,CriOS/) — Chromium-based browsers all advertiseSafari/in their UA for legacy compat. - Excludes other WebKit-wrapping shells we know don't hit the bug (
FxiOS/,EdgiOS/,AndroidUAs).
Existing WebGL gate (terminalTierWidthPx(host) >= 768) is preserved, so wide layouts on Chrome/Firefox/Edge still get the crisp WebGL rendering. Only Safari at any width goes to the DOM renderer.
Why not the issue's proposed rendererType: 'dom'?
The proposed new Terminal({ rendererType: 'dom' }) option is from xterm.js v4 and was removed in v5+. The repo is on @xterm/xterm@^6.0.0, where renderer choice is controlled via addons (WebglAddon, CanvasAddon). Skipping the WebglAddon is the modern equivalent.
Verification
tsc -b: clean.vite build: clean (1.65s).- Diff scope:
web/src/pages/ChatPage.tsxonly, +38/-1.
What I did NOT change
- Chrome/Firefox/Edge WebGL path (still active for wide layouts).
- Mobile/narrow layout fallback (
< 768px→ DOM renderer; unchanged). - The Safari path uses the default DOM renderer, not the canvas addon — adding
@xterm/addon-canvaswould be a bigger dependency change and the DOM renderer already renders box-drawing correctly on Safari per the issue and xterm.js docs.
cc @bb @W0921
Closes #18773
Changed files
web/src/pages/ChatPage.tsx(modified, +38/-1)
PR #17349: fix(compressor): shrink protect_first_n on recompaction (#17344)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/17349
Description (problem / solution / changelog)
Closes #17344.
Bug
Reporter traced a 6-session compression chain in which every child session carried the identical original first user request — as if no progress had been made. After resume (or on new sessions opened post-compression), the model re-executes the original first task instead of continuing from the handoff summary's ## Active Task.
Root cause
ContextCompressor protects protect_first_n=3 messages at the head — [system, user1, assistant1]. On every cycle that head is preserved verbatim:
[system + compaction note, user1 (ORIGINAL), assistant1, summary, …tail…, latest_user]SUMMARY_PREFIX says "resume from ## Active Task," but user1 (ORIGINAL) is sitting right next to it as a still-prominent user-role message. The model latches onto the first plausible unanswered request and re-executes it — structured summary prose loses against direct attention on a user message. After 6 cycles that same user1 has been re-anchored 6 times.
Fix
On the second and subsequent compactions — detected by checking whether messages[0] (the system prompt) already carries the compaction note we appended last time — shrink protect_first_n to 1 for that call. The original [user1, assistant1] then flow into the summariser pool, and the structured ## Active Task section becomes the sole steering signal as designed.
The shrink is per-call; self.protect_first_n is left untouched so fresh sessions continue to use the configured default.
effective_protect_first_n = self.protect_first_n
if self._is_recompaction(messages) and self.protect_first_n > 1:
effective_protect_first_n = 1Detection signal
Reuses the existing compaction note already written to the system prompt on first compaction. A new _COMPRESSION_NOTE_SENTINEL constant captures a stable substring ("earlier conversation turns have been compacted into a handoff summary") so PR #17301 — which expands the note text — will not break detection. New helper ContextCompressor._is_recompaction(messages) does the lookup with no I/O, returns False on malformed input, and handles multimodal system content via the existing _content_text_for_contains() helper.
Why not just strengthen SUMMARY_PREFIX?
The prefix already says "Respond ONLY to the latest user message that appears AFTER this summary." Stronger prose helps marginally but cannot compete with structural attention on a head-preserved user message. The reporter explicitly noted: "the model responds as if the session had just started." That's an architectural problem, not a wording problem.
Coordination with PR #17301 / #17251
PR #17301 (open, by @HiddenPuppy) addresses a sibling problem: SUMMARY_PREFIX over-applies "background reference" framing to memory and skills. Both fixes stem from the same root concern (compaction handoff misinterpreted by the model) but are orthogonal — #17301 carves out exceptions inside SUMMARY_PREFIX text; this PR shrinks protect_first_n on recompaction. They compose cleanly; merge order doesn't matter.
Out of scope
The reporter also flagged parent_session_id = NULL observations on chained sessions. That's a separate DB-write concern — run_agent.py:8891 explicitly passes parent_session_id=old_session_id and resolve_resume_session_id (#15000) handles chain-walking. If NULL is observed it's likely a different write-path failure and deserves its own bug. This PR stays focused on the message-level fix that unbreaks the user-visible "restarts first task" behaviour.
Tests
TestIsRecompaction (6 cases) — sentinel detection edge cases:
test_fresh_system_prompt_is_not_recompactiontest_system_prompt_with_compaction_note_is_recompactiontest_empty_messages_safetest_non_system_first_message_is_not_recompactiontest_multimodal_system_content_is_inspectedtest_garbage_content_does_not_raise
TestRecompactionShrinksProtectFirstN (5 cases) — behavioural:
test_first_compaction_preserves_first_exchange_in_head(control)test_recompaction_demotes_first_exchange_to_summary(the bug)test_recompaction_preserves_latest_user_message_in_tailtest_recompaction_keeps_protect_first_n_attribute_unchangedtest_protect_first_n_one_no_op_for_recompaction
TestRecompactionMinForCompressGate (1 case) — _min_for_compress early-return uses the effective (post-shrink) head count.
$ python -m pytest tests/agent/test_context_compressor.py \
tests/agent/test_context_compressor_recompaction.py \
tests/run_agent/test_compression_boundary_hook.py \
tests/run_agent/test_compression_persistence.py \
tests/run_agent/test_413_compression.py -q
99 passed in 8.26s87 pre-existing + 12 new, zero regressions.
Changed files
agent/context_compressor.py(modified, +48/-2)tests/agent/test_context_compressor_recompaction.py(added, +234/-0)
PR #17329: fix(delegate): surface tool_trace on N-API-call subagent timeouts (#17308)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/17329
Description (problem / solution / changelog)
Closes #17308.
Problem
When a subagent under delegate_task times out after making >0 API calls, the lead agent gets a vague string and nothing else:
Subagent timed out after 120s with 3 API call(s) completed — likely stuck on a slow API call or unresponsive network request.
There's no way to tell apart the two failure modes:
- Tool finished, next LLM request hung — the tool itself is fine; the provider froze.
- Tool itself hung — network partition, blocked I/O, etc.
This was the gap between the two existing diagnostic paths:
| Path | Coverage |
|---|---|
| Normal completion (#1175) | tool_trace in return dict |
| 0-API-call timeout (#15105) | diagnostic_path with structured log |
| N-API-call timeout | None ← this PR |
Fix
Three pieces:
1. Extract a shared trace builder
The normal-completion branch already reconstructs tool_trace from result['messages']. Pulled that loop out into a module-level _build_tool_trace_from_messages() helper so both branches use one implementation.
2. Reconstruct trace on the N-API-call timeout branch
In _run_single_child's timeout branch (when is_timeout and child_api_calls > 0):
- Read
child._session_messagesand run it through the helper. - If the trace tail has no matching tool-role response → mark
status='in_progress'(the tool itself is hung). - Read
get_activity_summary().current_tool. If it disagrees with the trace tail, prefer it — the tool-role write can lag because the agent writes the assistant message first and the tool response only after the tool returns.
3. Surface the diagnostics
Return dict now carries tool_trace, last_tool, last_tool_status, current_tool. Error message gets a last_tool=X (status=Y) suffix so it shows up in logs and the lead's prompt:
Subagent timed out after 120s with 3 API call(s) completed — likely stuck on a slow API call or unresponsive network request. last_tool=terminal (status=in_progress)
0-API-call timeouts (diagnostic_path branch) and non-timeout errors leave the new fields empty/None so consumers don't read stale data.
Tests
Added two test classes in tests/tools/test_delegate_subagent_timeout_diagnostic.py:
TestRunSingleChildTimeoutToolTrace — end-to-end through _run_single_child with a tiny timeout:
test_timeout_after_completed_tool_marks_status_ok— tool returned cleanly →status=ok,current_tool=Nonetest_timeout_inside_running_tool_marks_status_in_progress— tool never returned →status=in_progress,current_toolsettest_timeout_with_tool_error_preserves_error_status— error responses keepstatus=errortest_timeout_with_parallel_tool_calls_pairs_by_id— out-of-order replies still pair correctlytest_zero_api_call_timeout_skips_tool_trace— 0-API branch keeps the new fields empty (no stale data alongsidediagnostic_path)test_timeout_with_no_session_messages_attr_does_not_crash— degrades to empty trace if_session_messagesis absent
TestBuildToolTraceFromMessages — direct unit tests for the extracted helper (non-list input, non-dict entries, assistants without tool_calls, tool responses without tool_call_id).
$ python -m pytest tests/tools/test_delegate_subagent_timeout_diagnostic.py -q
................. [100%]
17 passed in 3.88sCombined with the existing test_delegate.py suite: 137/137 pass.
Changed files
tests/tools/test_delegate_subagent_timeout_diagnostic.py(modified, +254/-0)tools/delegate_tool.py(modified, +113/-32)
PR #17325: fix(telegram): stop large videos from triggering infinite model fallback (#17302)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/17325
Description (problem / solution / changelog)
Closes #17302.
Summary
When a Telegram video > 20 MB hits the bot, getFile() raises BadRequest("File is too big"). The current handler catches the exception, logs a warning, and falls through to handle_message(event) with an effectively empty event — the agent then burns through every fallback model (15+ retries in the reporter's logs) trying to respond to nothing.
This PR fixes it with three layered defenses, mirroring the size-check pattern that the non-video document branch already uses:
1. Pre-check file_size before downloading
Both the native msg.video branch and the video-as-document branch now verify file_size <= 20 MB before calling get_file(). Oversize / unverifiable videos short-circuit with a user-visible message ("Telegram's Bot API limits file downloads to 20 MB…") and message_type=VIDEO. The agent gets a meaningful event instead of a blank one.
2. Trap "File is too big" inside the except block
For forwarded-video / edited-message edge cases where file_size lies, the BadRequest is now caught at runtime and event.text is set to an explanatory message instead of being left blank. This is the surgical fix that prevents the fallback storm even when the pre-check is bypassed.
3. Optional opt-out: telegram.extra.ignore_videos: true
When set, video messages (native and video/* MIME documents) are dropped at the top of _handle_media_message, before any work is done. Other media (PDFs, photos, voice, audio) is unaffected.
Why not the issue's exact diff
The issue's proposed diff calls self._send_safe_message(...) which doesn't exist in this codebase (only self._bot.send_message(...) does). I kept the spirit of the suggestion — the friendly text message — but routed it through the existing event.text + handle_message() path that the document handler already uses for "Unsupported document type" and "too large or unverifiable", so the fix is consistent with the surrounding code rather than introducing a new send pattern.
Tests
Added 8 new tests in tests/gateway/test_telegram_documents.py::TestVideoDownloadBlock:
test_oversize_native_video_short_circuits_with_friendly_texttest_unverifiable_native_video_size_short_circuits(parity with the existing documentfile_size=Nonesecurity fix)test_oversize_video_document_short_circuitstest_native_video_get_file_too_big_does_not_send_blank_eventtest_video_document_get_file_too_big_does_not_send_blank_eventtest_ignore_videos_config_skips_native_videotest_ignore_videos_config_skips_video_documentstest_ignore_videos_does_not_block_pdfs
The existing _make_video() helper now defaults file_size=1024 so the prior happy-path test still passes through the new gate.
$ python -m pytest tests/gateway/test_telegram_documents.py -q
............................................ [100%]
44 passed in 3.50spython3 -c "import ast; ast.parse(open('gateway/platforms/telegram.py').read())" clean.
Out of scope
The reporter's _send_safe_message reply-via-Telegram pattern is more invasive than needed; the agent-event path is sufficient and consistent with how the document handler already communicates blocked uploads. Happy to add a direct reply if maintainers prefer it.
Changed files
gateway/platforms/telegram.py(modified, +82/-4)tests/gateway/test_telegram_documents.py(modified, +120/-1)
PR #17323: docs(run_agent): note xiaomi/MiMo empirical exclusion from reasoning whitelist (#17314)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/17323
Description (problem / solution / changelog)
Closes #17314 (the suggested follow-up #1 — inline doc).
Summary
#17314 empirically established that xiaomi/MiMo (mimo-v2.5-pro) accepts reasoning_effort at the schema layer but produces statistically indistinguishable reasoning depth, length, and accuracy across none / low / medium / high (4 efforts × N=5 on AIME 2025 II P2; Mann-Whitney U pairwise p > 0.1 on every pair; 100% accuracy on all 20 trials; identical solution paths in inspected reasoning_content).
The conservative default in _supports_reasoning_extra_body() — not whitelisting xiaomi — is therefore correct. Forwarding the field would just ship a no-op.
This PR adds a docstring note documenting the empirical test so a future PR doesn't "complete the list" by adding xiaomi without first re-verifying server-side behavior.
Changes
run_agent.py: extended_supports_reasoning_extra_body()docstring with an "Empirically excluded providers" section noting the xiaomi/MiMo result, the test methodology, and the link back to #17314.- No code-path changes. No behavior changes. AST-parses clean.
Not included (intentional)
The issue's optional follow-up #2 — surfacing a startup warning when agent.reasoning_effort is set in config.yaml for a provider that won't forward it — is broader-scope (touches every excluded provider, not just xiaomi) and is left as a separate optional follow-up so this PR stays a minimal, mergeable doc-only change.
Tests
python3 -c "import ast; ast.parse(open('run_agent.py').read())"clean.- Docs-only change; no functional test surface.
Changed files
run_agent.py(modified, +14/-0)
PR #16381: fix(doctor): use importlib.util.find_spec for editable-install detection
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/16381
Description (problem / solution / changelog)
Closes #16365.
Problem
hermes doctor reports ⚠ tinker-atropos found but not installed even when the package is installed editably (uv pip install -e ./tinker-atropos) and importable from the same interpreter — exactly the case @rbrowning85 hit.
$ python -c "import tinker_atropos; print('OK')"
OK
$ uv pip list | grep tinker
tinker-atropos 0.1.0 (editable install)
$ hermes doctor
◆ Submodules
⚠ tinker-atropos found but not installed (run: uv pip install -e ./tinker-atropos)Root cause: the doctor check uses __import__("tinker_atropos") inside a try/except ImportError. That probe can fail in launcher contexts (e.g. ~/.local/bin/hermes) whose sys.path or import-machinery state differs from the active shell — particularly for editable installs hooked through .pth shims — even though the spec is locatable via importlib.
Fix
Adopt the issue's preferred approach (option 2) and centralize it in a _module_available() helper:
def _module_available(module: str) -> bool:
import importlib.util
try:
return importlib.util.find_spec(module) is not None
except (ImportError, ValueError):
return Falsefind_spec only checks importability — never executes the module body — so it's:
- Reliable for editable installs across launcher contexts.
- Side-effect free.
- Robust to a transitive dep failing to import (we only care whether
tinker_atropositself is locatable). - Defensive against
ValueErrorfrom malformed spec strings andImportErrorfrom a parent package failing to load.
Then swap the __import__ probe in the tinker-atropos branch (hermes_cli/doctor.py) for _module_available("tinker_atropos").
I deliberately scoped this to the tinker-atropos check rather than the general required_packages / optional_packages loops, since those check user-facing third-party libs where the existing import-with-side-effects probe is fine and the install-cmd advice is the goal anyway. Happy to broaden if maintainers prefer.
Tests
tests/hermes_cli/test_doctor.py::TestModuleAvailable — 5 new tests:
test_returns_true_for_stdlib_module— sanity check (json).test_returns_false_for_missing_module— non-existent module returns False.test_returns_false_on_value_error— malformed spec swallowed.test_returns_false_when_parent_package_fails— parent-package ImportError swallowed.test_does_not_execute_module_body— confirmsfind_specpath doesn't import the body, the property that fixes the editable-install case.
pytest tests/hermes_cli/test_doctor.py -q
28 passed (23 pre-existing + 5 new)Changed files
hermes_cli/doctor.py(modified, +27/-3)tests/hermes_cli/test_doctor.py(modified, +48/-0)
PR #16380: fix(error_classifier): gate absolute msg/token heuristics to small context windows
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/16380
Description (problem / solution / changelog)
Closes #16351.
Problem
agent/error_classifier.py flagged non-context errors as context_overflow in long-context (1M) Codex/GPT-5.x sessions, purely because num_messages > 80 (generic 400) or num_messages > 200 (disconnect) — even when approx_tokens was a fraction of the actual budget.
Repro from the issue:
classify_api_error(
FakeHTTP400(),
provider="openai-codex",
model="gpt-5.5",
approx_tokens=74320,
context_length=1_000_000,
num_messages=432,
)
# Before: FailoverReason.context_overflow (retryable=True, should_compress=True)
# After: FailoverReason.format_error (retryable=False, should_compress=False)That sent format errors into the compression/probe-down path, causing unnecessary compaction and stale handoff pollution on 1M sessions.
Fix
Apply exactly the gate suggested in the issue body: scope absolute token/message-count fallbacks to context_length <= 256000. Relative pressure thresholds (> 0.6 for disconnect, > 0.4 for generic 400) still fire on any context size.
# server disconnect path
is_large = approx_tokens > context_length * 0.6 or (
context_length <= 256000 and (approx_tokens > 120000 or num_messages > 200)
)
# generic 400 path
is_large = approx_tokens > context_length * 0.4 or (
context_length <= 256000 and (approx_tokens > 80000 or num_messages > 80)
)Existing behavior for ~128K/200K context windows is unchanged.
Tests
tests/agent/test_error_classifier.py — 4 new tests covering the 1M-context regime:
test_400_generic_1m_context_high_message_count_not_overflow— exact repro from issue (74K tokens, 432 msgs, 1M ctx) →format_error.test_400_generic_1m_context_relative_pressure_still_overflow— 500K tokens / 1M ctx still →context_overflow.test_disconnect_1m_context_high_message_count_is_timeout— 150K tokens, 300 msgs, 1M ctx →timeout.test_disconnect_1m_context_relative_pressure_still_overflow— 700K tokens / 1M ctx still →context_overflow.
pytest tests/agent/test_error_classifier.py -q
122 passed (118 pre-existing + 4 new)Changed files
agent/error_classifier.py(modified, +6/-2)tests/agent/test_error_classifier.py(modified, +62/-0)
PR #16373: fix(memory): return existing entry previews on zero-match in replace/remove
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/16373
Description (problem / solution / changelog)
Closes #16266.
Problem
memory.replace / memory.remove zero-match returned a bare "No entry matched '...'" error with no context about what entries actually exist. LLMs that paraphrased instead of substring-matching would burn a full tool round trip every time, producing the consistent [error] → retry-success pattern @ahmadhawamdah documented.
Fix
Mirror the multi-match branch behavior on zero-match: return the same 80-char truncated previews under a new existing_entries field so the next LLM call can pick a correct old_text. Symmetric in remove().
Diff
tools/memory_tool.py — replace + remove zero-match returns:
previews = [e[:80] + ("..." if len(e) > 80 else "") for e in entries]
return {
"success": False,
"error": f"No entry matched '{old_text}'.",
"existing_entries": previews,
}Additive on the error response — no schema change for callers.
Tests
tests/tools/test_memory_tool.py — 2 new tests:
test_replace_no_match_returns_existing_previews— assertsexisting_entriesshape, truncation, ellipsis bound.test_remove_no_match_returns_existing_previews— same for remove.
pytest tests/tools/test_memory_tool.py -q
35 passed (33 pre-existing + 2 new)Changed files
tests/tools/test_memory_tool.py(modified, +19/-0)tools/memory_tool.py(modified, +12/-2)
PR #14354: fix(ssh): forward skill-allowlisted env vars over SSH via SendEnv
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/14354
Description (problem / solution / changelog)
Fixes #14091
The bug
tools/env_passthrough.py already builds an allowlist of env vars that should reach sandboxed environments — populated from skill required_environment_variables frontmatter and terminal.env_passthrough config. The local and code_execution backends consult is_env_passthrough() / get_all_passthrough(), but SSHEnvironment never did.
Result: when terminal_backend: ssh, every ssh ... bash -c subprocess inherits a stripped child environment that excludes the allowlist, and the remote bash session sees no skill-declared variables — even with AcceptEnv * configured on the remote sshd.
The reporter's diagnosis is correct: the SSH command was missing -o SendEnv=NAME.
Fix
In _build_ssh_command():
- Append
-o SendEnv=<NAME>for every var inget_all_passthrough()that is actually present inos.environ. - Names are sorted for deterministic command construction (so ControlMaster connection reuse stays stable).
In _run_bash():
-o SendEnvonly forwards names; the OpenSSH client reads values from its own process environment. Pass them explicitly via a new_build_subprocess_env()so allowlisted vars are guaranteed to be present even if a future caller scrubs the parent env.
Failure to import or call get_all_passthrough() is non-fatal — SSH still works, just without forwarding (matches the existing best-effort posture in skills_tool.py).
Why this is the right layer
- Same allowlist source as
local/code_execution— single source of truth (get_all_passthrough()), so security guarantees stay consistent (Hermes provider credentials are still blocked from skill registration per GHSA-rhgp-j443-p4rf). - Doesn't bypass remote sshd policy — admins still need
AcceptEnvon the remote (the issue notesAcceptEnv *); we just stop silently dropping the names client-side. - Zero behavior change when no skill/config registers anything — empty allowlist ⇒ no
SendEnvflags, no env override.
Verification
uv run --frozen --python 3.11 --extra dev pytest -o addopts='' \
tests/tools/test_ssh_environment.py \
tests/tools/test_ssh_bulk_upload.py \
tests/tools/test_sync_back_backends.py -q
52 passed, 11 skipped7 new targeted tests in TestBuildSSHCommand:
test_no_send_env_when_no_passthrough_registered— zero behavior change when allowlist emptytest_send_env_added_for_registered_passthrough_var— the actual #14091 casetest_send_env_skips_unset_vars— allowlisted-but-unset vars don't leak as emptySendEnvlinestest_send_env_is_deterministic— sorted order for ControlMaster reuse stabilitytest_passthrough_failure_is_non_fatal— SSH keeps working if env_passthrough breakstest_subprocess_env_includes_passthrough_values— values propagated to ssh client process envtest_subprocess_env_is_none_when_no_passthrough— don't override default child-env semantics unnecessarily
Notes
- Minimal, focused diff. Uses the existing
env_passthroughinfrastructure rather than introducing a parallel mechanism. - Doesn't touch
skills_tool.py'ssetup_neededreporting — that's accurate for the registration side; the bug was purely on the SSH consumer side. - No new dependencies.
Changed files
tests/tools/test_ssh_environment.py(modified, +83/-0)tools/environments/ssh.py(modified, +47/-1)
PR #14332: fix(gateway): treat recycled PID with unreadable start_time as stale (#14176)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/14332
Description (problem / solution / changelog)
What does this PR do?
gateway/status.py::find_gateway_pids() iterates over the PIDs recorded in ~/.hermes/gateway.lock to decide whether the gateway is "still running". For each candidate it:
- Checks the PID is alive (
os.kill(pid, 0)). - Compares the recorded
start_timeagainst the live process'sstart_timeto detect PID recycling. - Falls back to
_looks_like_gateway_process(pid)/_record_looks_like_gateway(record)heuristics.
When the recycled PID is owned by a different UID (typical on Linux when /proc/<pid>/stat is owned by another user, or under rootless container setups), _get_process_start_time returns None. The recorded-vs-live mismatch check then can't fire (current_start is None), and _looks_like_gateway_process can give a false positive on any long-lived python or hermes-related process the user happens to own. Result: the gateway thinks it's still running, refuses to start, and the user has to manually rm ~/.hermes/gateway.pid to recover.
Reporter (#14176) sees this in production with a systemd user service that restarts the gateway nightly — every few weeks the next PID up the queue lands on a recycled foreign PID, the lock file goes stale, and hermes gateway start fails with "Gateway already running".
Fix: be conservative. When the PID record carries a recorded_start but we can't read the candidate's current_start, skip the candidate (treat as stale) instead of falling through to the heuristic. Outside /proc-readable territory we don't have enough information to confirm this is the same gateway process, so prefer "no" over "maybe".
Related Issue
Fixes #14176
Type of Change
- 🐛 Bug fix (non-breaking change that fixes an issue)
Changes Made
gateway/status.py(+10 / −0): in thefind_gateway_pids()candidate loop, skip any PID whose recordedstart_timeexists but whose livestart_timeis unreadable. Same code path as the existing recorded-vs-live mismatch case, just covering the unreadable variant.tests/gateway/test_status.py(+40 / −0): one new regression case underTestGatewayPidState,test_get_running_pid_treats_recycled_pid_with_unreadable_start_time_as_stale. Monkeypatches_get_process_start_timeto returnNoneand_looks_like_gateway_processto returnTrue(the strongest stress for the false-positive path) and asserts the PID file is cleaned andget_running_pid()returnsNone.
Core diff:
recorded_start = record.get("start_time")
current_start = _get_process_start_time(pid)
if recorded_start is not None and current_start is not None and current_start != recorded_start:
continue
+ # If the PID record carries a recorded start_time but we can't read
+ # the current process's start_time, the PID may have been recycled by
+ # the OS to a process the current user can't introspect (typical on
+ # Linux when /proc/<pid>/stat is owned by another UID). The downstream
+ # _looks_like_gateway_process heuristic can give a false positive in
+ # that situation — e.g. another long-lived python process — leaving
+ # a stale PID file that blocks future starts. Be conservative and
+ # skip this candidate. See #14176.
+ if recorded_start is not None and current_start is None:
+ continue
if _looks_like_gateway_process(pid) or _record_looks_like_gateway(record):
return pidHow to Test
Reporter-style repro on a Linux host:
- Run the gateway, kill -9 the parent process to leave
~/.hermes/gateway.pidand~/.hermes/gateway.lockpopulated with the dead PID. - Start a long-lived python process under a different UID (e.g. another
hermesdaemon under another account) that the test user can see viapsbut NOT via/proc/<pid>/stat. Note its PID. - Edit the lock file to point at that recycled PID, keeping the original
start_timefield intact. - Run
hermes gateway start.
Before: refuses to start with "Gateway already running". After: detects the start_time mismatch is unverifiable, treats the entry as stale, cleans the lock file, and starts a fresh gateway.
Automated regression suite:
pytest tests/gateway/test_status.py::TestGatewayPidState -qChecklist
Code
- I've read the Contributing Guide
- My commit messages follow Conventional Commits (
fix(gateway):) - I searched for existing PRs to make sure this isn't a duplicate
- My PR contains only changes related to this fix
- I've run
pytest tests/gateway/test_status.py::TestGatewayPidState -qand all tests pass (11/11) - I've added tests for my changes
- I've tested on my platform: macOS 26.5 (arm64), Python 3.11.14 via uv
Documentation & Housekeeping
- Documentation updates — N/A (internal helper, no user-visible API change beyond bug fix)
-
cli-config.yaml.example— N/A (no new config) -
CONTRIBUTING.md/AGENTS.md— N/A - Cross-platform impact considered — change is conservative on every platform; the false positive fix matters most on Linux but doesn't regress macOS/Windows behavior
- Tool descriptions/schemas — N/A
Not in scope
- The reporter's bash script idea (a dedicated
hermes gateway clean-pidcommand) — that's nice-to-have but a separate UX surface; the in-band fix here is the higher-impact change since it stops the bad state from forming. - Auditing
gateway.target/Restart=semantics in the example systemd unit (the issue's secondary note) — that's a docs change fordocs/deploy/that deserves its own PR. - Hardening atexit-vs-SIGKILL paths so a
kill -9of the gateway doesn't leave a PID file behind — a real concern but out of scope for the reported bug, which is about PID-file interpretation, not creation.
Screenshots / Logs
Verification
$ python3 -m py_compile gateway/status.py tests/gateway/test_status.py
OK
$ uv run --no-project --with pytest --with pytest-xdist --with pyyaml \
--with python-dotenv --with prompt_toolkit --with rich --with httpx \
--with fastapi --with pydantic python -m pytest \
tests/gateway/test_status.py::TestGatewayPidState -q
........... [100%]
11 passed in 0.51s(11 = 10 existing + 1 new regression case, all green.)
Changed files
gateway/status.py(modified, +10/-0)tests/gateway/test_status.py(modified, +40/-0)
PR #13957: fix(skills): raise system-prompt skill description limit to match runtime tool (#13944)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/13957
Description (problem / solution / changelog)
What does this PR do?
The skill index injected into the system prompt hard-truncated every skill description to 60 characters, while the runtime skills_list() tool (tools/skills_tool.py) allowed up to 1024. The LLM saw a vague prefix in the system prompt — where the routing decision is actually made — and only got the full description after deciding to call skills_list(). That's backwards: the trigger criteria need to be visible at system-prompt time so the model can decide whether to route to the skill.
Example before vs after:
Before: "Complete guide to using and extending Hermes Agent — CLI ..."
After: "Complete guide to using and extending Hermes Agent — CLI tooling, skill authoring, and gateway integration"Fix: introduce SKILL_INDEX_MAX_DESCRIPTION_LENGTH = 1024 in agent/skill_utils.py and use it in extract_skill_description(). Descriptions under the limit are returned verbatim; over-limit ones are truncated to exactly SKILL_INDEX_MAX_DESCRIPTION_LENGTH with a trailing "..." included in the budget (same contract the runtime tool uses).
Related Issue
Fixes #13944
Type of Change
- 🐛 Bug fix (non-breaking change that fixes an issue)
Changes Made
agent/skill_utils.py(+19 / −4): newSKILL_INDEX_MAX_DESCRIPTION_LENGTHconstant;extract_skill_description()uses it instead of the hardcoded 60; docstring updated to describe the contract + issue context.tests/agent/test_extract_skill_description.py(+68, new file): 8 regression cases covering empty / short / boundary / long-below / at-new-limit / over-new-limit / strip() preservation, plus one that locks in equality withtools.skills_tool.MAX_DESCRIPTION_LENGTHso the two paths can't silently drift apart again.
Core diff:
-def extract_skill_description(frontmatter: Dict[str, Any]) -> str:
- """Extract a truncated description from parsed frontmatter."""
+SKILL_INDEX_MAX_DESCRIPTION_LENGTH = 1024
+
+
+def extract_skill_description(frontmatter: Dict[str, Any]) -> str:
+ """Extract a (possibly truncated) description from parsed frontmatter.
+
+ Descriptions under ``SKILL_INDEX_MAX_DESCRIPTION_LENGTH`` are returned
+ verbatim. Longer ones are truncated to that length, with a trailing
+ ``"..."`` included in the budget.
+ """
raw_desc = frontmatter.get("description", "")
if not raw_desc:
return ""
desc = str(raw_desc).strip().strip("'\"")
- if len(desc) > 60:
- return desc[:57] + "..."
+ if len(desc) > SKILL_INDEX_MAX_DESCRIPTION_LENGTH:
+ return desc[: SKILL_INDEX_MAX_DESCRIPTION_LENGTH - 3] + "..."
return descHow to Test
Before:
from agent.skill_utils import extract_skill_description
desc = "Complete guide to using and extending Hermes Agent — CLI tooling, skill authoring, and gateway integration"
extract_skill_description({"description": desc})
# 'Complete guide to using and extending Hermes Agent — CLI...' # 60 charsAfter: returns the full description verbatim (108 chars).
pytest tests/agent/test_extract_skill_description.py -qChecklist
Code
- I've read the Contributing Guide
- My commit messages follow Conventional Commits (
fix(skills):) - I searched for existing PRs to make sure this isn't a duplicate
- My PR contains only changes related to this fix
- I've run
pytest tests/agent/test_extract_skill_description.py -qand all tests pass - I've added tests for my changes (8 new regression cases)
- I've tested on my platform: macOS 26.5 (arm64), Python 3.11.14 via uv
Documentation & Housekeeping
- Documentation updates — N/A (internal constant; docstring updated in-place)
-
cli-config.yaml.example— N/A (no new config) -
CONTRIBUTING.md/AGENTS.md— N/A - Cross-platform impact — string slicing, no OS-specific paths
- Tool descriptions/schemas — N/A
Screenshots / Logs
Verification
$ uv run --with pytest --with pytest-xdist python -m pytest \
tests/agent/test_extract_skill_description.py -v
........ [100%]
8 passed in 0.56sNot in scope
The issue author's long-term suggestion (prompt_builder consuming skills_tool's metadata rather than maintaining a parallel parsing implementation) is out of scope for this PR — that's an architectural refactor that deserves its own proposal and review. This PR implements the reporter's minimum fix and adds a regression guard so the two paths can't silently drift apart again.
Changed files
agent/skill_utils.py(modified, +18/-3)tests/agent/test_extract_skill_description.py(added, +65/-0)
PR #13937: fix(skills): honor platform_disabled config in gateway-built system prompts (#13851)
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/13937
Description (problem / solution / changelog)
What does this PR do?
build_skills_system_prompt() resolves the active platform via HERMES_PLATFORM (os.environ) and HERMES_SESSION_PLATFORM (contextvar). When the gateway builds system prompts on an async task that doesn't inherit the session contextvars — the common path on the Signal gateway — both lookups return empty and skills.platform_disabled.<platform> is silently ignored. Every skill ships into the system prompt, which inflates the combined system-prompt+tools payload above the ~25K-char threshold where local LLMs (gemma4:26b, hermes3, qwen3:14b, mistral-small3.1) stop calling tools and just reply with text. Hermes Agent becomes effectively unusable with local models when many skills are installed.
The fix adds an explicit platform= parameter to build_skills_system_prompt() that is authoritative for both get_disabled_skill_names() resolution and the cache key. run_agent.AIAgent already carries self.platform from the gateway constructor (AIAgent(platform=platform_key, ...) in gateway/run.py:9700), so we thread it through the only caller site.
Backward compatible: platform=None retains the existing env/contextvar fallback chain so CLI, cron, and web-server callers are unchanged.
Related Issue
Fixes #13851
Type of Change
- 🐛 Bug fix (non-breaking change that fixes an issue)
Changes Made
agent/prompt_builder.py(+19 / −3): addplatformkwarg tobuild_skills_system_prompt(); it takes priority overHERMES_PLATFORM/HERMES_SESSION_PLATFORMwhen resolving the platform hint that's passed intoget_disabled_skill_names()and used as the cache-key dimension. Updated docstring to describe the gateway contextvar-propagation problem the new parameter solves.run_agent.py(+1): passself.platformwhen callingbuild_skills_system_promptfrom the agent's system-prompt build path.AIAgentalready stores the platform onself.platform(line 821) from the gateway constructor.tests/agent/test_prompt_builder.py(+115): four new regression tests underTestBuildSkillsSystemPrompt.
Implementation diff (core change):
def build_skills_system_prompt(
available_tools: "set[str] | None" = None,
available_toolsets: "set[str] | None" = None,
+ platform: "str | None" = None,
) -> str:
...
_platform_hint = (
- os.environ.get("HERMES_PLATFORM")
+ platform
+ or os.environ.get("HERMES_PLATFORM")
or get_session_env("HERMES_SESSION_PLATFORM")
or ""
)
- disabled = get_disabled_skill_names()
+ disabled = get_disabled_skill_names(platform=_platform_hint or None) # run_agent.py
skills_prompt = build_skills_system_prompt(
available_tools=self.valid_tool_names,
available_toolsets=avail_toolsets,
+ platform=self.platform,
)How to Test
Reporter's scenario:
# config.yaml
skills:
platform_disabled:
signal:
- apple-notes
- apple-reminders
# ... 107 skill nameshermes gateway run --replace
# Send a message via Signal.Before: all 129 skills appear in the system-prompt index, combined payload ~53K chars, local LLMs don't emit tool calls. After: only the non-disabled skills appear, payload drops proportionally, local LLMs recover the ability to call tools.
Automated regression suite:
pytest tests/agent/test_prompt_builder.py::TestBuildSkillsSystemPrompt -qChecklist
Code
- I've read the Contributing Guide
- My commit messages follow Conventional Commits (
fix(skills):) - I searched for existing PRs to make sure this isn't a duplicate
- My PR contains only changes related to this fix
- I've run the scoped tests (
tests/agent/test_prompt_builder.py::TestBuildSkillsSystemPrompt) and all tests pass - I've added tests for my changes (4 new regression cases)
- I've tested on my platform: macOS 26.5 (arm64), Python 3.11.14 via uv
Documentation & Housekeeping
- I've updated relevant documentation — N/A (
platform=is an internal kwarg on an internal helper; docstring updated in-line) - I've updated
cli-config.yaml.example— N/A (no new config) - I've updated
CONTRIBUTING.md/AGENTS.md— N/A - I've considered cross-platform impact — pure Python, no OS-specific code paths touched
- I've updated tool descriptions/schemas — N/A
Not in scope
The issue also mentions platform_toolsets.signal being ignored (26 tools passed to the model instead of the configured 7). That's a separate architectural problem — platform_toolsets filtering lives in hermes_cli/tools_config.py and the gateway invocation path doesn't consult it the same way the skills path does. It belongs in its own PR with its own regression suite, and the skills fix here already delivers the 16K-char savings from the 107 disabled skills — often enough to get the payload back under the local-LLM threshold on its own.
Screenshots / Logs
Regression tests added
| Test | Scenario |
|---|---|
test_explicit_platform_param_disables_skills_for_that_platform | build_skills_system_prompt(platform="signal") excludes skills listed under skills.platform_disabled.signal |
test_explicit_platform_param_wins_over_env_vars | Caller-provided platform= overrides HERMES_PLATFORM env var |
test_platform_none_falls_back_to_env | No platform= kwarg → legacy env-based resolution still works (backward compat) |
test_platform_in_cache_key_prevents_cross_platform_leak | Back-to-back calls with different platforms return correctly filtered outputs (no cache collision) |
Verification
$ python3 -m py_compile agent/prompt_builder.py run_agent.py tests/agent/test_prompt_builder.py
OK
$ uv run --with pytest --with pytest-xdist python -m pytest \
tests/agent/test_prompt_builder.py::TestBuildSkillsSystemPrompt -q
.............. [100%]
14 passed in 0.66s(14 tests = 10 existing + 4 new regression cases, all green. No existing behavior regressed.)
Changed files
agent/prompt_builder.py(modified, +19/-3)run_agent.py(modified, +1/-0)tests/agent/test_prompt_builder.py(modified, +115/-0)
PR #18989: fix(doctor): use get_env_value for API keys and base URLs instead of os.getenv
- Repository: NousResearch/hermes-agent
- Author: shellybotmoyer
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/18989
Description (problem / solution / changelog)
Problem
hermes_cli/doctor.py uses os.getenv() to read API keys and base URL env vars, which only reads from os.environ. Values stored in ~/.hermes/.env (the standard location for API keys) are silently missed.
This causes:
- API key blindspot: doctor reports keys as "not configured" when they exist only in
~/.hermes/.env - Base URL fallback: providers with custom
BASE_URLin.env(likeDASHSCOPE_BASE_URL) fall back to wrong registry defaults, causing false "invalid key" reports
Same pattern as #18757 (fixed in auth.py) — os.getenv() vs get_env_value() inconsistency.
Fix
Replace os.getenv() with get_env_value() (with os.getenv() fallback) at 3 call sites:
- Line 998:
openrouter_key = os.getenv("OPENROUTER_API_KEY")→_get_env_value("OPENROUTER_API_KEY") or os.getenv("OPENROUTER_API_KEY") - Line 1111:
_key = os.getenv(_ev, "")in provider loop →_get_env_value(_ev) or os.getenv(_ev, "") - Line 1123:
_base = os.getenv(_base_env, "")for base URL →_get_env_value(_base_env) or os.getenv(_base_env, "")
The fallback to os.getenv() ensures values injected directly into the process environment (e.g., Docker, CI) still work.
Related
- #18757 — same fix applied to
auth.py - Skill docs note this as a known standing issue:
doctor.py line 1122 (_base = os.getenv(_base_env, "")) still uses os.getenv
Changed files
hermes_cli/doctor.py(modified, +4/-3)
PR #19021: test(credential_pool): align with .env-first seed precedence
- Repository: NousResearch/hermes-agent
- Author: Sanjays2402
- State: open | merged: False
- Link: https://github.com/NousResearch/hermes-agent/pull/19021
Description (problem / solution / changelog)
Summary
Fixes one Tests failure observed on main (and therefore propagating to every open PR):
FAILED tests/tools/test_credential_pool_env_fallback.py::TestCredentialPoolSeedsFromDotEnv::test_os_environ_still_wins_over_dotenv
AssertionError: assert 'sk-dotenv-stale' == 'sk-env-fresh-xyz'Reference run: 25250051126 on 5d3be898a.
Root cause
The test docstring says:
get_env_valuechecksos.environfirst — verify seeding picks that up.
…which was true for _seed_from_env until commit 2ef1ad280 ("fix: prefer ~/.hermes/.env over os.environ when seeding credential pool", fixes #18254). That commit introduced a private _get_env_prefer_dotenv helper and deliberately flipped the precedence:
# Prefer ~/.hermes/.env over os.environ — the user's config file is the
# authoritative source for Hermes credentials. Stale env vars from parent
# processes (Codex CLI, test scripts, etc.) should not override deliberate
# changes to the .env file.
def _get_env_prefer_dotenv(key: str) -> str:
env_file = load_env()
val = env_file.get(key) or os.environ.get(key) or ""
return val.strip()The fix was correct (#18254 reported real users hitting silent 401s with stale auth.json caches), but the unit test was never updated to match.
Fix
Rename + rewrite the test to assert the current, deliberate behaviour: .env wins over os.environ, with a docstring explaining why (stale shell env vars from parent processes shadowing deliberate .env edits, leading to cached 401s in auth.json).
The cousin Auth* tests below already exercise os.environ-first semantics for _resolve_api_key_provider_secret (which still uses get_env_value), so the two precedence policies are both pinned now.
Validation
$ pytest tests/tools/test_credential_pool_env_fallback.py -q
9 passed in 1.59sRefs
- #18254 — bug report that motivated the precedence flip
- 2ef1ad280 — the precedence flip commit
- #18757 — cousin:
os.getenv→get_env_valuefix onauth.pybase_url path
Scope
- ✅ No production code change (test-only)
- ✅ All 9 tests in the file pass
- ✅ Test name + docstring now accurately describe the contract
Out of scope
The other ~10 main-CI failures — separate focused PRs (#18972, #18974, #18977, #18979 already up).
Changed files
tests/tools/test_credential_pool_env_fallback.py(modified, +17/-5)
Code Example
XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
---
auxiliary:
title_generation:
provider: xiaomi
model: mimo-v2.5
base_url: ''
---
Title generation failed: Error code: 401 - {'error': {'message': 'Invalid API Key', 'param': 'Please provide valid API Key', 'code': '401', 'type': 'invalid_key'}}
---
# API key — uses get_env_value() ✅ (reads .env file)
for env_var in pconfig.api_key_env_vars:
val = (get_env_value(env_var) or "").strip()
# base_url — uses os.getenv() ❌ (does NOT read .env file)
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip()
---
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip().rstrip("/")
---
# auth.py — resolve_api_key_provider_credentials()
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip()
# runtime_provider.py — same change
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip().rstrip("/")RAW_BUFFERClick to expand / collapse
Bug Description
resolve_api_key_provider_credentials() in hermes_cli/auth.py resolves base_url_env_var using os.getenv(), which does not read from ~/.hermes/.env. Providers with a custom base URL stored only in .env (not exported in the shell) get the wrong endpoint — the default inference_base_url from PROVIDER_REGISTRY.
The same function correctly uses get_env_value() for API key resolution, creating an inconsistency: API keys are found, but base URLs are not.
The identical pattern also exists in hermes_cli/runtime_provider.py.
Steps to Reproduce
- Configure a provider with a custom base URL (e.g., Xiaomi with
token-plan-cn.xiaomimimo.cominstead of the defaultapi.xiaomimimo.com) - Set the base URL in
~/.hermes/.envonly — do not export it in the shell:XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 - Set the same provider as an auxiliary task provider in
config.yaml:auxiliary: title_generation: provider: xiaomi model: mimo-v2.5 base_url: '' - Start a new conversation via gateway (Telegram/Discord/CLI)
- Observe that the auxiliary task (e.g., auto-title) fails with 401
Expected Behavior
The auxiliary client should resolve XIAOMI_BASE_URL from ~/.hermes/.env via get_env_value() and use https://token-plan-cn.xiaomimimo.com/v1 — the same way it resolves XIAOMI_API_KEY.
Actual Behavior
The auxiliary client uses os.getenv("XIAOMI_BASE_URL") which returns None (env var not in os.environ), then falls back to pconfig.inference_base_url (https://api.xiaomimimo.com/v1). The request hits the wrong endpoint and returns 401.
Affected Component
- Configuration (config.yaml, .env, hermes setup)
- Agent Core (conversation loop, context compression, memory)
Debug Report
N/A — this is a code-level bug identifiable from source. No environment-specific info needed.
Operating System
Ubuntu 24.04 (ARM64)
Python Version
3.12
Hermes Version
v0.12.0 (2026.4.30) — persists on origin/main (98c98821f)
Additional Logs / Traceback
Title generation failed: Error code: 401 - {'error': {'message': 'Invalid API Key', 'param': 'Please provide valid API Key', 'code': '401', 'type': 'invalid_key'}}Root Cause Analysis
hermes_cli/auth.py L3529-3531 — resolve_api_key_provider_credentials():
# API key — uses get_env_value() ✅ (reads .env file)
for env_var in pconfig.api_key_env_vars:
val = (get_env_value(env_var) or "").strip()
# base_url — uses os.getenv() ❌ (does NOT read .env file)
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip()hermes_cli/runtime_provider.py — same pattern:
env_url = ""
if pconfig.base_url_env_var:
env_url = os.getenv(pconfig.base_url_env_var, "").strip().rstrip("/")Proposed Fix
Replace os.getenv with get_env_value in both locations:
# auth.py — resolve_api_key_provider_credentials()
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip()
# runtime_provider.py — same change
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip().rstrip("/")Same Bug Class as #15914 and #17140
This is the same os.getenv() vs get_env_value() pattern fixed in:
- PR #16101 (closed #15914) — API key + credential_pool
- PR #17434 (closed #17140) — TTS/STT tools
The base_url_env_var resolution was missed in both fixes.
Are you willing to submit a PR for this?
- I'd like to fix this myself and submit a PR
extent analysis
TL;DR
Replace os.getenv() with get_env_value() in hermes_cli/auth.py and hermes_cli/runtime_provider.py to fix the inconsistency in resolving base URLs from .env files.
Guidance
- Identify the locations where
os.getenv()is used for resolving base URLs inhermes_cli/auth.pyandhermes_cli/runtime_provider.py. - Replace these instances with
get_env_value()to ensure consistency with API key resolution. - Verify that the change resolves the issue by testing with a custom base URL set in
.envand not exported in the shell. - Review previous fixes (PR #16101 and PR #17434) to understand the context and ensure the change is applied correctly.
Example
# auth.py — resolve_api_key_provider_credentials()
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip()
# runtime_provider.py — same change
env_url = ""
if pconfig.base_url_env_var:
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip().rstrip("/")Notes
This fix assumes that get_env_value() is correctly implemented to read from .env files. If this is not the case, additional changes may be required.
Recommendation
Apply the workaround by replacing os.getenv() with get_env_value() in the specified locations, as this is a direct fix for the identified issue.
Vote matrix · Quick signals
Still need to ship something?
×6Another batch ranked right after the header list — different links, same matching logic.
TRENDING
- Feature Request: Configurable per-minute rate limiting (RPM) for models to prevent 429 errors
- Android: Hermes App + Termux install share ~/.hermes and cause silent permission loops
- hermes update emits unicode-animations ANSI demo in non-interactive logs
- hermes update downgrades aiohttp from 3.13.4 to 3.13.3
- npm install warns about deprecated @babel/plugin-proposal-private-methods
- DingTalk inbound media URLs are skipped as unreadable native image paths
- fix(dashboard): ChatPage clears header action buttons on ALL pages, not just Sessions
- [Bug]: check_web_api_key() hardcodes built-in backends — third-party web search plugins silently disabled
- Hermes Web UI 修复经验:GatewayManager 补丁、进程 D 状态、数据库升级问题
- Telegram gateway can silently drop turn after /stop with response=0 chars while internal work continues
- Bug Report: v0.14.0 上下文污染 — 历史回复碎片回注到新请求
- Bug: hermes skills search table truncates Identifier column — install fails with copied value
- [skills-index-watchdog] Skills index is stale or degraded (degraded)
- Discord approval embed not rendering on web/mobile — embed data present in API but invisible
- Idea: Discord voice-channel participation / opt-in auto-join mode
- [Feature]: Claude Code--ultrawork
- build-arm64 job deterministically fails on cold cache (Azure SAS token expires mid-build)
- [Enhancement] computer_use: action=type should fall back to key events for terminal emulators (Ghostty/Terminal.app/iTerm2)
- Feature Request: Session Recovery on Temporary Provider Outage
- [Bug]: Hermes dashboard not working on NixOS (container)
- [Feature]: Add option to ignore @all/@everyone mentions in Feishu group chats
- QQ Bot WebSocket 频繁断开:长时间工具执行阻塞 asyncio 事件循环导致心跳超时
- patch tool: new_string escape sequences (\t) get written literally
- Feature Request: i18n / 多语言支持(国际化)
- Bug: web_crawl schema lets models auto-guess "instructions" instead of asking the user via clarify
- feat: `!command` prefix for direct shell execution (like Claude Code)
- Expose currently-running cron jobs via /api/jobs (or new endpoint)
- [Bug]: Kanban parent-child handoff: scratch workspace GC destroys artifacts before child can read them
- [Bug, Windows] hermes gateway restart loses session context — planned_stop_marker not written before SIGTERM
- [Bug]: Codex→DeepSeek fallback sends assistant turns without reasoning_content → HTTP 400 (require-side cross-provider failover)
- [Bug]: Update got stuck half way, reboot it, then ModuleNotFoundError: No module named 'hermes_cli'
- Kanban dispatcher corrupt-board handling and multi-profile gateway ownership ambiguity
- Gateway can resend a short fallback message when the real final Telegram response was already delivered
- [BUG] Bedrock: Fix 'Invalid API Key format' for presigned URL tokens
- Secret redaction corrupts code syntax in tool output (write_file, execute_code, terminal)
- Unable to connect Ollama Cloud with Pro Subscription to Hermes
- feat: fuzzy substring matching for /skill autocomplete
- PRD: Autonomous market-impact prediction briefing system
- Kanban dashboard should support task/card deep links
- [Feature] Native Feishu CardKit Streaming: consolidate best-in-class implementations
- [Feature]: Inject mental model into context when using Hindsight
- Interactive CLI hides tool output despite display.tool_progress=all, and hermes chat -v does not restore it
- fix(api_server): _handle_responses drops text.format JSON schema — structured output constraints silently ignored
- state.db FTS corruption goes undetected — no integrity check, no repair path
- bug: fallback routing can select text-only models for image requests and hide the primary failure
- feat(kanban): persist worker session_id per run and pass --resume on respawn after unblock
- feat(kanban): support GitHub/OMO lifecycle bridge for Xiyou-style automation
- Expose update-safe TUI/composer hooks for voice transcript and composer events
- Hide or configure voice transcript status rows in editable dictation mode
- [Feature]: Per-Tool / Per-Toolset Approval Policies
- Context compression creates orphan sessions missing from state.db
- messaging platform
- feat: Add read-only / silent monitoring mode for WhatsApp adapter
- double-.hermes path mismatch, the HOME env var leak, and the fallback-notification UX problem
- Bug: Plattform-Bundle name `hermes-yuanbao` in `agent.disabled_toolsets` silently kills ALL tools in gateway path (Telegram + cron), CLI unaffected
- CLI /yolo (in-chat) does not bypass dangerous command approvals — env var freeze + missing enable_session_yolo call
- OpenAI Codex provider crashes with "'NoneType' object is not iterable" (HTTP None)
- DEEPSEEK_API_KEY blocked by env blocklist in gateway process — cron jobs fail with deepseek provider
- fix(feishu): Card action callback routing issues - invalid message_id and unrecognized /card command
- Discord plugin: profiles without explicit `discord:` block silently get `require_mention=true` + `auto_thread=true` (regression in cc8e5ec2a)
- [Bug]: DISCORD_ALLOWED_ROLES ignored by gateway _is_user_authorized — role-authorized users get 'Unauthorized user' rejection
- [Bug]: /new, /clear, and /reset commands freeze the terminal session
- openai-codex subscription backend returns HTTP 200 with response.output=None, causing Slack/cron failures
- RFC: Centralized Model/Provider Registry
- bug: openai-codex provider — TypeError: 'NoneType' object is not iterable on every request (gpt-5.5)
- [Feature]: Source-aware instruction gate — architectural mitigation for indirect prompt injection
- Named custom provider stale_timeout_seconds ignored because runtime provider is normalized to `custom`
- guard test (ignore)
- [Feature]: per-platform LLM request_overrides (extra_body / reasoning_effort / service_tier)
- One-shot smoke: add Flue-backed orchestration fixture
- Gateway should not treat stale Codex app-server progress as final response after post-tool silence
- `docker_run_as_host_user: true` breaks bundled skills: Hermes home is mounted into `/root/.hermes` but the container runs as a non-root user (`HOME=/home/pn`)
- [Bug]: gateway api_server streaming bypasses server-side tool-call loop when chat_template_kwargs.enable_thinking=false (model emits tool name as plain text)
- [Feature]: Pre-install python-telegram-bot in Umbrel Hermes Docker image
- YouTube Shorts filter not working in youtube-content skill
- v0.15.0 PyPI release breaks ALL platforms — plugin.yaml manifests missing from package
- RFC: On-demand tool/skill/MCP discovery — decouple schema registration from process lifecycle
- Pixshelf: local-first stock photo workflow command center
- [Bug]: baoyu infographic skill should not silently bypass image_generate
- Pixshelf v1.5: manual submission tracking for stock agencies
- `hermes config set` silently accepts unknown keys, writing them where the runtime never reads
- Honcho memory prefetch hang on fresh CLI subprocess in v0.15.0 (regression from #27190)
- [Bug] v0.15.0 Docker image: stage2-hook.sh, main-wrapper.sh missing; container_boot module removed
- Feature: Reduce cache-read token overhead for DeepSeek providers — configurable cache_ttl, skills snapshot trimming, memory compaction
- Windows: three bugs from daily use (plugin discovery, gateway exit code, Unicode decode
- holographic memory: HRR silently degrades to FTS5 when numpy is missing
- Make max_tokens configurable for aux vision calls
- Conversation compression desynchronizes session ID between agent context and gateway routing, causing silent message loss
- [Bug]: v0.15.0 Docker image:The TUI cannot be used in the dashboard.
- cron: skip_memory=True blocks fact_store/memory tools from all cron jobs
- TUI: Node.js OOM crash when agent uses browser tools repeatedly
- feat: model_profiles — per-model toolset and memory config
- Automatic background skill patching disrupts active sessions (severe impact on local models)
- ensure_hermes_home() creates root-owned dirs in profile subdirectories when kanban workers are dispatched
- Feature: opt-in webhook bypass for DISCORD_ALLOW_BOTS — allow operator-initiated probes without weakening bot-loop guard
- v0.15.0: Codex requests fail HTTP 400 when participant display_name contains non-ASCII (emoji breaks input[].name pattern)
- Architecture: State Persistence Precedence (Memory vs Skills vs Hooks)
- [Bug]: cronjob tool: create action always fails with "schedule is required for create" even when parameters are provided
- codex-oauth: 'NoneType' object is not iterable in _run_codex_stream (gpt-5.5) — every turn fails non-retryably
- Docs/Config: Plugin local scope enablement ambiguity
- [Bug]: CLI freezes after using /new command (WSL)
- Profile Codex auth can ignore global credential pool when local state is stale
- [workflow-engine] CRITICAL: variable substitution crashes on regex metachars in user input
- [workflow-engine] HIGH: loop and bash nodes leak subprocesses on timeout
- [workflow-engine] HIGH: README documents config env vars the engine never reads
- [workflow-engine] MEDIUM: workflow_run rate limit bypassable via concurrent calls (TOCTOU)
- [workflow-engine] chore: manifest gaps, side-effectful register(), dead code, unauth kanban dispatch
- [mcp_lazy] HIGH: synthetic mcp_server_<name> stub collides with a real MCP server named 'server'
- [mcp_lazy] HIGH: promote_server eager flag documented but never persisted
- [mcp_lazy] MEDIUM: _prev_mode dict leaks and goes stale; not cleared on session evict
- [mcp_lazy] MEDIUM: get_pool has unlocked check-then-set race on pool creation
- [mcp_lazy] MEDIUM: pre_tool_call gives no guidance for unpromoted server-stub calls
- [mcp_lazy] chore: undeclared pre_tool_call hook, nonexistent 'mcp_load_tools' name in docs, missing tests
- [a2a_fleet] CRITICAL: server never auto-starts — register() runs outside an event loop
- [a2a_fleet] CRITICAL: auth_required defaults to false on a cross-machine surface
- [a2a_fleet] HIGH: remove invented disable() hook — loader never calls it, port leaks on reload
- [a2a_fleet] HIGH: plugin.yaml missing kind / provides_tools / requires_env (token env undeclared)
- [a2a_fleet] MEDIUM: tighten wide-open CORS, anonymous /health peer leak, and peer-URL SSRF
- [a2a_fleet] MEDIUM: relocate tests to tests/plugins/ and cover sync-register + auth-default paths
- xai-oauth auxiliary client incorrectly uses Responses API (CodexAuxiliaryClient), causing 403 on compression/vision/web_extract
- [Bug]: Direct Copilot gpt-5.5 large resumes are killed by 12s Codex TTFB watchdog
- [Bug]: `hermes uninstall` does not work on Windows
- TUI: Thinking block leaks raw JSON and Σ character
- Hostinger VPS: migration Hermes Agent → Hermes WebUI impossible (tini + UID mismatch + sessions)
- /goal judge over-continues exploratory goals unless the assistant explicitly says the goal is complete
- /goal auto-continuation can be amplified by preflight compression/session split and resurrect stale task state
- Dashboard infinite reload loop in loopback mode — GET /api/auth/me returns 401 on every page load
- [Bug]: Provider/LLM switch leaves stale encrypted_content causing 400 errors on Telegram sessions
- [Bug]: Infinite reload loop / React state loop on Sessions tab (Firefox + Chrome) — repeated 401 on /api/auth/me (v0.15.0)
- show_reasoning should work independently of streaming in CLI mode
- Feature Request: Strip reasoning/<think> blocks from TTS preprocessing
- mcp add / mcp test raise NameError when mcp package not installed
- v0.14.0 dashboard breaks behind reverse proxies — two regressions
- Skills hub creates empty category directories when no skills installed
- [Bug]: Custom endpoint: ChatCompletions returns content, but Hermes treats response as empty (v0.14.0)
- fix: atomic_replace() fails with EXDEV when HERMES_HOME is a cross-filesystem symlink
- fix(gateway): Feishu session cancellation orphans session guard, permanently blocking messages
- Custom endpoint pricing can overestimate Crof qwen3.5-9b cost by 1,000,000x
- MCP OAuth callback: module-level port global causes port collisions and structural weaknesses vs upstream
- Bug: send_message tool bypasses validate_media_delivery_path security check
- Proposal: Add Mnemosyne to official memory provider documentation
- feat(swarm): support custom verifier/synthesizer body + skills
- Template conversion failed
- Error occurred in the operation of the agent node in the workflow.
- PubSub client overrides Sentinel client when REDIS_USE_SENTINEL is enabled
- Frontend description of the Retrieval node output does not match the actual output
- JSON type input var raise Intenal server error
- cannot extract elements from a scalar
- 负载均衡 为模型配置多组凭据,并自动调用,此功能无法选择
- add models is error
- panic: could not create filter
- Persist partially generated messages when /chat-messages/:task_id/stop is called
- MCP server connection fails with 403 — request never leaves Dify (SSRF proxy suspected)
- Support durable async execution backends for long-running workflow steps
- [Xiaomi MiMo] Credentials validation fails with 400 "Not supported model mimo-v2-flash" when using Token Plan endpoint (v0.0.7)
- After clicking preview on a parent-child segmented knowledge base, it shows 0 chunks
- Retrieval score differs between UI upload (.docx) and API upload (.txt) despite identical chunk content and embedding model
- gemini cli crash again
- Xbox gift card code damage
- Damage caused by the gemini cli crash
- ioctl(2) failed, EBADF (Bad File Descriptor)
- Feat: Support Bun as an alternative runtime/package manager for updates and extensions
- fatal error again!!!!
- ioctl error
- Critical Crash: ioctl(2) failed, EBADF in ShellExecutionService.resizePty
- ioctl(2) failed, EBADF
- v0.44.0 Regression: Critical crash with ioctl(2) failed, EBADF during PTY resize
- Crash on startup: ioctl(2) failed, EBADF in UnixTerminal.resize
- Crash: `ioctl(2) failed, EBADF` in `node-pty` during PTY resize on macOS
- Gemini CLI crashes with `ioctl(2) failed, EBADF` in `node-pty` during `resizePty`
- Remote Role
- ERROR ioctl(2) failed, EBADF /home/mich
- RangeError: Maximum call stack size exceeded
- EBADF Error during folder creationg broke session and terminal glitches
- MAIP / Gargoub Project - Mediterania - North Coast
- Gemini cli crash again in this morning
- ERROR ioctl(2) failed, EBADF
- Verified node install fails — Checksum verification failed (Cloud)
- The extended debugging key did not arrive during registration.
- CollaborationPane unmounts collaboration store on single-user instances, causing permanent "No network connection" state
- Workflow cannot be saved when the name contains "->" (Potentially malicious string)
- automation does not work and does not show an error
- Raj Ai Automation
- Default Data Loader: DOMMatrix is not defined error
- Feature: Per-node execution timestamp overlay on canvas during workflow run
- AI Agent + Vertex `gemini-3.5-flash`: 400 "missing thought_signature" on sequential multi-turn tool calls (post-#24982)
- PDF Loader in Pinecone Vector Store fails due to pdf-parse version conflict (v2 not supported)
- emailReadImap: add UID deduplication, batch size cap, and numeric uid enforcement
- Manual node execution fails with "Could not find a node" when autosave is disabled (N8N_WORKFLOWS_AUTOSAVE_DISABLED)
- Schedule Trigger stopped firing — workflow Published & active, manual executions succeed, no automated fires for 2+ hours
- [MCP SDK] create_workflow_from_code intermittently returns HTTP 500, often as a false negative (workflow persists anyway, causing duplicates on retry)
- Credential-load wedge: workflows using googleApi/jwtAuth credentials silently fail to execute after key rotation
- Google Sheets Trigger every minute is not working manual Execute is working sent email
- [BUG] Plugin marketplace MCP connector remains stuck "still connecting" when mcp-remote requires OAuth
- [redacted at user request]
- Opus 4.7 behavioral regression: loaded instruction-following discipline degraded in recent Claude Code/Cowork updates
- [BUG] Tailscale via Homebrew CLI + Mac App Store GUI, both Macs on macOS, Cowork blocked by VPN detector despite Tailscale being a mesh VPN with no traffic interception
- stopShellPty on tab switch kills active sessions (exit 143) — regression in May 27 build
- [BUG] Long URLs are broken into multiple lines and become unclickable in terminal output
- [BUG] claude rm/stop/reap SIGKILLs background session tree without SIGTERM grace, orphaning git index.lock and similar
- [BUG] Default git workflow in the system prompt was pushed without context or consent
- [MODEL] Inconsistent output quality / Ignoring instructions (overfitting and inappropriate repetition of Korean vocabulary)
- You've hit your weekly limit · resets May 31 at 5pm (Asia/Shanghai)
- Paid yearly subscription silently downgraded to Free with no user action
- [Regression v2.1.153] Plugin bash hooks fail with "echo: write error: Permission denied" on Windows (claude-mem, shell: "bash")
- [BUG] Connector toggles in conversation are not clickable — must click text label instead
- [remote-control] Input from mobile app/browser not reaching host session — output works fine
- Model fails to read/reference CLAUDE.md contents despite being loaded in context
- [BUG] Claude Desktop reinstall destroys Code chat history (transcripts + Recents) while regular Chat history, project files, and memory all survive
- Bypass mode clamps to Accept Edits even with the toggle ON (Claude Code Desktop 1.9255.2 / CC 2.1.149)
- [BUG] TUI input freezes randomly mid-typing — entire prompt becomes unresponsive for minutes
- [BUG] Cowork downloads Linux ELF binary instead of macOS binary on macOS Sonoma 14.8.7 — exit code 132 (SIGILL) on every session
- [Feature Request] Persistent project memory — sessions forget everything on close, forcing users to keep many sessions open
- [Bug] Thread context stale after sleep/resume, returns outdated date and calendar data
- [FEATURE] Add context window usage indicator and warning before auto-compaction
- [BUG] Dictation error: Invalid character in header content ["x-config-keyterms"] on Windows
- [Bug] Anthropic API Error: Server rate limiting despite normal usage
- Does delegating work to `claude -p` subprocesses reduce context accumulation in the parent session?
- [BUG] Claude Code hangs on M1 Mac when terminal says "opening browser to sign in" and browser opens
- [BUG] Claude_Preview MCP preview_start spawns dev server with main-repo cwd instead of session's worktree cwd
- [Bug] Anthropic API Error: Server rate limiting during request execution
- [Bug] Anthropic API Error: Server rate limiting on concurrent requests
- [Bug] Ultraplan ready notification fires before cloud agent completes execution
- [BUG] API 500 ERROR ALL THROUGHOUT THE DAY
- [BUG] Cowork: Live Artifacts folder path changed in 1.9255.2, no automatic migration from Documents\Claude\Artifacts
- [Bug] Auto-compact never triggers despite statusline reporting "100% context used" (v2.1.153, Max sub, 200K mode)
- [BUG] [Desktop / macOS] 'Open in → New Window' detached session: font renders smaller than main, no per-window controls, Cmd+/Cmd- keystrokes routed to main window instead
- Feature request: option to switch between classic and new minimal UI
- [Feature Request] Show timestamps for each message
- [BUG] Terminal corruption when permission prompt appears while navigating Agent Teams agent selection menu
- [FEATURE] Allow users to customize the background color of the Claude desktop app beyond the current light/dark theme presets.
- [BUG] Statusline not displaying on Windows [fixed]
- Background agent UI Stop button is a no-op for stuck agents — process keeps consuming tokens
- Background agents silently die on session pause/resume — no completion notification, no work recovery
- Add option to hide email address from welcome banner
- [BUG] SSH Remote: `projects` field in remote ~/.claude.json becomes null after desktop restart — jsonl files intact, UI shows 'No messages yet' for every session
- [Bug] Claude Code not applying fixes despite claiming to complete tasks
- billing is unfair and poorly documented
- [BUG] Claude Code on the web: declared plugins inactive on first session, require restart to fully load
- [BUG] Restore from archive deleted sessions instead of restoring them
- [BUG] M365 connector fails with AADSTS50011 in Cowork — localhost vs 127.0.0.1 redirect URI mismatch
- claude agents: workflow slash-commands missing from dispatch-input completion (regression-adjacent to #61424)
- Claude Desktop's Info.plist missing TCC usage strings, blocks all EventKit-based MCP servers
- False-positive safety blocks on self-administered governance amendments — request for owner-authority mode for verified professional users
- [BUG] Stop pushing "AUTO"-mode
- [DOCS] Plugin marketplace guide omits `skipLfs` option for git-based sources
- [DOCS] MCP docs omit combined startup notification for MCP server and connector authentication
- [DOCS] Agent view docs omit macOS Privacy & Security identity for background agents
- [DOCS] Npm update docs do not explain release-channel behavior for `claude update`
- [DOCS] Agent SDK docs omit `subagent_type: "claude"` worktree and output persistence behavior
- [DOCS] Background session docs omit `$CLAUDE_JOB_DIR` temp-file behavior
- [FR] mask env-var values in 'claude mcp get <server>' output
- [FR] subagent worktrees should not inherit stale local 'user.email' from prior dispatches
- [BUG] Windows: Grep tool leaks rg.exe + conhost.exe processes (~2000 zombies / 14 GB RAM in long sessions)
- [BUG] Stats dashboard "Peak hour" appears off by one hour
- [BUG] Diff highlight (teal SGR background) bleeds past changed text in 2.1.150–2.1.153
- [FEATURE] confirm before deleting session
- Plugin PostToolUse hooks still silently skip in Claude Desktop / Cowork (re-filing closed #51904)
- /code-review skill: silent fallback to main...HEAD reviews other people's commits, and JSON-only output is hard to read
- Monitor tool doesn't source the shell snapshot like Bash does; PATH-dependent tools (jq, sleep, etc.) fail in Monitor commands on macOS/Nix
- [Bug] Long input lines truncated with ellipsis while typing instead of wrapping in terminal UI
- [FEATURE] VS Code extension: Render submitted user messages as Markdown in chat
- OSC 52 copy from Claude TUI doesn't reach clipboard inside tmux (regression in 2.1.146–2.1.153)
- [BUG] RemoteTrigger create/update returns HTTP 400 with circular error: "event_type is required" / "unknown field event_type"
- [BUG] Option to hide or minimize the built-in "status footer" (multi-line debug/cost panel) [re-raise of #31475]
- [Bug] Feedback submissions being closed without review or action
- [FEATURE] Word-jump cursor navigation in Chat input (option+arrow / bindable actions)
- [FEATURE] ! shell mode: filesystem tab completion
- [BUG] API Error: Usage credits required for 1M context
- claude agents: OSC 52 clipboard emission broken in tmux (regression in 2.1.146–2.1.153)
- CLI crashes on macOS 15 M3 - exit code 1
- [FEATURE] Support Cmd+V image paste from clipboard
- [FEATURE] Enhance claude.ai M365 connector to support MS Planner
- [BUG] Slash command autocomplete hijacks pasted absolute file paths starting with /
- PreToolUse hook `if` filter false-positives on complex Bash commands
- [BUG] Diff panel hangs/whites out
- Feature Request: Support drag-and-drop for binary documents (.wps, .doc, .docx, .xlsx, .pdf) in VS Code extension
- [BUG] activation of 1M context in VSCode
- [FEATURE] Support i18n / language localization for built-in slash command outputs
- Ctrl+V para colar imagens deixou de funcionar no CLI (Windows, PowerShell)
- [FEATURE] Please add Norwegian (Bokmål/Nynorsk) language support to the Claude Code interface
- [BUG] OTel log events (claude_code.user_prompt, api_request_body, tool_decision, hook_execution_complete) emitted with empty trace_id/span_id while sibling spans correlate correctly
- [BUG] Cowork crashes on every message, no VM logs generated, missing AppData\Roaming\Claude
- [FEATURE] first-class session handoff + per-session token budgets for unattended runs
- [FEATURE] Smart paste: convert clipboard code to file reference chips (like Cursor)
- [Feature Request] Restore chat pin functionality to title chat submenu
- [BUG] SIGILL issues with version 2.1.153
- [BUG] Cowork plugin upload fails with generic "Plugin validation failed" when a `description` field in any SKILL.md frontmatter contains angle brackets (`<…>`)
- [BUG] Desktop App 2.1.144+: startup scanner deletes cliSessionId from claude-code-sessions local files on every launch — session not found on disk
- [Feature Request] Add keyboard shortcut to copy last message with proper formatting
- [MODEL] Opus 4.7 not 1M
- Allow naming/renaming background agents in `claude agents` view
- Stale worktrees in .claude/worktrees/ are never cleaned up, consuming massive disk space
- Agent worktrees are never cleaned up, silently consuming disk space
- Subagent worktrees not auto-cleaned when reviewer writes scratch files
- [Bug] Skill initialization hangs for extended duration in Plan Mode
- Claude Desktop writes malformed registry Run entry (nested escaped quotes) - crashes Windows Task Manager and other Run-key parsers
- IME candidate window shows at bottom-right corner instead of caret position (Windows CMD)
- [BUG] Pressing 'Escape' doesn't close the /BTW conversation when the main conversation is asking for approval
- [BUG] Opus 4.7 (1M) intermittently emits empty-string values for tool_use.input fields, killing the session
- FleetView agent UI shows "running" with incrementing elapsed time after agent has returned
- /doctor flags context-scoped cmd+c binding as macOS conflict (false positive)
- [BUG] Text Rendering in Elvish
- Desktop app: Bypass Permissions mode flips to Accept Edits on first prompt (M5 / macOS 26.5)
- [Workaround] Date-Weekday Verification Hook — Prevents Claude from writing wrong weekdays
- [BUG] Claude Code create c:/memfs directory without asking me.
- [BUG] Claude Code's Bash execution waits forever with no processes running
- [BUG] usage stays stuck waiting for 5 hr limit after upgrading to premium seat in team plan
- [Workflow tool] resume cache is unreachable for nontrivial workflows because LLM dispatchers can't transcribe args byte-exactly
- Code review (Preview): "Add a repository" shows no results for private GitHub org repos
- [BUG] /context commands blows up context
- [Feature Request] Add precache expiry hook to enable proactive compaction before token eviction
- [BUG] Context indicator shows 0% at session start despite ~20K+ tokens already loaded
- [Feature Request] Add semantic search for --resume session history
- [Feature Request] Add session search, tagging, and filtering capabilities
- [BUG] Cowork Dispatch reports "desktop not available" on Windows 11 while standard Cowork works normally
- [Bug] Claude Code provides incorrect suggestions with high confidence despite errors
- defaultMode: acceptEdits silently overrides per-path permissions.ask rules for Write/Edit
- [FEATUR configurable tip interval (e.g. tipIntervalSeconds: 30 in settings)E]
- Plugin marketplace fails to load: schema rejects 'displayName' key (v2.1.153)
- claude agents: in-session copy uses broken OSC 52 path while overview correctly uses tmux buffer
- [BUG] Plugin agent descriptions (and custom agents) load unconditionally into context — no parity with disable-model-invocation for skills
- Crashed ultrareview consumed a free credit despite producing zero findings
- [Bug] Character rendering issue - invisible or missing text display
- [BUG] Cowork: processo Claude Code encerra com código 3 — .claude.json não contém token de autenticação (Windows 11 25H2)
- [BUG] 2.1.153 silently discards tools/list response from rmcp 0.12.0 HTTP MCP server (works in 2.1.152, wire-identical handshake)
- VS Code extension: option to auto-resume last session when reopening a workspace folder
- [Bug] Conversation continuation failure
- [BUG] Cowork crashes every time I start a new chat or attempt to continue an existing one in any project. The error displayed is: "Claude Code è andato in crash
- [Bug] Unannounced quota changes
- Native update/install fails with 'socket connection was closed unexpectedly' behind proxy — undici TLS incompatibility
- [BUG] Session name reverting after manual change
- [BUG] 非正常思考,上下文过长时,一直显示思考,点击interrupt按钮失效
- Honor `tools:` frontmatter when an agent is invoked via `@mention` — strip `Task` only when the agent did not declare it
- macOS TCC popup still recurring on v2.1.153 — "2.1.153" would like to access data from other apps
- Claude Code leaks pty handles — exhausts pseudo-terminals on macOS after long session
- [Bug] Agent fails to execute or respond to user input
- [BUG] Persistent "Expecting value: line 1 column 1 (char 0)" JSON parse error after tool execution
- [Feature Request] Implement proactive unit test coverage recommendations for recurring bugs
- VS Code panel lacks status line + terminal lacks image paste in Codespaces, forcing a tradeoff
- `/powerup` only shows ~10 lessons — allow viewing the full catalog
- [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin
- [Bug] VSCode terminal output displays corrupted text with garbled symbols
- [Feature Request] Add LaTeX/KaTeX math rendering to TUI
- [Bug] Sub-agent PR review results not validated by orchestrating agent
- Subagents on Pro 1M tier: trivial probes pass, real workloads fail at first tool call (probe-vs-workload divergence)
- Path-scoped rules and subdirectory CLAUDE.md not loaded when creating new files matching the pattern
- AskUserQuestion: cancelling during extended thinking poisons the whole session with 400 'thinking blocks cannot be modified' (2.1.153); concurrent prompts overwrite each other
- Ideas Missing from Claude Cowork Menu (Windows)
- [BUG_BOUNTY_SAFE_POC_2026] Prompt Injection RCE Test - Command Execution Proof
- [BUG] Cowork scheduled task: execution history row not showing after successful run
- Resuming an extended-thinking session fails permanently with 400 "thinking blocks cannot be modified" (transcript stores thinking text as empty but keeps signature)
- [Bug] Plugin-registered CwdChanged and FileChanged hooks don't fire (settings.json works) — v2.1.153
- Auto-archive on PR merge / branch delete — clarify autoArchiveSessions semantics or add dedicated opt-out
- `claude mcp add` echoes Authorization header value verbatim to stdout, leaks bearer tokens to terminal and session transcripts
- [BUG] Bug report — /insights skill, Claude Code The /insights skill outputs a malformed file path.
- Plugin slash commands render with '*'-inline format instead of two-column, despite matching official plugin shape
- [Bug] Unexpected long text generation without user input or goal
- [Bug] Thinking blocks causing task progression blocked without user modification
- [BUG] (Critical!) contamination by an unknown session simirlar to the report => [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin #63137
- [Critical] Opus 4.7 Korean output degeneration — Korean grammar itself collapses in long contexts
- [BUG] Title: Autocompact buffer persists across /clear — wastes tokens for irrelevant old context
- [Bug] Auto-Compact loses user input before processing in conversation history
- Feature: per-invocation effort parameter + runtime session-config introspection for skills
- Auto-mode classifier mislabels Azure DevOps vote -5 as "Reject" when denying PR vote actions
- [BUG] Claude Desktop and Claude Code CLI never re-register MCP tools after OAuth 2.1 handshake on a remote HTTP server
- [BUG] Workspace file tags leak across sessions
- [BUG] Ink renderer crashes on Windows 11 build 26200 (Canary) duplicate banners, terminal mode leaks, mid-operation aborts
- [BUG] Claude Code Desktop issue
- PTY master fd leak in Claude desktop app exhausts macOS kern.tty.ptmx_max after ~2-3 days
- [BUG] Claude Code — Session Management after Unexpected Interruption
- [Windows] Cowork OpenTelemetry exporter does not initialize - zero events emitted to any destination, including loopback
- [Bug] Opus 4.7: 400 `thinking blocks ... cannot be modified` on long extended-thinking sessions, triggered by history-altering events (scheduled prompts / parallel tool-call cancellation)
- [BUG] API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited
- Multi-plugin custom marketplace: only first plugin registered in installed_plugins.json, skills don't load
- [BUG] Git push through the SDK's git proxy fan-outs into ~500 GitHub REST API calls, exhausting the 5,000/hour budget after a handful of pushes
- [BUG] Claude took liberties it really shouldn't with my global config
- [BUG] Agent window focus lost after navigating with arrow keys, causing scroll deadlock
- [BUG] `--model` flag silently ignored in interactive sessions (works in `--print` only)
- [BUG] Dispatch permanently shows "desktop appears offline" on Windows 11 - never worked on first use
- feat: support per-command enableWeakerNetworkIsolation as safer alternative to dangerouslyDisableSandbox
- /code-review outputs a raw JSON array instead of readable findings
- [BUG] Cowork — Additional allowed domains ignored on Team plan; same domain works on Pro plan
- Haiku
- [Bug] False positive blocking beneficial outcomes in tool execution
- 3P Bedrock SSO: credentials silently expire without triggering re-auth on day 2+
- CLAUDE_AUTOCOMPACT_PCT_OVERRIDE in settings.json env block silently ignored by autocompact logic
- Auto-compaction deletes main session JSONL before verifying summary completion, causing data loss
- [Bug] Claude Code not executing stated actions or producing expected results
- [FEATURE] Deferred Messages — Queue Input for End of Turn
- [BUG] Up/Down arrows in input box navigate history instead of moving cursor — regression in 2.1.149+
- Cancelling a parallel tool-call batch corrupts thinking blocks -> 400 "thinking blocks cannot be modified" permanently wedges the session
- Claude Code caused data loss, then contradicted itself about recovery (two incidents, one session)
- [Bug] Unclear error messages from Claude Code CLI
- [Bug] Agent tool rejecting due to context size limit exceeded
- claude agents: daemon and bg-spare processes spin at ~100% CPU when idle
- [BUG] Compaction fails with "context window limit" error even when context usage is low (e.g., 20%) — regression in v2.1.153
- Remote Control entitlement lost after May 27-28 incident — `Error: Remote Control is not yet enabled for your account` on active Max subscription
- PreToolUse hook exit code 2 does not block Write tool
- [Bug] Thinking blocks in latest assistant message are immutable
- GUI: dispatch file:// and custom-scheme clicks to OS shell handler
- Show current model in statusLine by default
- [Bug] Agent console becomes unresponsive to keyboard input after multiple agents initialized
- [FEATURE] PreToolUse hooks should have a way of updating the environment
- [Bug] Unable to start or use Claude Code CLI
- [BUG] Repository not visible in Claude Code web repo picker
- Session permanently wedged on 400 "thinking blocks cannot be modified" after parallel tool_results
- [Bug] @ autocomplete loses sibling repos after a file edit in multi-repo workspace
- Unclear error message when creating sub-agent without authentication
- [Bug] Anthropic API errors causing frequent failures and high token usage
- [BUG] @ mention file picker only shows packages, not individual files (desktop app - Code tab)
- [Bug] TUI panel footer remains sticky and consumes excessive terminal space
- PR-status polling exhausts GitHub GraphQL rate limit on repos with many open PRs
- [BUG] Windows: welcome panel not shown in some project folders (2.1.153)
- [Bug] Anthropic API Error: thinking blocks corrupted during context compaction with extended thinking enabled
- API 400 "thinking blocks cannot be modified" permanently bricks session during agent activation (interleaved thinking + tool use)
- Right-click Copy copies the whole message instead of the selection; pasted text retains dark background
- Mid-session model switch corrupts conversation when extended thinking is enabled (API 400: 'thinking blocks cannot be modified')
- [BUG] Markdown file links in chat output do not open files when clicked (VS Code extension)
- Stuck retry loop: `400 thinking blocks cannot be modified` on large interleaved-thinking turns using AskUserQuestion
- [FEATURE] Prompt user for approval before auto-compaction proceeds
- Custom MCP connectors not attachable to scheduled routines — no UUID discovery path
- [BUG] Claude in Chrome — Navigation blocked for teams.cloud.microsoft and outlook.cloud.microsoft after Microsoft domain migration**
- [BUG] Claude Desktop — Personal plugins panel renders list but is entirely non-interactive (macOS, v1.9255.2)
- [Bug] error when using Workflows
- [BUG] Persistent "update available" notification despite being on latest version
- [BUG] Sweep Agent from /code-review never completes
- [Bug] Tool calls not executing or returning results
- [FEATURE] Cloud-synced memory and settings across machines
- [Bug] Terminal UI freezes when Ctrl+O view exits during interactive prompt in plan mode
- Continuous api errors when using claude code with Opus 4.7 with thinking on low
- [Feature Request] Add support for installing and using previous Claude Code versions
- [Bug] Extended Thinking: Summarized thinking blocks fail signature validation when resent to API
- [Bug] Anthropic API Error: 'thinking' blocks cannot be modified
- [Bug] Anthropic API Error: Thinking blocks cannot be modified with extended thinking mode
- Feature request: Lazy/on-demand MCP server connections
- [Bug] Tool Arguments Parsed as String Instead of Object
- [Bug] Anthropic API Error: Insufficient context provided
- [Bug] Claude Opus occasionally uses moskovian(russian) orthography instead of Ukrainian in system-prompted responses
- Opus 4.8: backgrounded task completions (subagents AND Bash) crash with 400 "thinking blocks cannot be modified"
- [Bug] Opus 4.7 fabricates stable preferences ("my default") to rationalize arbitrary choices when challenged
- [Bug] Unable to update Claude Code CLI
- [BUG] Desktop app: /remote-control mints link + connects bridge (main.log) but in-chat link/QR panel never renders
- Feature: sessionColor and sessionName in .claude/settings.json
- [BUG] Anthropic API error: thinking blocks
- [FEATURE] Support Remote MCPs in Cowork as in Claude Code
- [Bug] Anthropic API Error: 400 Bad Request with Redacted Thinking - 0 4.7 & 4.8
- [Bug] Anthropic API Error: Cannot modify thinking blocks from different model versions
- Interleaved thinking + multi-tool turn corrupts thinking block (text blanked, signature kept) → permanent 400 'blocks must remain as they were'
- [BUG] Mode/permission changes mid-tool-loop (effortLevel: xhigh) poisons entire session
- Session failure log: Opus 4.6 ignores its own rules for an entire session
- [BUG] "400 Guardrail was enabled" error when using Claude Opus 4.8 with AWS Bedrock
- [Feature Request] Add subagent approach selection option to avoid accidental feedback
- Persistent 400 'thinking blocks in the latest assistant message cannot be modified' — interleaved thinking persisted with empty text + signature bricks sessions
- [BUG] DesktopvsApp
- [BUG] Opus 4.7 cache hit rate collapse after May 27 incident — Messages 1.1k→88.9k in 9 minutes, $630/session
- [Bug] Anthropic API Error: Invalid thinking block format
- [BUG] FUCK CLAUDE
- Opus 4.8 extended thinking: Stop hook block re-entry corrupts thinking blocks → 400
- [Bug] 4.8 Fails when accessing previous model history
- [Bug] Unintended File Modifications During Execution
- [DOCS] Model configuration docs omit lean system prompt default scope and model exceptions
- Add "Always allow globally" option to permission prompts
- Server-side model upgrade (Opus 4.7→4.8) wedges in-flight sessions with `thinking blocks cannot be modified` 400
- [DOCS] AskUserQuestion docs missing multiple-choice prompt decision threshold
- [DOCS] Agent view docs omit shell-command background session launch syntax
- [DOCS] Agent view dispatch input docs incorrectly imply `/logout` dispatches as a prompt
- [DOCS] Claude in Chrome docs omit connected-browser selection behavior
- [DOCS] Plugin docs omit `defaultEnabled: false` for opt-in plugins
- Feature Request: Customizable chat text colors for user and assistant messages
- [DOCS] `/plugin` Discover tab docs omit directory-based suggested plugin pins
- VSCode Chrome integration silently fails: 3 distinct bugs
- [DOCS] MCP stdio docs omit session environment variables
- [Bug] Anthropic API error on second request within session with Claude Opus 4.8
- Cowork emits a blank session "index" handoff on focus when a CLI session is paused awaiting input
- [DOCS] MCP docs omit `claude mcp list/get` pending-approval output for unapproved project servers
- [BUG] /compact fails with 400 error when last assistant turn contains thinking blocks
- [DOCS] `/claude-api` docs omit Opus 4.8 migration guidance
- [DOCS] Fast mode docs still recommend deprecated Opus 4.6 override variable
- [DOCS] Bash tool docs omit `$TMPDIR` consistency across sandboxed and unsandboxed commands
- [Bug] Anthropic API Error: 400 Bad Request on Extended Thinking
- [DOCS] Background session docs omit worktree-isolation behavior for spawned subagents
- Built-in mechanistic self-verification of verifiable claims (symmetric to the auto permission gate)
- [DOCS] Worktree docs do not clarify `worktree.baseRef: "head"` inside linked worktrees
- [BUG] Excessive RAM usage with multiple parallel chats (~10 sessions → 30 GB memory pressure, macOS OOM)
- [DOCS] Managed MCP policy docs omit invalid `allowedMcpServers`/`deniedMcpServers` entry behavior
- [DOCS] Effort docs omit `CLAUDE_CODE_ALWAYS_ENABLE_EFFORT` unsupported-model behavior
- Regression (2.1.147–2.1.150?): resuming an extended-thinking session after a CC update/model-switch → unrecoverable 400, session bricked
- [DOCS] Windows updater docs omit `claude.exe` in-use recovery guidance
- [DOCS] VS Code auto mode docs still tie mode-picker visibility to bypass-permissions setting
- [DOCS] MCP docs omit `/mcp` tool list and detail rendering behavior
- [DOCS] Fine-grained tool streaming docs still describe provider opt-in behavior
- bypassPermissions: session startup reads flat pref, GUI toggle writes per-account pref — they never sync
- [BUG] Claude Desktop Code tab causes disk write limit violation — 8.5GB in 11 min, macOS kills app (M5, v1.9659.1)
- Ultrareview v2.1.96: docs describe /tasks command + claude ultrareview --json subcommand that don't exist; findings hard to read after completion
- I'd be happy to help create a GitHub issue title, but I don't see the error message in your message. Could you please share the specific error you're encountering? That way I can generate an accurate and descriptive issue title for you.
- [BUG] Claude in Chrome `file_upload` rejects all scheduled-task sessions with misleading error (real cause: INVALID_SESSION)
- Extended thinking: signed thinking block 'cannot be modified' (400) permanently wedges session
- RTL text support for Hebrew (and Arabic) in Claude Code
- [Bug] Random errors occurring across multiple operations