fix(tui): demote gateway log-noise from Activity to info tone

Restore the old-CLI contract where only complete failures tint Activity
red. Everything else is still visible for debugging but no longer
commandeers attention.

- gateway.stderr: always tone='info' (drops the ERRLIKE_RE regex)
- gateway.protocol_error: both pushes demoted to 'info'
- commands.catalog cold-start failure: demoted to 'info'
- approval.request: no longer duplicates the overlay into Activity

Kept as 'error': terminal `error` event, gateway.start_timeout,
gateway-exited, explicit status.update kinds.
This commit is contained in:
Brooklyn Nicholson 2026-04-21 22:51:37 -05:00 committed by Teknium
parent 76ad697dcb
commit 572e27c93f
2 changed files with 44 additions and 8 deletions

View file

@ -1,9 +1,9 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createGatewayEventHandler } from '../app/createGatewayEventHandler.js'
import { resetOverlayState } from '../app/overlayStore.js'
import { getOverlayState, resetOverlayState } from '../app/overlayStore.js'
import { turnController } from '../app/turnController.js'
import { resetTurnState } from '../app/turnStore.js'
import { getTurnState, resetTurnState } from '../app/turnStore.js'
import { patchUiState, resetUiState } from '../app/uiStore.js'
import { estimateTokensRough } from '../lib/text.js'
import type { Msg } from '../types.js'
@ -273,4 +273,42 @@ describe('createGatewayEventHandler', () => {
role: 'system'
})
})
it('keeps gateway noise informational and approval out of Activity', async () => {
const appended: Msg[] = []
const ctx = buildCtx(appended)
ctx.gateway.rpc = vi.fn(async () => {
throw new Error('cold start')
})
const onEvent = createGatewayEventHandler(ctx)
onEvent({ payload: { line: 'Traceback: noisy but non-fatal' }, type: 'gateway.stderr' } as any)
onEvent({ payload: { preview: 'bad framing' }, type: 'gateway.protocol_error' } as any)
onEvent({
payload: { command: 'rm -rf /tmp/nope', description: 'dangerous command' },
type: 'approval.request'
} as any)
onEvent({ payload: {}, type: 'gateway.ready' } as any)
await Promise.resolve()
await Promise.resolve()
expect(getOverlayState().approval).toMatchObject({ description: 'dangerous command' })
expect(getTurnState().activity).toMatchObject([
{ text: 'Traceback: noisy but non-fatal', tone: 'info' },
{ text: 'protocol noise detected · /logs to inspect', tone: 'info' },
{ text: 'protocol noise: bad framing', tone: 'info' },
{ text: 'command catalog unavailable: cold start', tone: 'info' }
])
})
it('still surfaces terminal turn failures as errors', () => {
const appended: Msg[] = []
const onEvent = createGatewayEventHandler(buildCtx(appended))
onEvent({ payload: { message: 'boom' }, type: 'error' } as any)
expect(getTurnState().activity).toMatchObject([{ text: 'boom', tone: 'error' }])
})
})

View file

@ -11,7 +11,6 @@ import { patchOverlayState } from './overlayStore.js'
import { turnController } from './turnController.js'
import { getUiState, patchUiState } from './uiStore.js'
const ERRLIKE_RE = /\b(error|traceback|exception|failed|spawn)\b/i
const NO_PROVIDER_RE = /\bNo (?:LLM|inference) provider configured\b/i
const statusFromBusy = () => (getUiState().busy ? 'running…' : 'ready')
@ -111,7 +110,7 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
turnController.pushActivity(String(r.warning), 'warn')
}
})
.catch((e: unknown) => turnController.pushActivity(`command catalog unavailable: ${rpcErrorMessage(e)}`, 'warn'))
.catch((e: unknown) => turnController.pushActivity(`command catalog unavailable: ${rpcErrorMessage(e)}`, 'info'))
if (!STARTUP_RESUME_ID) {
patchUiState({ status: 'forging session…' })
@ -201,7 +200,7 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
case 'gateway.stderr': {
const line = String(ev.payload.line).slice(0, 120)
turnController.pushActivity(line, ERRLIKE_RE.test(line) ? 'error' : 'warn')
turnController.pushActivity(line, 'info')
return
}
@ -222,11 +221,11 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
if (!turnController.protocolWarned) {
turnController.protocolWarned = true
turnController.pushActivity('protocol noise detected · /logs to inspect', 'warn')
turnController.pushActivity('protocol noise detected · /logs to inspect', 'info')
}
if (ev.payload?.preview) {
turnController.pushActivity(`protocol noise: ${String(ev.payload.preview).slice(0, 120)}`, 'warn')
turnController.pushActivity(`protocol noise: ${String(ev.payload.preview).slice(0, 120)}`, 'info')
}
return
@ -299,7 +298,6 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
const description = String(ev.payload.description ?? 'dangerous command')
patchOverlayState({ approval: { command: String(ev.payload.command ?? ''), description } })
turnController.pushActivity(`approval needed · ${description}`, 'warn')
setStatus('approval needed')
return