Skip to content

Commit d0fc470

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/activity-feed-default-open
# Conflicts: # CHANGELOG.md
2 parents 601687a + cf003ae commit d0fc470

16 files changed

Lines changed: 814 additions & 20 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@
88
- Activity disclosures now have a Settings → Appearance option to expand the activity feed by default, while still honoring per-turn manual collapse/expand choices.
99
- Live Activity waiting rows now explain the agent's current step instead of stopping at `Waiting on model`, including a prompt/context review hint before tools run and the latest tool-derived action while the model chooses the next step.
1010

11+
## [v0.51.157] — 2026-05-28 — Release EC (stage-batch39 — 5-PR mixed-risk cleanup: gateway prefill forward + prefill budget + compressed-continuation sidebar + browser-transcript memory guidance + reasoning max parity)
12+
13+
### Added
14+
15+
- The reasoning-effort selector now offers a `max` level, matching the agent's `hermes_constants.VALID_REASONING_EFFORTS`. This restores parity with the underlying set (the WebUI mirror previously stopped at `xhigh`) so providers such as Anthropic that support the `max` thinking level are selectable from the composer dropdown and the `/reasoning` command.
16+
17+
### Changed
18+
19+
- WebUI's browser-session surface prompt now explicitly tells agents not to dump browser transcripts into external notes or durable memory by default; it limits saving to explicit captures and clearly reusable durable signals such as preferences, decisions, blockers, and runbook-worthy workflows.
20+
21+
### Fixed
22+
23+
- Gateway-backed WebUI chat now forwards configured prefill/session-recall context and a compact WebUI session-context block into delegated Gateway turns, so browser sessions retain note recall, connected-platform awareness, and delivery hints instead of sending only the latest user message. If the dynamic prefill script fails, WebUI falls back to the configured static router prefill when available.
24+
- Oversized WebUI startup prefill payloads now respect a configurable context budget (`webui_prefill_context_max_chars` / `HERMES_WEBUI_PREFILL_CONTEXT_MAX_CHARS`, default 12,000 chars). When a dynamic prefill script exceeds the budget and a compact static prefill file is configured, WebUI falls back to the compact file; otherwise it injects a small retrieval instruction instead of dumping the full note/body payload into every new chat.
25+
- Sidebar now keeps the newest active continuation visible when it has more recent activity than an older fuller pre-compression snapshot in the same lineage. Adds lineage-aware dedupe for WebUI-origin state-db projections, restores normal context-only turns into the visible transcript after compression while preserving order, and recognizes `[Session Arc Summary]` as a compression marker so it isn't backfilled into the chat transcript.
26+
1127
## [v0.51.156] — 2026-05-28 — Release EB (stage-batch38 — 2-PR Tier B cleanup: WebUI request/runtime hardening + chat-start provider fallback)
1228

1329
### Fixed

api/compression_anchor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def is_context_compression_marker(message):
6868
text.startswith("[context compaction")
6969
or text.startswith("context compaction")
7070
or text.startswith("[your active task list was preserved across context compression]")
71+
or text.startswith("[session arc summary")
7172
)
7273

7374

api/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,7 @@ def get_effective_default_model(config_data: dict | None = None) -> str:
20732073
# Mirrors hermes_constants.parse_reasoning_effort so WebUI can validate without
20742074
# importing from the agent tree (which may not be installed). Any drift here
20752075
# will show up in the shared test suite since both sides accept the same set.
2076-
VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh")
2076+
VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh", "max")
20772077

20782078

20792079
def parse_reasoning_effort(effort):

api/gateway_chat.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,22 @@ def put_gateway_event(event, data):
223223
from api.config import get_config # imported lazily to avoid config-cycle churn
224224

225225
cfg = get_config()
226+
try:
227+
from api.streaming import (
228+
_load_webui_prefill_context,
229+
_prefill_messages_with_webui_context,
230+
_public_prefill_context_status,
231+
)
232+
233+
prefill_context = _load_webui_prefill_context(cfg)
234+
prefill_messages = _prefill_messages_with_webui_context(prefill_context, cfg)
235+
put_gateway_event("context_status", {
236+
"session_id": session_id,
237+
"prefill": _public_prefill_context_status(prefill_context),
238+
})
239+
except Exception:
240+
logger.debug("Failed to load WebUI gateway prefill context", exc_info=True)
241+
prefill_messages = []
226242
base_url = _gateway_base_url(cfg)
227243
api_key = _gateway_api_key()
228244
url = f"{base_url}/v1/chat/completions"
@@ -248,7 +264,7 @@ def put_gateway_event(event, data):
248264
body = {
249265
"model": model or "default",
250266
"stream": True,
251-
"messages": [{"role": "user", "content": message_content}],
267+
"messages": [*prefill_messages, {"role": "user", "content": message_content}],
252268
}
253269
if model_provider:
254270
body["provider"] = model_provider

