Commit graph

95 commits

Author SHA1 Message Date
nesquena-hermes
db87fd3a07 Merge PR #5630 into visible-batch stage 2026-07-06 05:37:45 +00:00
nesquena-hermes
f10cb2a35c review: merge #5555 (11eed2af) 2026-07-06 01:44:21 +00:00
Anthony Ruiz
5901ed68e1 fix(steer): scope upload UX to owner session 2026-07-05 16:02:54 +00:00
nesquena-hermes
c78d5083ad fix(#5459): dedup steer upload on retry + remove only delivered files by identity (gate)
Codex gate found two SILENT defects, fixed:
1) Failed-steer RETRY re-uploaded the same File objects (uploadPendingFiles ran
   again with clearPending:false). Added _steerUploadCache keyed by ownerSid +
   file signature so a retry with the same staged set reuses the uploaded paths;
   invalidated when the staged set changes (signature miss) or on accepted steer.
2) Accepted steer cleared ALL S.pendingFiles, dropping files staged during the
   upload/API await. Now removes ONLY the delivered snapshot by object identity,
   preserving newly staged files.

(The owner-session draft is still cleared on accepted steer — clearing the
delivered session's draft is correct even after a mid-upload switch; the existing
test asserts this.) Tests updated for the identity-based removal + new
cache/dedup guard. 69 steer/composer tests pass, node --check clean.
2026-07-05 07:10:23 +00:00
nesquena-hermes
ceea2438eb fix: include attachments in steer submissions
Steering (submitting mid-stream) dropped any pending composer attachments. Now the
pending files are uploaded before the /steer payload is sent, the uploaded file
paths are appended to the steer text so the active agent can inspect them with
tools, file chips stay staged if the steer fails (so nothing is lost), and
file-only busy submissions are handled.

