Commit Graph
3 Commits
Author SHA1 Message Date
happy5318 95c174f038 fix(session): restore paging after compress; suffix-exclusive settle strategy (review round 3)
Two data-integrity blockers from round-3 review (nesquena-hermes 2026-09-19):

1. [CORE] A successful /compress returns the full transcript, but
   _applyManualCompressionResult kept the bounded preflight's paging
   signals (_messagesTruncated/_oldestIdx). Render now restores them
   before renderMessages(), same ordering as settle/cancel paths.

2. [SILENT] _restoreSettledSession preservation used
   (prefix || suffix), so repeated identical turns (colliding
   _messageIdentityKey) made BOTH comparisons succeed and the prefix
   branch spliced at the wrong offset - silently dropping rows and
   duplicating others. The server truncation signal now decides the
   strategy: truncated => suffix-exclusive, untruncated => prefix.

Tests: node behavioral driver covering /compress paging restore,
repeated-turn settle (no drop/dup, authoritative offset), and
gap-free msg_before paging after bounded recovery.
2026-09-19 12:55:40 +08:00
happy5318 f4b28346e5 fix(session): keep bounded-tail recovery suffix and paging offset
Bounded settle/cancel reloads return the latest 30 messages, not a
prefix of the visible window. The shorter-terminal preserve path only
compared prefixes, so a long session dropped the Connection-interrupted
marker. Anchor persist also ran before _oldestIdx was refreshed from
the bounded response, writing the scene against the wrong transcript
row.

- Treat a suffix match like a prefix match when preserving the terminal
  recovery marker
- Refresh _messagesTruncated / _oldestIdx before attaching/persisting
  the projected scene on settle, cancel, and apperror
- Document the msg_limit=30 / msg_limit=all contract
- Add runtime + source-order regression tests
2026-09-18 22:00:45 +08:00
happy5318 d24d6377f9 perf(api/session): bound recovery calls; add msg_limit=all escape hatch
Fixes the #7310 stall class: six frontend recovery paths (offline/bfcache
refreshSession, stream-end settle, cancel sync, /compress preflight, /retry,
/undo) issued bare GET /api/session reads that re-walked, re-redacted and
re-serialized the ENTIRE transcript (4-15s / 28MB on a 5k-row session,
#7625), and the same shape shipped the full session-level tool_calls list
because windowing was gated on msg_limit being present.

Changes:
- Server: add explicit msg_limit=all escape hatch; window session-level
  tool_calls by actual truncation (length comparison) instead of the
  presence of a limit parameter. The bare no-limit shape keeps its
  full-transcript contract (pinned by
  test_state_db_reconciliation_preserves_sidecar_order_when_timestamps_collide
  and the run-journal snapshot test).
- Client: refreshSession / _restoreSettledSession / cancel sync /
  /compress preflight / /retry / /undo now request the bounded tail
  (msg_limit=30) and restore the _messages_truncated/_messages_offset paging
  gate instead of hardcoding 'not truncated'; jump-to-start
  (_ensureAllMessagesLoaded) and outline jump opt in to the full transcript
  via msg_limit=all.

Tests: test_session_default_window.py covers helper semantics, per-call-site
bounded/all shapes, truncation-signal restoration, and a regression guard
that no bare GET /api/session fetch remains in static/. 454 related tests
pass; reverse-verified (reverting the fixes turns the tests red).
2026-09-18 11:24:51 +08:00