mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
A live run was the settled view under a CSS max-height: the real rows, capped at ~6.75rem, with an escape hatch that let an open diff lift the cap entirely. So it was neither a single line nor an honest expanded block, and a run could balloon mid-turn. Split the two presentations instead. Live, a run is its summary plus a one-line reel that slides each finished action up and out, so a turn touching thirty files reads as one line ticking over in place. Settled, the summary is the whole of it until opened. Cards — file edits, clarify, image_generate — leave the run entirely and stay on screen where they happened, since the diff or the question IS the point of the turn. Drops useToolWindow, the bounded-window threshold, the scroll container, the fade mask and the :has([data-tool-open]) escape hatch, all of which existed to make "the real rows, but shorter" work. Also unifies transcript scaffolding on one colour: thinking headers painted --ui-text-secondary and tool summaries --ui-text-tertiary under a shared opacity, which is two greys for one kind of line. Both now render through ScaffoldRow at a token pitched between them.
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { atom, computed } from 'nanostores'
|
|
|
|
// The tool whose arguments the model is streaming right now, and when it
|
|
// started. Generating a large payload (a 45 KB write_file) takes seconds during
|
|
// which the tool has not begun and the transcript has nothing to say.
|
|
// Per-session because a transcript may be a tile, and a tile must never narrate
|
|
// the primary chat's work.
|
|
const keyFor = (sessionId: string | null | undefined): string => sessionId ?? ''
|
|
|
|
export interface DraftingTool {
|
|
name: string
|
|
since: number
|
|
}
|
|
|
|
export const $draftingToolSessions = atom<Record<string, DraftingTool>>({})
|
|
|
|
/** What `sessionId` is drafting, if anything. */
|
|
export function sessionDraftingTool(sessionId: null | string) {
|
|
return computed($draftingToolSessions, sessions => sessions[keyFor(sessionId)] ?? null)
|
|
}
|
|
|
|
export function setSessionDraftingTool(sessionId: string | null | undefined, name: string): void {
|
|
const key = keyFor(sessionId)
|
|
const sessions = $draftingToolSessions.get()
|
|
|
|
if (!name) {
|
|
if (!(key in sessions)) {
|
|
return
|
|
}
|
|
|
|
const next = { ...sessions }
|
|
delete next[key]
|
|
$draftingToolSessions.set(next)
|
|
|
|
return
|
|
}
|
|
|
|
// Re-announcing the same tool keeps the original timestamp, so the elapsed
|
|
// count reads as one continuous wait rather than restarting.
|
|
if (sessions[key]?.name === name) {
|
|
return
|
|
}
|
|
|
|
$draftingToolSessions.set({ ...sessions, [key]: { name, since: Date.now() } })
|
|
}
|