feat(tui): render self-improvement review summaries in the transcript

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 commit is contained in:
Teknium 2026-04-30 13:44:04 -07:00
parent 80a676658c
commit bbbce92651
5 changed files with 174 additions and 0 deletions

View file

@ -132,6 +132,33 @@ describe('createGatewayEventHandler', () => {
expect(ctx.system.sys).toHaveBeenCalledWith('compressing 968 messages (~123,400 tok)…')
})
it('surfaces self-improvement review summaries as a persistent system line', () => {
const appended: Msg[] = []
const ctx = buildCtx(appended)
const onEvent = createGatewayEventHandler(ctx)
onEvent({
payload: { text: "💾 Self-improvement review: Skill 'hermes-release' patched" },
type: 'review.summary'
} as any)
expect(ctx.system.sys).toHaveBeenCalledWith(
"💾 Self-improvement review: Skill 'hermes-release' patched"
)
})
it('ignores review.summary events with empty or missing text', () => {
const appended: Msg[] = []
const ctx = buildCtx(appended)
const onEvent = createGatewayEventHandler(ctx)
onEvent({ payload: { text: '' }, type: 'review.summary' } as any)
onEvent({ payload: { text: ' ' }, type: 'review.summary' } as any)
onEvent({ payload: undefined, type: 'review.summary' } as any)
expect(ctx.system.sys).not.toHaveBeenCalled()
})
it('clears the visible todo list when the todo tool returns an empty list', () => {
const appended: Msg[] = []
const todos = [{ content: 'Boil water', id: 'boil', status: 'in_progress' }]