Commit Graph

765 Commits

Author SHA1 Message Date
Hermes Agent 7c2b2785e7 stage-348: apply Opus SHOULD-FIX-pre-merge — add '://' to _SENSITIVE_LOWER_MARKERS
Opus advisor flagged that PR #2171's credential prefilter only listed
specific DB scheme prefixes and form keys, letting OAuth callback URLs,
URL userinfo, signed-URL query params bypass the hard agent redactor.

Adding the generic '://' marker restores the WebUI-as-hard-safety-boundary
contract. Plain URLs without sensitive substrings still pass through
unchanged because the redactor itself only mutates sensitive substrings.

Regression-pinned with 5 new parametric cases in test_security_redaction.py
plus 1 negative-case companion. Verified test FAILS without the fix and
PASSES with it.
2026-05-13 16:54:36 +00:00
Hermes Agent 39df1a1ef3 Merge pull request #2171 into stage-348
Trim session tail response overhead (franksong2702)
2026-05-13 16:34:43 +00:00
Hermes Agent ef042ad8c2 Merge pull request #2188 into stage-348
fix: refresh context ring after compression (LumenYoung)
2026-05-13 16:34:42 +00:00
Lumen Yang 3289c44fb6 fix: refresh context ring after compression 2026-05-13 14:02:28 +02:00
Frank Song da73c00f06 Harden session tail redaction prefilter 2026-05-13 18:58:49 +08:00
fxd-jason 9e45de463d fix: prevent 404 on /api/session/compress/status during session switch
Two-part fix:
- Backend: handle_get returns True (not None from j()) for compress/status
  route, preventing edge-case 404 fallback in do_GET
- Frontend: resumeManualCompressionForSession silently returns on 404
  instead of showing "Compression failed: not found" toast

Includes 6 regression tests covering backend return value, idle/empty
session responses, and frontend 404 guard presence.
2026-05-13 18:56:55 +08:00
Frank Song b7ac5a8b88 Trim session tail response overhead 2026-05-13 15:57:29 +08:00
Hermes Agent 8060b2ba3a Merge pull request #2179 into stage-347
fix(config): preserve nvidia/ prefix on NVIDIA NIM (closes #2177)

Self-built. nesquena APPROVED with extensive end-to-end trace including
cross-tool agent CLI verification and 12-shape behavioural harness.
2026-05-13 07:33:45 +00:00
nesquena-hermes 9b1d786459 fix(config): preserve nvidia/ prefix on NVIDIA NIM (closes #2177)
Move the `_PORTAL_PROVIDERS` guard in `resolve_model_provider()` to run
BEFORE the `prefix == config_provider` strip branch. The guard was added
for NVIDIA (along with the Nous portal cases in #854 / #894) but was
placed after the strip, so it never fired when `config_provider == "nvidia"`
and the model id started with `nvidia/`.

For `model_id="nvidia/nemotron-3-super-120b-a12b"`,
`config_provider="nvidia"`:
  - prefix = "nvidia", bare = "nemotron-3-super-120b-a12b"
  - prefix == config_provider → True → strip branch returned bare name
  - `_PORTAL_PROVIDERS` guard never reached
  - bare "nemotron-3-super-120b-a12b" sent to NVIDIA NIM → HTTP 404

NIM requires the full namespaced path. The fix moves the portal guard
to run first, so all portal providers (Nous, OpenCode-Zen, OpenCode-Go,
NVIDIA NIM) always preserve the full `provider/model` id regardless of
whether the prefix happens to equal the provider name.

This also closes a latent symmetric bug for the Nous case if a
`nous/<model>` id ever existed in the catalog.

Test plan:
- New `tests/test_issue2177_nvidia_prefix_preservation.py` covers:
  - nvidia/nemotron-... under nvidia (the reported case)
  - cross-namespace qwen/ and meta/ under nvidia (regression pin)
  - every static nvidia model in `_PROVIDER_MODELS` resolves to itself
  - latent nous/<model> under nous (structural ordering pin)
  - non-portal providers (anthropic) still strip — fix doesn't over-correct
- Existing portal-routing suites (test_nous_portal_routing.py,
  test_issue895_894_nous_prefix.py) continue to pass.
- Full test suite: 5320 passed, 4 skipped, 3 xpassed.

Reported on Discord by @vishnu (Nathan forwarded as #2177).
2026-05-13 07:05:57 +00:00
Hermes Agent afe42b96c1 Merge pull request #2156 into stage-346
Issue #2057 Slice 2: Add guarded worktree remove action
2026-05-13 06:56:25 +00:00
Hermes Agent 2a9d011022 Merge pull request #2160 into stage-346
Add CSP report collector endpoint (closes #2095)
2026-05-13 06:56:22 +00:00
Hermes Agent 4109394cdf Merge pull request #2159 into stage-346
Fix stale stream state in session list (closes #2157)

# Conflicts:
#	CHANGELOG.md
2026-05-13 06:56:21 +00:00
Hermes Agent 7b866df79a Merge pull request #2170 into stage-346
Skip CLI metadata lookup for native session loads

# Conflicts:
#	CHANGELOG.md
2026-05-13 06:56:20 +00:00
Hermes Agent 129e42873c Merge pull request #2158 into stage-346
Fix stale stream exception writeback guards (closes #2154)

# Conflicts:
#	CHANGELOG.md
2026-05-13 06:56:17 +00:00
MrFant a4417d11f9 fix: handle dict model entries in provider models list
When a provider's 'models' config contains dicts (e.g. {"id": "x", "label": "y"})
instead of plain strings, _apply_provider_prefix() crashes with:
  AttributeError: 'dict' object has no attribute 'startswith'

This happens because the list comprehension at line 3505 passes the raw dict
as the model ID. The fix extracts 'id' and 'label' from dict entries while
keeping string entries as-is.

Fixes the /api/models and /api/onboarding/status 500 errors.
2026-05-13 13:49:40 +08:00
Frank Song e78945e7ca Skip CLI metadata lookup for native sessions 2026-05-13 12:35:12 +08:00
Frank Song 57ee0ce069 Add CSP report collector endpoint 2026-05-13 10:52:59 +08:00
Frank Song 5ae63ddd13 Fix stale stream state in session list 2026-05-13 10:28:12 +08:00
Frank Song 9ea4f1145d Fix stale stream exception writeback guards 2026-05-13 10:23:03 +08:00
Frank Song 46c62851ad Harden worktree removal safeguards 2026-05-13 09:49:15 +08:00
Frank Song 93b7d35bfa Issue #2057 Slice 2: Add worktree remove action
Backend:
- POST /api/session/worktree/remove — removes a session's git worktree
- Guards: stream/terminal lock, dirty/untracked without force
- remove_worktree_for_session() in api/worktrees.py

Frontend:
- 'Remove Worktree' context menu item + confirm modal
- i18n keys for all 11 locales

Tests:
- 5 tests: clean remove, missing worktree, no-path, route success, 404
2026-05-13 09:11:55 +08:00
Hermes Agent 20717a0d0a Merge pull request #2136 into stage-345
fix: guard stale stream writebacks (LumenYoung)

Prevents stale WebUI stream workers from writing old results into a session
after that session has already moved on to another stream. Adds new helper
_stream_writeback_is_current() (a token equality check against the session's
active_stream_id) and short-circuits the two finalize/cancel paths when the
worker no longer owns the session writeback.
2026-05-12 23:11:48 +00:00
Lumen Yang 4b57b202a0 fix: guard stale stream writebacks 2026-05-13 00:05:09 +02:00
Hermes Agent 2def05f385 stage-344: apply Opus SHOULD-FIX #1+#2 — #2128 multi-tab race + stale-done re-emit
(1) compress/status no longer pops the job entry on first read of `done` payload.
    Second open tab no longer sees `idle` and a stale-job toast.
(2) compress/start no longer short-circuits to a stale `done` payload when
    re-invoked within the 10-minute TTL. Re-running /compress always starts
    fresh, so closing-and-reopening a tab mid-compress works correctly.

Third SHOULD-FIX (#2135 cfg["model"] fallback tightening when no custom_providers
entry matches) deferred to follow-up — strictly no-worse-than-master behavior.

tests/test_sprint46.py 10/10 still passes.
2026-05-12 16:37:37 +00:00
Hermes Agent 7116c680df stage-344: maintainer fix for #2142 fr locale — add LOCALES tuple entries + _LOGIN_LOCALE block
#2142 (legeantbleu) added the fr locale to static/i18n.js but didn't update:
1. tests/test_issue1488_composer_voice_buttons.py: two TestComposerVoiceButtonI18n + TestVoiceModePreferenceGate LOCALES tuples needed 'fr'
2. api/routes.py: _LOGIN_LOCALE needed an 'fr' block so the login page localizes for French users (issue #1442 parity contract)
3. tests/test_login_locale_parity.py: the test asserting 'fr' falls-back-to-'en' is inverted — fr now resolves to fr, with sibling assertions for fr-FR and fr-CA

Mirrors the stage-340 fix for the it locale (PR #2067 → maintainer adds tuple entries). 46/46 i18n tests pass after fix.
2026-05-12 16:14:47 +00:00
Hermes Agent c677c19a8f Merge pull request #2128 into stage-344
Fix manual compression proxy timeouts (closes #2087)

# Conflicts:
#	CHANGELOG.md
2026-05-12 16:13:01 +00:00
Hermes Agent 1ee8627acb Merge pull request #2135 into stage-344
Fix custom live model scoping (closes #2126, refs #2131)

# Conflicts:
#	CHANGELOG.md
2026-05-12 16:13:00 +00:00
Hermes Agent aa85bd2e7c Merge pull request #2138 into stage-344
fix: recover from stale deleted workspaces
2026-05-12 16:12:58 +00:00
Hermes Agent 8dd0b4ec31 Merge pull request #2139 into stage-344
fix: audit turn journal terminal collisions
2026-05-12 16:12:56 +00:00
Hermes Agent a06952ab00 Merge pull request #2140 into stage-344
Preserve fallback provider credential hints (closes #2133)

# Conflicts:
#	CHANGELOG.md
2026-05-12 16:12:54 +00:00
Frank Song 76e611d49f Preserve fallback provider credential hints 2026-05-12 20:42:55 +08:00
dobby-d-elf 516d942d6a refactor: reduce stale workspace recovery fix 2026-05-12 06:28:35 -06:00
Michael Lam f5f59a5813 fix: audit turn journal terminal collisions 2026-05-12 05:20:06 -07:00
Frank Song b7c5ba640c Fix custom live model scoping 2026-05-12 20:05:28 +08:00
dobby-d-elf e03c197cdf fix: recover from stale deleted workspaces 2026-05-12 05:52:16 -06:00
Frank Song 8fa92c680f Fix manual compression proxy timeouts 2026-05-12 17:33:59 +08:00
Michael Lam 265496782a docs: clarify compression anchor helpers 2026-05-12 01:43:16 -07:00
Hermes Agent 10cfcee30e stage-342: apply Opus SHOULD-FIX — tighten worktree status _run_git timeout 5s → 2s
Worst case 4×5s=20s per polling request on ThreadingHTTPServer pool is risky
given today's _cron_env_lock near-miss on production 8787. Status probes
should fail fast; client can retry. All four call sites use default timeout.
2026-05-12 05:22:01 +00:00
Hermes Agent 4d64f6eee9 Merge pull request #2116 from starship-s/fix/codex-quota-pool-usage
fix(providers): load Codex quota from credential pool
2026-05-12 05:10:23 +00:00
starship-s 573fc25f96 fix(providers): load Codex quota from credential pool 2026-05-11 21:46:24 -06:00
Frank Song 6e1e9fafbe Add worktree status endpoint 2026-05-12 10:08:01 +08:00
nesquena-hermes d75b59135a stage-341: apply Opus SHOULD-FIX (it i18n + short-circuit logger.debug + docstring)
Opus advisor pass on stage-341 found three surgical items:

1. static/i18n.js:it — PR #2064 branched before stage-340 landed the 'it'
   locale (#2067), missing 9 session_*worktree* keys. Mechanical mirror of
   en/ja position. Italian falls back to English silently without this fix.
2. api/streaming.py — PR #2107's new break short-circuit was silent in both
   the aux and agent title-generation paths. Added logger.debug calls before
   each break so production logs surface the exit shape.
3. api/streaming.py — Expanded _title_should_skip_remaining_attempts docstring
   to document the membership criterion explicitly (vs the implicit
   reasoning-only-burn case it ships with today). Future additions
   (llm_safety_blocked, llm_oauth_quota) have a clear inclusion test.

CHANGELOG updated under the Stage-341 maintainer fixes section to mirror
the stage-340 pattern. All targeted tests pass (57/57 in the affected
modules).
2026-05-12 00:16:33 +00:00
Frank Song 2da4f108c5 Clarify worktree session archive/delete semantics
(cherry picked from commit f5c8fb58d1)
2026-05-12 00:05:05 +00:00
nesquena-hermes e20eb2c784 fix: skip budget-doubling title retry for reasoning-only responses (#2083)
Reasoning models (Qwen3-thinking via LM Studio, DeepSeek-R1, Kimi-K2,
etc.) can burn their entire output budget on hidden reasoning tokens and
emit no visible content. The previous title-generation retry path
classified that as llm_length and doubled the budget — but the second
call produces the same shape, so the retry only doubled the GPU/credit
burn. Repeated across the two prompts in _title_prompts() this came to
~3000 reasoning tokens of GPU work per new chat. On local LM Studio
servers behind a custom: provider (where is_lmstudio=False means
reasoning_effort: none never reaches the model) it manifested as the GPU
never going idle after a prompt.

Fix:
  - _extract_title_response: classify reasoning-bearing empty responses
    as llm_empty_reasoning regardless of finish_reason. The presence of
    reasoning_content is the diagnostic signal, not finish_reason.
  - _title_retry_status: drop llm_empty_reasoning from the retry set.
    Length-truncated responses WITHOUT reasoning still retry (those are
    legitimately recoverable by a larger budget).
  - Add _title_should_skip_remaining_attempts() and break out of the
    prompt-iteration loop on empty-reasoning. A second prompt against
    the same model would produce the same shape.
  - Falls through to _fallback_title_from_exchange for a local-summary
    title.

Tests updated to invert the previous reasoning-retry assertions:
  - test_aux_short_circuits_on_empty_reasoning_without_retrying
  - test_aux_still_retries_finish_length_without_reasoning
  - test_agent_route_short_circuits_on_empty_reasoning_without_retrying
  - test_agent_route_still_retries_finish_length_without_reasoning

Companion agent-side work (LM Studio classifier for custom: providers)
is tracked separately on the hermes-agent side; this WebUI fix is the
belt-and-braces guard so the loop stops regardless of agent classifier
state.

Reported by @darkopetrovic. Closes #2083.

Co-authored-by: darkopetrovic <darkopetrovic@users.noreply.github.com>
(cherry picked from commit efeae4a86e)
2026-05-12 00:04:11 +00:00
Samuel Gudi ba3cc2c541 feat(i18n): add Italian (it) locale
Adds complete Italian translation for all ~280 UI strings in static/i18n.js
and the login page strings in api/routes.py (_LOGIN_LOCALE).

Ordered alphabetically: en → it → ja in both files.
Preserves all JS function templates, template literals, and plural forms.

(cherry picked from commit c66e04b190)
2026-05-11 23:13:55 +00:00
Lumen Yang e37c69cf57 fix(agent-health): treat stale running gateway as unknown
(cherry picked from commit 4be346fece)
2026-05-11 23:13:09 +00:00
ai-ag2026 52fedbc783 feat: add per-cron toast notification toggle 2026-05-11 21:58:35 +02:00
nesquena-hermes 96ca83bf53 fix(security): drop unsafe-eval + add jsdelivr to CSP, sanitize plugin error
Opus stage-339 review SHOULD-FIX items:

1. server.py: drop 'unsafe-eval' from CSP report-only policy.
   Verified by grepping all production JS — zero matches for eval(),
   new Function(), or string-form setTimeout/setInterval. Keeping it
   was a gratuitous privilege.

2. server.py: add https://cdn.jsdelivr.net to script-src + style-src.
   index.html loads Prism/xterm/katex from this CDN with SRI hashes —
   without the allowance every page load fires known-good CSP violations
   that drown out real signal once a collector is wired.

3. api/commands.py: sanitize plugin command error. Previously returned
   f'Plugin command error: {exc}' which would leak paths/env from
   FileNotFoundError('/etc/something/secret.key') etc. Now returns only
   the exception type name; full traceback goes to server log.

Test asserts updated to match the new policy shape.

Co-authored-by: Opus advisor <opus-advisor@hermes.local>
2026-05-11 17:53:02 +00:00
nesquena-hermes fd069155af Merge PR #2062 into stage-339
feat: record turn journal lifecycle events
by @ai-ag2026
2026-05-11 17:43:58 +00:00
nesquena-hermes f6ce79185c Merge PR #2059 into stage-339
feat: add crash-safe turn journal writer
by @ai-ag2026
2026-05-11 17:43:58 +00:00