hermes - 💡(How to fix) Fix Desktop composer can submit stale/empty/truncated hand-typed text on immediate Enter

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

Pasted long text appears less likely to reproduce because the internal state catches up differently; the deterministic repro is hand-typing followed by immediate Enter.

Fix Action

Fix / Workaround

A local hotfix was tested successfully by reading the current plain text directly from editorRef.current at submit/Enter time and synchronizing it back into assistant-ui before deciding whether to submit/queue/cancel.

Relevant local patch shape:

After applying the local hotfix:

Code Example

const readCurrentDraft = useCallback(() => {
  const current = editorRef.current ? composerPlainText(editorRef.current) : draftRef.current

  if (current !== draftRef.current) {
    draftRef.current = current
    aui.composer().setText(current)
  }

  return current
}, [aui])

---

Preflight compression: ~140,029 tokens >= 136,000 threshold
context compression started
Auxiliary compression ... exceeded 120.0s total timeout
RAW_BUFFERClick to expand / collapse

Bug Description

In Hermes Desktop, text typed directly into the chat composer can diverge from the internal assistant-ui composer state. The visible contentEditable editor may contain the full text, but the submitted draft can be stale/empty/partial.

This causes two user-visible failures:

  1. After typing text and immediately pressing Enter, the message sometimes does not send. The UI still treats the composer as empty (the right-side action remains the voice/start button instead of send).
  2. For longer hand-typed messages, the submitted message can be truncated at the tail even though the visible composer DOM contains the full text.

Pasted long text appears less likely to reproduce because the internal state catches up differently; the deterministic repro is hand-typing followed by immediate Enter.

Steps to Reproduce

  1. Open Hermes Desktop on Windows.
  2. Focus the chat composer.
  3. Type a long message manually (not paste).
  4. Press Enter immediately after finishing the last character.
  5. Observe either:
    • the message is not sent until another keypress/space or a short delay, or
    • the message sends but the tail of the typed text is missing.

A shorter repro for the stale-empty case:

  1. Type a short message such as hi.
  2. Press Enter immediately.
  3. The composer can still be treated as empty, so Enter does not submit.
  4. Pressing space or waiting about a second before Enter allows it to submit.

Expected Behavior

  • The Desktop composer should submit the text that is visibly present in the editor at the time Enter/click submit is triggered.
  • The send/voice button state should be based on the visible composer contents, not stale internal state.
  • Immediate Enter after typing should send without requiring a delay or extra keypress.
  • Long hand-typed messages should not be truncated.

Actual Behavior

  • The visible contentEditable content can be complete, but the draft read by submit logic is stale, empty, or missing the tail.
  • Immediate Enter after typing may do nothing because the app thinks there is no payload.
  • Long hand-typed messages may submit partially.

Environment

  • OS: Windows 10
  • App: Hermes Desktop
  • Repo: NousResearch/hermes-agent
  • Observed around Desktop version 0.15.1

Investigation Notes

The issue appears to be in apps/desktop/src/app/chat/composer/index.tsx.

The Desktop composer uses a custom contentEditable editor plus assistant-ui composer state. During rapid hand typing, the visible editor DOM can advance ahead of draft / aui.composer().getState().text. Submit paths and hasComposerPayload decisions rely on the stale state, so they can incorrectly treat the composer as empty or submit a stale/partial draft.

A local hotfix was tested successfully by reading the current plain text directly from editorRef.current at submit/Enter time and synchronizing it back into assistant-ui before deciding whether to submit/queue/cancel.

Relevant local patch shape:

const readCurrentDraft = useCallback(() => {
  const current = editorRef.current ? composerPlainText(editorRef.current) : draftRef.current

  if (current !== draftRef.current) {
    draftRef.current = current
    aui.composer().setText(current)
  }

  return current
}, [aui])

Then handleKeyDown, submitDraft, and queueing paths use readCurrentDraft() / currentHasPayload instead of relying only on draft / hasComposerPayload.

Verification

After applying the local hotfix:

  • npm run type-check passed.
  • npm run build passed.
  • Manual Desktop verification passed:
    • hand-typed long text + immediate Enter sent successfully;
    • submitted content was not truncated;
    • new Desktop conversation responded normally.

One separate apparent delay during testing was traced to an unrelated old-session context compression timeout, not to the composer submit path. Logs showed the message was received and compression started:

Preflight compression: ~140,029 tokens >= 136,000 threshold
context compression started
Auxiliary compression ... exceeded 120.0s total timeout

So the composer bug should be scoped to stale composer state / contentEditable synchronization, not model latency.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING