Skip to content

fix(mobile): recognize merge-into-tail compaction envelope (P0 erased/unanswered chat) - #810

Merged
IgorGanapolsky merged 5 commits into
mainfrom
fix/chat-compaction-prior-context-20260722
Jul 22, 2026
Merged

fix(mobile): recognize merge-into-tail compaction envelope (P0 erased/unanswered chat)#810
IgorGanapolsky merged 5 commits into
mainfrom
fix/chat-compaction-prior-context-20260722

Conversation

@IgorGanapolsky

Copy link
Copy Markdown
Owner

Root cause

Igor reported Hermes Mobile "erasing inputs and not answering" on thread "Why we made zero dollars? #7" (screenshot showed a raw wall of [PRIOR CONTEXT — for reference only...] / [END OF PRIOR CONTEXT — COMPACTION SUMMARY BELOW] / [CONTEXT COMPACTION — REFERENCE ONLY] text with an empty composer).

Traced to the gateway agent (~/.hermes/hermes-agent, agent/context_compressor.py): when compaction needs to insert a summary but head/tail role alternation would collide, it uses a merge-into-tail path (_MERGED_PRIOR_CONTEXT_HEADER / _MERGED_SUMMARY_DELIMITER) that wraps the REAL preserved turn (often the user's actual next message, or an empty tail message) like this:

[PRIOR CONTEXT — for reference only; not a new message]
<the real preserved turn>

[END OF PRIOR CONTEXT — COMPACTION SUMMARY BELOW]

[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted...
<summary body>

--- END OF CONTEXT SUMMARY — respond to the message below, not the summary above ---

hermes-mobile/src/utils/chatCompactionHandoff.ts only recognized the standalone [CONTEXT COMPACTION — REFERENCE ONLY] prefix (added for #123/#164/#186). It never learned this merge-into-tail envelope, so isContextCompactionHandoff / splitCompactionHandoff both fell through and the raw scaffold rendered unfiltered in the chat transcript — with the real turn buried and invisible inside it. That is the "erased input" illusion, and (per the compressor's own prompt engineering comments, #11475/#14521) is also a plausible contributor to weak-model non-answers when this format leaks into the prompt unexpectedly.

Fix

chatCompactionHandoff.ts:

  • isContextCompactionHandoff now also matches the [PRIOR CONTEXT — for reference only; not a new message] header.
  • splitCompactionHandoff extracts the real preserved turn (between the header and the delimiter) as remainder, and hides everything from the delimiter onward as summary — mirroring the Python side's own _is_context_summary_content / _strip_summary_prefix handling of the same envelope.
  • A malformed/truncated envelope (missing delimiter) is hidden rather than shown raw.
  • An envelope wrapping empty real content is dropped entirely — never rendered as a scary empty wall.

This flows through the existing prepareMessagesForDisplaystripCompactionHandoffsFromMessages pipeline already wired into ChatScreen.tsx, and reuses the exact SSOT filtering pattern already shipped for the #777 cron-delivery-scaffolding filter. The existing "Start fresh chat" CTA (lastTurnIsCompactionStall / shouldAutoOfferFreshOnCompactionStall, already on main) now also sees through this envelope correctly.

Scope note

Investigation also found PR #795 (merged same day) already fixed partial-history reconciliation, and PR #777 already established the scaffold-hiding pattern this PR extends. No ChatScreen.tsx / chatMessageDisplay.ts edits were needed — the fix is fully contained in the mobile display SSOT (chatCompactionHandoff.ts), per the mission's "filter on mobile display SSOT" instruction. No Chrome/computer-use was used — CLI/git/Jest only.

Verification

  • Focused: 13/13 new chatCompactionHandoff cases (merge-into-tail detect / extract / empty-drop / malformed-hide / prepareMessagesForDisplay).
  • Related: 5 suites / 78 tests green (chatMessageDisplay, sessionTokenGuards, emptyStreamReplyRecovery, streamAssistantText, chatCompactionHandoff).
  • Full: 223 suites / 1969 tests green (1 pre-existing unrelated skip).
  • tsc --noEmit clean.
  • Pre-commit hook (typecheck + related-file Jest, 54 suites / 668 tests) passed.

Made with Cursor

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@IgorGanapolsky
IgorGanapolsky enabled auto-merge (squash) July 22, 2026 18:08
@gitar-bot

gitar-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

Gitar is working

Gitar

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates the mobile chat compaction filter to handle merge-into-tail envelopes. The main changes are:

  • Recognizes [PRIOR CONTEXT] compaction envelopes in chatCompactionHandoff.ts.
  • Extracts the real preserved turn while hiding the compaction scaffold.
  • Drops empty or malformed merge envelopes instead of rendering raw markers.
  • Adds tests for detection, extraction, empty-drop, malformed-hide, and display pipeline behavior.

Confidence Score: 5/5

Safe to merge with low risk.

The change is narrowly scoped to the mobile display-side compaction filter and includes focused tests for the new envelope formats. No verified correctness or security issues were found in the reviewed paths.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the Jest test suite and confirmed all 13 tests passed with exit code 0.
  • Ran TypeScript typechecking with noEmit and confirmed it completed successfully with exit code 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
hermes-mobile/src/utils/chatCompactionHandoff.ts Adds merge-into-tail envelope detection and splitting while preserving real chat content and hiding scaffold text.
hermes-mobile/src/tests/chatCompactionHandoff.test.ts Adds regression tests for merge-into-tail compaction handoff display behavior.

Reviews (1): Last reviewed commit: "fix(mobile): recognize merge-into-tail c..." | Re-trigger Greptile

@IgorGanapolsky
IgorGanapolsky force-pushed the fix/chat-compaction-prior-context-20260722 branch from 2dc757a to 9e103e5 Compare July 22, 2026 18:18
…/unanswered chat)

Root cause of Igor's report ('Why we made zero dollars? #7' thread): the
gateway's ~/.hermes/hermes-agent agent/context_compressor.py has a
merge-into-tail compaction path (_MERGED_PRIOR_CONTEXT_HEADER /
_MERGED_SUMMARY_DELIMITER) used when head/tail role alternation would
otherwise collide. It wraps the real preserved turn in
'[PRIOR CONTEXT — for reference only; not a new message]' /
'[END OF PRIOR CONTEXT — COMPACTION SUMMARY BELOW]' before the compaction
summary scaffold. hermes-mobile's chatCompactionHandoff.ts only recognized
the standalone '[CONTEXT COMPACTION — REFERENCE ONLY]' prefix, so this
envelope rendered as a raw, scary wall of scaffold text with the user's
real turn buried and invisible inside it -- looking exactly like erased
input with no answer.

Fix: isContextCompactionHandoff / splitCompactionHandoff now detect the
merge-into-tail envelope, extract the real preserved turn as the visible
message, and hide the summary scaffold from the transcript -- the same
mobile-display-SSOT pattern as the #777 cron-delivery-scaffolding filter.
A malformed/truncated envelope (missing delimiter) is hidden rather than
shown raw. An envelope wrapping empty real content is dropped entirely
so it never renders as a scary empty wall.

Verification:
- Focused: 13/13 new chatCompactionHandoff cases (merge-into-tail detect/
  extract/empty-drop/malformed-hide/prepareMessagesForDisplay), 5 related
  suites 78/78 green.
- Full: 223 suites / 1969 tests green (1 pre-existing unrelated skip).
- tsc --noEmit clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
@IgorGanapolsky
IgorGanapolsky force-pushed the fix/chat-compaction-prior-context-20260722 branch from 19e03c9 to 3fd1920 Compare July 22, 2026 18:52
@IgorGanapolsky
IgorGanapolsky merged commit e2bafc3 into main Jul 22, 2026
13 checks passed
@IgorGanapolsky
IgorGanapolsky deleted the fix/chat-compaction-prior-context-20260722 branch July 22, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant