fix: surface self-improvement review summaries across CLI, TUI, and gateway#18073
Merged
Conversation
When the self-improvement background review fires after a turn, it runs in a bg thread and emits a ' 💾 <summary>' line to announce what it saved to memory or skills. Two problems made this invisible to users even when the review successfully modified a skill: 1. The print went through `_cprint` (prompt_toolkit's print_formatted_text) on a bg thread while the CLI's PromptSession was live. Direct print_formatted_text races with the input-area redraw and the line can land behind/above the prompt, scrolled off without the user seeing it. 2. The message said only '💾 Skill created.' / '💾 Memory updated' with no indication that the self-improvement loop was the one doing this. Users who did catch the line couldn't tell the background review from some other agent action. Fixes: - `_cprint` now detects when it's called from a non-app thread with a running prompt_toolkit Application, and routes through `run_in_terminal` via `loop.call_soon_threadsafe`. That pauses the input, prints the line above the prompt, and redraws — the normal prompt_toolkit contract for bg-thread output. Direct-print fallback preserved for the no-app / same-thread / import-error paths. Affects every bg-thread emission, not just the review summary (curator summaries and auxiliary failure prints benefit too). - The summary now reads ' 💾 Self-improvement review: <summary>' in both the CLI and the gateway `background_review_callback` path, so the origin is unambiguous. Tests: - New `tests/cli/test_cprint_bg_thread.py` covers all five routing branches (no app, app-not-running, cross-thread schedule, same-thread direct, app-loop-attribute-error, import-error). - New case in `tests/run_agent/test_background_review.py` asserts the attributed prefix shows up in both `_safe_print` and `background_review_callback`. Live E2E: exercised _cprint from a bg thread inside a real Application event loop; confirmed get_app_or_none() sees the app, call_soon_threadsafe schedules run_in_terminal, and the inner _pt_print runs.
The Ink TUI (\`hermes --tui\` + dashboard \`/chat\`) had no wiring for the
background self-improvement review. When the review fired and patched
a skill or saved a memory entry, the change landed but the user had
no visual indication it happened — only the CLI had a print surface
for the '💾 Self-improvement review: …' line.
Changes:
- tui_gateway/server.py: in _init_session, attach
agent.background_review_callback to an _emit('review.summary',
sid, {text}) closure. Wrapped in try/except so agents with locked
attribute slots don't break session startup.
- ui-tui/src/app/createGatewayEventHandler.ts: handle 'review.summary'
by routing ev.payload.text through sys(…), matching the existing
'background.complete' pattern. Empty / whitespace payloads are
ignored so the transcript never gets a blank system line.
- ui-tui/src/gatewayTypes.ts: extend the GatewayEvent discriminated
union with { type: 'review.summary', payload?: { text?: string } }.
Gateway platforms (Telegram, Discord, Slack, …) already route the
review summary via background_review_callback → post-delivery queue
in gateway/run.py, so they pick up the new 'Self-improvement review:'
prefix from the companion run_agent change with no platform edits.
Tests:
- tests/tui_gateway/test_review_summary_callback.py (Python, 2 tests):
_init_session attaches a callback that emits the right event; the
callback path survives agents that can't accept the attribute.
- ui-tui/src/__tests__/createGatewayEventHandler.test.ts (vitest, 2
new cases): review.summary events feed sys(...) with the full text;
empty / missing payloads are no-ops.
- TypeScript type-check passes.
- tui_gateway suite: 64/64 pass.
This was referenced May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The self-improvement background review's
💾 …summary (the line that tells you it patched a skill or saved a memory entry) now reliably surfaces to the user across every interface — CLI, Ink TUI (hermes --tui+ dashboard/chat), and messaging gateways — and is attributed to the self-improvement loop so it's unambiguous.Root cause
Real-world trigger: on 2026-04-30 the
hermes-releaseskill was patched at 11:33:47 by a bg review (.usage.jsonrecords it) but no💾ever appeared in the still-open CLI session. Three silent failure modes:_cprintcalledprompt_toolkit.print_formatted_textdirectly while aPromptSessionwas live → raced with the input-area redraw, line could end up buried.agent.background_review_callbackwas never set by the tui_gateway, so Ink never saw the summary.💾 Skill updatedgave no hint the self-improvement loop was responsible.Changes
cli._cprint: detect cross-thread invocation with a live PTApplication; schedulerun_in_terminalvialoop.call_soon_threadsafe. Input pauses, line prints clean, prompt redraws. Direct-print fallbacks preserved for no-app, same-thread, import-error, and attribute-error paths. Fixes every bg-thread emission (curator summaries, aux failures), not just the review.run_agent._spawn_background_review: summary now reads💾 Self-improvement review: <actions>in both the_safe_printpath (CLI) and thebackground_review_callbackpath (TUI + gateway).tui_gateway/server.py: in_init_session, attachagent.background_review_callbackto an_emit('review.summary', sid, {text})closure. Safe on agents with locked__slots__.ui-tui/src/app/createGatewayEventHandler.ts: newreview.summarycase routespayload.textthroughsys(…)so it persists in the transcript, matching thebackground.completepattern. Empty / whitespace payloads are ignored.ui-tui/src/gatewayTypes.ts: extendGatewayEventunion with{ type: 'review.summary', payload?: { text?: string } }.background_review_callbackpost-delivery queue ingateway/run.pypicks up the new prefix string automatically.Validation
_cprintw/ live app_pt_printraces w/ prompt redrawrun_in_terminalvia app loopreview.summaryevent →sys(…)transcript line💾 Skill created.💾 Self-improvement review: Skill created.💾 Self-improvement review: <actions>everywhere_cprintrouting, 1 new bg-review prefix, 2 new tui_gateway callback)review.summary)_cprintcorrectly schedulesrun_in_terminalinside a real PT ApplicationScope limits
Unchanged: nudge thresholds, review prompt text, summary-action detection, gateway platform adapters, memory/skill core code. Surgical across three surfaces.