hermes-agent/apps/desktop/src/app/chat/thread-loading.ts
Brooklyn Nicholson d208f2c2c0 feat(desktop): reconcile live tool events, polish thread chrome, harden boot
- chat-messages: match tool rows by overlapping query/context/preview values
  so preview-first `tool.progress` rows reliably adopt later stable-id
  `tool.start` payloads instead of spawning ghost rows or mis-merging
  parallel same-name calls; preserve prior args/result across phases.
- tui_gateway: emit full args + parsed result on `tool.start` / `tool.complete`,
  drop redundant `tool.started` re-emit from `tool.progress`.
- electron/main: prefer SOURCE_REPO_ROOT before PATH `hermes` in dev so
  local backend edits actually run; split hardening helpers into
  `electron/hardening.cjs` with tests.
- thread/tool UI: one-shot enter animation keyed by stable ids, braille
  spinner for running rows, Cursor-like disclosure rows, drill-down +
  duration/count formatting via new tool-fallback-model.
- composer: extract `text-utils`, drop liquid-glass overrides.
- right-rail: split preview-pane into preview-console / preview-file.
- runtime: incremental external-store runtime + runtime-readiness gate;
  onboarding store + tests; route-resume hook test.
- regression tests for live tool reconciliation (parallel tools, id-less
  progress, preview-first rows, structured args/results).
2026-05-11 21:38:47 -04:00

26 lines
638 B
TypeScript

import type { ChatMessage } from '@/lib/chat-messages'
export type ThreadLoadingState = 'response' | 'session'
export function lastVisibleMessageIsUser(messages: ChatMessage[]): boolean {
const lastVisible = [...messages].reverse().find(message => !message.hidden)
return lastVisible?.role === 'user'
}
export function threadLoadingState(
loadingSession: boolean,
busy: boolean,
awaitingResponse: boolean,
lastVisibleIsUser: boolean
): ThreadLoadingState | undefined {
if (loadingSession) {
return 'session'
}
if (busy && awaitingResponse && lastVisibleIsUser) {
return 'response'
}
return undefined
}