Commit graph

292 commits

Author SHA1 Message Date
dobby-d-elf
e03c197cdf fix: recover from stale deleted workspaces 2026-05-12 05:52:16 -06:00
Frank Song
6e1e9fafbe Add worktree status endpoint 2026-05-12 10:08:01 +08:00
Frank Song
2da4f108c5 Clarify worktree session archive/delete semantics
(cherry picked from commit f5c8fb58d1892f2c964389295530e8be5d84323f)
2026-05-12 00:05:05 +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 c66e04b190e960de2a2902157261a5e407501054)
2026-05-11 23:13:55 +00:00
ai-ag2026
52fedbc783 feat: add per-cron toast notification toggle 2026-05-11 21:58:35 +02: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
nesquena-hermes
2a1244f342 Merge PR #2089 into stage-339
support slash commands implemented in hermes plugin
by @plerohellec
2026-05-11 17:43:57 +00:00
nesquena-hermes
6a016dae6c Merge PR #2077 into stage-338
Refactor compression anchor visibility helpers
by @franksong2702
2026-05-11 17:17:25 +00:00
Philippe Le Rohellec
281a57b60a support slash commands implemented in hermes plugin 2026-05-11 09:42:40 -07:00
ai-ag2026
d04d48f5a0 fix: harden turn journal submitted writes 2026-05-11 17:13:57 +02:00
Frank Song
18124ced62 Refactor compression anchor visibility helpers 2026-05-11 20:56:30 +08:00
Frank Song
2cd10868aa Fix session recovery polish 2026-05-11 16:30:25 +08:00
ai-ag2026
5cd001d545 feat: add crash-safe turn journal writer 2026-05-11 08:49:53 +02:00
nesquena-hermes
44e7378be8 Merge PR #2053: feat: worktree-backed session creation
# Conflicts:
#	CHANGELOG.md
2026-05-11 05:12:00 +00:00
nesquena-hermes
e3001d16fc Merge PR #2048: [security] validate workspace on import 2026-05-11 05:11:21 +00:00
Frank Song
db6857ba86 Address worktree session review notes 2026-05-11 12:51:57 +08:00
Frank Song
186453ea0e Add worktree-backed session creation 2026-05-11 12:12:40 +08:00
hinotoi-agent
3fd20599e8 fix: validate workspaces on session import 2026-05-11 10:46:17 +08:00
Chris Watson
8566462b72 feat: add MEDIA_ALLOWED_ROOTS env var for configurable /api/media whitelist
The /api/media endpoint only serves files from ~/.hermes, /tmp, and the
active workspace. Power users with media in custom directories (models,
Downloads, Pictures, ComfyUI outputs) have no way to serve those files
inline without copying or symlinking.

Add MEDIA_ALLOWED_ROOTS env var — a colon-separated list of absolute
paths — that extends the allowed roots at runtime. Each entry is resolved
and validated as an existing directory before being appended. Non-existent
or invalid paths are silently skipped.

This is purely additive: the built-in security whitelist is unchanged,
and if MEDIA_ALLOWED_ROOTS is unset, behavior is identical to before.
2026-05-11 02:45:46 +00:00
ai-ag2026
90c3611732 feat: expose session recovery audit and safe repair endpoints 2026-05-11 02:43:00 +00:00
nesquena-hermes
db32b70771 Merge PR #2038 into stage-335
# Conflicts:
#	CHANGELOG.md
2026-05-11 00:25:35 +00:00
nesquena-hermes
97b283c5a4 Merge PR #2039 into stage-335 2026-05-11 00:25:07 +00:00
ai-ag2026
2ead7daa2f fix: expose active run lifecycle in health 2026-05-11 02:15:00 +02:00
Frank Song
642249747f Fix session message identity dedup 2026-05-11 08:14:50 +08:00
ai-ag2026
663817570c fix: recover orphaned session backups on startup 2026-05-11 02:03:37 +02:00
nesquena-hermes
c624770c63 Stage 331: PR #2015 — fix(sessions): stitch continued session transcripts by @Jellypowered 2026-05-10 17:09:21 +00:00
nesquena-hermes
44dc7d05e8 Stage 331: PR #2014 — fix(sessions): keep explicit fork sessions out of compression lineage by @ai-ag2026 2026-05-10 17:09:21 +00:00
nesquena-hermes
b68d7c62e7 Stage 331: PR #2012 — feat(sessions): read-only session lineage report endpoint by @dso2ng 2026-05-10 17:09:21 +00:00
Jellypowered
8aed650b4c Stitch continued session transcripts in WebUI 2026-05-10 11:10:54 -05:00
ai-ag2026
017a631b6c fix: keep explicit fork sessions out of compression lineage 2026-05-10 18:03:21 +02:00
Dennis Soong
c3cf8b10e9 feat: add read-only session lineage report 2026-05-10 23:28:14 +08:00
Frank Song
1bec8070f2 fix(1833): persist compression anchor summary for reload UI 2026-05-10 16:45:16 +08:00
nesquena-hermes
8782fd2675 fix(stage-326): apply Opus advisor critical + recommended fixes
CRITICAL: #1951 PENDING_GOAL_CONTINUATION race
  Removes `PENDING_GOAL_CONTINUATION.discard(session_id)` from the
  streaming worker's `finally` cleanup block. The marker is set inside
  the SAME function call (line ~3328 on `goal_continue`) and the discard
  in the `finally` (line ~3553) almost always raced ahead of the
  frontend's SSE-receive → POST /api/chat/start round-trip, erasing
  the marker before the consumer in routes.py could read it. The
  consumer (`_start_chat_stream_for_session` in routes.py:6522) already
  discards atomically when consuming, so removing the streaming-side
  discard preserves single-use semantics and unblocks the
  goal-continuation chain.

  Adds tests/test_stage326_pending_goal_continuation_race.py with 5
  regression guards:
  1. streaming.py's finally must NOT discard PENDING_GOAL_CONTINUATION
  2. routes.py consumer must check + set + discard atomically
  3. PENDING_GOAL_CONTINUATION must be a set (GIL-safe single-op)
  4. STREAM_GOAL_RELATED.pop must be keyed by stream_id, not session_id
  5. PENDING_GOAL_CONTINUATION.add must precede the goal_continue SSE
     emission in source ordering

HARDENING: #1956 composer-draft input validation
  Per Opus, the POST /api/session/draft handler accepted unbounded /
  arbitrary-typed text and files inputs. With the 400ms debounced
  auto-save firing on every keystroke, a misbehaving client could
  persist multi-MB strings into the session JSON. Adds:
  - text: coerced to str if not already; clamped to 50_000 chars
  - files: coerced to list if not already; clamped to 50 entries
  Validation runs BEFORE the session lock acquire / save.

  Adds tests/test_stage326_composer_draft_validation.py with 5 guards.

Verdict from Opus advisor on stage-326: SHIP-WITH-FIXES.
This commit applies the required + recommended fixes; #1957 hardening
fixed in a prior stage commit.
2026-05-09 18:36:01 +00:00
nesquena-hermes
7cf8dcff4c Stage 326: PR #1956 — feat: persistent composer draft — server-side, cross-client, survives refresh by @JKJameson 2026-05-09 18:17:51 +00:00
Minimax
08c4ef8d88 feat: persistent composer draft — server-side, cross-client, survives refresh
- Session.composer_draft field: {text, files} stored in session JSON
- POST+GET /api/session/draft endpoint for save/load
- loadSession: save draft before switch, restore from S.session.composer_draft
- textarea input: debounced 400ms auto-save to server
- send(): clear draft after message is sent
- lockComposerForClarify(): save draft before card locks composer
- _restoreComposerDraft: clears textarea when target has no draft, guards
  against stale responses racing new session loads, exact text comparison
- Session.compact(): includes composer_draft in response
- Fix: use handler.command instead of parsed.method (ParseResult has no .method)

Co-authored-by: Minimax <noreply@minimax.io>
2026-05-09 13:47:57 +01:00
zqy
6fd07c2af4 fix: only evaluate goal hook on goal-related turns (#1932)
The goal evaluation hook was firing on every completed assistant turn
when a goal was active, even for unrelated messages like "what time is
it". This burned the goal budget, triggered continuation prompts that
interrupted unrelated conversations, and made /goal status numbers
misleading.

Add STREAM_GOAL_RELATED and PENDING_GOAL_CONTINUATION flags to gate
the evaluate_goal_after_turn() call in the streaming loop. Only streams
started from goal kickoff (/goal <text>) or goal continuation are
marked as goal-related. Normal user messages skip the hook entirely.
2026-05-09 15:08:13 +08:00
nesquena-hermes
b71a2d4cba Stage 323: PR #1866 — add WebUI /goal command support by @Michaelyklam 2026-05-08 17:40:31 +00:00
Michael Lam
0db5bc6b76 feat: add WebUI goal command support 2026-05-08 17:12:01 +00:00
Samuel Gudi
c613cfa9a7 refactor(profiles): relocate _profiles_match to api/profiles.py (#1895 review)
Maintainer review on PR #1895 flagged that mcp_server.py duplicated the
visibility model from api/routes.py:75. Move the canonical helper into
api/profiles.py (next to _is_root_profile, on which it depends) so both
api/routes.py and mcp_server.py import the same function instead of
carrying parallel definitions that could drift as the model evolves.

- api/profiles.py: + _profiles_match (verbatim from former routes.py:75-97)
- api/routes.py:   replace local definition with re-export to keep all
                   existing _profiles_match(...) call sites resolving
                   without per-call-site refactors
- mcp_server.py:   drop local copy, import _profiles_match alongside the
                   existing api.profiles imports (line 59)
- tests:           + test_profiles_match_single_source_of_truth asserts
                   identity (mcp.module._profiles_match is api.profiles._profiles_match
                   is api.routes._profiles_match) so any re-introduction of
                   a local copy trips the test
                   + test_profiles_match_input_matrix parametrize across
                   the (None|''|'default'|'foo') x (None|''|'default'|'foo'|'bar')
                   visibility matrix per maintainer suggestion

Behaviour unchanged. Zero call-site changes anywhere in api/routes.py.

Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-05-08 17:12:01 +00:00
nesquena-hermes
8c4c253654 Stage 322: PR #1814 — custom named provider API key resolution by @hualong1009 2026-05-08 16:55:20 +00:00
王浩生
cdbdc28f5c fix(config): custom named provider API key resolution in WebUI
- add robust custom provider credential/base_url resolver
- apply fallback in streaming and routes agent init/self-heal paths
- support slug normalization and config fallbacks for custom:* providers
2026-05-08 16:40:17 +00:00
nesquena-hermes
b8426d047c Stage 321: PR #1900 — pass config overrides into context-length fallback (closes #1896) 2026-05-08 16:08:42 +00:00
Nathan Esquenazi
15b7b7ae12 fix(routes): pass config overrides into session-load context-length fallback
PR #1900 patches the two get_model_context_length() fallback callsites in
api/streaming.py to pass config_context_length, provider, and
custom_providers — but a third callsite of the same shape lives at
api/routes.py:2849, in the /api/session/get path that resolves
context_length for older sessions (pre-#1318) that have context_length=0
persisted.

Same bug shape: only `(model, base_url)` were forwarded, so the resolver
fell through to the 256K DEFAULT_FALLBACK_CONTEXT even when the user had
`model.context_length: 1048576` set in config.yaml. Visible symptom: the
very first paint of a reloaded old session shows the wrong window in the
chat-toolbar indicator until a turn fires (which would then trigger the
streaming.py fallbacks fixed in this PR and overwrite with the correct
value).

Fix mirrors streaming.py: pass `config_context_length=`,
`provider=effective_provider or ""`, and `custom_providers=` from the
per-profile config (`get_config()`), with a TypeError fallback that
retries the legacy 2-arg form for older hermes-agent builds whose
get_model_context_length signature pre-dates the new kwargs.

Adds `test_routes_session_load_fallback_passes_config_overrides` to lock
the call shape — verified to fail pre-fix with the same "missing
config_context_length=" error the streaming.py tests catch.

Defense-in-depth completion of #1896 — closes the third leg of the same
bug shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:08:42 +00:00
Michael Lam
6c4b769324 fix: scope skills endpoints to active profile 2026-05-08 16:07:49 +00:00
Frank Song
7e2709e281 fix: add request wedge diagnostics 2026-05-08 15:37:08 +00:00
ai-ag2026
f69a81c8c3 fix: preserve pending turn during stale cleanup 2026-05-07 23:57:01 +02:00
Michael Lam
78c09e1fd9 fix: keep shell route errors html 2026-05-07 18:41:13 +00:00
Nathan Esquenazi
f3b56d8793 fix(kanban): avoid double 404 when bridge already sent error response
PR #1837's new `_kanban_unknown_endpoint` wrapper was triggered for any
falsy bridge return — but `handle_kanban_*` returns `None` (not `True`)
when an inner handler calls `bad(...)` to send an error response. The
wrapper then sent a SECOND 404 on top of the bridge's response, producing
concatenated JSON bodies on the wire.

Concrete reproducer (caught by behavioural harness, not the merged tests):

    GET /api/kanban/tasks/<missing-id>/log
    →  '{"error":"task not found"}{"error":"unknown Kanban endpoint: GET ..."}'

This affected every `bad(...)`-shaped error path in the bridge:
- task-not-found returns from `_task_log_payload` / `_task_detail_payload`
- exception handlers for ImportError (503), LookupError (404),
  ValueError (400), RuntimeError (409) across all four method handlers
- the `_handle_events_sse_stream` board-resolution failure path

The fix: distinguish an explicit `False` (truly unmatched path) from
`None` (handled, response already sent). Only `False` should trigger
the unknown-endpoint diagnostic.

Adds a regression test that exercises the task-not-found path through
`routes.handle_get` and asserts only one JSON body is on the wire.

Follow-on to #1837 (already merged into master at v0.51.20).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:35:57 -07:00
Michael Lam
bb75707331 fix: surface stale Kanban client recovery 2026-05-07 17:57:09 +00:00
Frank Song
8bc2677691 fix: repair file picker and html preview interactions 2026-05-07 16:59:00 +00:00