HERMES-GIELIE/api
nesquena-hermes cccb97d970
Release v0.51.222 — Release GP (stage-p4 — backend bugfix batch: title language drift #3293 + orphaned CLI sidecar prune #3238 + pin-quota lineage #3288) (#3452)
* fix: reject cross-script drifted auto-generated session titles (#3293)

The title-language mismatch guard only knew two states: German (de) or empty,
and _title_language_mismatch early-returned False whenever the user start
wasn't German. So an English conversation whose LLM-generated title came back
in Chinese / Spanish / Russian sailed through and persisted with
llm_title_generated=true. The German case was the only one covered because
that's the one prior report it was built for.

Generalize from a German-specific binary to a language-agnostic cross-script
check. Add _script_counts() + _dominant_script() (cheap, dependency-free
Unicode-block classification: latin / cjk / cyrillic / arabic / hebrew / greek
/ devanagari). _title_language_mismatch now rejects a title that introduces a
substantial amount (>=35% of alphabetic chars, min 2) of a script different
from the conversation start's dominant script — so short titles that embed a
borrowed Latin technical term still trip, while an English title with a single
foreign place-name does not. The legacy German->English same-script heuristic
is preserved verbatim.

Kept api/streaming.py ASCII-only (the test_title_generation_source_has_no_cjk_
literals guard) — all CJK examples live in the test file, not the source.

Closes #3293

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

* fix: prune orphaned imported-CLI sidecars from the WebUI sidebar (#3238)

When a CLI/agent session is opened in WebUI it gets a WebUI-owned sidecar
(webui/sessions/<id>.json + _index.json row) so it can render and reopen;
all_sessions() then returns it independently of the agent state.db. If the
user later deletes that session from the CLI / local Hermes storage, nothing
pruned the sidecar — the merge loop only overlays CLI metadata when a matching
state.db row exists and otherwise continues, so the stale row lingered in the
sidebar indefinitely (there is no WebUI delete affordance for CLI rows).

Add api.models.agent_session_row_exists(): an exact, uncapped existence probe
against the state.db sessions table. The sidebar merge loop now drops a row
that is_cli_session_row + not WebUI-native + absent from cli_by_id + whose
state.db row is genuinely gone, and calls prune_session_from_index() so
_index.json self-heals.

The state.db probe is deliberate: get_cli_sessions() caps at
CLI_VISIBLE_SESSION_LIMIT (20), so a still-existing session can fall out of
that window and look deleted — pruning on cli_by_id absence alone would delete
live sessions. WebUI-native rows with a CLI ancestor are never pruned, and any
probe error degrades to keep-the-row so a transient failure can't lose data.

Closes #3238

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

* fix: count pin quota by visible session lineage

* docs(changelog): v0.51.222 — backend bugfix batch (#3293 title drift, #3238 sidecar prune, #3288 pin lineage)

* fix(pins): forks count as own pin lineage, not collapsed to parent (#3288 Codex follow-up)

Codex review of the batch found a pin-limit UNDERCOUNT: _session_row_lineage_root_id
followed any parent_session_id to the root, but /api/session/branch creates
independent visible fork sessions that also carry parent_session_id (session_source=
'fork'). Two pinned forks of the same parent collapsed to one quota lineage, letting
a user exceed pinned_sessions_limit with no 400. Fix: a fork returns its own id as
its lineage root (it's a separately-visible session); only compression/continuation
rows still collapse to a shared root. Adds a regression test with two pinned forks +
the parent counting as three distinct lineages, and confirms the existing
pre-compression-snapshot collapse case still passes.

* test(pins): update #2508/#2821 source-match tests for #3288 lineage rename

#3288 replaced the raw-session-id pin counter (pinned_ids set) with a
visible-lineage counter (pinned_lineage_ids via _visible_pinned_lineage_ids over
persisted_rows/candidate_rows). Two pre-existing source-string-matching tests
asserted the OLD implementation literals (pinned_ids = {, _session_field(existing,
session_id...), len(pinned_ids) >=). Updated both to assert the new mechanism while
preserving the invariants they actually guard: snapshot computed BEFORE LOCK (no
all_sessions()-inside-LOCK deadlock), quota filtering via the shared
_session_counts_toward_pin_quota helper, and the limit/400 guard. Behaviour
unchanged; these were implementation-detail assertions, not behaviour tests.

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: andrewkangkr <andrewkangkr@users.noreply.github.com>
Co-authored-by: Luxciax <Luxciax@users.noreply.github.com>
Co-authored-by: Andy Kang <andrewkang.kr@gmail.com>
2026-06-02 17:35:18 -07:00
..
__init__.py
agent_health.py fix: probe remote gateway via HERMES_API_URL before local fallback (#3312) 2026-06-01 19:38:00 +00:00
agent_sessions.py fix: keep webui mirrored sessions out of cli filter 2026-05-28 00:51:12 +02:00
auth.py fix: harden WebUI bugfix sweep 2026-05-28 13:38:50 -04:00
background.py feat(commands): /background, /btw slash commands + undo button + reasoning chip 2026-04-24 01:24:51 +00:00
clarify.py feat: add distinct attention request sound 2026-05-30 19:40:40 +00:00
commands.py fix(security): drop unsafe-eval + add jsdelivr to CSP, sanitize plugin error 2026-05-11 17:53:02 +00:00
compression_anchor.py fix(sidebar): keep newer continuation visible over older snapshot 2026-05-28 13:38:06 -06:00
config.py fix(config): extend #3429 URI-scheme fix to backend normalization + model-key matching (#3436, @b3nw) 2026-06-02 21:04:02 +00:00
dashboard_probe.py fix: preserve IPv6 dashboard link brackets 2026-05-19 21:35:10 -04:00
extensions.py Opus pre-release follow-ups for PR #1445 2026-05-02 03:49:40 +00:00
gateway_chat.py fix(gateway): carry WebUI delivery context on gateway-backed path too (Codex MUST-FIX on #3324) 2026-06-02 19:42:14 +00:00
gateway_watcher.py release: v0.50.241 (#1293) 2026-04-29 19:54:07 -07:00
goals.py fix(i18n): localize /goal runtime status strings 2026-05-10 15:21:24 +08:00
helpers.py fix(todos): hydrate cold-load state from session snapshot 2026-06-02 17:33:49 +00:00
kanban_bridge.py perf(http): enable HTTP/1.1 keep-alive 2026-05-24 18:26:56 +00:00
metering.py fix: show TPS in assistant message headers 2026-05-04 21:26:43 +00:00
models.py Release v0.51.222 — Release GP (stage-p4 — backend bugfix batch: title language drift #3293 + orphaned CLI sidecar prune #3238 + pin-quota lineage #3288) (#3452) 2026-06-02 17:35:18 -07:00
oauth.py fix(oauth): serialize Anthropic env fallback reads 2026-05-07 02:47:19 +00:00
onboarding.py Classify onboarding DNS probe failures consistently 2026-05-15 08:51:30 +08:00
passkeys.py feat: support passkey-only auth 2026-05-25 00:14:38 +00:00
paths.py fix(profiles): avoid config import cycle 2026-06-01 05:05:16 +00:00
plugins.py fix(#2622): plugin card UX — legible Open button, single badge, visible toggle + reject protocol-relative tab.path 2026-06-02 03:58:02 +00:00
profiles.py fix(profiles): TOCTOU-safe _SKILLS_STATS_CACHE read via .get() (Opus pre-release nit on #3341) 2026-06-01 18:38:39 +00:00
providers.py fix: honor shared OpenCode key at runtime 2026-05-30 02:18:06 +00:00
request_diagnostics.py fix: add request wedge diagnostics 2026-05-08 15:37:08 +00:00
rollback.py v0.50.255: Opus follow-ups (4 fixes) + CHANGELOG 2026-05-01 17:19:53 +00:00
routes.py Release v0.51.222 — Release GP (stage-p4 — backend bugfix batch: title language drift #3293 + orphaned CLI sidecar prune #3238 + pin-quota lineage #3288) (#3452) 2026-06-02 17:35:18 -07:00
run_journal.py fix: filter interrupted recovery control text from visible transcript (#3321) 2026-06-01 20:07:16 +00:00
runner_client.py harden: runner client rejects non-http(s) base_url + no redirect-follow (#3073) 2026-05-31 17:49:26 +00:00
runtime_adapter.py Stage 398: PR #2696 — feat(runtime): add runner-local adapter selection (RuntimeAdapter slice 4c, feature-flagged) 2026-05-21 17:43:54 +00:00
session_discoverability.py Merge PR #3048 2026-05-28 17:47:33 +00:00
session_events.py sync session list from server events 2026-05-20 08:18:56 -06:00
session_lifecycle.py fix: align WebUI memory commits with CLI boundaries 2026-05-17 05:04:57 -06:00
session_ops.py fix(session): preserve sidecar truncation boundary 2026-05-25 21:21:15 +08:00
session_recovery.py fix(session): rebuild missing startup index 2026-05-20 23:43:30 +08:00
skill_usage.py fix: remove webui-side writer to avoid conflict with agent 2026-05-27 20:57:32 +08:00
startup.py fix: P0 hotfixes — API regression, code block parser, chmod override 2026-05-01 12:10:48 +00:00
state_sync.py fix: use Optional[T] for default None parameters in state_sync.py 2026-06-01 17:20:17 +00:00
streaming.py Release v0.51.222 — Release GP (stage-p4 — backend bugfix batch: title language drift #3293 + orphaned CLI sidecar prune #3238 + pin-quota lineage #3288) (#3452) 2026-06-02 17:35:18 -07:00
system_health.py feat: add VPS resource health panel 2026-05-05 17:30:56 +00:00
terminal.py fix(terminal): harden supervisor lifecycle and eliminate timeout race conditions 2026-05-25 20:45:17 +05:00
todo_state.py fix(todos): hydrate cold-load state from session snapshot 2026-06-02 17:33:49 +00:00
turn_journal.py fix: handle Windows media and turn journal edge cases 2026-05-30 11:15:35 +02:00
updates.py fix(updates): re-exec correctly in both source and frozen builds 2026-06-02 17:33:49 +00:00
upload.py Release v0.51.221 — Release GO (stage-p3e — block all workspace symlink escapes + portable TOCTOU hardening [security]) (#3398) (#3451) 2026-06-02 16:56:30 -07:00
usage.py fix: display canonical cache hit percentage 2026-05-19 02:27:12 -06:00
workspace.py Release v0.51.221 — Release GO (stage-p3e — block all workspace symlink escapes + portable TOCTOU hardening [security]) (#3398) (#3451) 2026-06-02 16:56:30 -07:00
workspace_git.py fix(workspace): tighten git subprocess trust boundary 2026-05-20 11:02:45 +00:00
worktrees.py Harden worktree removal safeguards 2026-05-13 09:49:15 +08:00