Commit Graph
18 Commits
Author SHA1 Message Date
nesquena-hermesandfranksong2702 3c493a9e37 fix(cancel): preserve partial turn payload via embedded cancel snapshot (#4647)
Cancelling a running turn briefly collapsed the already-streamed reasoning,
tool cards, and text to a bare "Task cancelled" marker before a follow-up
GET /api/session repopulated them — a visible race between the terminal
cancel SSE event and the refetch (#4076, #1361).

The terminal cancel event now carries a canonical, redacted session snapshot
captured under the per-session agent lock right after the cancelled turn is
persisted (including the recovered partial-assistant message). The frontend
applies that embedded snapshot immediately via _applyCancelSessionPayload and
skips the GET round-trip. The snapshot is a fully detached deep copy (redaction
rebuilds every container) so it cannot alias or be staled by concurrent writes;
it is only applied when its session_id matches the active view; and a
missing/failed snapshot degrades cleanly to the existing GET fallback.

The seven other cancel call sites are standardized via _cancel_event_payload()
(message + type + status), backward-compatible (no session key when none).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-27 16:43:37 +00:00
allenliang2022 363097adad fix(webui): hold per-session lock for state.db sidecar self-heal (TOCTOU + clobber) 2026-06-27 15:24:15 +00:00
allenliang2022 dbe470aa6f test(webui): add registration-window race regression + grace guard 2026-06-27 15:24:15 +00:00
allenliang2022 f3da26dc07 fix(webui): address greptile P1 - snapshot-first save, safer recovery gate 2026-06-27 15:24:15 +00:00
allenliang2022 a3030fb498 fix: sync stale sidecars from newer state db 2026-06-27 15:24:15 +00:00
nesquena-hermesandDennis Soong c592ae3acc fix(#4216): insert state.db-only user prompts chronologically in merge_session_messages_append_only
A user prompt that exists only in Hermes state.db (no sidecar copy) and falls
chronologically BEFORE a newer sidecar message tail was being appended after the
tail (or skipped) by merge_session_messages_append_only — losing or mis-ordering
it on load. The merge now inserts such a state.db user message at its correct
chronological position via _insert_state_message_chronologically, with a
role-aware tie-break for equal timestamps. Append-only invariant + dedup
(seen_content_keys) preserved.

Deterministic-ordering guard (maintainer add, addresses Codex review): on an
equal-timestamp collision the insert no longer lands at a slot whose left
neighbour shares the message's role — it advances past the same-role
equal-timestamp run, so an already-answered earlier turn can't be re-ordered
into user,user,assistant. (The agent core merges adjacent users before send, so
this was benign in practice, but the guard keeps the merged transcript correctly
ordered regardless.) +1 same-second multi-turn regression test, toothless-checked.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-06-18 04:25:11 +00:00
Ed 79f526b1b2 fix(helpers): use ssl.SSLError instead of broad OSError in disconnect tuple
OSError is too broad — it masks real errors like file-not-found.
ssl.SSLError specifically catches SSL-level disconnects without
swallowing unrelated OSError subtypes.

Closes the test_excludes_broad_oserror CI failure.
2026-05-31 01:56:39 +00:00
ai-ag2026 5f42e87aa9 fix: skip stale repair for compression parents 2026-05-28 08:02:24 +02:00
ai-ag2026 2f1ca959f1 fix(chat): classify interrupted response causes
(cherry picked from commit 5c1e802cd6ee8565da74c7ffe57e6407fe21bf02)
2026-05-25 02:06:42 +02:00
ai-ag2026 efe3d7c296 fix(chat): avoid false restart wording for interrupted responses
(cherry picked from commit ef8fd879682aeb729a7b7afa1e7c46478ca5ebb6)
2026-05-25 02:06:42 +02:00
ai-ag2026 dd07334d6c fix(session): keep state db replays out of sidecar tail 2026-05-22 16:25:10 +00:00
Hermes Agentandai-ag2026 4db8df5e29 Stage 399: PR #2686 — fix(session): dedupe restamped state.db replay rows in /api/session display merge
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-05-21 17:56:40 +00:00
Isla Liu 37c3e84ad2 test(session): cover lazy journal retry give-up paths 2026-05-20 20:55:08 +08:00
Isla Liu 2a303de2a3 fix(session): preserve retry budget while journal is still arriving 2026-05-20 20:55:07 +08:00
Isla Liu d5a185d9c6 fix(session): serialize lazy journal retry per session 2026-05-20 20:48:38 +08:00
Isla Liu 1957785332 fix(session): address Copilot round-2 review — correct stale comment and drop unused fixture arg
Two non-functional cleanups from the second Copilot pass:

1. The inline comment in `test_error_marker_no_preserved_as_draft`
   said the legacy "user message above was preserved" wording was used
   for the post-retry-give-up case.  The actual implementation demotes
   give-up markers to a different neutral wording ("Partial output may
   have been lost.").  Comment rewritten to match the contract.

2. The regression test `test_lost_response_recovered_on_second_read`
   declared a `monkeypatch` parameter it never used.  Dropped.
2026-05-20 13:08:08 +08:00
Isla Liu 9870e8f111 fix(session): address Copilot review — scope tool-card dedupe by stream id + tighten docs
Four code-review comments from the automated Copilot reviewer on this PR:

1. `_journal_tool_already_present` dedupe was session-wide, so a
   legitimately-repeated tool (e.g. a second `terminal: ls` in an
   earlier turn) could cause the retry path to falsely skip
   materializing the recovered tool card.  The helper now takes a
   keyword `stream_id` argument; when supplied, a tool card whose
   `_recovered_stream_id` is set AND differs from the candidate is no
   longer treated as a duplicate.  Untagged tool cards (live tools, or
   tool cards carried over from a pre-tagging core transcript) still
   match, preserving the existing 'core transcript already has this
   tool, don't duplicate' invariant.  Two new tests in
   `TestJournalToolDedupeScoping` cover both legs of the rule.

2./3. The troubleshooting FAQ pointed at `~/.hermes/webui/sessions/session_<sid>.json`
   and `~/.hermes/_run_journal/...`.  The actual sidecar filename has
   no `session_` prefix and the run-journal lives under the WebUI
   sessions dir (`~/.hermes/webui/sessions/_run_journal/<sid>/<stream>.jsonl`,
   default).  Both paths fixed and an explicit note added about
   `HERMES_WEBUI_STATE_DIR` overriding the state root.

4. Drop unused `json` / `queue` / `Path` imports from
   `tests/test_session_lost_response_regression.py` so the file stops
   carrying noise that future linting would flag.
2026-05-20 12:18:03 +08:00
Isla Liu e8cd0bcc66 test(session): end-to-end regression for lost-response self-heal
Reproduces the production failure mode:

1. Stage 1 — sidecar repair runs while the run-journal for the dead
   stream is empty on disk. Assert the marker arms the lazy-retry
   hook (`_pending_journal_recovery=True`,
   `_journal_retry_stream_id`, `_journal_retry_attempts=0`,
   `_journal_retry_first_seen_ts`) and does NOT carry the legacy
   "no agent output was recovered" wording. Pending sidecar fields
   are cleared regardless.

2. Stage 2 — journaled token / tool / tool_complete / token events
   appear on disk. Call `get_session(sid)` and assert the marker
   self-heals: wording promotes to "recovered from the run journal",
   journaled assistant rows + tool card land above the marker in
   chronological order, all retry meta is stripped.

Without the lazy-retry path this test fails at the very first
assertion (marker still carries the legacy no-output wording).
2026-05-20 11:58:37 +08:00