api/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,6 +2495,14 @@ def _prefer_fuller_snapshots_for_sidebar(sessions: list[dict]) -> list[dict]:
24952495
if _sidebar_message_count(best_snapshot) <= best_visible_count:
24962496
continue
24972497

2498+
newest_visible_ts = max(_session_sort_timestamp(session) for session in visible)
2499+
snapshot_ts = _session_sort_timestamp(best_snapshot)
2500+
# Keep the active continuation visible when it has newer activity than
2501+
# the archived snapshot. A fuller snapshot can still be older than a
2502+
# continuation that contains the latest turns after compression.
2503+
if newest_visible_ts > snapshot_ts:
2504+
continue
2505+
24982506
snapshot_ids_to_show.add(str(best_snapshot.get('session_id')))
24992507
continuation_ids_to_hide.update(
25002508
str(session.get('session_id'))

api/routes.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,43 @@ def _normalize_sidebar_source_flags(session: dict) -> dict:
24572457
return normalized
24582458

24592459

2460+
def _session_source_is_webui(session: dict) -> bool:
2461+
"""Return True for state.db/sidebar rows that describe WebUI-origin sessions."""
2462+
if not isinstance(session, dict):
2463+
return False
2464+
for key in ("source_tag", "raw_source", "session_source", "source"):
2465+
if str(session.get(key) or "").strip().lower() == "webui":
2466+
return True
2467+
return False
2468+
2469+
2470+
def _session_lineage_ids(session: dict) -> set[str]:
2471+
"""Return known ids that identify one logical sidebar lineage."""
2472+
if not isinstance(session, dict):
2473+
return set()
2474+
ids: set[str] = set()
2475+
for key in ("session_id", "_lineage_root_id", "_lineage_tip_id"):
2476+
value = session.get(key)
2477+
if value:
2478+
ids.add(str(value))
2479+
return ids
2480+
2481+
2482+
def _is_duplicate_webui_state_projection(session: dict, represented_webui_ids: set[str]) -> bool:
2483+
"""Return True when a state.db row is only a duplicate WebUI-origin projection.
2484+
2485+
The "Show non-WebUI sessions" toggle should add external/agent-owned
2486+
conversations, not make WebUI compression continuations appear only when the
2487+
external-session bridge is enabled. WebUI-origin state.db rows are still
2488+
useful metadata sidecars, but if any id in their compression lineage is
2489+
already represented by WebUI session JSON, they should not be injected as an
2490+
additive external row.
2491+
"""
2492+
if not _session_source_is_webui(session):
2493+
return False
2494+
return bool(_session_lineage_ids(session) & represented_webui_ids)
2495+
2496+
24602497
CLI_VISIBLE_SESSION_CAP = 20
24612498

24622499

@@ -4518,9 +4555,17 @@ def handle_get(handler, parsed) -> bool:
45184555
# Apply the same CLI visibility semantics to imported local copies so
45194556
# low-value imported artifacts do not leak into the sidebar.
45204557
webui_sessions = [s for s in webui_sessions if is_cli_session_row_visible(s)]
4521-
webui_ids = {s["session_id"] for s in webui_sessions}
4558+
represented_webui_ids = set()
4559+
for s in webui_sessions:
4560+
represented_webui_ids.update(_session_lineage_ids(s))
45224561
from api.models import _hide_from_default_sidebar as _cron_hide
4523-
deduped_cli = [s for s in cli if s["session_id"] not in webui_ids and is_cli_session_row_visible(s) and not _cron_hide(s)]
4562+
deduped_cli = [
4563+
s for s in cli
4564+
if s["session_id"] not in represented_webui_ids
4565+
and not _is_duplicate_webui_state_projection(s, represented_webui_ids)
4566+
and is_cli_session_row_visible(s)
4567+
and not _cron_hide(s)
4568+
]
45244569
else:
45254570
diag.stage("filter_webui_sessions")
45264571
webui_sessions = [s for s in webui_sessions if not _is_cli_session_for_settings(s)]

0 commit comments

Comments
 (0)