_gateway_root_pid_path() unconditionally returned <hermes_root>/gateway.pid.
Profile-scoped gateways (started with --profile <name> or via active_profile)
write their runtime files under <hermes_root>/profiles/<name>/ instead of the
root, so the root-level path never existed.
build_agent_health_payload() therefore always received a non-existent pid_path,
fell through to the stale root-level gateway_state.json, and returned alive=None.
This caused the cron/scheduled-jobs page to display "Gateway not configured" even
when a gateway was actively running.
Fix: after failing to find a root-level gateway.pid, fall back to the active
profile directory via get_active_hermes_home(). Root-level wins when it exists,
so deployments that do write there are unaffected. Errors from profile lookup are
swallowed and the root path is returned, preserving the previous safe default.
Adds five focused unit tests covering the new fallback, the priority rule, and
the error-handling path.
Instead of using len(visible_after)-1 (which points to the last visible
message and gets pushed behind the render window as more turns accumulate),
find the last [CONTEXT COMPACTION] marker in s.messages and compute the
anchor from visible messages before it.
This keeps the compression reference card at the correct boundary even
after 50+ subsequent turns have scrolled the render window past the old
anchor position.
Fixes a bug where the assistant's output message appeared to disappear
after automatic context compression because the reference card was placed
at the wrong position.
_loadOlderMessages() previously fetched older messages with the legacy
index-cursor page (msg_before=_oldestIdx&msg_limit=30) and prepended
the page to S.messages. After #2716 the backend always runs the full
append-only merge for /api/session?messages=1 — the same merge as a
larger msg_limit on the same call — so we can ask for a larger
authoritative tail window directly instead of stitching pages on the
client.
Behavior
* Default request shape becomes msg_limit=currentLoaded+30. The newly
exposed head of the response is what the user sees as 'older
messages'. No new query parameters.
* msg_before remains supported by the backend and is retained in the
client as a race-fallback path: if the returned tail no longer has
the currently displayed messages as a suffix (because the session
appended new messages mid-flight, or merge filtered something), the
client issues the legacy msg_before page and prepends it instead.
This preserves correctness under concurrent appends.
* Suffix-continuity uses the existing _sameTranscriptMessage helper,
which tolerates timestamp drift and content-array reshapes.
* Existing race guards (loadingSessionId, S.session.session_id, and
the _messagesGeneration snapshot from #1937) are reapplied after
the fallback await.
Tests
Updated four static-string assertions in the existing scroll/viewport
tests to track the new mutation site (S.messages = nextMessages) and
the new msg_limit=requestedLimit shape, while still asserting that
msg_before remains in the body for the race-fallback path.
pytest -q
tests/test_older_history_viewport_preservation.py
tests/test_parallel_session_switch.py
tests/test_issue1937_endless_scroll_jumpstart_race.py
tests/test_session_tail_payload.py
-> 52 passed
node --check static/sessions.js -> ok
Notes
Originally part of PR #2835. That PR was closed because of an
architectural conflict with #2716 on a different file (api/models.py
metadata-only path). #2716 left static/sessions.js untouched — this
change applies cleanly on post-#2716 master with no rebase work.
Model picker onchange now calls syncReasoningChip after session model/
provider updates, and dropdown selections pass providerId so duplicate
bare model ids resolve to the correct backend capabilities.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
Ensure cursor/composer IDs always resolve via @cursor-acp:, carry the
visible picker selection into POST /api/session/new, persist model
changes before a session exists, and evict cached agents on model switch.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add cursor-acp to _PROVIDER_DISPLAY with label 'Cursor ACP'
- Add cursor-acp static model list to _PROVIDER_MODELS
- composer-2.5, composer-2, default, cursor-acp