Commit Graph

2428 Commits

Author SHA1 Message Date
nesquena-hermes 3a8f980e2b Release exp-v0.52.44: Docker/no-git image update notices (#5733, #2729) (#5958)
* fix(#2729): show Docker image update notices

* Release exp-v0.52.44: show Docker/no-git image update notices (#5733, #2729)

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 16:46:13 -07:00
nesquena-hermes b7b6b3a4c8 Release exp-v0.52.43: trusted-header auth / reverse-proxy SSO (#5568, #3351) (#5956)
* feat(#3351): add trusted-header auth core

* fix(#3351): reconcile trusted sessions per request

* fix(#3351): preserve valid trusted group mappings

* fix(#3351): defer trusted logout reconciliation until after csrf

* fix(auth): clear _pending_set_cookies in reset_trusted_auth_request_state (keep-alive cookie cross-request leak)

Codex gate CORE finding: a Set-Cookie queued in request N but not flushed
survived onto the reused HTTP/1.1 handler and could be emitted by request N+1
— after trusted-identity rotation on logout this could overwrite a valid login
cookie and 401 the user. Reset the queue at the per-request boundary + add a
keep-alive regression test.

* docs(.env.example): document trusted-header auth / reverse-proxy SSO env vars

HERMES_WEBUI_TRUSTED_AUTH_HEADER + TRUSTED_PROXY_CIDRS (with exact-proxy-IP
security warning) + optional groups-header/JSON group-profile-map/logout-url.

* Release exp-v0.52.43: trusted-header auth / reverse-proxy SSO (#5568, #3351) + keep-alive cookie fix + docs

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 16:34:14 -07:00
nesquena-hermes 18793a1ba4 Release exp-v0.52.42: ?profile= deep links (#5730) + Russian locale refresh (#5778) (#5953)
* Refresh Russian localization bundle

* feat(#5682): honor profile query during boot

* fix(profile): refresh deep-link reasoning state (#5682)

* test(ci): initialize reasoning transition harness state

* test(ci): accept reasoning fetch context override

* fix(#5682): preserve retryable profile query intent

* test(#5682): accept profile switch result contract

* fix(#5682): bind reasoning fetches to profile transitions

* Release exp-v0.52.42: ?profile= deep links (#5730) + Russian locale refresh (#5778)

---------

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 15:58:47 -07:00
nesquena-hermes 20d258d552 Release exp-v0.52.40: ACP sessions in sidebar (#5939) + date-stamped Claude 3.0 reasoning fix (#5934) (#5951)
* fix(reasoning): stop date-stamped Claude 3.0 ids from reading reasoning-capable

_candidate_supports_reasoning captured a trailing date stamp as the minor
version so bare date-stamped Claude 3.0 ids like claude-3-opus-20240229
satisfied the major==3 and minor>=7 gate and wrongly surfaced reasoning-effort
controls. Cap the minor group to 1-2 digits with a (?!\d) guard so the date
stamp is not read as a minor version -- the same date-stamp defense
_is_pre_adaptive_anthropic already uses. Claude 3.0/3.5 now correctly report
no reasoning support; 3.7+ and 4.x (incl. date-stamped builds) are unaffected.

* test(reasoning): cover date-stamped Claude 3.0 not reasoning-capable

Locks the heuristic so claude-3-opus-20240229 (and sibling date-stamped 3.0
ids) report no reasoning support while 3.7+/4.x stay capable.

* fix(session): classify ACP adapter sessions into the CLI sidebar family

Gateway ACP adapter sessions (source='acp' in state.db — Zed, external
device bridges like a Rabbit R1) normalised to session_source 'other',
which fell through BOTH sidebar buckets: sidebar_source=webui skips the
state.db projection entirely (and ACP rows have no WebUI sidecar), while
sidebar_source=cli keeps only CLI-classified rows. Result: ACP
conversations were invisible and could never be clicked/imported.

Classify 'acp' as a CLI-family source, mirroring 'tui' (a local
interactive agent client): normalize_agent_session_source maps it to
session_source 'cli' with label 'ACP', is_cli_session_row recognises the
raw source, is_cli_session_row_visible keeps ended/untitled ACP rows
visible (they are always user-driven), and frontend _isCliSession
accepts raw === 'acp'. Zero-message ACP connect/reconnect stubs stay
hidden via the existing message-count guard. Claim policy is untouched:
ACP stays claimable like CLI/TUI per the documented 'future local agent
sources' allowance in _is_claimable_cli_source.

Rollback: revert this commit (pre-change file copies also in
~/.hermes/backups/acp-sidebar-fix-20260711/), then restart WebUI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(session): require a user turn before surfacing ACP rows

An ended ACP connection can record only assistant/tool/system messages
(replayed or aborted turn), which the blanket TUI-style visibility
exception would surface despite not being user-driven. Gate the ACP
branch on _count_user_turns(row) > 0, per Greptile review on #5939.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Release exp-v0.52.40: ACP sessions in sidebar (#5939) + date-stamped Claude 3.0 reasoning-control fix (#5934)

---------

Co-authored-by: 黄云龙 <76432572+nankingjing@users.noreply.github.com>
Co-authored-by: ai-ag2026 <261867348+ai-ag2026@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-11 15:20:41 -07:00
Matt Van Horn 14f6103655 fix: teach ephemeral system prompt that redacted credential values are intentional 2026-07-11 00:15:47 -07:00
nesquena-hermes 7f88b66ff9 Merge salvage/5461-plugin-catalog 2026-07-11 05:27:11 +00:00
nesquena-hermes 7176b3a756 fix(config): route plugin providers before config_provider bare-passthrough
Gate found the plugin-routing branch ran AFTER the provider==config_provider
early-return, so when an ACTIVE plugin provider was also the configured default,
model_with_provider_context returned a bare model and dropped the '@plugin:'
hint -> wrong backend. Moved the _is_plugin_model_provider() branch above the
config_provider passthrough. Regression test (GMI + anthropic/claude-sonnet-4.6
as the active plugin+configured provider) added. Co-authored-by: alexfoxtm.
2026-07-11 05:18:27 +00:00
nesquena-hermes bd1cdf24c7 Merge #5910: add 'max' reasoning effort level (opt-in, availability-gated) 2026-07-11 04:47:37 +00:00
nesquena-hermes 742ccd59b5 feat(reasoning): make 'max' selectable only when actually available (#3505 refinement)
Maintainer call (2026-07-11): 'set max when available, don't when not.' Two layers:
- resolve_model_reasoning_efforts() now uniformly passes its sourced list through
  the provider ceiling filter (preserving any 'none' sentinel), so the UI dropdown
  offers 'max' ONLY for models whose native ladder genuinely includes it (adaptive
  Claude 4.6+, DeepSeek) and hides it everywhere it would be rejected/mishandled
  (GPT-5, o-series, Gemini, legacy/cloud-hosted Claude, unknown providers).
- coerce_reasoning_effort_for_model() default-denies a stale/CLI 'max' to xhigh on
  an UNRECOGNIZED provider (empty capability list) so it can never 400, while a
  RECOGNIZED reasoning provider whose exact model id we couldn't resolve (e.g.
  claude-opus-latest) still preserves 'max'. All other levels keep #3505
  preserve-verbatim. New _provider_known_reasoning_capable helper + regression tests.

Co-authored-by: perejaslav <perejaslav@users.noreply.github.com>
2026-07-11 04:42:35 +00:00
nesquena-hermes b779f0b777 fix(reasoning): extend max ceiling to OpenAI-family + cloud-hosted Claude lanes
Re-gate found 'max' still reached provider adapters that reject/mis-handle it on
lanes beyond openai-codex/native: direct openai + openai-api + azure-foundry
GPT-5 (APIs top out at xhigh), and Azure-Foundry/Bedrock-hosted LEGACY Claude
(the pre-adaptive classifier only matched native Anthropic provider names, so max
survived to the 8k manual-thinking budget). Extended the GPT-5/o-series ceiling
to all OpenAI-family lanes and the pre-adaptive-Claude ceiling to azure/bedrock/
vertex lanes (gated on 'claude' in the model id so non-Claude models on those
lanes are untouched). Regression tests for both lanes.
2026-07-11 04:06:36 +00:00
nesquena-hermes 381bcb4822 fix(reasoning): classify date-stamped + claude-3 IDs as pre-adaptive
Re-gate found _is_pre_adaptive_anthropic misclassified date-stamped legacy IDs
(claude-sonnet-4-20250514, claude-opus-4-20250514) and the claude-3 family
(claude-3-opus-20240229) as adaptive, so 'max' survived to the 8k manual-thinking
fallback instead of degrading to xhigh (32k-equivalent). Rewrote the helper:
claude-3* always pre-adaptive; a 6+ digit date stamp is not a minor version so
'4-20250514' is a pre-adaptive 4.0 build; 4.6+/latest stay adaptive. Extended
regression tests to both ID shapes.
2026-07-11 03:52:10 +00:00
nesquena-hermes 96b04359ab fix(reasoning): degrade 'max' via provider ceiling, not sourced list
Gate found removing the max->xhigh coercion let 'max' reach provider adapters
whose native ladder tops out lower, degrading WORSE than before: Gemini treats
unknown 'max' as medium; pre-adaptive Claude (3.7/4.5) manual-thinking lacks a
'max' budget and falls to 8k. Fix: coerce_reasoning_effort_for_model now applies
the hard provider ceiling (_filter_reasoning_efforts_for_provider) FIRST and
degrades down the ladder when the ceiling excludes the requested level — even
when the sourced capability list is empty (unrecognized) or wrongly advertises
the level. Gemini + all Google aliases and pre-4.6 Anthropic now cap 'max' at
xhigh; adaptive Claude 4.6+/DeepSeek/etc. preserve it. Regression tests added.

Co-authored-by: perejaslav <perejaslav@users.noreply.github.com>
2026-07-11 03:40:11 +00:00
nesquena-hermes 54efd6078e feat(workspace): opt-in 'start new chat when switching workspace' (#5473)
Salvage-rebuild of #5473 (@ruizanthony), reworked to OPT-IN per maintainer
decision. New default-OFF setting new_chat_on_workspace_switch: when enabled,
selecting a DIFFERENT workspace starts a fresh chat bound to that workspace
(leaving the current conversation on its original workspace, avoiding stale
cross-workspace context) instead of mutating the current session in place.
Same-workspace selection stays an in-place refresh. Default off preserves the
shipped behavior for everyone.

Wiring: config default + bool key; window._newChatOnWorkspaceSwitch set on
boot + autosave; Settings checkbox (Preferences); gated branch in
switchToWorkspace() that runs before the in-place /api/session/update; 3 i18n
keys across all 15 locales; regression tests (default-off, gated-branch-present,
flag-wiring, checkbox+i18n).

Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-11 03:13:48 +00:00
nesquena-hermes 8c9d3635e2 lint: fix B904 raise-from in share session-resolve + drop unused importlib
CI ruff gate flagged 3 new violations: two api/routes.py KeyError re-raises in an
except-KeyError block (add 'from None') + an unused importlib import in the share
security test. All trivial.
2026-07-11 02:42:59 +00:00
nesquena-hermes a1312acc5d fix(share): reject dict-valued text-blocks and dict titles (re-gate edge)
The re-gate found the always-on sanitizer still had two str()-on-dict paths:
a list text-block whose 'text' is itself a dict (via /api/session/import), and
a dict-valued session title. Both are now type-checked to str; a dict title
falls back to 'Untitled'. Regression cases added. Co-authored-by: MicoRobot.
2026-07-11 02:14:07 +00:00
nesquena-hermes 75153f5b79 fix(share): always-on redaction boundary + rebase/UX fixes (gate findings)
Adversarial security re-gate on the rebased head found 4 issues; fixed:

1. [CORE] api/shares.py public snapshot could leak credentials/paths/tool
   payloads. The sanitizer relied on redact_session_data (gated on the
   user-toggleable api_redact_enabled) — disabling it exposed API keys, a dict
   assistant content was stringified as raw tool data, and session.workspace
   survived in transcript text. Now the per-message sanitizer applies ALWAYS-ON
   credential redaction (_redact_fn_cached, force=True, ignores the setting) +
   scrubs known session/workspace/home paths, and refuses to stringify non-text
   structured content. Title is force-redacted too. Regression test proves the
   boundary holds with api_redact_enabled=False.
2. [SILENT] api/models.py: my rebase wrongly kept anchor_activity_scenes in
   METADATA_FIELDS, moving 250-480KB scene bodies before messages and undoing
   master's metadata fast-path (#5854). Removed; 17 #5854 tests pass again.
3. [SILENT] static/index.html: the Share/StopSharing/ExportHTML button triplet
   was double-applied by the rebase (duplicate IDs → only the first wired).
   Deduped to one each + a count assertion test.
4. tests: fixed the stale Session.load-after-/api/session/new setup (master
   keeps new sessions memory-only until first message).

Also (UX, found in the visual flow sweep): a clipboard-copy failure after a
SUCCESSFUL share showed a red 'Share failed' toast and skipped opening the page.
The copy is now isolated — the share succeeds, the page opens, and the toast
shows the link if the clipboard write fails, never a false failure.

Co-authored-by: MicoRobot.
2026-07-11 02:06:34 +00:00
nesquena-hermes e061e8534c Rebase #5337 (public read-only conversation sharing) onto master
Rebased 363 commits forward (branch-point 2026-07-05). Applied the PR's code
diff cleanly across 16 files; resolved api/models.py additively (both master's
process_wakeup_pause/anchor_scene_index fields AND the PR's share_token/
share_created_at fields kept in the Session constructor, metadata list, and
to_dict). CI workflow (.github/workflows/tests.yml) intentionally NOT taken from
the stale branch — master owns it (the branch's apparent removal of Playwright/
Office steps was a PAT-workflow-scope revert; rebase restores master's CI).

Co-authored-by: MicoRobot. Concept approved by maintainer (opt-in public share,
2026-07-11). Note: 4 dynamic share tests fail identically on the original
unrebased head (pre-existing test-env Session.load fragility, not rebase-caused);
to be resolved during the security re-gate in a proper sandbox.
2026-07-11 01:39:42 +00:00
nesquena-hermes d56123f796 feat: add max reasoning effort level (salvage of #4627)
Salvaged from #4627 by @perejaslav. Core Hermes already supports
reasoning_effort=max; this closes the WebUI parity gap (Anchor-A) — adds
max to VALID_REASONING_EFFORTS + dropdown/chip/reasoning-command, keeps
the degrade ladder for capped models.

Co-authored-by: perejaslav <perejaslav@users.noreply.github.com>
2026-07-11 01:13:36 +00:00
nesquena-hermes 553ad8146e fix(api/config): surface + route plugin-only providers in static catalog
Salvaged from #5461 by @alexfoxtm — the catalog-surfacing half was correct; this rebuilds it fresh on master and adds the missing routing (model_with_provider_context -> @plugin:model) + a routing regression test.

Co-authored-by: alexfoxtm <alexfoxtm@users.noreply.github.com>
2026-07-11 01:11:37 +00:00
nesquena-hermes 0af0a5fcf0 Merge #5746: final answer only activity display mode 2026-07-10 23:50:28 +00:00
t 3188f88599 fix(#4812): break live SSE loop on a terminal event even when deduped at cutoff
Codex round-3 finding #2 [CORE]: when reconciliation replayed the active run's
terminal event at the subscribe cutoff, the live queue's copy hit the cutoff/dedup
'continue' BEFORE the terminal 'break' check — so the handler never broke, stayed
blocked on the dead run's queue, and missed subsequent session runs. Check the
terminal event type before the already-delivered continue: a terminal always ends
the loop (after emitting if not already sent). Regression test asserts the handler
unsubscribes (doesn't hang) when the terminal is deduped. 24/24 SSE tests green.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:47:06 +00:00
t 2b22690fa4 fix(#4812): capture resume cursor + journal baseline before SSE headers commit
Codex round-3 finding #1 [SILENT]: baselining after end_sse_headers() left a window
where a run completing between header commit and baseline was absorbed into the
baseline + silently lost. Move resume-id parse + fingerprint baseline before
send_response/headers (both side-effect-free: header read + stat-only fingerprint).

Codex round-3 also flagged finding #2 [CORE] (reconciliation terminal-event-at-cutoff
dedup keeps the handler attached to a dead run queue) — NOT addressed here; escalating
to Nathan per Codex-depth diminishing-returns (3rd round into the same SSE seam class).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:44:10 +00:00
t 3c642e08f1 fix(#4812): hoist idle SSE fingerprint baseline to earliest point (before preliminary lookup + initial replay)
Codex re-gate found the baseline was still after the preliminary
_active_run_stream_for_session() lookup + the initial cursor read_session_run_events()
replay — a run completing during either window folded into the baseline and was
silently missed. Move the fingerprint baseline to immediately after resume_event_id
parsing, before both operations, so no pre-baseline completion window remains. Remove
the now-redundant later baseline. 23/23 SSE tests green.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:36:22 +00:00
t 3ac19a5518 fix(#4812): capture idle SSE fingerprint baseline BEFORE first attach (TOCTOU)
Final-gate Codex caught a TOCTOU race in the prior idle-seam fix: the fingerprint
baseline was captured AFTER the first attach_active_stream() lookup, so a run
completing DURING that first attach folded into the baseline and was silently
missed (only : keepalive emitted, no snapshot). Move the baseline capture before
the first attach so any completion from that point on is detected. Add an ordering
regression test (baseline fp precedes the idle-loop attach + advance still emits
one snapshot). 23/23 SSE tests green.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:24:47 +00:00
t 2829843a2a fix(#4812): re-sync idle no-cursor SSE subscriber on run completing during keepalive wait
Fable gate found a seam Codex's 3 boundary findings didn't cover: an idle no-cursor
subscriber to /api/sessions/{id}/events misses a run that starts AND finishes inside a
single keepalive tick — the wait loop only looked for a live in-memory STREAMS entry to
attach, never noticing the journal advanced, so the run's transcript was silently missing
until manual refresh.

Add a cheap bounded session_journal_fingerprint() (file-count/max-mtime/total-size from
stat only, never parses bodies) and, inside the idle attach-wait loop, emit a session_snapshot
re-sync whenever the fingerprint advances with no live stream to attach — the same honest
recovery contract used for a failed reconciliation. Re-baselines after each re-sync so a quiet
idle connection never spams snapshots. 2 regression tests; 22/22 SSE tests green.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:15:49 +00:00
t 9600e1546a Merge #5677: resumable session SSE stream (#4812)
Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-10 20:12:27 +00:00
t d967131056 fix(#3929 ux): gate wakeup-pause disclosure on actual pause + cover both error paths
Codex re-gate caught 2 defects in the prior UX commit:
1. streaming.py:8921 — the disclosure fired for ANY process_wakeup failure (e.g.
   rate-limit) even when no pause was recorded -> falsely claimed 'retries paused'
   while process_wakeup_pause stayed empty. Now gated on the record_* return value.
2. streaming.py:10130 — the raised-exception credential_pool_empty path (exercised
   by the existing agent test) recorded the pause but omitted the disclosure. Added
   the same conditional disclosure there.
Both paths now keep _error_payload['hint'] in sync with the persisted bubble, and
the disclosure appears only when a pause was actually recorded. 69 tests green.
2026-07-10 19:28:06 +00:00
t c26bc21194 ux(#3929): disclose wakeup pause in error card + changelog
Fable UX-gate follow-up on #5732: after the first credential-exhausted error,
automatic wakeups are silently suppressed. Disclose it in the wakeup-source
error card ('Automatic retries for this conversation are paused until you send
a message, switch the model/provider, or fix the credentials.') so the silence
reads as intentional, not a stuck agent — Gmail/Dropbox account-needs-attention
convention. Scoped to the process_wakeup + credential_pool_empty seam only.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-07-10 19:16:48 +00:00
Frank Song 3c95b3a9d5 Merge remote-tracking branch 'origin/master' into paperclip/pr-5732 2026-07-11 02:07:37 +08:00
Rod Boev 4cbc70c778 fix(#4812): satisfy replay boundary lint gate 2026-07-10 10:51:34 -04:00
Rod Boev 956c276362 fix(#4812): close session SSE replay recovery gaps 2026-07-10 10:46:47 -04:00
Frank Song 4c2400261a Merge remote-tracking branch 'origin/master' into paperclip/pr-5732 2026-07-10 21:21:47 +08:00
igorroncevic dea5e8a96d fix: fail closed on incomplete OpenRouter pricing 2026-07-10 15:18:39 +02:00
nesquena-hermes d7c3c2b734 Release: repair stale session provider binding at chat start (#5873, #5731) (#5881)
* fix(#5731): repair poisoned session provider at chat start

* fix(#5731): fail safe on incomplete catalog ownership

* changelog: session provider-repair on stale binding (#5873, #5731)

---------

Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: t <a@b>
2026-07-10 06:11:34 -07:00
Rod Boev da885659bd fix(streaming): satisfy session SSE lint gate 2026-07-10 07:37:16 -04:00
Rod Boev b2a9ca28ad fix(streaming): harden session SSE replay (#4812) 2026-07-10 07:31:00 -04:00
Frank Song c641a9d37c Keep streaming terminal events after no-pause success 2026-07-10 14:43:41 +08:00
Frank Song a61586bd7f Preserve latest session on wakeup pause clear 2026-07-10 13:20:40 +08:00
Frank Song 92ea69c7c3 Require fingerprint evidence for wakeup credential recovery 2026-07-10 12:23:02 +08:00
Frank Song 8eabf63049 Lock streaming wakeup pause success clear 2026-07-10 12:01:24 +08:00
Frank Song e604b76720 Merge remote-tracking branch 'origin/master' into paperclip/pr-5732 2026-07-10 11:39:50 +08:00
Frank Song 920b5e8864 Preserve wakeup pause on cancelled stale credential failure 2026-07-10 11:39:31 +08:00
nesquena-hermes de0e6eb452 Release: centralize auxiliary-model task catalog (#5865) (#5870)
Auxiliary-model task definitions were maintained in BOTH Python and JS and could
drift (the retired session_search task lingered in one). Centralize to a single
backend catalog that panels.js renders+saves from exclusively; purge the retired
slot on reset while preserving other custom bindings; old configs still load.

Gate-pass: Codex SAFE (only session_search removed, 11 other tasks + defaults
unchanged, 22 keys match across 15 locales, legacy migrate preserves custom
bindings, executable DOM harness rendered all rows). 53 own + 887 regression green.

Co-authored-by: t <a@b>
Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-07-09 20:31:54 -07:00
Frank Song bd083c8b54 Merge remote-tracking branch 'origin/master' into paperclip/pr-5732 2026-07-10 11:19:07 +08:00
nesquena-hermes 9c1e77de48 Release: fix Experimental channel version display on stable-tagged installs (#5864, #5862) (#5868)
* fix(updates): scope installed-version fallback to webui + verified current_sha (#5864 gate fixes)

Gate (Codex) found 2 CORE in the #5862/#5864 experimental-version-display fix:
1. api/updates.py:947 — the installed-version fallback ran in shared _check_repo_release
   for BOTH repos, injecting WEBUI_VERSION (v0.52.0, not an Agent ref) into Agent payloads.
   Now scoped to name=='webui'.
2. api/updates.py:996 — current_sha reused current_version_display, but WEBUI_VERSION can be
   vX.Y.Z-dirty-<hash>/-N-g<sha>/bare-SHA/unknown (not git refs) -> broken /compare link +
   lost update-summary subjects. current_sha now derives from a git-VERIFIED exact-match tag
   (channel-neutral) or None; current_version keeps the real display string.

+2 regression tests (agent-repo-no-inject, dirty-HEAD-verified-sha). 21/21 update-channel green.

* changelog: #5864 experimental-channel version display fix (#5862)

---------

Co-authored-by: t <a@b>
2026-07-09 20:18:03 -07:00
Frank Song fc262a4aec Fix named-profile wakeup credential recovery 2026-07-10 11:14:08 +08:00
Frank Song 63dda7fe2d Merge remote-tracking branch 'origin/master' into paperclip/pr-5732
# Conflicts:
#	api/models.py
2026-07-10 10:15:21 +08:00
nesquena-hermes e4342a65cf Release: split anchor_activity_scenes after messages to fix #4633 server memory growth (#5858, #5854) (#5863)
Reasoning-heavy sessions accumulate 250-480KB of anchor_activity_scenes serialized
BEFORE the messages array, so the 64KB metadata-prefix read always overflowed ->
full-parse fallback on every read -> memory growth. Move scene data AFTER messages;
metadata prefix now ~2KB, modern reads skip the full parse. Old-layout files still
load (one authoritative parse then cached); round-trip preserves scenes exactly.

Independent review: nesquena APPROVED (end-to-end, on head). Gate-pass: Codex SAFE
(933KB legacy sidecar loads clean + round-trip verified), 17 own + 2945 regression green.

Co-authored-by: t <a@b>
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-07-09 19:05:54 -07:00
Frank Song d7e7fb3013 Merge remote-tracking branch 'origin/master' into paperclip/pr-5732 2026-07-10 07:08:53 +08:00
Frank Song 67b001e57c Fix wakeup pause recovery races 2026-07-10 07:08:36 +08:00