mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-30 11:52:04 +00:00
* feat(tui): single /model command + unified Sessions overlay Collapse the redundant `/provider` alias so `/model` is the only name everywhere (it already drove the same 2-step ModelPicker in the TUI). Merge the separate `/resume` (cold history browser) and `/sessions` (live switcher) surfaces into one Sessions overlay reached by `/resume`, `/sessions`, `/session`, and `/switch`. It pins a "+ new" row at the top (always visible), lists live sessions with status, and lists resumable history below — dispatching session.activate for live rows vs resume for cold ones, with close/delete in place. Fixes `/session` opening an empty live-only switcher and the hidden new-session affordance. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(tui): address Copilot review on the Sessions overlay - Track the armed history-delete by session id instead of row index so the 1.5s live-status poll re-indexing rows can't redirect the second `d` to a different session. - Re-add the busy-session guard to immediate `/resume <id>` and `/sessions new` actions (browsing the bare overlay stays allowed) so resuming/switching can't corrupt an in-flight turn's streaming/busy state. * fix(tui): guard cold-resume (not live-switch/new) from the Sessions overlay Copilot flagged that overlay actions bypassed the busy guard. Only cold resume actually closes the current session, so only it is guarded — both from the slash path and now from the overlay (appActions.resumeById). Switching between live sessions and starting a `+ new` live session keep the current session running in the background, so they stay unguarded: that concurrency is the orchestrator's whole purpose. Also dropped the over-broad guard on `/sessions new` for the same reason. * fix(tui): address Copilot review (history dedup + desktop /provider) - The 1.5s poll now re-derives the resumable list from the RAW session.list results (rawHistoryRef) against the current live set, so a session hidden while live reappears in history once it closes — instead of being lost until a full reload. Delete also prunes the raw ref. - Drop the dead `/provider` entry from the desktop PICKER_OWNED_COMMANDS now that the alias is gone, so the desktop client no longer advertises it. * fix(tui): surface session.list errors + keep selection stable across polls - A garbled session.list response now surfaces an error and preserves the last good raw history, instead of silently blanking the resumable section. - The 1.5s poll re-anchors the selection to the same row by session id (live or history) when the live list grows/shrinks, so the highlight no longer drifts to a different row mid-interaction. * fix(tui): degrade session.list independently + cover overlay helpers - Fetch active_list and session.list via Promise.allSettled so a failing session.list no longer rejects the whole load: live sessions still render and only the resumable history degrades (with an error). - Add unit tests for the new helpers (sessionRowKindAt row ordering, resumableHistory dedupe, sessionsCountLabel, relativeSessionAge). * test(tui-gateway): assert /provider alias is gone, /model remains The CI test_complete_slash_includes_provider_alias asserted the removed `/provider` alias still autocompleted. Flip it to lock in the removal: `/pro` no longer offers `provider`, and `/mod` still completes `model`. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
73 lines
2 KiB
TypeScript
73 lines
2 KiB
TypeScript
import { Box, Text } from '@hermes/ink'
|
|
|
|
import { HOTKEYS } from '../content/hotkeys.js'
|
|
import type { Theme } from '../theme.js'
|
|
|
|
const COMMON_COMMANDS: [string, string][] = [
|
|
['/help', 'full list of commands + hotkeys'],
|
|
['/clear', 'start a new session'],
|
|
['/resume', 'switch live or resume past sessions'],
|
|
['/details', 'control transcript detail level'],
|
|
['/copy', 'copy selection or last assistant message'],
|
|
['/quit', 'exit hermes']
|
|
]
|
|
|
|
const HOTKEY_PREVIEW = HOTKEYS.slice(0, 8)
|
|
|
|
export function HelpHint({ t }: { t: Theme }) {
|
|
const labelW = Math.max(
|
|
...COMMON_COMMANDS.map(([k]) => k.length),
|
|
...HOTKEY_PREVIEW.map(([k]) => k.length)
|
|
)
|
|
|
|
const pad = (s: string) => s + ' '.repeat(Math.max(0, labelW - s.length + 2))
|
|
|
|
return (
|
|
<Box alignItems="flex-start" bottom="100%" flexDirection="column" left={0} position="absolute" right={0}>
|
|
<Box
|
|
alignSelf="flex-start"
|
|
borderColor={t.color.primary}
|
|
borderStyle="round"
|
|
flexDirection="column"
|
|
marginBottom={1}
|
|
opaque
|
|
paddingX={1}
|
|
>
|
|
<Text>
|
|
<Text bold color={t.color.primary}>
|
|
? quick help
|
|
</Text>
|
|
<Text color={t.color.muted}>
|
|
{' · type /help for the full panel · backspace to dismiss'}
|
|
</Text>
|
|
</Text>
|
|
|
|
<Box marginTop={1}>
|
|
<Text bold color={t.color.accent}>
|
|
Common commands
|
|
</Text>
|
|
</Box>
|
|
|
|
{COMMON_COMMANDS.map(([k, v]) => (
|
|
<Text key={k}>
|
|
<Text color={t.color.label}>{pad(k)}</Text>
|
|
<Text color={t.color.muted}>{v}</Text>
|
|
</Text>
|
|
))}
|
|
|
|
<Box marginTop={1}>
|
|
<Text bold color={t.color.accent}>
|
|
Hotkeys
|
|
</Text>
|
|
</Box>
|
|
|
|
{HOTKEY_PREVIEW.map(([k, v]) => (
|
|
<Text key={k}>
|
|
<Text color={t.color.label}>{pad(k)}</Text>
|
|
<Text color={t.color.muted}>{v}</Text>
|
|
</Text>
|
|
))}
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|