mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
refactor(tui): drive the vibe heart from the core reaction event
Replace the client-side GOOD_VIBES_RE detection with the backend `reaction` event: on it, flash the status-bar heart and the pet's celebrate pose. Detection now lives once in the core, so the TUI, CLI, and desktop stay in sync.
This commit is contained in:
parent
0e2adf9dad
commit
fbefb5c075
7 changed files with 21 additions and 17 deletions
|
|
@ -38,7 +38,6 @@ function makeDeps(gw: GatewayClient, over: Partial<SubmitPromptDeps> = {}): Subm
|
|||
enqueue: vi.fn(),
|
||||
expand: (t: string) => t,
|
||||
gw,
|
||||
maybeGoodVibes: vi.fn(),
|
||||
setLastUserMsg: vi.fn(),
|
||||
sys: vi.fn(),
|
||||
...over
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import type { Msg, SubagentProgress, SubagentStatus } from '../types.js'
|
|||
import { applyDelegationStatus, getDelegationState } from './delegationStore.js'
|
||||
import type { GatewayEventHandlerContext } from './interfaces.js'
|
||||
import { getOverlayState, patchOverlayState } from './overlayStore.js'
|
||||
import { flashPet } from './petFlashStore.js'
|
||||
import { flashGoodVibes, flashPet } from './petFlashStore.js'
|
||||
import { turnController } from './turnController.js'
|
||||
import { getTurnState } from './turnStore.js'
|
||||
import { getUiState, patchUiState } from './uiStore.js'
|
||||
|
|
@ -717,6 +717,14 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
|
|||
|
||||
return
|
||||
|
||||
case 'reaction':
|
||||
// Core-detected affection (ily / <3 / good bot): flash the ♥ and let the
|
||||
// pet celebrate. Same signal drives the desktop's floating hearts.
|
||||
flashGoodVibes()
|
||||
flashPet('jump')
|
||||
|
||||
return
|
||||
|
||||
case 'tool.start':
|
||||
turnController.recordTodos(ev.payload.todos)
|
||||
turnController.recordToolStart(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ export const $petFlash = atom<PetFlash | null>(null)
|
|||
|
||||
export const flashPet = (state: PetState, ms = 1600) => $petFlash.set({ state, until: Date.now() + ms })
|
||||
|
||||
// Affection-heart beat: a monotonic tick the status-bar ♥ flashes on. Bumped by
|
||||
// the gateway `reaction` event (core-detected ily / <3 / good bot) — the TUI's
|
||||
// share of the same signal that plays the desktop's floating hearts.
|
||||
export const $goodVibesTick = atom(0)
|
||||
|
||||
export const flashGoodVibes = () => $goodVibesTick.set($goodVibesTick.get() + 1)
|
||||
|
||||
// The floating pet's footprint, or null when no pet is shown. The transcript
|
||||
// keeps its text clear of the pet responsively: on wide terminals it reserves a
|
||||
// right gutter (`width`) so lines wrap to the pet's LEFT; on narrow terminals it
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export interface SubmitPromptDeps {
|
|||
enqueue: (text: string) => void
|
||||
expand: (text: string) => string
|
||||
gw: GatewayClient
|
||||
maybeGoodVibes: (text: string) => void
|
||||
setLastUserMsg: (value: string) => void
|
||||
sys: (text: string) => void
|
||||
}
|
||||
|
|
@ -61,7 +60,6 @@ export function submitPrompt(text: string, deps: SubmitPromptDeps, showUserMessa
|
|||
}
|
||||
|
||||
turnController.clearStatusTimer()
|
||||
deps.maybeGoodVibes(submitText)
|
||||
deps.setLastUserMsg(text)
|
||||
|
||||
if (show) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import { planGatewayRecovery } from './gatewayRecovery.js'
|
|||
import { getInputSelection } from './inputSelectionStore.js'
|
||||
import { type GatewayRpc, type TranscriptRow } from './interfaces.js'
|
||||
import { $overlayState, patchOverlayState } from './overlayStore.js'
|
||||
import { $goodVibesTick } from './petFlashStore.js'
|
||||
import { scrollWithSelectionBy } from './scroll.js'
|
||||
import { turnController } from './turnController.js'
|
||||
import { patchTurnState, useTurnSelector } from './turnStore.js'
|
||||
|
|
@ -48,7 +49,6 @@ import { useLongRunToolCharms } from './useLongRunToolCharms.js'
|
|||
import { useSessionLifecycle } from './useSessionLifecycle.js'
|
||||
import { useSubmission } from './useSubmission.js'
|
||||
|
||||
const GOOD_VIBES_RE = /\b(good bot|thanks|thank you|thx|ty|ily|love you)\b/i
|
||||
const BRACKET_PASTE_ON = '\x1b[?2004h'
|
||||
const BRACKET_PASTE_OFF = '\x1b[?2004l'
|
||||
const MAX_HEIGHT_CACHE_BUCKETS = 12
|
||||
|
|
@ -185,7 +185,8 @@ export function useMainApp(gw: GatewayClient) {
|
|||
const [sessionStartedAt, setSessionStartedAt] = useState(() => Date.now())
|
||||
const [turnStartedAt, setTurnStartedAt] = useState<null | number>(null)
|
||||
const [lastTurnEndedAt, setLastTurnEndedAt] = useState<null | number>(null)
|
||||
const [goodVibesTick, setGoodVibesTick] = useState(0)
|
||||
// Bumped by the gateway `reaction` event (core-detected affection).
|
||||
const goodVibesTick = useStore($goodVibesTick)
|
||||
const [bellOnComplete, setBellOnComplete] = useState(false)
|
||||
|
||||
const ui = useStore($uiState)
|
||||
|
|
@ -445,12 +446,6 @@ export function useMainApp(gw: GatewayClient) {
|
|||
[sys]
|
||||
)
|
||||
|
||||
const maybeGoodVibes = useCallback((text: string) => {
|
||||
if (GOOD_VIBES_RE.test(text)) {
|
||||
setGoodVibesTick(v => v + 1)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const rpc: GatewayRpc = useCallback(
|
||||
async <T extends Record<string, any> = Record<string, any>>(
|
||||
method: string,
|
||||
|
|
@ -690,7 +685,6 @@ export function useMainApp(gw: GatewayClient) {
|
|||
composerRefs,
|
||||
composerState,
|
||||
gw,
|
||||
maybeGoodVibes,
|
||||
setLastUserMsg,
|
||||
slashRef,
|
||||
submitRef,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
|||
composerRefs,
|
||||
composerState,
|
||||
gw,
|
||||
maybeGoodVibes,
|
||||
setLastUserMsg,
|
||||
slashRef,
|
||||
submitRef,
|
||||
|
|
@ -87,14 +86,13 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
|||
enqueue: composerActions.enqueue,
|
||||
expand,
|
||||
gw,
|
||||
maybeGoodVibes,
|
||||
setLastUserMsg,
|
||||
sys
|
||||
},
|
||||
showUserMessage
|
||||
)
|
||||
},
|
||||
[appendMessage, composerActions, composerState.pasteSnips, gw, maybeGoodVibes, setLastUserMsg, sys]
|
||||
[appendMessage, composerActions, composerState.pasteSnips, gw, setLastUserMsg, sys]
|
||||
)
|
||||
|
||||
const shellExec = useCallback(
|
||||
|
|
@ -362,7 +360,6 @@ export interface UseSubmissionOptions {
|
|||
composerRefs: ComposerRefs
|
||||
composerState: ComposerState
|
||||
gw: GatewayClient
|
||||
maybeGoodVibes: (text: string) => void
|
||||
setLastUserMsg: (value: string) => void
|
||||
slashRef: MutableRefObject<(cmd: string) => boolean>
|
||||
submitRef: MutableRefObject<(value: string) => void>
|
||||
|
|
|
|||
|
|
@ -618,6 +618,7 @@ export type GatewayEvent =
|
|||
| { payload?: GatewaySkin; session_id?: string; type: 'skin.changed' }
|
||||
| { payload: SessionInfo; session_id?: string; type: 'session.info' }
|
||||
| { payload?: { text?: string }; session_id?: string; type: 'thinking.delta' }
|
||||
| { payload?: { kind?: string }; session_id?: string; type: 'reaction' }
|
||||
| { payload?: undefined; session_id?: string; type: 'message.start' }
|
||||
| { payload?: { kind?: string; text?: string }; session_id?: string; type: 'status.update' }
|
||||
| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue