Commit Graph

908 Commits

Author SHA1 Message Date
Dennis Soong 7da1e074e4 fix: expose session lineage metadata in API (#1370)
PR #1358 added the client-side lineage collapse helper, but
/api/sessions often did not include _lineage_root_id for the WebUI
JSON sessions visible in the sidebar. In that case the helper has no
grouping key and multiple same-title continuation rows remain visible.

This PR:
- Reads parent_session_id and end_reason from state.db.sessions for
  the WebUI sidebar's session ids
- Walks the parent chain when end_reason is 'compression' or
  'cli_close', producing _lineage_root_id and _compression_segment_count
- Cycle-detects via a 'seen' set
- Preserves projected lineage metadata on imported/gateway session rows
- Allows sidebar collapse to group cross-surface continuation chains
  (CLI-close → WebUI continuation) while keeping non-continuation
  parent rows flat

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 23:04:49 +00:00
nesquena-hermes ffd11037b1 Merge pull request #1368 from nesquena/release/v0.50.250
release: v0.50.250
v0.50.250
2026-04-30 15:49:58 -07:00
nesquena-hermes f8754ded70 fix(autosave): guard preferences-autosave dirty-clear when password/model pending (Opus SHOULD-FIX Q1)
Pre-release Opus review of v0.50.250 caught a UX regression in PR
#1369: _autosavePreferencesSettings unconditionally cleared
_settingsDirty=false and hid the unsaved-changes bar on every
successful autosave. But password and model are still committed via
the explicit 'Save Settings' button (password for security; model
goes through /api/default-model). Race scenario:

  1. User opens System pane, types a new password (sets
     _settingsDirty=true; bar appears on close)
  2. User switches to Preferences, toggles any checkbox -> autosave
     fires -> _settingsDirty=false, bar permanently suppressed
  3. User closes panel -> _closeSettingsPanel short-circuits because
     !_settingsDirty -> typed password silently discarded
     (loadSettingsPanel blanks pwField.value='' on next open)

Same shape with model selector: pick a new default model, then
toggle any preference -> autosave fires -> no warning on close ->
model never persists.

Fix: only clear _settingsDirty and hide settingsUnsavedBar when both
the password field is empty AND the model selector matches its
on-open snapshot.

Pinned by an updated regression test asserting the conditional guard
exists.
2026-04-30 22:48:20 +00:00
nesquena-hermes 3a13be297e test: add Phase 2 preferences autosave regression suite (#1369)
9 source-level invariants covering #1369:
- All 13 preference fields appear in _preferencesPayloadFromUi
- Listeners use _schedulePreferencesAutosave, not _markSettingsDirty
- Password field STILL uses _markSettingsDirty (security invariant)
- _autosavePreferencesSettings clears _settingsDirty + hides unsaved bar on success
- Status div present in static/index.html
- Status function uses shared i18n keys from Phase 1
- Retry function falls back gracefully when no stored payload
- Debounce clears prior timer (350ms, matching Phase 1)
- Phase 1 (Appearance) autosave still intact
2026-04-30 22:42:20 +00:00
Feco Linhares 645dfa25af fix: autosave preferences settings (#1369, fixes #1003 phase 2)
Phase 2 of #1003: extend the autosave pattern from the Appearance
panel to the Preferences panel so all preference changes are saved
automatically without requiring a manual 'Save Settings' click.

Mirrors the Phase 1 (Appearance) pattern exactly:
- 350ms debounce on field changes (500ms additional debounce on
  the bot_name text input — effective ~850ms latency for typing)
- Inline status feedback (saving / saved / failed + retry button)
- Clears dirty flag and hides unsaved-changes bar after successful save
- Password field excluded — still requires explicit save (security)
- Model selector excluded — still requires explicit save

13 fields now autosaving: send_key, language, show_token_usage,
simplified_tool_calling, show_cli_sessions, sync_to_insights,
check_for_updates, sound_enabled, notifications_enabled,
sidebar_density, auto_title_refresh_every, busy_input_mode, bot_name.

i18n keys (settings_autosave_saving/saved/failed/retry) already exist
in all 8 locales from Phase 1.

Co-authored-by: Feco Linhares <feco.linhares@gmail.com>
2026-04-30 22:38:44 +00:00
nesquena-hermes 07350426f2 docs(changelog): correct stale-detector claim per Opus NIT-2
The timer fires every 60s either way; what changed is whether it
triggers a reconnect. Under steady clarify traffic the reconnect
never happens; on long-idle sessions it still reconnects every
60-120s (the residual idle churn is now a tracked follow-up rather
than the original v0.50.249 unconditional per-minute reconnect).

Tightens the CHANGELOG language to match observed behavior.
2026-04-30 22:34:22 +00:00
nesquena-hermes bc10a229e3 release: v0.50.250
Bundles 2 PRs:
- #1366 fix: guard finalizeThinkingCard with session ID check (with pre-release fix)
- #1367 fix(clarify-sse): stale-detector health timer (Opus SHOULD-FIX from v0.50.249)

Pre-release fix on #1366: the contributor's guard depends on
liveAssistantTurn.dataset.sessionId, but no code in the repo sets
that attribute. Without the fix, the guard would always early-return
(undefined !== sid is always true), breaking the streaming UI
completely — every assistant turn's thinking card would stay open
forever. Added per-site stamps at all 3 places that create
liveAssistantTurn in static/ui.js, plus a regression test that fails
any future creation site that forgets the stamp.
2026-04-30 22:27:40 +00:00
Josh d0257e8bcf fix: guard finalizeThinkingCard with session ID check (#1366)
Without this check, switching browser tabs while a stream is running
causes finalizeThinkingCard() to operate on the wrong session's
thinking card DOM — the card belongs to the stream that started it,
not the session currently displayed in the tab. The guard ensures
finalize only runs when the live assistant turn's session matches
the current session.

Co-authored-by: Josh <josh@fyul.link>
2026-04-30 22:25:25 +00:00
nesquena-hermes 2566b434d1 fix(clarify-sse): make health timer a stale-detector, not unconditional reconnect (#1367)
Follow-up to v0.50.249 / PR #1365 absorbing Opus SHOULD-FIX #2.
Originally reset out of #1365 because the reviewer flagged it as
out-of-scope; brought back per follow-up guidance that
correctness-improving changes should ship even when out of scope.

The clarify SSE health timer at static/messages.js:1715 was an
unconditional 60s force-reconnect, not the 'no event in 60s' detector
its comment claimed. Now actually a stale-detector that tracks
lastEventAt on initial+clarify event arrivals; only reconnects when
the gap exceeds 60s. Under healthy conditions the timer never fires.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 22:25:12 +00:00
nesquena-hermes d72399ae22 Merge pull request #1365 from nesquena/release/v0.50.249
release: v0.50.249
v0.50.249
2026-04-30 15:02:27 -07:00
Nathan Esquenazi 604b44a254 fix(clarify-sse): inline snapshot under _lock to avoid deadlock in handler
The new _handle_clarify_sse_stream handler in #1355 holds clarify._lock and
then calls clarify.get_pending(sid) under the lock. get_pending also acquires
_lock internally — and clarify._lock is a non-reentrant threading.Lock(),
so the second acquisition deadlocks the SSE handler thread the moment any
client connects to /api/clarify/stream.

Existing tests pass because they only exercise sse_subscribe, sse_unsubscribe,
_clarify_sse_notify, and submit_pending directly — none of them invoke the
route handler. The deadlock would only manifest when a real EventSource opens
the connection.

Reproduced with a tiny harness that holds _lock and calls get_pending: the
worker thread is still blocked after a 2s timeout. With the fix, both empty
and populated queue cases complete in <1ms.

Fix: read clarify._gateway_queues / clarify._pending inline under the same
_lock acquisition, mirroring the approval SSE handler's pattern at
api/routes.py:2785-2793. No recursive lock; head-of-queue snapshot is
identical to what get_pending would have returned.

Added tests/test_pr1355_sse_handler_no_deadlock.py with three tests:
- behavioural: empty queue snapshot completes within 2s
- behavioural: populated queue snapshot returns the head entry
- source-level invariant: routes.py must not call get_clarify_pending()
  inside `with _clarify_lock:` block (locks the regression in)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:39:37 -07:00
nesquena-hermes 36d87f54d5 release: v0.50.249
Bundles 5 community PRs:
- #1355 feat(clarify): SSE long-connection (mirrors #1350 pattern, includes all correctness lessons)
- #1356 fix: context window indicator overflow (live SSE fallback) + uploading status clear
- #1357 fix: preserve imported session source metadata
- #1358 fix: collapse sidebar session lineage rows
- #1359 fix: sync active session across tabs

Tests: 3444 passing (3411 -> 3444, +33)
2026-04-30 21:34:26 +00:00
fxd-jason d2d464aac3 feat(clarify): SSE long-connection for real-time clarify notifications (#1355)
Replaces the 1.5s HTTP polling loop for clarify with a Server-Sent Events endpoint at /api/clarify/stream that pushes clarify events to the browser instantly. Mirrors the approval SSE pattern from v0.50.248 (#1350) including all the correctness lessons:

- Atomic subscribe + initial snapshot under clarify._lock
- _clarify_sse_notify called inside _lock for ordering guarantees (no notify-out-of-order race)
- Notify passes head=q[0].data (head-fidelity, not the just-appended entry)
- resolve_clarify also calls notify after pop so trailing clarifies surface immediately (no stuck-clarify bug)
- Empty-state notify with None,0 after pop-empty so frontend hides the card
- 30s keepalive comments, _CLIENT_DISCONNECT_ERRORS handling
- Bounded queue (maxsize=16) with silent drop on full
- Frontend: EventSource with automatic 3s HTTP polling fallback on onerror

Co-authored-by: fxd-jason <wujiachen7@gmail.com>
2026-04-30 21:32:51 +00:00
Dennis Soong 6a736809ef fix: sync active session across tabs (#1359)
Adds a 'storage' event listener for the hermes-webui-session localStorage key. Idle tabs auto-load the new active session and re-render the sidebar; busy tabs show a toast and do not interrupt the active turn.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 21:32:50 +00:00
Dennis Soong f13230f7cd fix: collapse sidebar session lineage rows (#1358)
When a session's compression lineage spans multiple segments (linked via _lineage_root_id from api/agent_sessions.py), the sidebar previously rendered each segment as a separate top-level row. Adds _collapseSessionLineageForSidebar() that groups by lineage root and keeps only the most recently active tip per group, with a _lineage_collapsed_count marker for future UI affordances.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 21:32:48 +00:00
Dennis Soong 70dac0135c fix: preserve imported session source metadata (#1357)
Session.load_metadata_only().compact() was dropping is_cli_session, source_tag, session_source, and source_label, so imported CLI/gateway sessions lost their provenance in sidebar/API payloads. Adds these to METADATA_FIELDS and Session.compact().

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 21:32:46 +00:00
nesquena-hermes bbdacdca5c fix: context window indicator overflow (#1356)
- api/streaming.py SSE payload now falls back to agent.model_metadata.get_model_context_length when compressor doesn't supply context_length (mirrors the session-save fallback shipped in v0.50.247).
- api/streaming.py also falls back to s.last_prompt_tokens to avoid using the cumulative input_tokens counter.
- static/ui.js tracks rawPct separately from pct and shows '(context exceeded)' tooltip when rawPct > 100 instead of misleading '100% used (0% left)'.
- static/messages.js clears 'Uploading...' composer status after upload completes.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 21:32:45 +00:00
nesquena-hermes 9303636dd9 Merge pull request #1351 from nesquena/release/v0.50.248
release: v0.50.248
v0.50.248
2026-04-30 11:50:17 -07:00
nesquena-hermes e68f74ac99 fix(approval): close SSE notify-ordering, head-fidelity, and trailing-approval gaps (Opus MUST-FIX A/C/D)
Pre-release Opus review caught three correctness bugs in the original
PR #1350 SSE wiring beyond the snapshot/subscribe race:

A) **Notify-ordering race (MUST-FIX A):** _approval_sse_notify took _lock
   only for the subscriber-list snapshot, then released it before
   put_nowait. With two parallel submit_pending calls, T2's notify
   could fire before T1's, leaving the UI showing pending_count=1 while
   the server actually had 2 queued.

C) **Trailing approval lost (MUST-FIX C):** _handle_approval_respond
   never called _approval_sse_notify after popping. With parallel
   tool-call approvals (#527), a second approval queued behind the one
   being responded to was invisible until the next event ever fired —
   in practice, the agent thread parked on it would appear hung.

D) **Payload showed tail not head (MUST-FIX D):** payload built from
   the just-appended entry instead of queue[0]. /api/approval/pending
   returns the head; SSE returned the tail. Diverging contracts.

Fix:
- Split into _approval_sse_notify_locked (caller holds _lock, no
  internal locking) and _approval_sse_notify (convenience wrapper).
- submit_pending: call _locked variant inside the queue-mutation lock,
  passing queue_list[0] as head.
- _handle_approval_respond: call _locked variant inside the pop lock,
  passing the new head (or None/0 if queue is empty).
- Restore fallback poll to 1500ms (was bumped to 3000ms; degraded-mode
  parity with v0.50.247 is more important than save 1.5s of polling).

New regression tests in tests/test_pr1350_sse_notify_correctness.py:
- test_second_submit_pending_sends_head_not_tail (D)
- test_respond_to_first_pushes_second_as_new_head (C)
- test_respond_to_only_pending_pushes_empty_state (C edge)
- test_pending_count_is_monotonic_under_contention (A)

Updated test_approval_sse.py to pin the new contract:
- _approval_sse_notify_locked(session_key, head, total)
- 1500ms fallback interval

Total: 3411 tests passing.

Co-authored-by: jasonjcwu <jasonjcwu@users.noreply.github.com>
2026-04-30 18:45:15 +00:00
nesquena-hermes d6b9cfac23 release: v0.50.248
Bundles:
- #1349 fix(ui): show context indicator percentage without explicit context_length
- #1350 feat(approval): SSE long-connection for real-time approval notifications

Pre-release fixes applied:
- Inline subscribe + snapshot under a single _lock acquisition in
  _handle_approval_sse_stream() to close the snapshot/subscribe race
  flagged in pre-release review. A submit_pending() arriving between
  the snapshot read and subscribe call would have been lost (appended
  to _pending after our snapshot AND notified to subscribers before we
  joined). Now atomic.
- Added tests/test_pr1350_sse_atomic_subscribe.py (4 source-level
  invariants covering the atomic-lock-block guarantee).

Co-authored-by: jasonjcwu <jasonjcwu@users.noreply.github.com>
2026-04-30 18:34:37 +00:00
fxd-jason 932694aec6 feat(approval): SSE long-connection for real-time approval notifications (#1350)
Replaces the 1.5s HTTP polling loop with a Server-Sent Events endpoint
at /api/approval/stream that pushes approval events to the browser
instantly. The backend uses a thread-safe subscriber registry
(_approval_sse_subscribers) with bounded queues to prevent memory
leaks from slow clients. Frontend uses EventSource with automatic
fallback to 3s HTTP polling on SSE error.

- Backend: subscribe/unsubscribe/notify lifecycle in api/routes.py
- New route: GET /api/approval/stream?session_id=
- submit_pending() now calls _approval_sse_notify() after queue append
- Frontend: EventSource with onerror -> _startApprovalFallbackPoll()
- 30s keepalive comments, _CLIENT_DISCONNECT_ERRORS handling
- 42 new tests (static analysis + unit + concurrency)

Co-authored-by: jasonjcwu <jasonjcwu@users.noreply.github.com>
2026-04-30 18:31:42 +00:00
fxd-jason 1df89e7a52 fix(ui): show context indicator percentage without explicit context_length (#1349)
Frontend companion to backend fix in v0.50.246 (#1341 + a5c10d5).
Default context window to 128K when usage.context_length is falsy.
Show '(est. 128K)' label when using the default.
Use input_tokens as fallback for last_prompt_tokens.

Co-authored-by: jasonjcwu <jasonjcwu@users.noreply.github.com>
2026-04-30 18:31:30 +00:00
nesquena-hermes 880350312a fix(streaming): fallback to model_metadata for context_length when compressor missing (#1318 follow-up) (#1348)
* fix(streaming): fallback to model_metadata for context_length when compressor missing (#1318 follow-up)

PR #1318 (shipped in v0.50.246 via PR #1341 + commit a5c10d5) persisted
context_length on the session so the context-ring indicator survives
page reloads. But the writer only fired when agent.context_compressor
was present and reported a non-zero value. Fresh agents, interrupted
streams, or compressors without the attribute would still leave
s.context_length=0 — and the indicator would still show 0% on reload.

This follow-up adds a fallback that calls
agent.model_metadata.get_model_context_length(model, base_url) when the
compressor didn't populate the value. The function returns a sensible
static context window for any known model (with a 256K default for
unknown models). Wrapped in a broad try/except because older
hermes-agent builds may not expose the helper.

Sourced from PR #1344 (@jasonjcwu) — extracted into this focused
follow-up after #1344 was closed as superseded by #1341.

Adds 6 structural tests covering: import + call presence, falsy-gate,
agent.model/base_url passing, exception swallowing, save() ordering,
result assignment.

Closes the data-flow gap in #1318 for the compressor-missing case.

* test: relax pr1341 block-size assertion to accommodate the new fallback

---------

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 10:27:56 -07:00
nesquena-hermes f70b791bf8 Merge pull request #1347 from nesquena/release/v0.50.247
release: v0.50.247 — Cron Jobs project auto-assignment (#1345)
v0.50.247
2026-04-30 10:26:56 -07:00
nesquena-hermes c98fff79c2 perf(cron): memoize ensure_cron_project() per get_cli_sessions() scan
Pre-release Opus review on PR #1345 (Finding #3) flagged that
get_cli_sessions() was calling ensure_cron_project() once per cron
session in the loop — N lock acquires + N disk reads of projects.json
for N cron sessions per sidebar refresh.

Hoist a per-scan lazy memoizer (_cron_pid()) so we pay the resolution
cost at most once per get_cli_sessions() call. The memoizer is local
to the function (closure) so it's naturally scoped to a single scan
and doesn't leak across calls.

Could also have made ensure_cron_project() module-memoized, but that
would need invalidation on project deletion — the per-scan cache is
simpler and correct without coordination.
2026-04-30 17:21:51 +00:00
nesquena-hermes 77b456b755 release: v0.50.247
Single PR — #1345 (@bergeouss): auto-assign cron job sessions to a
dedicated 'Cron Jobs' project (closes #1079).

143 LOC, 5 new tests, locale parity across 8 languages, CI green
on all Python versions before merge.
2026-04-30 17:14:21 +00:00
nesquena-hermes eb678d5b54 feat(cron): auto-assign cron job sessions to dedicated 'Cron Jobs' project (#1079)
From PR #1345.

Co-authored-by: bergeouss <bergeouss@users.noreply.github.com>
2026-04-30 17:13:59 +00:00
nesquena-hermes dec4b48607 Merge pull request #1343 from nesquena/release/v0.50.246
release: v0.50.246 — 5-PR batch
v0.50.246
2026-04-30 09:49:14 -07:00
nesquena-hermes a5c10d594d fix(streaming): persist context_length on session — completes #1318 fix
Pre-release Opus + nesquena review on v0.50.246 caught that PR #1341
added the data-structure scaffolding (Session.__init__ accepts the 3
fields, save() persists them, compact() exposes them, GET /api/session
returns them) but did NOT add the writer that actually populates them.

Without a writer, the user-visible bug (context-ring shows 0% after
page reload) was NOT fixed by #1341 alone — the fields stayed None
forever because nothing wrote to s.context_length anywhere.

Adds the writer at api/streaming.py:2188 (post-merge per-turn save block,
before s.save()) so the values from agent.context_compressor land on
disk and survive page reloads.

Also moves the SSE usage payload comment to clarify that the live SSE
payload and the session-level persistence are now distinct paths
(payload below, persistence above).

Adds tests/test_pr1341_context_window_persistence.py — 6 structural +
round-trip tests covering Session __init__/save/compact, the routes
response, and the streaming.py writer placement.

Closes #1318 (the actual user-visible bug, not just the scaffolding).
2026-04-30 16:42:32 +00:00
nesquena-hermes f328f3b843 fix(cancel): gate substring guard on pending_started_at timestamp (Opus review)
Pre-release Opus review on v0.50.246 caught a SHOULD-FIX in PR #1338's
cancel_stream synthesis: the symmetric substring guard
(_pending_user in _last_content OR _last_content in _pending_user) was too
loose. Common confirmation replies ("ok", "yes", "go") in the prior turn
would match longer follow-up prompts ("ok please continue"), the synthesis
would be skipped, and the user's typed text would be lost — exactly the
data-loss bug #1298 was supposed to fix.

The fix: gate the substring check on a timestamp comparison. Only treat
the latest user turn as 'already merged by the streaming thread' if its
timestamp is at or after pending_started_at. Earlier turns whose content
happens to be a substring of the pending must not short-circuit synthesis.

Also drops the symmetric (_last_content in _pending_user) branch — that
direction was the false-positive vector. Keeps the equality and prefix
match (workspace-prefix tolerance from the streaming thread).

Adds tests/test_issue1298_cancel_and_activity.py::
test_cancel_synthesizes_when_prior_turn_content_is_substring_of_pending —
regression for the exact 'ok' → 'ok please continue' scenario.
2026-04-30 16:28:20 +00:00
nesquena-hermes 929461ffbc release: v0.50.246
Combines:
- 4 contributor PRs (#1335 user fenced code, #1337 mermaid+cache-bust,
  #1339 fallback_providers list, #1341 context_length persistence)
- Self-built #1338 (cancel data-loss + activity panel) — already
  independently APPROVED by nesquena before absorption
- CONTRIBUTORS.md and markdown refresh from #1340

See CHANGELOG.md for the full list with author credit.
2026-04-30 16:21:18 +00:00
nesquena-hermes 50418cd47b test: stabilize flaky checkpoint test + add regression for #1339 fallback list
- tests/test_issue765_streaming_persistence.py — replace timing-based polling
  in test_checkpoint_fires_on_activity_counter_increment with deterministic
  threading.Event-driven sync. The old version used time.sleep(0.15)+(0.25)+(0.25)
  with a 0.1s polling thread, which under CI scheduling jitter could miss the
  second increment and complete with only 1 save instead of 2. Now waits up
  to 3.0s for save_count to advance to the target after each increment.
  Locally observed flake on Python 3.11 in CI run 25175204451.

- tests/test_pr1339_fallback_providers_list.py — new structural test that
  asserts streaming.py handles both legacy fallback_model (single dict) and
  new fallback_providers (list form) without calling .get() on a list. Three
  assertions: both keys consulted, list-form has explicit isinstance check,
  _fallback_resolved defaults to None.
2026-04-30 16:20:05 +00:00
nesquena-hermes d4b055c30b fix(streaming+ui): preserve user message on cancel + persist activity-panel expand state (#1298)
From PR #1338. Already independently APPROVED by nesquena before being absorbed into v0.50.246.

CHANGELOG entries from this PR were dropped during squash (the v0.50.245 section is already
shipped); they will be re-added under [v0.50.246] in the release commit.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 16:18:41 +00:00
nesquena-hermes 1fa740d32f feat(chat): render fenced code blocks in user messages (#1325)
From PR #1335.

Co-authored-by: bergeouss <bergeouss@users.noreply.github.com>
2026-04-30 16:18:02 +00:00
nesquena-hermes fbe84d26e6 fix(ui+pwa): avoid stale Mermaid render errors and bust cached static asset URLs on every release
From PR #1337.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 16:18:01 +00:00
nesquena-hermes 09e12e3c60 fix(streaming): handle list fallback_providers config in addition to single fallback_model dict
From PR #1339.

Co-authored-by: Jim Dawdy <jimdawdy@Jims-MacBook-Pro.local>
2026-04-30 16:18:00 +00:00
nesquena-hermes e2d33ffce4 fix(models): persist context_length/threshold_tokens/last_prompt_tokens in Session model (#1318 split)
From PR #1341.

Co-authored-by: fxd-jason <wujiachen7@gmail.com>
2026-04-30 16:17:59 +00:00
nesquena-hermes 280ab86480 Merge pull request #1340 from nesquena/chore/markdown-refresh-v0.50.245
docs: refresh markdown to v0.50.245 + add CONTRIBUTORS.md
2026-04-30 09:15:31 -07:00
nesquena-hermes d356e081ed docs: refresh markdown to v0.50.245 + add CONTRIBUTORS.md
- New CONTRIBUTORS.md: full ranked credit roll for all 66 contributors
  (5+ tiers), with first/latest release versions, single-PR roll, and
  attribution methodology. Generated from git log + gh pulls API +
  CHANGELOG mention parsing.

- README.md: stack-ranked top-10 contributors table at the top of the
  Contributors section, link to CONTRIBUTORS.md for the full list.
  Updated test count (1898 → 3309). Refreshed @franksong2702 and
  @bergeouss entries to reflect their broader bodies of work (now
  the #1 and #2 external contributors).

- ARCHITECTURE.md: removed stale 'tracks upstream v0.50.36' header;
  bumped current shipped build to v0.50.245 with current architecture
  state notes (streaming-markdown vendoring, byte-range streaming,
  configurable-model-badges).

- ROADMAP.md / SPRINTS.md / TESTING.md: header/last-updated bumps to
  v0.50.245 and 3309 tests. SPRINTS.md 'Where we are now' section
  refreshed for current CLI/Claude parity (~95% Claude parity now).

Generated by aggregating CHANGELOG attribution lines, gh PR API
authors, and CHANGELOG version-section walks. Internal/bot accounts
filtered out.
2026-04-30 16:00:38 +00:00
nesquena-hermes 52e1567bd1 Merge pull request #1334 from nesquena/release/v0.50.245
release: v0.50.245 — 10-PR batch
v0.50.245
2026-04-30 08:48:53 -07:00
nesquena-hermes 06fd6d9ccc release: tighten v0.50.245 CHANGELOG sidebar-filter wording
Per Opus pre-release review (SHOULD-FIX #1): the CHANGELOG claimed both
filter sites exempt 'active_stream_id OR pending_user_message', but the
index path operates on compact() output which doesn't include
pending_user_message. The behavior is correct in both paths because both
fields are set/cleared in lockstep during streaming, but the wording was
stronger than what the code does. Tightened to describe what each path
actually checks.
2026-04-30 15:42:31 +00:00
nesquena-hermes 4651e0fad0 release: v0.50.245
10 contributor fixes — cron worker scope, compression banner, mobile workspace
sliver, streaming session sidebar exemption, slash-qualified model dedup,
configured-fallback dropdown synthesis, copy-button idempotency, zh-Hant
locale restore, Docker HEALTHCHECK, .env.example state-dir alignment.

See CHANGELOG.md for the full list with author credit.
2026-04-30 15:25:52 +00:00
nesquena-hermes aa2b9d504d fix(mobile): workspace panel sliver + composer footer collapse (#1300)
From PR #1328.

Co-authored-by: Frank Song <franksong2702@gmail.com>
2026-04-30 15:24:36 +00:00
nesquena-hermes 4683a4a0d0 fix(models): default model rehydration when providers share slash-qualified IDs (#1313)
From PR #1326.

Co-authored-by: hacker2005 <chen20057275@outlook.com>
2026-04-30 15:24:35 +00:00
nesquena-hermes e86de0aff3 fix(ui): show configured fallback models missing from catalog
From PR #1322.

Co-authored-by: renatomott <renato.mott@gmail.com>
2026-04-30 15:24:34 +00:00
nesquena-hermes 92121324a0 fix(models): exempt streaming sessions from Untitled+0-message sidebar filter (#1327)
From PR #1330.

Co-authored-by: Frank Song <franksong2702@gmail.com>
2026-04-30 15:24:33 +00:00
nesquena-hermes 1ccd958e23 fix(ui): avoid duplicate header copy buttons (#1096)
From PR #1324.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 15:24:32 +00:00
nesquena-hermes eb95c6a341 fix(i18n): restore zh-Hant locale labels
From PR #1323.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-04-30 15:24:31 +00:00
nesquena-hermes 5bde48bb6e fix(streaming): compare compression_count against per-turn snapshot to stop repeated banner
From PR #1316.

Co-authored-by: qxxaa <mrhanoi@outlook.com>
2026-04-30 15:24:31 +00:00
nesquena-hermes d0f6ee2ef9 fix(cron): import run_job inside _run_cron_tracked to fix NameError (#1310)
From PR #1317.

Co-authored-by: fxd-jason <wujiachen7@gmail.com>
2026-04-30 15:24:30 +00:00