Contributor stage; gate-pass. Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-05 06:50:34 +00:00
Rod Boev
cbc164de0c feat(#5477): fork read-only cron sessions 2026-07-04 16:38:27 -04:00
nesquena-hermes
0735a1b853 fix(#5439): hide read-only cron fork affordances
Clean rebase of rodboev's #5449 (rebase-first).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-03 00:58:30 +00:00
nesquena-hermes
b931ae2f9b fix(settings): reconcile #5145 rename+steer-flip onto master's #5170 mirror
Rebase PR #5162 (rename busy_input_mode -> default_message_mode; flip the
default from 'queue' to 'steer') onto current origin/master WITHOUT dropping
the shipped #5170 localStorage persistence mirror.

Rename the mirror machinery to the new setting name for consistency:
  _BUSY_INPUT_MODES        -> _DEFAULT_MESSAGE_MODES        (values unchanged)
  _normalizeBusyInputMode  -> _normalizeDefaultMessageMode  (fallback now 'steer')
  _persistBusyInputMode    -> _persistDefaultMessageMode
  _readPersistedBusyInputMode -> _readPersistedDefaultMessageMode
  window._busyInputMode    -> window._defaultMessageMode (+ renamed exports)

localStorage: write the new 'hermes-default-message-mode' key; read it with a
fallback to the legacy 'hermes-busy-input-mode' key so an existing user's
persisted preference survives the rename.

Preserve #5170 behavior at every mirror site under the new names:
  - boot success  -> window._defaultMessageMode=_persistDefaultMessageMode(...)
  - boot FAILURE  -> window._defaultMessageMode=_readPersistedDefaultMessageMode()
    (NOT a hardcoded 'steer' — a saved 'interrupt'/'queue' must still apply when
    the server is unreachable; do not regress #5167/#5132)
  - preferences autosave, settings-panel load, and _applySavedSettingsUi all
    persist through _persistDefaultMessageMode(...)

Tests updated for the rename while keeping the persistence-behavior assertions
(test_1062, test_5145, test_5167); test_5167 gains explicit guards that the
load-failure path reads the persisted pref and never hardcodes a literal mode,
plus autosave/panel-load mirror-write coverage.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-02 05:44:48 +00:00
nesquena-hermes
9fb2731848 #5310: push-to-talk hold gesture (#3700) 2026-07-01 23:26:15 +00:00
nesquena-hermes
6716f03f4d fix(webui): stop false clarify-unavailable toast; add interrupt provenance (#5345)
/api/clarify/pending always returns HTTP 200 when present (returns
{"pending": null} for an unknown session — it never 404s). The front-end
clarify poller warned "Clarify endpoint unavailable. Please restart
server." on ANY caught error whose message merely contained "404" or
"not found", so an unrelated stale-session 404 ("Session not found", e.g.
an old-profile session polling briefly after a profile switch) or a
transient error produced a misleading missing-endpoint toast that pointed
operators at the wrong layer.

Clarify polling now branches on the structured HTTP status that api()
attaches to the thrown Error (err.status):
- 404 "Session not found" -> handled as a stale-session poll (stop + hide
  card silently), no toast;
- restart-server warning fires only on a genuine route-not-found 404 whose
  body is NOT session-scoped;
- poll failures are logged with path, status, polling session id, and
  current session id for diagnosis.

Interrupt provenance: cancelStream()/cancelSessionStream() now log a
'[stream] cancel requested' line with the trigger reason
(composer-stop / slash-stop / slash-interrupt / busy-interrupt /
sidebar-stop). Passive UI lifecycle events (session switch, tab hide, page
unload) already tear down only the local SSE transport via
closeLiveStream() and never call /api/chat/cancel — only explicit
Stop/interrupt paths interrupt the backend agent/tool run. This is
confirmed by test_clarify_pending_never_404s locking the handler shape.

Supersedes #5343 (which handled only the profile-switch sub-case and kept
the broad message-scrape). Adds tests/test_issue5345_*.py (9 tests).

Co-authored-by: claw-io <claw-io@users.noreply.github.com>
Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-01 17:17:11 +00:00
Rod Boev
e2a1a54b37 fix(#4843): preserve falsy /pet hook messages 2026-06-29 18:26:40 -04:00
Rod Boev
ec7ecf9e46 Avoid misleading pet recovery guidance 2026-06-28 20:24:29 -04:00
Rod Boev
6f333984f6 fix(#4843): respect truthy pet hook handoffs 2026-06-28 19:56:05 -04:00
Rod Boev
25d213cfe9 feat(#4843): add WebUI pet extension handoff 2026-06-28 19:32:42 -04:00
Rod Boev
b70949f484 fix(steer): rename typo, catch retry rejection, update JSDoc (#4749) 2026-06-25 03:23:36 +00:00
Rod Boev
5312f96746 fix: move _trySteer JSDoc above its function definition 2026-06-25 03:23:36 +00:00
Rod Boev
764d5b8601 fix(#4749): show steer failure reason and offer retry/dismiss recovery 2026-06-25 03:23:36 +00:00
nesquena-hermes
0456003c84
stage #4577 (dso2ng): keep failed steer from cancelling active runs (#4670)
A failed /steer (or busy-mode steer) no longer silently falls back to interrupt+queue
(which cancelled the active stream). _trySteer now returns delivered:bool, restores the
draft + leaves the stream running on failure; send() only clears staged files when
_steerDelivered===true. Backend hunk comment-only (failure paths already returned
{accepted:False}, never touched CANCEL_FLAGS). i18n value-only on pre-existing keys
(locale-parity preserved, 173 locale tests pass). 48 own tests pass.

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: dso2ng <dso2ng@users.noreply.github.com>
2026-06-21 23:22:51 -07:00
Tom Tong
fcc6a253b5 frontend: subpath-safe fetch() in /model command
cmdModel() had the only two root-absolute fetch('/api/...') calls left in the
frontend. Under a reverse proxy that mounts the app at a subpath (e.g.
jupyter-server-proxy at /proxy/<port>/), a root-absolute path escapes the mount
and 404s, so /model can't load the catalog or update the session. Resolve both
against document.baseURI (falling back to location.href) like every other fetch
in the frontend already does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:44:43 +00:00
Rod Boev
fcaab02e93 fix(autocomplete): support slash commands after non-ASCII prefix and multiple slashes (#3924) 2026-06-14 19:57:17 +00:00
nesquena-hermes
5812df5f7b Merge #4112 (skill bundles in WebUI slash commands, #4087) onto master 2026-06-13 23:13:28 +00:00
Rod Boev
d7e8f70423 fix(commands): block transient bundle shadowing 2026-06-13 18:53:46 -04:00
Rod Boev
c7c313672c fix(reasoning): hide unsupported max effort (#4119) 2026-06-13 14:33:26 -04:00
Rod Boev
6c7d551b41 fix(commands): keep slash command ownership ahead of bundles (#4087) 2026-06-13 14:06:35 -04:00
Rod Boev
5a3c6c6f05 fix(commands): surface skill bundles in WebUI slash commands (#4087) 2026-06-13 14:06:35 -04:00
nesquena-hermes
52c502a984 Merge #3971 (hide Worklog reasoning when Thinking disabled) onto master 2026-06-13 02:52:31 +00:00
nesquena-hermes
0eee1b85ec Merge #4010 into stage-mk 2026-06-12 23:18:00 +00:00
Rod Boev
5f8ab6c452 Avoid redundant skill_view calls during forced skill sends 2026-06-12 08:49:56 -04:00
Rod Boev
80dab6f3eb fix(commands): inject forced skill content for /use (#4044) 2026-06-12 08:34:27 -04:00
Rod Boev
1753cc0c60 fix(commands): hide CLI-only slash collisions from autocomplete (#3969) 2026-06-11 20:58:51 -04:00
Rod Boev
7d925a324a fix(#3968): autocomplete skill names for /use 2026-06-11 16:55:14 -04:00
Frank Song
c4284dc77c fix: hide Worklog reasoning with Thinking 2026-06-11 13:30:23 +08:00
nesquena-hermes
4f842d5095
Release v0.51.297 — stage-3711 (terminal remote-backend guard #3711) (#3745)
* fix(terminal): guard embedded terminal on remote backends (#3673)

* fix(terminal): add missing remote-backend locale key

* fix(terminal): add missing remote-backend locale coverage (#3673)

* docs(changelog): v0.51.297 — terminal remote-backend guard (#3711) only

Dropped #3725 (descendant reaper) from this stage: Codex caught a SILENT exit-code
clobber — its process-wide os.waitpid(-1, WNOHANG) can reap a sibling WebUI child that
another subsystem is waiting on, coercing that child's returncode to 0 (failures become
successes). Held for the contributor to scope the reaper to terminal PGIDs
(os.waitpid(-term.proc.pid, WNOHANG)) or a terminal-PGID registry.

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <[email protected]>
2026-06-06 14:17:17 -07:00
nesquena-hermes
58528a4d88
Release v0.51.270 — Release IL (stage-u1 — un-hold batch: #3517 #3624 #3613) (#3674)
* feat(commands): add /use to force a skill for the next turn (#3517, #2977)

Co-authored-by: Rod Boev <rod.boev@gmail.com>

* fix(auth): cap pending passkey challenges by evicting oldest, not rejecting (#3624)

Co-authored-by: Hinotobi <paperlantern.agent@gmail.com>

* fix(providers): expose model-provider plugins in WebUI (#3613)

Co-authored-by: Pamnard <pamnard@users.noreply.github.com>

* docs(changelog): v0.51.270 — Release IL (stage-u1, 3-PR un-hold batch; #3448 + #3618 dropped)

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: Hinotobi <paperlantern.agent@gmail.com>
Co-authored-by: Pamnard <pamnard@users.noreply.github.com>
2026-06-05 11:35:26 -07:00
nesquena-hermes
ba987040c7
Release v0.51.260 — Release IB (stage-r8) (#3614)
## Release v0.51.260 — Release IB (stage-r8)

Un-held safety fixes (author resolved my earlier hold findings; re-reviewed fresh) + a clean fix batch. 6 PRs.

### Fixed
| Issue/PR | Author | Fix |
|----------|--------|-----|
| #3535 (#3538) | @rodboev | **Self-update recovers from a stash-pop conflict without data loss.** Was a BRICK bug (`git reset --merge` + `git stash drop` discarded local mods while reporting success). Now keeps the stash, returns `ok:false` + "preserved in `stash@{0}`", no restart on conflict. *(was held — fix verified)* |
| #1909 s3 (#3562) | @rodboev | **Auth `Secure` cookie no longer locks out plain-HTTP LAN/Tailscale users.** Secure now keys only on real TLS evidence (env / TLS socket / opt-in `TRUST_FORWARDED_PROTO`); non-loopback plain-HTTP is no longer force-Secure. SameSite back to `Lax`. *(was held — fix verified)* |
| #2785 (#3559) | @franksong2702 | Clearer cron/gateway diagnostics for single-container Docker (gateway configured, no daemon → jobs silently don't fire). |
| #3555 | @lambyangzhao | Long TTS responses chunked at sentence boundaries (works around the browser's ~32K silent-truncation). |
| #3340 (#3342) | @rly09 | Persistent-state toast when a turn has saved memory / created-updated a skill. |
| #3533 | @franksong2702 | `/reload-mcp` marked `cli_only` so the WebUI doesn't dispatch it as an LLM prompt. |

### Gate
- Full pytest suite: **7681 passed, 0 failed**
- ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN
- Codex (regression): **SAFE TO SHIP** — confirmed the stash-conflict path never drops the stash / never restarts on conflict, auth Secure handles LAN-HTTP correctly with no header-forgery hole, `/reload-mcp` allowlisted, state-toast has a real backend writer + active-session guard, diagnostics leak no paths, TTS chunking preserves order.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
Co-authored-by: lambyangzhao <lambyangzhao@users.noreply.github.com>
Co-authored-by: rly09 <rly09@users.noreply.github.com>
2026-06-04 15:21:41 -07:00
nesquena-hermes
15e654d468
Release v0.51.251 — Release HS (stage-q23) (#3527)
## Release v0.51.251 — Release HS (stage-q23)

UX-verified live (path dropdown opens on `~/`).

### Fixed
| Issue | Author | Fix |
|-------|--------|-----|
| #3433 | @puneetdixit200 | **Composer `~/` path autocomplete** (TUI parity). Typing a `~/` token in the composer opens a home-directory path-suggestion dropdown. Reuses the existing slash-command dropdown (positioning + keyboard nav) and the trusted `/api/workspaces/suggest` endpoint; replaces only the matched token on selection (surrounding text preserved). Slash-command autocomplete still takes precedence for `/`-prefixed input. |

### Gate
- Full pytest suite: **7568 passed, 0 failed**
- ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN · live-verified the dropdown opens on `~/`
- Codex (regression): **SAFE TO SHIP** — slash-precedence preserved, `~/../../etc` → no suggestions (path-escape blocked via root-confined endpoint), bounds-clamped token replacement, esc-escaped, URLSearchParams-encoded

Co-authored-by: puneetdixit200 <puneetdixit200@users.noreply.github.com>
2026-06-03 21:12:23 -07:00
nesquena-hermes
0b0a179e4c
Release v0.51.231 — Release GY (stage-q1) (#3484)
## Release v0.51.231 — Release GY (stage-q1)

Four independent, low-risk fixes batched into one release. All gates green.

### Fixes
| PR | Author | Fix |
|----|--------|-----|
| #3368 | @nesquena-hermes | `/model` resolves models from the truncated `extra_models` tail of a large provider catalog (completes the half-done #3368 fix; @garyd9's `mimo-v2.5` case) |
| #3457 | @pix0127 | Auto-hide the Plugins settings tab when no plugins are installed; deep-link fallback to Conversation |
| #3474 | @Mubashirrrr | Guard malformed/negative `depth` on `/api/sessions/search` (no more 500 on `?depth=deep`, no silent newest-message drop on negative) |
| #3433 | @sjh9714 | Resolve symlinked home before the trust comparison so `~/` workspace suggestions work when the process home is a symlink |

### Note on #3457
The original contributor patch reassigned a `const section` (a **runtime `TypeError` brick** on the settings panel) and placed the hidden-tab fallback *after* the panes were already toggled. Refactored to `let` + hoist the fallback above all consumers of `section`. ESLint runtime gate now clean.

### Gate results
- **Full pytest suite**: 7441 passed, 7 skipped, 3 xpassed, **0 failed**
- **ESLint runtime gate**: CLEAN (caught + fixed the #3457 const-reassign)
- **ruff forward gate**: CLEAN (no new violations on changed lines)
- **browser-smoke gate**: CLEAN (`/`, `/#settings`, `/#sessions` — zero console errors)
- **Codex (regression)**: SAFE TO SHIP
- **Opus (correctness)**: SAFE TO SHIP

Closes #3368. Closes #3457. Closes #3433.

Co-authored-by: pix0127 <pix0127@users.noreply.github.com>
Co-authored-by: Mubashirrrr <Mubashirrrr@users.noreply.github.com>
Co-authored-by: sjh9714 <sjh9714@users.noreply.github.com>
2026-06-03 09:13:01 -07:00
nesquena-hermes
7c6fb33549
Release v0.51.229 — Release GW (stage-p13 — /model never silently snaps a versioned name to a -tier variant #3368) (#3465)
* Release v0.51.229 (stage-p13): /model never silently snaps versioned name to -tier variant (#3368, @nesquena-hermes)

Agent-authored, nesquena-APPROVED. Rebased onto current master. Both _findModelInDropdown
(ui.js) and _bestModelMatch (commands.js) reject a prefix-snap when the typed target ends
in a version digit and the longer option's extra text is a variant/tier suffix (.pro) rather
than a version continuation (.digit). Adds _nearestModelSuggestion + 'did you mean?' toast.
34 tests pass (14 new #3368 + 20 regression: #1188 fuzzy + #3360 collision).

* fix(commands): /model did-you-mean toast renders suggestion + single quotes (#3368 review)

Live-render review of the approved #3437 caught two toast-assembly bugs in cmdModel:
(1) t('model_did_you_mean') was called WITHOUT the suggestion arg — model_did_you_mean
is a (m)=>... template that t() invokes, so it rendered 'did you mean "undefined"?';
fixed to t('model_did_you_mean', suggestion). (2) no_model_match already ends with an
opening quote, so '"${args}"' doubled it ('No model matching ""deepseek-v4""'); fixed
to '${args}"'. +4 source-assertion regression tests. Verified live: toast now reads
'No model matching "deepseek-v4" — did you mean "deepseek/deepseek-v4-pro"?'.

* fix(commands): slash-qualified versioned no-snap falls through to suggestion (#3368 Codex CORE)

Codex review found a 2nd no-snap layer the version guard missed: for a slash-qualified
versioned query (e.g. 'xiaomi/mimo-v2.5') whose only near catalog entry is a rejected
tier variant ('xiaomi/mimo-v2.5-pro'), cmdModel's cross-provider /api/session/update
fallback would silently persist the invalid model + 'Switched to...'. Now gated on
!versionedNoSnap (_looksLikeVersionedModel(bare) && a near suggestion exists) so it falls
through to the 'did you mean?' toast; genuinely off-catalog providers (no near variant)
still direct-update. Verified live: '/model deepseek/deepseek-v4' no longer switches, shows
suggestion toast. +1 regression test.

---------

Co-authored-by: nesquena-hermes <[email protected]>
2026-06-02 21:35:09 -07:00
Vanshaj Pahwa
0ea5e4ef69 Fix model selection shadowing shorter model names (#3368)
The /model slash command's fuzzy fallback used a substring match that
took the first option containing the query, so "/model mimo-v2.5"
selected "mimo-v2.5-pro" instead of "mimo-v2.5".

Replace both fallback loops with a shared _bestModelMatch helper that
prefers an exact id/label match and otherwise returns the shortest
option containing the query, so a more specific query is no longer
shadowed by a longer variant. The longer variant is still selectable by
querying its full name.

Closes #3368
2026-06-02 17:11:37 +00:00
theseussss
b554fd8078 fix(reasoning): 推理强度补齐 max 档,与 hermes_constants 对齐
WebUI 镜像的 VALID_REASONING_EFFORTS 此前止于 xhigh,漏了底层
hermes_constants 已有的 max 档。补齐后端常量、前端命令/下拉/帮助文本,
并同步快照测试。支持 max thinking 的 provider(如 Anthropic)现可在
composer 下拉和 /reasoning 命令中选到 Max。
2026-05-29 03:05:02 +08:00
Roberto Villegas
a9ce2889af
fix(ui): hide reasoning chip when model lacks effort levels
Resolve supported reasoning efforts per active model/provider and pass
that context through /api/reasoning so Composer and other non-configurable
models no longer show a misleading effort picker.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 18:42:31 -06:00
Eleanor Berger
2e91c0f81e fix: honour skin value in theme command 2026-05-20 00:09:06 +00:00
nesquena-hermes
b170980522 Stage 382: PR #2504 2026-05-18 03:43:01 +00:00
nesquena-hermes
50d4f4cfb9 Stage 379: PR #2480
# Conflicts:
#	CHANGELOG.md
2026-05-17 23:35:19 +00:00
Michael Lam
f986507809 fix: align fork-from-here with merged messaging history 2026-05-17 15:01:57 -07:00
ts2111
64db8bd794 fix: support /model alias switch for cross-provider custom models
Backend (api/config.py):
- resolve_model_provider(): check custom_providers for prefix match
  BEFORE the config_base_url branch. Previously, providers with a
  base_url set (e.g. deepseek) would catch all slash-delimited model
  ids and return the config provider, preventing custom provider
  routing.
- get_available_models(): include model aliases in response so the
  frontend can resolve them on /model commands.

Frontend (static/commands.js):
- cmdModel(): resolve aliases by fetching /api/models before fuzzy
  matching the dropdown.
- Add bare-model fallback when the alias resolves to a slash-delimited
  provider/model id (e.g. "deepseek/deepseek-v4-flash").
- Add cross-provider fallback: when the model is from a custom provider
  not in the active provider dropdown, call /api/session/update directly
  with the provider/model id and provider override.
2026-05-17 21:22:06 +02:00
Michael Lam
3bb8c7b276 fix: guard localStorage quota writes 2026-05-16 07:31:44 -07:00
Hermes Agent
97b60c68b8 Merge pull request #2198 into stage-349
Fix fork-from-here keep count for truncated sessions (Michaelyklam)
2026-05-13 19:02:59 +00:00
Michael Lam
c522384c6d Fix fork-from-here keep count for truncated sessions 2026-05-13 09:57:40 -07:00
Hermes Agent
fbd1e27181 Merge pull request #2185 into stage-348
fix: prevent 404 on /api/session/compress/status during session switch (jasonjcwu)
2026-05-13 16:34:40 +00:00